Commit 162fd62

derdilla <82763757+NobodyForNothing@users.noreply.github.com>
2024-06-25 18:13:59
fix and stabilize compact list
Signed-off-by: derdilla <82763757+NobodyForNothing@users.noreply.github.com>
1 parent 21405b4
app/lib/components/measurement_list/measurement_list.dart
@@ -12,28 +12,18 @@ class MeasurementList extends StatelessWidget {
   /// Create a list to display measurements and intakes.
   const MeasurementList({super.key,
     required this.settings,
-    required this.records,
-    required this.notes,
-    required this.intakes,
+    required this.entries,
   });
 
   /// Settings that determine general behavior.
   final Settings settings;
 
-  /// Records to display.
-  final List<BloodPressureRecord> records;
-
-  /// Complementary notes info to show.
-  final List<Note> notes;
-
-  /// Medicine intake info to show.
-  final List<MedicineIntake> intakes;
+  /// Entries sorted with newest comming first.
+  final List<FullEntry> entries;
 
   @override
   Widget build(BuildContext context) {
     final localizations = AppLocalizations.of(context)!;
-    final entries = FullEntryList.merged(records, notes, intakes);
-    entries.sort((a, b) => b.time.compareTo(a.time)); // newest first
     return Column(
       mainAxisSize: MainAxisSize.min,
       children: [
app/lib/components/measurement_list/measurement_list_entry.dart
@@ -7,6 +7,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:flutter_gen/gen_l10n/app_localizations.dart';
 import 'package:health_data_store/health_data_store.dart';
 import 'package:intl/intl.dart';
+import 'package:provider/provider.dart';
 
 /// Display of a blood pressure measurement data.
 class MeasurementListRow extends StatelessWidget {
@@ -24,7 +25,7 @@ class MeasurementListRow extends StatelessWidget {
   final Settings settings;
 
   /// Called when the user taps on the edit icon.
-  final void Function() onRequestEdit;
+  final void Function() onRequestEdit; // TODO: consider removing in favor of context methods
 
   @override
   Widget build(BuildContext context) {
@@ -51,7 +52,7 @@ class MeasurementListRow extends StatelessWidget {
                 tooltip: localizations.edit,
               ),
               IconButton(
-                onPressed: () => _deleteEntry(settings, context, localizations),
+                onPressed: () => context.deleteEntry(data),
                 icon: const Icon(Icons.delete),
                 tooltip: localizations.delete,
               ),
@@ -112,27 +113,33 @@ class MeasurementListRow extends StatelessWidget {
       ),
     ],
   );
+}
+
+extension DeleteEntry on BuildContext {
+  /// Delete record and note of an entry from the repositories.
+  Future<void> deleteEntry(FullEntry entry) async {
+    final localizations = AppLocalizations.of(this)!;
+    final settings = Provider.of<Settings>(this, listen: false);
+    final bpRepo = RepositoryProvider.of<BloodPressureRepository>(this);
+    final noteRepo = RepositoryProvider.of<NoteRepository>(this);
+    final messenger = ScaffoldMessenger.of(this);
 
-  void _deleteEntry(Settings settings, BuildContext context, AppLocalizations localizations) async {
-    final bpRepo = RepositoryProvider.of<BloodPressureRepository>(context); // TODO: extract
-    final noteRepo = RepositoryProvider.of<NoteRepository>(context);
-    final messenger = ScaffoldMessenger.of(context);
     bool confirmedDeletion = true;
     if (settings.confirmDeletion) {
-      confirmedDeletion = await showConfirmDeletionDialoge(context);
+      confirmedDeletion = await showConfirmDeletionDialoge(this);
     }
 
     if (confirmedDeletion) {
-      await bpRepo.remove(data.$1);
-      await noteRepo.remove(data.$2);
+      await bpRepo.remove(entry.$1);
+      await noteRepo.remove(entry.$2);
       messenger.removeCurrentSnackBar();
       messenger.showSnackBar(SnackBar(
         content: Text(localizations.deletionConfirmed),
         action: SnackBarAction(
           label: localizations.btnUndo,
           onPressed: () async {
-            await bpRepo.add(data.$1);
-            await noteRepo.add(data.$2);
+            await bpRepo.add(entry.$1);
+            await noteRepo.add(entry.$2);
           },
         ),
       ),);
app/lib/components/pressure_text.dart
@@ -20,6 +20,6 @@ class PressureText extends StatelessWidget {
     switch (context.watch<Settings>().preferredPressureUnit) {
       PressureUnit.mmHg => pressure?.mmHg,
       PressureUnit.kPa => pressure?.kPa,
-    }.toString(),
+    }?.toString(),
   );
 }
app/lib/l10n/app_en.arb
@@ -506,5 +506,7 @@
   "titleInCsv": "Title in CSV",
   "@titleInCsv": {},
   "preferredPressureUnit": "Preferred pressure unit",
-  "@preferredPressureUnit": {}
+  "@preferredPressureUnit": {},
+  "compactList": "Compact measurement list",
+  "@compactList": {}
 }
app/lib/model/storage/settings_store.dart
@@ -139,7 +139,7 @@ class Settings extends ChangeNotifier {
       'allowMissingValues': allowMissingValues,
       'drawRegressionLines': drawRegressionLines,
       'startWithAddMeasurementPage': startWithAddMeasurementPage,
-      'useLegacyList': useLegacyList,
+      'useLegacyList': compactList,
       'language': ConvertUtil.serializeLocale(language),
       'horizontalGraphLines': horizontalGraphLines.map(jsonEncode).toList(),
       'needlePinBarWidth': _needlePinBarWidth,
@@ -322,8 +322,8 @@ class Settings extends ChangeNotifier {
 
   bool _useLegacyList = false;
   /// Whether to use the compact list with swipe deletion.
-  bool get useLegacyList => _useLegacyList;
-  set useLegacyList(bool value) {
+  bool get compactList => _useLegacyList;
+  set compactList(bool value) {
     _useLegacyList = value;
     notifyListeners();
   }
app/lib/model/storage/update_legacy_settings.dart
@@ -181,7 +181,7 @@ Future<void> updateLegacySettings(Settings settings, ExportSettings exportSettin
             HorizontalGraphLine.fromJson(jsonDecode(e)),).toList();
         break;
       case 'useLegacyList':
-        settings.useLegacyList = sharedPreferences.getBool(key)!;
+        settings.compactList = sharedPreferences.getBool(key)!;
         break;
       case 'lastAppVersion':
         break;
app/lib/screens/elements/legacy_measurement_list.dart
@@ -1,67 +1,149 @@
-import 'dart:collection';
-
-import 'package:blood_pressure_app/components/dialoges/add_measurement_dialoge.dart';
-import 'package:blood_pressure_app/components/dialoges/confirm_deletion_dialoge.dart';
 import 'package:blood_pressure_app/components/nullable_text.dart';
-import 'package:blood_pressure_app/model/storage/intervall_store.dart';
 import 'package:blood_pressure_app/model/storage/settings_store.dart';
-import 'package:blood_pressure_app/screens/elements/blood_pressure_builder.dart';
 import 'package:flutter/material.dart';
-import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:flutter_gen/gen_l10n/app_localizations.dart';
 import 'package:health_data_store/health_data_store.dart';
 import 'package:intl/intl.dart';
-import 'package:provider/provider.dart';
 
 import '../../components/pressure_text.dart';
 
 /// A old more compact [BloodPressureRecord] list, that lacks some of the new
 /// features.
-class LegacyMeasurementsList extends StatelessWidget {
+class LegacyMeasurementsList extends StatefulWidget {
   /// Create a more compact measurement list without all new features.
-  LegacyMeasurementsList(BuildContext context, {super.key}) {
-    if (MediaQuery.of(context).size.width < 1000) {
-      _tableElementsSizes = [33, 9, 9, 9, 30];
-      _sideFlex = 1;
-    } else {
-      _tableElementsSizes = [20, 5, 5, 5, 60];
-      _sideFlex = 5;
-    }
-  }
+  LegacyMeasurementsList({super.key,
+    required this.data,
+    required this.settings,
+  });
+
+  /// Entries sorted with newest ordered first.
+  final List<FullEntry> data;
+
+  /// Settings that determine general behavior.
+  final Settings settings;
+
+  @override
+  State<LegacyMeasurementsList> createState() => _LegacyMeasurementsListState();
+}
+
+class _LegacyMeasurementsListState extends State<LegacyMeasurementsList> {
+
   late final List<int> _tableElementsSizes;
+
   late final int _sideFlex;
 
-  @override
-  Widget build(BuildContext context) => Consumer<Settings>(builder: (context, settings, child) =>
-      Column(
+  Widget _buildHeader() => Row(
+    children: [
+      Expanded(
+        flex: _sideFlex,
+        child: const SizedBox(),
+      ),
+      Expanded(
+        flex: _tableElementsSizes[0],
+        child: Text(AppLocalizations.of(context)!.time, style: const TextStyle(fontWeight: FontWeight.bold)),),
+      Expanded(
+        flex: _tableElementsSizes[1],
+        child: Text(AppLocalizations.of(context)!.sysShort, style: TextStyle(fontWeight: FontWeight.bold, color: widget.settings.sysColor)),),
+      Expanded(
+        flex: _tableElementsSizes[2],
+        child: Text(AppLocalizations.of(context)!.diaShort, style: TextStyle(fontWeight: FontWeight.bold, color: widget.settings.diaColor)),),
+      Expanded(
+        flex: _tableElementsSizes[3],
+        child: Text(AppLocalizations.of(context)!.pulShort, style: TextStyle(fontWeight: FontWeight.bold, color: widget.settings.pulColor)),),
+      Expanded(
+        flex: _tableElementsSizes[4],
+        child: Text(AppLocalizations.of(context)!.notes, style: const TextStyle(fontWeight: FontWeight.bold)),),
+      Expanded(
+        flex: _sideFlex,
+        child: const SizedBox(),
+      ),
+    ],
+  );
+
+  Widget _itemBuilder(context, int index) {
+    final formatter = DateFormat(widget.settings.dateFormatString);
+    return Column(
       children: [
-        Row(
-          children: [
-            Expanded(
-              flex: _sideFlex,
-              child: const SizedBox(),
-            ),
-            Expanded(
+        Dismissible(
+          key: Key(widget.data[index].time.toIso8601String()),
+          confirmDismiss: (direction) async {
+            if (direction == DismissDirection.startToEnd) { // edit
+              await context.createEntry(widget.data[index]);
+              return false;
+            } else { // delete
+              await context.deleteEntry(widget.data[index]);
+              return false;
+            }
+          },
+          onDismissed: (direction) {},
+          background: Container(
+            width: 10,
+            decoration:
+            BoxDecoration(color: Colors.blue, borderRadius: BorderRadius.circular(5)),
+            child: const Align(alignment: Alignment(-0.95, 0), child: Icon(Icons.edit)),
+          ),
+          secondaryBackground: Container(
+            width: 10,
+            decoration:
+            BoxDecoration(color: Colors.red, borderRadius: BorderRadius.circular(5)),
+            child: const Align(alignment: Alignment(0.95, 0), child: Icon(Icons.delete)),
+          ),
+          child: Container(
+            constraints: const BoxConstraints(minHeight: 40),
+            child: Row(children: [
+              Expanded(
+                flex: _sideFlex,
+                child: const SizedBox(),
+              ),
+              Expanded(
                 flex: _tableElementsSizes[0],
-                child: Text(AppLocalizations.of(context)!.time, style: const TextStyle(fontWeight: FontWeight.bold)),),
-            Expanded(
-                flex: _tableElementsSizes[1],
-                child: Text(AppLocalizations.of(context)!.sysShort, style: TextStyle(fontWeight: FontWeight.bold, color: settings.sysColor)),),
-            Expanded(
+                child: Text(formatter.format(widget.data[index].time)),),
+              Expanded(
+                  flex: _tableElementsSizes[1],
+                  child: PressureText(widget.data[index].sys)),
+              Expanded(
                 flex: _tableElementsSizes[2],
-                child: Text(AppLocalizations.of(context)!.diaShort, style: TextStyle(fontWeight: FontWeight.bold, color: settings.diaColor)),),
-            Expanded(
+                child: PressureText(widget.data[index].dia),),
+              Expanded(
                 flex: _tableElementsSizes[3],
-                child: Text(AppLocalizations.of(context)!.pulShort, style: TextStyle(fontWeight: FontWeight.bold, color: settings.pulColor)),),
-            Expanded(
+                child: NullableText(widget.data[index].pul?.toString()),),
+              Expanded(
                 flex: _tableElementsSizes[4],
-                child: Text(AppLocalizations.of(context)!.notes, style: const TextStyle(fontWeight: FontWeight.bold)),),
-            Expanded(
-              flex: _sideFlex,
-              child: const SizedBox(),
-            ),
-          ],
+                child: NullableText(() {
+                  String note = widget.data[index].note ?? '';
+                  for (final i in widget.data[index].intakes) {
+                    note += '${i.medicine.designation}(${i.dosis.mg}mg)';
+                  }
+                  return note.isEmpty ? null : note;
+                }()),
+              ),
+              Expanded(
+                flex: _sideFlex,
+                child: const SizedBox(),
+              ),
+            ],),
+          ),
+        ),
+        const Divider(
+          thickness: 1,
+          height: 1,
         ),
+      ],
+    );
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    if (MediaQuery.of(context).size.width < 1000) {
+      _tableElementsSizes = [33, 9, 9, 9, 30];
+      _sideFlex = 1;
+    } else {
+      _tableElementsSizes = [20, 5, 5, 5, 60];
+      _sideFlex = 5;
+    }
+    return Column(
+      children: [
+        _buildHeader(),
         const SizedBox(
           height: 10,
         ),
@@ -70,99 +152,20 @@ class LegacyMeasurementsList extends StatelessWidget {
           thickness: 2,
           color: Theme.of(context).colorScheme.primaryContainer,
         ),
-        Expanded(// TODO: intakes
-          child: BloodPressureBuilder(
-            rangeType: IntervallStoreManagerLocation.mainPage,
-            onData: (BuildContext context, UnmodifiableListView<BloodPressureRecord> records) {
-              if (records.isEmpty) return Text(AppLocalizations.of(context)!.errNoData);
+        Expanded(
+          child: Builder(
+            builder: (BuildContext context) {
+              if (widget.data.isEmpty) return Text(AppLocalizations.of(context)!.errNoData);
               return ListView.builder(
-                itemCount: records.length,
+                itemCount: widget.data.length,
                 shrinkWrap: true,
                 padding: const EdgeInsets.all(2),
-                itemBuilder: (context, index) {
-                  final formatter = DateFormat(settings.dateFormatString);
-                  return Column(
-                    children: [
-                      Dismissible(
-                        key: Key(records[index].time.toIso8601String()),
-                        confirmDismiss: (direction) async {
-                          final repo = RepositoryProvider.of<BloodPressureRepository>(context);
-                          if (direction == DismissDirection.startToEnd) {
-                            // edit
-                            await context.createEntry((records[index], Note(time: records[index].time), []));
-                            return false;
-                          } else { // delete
-                            if (!settings.confirmDeletion || await showConfirmDeletionDialoge(context)) {
-                              await repo.remove(records[index]);
-                              if (!context.mounted) return true;
-                              ScaffoldMessenger.of(context).removeCurrentSnackBar();
-                              ScaffoldMessenger.of(context).showSnackBar(SnackBar(
-                                content: Text(AppLocalizations.of(context)!.deletionConfirmed),
-                                action: SnackBarAction(
-                                  label: AppLocalizations.of(context)!.btnUndo,
-                                  onPressed: () async {
-                                    await repo.add(records[index]);
-                                  },
-                                ),
-                              ),);
-                              return true;
-                            }
-                            return false;
-                          }
-                        },
-                        onDismissed: (direction) {},
-                        background: Container(
-                          width: 10,
-                          decoration:
-                          BoxDecoration(color: Colors.blue, borderRadius: BorderRadius.circular(5)),
-                          child: const Align(alignment: Alignment(-0.95, 0), child: Icon(Icons.edit)),
-                        ),
-                        secondaryBackground: Container(
-                          width: 10,
-                          decoration:
-                          BoxDecoration(color: Colors.red, borderRadius: BorderRadius.circular(5)),
-                          child: const Align(alignment: Alignment(0.95, 0), child: Icon(Icons.delete)),
-                        ),
-                        child: Container(
-                          constraints: const BoxConstraints(minHeight: 40),
-                          child: Row(children: [
-                            Expanded(
-                              flex: _sideFlex,
-                              child: const SizedBox(),
-                            ),
-                            Expanded(
-                              flex: _tableElementsSizes[0],
-                              child: Text(formatter.format(records[index].time)),),
-                            Expanded(
-                              flex: _tableElementsSizes[1],
-                              child: PressureText(records[index].sys)),
-                            Expanded(
-                              flex: _tableElementsSizes[2],
-                              child: PressureText(records[index].dia),),
-                            Expanded(
-                              flex: _tableElementsSizes[3],
-                              child: NullableText(records[index].pul?.toString()),),
-                            // FIXME: reimplement notes
-                            /*Expanded(
-                                  flex: _tableElementsSizes[4],
-                                  child: Text(data[index].),),*/
-                            Expanded(
-                              flex: _sideFlex,
-                              child: const SizedBox(),
-                            ),
-                          ],),
-                        ),
-                      ),
-                      const Divider(
-                        thickness: 1,
-                        height: 1,
-                      ),
-                    ],
-                  );
-                },);
+                itemBuilder: _itemBuilder,
+              );
             },
           ),
         ),
       ],
-    ),);
+    );
+  }
 }
app/lib/screens/home_screen.dart
@@ -54,23 +54,29 @@ class AppHome extends StatelessWidget {
                     sys:Pressure.mmHg(1), dia: Pressure.mmHg(2), pul: 3), Note(time: DateTime(2023), note: 'testTxt',), [])),*/
                   const MeasurementGraph(),
                   Expanded(
-                    child: (settings.useLegacyList) ?
-                      LegacyMeasurementsList(context) :
-                      BloodPressureBuilder(
+                    child: BloodPressureBuilder(
+                      rangeType: IntervallStoreManagerLocation.mainPage,
+                      onData: (context, records) => RepositoryBuilder<MedicineIntake, MedicineIntakeRepository>(
                         rangeType: IntervallStoreManagerLocation.mainPage,
-                        onData: (context, records) => RepositoryBuilder<MedicineIntake, MedicineIntakeRepository>(
+                        onData: (BuildContext context, List<MedicineIntake> intakes) => RepositoryBuilder<Note, NoteRepository>(
                           rangeType: IntervallStoreManagerLocation.mainPage,
-                          onData: (BuildContext context, List<MedicineIntake> intakes) => RepositoryBuilder<Note, NoteRepository>(
-                            rangeType: IntervallStoreManagerLocation.mainPage,
-                            onData: (BuildContext context, List<Note> notes) => MeasurementList(
+                          onData: (BuildContext context, List<Note> notes) {
+                            final entries = FullEntryList.merged(records, notes, intakes);
+                            entries.sort((a, b) => b.time.compareTo(a.time)); // newest first
+                            if (settings.compactList) {
+                              return LegacyMeasurementsList(
+                                data: entries,
+                                settings: settings,
+                              );
+                            }
+                            return MeasurementList(
                               settings: settings,
-                              records: records,
-                              notes: notes.cast(),
-                              intakes: intakes.cast(),
-                            ),
-                          ),
+                              entries: entries,
+                            );
+                          },
                         ),
                       ),
+                    ),
                   ),
                 ],),
               ),),
app/lib/screens/settings_screen.dart
@@ -133,12 +133,12 @@ class SettingsPage extends StatelessWidget {
                 initialColor: settings.pulColor,
                 title: Text(localizations.pulColor),),
               SwitchListTile(
-                value: settings.useLegacyList,
+                value: settings.compactList,
                 onChanged: (value) {
-                  settings.useLegacyList = value;
+                  settings.compactList = value;
                 },
                 secondary: const Icon(Icons.list_alt_outlined),
-                title: Text(localizations.useLegacyList),),
+                title: Text(localizations.compactList),),
             ],),
 
             TitledColumn(title: Text(localizations.behavior), children: [
app/test/model/json_serialization_test.dart
@@ -114,7 +114,7 @@ void main() {
       expect(initial.allowMissingValues, fromJson.allowMissingValues);
       expect(initial.drawRegressionLines, fromJson.drawRegressionLines);
       expect(initial.startWithAddMeasurementPage, fromJson.startWithAddMeasurementPage);
-      expect(initial.useLegacyList, fromJson.useLegacyList);
+      expect(initial.compactList, fromJson.compactList);
       expect(initial.horizontalGraphLines.length, fromJson.horizontalGraphLines.length);
       expect(initial.horizontalGraphLines.first.color.value, fromJson.horizontalGraphLines.first.color.value);
       expect(initial.horizontalGraphLines.first.height, fromJson.horizontalGraphLines.first.height);
@@ -140,7 +140,7 @@ void main() {
 
       expect(v1.pulColor.value, Settings().pulColor.value);
       expect(v2.validateInputs, Settings().validateInputs);
-      expect(v3.useLegacyList, Settings().useLegacyList);
+      expect(v3.compactList, Settings().compactList);
     });
   });