import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:logging/logging.dart'; import 'package:moneymgr_mobile/services/storage/expenses.dart'; import 'package:moneymgr_mobile/services/storage/prefs.dart'; import 'package:moneymgr_mobile/utils/ocr_utils.dart'; import 'package:moneymgr_mobile/utils/pdf_utils.dart'; import 'package:moneymgr_mobile/widgets/expense_editor.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'scan_screen.g.dart'; /// Scan a document & return generated PDF as byte file @riverpod Future<(Uint8List?, BaseExpenseInfo?)> _scanDocument(Ref ref) async { final prefs = ref.watch(prefsProvider).requireValue; final pdf = await scanDocAsPDF(); final img = await renderPdf(pdfBytes: pdf); final amount = await extractInfoFromBill( imgBuff: img, extractDates: !prefs.disableExtractDates(), ); return (pdf, amount); } class ScanScreen extends HookConsumerWidget { const ScanScreen({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final scanDocProvider = ref.watch(_scanDocumentProvider); final expenses = ref.watch(expensesProvider).requireValue; restartScan() async { try { final val = ref.refresh(_scanDocumentProvider); Logger.root.info("Load again startup result: $val"); } catch (e, s) { Logger.root.shout("Failed to try again startup loading! $e $s"); } } return Padding( padding: const EdgeInsets.all(8.0), child: switch (scanDocProvider) { AsyncData(:final value) when value.$1 != null => ExpenseEditor( file: value.$1!, initialData: value.$2, onFinished: (expense) async { await expenses.add( info: expense, fileContent: value.$1!, fileMimeType: "application/pdf", ); restartScan(); }, onRescan: restartScan, ), // No data AsyncData(:final value) when value.$1 == null => ScanErrorScreen( message: "No document scanned!", onTryAgain: restartScan, ), // Error AsyncError(:final error) => ScanErrorScreen( message: error.toString(), onTryAgain: restartScan, ), _ => const Center(child: CircularProgressIndicator()), }, ); } } class ScanErrorScreen extends StatelessWidget { final String message; final Function() onTryAgain; const ScanErrorScreen({ super.key, required this.message, required this.onTryAgain, }); @override Widget build(BuildContext context) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Spacer(flex: 5), Text("An error occurred while scanning"), Spacer(flex: 1), Text(message, textAlign: TextAlign.center), Spacer(flex: 1), MaterialButton( onPressed: onTryAgain, child: Text("Try again".toUpperCase()), ), Spacer(flex: 5), ], ), ); } }