Commit 1c4ea25

derdilla <82763757+NobodyForNothing@users.noreply.github.com>
2024-03-11 07:32:42
implement import preview data returning
Signed-off-by: derdilla <82763757+NobodyForNothing@users.noreply.github.com>
1 parent fe6ccdd
Changed files (2)
lib
components
screens
subsettings
lib/components/dialoges/import_preview.dart
@@ -1,13 +1,19 @@
 
 import 'dart:math';
 
+import 'package:blood_pressure_app/components/custom_banner.dart';
 import 'package:blood_pressure_app/components/dialoges/fullscreen_dialoge.dart';
+import 'package:blood_pressure_app/model/blood_pressure/record.dart';
 import 'package:blood_pressure_app/model/export_import/column.dart';
 import 'package:blood_pressure_app/model/export_import/csv_record_parsing_actor.dart';
 import 'package:blood_pressure_app/model/storage/export_columns_store.dart';
 import 'package:flutter/material.dart';
+import 'package:flutter/scheduler.dart';
 import 'package:flutter_gen/gen_l10n/app_localizations.dart';
 
+/// A preview that allows customizing columns used for csv data import.
+///
+/// Pops the scope with a list of measurements on save ([List<BloodPressureRecord>?]).
 class ImportPreview extends StatefulWidget {
   /// Create a preview of how the app would import csv with options.
   const ImportPreview({super.key,
@@ -34,16 +40,52 @@ class _ImportPreviewState extends State<ImportPreview> {
 
   late CsvRecordParsingActor _actor;
 
+  late final ScaffoldMessengerState messenger = ScaffoldMessenger.of(context);
+
+  bool _showingError = false;
+
   @override
   void initState() {
     super.initState();
     _actor = widget.initialActor;
+    SchedulerBinding.instance.addPostFrameCallback((_) => _updateBanner());
+  }
+
+  void _updateBanner() {
+    if (_showingError) {
+      _showingError = false;
+      messenger.removeCurrentMaterialBanner();
+    }
+    final err = _actor.attemptParse().error;
+    if (err != null) {
+      final localizations = AppLocalizations.of(context)!;
+      messenger.showMaterialBanner(CustomBanner(
+        content: Text(err.localize(localizations),
+          style: TextStyle(color: Theme.of(context).colorScheme.error),),
+      ),);
+      _showingError = true;
+    }
+  }
+
+  @override
+  void dispose() {
+    if(_showingError) {
+      SchedulerBinding.instance.addPostFrameCallback(
+        // TODO: add on close hook to dialoge and remove anti-pattern
+          (_) => messenger.removeCurrentMaterialBanner(),);
+    }
+    super.dispose();
   }
 
   @override
   Widget build(BuildContext context) => FullscreenDialoge(
     actionButtonText: AppLocalizations.of(context)!.import,
     bottomAppBar: widget.bottomAppBar,
+    onActionButtonPressed: (_showingError) ? null : () {
+      final result = _actor.attemptParse();
+      if (result.hasError()) return;
+      Navigator.pop<List<BloodPressureRecord>>(context, result.getOr((e) => null));
+    },
     body: SingleChildScrollView(
       child: SingleChildScrollView(
         scrollDirection: Axis.horizontal,
@@ -54,7 +96,7 @@ class _ImportPreviewState extends State<ImportPreview> {
               Column(
                 crossAxisAlignment: CrossAxisAlignment.end,
                 children: [
-                  DropdownButton(
+                  DropdownButton( // TODO: show original column name
                     items: [
                       DropdownMenuItem(
                         child: Text(
@@ -68,7 +110,7 @@ class _ImportPreviewState extends State<ImportPreview> {
                         DropdownMenuItem(
                           value: parser,
                           child: Padding(
-                            padding: EdgeInsets.only(top: 8),
+                            padding: const EdgeInsets.only(top: 8),
                             child: Column(
                               crossAxisAlignment: CrossAxisAlignment.start,
                               children: [
@@ -85,6 +127,7 @@ class _ImportPreviewState extends State<ImportPreview> {
                       setState(() {
                         _actor.changeColumnParser(_actor.columnNames[colIdx], parser);
                       });
+                      _updateBanner();
                     },
                   ),
                   const Divider(),
@@ -99,8 +142,8 @@ class _ImportPreviewState extends State<ImportPreview> {
                       alignment: AlignmentDirectional.center,
                       child: Padding(
                         padding: EdgeInsets.symmetric(horizontal: 10),
-                        child: Text('...')
-                      )
+                        child: Text('...'),
+                      ),
                     ),
                 ],
               ),
@@ -141,3 +184,22 @@ class _ImportPreviewState extends State<ImportPreview> {
     );
   }
 }
+
+// TODO: add setting for row limiting to make errors actionable
+
+/// Shows a dialoge to preview import of a csv file
+Future<List<BloodPressureRecord>?> showImportPreview(
+  BuildContext context,
+  CsvRecordParsingActor initialActor,
+  ExportColumnsManager columnsManager,
+  bool bottomAppBar,) =>
+  showDialog<List<BloodPressureRecord>>(
+    context: context, builder: (context) =>
+    Dialog.fullscreen(
+      child: ImportPreview(
+        bottomAppBar: bottomAppBar,
+        initialActor: initialActor,
+        columnsManager: columnsManager,
+      ),
+    ),
+  );
lib/screens/subsettings/export_import/export_button_bar.dart
@@ -8,7 +8,6 @@ import 'package:blood_pressure_app/model/blood_pressure/record.dart';
 import 'package:blood_pressure_app/model/export_import/csv_converter.dart';
 import 'package:blood_pressure_app/model/export_import/csv_record_parsing_actor.dart';
 import 'package:blood_pressure_app/model/export_import/pdf_converter.dart';
-import 'package:blood_pressure_app/model/export_import/record_parsing_result.dart';
 import 'package:blood_pressure_app/model/storage/export_columns_store.dart';
 import 'package:blood_pressure_app/model/storage/export_csv_settings_store.dart';
 import 'package:blood_pressure_app/model/storage/export_pdf_settings_store.dart';
@@ -75,20 +74,18 @@ class ExportButtonBar extends StatelessWidget {
                       Provider.of<CsvExportSettings>(context, listen: false),
                       Provider.of<ExportColumnsManager>(context, listen: false),
                     );
-                    // TODO: integrate properly
-                    await showDialog(context: context, builder: (context) =>
-                      Dialog.fullscreen(
-                        child: ImportPreview(
-                          bottomAppBar: true,
-                          initialActor: CsvRecordParsingActor(
-                            converter,
-                            utf8.decode(binaryContent),
-                          ),
-                          columnsManager: Provider.of<ExportColumnsManager>(context, listen: false),
-                        ),
+                    // TODO: verify that works
+                    final importedRecords = await showImportPreview(
+                      context,
+                      CsvRecordParsingActor(
+                        converter,
+                        utf8.decode(binaryContent),
                       ),
+                      Provider.of<ExportColumnsManager>(context, listen: false),
+                      true, // TODO use settings
                     );
-                    final result = converter.parse(utf8.decode(binaryContent));
+
+                    /*final result = converter.parse(utf8.decode(binaryContent));
                     final importedRecords = result.getOr((error) {
                       switch (error) {
                         case RecordParsingErrorEmptyFile():
@@ -111,6 +108,8 @@ class ExportButtonBar extends StatelessWidget {
                       return null;
                     });
                     if (result.hasError()) return;
+                     */
+                    if (importedRecords == null || !context.mounted) return;
                     final model = Provider.of<BloodPressureModel>(context, listen: false);
                     await model.addAll(importedRecords, null);
                     messenger.showSnackBar(SnackBar(content: Text(