import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; import 'package:go_router/go_router.dart'; import 'package:mobile_scanner/mobile_scanner.dart'; import 'package:seshat/domain/models/book.dart'; import 'package:seshat/routing/routes.dart'; import 'package:seshat/ui/add_page/view_model/add_view_model.dart'; import 'package:seshat/ui/add_page/widgets/form_popup.dart'; import 'package:seshat/ui/add_page/widgets/owner_popup.dart'; import 'package:seshat/ui/add_page/widgets/confirmation_popup.dart'; import 'package:seshat/ui/core/ui/navigation_bar.dart'; import 'package:seshat/ui/core/ui/await_loading.dart'; import 'package:seshat/utils/result.dart'; class AddPage extends StatefulWidget { const AddPage({super.key, required this.viewModel}); final AddViewModel viewModel; @override State createState() => _AddPageState(); } class _AddPageState extends State { final MobileScannerController scannerController = MobileScannerController( formats: [BarcodeFormat.ean13], detectionTimeoutMs: 1000, ); @override void dispose() { scannerController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final theme = Theme.of(context); return Scaffold( bottomNavigationBar: AppNavigationBar(startIndex: 1), body: ListenableBuilder( listenable: widget.viewModel, builder: (context, child) => switch (widget.viewModel.isLoaded) { false => AwaitLoading(), true => switch (widget.viewModel.ongoingBal) { null => Center( child: SizedBox( width: 300, child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Text( "Aucune bal n'est active.", style: TextStyle(fontSize: 25), textAlign: TextAlign.center, ), SizedBox(height: 15), Text( "Vous devez créer puis activer une BAL pour pouvoir scanner des livres.", textAlign: TextAlign.center, ), SizedBox(height: 30), ElevatedButton( onPressed: () { context.go(Routes.home); }, child: Text("Gérer les BALs"), ), ], ), ), ), _ => Stack( children: [ ColoredBox(color: Colors.black), MobileScanner( controller: scannerController, onDetect: (barcodes) async { if (barcodes.barcodes.isEmpty) { return; } if (widget.viewModel.currentOwner == null) { _showMissingOwnerSnackBar( context, scannerController, widget.viewModel, ); return; } _scanEan( context, widget.viewModel, barcodes.barcodes.first.rawValue!, scannerController, ); }, ), Center( child: SvgPicture.asset( 'assets/scan-overlay.svg', height: (MediaQuery.sizeOf(context).height / 5) * 2, ), ), SafeArea( child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: [ SizedBox(height: 5), Center( child: Card( margin: EdgeInsets.symmetric(horizontal: 50), child: Column( mainAxisSize: MainAxisSize.min, children: [ ListTile( leading: Icon(Icons.person), title: TextButton( child: Text( (widget.viewModel.currentOwner == null) ? "Aucun" : "${widget.viewModel.currentOwner!.firstName} ${widget.viewModel.currentOwner!.lastName}", ), onPressed: () => _ownerDialogBuilder( context, scannerController, widget.viewModel, ), ), ), ListTile( leading: Icon(Icons.attach_money), title: TextButton( child: Text( (widget.viewModel.askPrice) ? "Demander à chaque fois" : "Prix libre toujours", ), onPressed: () { setState(() { widget.viewModel.askPrice = !widget.viewModel.askPrice; }); }, ), ), ], ), ), ), ], ), ), ), SafeArea( child: Column( mainAxisAlignment: MainAxisAlignment.end, children: [ Center( child: TextButton( style: ButtonStyle( backgroundColor: WidgetStatePropertyAll( theme.cardColor, ), ), onPressed: () { if (widget.viewModel.currentOwner == null) { _showMissingOwnerSnackBar( context, scannerController, widget.viewModel, ); return; } _formDialogBuilder( context, scannerController, widget.viewModel, ); }, child: Text("Enregistrer manuellement"), ), ), SizedBox(height: 5), ], ), ), ], ), }, }, ), ); // }, // ); } } void _scanEan( BuildContext context, AddViewModel viewModel, String ean, MobileScannerController scannerController, { Function(BuildContext)? leaveLastPopup, }) async { Result result = await viewModel.scanBook(ean); if (context.mounted) { if (leaveLastPopup != null) { leaveLastPopup(context); } switch (result) { case Ok(): await _confirmationDialogBuilder( context, scannerController, viewModel, result.value, ); break; case Error(): ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text("Erreur : ${result.error}"), behavior: SnackBarBehavior.floating, ), ); break; } } } void _showMissingOwnerSnackBar( BuildContext context, MobileScannerController scannerController, AddViewModel viewModel, ) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text("Attention : vous devez choisir un·e propriétaire"), duration: Duration(seconds: 4), action: SnackBarAction( label: "Choisir", onPressed: () => _ownerDialogBuilder(context, scannerController, viewModel), ), behavior: SnackBarBehavior.floating, ), ); } Future _confirmationDialogBuilder( BuildContext context, MobileScannerController scannerController, AddViewModel viewModel, Book book, ) { scannerController.stop(); // Utility function to pass to downwards widgets void exitPopup(BuildContext localContext) { Navigator.of(localContext).pop(); scannerController.start(); } return showDialog( context: context, barrierDismissible: false, builder: (context) => ConfirmationPopup( exitPopup: exitPopup, viewModel: viewModel, book: book, ), ); } Future _formDialogBuilder( BuildContext context, MobileScannerController controller, AddViewModel viewModel, ) { controller.stop(); // Utility function to pass to downwards widgets void exitPopup(BuildContext localContext) { Navigator.of(localContext).pop(); controller.start(); } return showDialog( context: context, barrierDismissible: false, builder: (context) => FormPopup( viewModel: viewModel, exitPopup: exitPopup, scannerController: controller, scanEan: _scanEan, ), ); } Future _ownerDialogBuilder( BuildContext context, MobileScannerController controller, AddViewModel viewModel, ) { controller.stop(); // Utility function to pass to downwards widgets void exitPopup(BuildContext localContext) { Navigator.of(localContext).pop(); controller.start(); } return showDialog( context: context, barrierDismissible: false, builder: (context) => OwnerPopup(viewModel: viewModel, exitPopup: exitPopup), ); }