feat-add-files-from-qr (#8)

Reviewed-on: http://catelliub.zapto.org:3000/brontomark/flux/pulls/8
Co-authored-by: Mark M2 Macbook <marco@catelli.it>
Co-committed-by: Mark M2 Macbook <marco@catelli.it>
This commit is contained in:
2026-04-26 10:15:34 +02:00
committed by brontomark
parent 90bd5ecacf
commit 1c2bcf9df7
46 changed files with 2376 additions and 327 deletions

View File

@@ -1,6 +1,6 @@
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:supabase_flutter/supabase_flutter.dart'; // <--- AGGIUNGI QUESTO
import 'package:flux/core/utils/functions.dart';
class ImageViewerWidget extends StatelessWidget {
final String? storagePath; // ATTENZIONE: Ora contiene lo storagePath!
@@ -12,13 +12,6 @@ class ImageViewerWidget extends StatelessWidget {
'Errore: Devi fornire un Path valido o i bytes del file!',
);
// Funzione che chiede le chiavi a Supabase
Future<String> _getSignedUrl() async {
return await Supabase.instance.client.storage
.from('documents')
.createSignedUrl(storagePath!, 60); // Link che si autodistrugge in 60s
}
@override
Widget build(BuildContext context) {
return Scaffold(
@@ -37,7 +30,7 @@ class ImageViewerWidget extends StatelessWidget {
child: bytes != null
? Image.memory(bytes!)
: FutureBuilder<String>(
future: _getSignedUrl(),
future: getSignedUrl(storagePath!),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();

View File

@@ -1,9 +1,8 @@
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'package:flux/core/utils/functions.dart';
import 'package:pdfx/pdfx.dart';
import 'package:internet_file/internet_file.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
class PdfViewerWidget extends StatefulWidget {
final String? storagePath;
@@ -39,11 +38,7 @@ class _PdfViewerWidgetState extends State<PdfViewerWidget> {
pdfData = widget.bytes!;
} else if (widget.storagePath != null && widget.storagePath!.isNotEmpty) {
// SCENARIO 2: Pratica salvata, scarichiamo da Supabase (Remoto)
final signedUrl = await GetIt.I
.get<SupabaseClient>()
.storage
.from('documents')
.createSignedUrl(widget.storagePath!, 60);
final signedUrl = await getSignedUrl(widget.storagePath!);
pdfData = await InternetFile.get(signedUrl);
} else {
throw Exception("Nessun documento trovato");

View File

@@ -0,0 +1,93 @@
import 'package:flutter/material.dart';
import 'package:qr_flutter/qr_flutter.dart';
class QrUploadDialog extends StatelessWidget {
final String deepLinkUrl;
final String title;
const QrUploadDialog({
super.key,
required this.deepLinkUrl,
required this.title,
});
@override
Widget build(BuildContext context) {
// Usiamo i colori del tema per renderlo coerente col tuo design
final theme = Theme.of(context);
return AlertDialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
backgroundColor: theme.colorScheme.surface,
title: Column(
children: [
Icon(
Icons.qr_code_scanner,
size: 48,
color: theme.colorScheme.primary,
),
const SizedBox(height: 16),
Text(
title,
textAlign: TextAlign.center,
style: const TextStyle(fontWeight: FontWeight.bold),
),
],
),
content: SizedBox(
height: 400,
width: 350,
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min, // Fondamentale per i dialog
children: [
const Text(
"Inquadra questo codice con la fotocamera del tuo telefono per scattare e caricare i documenti direttamente qui.",
textAlign: TextAlign.center,
style: TextStyle(fontSize: 14, color: Colors.grey),
),
const SizedBox(height: 24),
// IL CUORE DELLA MAGIA
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors
.white, // Lo sfondo bianco salva la vita sui temi scuri
borderRadius: BorderRadius.circular(16),
),
child: QrImageView(
data: deepLinkUrl,
version: QrVersions.auto,
size: 200.0,
//Opzionale: puoi metterci il logo di FLUX in mezzo!
embeddedImage: const AssetImage('assets/images/logo.png'),
embeddedImageStyle: const QrEmbeddedImageStyle(
size: Size(40, 40),
),
),
),
const SizedBox(height: 16),
Text(
"In attesa di file...",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
color: theme.colorScheme.primary,
),
),
const SizedBox(height: 8),
const LinearProgressIndicator(), // Per far capire che è "in ascolto"
],
),
),
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text("CHIUDI"),
),
],
actionsAlignment: MainAxisAlignment.center,
);
}
}