| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -7,17 +7,17 @@ import 'package:flutter_hooks/flutter_hooks.dart';
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				import 'package:flutter_pdfview/flutter_pdfview.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/time_utils.dart';
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				import 'package:riverpod_annotation/riverpod_annotation.dart';
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				import 'package:scanbot_sdk/scanbot_sdk.dart';
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				import 'package:scanbot_sdk/scanbot_sdk_ui_v2.dart';
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				import '../../services/storage/expenses.dart';
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				import 'package:scanbot_sdk/scanbot_sdk_ui_v2.dart' hide IconButton, EdgeInsets;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				part 'scan_screen.g.dart';
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				@riverpod
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				Future<Uint8List?> _scanDocument(Ref ref) async {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  DocumentDataExtractionResult d;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  var configuration = DocumentScanningFlow(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    appearance: DocumentFlowAppearanceConfiguration(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      statusBarMode: StatusBarMode.DARK,
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -46,6 +46,8 @@ Future<Uint8List?> _scanDocument(Ref ref) async {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				class ScanScreen extends HookConsumerWidget {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  const ScanScreen({super.key});
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  @override
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  Widget build(BuildContext context, WidgetRef ref) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    final boredSuggestion = ref.watch(_scanDocumentProvider);
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -60,25 +62,28 @@ class ScanScreen extends HookConsumerWidget {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // Perform a switch-case on the result to handle loading/error states
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    return switch (boredSuggestion) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      AsyncData(:final value) when value != null => ExpenseEditor(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        file: value,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        onFinished: (e) {},
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      ),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    return Padding(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      padding: const EdgeInsets.all(8.0),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      child: switch (boredSuggestion) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        AsyncData(:final value) when value != null => ExpenseEditor(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          file: value,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          onFinished: (e) {},
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        ),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      // No data
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      AsyncData(:final value) when value == null => ScanErrorScreen(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        message: "No document scanned!",
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        onTryAgain: restartScan,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      ),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // No data
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        AsyncData(:final value) when value == null => ScanErrorScreen(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          message: "No document scanned!",
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          onTryAgain: restartScan,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        ),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      // Error
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      AsyncError(:final error) => ScanErrorScreen(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        message: error.toString(),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        onTryAgain: restartScan,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      ),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      _ => const Center(child: CircularProgressIndicator()),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    };
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // Error
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        AsyncError(:final error) => ScanErrorScreen(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          message: error.toString(),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          onTryAgain: restartScan,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        ),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        _ => const Center(child: CircularProgressIndicator()),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      },
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    );
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -115,7 +120,7 @@ class ScanErrorScreen extends StatelessWidget {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				class ExpenseEditor extends HookWidget {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				class ExpenseEditor extends HookConsumerWidget {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  final Uint8List file;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  final Function(Expense) onFinished;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -126,9 +131,27 @@ class ExpenseEditor extends HookWidget {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  });
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  @override
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  Widget build(BuildContext context) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  Widget build(BuildContext context, WidgetRef ref) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    final serverConfig = ref.watch(prefsProvider).requireValue.serverConfig()!;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    final labelController = useTextEditingController();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    final costController = useTextEditingController();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    final timeController = useState(DateTime.now());
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // Pick a new date
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    handlePickDate() async {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      final date = await showDatePicker(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        context: context,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        firstDate: DateTime(2000),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        lastDate: DateTime(2099),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        initialDate: timeController.value,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      );
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      if (date != null) timeController.value = date;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    return ListView(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      children: [
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // Expense preview
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        SizedBox(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          height: 200,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          child: PDFView(
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -140,6 +163,46 @@ class ExpenseEditor extends HookWidget {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            fitPolicy: FitPolicy.BOTH,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          ),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        ),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        SizedBox(height: 10),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // Cost
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        TextField(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          controller: costController,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          keyboardType: TextInputType.number,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          decoration: const InputDecoration(labelText: 'Cost'),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          textInputAction: TextInputAction.done,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        ),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        SizedBox(height: 10),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // Label
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        TextField(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          controller: labelController,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          keyboardType: TextInputType.text,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          decoration: const InputDecoration(labelText: 'Label'),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          textInputAction: TextInputAction.done,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          maxLength: serverConfig.constraints.inbox_entry_label.max,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        ),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        SizedBox(height: 10),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // Date
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        TextField(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          enabled: true,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          readOnly: true,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          controller: TextEditingController(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            text: timeController.value.simpleDate,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          ),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          keyboardType: TextInputType.datetime,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          decoration: InputDecoration(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            labelText: 'Date',
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            suffixIcon: IconButton(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				              onPressed: handlePickDate,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				              icon: const Icon(Icons.date_range),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            ),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          ),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        ),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      ],
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    );
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  }
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				 
 |