diff --git a/TODO b/TODO index e0485a2..d0ded7b 100644 --- a/TODO +++ b/TODO @@ -2,9 +2,10 @@ BUGFIXES: General/Framework: ☐ fix preloading of dropdown values (appear blank at first as of now) ☐ make sure 'null' isn't shown in text fields + Log Entry: + ☐ glucose target isn't displaed correctly anymore Basal/Bolus: ☐ "no element" error on creating basal/bolus rates when working from apk - ☐ scrollbars in rate overview not showing MAIN TASKS: Layout: @@ -20,23 +21,15 @@ MAIN TASKS: ☐ add clear button to dropdown (or all text fields?) ☐ check through all detail forms and set required fields/according messages ☐ implement component for durations - ☐ order category lists (meals, meal sources,...) alphabetically + ☐ change placement of delete and floating button because its very easy to accidentally hit delete Basal/Bolus: - ☐ show daily Basal sum in overview - ☐ show KI and stuff for Bolus in overview ☐ add save and close and next buttons on rate creations - Log Overview: - ☐ apply target color settings to glucose Log Entry: ☐ add save and close button ☐ move on to newly created entry after saving - ☐ handle text overflow for log meal list ☐ add option to specify trend for blood sugar ☐ recalculate bolus upon deactivating 'set manually' option ☐ account for delayed percentage setting on meals - Meal: - ☐ change delayed bolus rate to percentage - ☐ add meal source as subtitle in list Event Types: ☐ add colors as indicators for log entries (and later graphs in reports) Settings: @@ -44,6 +37,7 @@ MAIN TASKS: ☐ add fields for preferred date and time formats ☐ add fields for glucose target (as map of cutoff glucose and colors) ☐ add field for active insulin duration + ☐ add setting for carb units/bread units ☐ add option to switch 'save' and 'save & close' buttons FUTURE TASKS: @@ -52,14 +46,13 @@ FUTURE TASKS: ☐ find a better way to work with multiple glucose measurements (or disable it?) ☐ evaluate if some fields should be readonly instead of completely hidden ☐ alternate languages + ☐ log hba1c Reports: ☐ evaluate what type of reports there should be - Basal/Bolus: Log Overview: ☐ add pagination Log Entry: ☐ check if there is still an active bolus when suggesting glucose bolus - Meal: Event Types: ☐ add pagination ☐ implement reminders as push notifications @@ -67,6 +60,15 @@ FUTURE TASKS: ☐ add option to hide extra customization options (ie. changing pre calculated values)? Archive: + ✔ scrollbars in rate overview not showing @done(21-12-06 20:01) @project(BUGFIXES.Basal/Bolus) + ✔ order category lists (meals, meal sources,...) alphabetically @done(21-12-06 20:34) @project(MAIN TASKS.General/Framework) + ✔ add delay to auto conversions @done(21-12-06 20:25) @project(MAIN TASKS.General/Framework) + ✔ show daily Basal sum in overview @done(21-12-06 21:09) @project(MAIN TASKS.Basal/Bolus) + ✔ show KI and stuff for Bolus in overview @done(21-12-06 21:44) @project(MAIN TASKS.Basal/Bolus) + ✔ apply target color settings to glucose @done(21-12-06 22:57) @project(MAIN TASKS.Log Overview) + ✔ improve log meal list display @done(21-12-06 20:25) @project(MAIN TASKS.Log Entry) + ✔ change delayed bolus rate to percentage @done(21-12-06 20:47) @project(MAIN TASKS.Meal) + ✔ add meal source, carbs and portion size as subtitle in list @done(21-12-06 22:01) @project(MAIN TASKS.Meal) ✔ add option to hide warning dialogs on cancel, delete or event stop @done(21-12-05 19:18) @project(FUTURE TASKS.Settings) ✔ fix settings saving @done(21-12-05 19:08) @project(MAIN TASKS.Settings) ✔ add objectbox settings class and use instead of shared preferences @done(21-12-05 00:41) @project(MAIN TASKS.Settings) diff --git a/lib/models/basal.dart b/lib/models/basal.dart index 102d943..fd583fb 100644 --- a/lib/models/basal.dart +++ b/lib/models/basal.dart @@ -48,6 +48,25 @@ class Basal { return builder.build().find(); } + static double getDailyTotalForProfile(int id) { + double sum = 0.0; + + QueryBuilder builder = box.query(Basal_.deleted.equals(false)); + builder.link(Basal_.basalProfile, BasalProfile_.id.equals(id)); + + List basalRates = builder.build().find(); + for (Basal basal in basalRates) { + double rateDuration = + basal.endTime.difference(basal.startTime).inMinutes / 60; + if (rateDuration < 0) { + rateDuration += 24; + } + sum += basal.units * rateDuration; + } + + return sum; + } + @override String toString() { return DateTimeUtils.displayTime(startTime); diff --git a/lib/models/basal_profile.dart b/lib/models/basal_profile.dart index 7111d8a..8886025 100644 --- a/lib/models/basal_profile.dart +++ b/lib/models/basal_profile.dart @@ -28,7 +28,8 @@ class BasalProfile { static void put(BasalProfile basalProfile) => box.put(basalProfile); static List getAll() { - QueryBuilder all = box.query(BasalProfile_.deleted.equals(false)); + QueryBuilder all = box.query(BasalProfile_.deleted.equals(false)) + ..order(BasalProfile_.name); return all.build().find(); } diff --git a/lib/models/bolus_profile.dart b/lib/models/bolus_profile.dart index 11fc186..045cd9c 100644 --- a/lib/models/bolus_profile.dart +++ b/lib/models/bolus_profile.dart @@ -29,7 +29,7 @@ class BolusProfile { static List getAll() { QueryBuilder all = - box.query(BolusProfile_.deleted.equals(false)); + box.query(BolusProfile_.deleted.equals(false))..order(BolusProfile_.name); return all.build().find(); } diff --git a/lib/models/glucose_target.dart b/lib/models/glucose_target.dart new file mode 100644 index 0000000..41a4831 --- /dev/null +++ b/lib/models/glucose_target.dart @@ -0,0 +1,121 @@ +import 'package:diameter/main.dart'; +import 'package:diameter/models/settings.dart'; +import 'package:flutter/material.dart'; +import 'package:objectbox/objectbox.dart'; +import 'package:diameter/objectbox.g.dart' show GlucoseTarget_; + +@Entity(uid: 5041265995704044399) +class GlucoseTarget { + static final Box box = objectBox.store.box(); + + // properties + int id; + bool deleted; + int fromMgPerDL; + int toMgPerDl; + double fromMmolPerL; + double toMmolPerL; + int color; + + // constructor + GlucoseTarget({ + this.id = 0, + this.deleted = false, + required this.fromMgPerDL, + required this.toMgPerDl, + required this.fromMmolPerL, + required this.toMmolPerL, + required this.color, + }); + + // methods + static GlucoseTarget? get(int id) => box.get(id); + + // methods + static List getAll() { + if (box.getAll().isEmpty) { + reset(); + } + return box.getAll(); + } + + static Color getColorForGlucose({int mgPerDl = 0, double mmolPerL = 0}) { + if (box.getAll().isEmpty) { + reset(); + } + + Condition condition; + if (mgPerDl > 0 && + (mmolPerL == 0 || + Settings.glucoseMeasurement == GlucoseMeasurement.mgPerDl)) { + condition = GlucoseTarget_.fromMgPerDL.lessOrEqual(mgPerDl).and(GlucoseTarget_.toMgPerDl.greaterOrEqual(mgPerDl)); + } else if (mmolPerL > 0 && + (mgPerDl == 0 || + Settings.glucoseMeasurement == GlucoseMeasurement.mmolPerL)) { + condition = GlucoseTarget_.fromMmolPerL.lessOrEqual(mmolPerL).and(GlucoseTarget_.toMmolPerL.greaterOrEqual(mmolPerL)); + } else { + return Colors.black; + } + + List result = box + .query(GlucoseTarget_.deleted.equals(false) & condition) + .build() + .find(); + + if (result.length != 1) { + return Colors.black; + } + return Color(result.single.color); + } + + static void put(GlucoseTarget glucoseTarget) => box.put(glucoseTarget); + static void remove(int id) { + final item = box.get(id); + if (item != null) { + item.deleted = true; + box.put(item); + } + } + + static void reset() { + box.removeAll(); + List defaultTargets = [ + GlucoseTarget( + fromMgPerDL: 0, + toMgPerDl: 69, + fromMmolPerL: 0, + toMmolPerL: 3.83, + color: Colors.red.value, + ), + GlucoseTarget( + fromMgPerDL: 70, + toMgPerDl: 99, + fromMmolPerL: 3.84, + toMmolPerL: 5.48, + color: Colors.orange.value, + ), + GlucoseTarget( + fromMgPerDL: 100, + toMgPerDl: 140, + fromMmolPerL: 5.49, + toMmolPerL: 7.77, + color: Colors.green.value, + ), + GlucoseTarget( + fromMgPerDL: 141, + toMgPerDl: 240, + fromMmolPerL: 7.78, + toMmolPerL: 13.32, + color: Colors.orange.value, + ), + GlucoseTarget( + fromMgPerDL: 241, + toMgPerDl: 999, + fromMmolPerL: 13.33, + toMmolPerL: 55.44, + color: Colors.deepOrange.value, + ), + ]; + box.putMany(defaultTargets); + } +} diff --git a/lib/models/log_entry.dart b/lib/models/log_entry.dart index a9e6cbc..8127c9d 100644 --- a/lib/models/log_entry.dart +++ b/lib/models/log_entry.dart @@ -43,8 +43,8 @@ class LogEntry { static bool hasUncorrectedGlucose(int id) { final entry = box.get(id); - if (((entry?.mgPerDl ?? 0) > Settings.get().moderateGlucoseMgPerDl || - (entry?.mmolPerL ?? 0) > Settings.get().moderateGlucoseMmolPerL)) { + if (((entry?.mgPerDl ?? 0) > Settings.targetMgPerDl() || + (entry?.mmolPerL ?? 0) > Settings.targetMmolPerL())) { return !LogBolus.glucoseBolusForEntryExists(id); } return false; diff --git a/lib/models/meal.dart b/lib/models/meal.dart index bc21c3c..27c949b 100644 --- a/lib/models/meal.dart +++ b/lib/models/meal.dart @@ -3,6 +3,7 @@ import 'package:diameter/models/accuracy.dart'; import 'package:diameter/models/meal_category.dart'; import 'package:diameter/models/meal_portion_type.dart'; import 'package:diameter/models/meal_source.dart'; +import 'package:diameter/objectbox.g.dart' show Meal_; import 'package:objectbox/objectbox.dart'; enum PortionCarbsParameter { carbsRatio, portionSize, carbsPerPortion } @@ -19,7 +20,7 @@ class Meal { double? portionSize; double? carbsPerPortion; int? delayedBolusDuration; - double? delayedBolusRate; + double? delayedBolusPercentage; String? notes; // relations @@ -38,7 +39,7 @@ class Meal { this.portionSize, this.carbsPerPortion, this.delayedBolusDuration, - this.delayedBolusRate, + this.delayedBolusPercentage, this.notes, }); @@ -46,7 +47,10 @@ class Meal { static Meal? get(int id) => box.get(id); static void put(Meal meal) => box.put(meal); - static List getAll() => box.getAll(); + static List getAll() { + QueryBuilder builder = box.query(Meal_.deleted.equals(false))..order(Meal_.value); + return builder.build().find(); + } static void remove(int id) { final item = box.get(id); diff --git a/lib/models/meal_category.dart b/lib/models/meal_category.dart index 7d66978..67f56ca 100644 --- a/lib/models/meal_category.dart +++ b/lib/models/meal_category.dart @@ -1,5 +1,6 @@ import 'package:diameter/main.dart'; import 'package:objectbox/objectbox.dart'; +import 'package:diameter/objectbox.g.dart' show MealCategory_; @Entity(uid: 3158200688796904913) class MealCategory { @@ -23,7 +24,10 @@ class MealCategory { static MealCategory? get(int id) => box.get(id); static void put(MealCategory mealCategory) => box.put(mealCategory); - static List getAll() => box.getAll(); + static List getAll() { + QueryBuilder builder = box.query(MealCategory_.deleted.equals(false))..order(MealCategory_.value); + return builder.build().find(); + } static void remove(int id) { final item = box.get(id); diff --git a/lib/models/meal_portion_type.dart b/lib/models/meal_portion_type.dart index e027c1f..6e6a696 100644 --- a/lib/models/meal_portion_type.dart +++ b/lib/models/meal_portion_type.dart @@ -1,5 +1,6 @@ import 'package:diameter/main.dart'; import 'package:objectbox/objectbox.dart'; +import 'package:diameter/objectbox.g.dart' show MealPortionType_; @Entity(uid: 2111511899235985637) class MealPortionType { @@ -23,7 +24,10 @@ class MealPortionType { static MealPortionType? get(int id) => box.get(id); static void put(MealPortionType mealPortionType) => box.put(mealPortionType); - static List getAll() => box.getAll(); + static List getAll() { + QueryBuilder builder = box.query(MealPortionType_.deleted.equals(false))..order(MealPortionType_.value); + return builder.build().find(); + } static void remove(int id) { final item = box.get(id); diff --git a/lib/models/meal_source.dart b/lib/models/meal_source.dart index 0cc65cb..99e1706 100644 --- a/lib/models/meal_source.dart +++ b/lib/models/meal_source.dart @@ -3,6 +3,7 @@ import 'package:diameter/models/accuracy.dart'; import 'package:diameter/models/meal_category.dart'; import 'package:diameter/models/meal_portion_type.dart'; import 'package:objectbox/objectbox.dart'; +import 'package:diameter/objectbox.g.dart' show MealSource_; @Entity(uid: 1283034494527412242) class MealSource { @@ -32,7 +33,10 @@ class MealSource { static MealSource? get(int id) => box.get(id); static void put(MealSource mealSource) => box.put(mealSource); - static List getAll() => box.getAll(); + static List getAll() { + QueryBuilder builder = box.query(MealSource_.deleted.equals(false))..order(MealSource_.value); + return builder.build().find(); + } static void remove(int id) { final item = box.get(id); diff --git a/lib/models/settings.dart b/lib/models/settings.dart index 969cc25..cb0838a 100644 --- a/lib/models/settings.dart +++ b/lib/models/settings.dart @@ -57,12 +57,8 @@ class Settings { bool showConfirmationDialogOnDelete; bool showConfirmationDialogOnStopEvent; - int lowGlucoseMgPerDl; - int moderateGlucoseMgPerDl; - int highGlucoseMgPerDl; - double lowGlucoseMmolPerL; - double moderateGlucoseMmolPerL; - double highGlucoseMmolPerDl; + int targetGlucoseMgPerDl; + double targetGlucoseMmolPerL; // constructor Settings({ @@ -77,12 +73,8 @@ class Settings { this.showConfirmationDialogOnCancel = true, this.showConfirmationDialogOnDelete = true, this.showConfirmationDialogOnStopEvent = true, - this.lowGlucoseMgPerDl = 80, - this.moderateGlucoseMgPerDl = 140, - this.highGlucoseMgPerDl = 240, - this.lowGlucoseMmolPerL = 4.44, - this.moderateGlucoseMmolPerL = 7.77, - this.highGlucoseMmolPerDl = 13.32, + this.targetGlucoseMgPerDl = 100, + this.targetGlucoseMmolPerL = 5.49, }); // methods @@ -105,6 +97,9 @@ class Settings { static String get glucoseMeasurementSuffix => glucoseMeasurementSuffixes[get().glucoseMeasurementIndex]; + static int targetMgPerDl() => get().targetGlucoseMgPerDl; + static double targetMmolPerL() => get().targetGlucoseMmolPerL; + static void put(Settings settings) => box.put(settings); static void reset() { diff --git a/lib/objectbox-model.json b/lib/objectbox-model.json index def03cc..ba4f633 100644 --- a/lib/objectbox-model.json +++ b/lib/objectbox-model.json @@ -439,7 +439,7 @@ }, { "id": "10:382130101578692012", - "lastPropertyId": "14:3567196286623536415", + "lastPropertyId": "15:8283810711091063880", "name": "Meal", "properties": [ { @@ -473,11 +473,6 @@ "name": "delayedBolusDuration", "type": 6 }, - { - "id": "7:2172890064639236018", - "name": "delayedBolusRate", - "type": 8 - }, { "id": "8:6111684052388229887", "name": "notes", @@ -527,6 +522,11 @@ "id": "14:3567196286623536415", "name": "deleted", "type": 1 + }, + { + "id": "15:8283810711091063880", + "name": "delayedBolusPercentage", + "type": 8 } ], "relations": [] @@ -794,7 +794,7 @@ }, { "id": "16:3989341091218179227", - "lastPropertyId": "20:6560414475711071975", + "lastPropertyId": "22:3595473653451456068", "name": "Settings", "properties": [ { @@ -838,36 +838,6 @@ "name": "showConfirmationDialogOnStopEvent", "type": 1 }, - { - "id": "9:310032577683835406", - "name": "lowGlucoseMgPerDl", - "type": 6 - }, - { - "id": "10:596980591281311896", - "name": "moderateGlucoseMgPerDl", - "type": 6 - }, - { - "id": "11:5588897884422150510", - "name": "highGlucoseMgPerDl", - "type": 6 - }, - { - "id": "12:7638848982383620744", - "name": "lowGlucoseMmolPerL", - "type": 8 - }, - { - "id": "13:3633551763915044903", - "name": "moderateGlucoseMmolPerL", - "type": 8 - }, - { - "id": "14:3282706593658092097", - "name": "highGlucoseMmolPerDl", - "type": 8 - }, { "id": "18:1203593429961092769", "name": "nutritionMeasurementIndex", @@ -882,12 +852,66 @@ "id": "20:6560414475711071975", "name": "glucoseMeasurementIndex", "type": 6 + }, + { + "id": "21:7934134105044248002", + "name": "targetGlucoseMgPerDl", + "type": 6 + }, + { + "id": "22:3595473653451456068", + "name": "targetGlucoseMmolPerL", + "type": 8 + } + ], + "relations": [] + }, + { + "id": "17:5041265995704044399", + "lastPropertyId": "7:1333487551279074696", + "name": "GlucoseTarget", + "properties": [ + { + "id": "1:4322960567133959537", + "name": "id", + "type": 6, + "flags": 1 + }, + { + "id": "2:7533461804561299987", + "name": "deleted", + "type": 1 + }, + { + "id": "3:4949963248761074916", + "name": "fromMgPerDL", + "type": 6 + }, + { + "id": "4:8685380695305799464", + "name": "toMgPerDl", + "type": 6 + }, + { + "id": "5:2925449628924807050", + "name": "fromMmolPerL", + "type": 8 + }, + { + "id": "6:3244873743284485064", + "name": "toMmolPerL", + "type": 8 + }, + { + "id": "7:1333487551279074696", + "name": "color", + "type": 6 } ], "relations": [] } ], - "lastEntityId": "16:3989341091218179227", + "lastEntityId": "17:5041265995704044399", "lastIndexId": "28:4563029809754152081", "lastRelationId": "0:0", "lastSequenceId": "0:0", @@ -918,7 +942,14 @@ 8031421171668506924, 1614362036318874174, 1675040259141389754, - 7518219134349037920 + 7518219134349037920, + 2172890064639236018, + 310032577683835406, + 5588897884422150510, + 7638848982383620744, + 3282706593658092097, + 596980591281311896, + 3633551763915044903 ], "retiredRelationUids": [], "version": 1 diff --git a/lib/objectbox.g.dart b/lib/objectbox.g.dart index 507e5f7..42a1068 100644 --- a/lib/objectbox.g.dart +++ b/lib/objectbox.g.dart @@ -14,6 +14,7 @@ import 'models/basal.dart'; import 'models/basal_profile.dart'; import 'models/bolus.dart'; import 'models/bolus_profile.dart'; +import 'models/glucose_target.dart'; import 'models/log_bolus.dart'; import 'models/log_entry.dart'; import 'models/log_event.dart'; @@ -451,7 +452,7 @@ final _entities = [ ModelEntity( id: const IdUid(10, 382130101578692012), name: 'Meal', - lastPropertyId: const IdUid(14, 3567196286623536415), + lastPropertyId: const IdUid(15, 8283810711091063880), flags: 0, properties: [ ModelProperty( @@ -484,11 +485,6 @@ final _entities = [ name: 'delayedBolusDuration', type: 6, flags: 0), - ModelProperty( - id: const IdUid(7, 2172890064639236018), - name: 'delayedBolusRate', - type: 8, - flags: 0), ModelProperty( id: const IdUid(8, 6111684052388229887), name: 'notes', @@ -533,6 +529,11 @@ final _entities = [ id: const IdUid(14, 3567196286623536415), name: 'deleted', type: 1, + flags: 0), + ModelProperty( + id: const IdUid(15, 8283810711091063880), + name: 'delayedBolusPercentage', + type: 8, flags: 0) ], relations: [], @@ -794,7 +795,7 @@ final _entities = [ ModelEntity( id: const IdUid(16, 3989341091218179227), name: 'Settings', - lastPropertyId: const IdUid(20, 6560414475711071975), + lastPropertyId: const IdUid(22, 3595473653451456068), flags: 0, properties: [ ModelProperty( @@ -837,36 +838,6 @@ final _entities = [ name: 'showConfirmationDialogOnStopEvent', type: 1, flags: 0), - ModelProperty( - id: const IdUid(9, 310032577683835406), - name: 'lowGlucoseMgPerDl', - type: 6, - flags: 0), - ModelProperty( - id: const IdUid(10, 596980591281311896), - name: 'moderateGlucoseMgPerDl', - type: 6, - flags: 0), - ModelProperty( - id: const IdUid(11, 5588897884422150510), - name: 'highGlucoseMgPerDl', - type: 6, - flags: 0), - ModelProperty( - id: const IdUid(12, 7638848982383620744), - name: 'lowGlucoseMmolPerL', - type: 8, - flags: 0), - ModelProperty( - id: const IdUid(13, 3633551763915044903), - name: 'moderateGlucoseMmolPerL', - type: 8, - flags: 0), - ModelProperty( - id: const IdUid(14, 3282706593658092097), - name: 'highGlucoseMmolPerDl', - type: 8, - flags: 0), ModelProperty( id: const IdUid(18, 1203593429961092769), name: 'nutritionMeasurementIndex', @@ -881,6 +852,60 @@ final _entities = [ id: const IdUid(20, 6560414475711071975), name: 'glucoseMeasurementIndex', type: 6, + flags: 0), + ModelProperty( + id: const IdUid(21, 7934134105044248002), + name: 'targetGlucoseMgPerDl', + type: 6, + flags: 0), + ModelProperty( + id: const IdUid(22, 3595473653451456068), + name: 'targetGlucoseMmolPerL', + type: 8, + flags: 0) + ], + relations: [], + backlinks: []), + ModelEntity( + id: const IdUid(17, 5041265995704044399), + name: 'GlucoseTarget', + lastPropertyId: const IdUid(7, 1333487551279074696), + flags: 0, + properties: [ + ModelProperty( + id: const IdUid(1, 4322960567133959537), + name: 'id', + type: 6, + flags: 1), + ModelProperty( + id: const IdUid(2, 7533461804561299987), + name: 'deleted', + type: 1, + flags: 0), + ModelProperty( + id: const IdUid(3, 4949963248761074916), + name: 'fromMgPerDL', + type: 6, + flags: 0), + ModelProperty( + id: const IdUid(4, 8685380695305799464), + name: 'toMgPerDl', + type: 6, + flags: 0), + ModelProperty( + id: const IdUid(5, 2925449628924807050), + name: 'fromMmolPerL', + type: 8, + flags: 0), + ModelProperty( + id: const IdUid(6, 3244873743284485064), + name: 'toMmolPerL', + type: 8, + flags: 0), + ModelProperty( + id: const IdUid(7, 1333487551279074696), + name: 'color', + type: 6, flags: 0) ], relations: [], @@ -907,7 +932,7 @@ Future openStore( ModelDefinition getObjectBoxModel() { final model = ModelInfo( entities: _entities, - lastEntityId: const IdUid(16, 3989341091218179227), + lastEntityId: const IdUid(17, 5041265995704044399), lastIndexId: const IdUid(28, 4563029809754152081), lastRelationId: const IdUid(0, 0), lastSequenceId: const IdUid(0, 0), @@ -931,7 +956,14 @@ ModelDefinition getObjectBoxModel() { 8031421171668506924, 1614362036318874174, 1675040259141389754, - 7518219134349037920 + 7518219134349037920, + 2172890064639236018, + 310032577683835406, + 5588897884422150510, + 7638848982383620744, + 3282706593658092097, + 596980591281311896, + 3633551763915044903 ], retiredRelationUids: const [], modelVersion: 5, @@ -1346,14 +1378,13 @@ ModelDefinition getObjectBoxModel() { final valueOffset = fbb.writeString(object.value); final notesOffset = object.notes == null ? null : fbb.writeString(object.notes!); - fbb.startTable(15); + fbb.startTable(16); fbb.addInt64(0, object.id); fbb.addOffset(1, valueOffset); fbb.addFloat64(2, object.carbsRatio); fbb.addFloat64(3, object.portionSize); fbb.addFloat64(4, object.carbsPerPortion); fbb.addInt64(5, object.delayedBolusDuration); - fbb.addFloat64(6, object.delayedBolusRate); fbb.addOffset(7, notesOffset); fbb.addInt64(8, object.mealSource.targetId); fbb.addInt64(9, object.mealCategory.targetId); @@ -1361,6 +1392,7 @@ ModelDefinition getObjectBoxModel() { fbb.addInt64(11, object.portionSizeAccuracy.targetId); fbb.addInt64(12, object.carbsRatioAccuracy.targetId); fbb.addBool(13, object.deleted); + fbb.addFloat64(14, object.delayedBolusPercentage); fbb.finish(fbb.endTable()); return object.id; }, @@ -1382,8 +1414,8 @@ ModelDefinition getObjectBoxModel() { .vTableGetNullable(buffer, rootOffset, 12), delayedBolusDuration: const fb.Int64Reader() .vTableGetNullable(buffer, rootOffset, 14), - delayedBolusRate: const fb.Float64Reader() - .vTableGetNullable(buffer, rootOffset, 16), + delayedBolusPercentage: const fb.Float64Reader() + .vTableGetNullable(buffer, rootOffset, 32), notes: const fb.StringReader() .vTableGetNullable(buffer, rootOffset, 18)); object.mealSource.targetId = @@ -1662,7 +1694,7 @@ ModelDefinition getObjectBoxModel() { final longTimeFormatOffset = object.longTimeFormat == null ? null : fbb.writeString(object.longTimeFormat!); - fbb.startTable(21); + fbb.startTable(23); fbb.addInt64(0, object.id); fbb.addOffset(1, dateFormatOffset); fbb.addOffset(2, longDateFormatOffset); @@ -1671,15 +1703,11 @@ ModelDefinition getObjectBoxModel() { fbb.addBool(5, object.showConfirmationDialogOnCancel); fbb.addBool(6, object.showConfirmationDialogOnDelete); fbb.addBool(7, object.showConfirmationDialogOnStopEvent); - fbb.addInt64(8, object.lowGlucoseMgPerDl); - fbb.addInt64(9, object.moderateGlucoseMgPerDl); - fbb.addInt64(10, object.highGlucoseMgPerDl); - fbb.addFloat64(11, object.lowGlucoseMmolPerL); - fbb.addFloat64(12, object.moderateGlucoseMmolPerL); - fbb.addFloat64(13, object.highGlucoseMmolPerDl); fbb.addInt64(17, object.nutritionMeasurementIndex); fbb.addInt64(18, object.glucoseDisplayModeIndex); fbb.addInt64(19, object.glucoseMeasurementIndex); + fbb.addInt64(20, object.targetGlucoseMgPerDl); + fbb.addFloat64(21, object.targetGlucoseMmolPerL); fbb.finish(fbb.endTable()); return object.id; }, @@ -1709,14 +1737,51 @@ ModelDefinition getObjectBoxModel() { .vTableGet(buffer, rootOffset, 16, false), showConfirmationDialogOnStopEvent: const fb.BoolReader() .vTableGet(buffer, rootOffset, 18, false), - lowGlucoseMgPerDl: - const fb.Int64Reader().vTableGet(buffer, rootOffset, 20, 0), - moderateGlucoseMgPerDl: - const fb.Int64Reader().vTableGet(buffer, rootOffset, 22, 0), - highGlucoseMgPerDl: const fb.Int64Reader().vTableGet(buffer, rootOffset, 24, 0), - lowGlucoseMmolPerL: const fb.Float64Reader().vTableGet(buffer, rootOffset, 26, 0), - moderateGlucoseMmolPerL: const fb.Float64Reader().vTableGet(buffer, rootOffset, 28, 0), - highGlucoseMmolPerDl: const fb.Float64Reader().vTableGet(buffer, rootOffset, 30, 0)); + targetGlucoseMgPerDl: + const fb.Int64Reader().vTableGet(buffer, rootOffset, 44, 0), + targetGlucoseMmolPerL: + const fb.Float64Reader().vTableGet(buffer, rootOffset, 46, 0)); + + return object; + }), + GlucoseTarget: EntityDefinition( + model: _entities[15], + toOneRelations: (GlucoseTarget object) => [], + toManyRelations: (GlucoseTarget object) => {}, + getId: (GlucoseTarget object) => object.id, + setId: (GlucoseTarget object, int id) { + object.id = id; + }, + objectToFB: (GlucoseTarget object, fb.Builder fbb) { + fbb.startTable(8); + fbb.addInt64(0, object.id); + fbb.addBool(1, object.deleted); + fbb.addInt64(2, object.fromMgPerDL); + fbb.addInt64(3, object.toMgPerDl); + fbb.addFloat64(4, object.fromMmolPerL); + fbb.addFloat64(5, object.toMmolPerL); + fbb.addInt64(6, object.color); + fbb.finish(fbb.endTable()); + return object.id; + }, + objectFromFB: (Store store, ByteData fbData) { + final buffer = fb.BufferContext(fbData); + final rootOffset = buffer.derefObject(0); + + final object = GlucoseTarget( + id: const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0), + deleted: + const fb.BoolReader().vTableGet(buffer, rootOffset, 6, false), + fromMgPerDL: + const fb.Int64Reader().vTableGet(buffer, rootOffset, 8, 0), + toMgPerDl: + const fb.Int64Reader().vTableGet(buffer, rootOffset, 10, 0), + fromMmolPerL: + const fb.Float64Reader().vTableGet(buffer, rootOffset, 12, 0), + toMmolPerL: + const fb.Float64Reader().vTableGet(buffer, rootOffset, 14, 0), + color: + const fb.Int64Reader().vTableGet(buffer, rootOffset, 16, 0)); return object; }) @@ -2018,36 +2083,36 @@ class Meal_ { static final delayedBolusDuration = QueryIntegerProperty(_entities[8].properties[5]); - /// see [Meal.delayedBolusRate] - static final delayedBolusRate = - QueryDoubleProperty(_entities[8].properties[6]); - /// see [Meal.notes] - static final notes = QueryStringProperty(_entities[8].properties[7]); + static final notes = QueryStringProperty(_entities[8].properties[6]); /// see [Meal.mealSource] static final mealSource = - QueryRelationToOne(_entities[8].properties[8]); + QueryRelationToOne(_entities[8].properties[7]); /// see [Meal.mealCategory] static final mealCategory = - QueryRelationToOne(_entities[8].properties[9]); + QueryRelationToOne(_entities[8].properties[8]); /// see [Meal.mealPortionType] static final mealPortionType = - QueryRelationToOne(_entities[8].properties[10]); + QueryRelationToOne(_entities[8].properties[9]); /// see [Meal.portionSizeAccuracy] static final portionSizeAccuracy = - QueryRelationToOne(_entities[8].properties[11]); + QueryRelationToOne(_entities[8].properties[10]); /// see [Meal.carbsRatioAccuracy] static final carbsRatioAccuracy = - QueryRelationToOne(_entities[8].properties[12]); + QueryRelationToOne(_entities[8].properties[11]); /// see [Meal.deleted] static final deleted = - QueryBooleanProperty(_entities[8].properties[13]); + QueryBooleanProperty(_entities[8].properties[12]); + + /// see [Meal.delayedBolusPercentage] + static final delayedBolusPercentage = + QueryDoubleProperty(_entities[8].properties[13]); } /// [MealCategory] entity fields to define ObjectBox queries. @@ -2253,39 +2318,54 @@ class Settings_ { static final showConfirmationDialogOnStopEvent = QueryBooleanProperty(_entities[14].properties[7]); - /// see [Settings.lowGlucoseMgPerDl] - static final lowGlucoseMgPerDl = - QueryIntegerProperty(_entities[14].properties[8]); - - /// see [Settings.moderateGlucoseMgPerDl] - static final moderateGlucoseMgPerDl = - QueryIntegerProperty(_entities[14].properties[9]); - - /// see [Settings.highGlucoseMgPerDl] - static final highGlucoseMgPerDl = - QueryIntegerProperty(_entities[14].properties[10]); - - /// see [Settings.lowGlucoseMmolPerL] - static final lowGlucoseMmolPerL = - QueryDoubleProperty(_entities[14].properties[11]); - - /// see [Settings.moderateGlucoseMmolPerL] - static final moderateGlucoseMmolPerL = - QueryDoubleProperty(_entities[14].properties[12]); - - /// see [Settings.highGlucoseMmolPerDl] - static final highGlucoseMmolPerDl = - QueryDoubleProperty(_entities[14].properties[13]); - /// see [Settings.nutritionMeasurementIndex] static final nutritionMeasurementIndex = - QueryIntegerProperty(_entities[14].properties[14]); + QueryIntegerProperty(_entities[14].properties[8]); /// see [Settings.glucoseDisplayModeIndex] static final glucoseDisplayModeIndex = - QueryIntegerProperty(_entities[14].properties[15]); + QueryIntegerProperty(_entities[14].properties[9]); /// see [Settings.glucoseMeasurementIndex] static final glucoseMeasurementIndex = - QueryIntegerProperty(_entities[14].properties[16]); + QueryIntegerProperty(_entities[14].properties[10]); + + /// see [Settings.targetGlucoseMgPerDl] + static final targetGlucoseMgPerDl = + QueryIntegerProperty(_entities[14].properties[11]); + + /// see [Settings.targetGlucoseMmolPerL] + static final targetGlucoseMmolPerL = + QueryDoubleProperty(_entities[14].properties[12]); +} + +/// [GlucoseTarget] entity fields to define ObjectBox queries. +class GlucoseTarget_ { + /// see [GlucoseTarget.id] + static final id = + QueryIntegerProperty(_entities[15].properties[0]); + + /// see [GlucoseTarget.deleted] + static final deleted = + QueryBooleanProperty(_entities[15].properties[1]); + + /// see [GlucoseTarget.fromMgPerDL] + static final fromMgPerDL = + QueryIntegerProperty(_entities[15].properties[2]); + + /// see [GlucoseTarget.toMgPerDl] + static final toMgPerDl = + QueryIntegerProperty(_entities[15].properties[3]); + + /// see [GlucoseTarget.fromMmolPerL] + static final fromMmolPerL = + QueryDoubleProperty(_entities[15].properties[4]); + + /// see [GlucoseTarget.toMmolPerL] + static final toMmolPerL = + QueryDoubleProperty(_entities[15].properties[5]); + + /// see [GlucoseTarget.color] + static final color = + QueryIntegerProperty(_entities[15].properties[6]); } diff --git a/lib/screens/basal/basal_list.dart b/lib/screens/basal/basal_list.dart index da88c6c..f59394f 100644 --- a/lib/screens/basal/basal_list.dart +++ b/lib/screens/basal/basal_list.dart @@ -108,51 +108,45 @@ class _BasalListScreenState extends State { @override Widget build(BuildContext context) { - return widget.basalRates.isNotEmpty ? SingleChildScrollView( - child: Column( - children: [ - ListView.builder( - shrinkWrap: true, - itemCount: widget.basalRates.length, - itemBuilder: (context, index) { - final basal = widget.basalRates[index]; - final error = validateTimePeriod(index); - return ListTile( - tileColor: error != null ? Colors.red.shade100 : null, - onTap: () { - handleEditAction(basal); - }, - title: Row( - mainAxisSize: MainAxisSize.max, - children: [ - Expanded( - child: Text( - '${DateTimeUtils.displayTime(basal.startTime)} - ${DateTimeUtils.displayTime(basal.endTime)}')), - const Spacer(), - Expanded(child: Text('${basal.units} U')), - ], - ), - subtitle: error != null - ? Text(error, style: const TextStyle(color: Colors.red)) - : Container(), - trailing: Row( - mainAxisSize: MainAxisSize.min, - children: [ - IconButton( - icon: const Icon( - Icons.delete, - color: Colors.blue, - ), - onPressed: () => handleDeleteAction(basal), - ), - ], - ), - ); - }, + return widget.basalRates.isNotEmpty ? ListView.builder( + shrinkWrap: true, + itemCount: widget.basalRates.length, + itemBuilder: (context, index) { + final basal = widget.basalRates[index]; + final error = validateTimePeriod(index); + return ListTile( + tileColor: error != null ? Colors.red.shade100 : null, + onTap: () { + handleEditAction(basal); + }, + title: Row( + mainAxisSize: MainAxisSize.max, + children: [ + Expanded( + child: Text( + '${DateTimeUtils.displayTime(basal.startTime)} - ${DateTimeUtils.displayTime(basal.endTime)}')), + const Spacer(), + Expanded(child: Text('${basal.units} U')), + ], ), - ], - ), - ): const Center( + subtitle: error != null + ? Text(error, style: const TextStyle(color: Colors.red)) + : Container(), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + icon: const Icon( + Icons.delete, + color: Colors.blue, + ), + onPressed: () => handleDeleteAction(basal), + ), + ], + ), + ); + }, + ) : const Center( child: Text('You have not created any Basal Rates yet!'), ); } diff --git a/lib/screens/basal/basal_profile_list.dart b/lib/screens/basal/basal_profile_list.dart index fc341e0..8c9de13 100644 --- a/lib/screens/basal/basal_profile_list.dart +++ b/lib/screens/basal/basal_profile_list.dart @@ -1,5 +1,6 @@ import 'package:diameter/components/dialogs.dart'; import 'package:diameter/components/dropdown.dart'; +import 'package:diameter/models/basal.dart'; import 'package:diameter/models/settings.dart'; import 'package:diameter/navigation.dart'; import 'package:flutter/material.dart'; @@ -167,19 +168,38 @@ class _BasalProfileListScreenState extends State { itemCount: _basalProfiles.length, itemBuilder: (context, index) { final basalProfile = _basalProfiles[index]; + double dailyTotal = + Basal.getDailyTotalForProfile(basalProfile.id); String activeProfileText = basalProfile.active ? 'Default Profile' : basalProfile.id == _activeProfile?.id ? 'Current Active Profile' : ''; - if (activeProfileText != '' && (basalProfile.notes ?? '') != '') { + if (activeProfileText != '' && + (basalProfile.notes ?? '') != '') { activeProfileText += '\n'; } return ListTile( - selected: basalProfile.active || basalProfile.id == _activeProfile?.id, + selected: basalProfile.active || + basalProfile.id == _activeProfile?.id, onTap: () => onEdit(basalProfile), title: Text(basalProfile.name), - subtitle: Text('$activeProfileText${basalProfile.notes ?? ''}'), + subtitle: Row( + children: [ + Text( + '$activeProfileText${basalProfile.notes ?? ''}'), + Expanded( + child: Column( + children: dailyTotal > 0 + ? [ + Text(dailyTotal.toStringAsPrecision(3)), + const Text('U/day', textScaleFactor: 0.75), + ] + : [], + ), + ), + ], + ), trailing: Row( mainAxisSize: MainAxisSize.min, children: [ diff --git a/lib/screens/bolus/bolus_detail.dart b/lib/screens/bolus/bolus_detail.dart index 482f7a1..ceaeca9 100644 --- a/lib/screens/bolus/bolus_detail.dart +++ b/lib/screens/bolus/bolus_detail.dart @@ -321,10 +321,12 @@ class _BolusDetailScreenState extends State { suffixText: 'mg/dl', ), controller: _mgPerDlController, - onChanged: (_) => - convertBetweenMgPerDlAndMmolPerL( + onChanged: (_) async { + await Future.delayed(const Duration(seconds: 1)); + convertBetweenMgPerDlAndMmolPerL( calculateFrom: - GlucoseMeasurement.mgPerDl), + GlucoseMeasurement.mgPerDl); + }, keyboardType: const TextInputType.numberWithOptions(), validator: (value) { @@ -355,10 +357,12 @@ class _BolusDetailScreenState extends State { suffixText: 'mmol/l', ), controller: _mmolPerLController, - onChanged: (_) => - convertBetweenMgPerDlAndMmolPerL( + onChanged: (_) async { + await Future.delayed(const Duration(seconds: 1)); + convertBetweenMgPerDlAndMmolPerL( calculateFrom: - GlucoseMeasurement.mmolPerL), + GlucoseMeasurement.mmolPerL); + }, keyboardType: const TextInputType.numberWithOptions( decimal: true), diff --git a/lib/screens/bolus/bolus_list.dart b/lib/screens/bolus/bolus_list.dart index c9c27fc..583f7c2 100644 --- a/lib/screens/bolus/bolus_list.dart +++ b/lib/screens/bolus/bolus_list.dart @@ -104,52 +104,80 @@ class _BolusListScreenState extends State { @override Widget build(BuildContext context) { - return widget.bolusRates.isNotEmpty ? SingleChildScrollView( - padding: const EdgeInsets.only(top: 10.0), - child: Column( - children: [ - ListView.builder( - shrinkWrap: true, - itemCount: widget.bolusRates.length, - itemBuilder: (context, index) { - final bolus = widget.bolusRates[index]; - final error = validateTimePeriod(index); - return ListTile( - isThreeLine: true, - tileColor: error != null ? Colors.red.shade100 : null, - onTap: () { - handleEditAction(bolus); - }, - title: Text( - '${DateTimeUtils.displayTime(bolus.startTime)} - ${DateTimeUtils.displayTime(bolus.endTime)}'), - subtitle: Column( - mainAxisSize: MainAxisSize.max, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - '${bolus.units} U per ${bolus.carbs}${Settings.nutritionMeasurementSuffix} carbs/${Settings.glucoseMeasurement == GlucoseMeasurement.mgPerDl ? bolus.mgPerDl : bolus.mmolPerL} ${Settings.glucoseMeasurementSuffix}'), - error != null - ? Text(error, - style: const TextStyle(color: Colors.red)) - : const Text('') - ]), - trailing: Row( - mainAxisSize: MainAxisSize.min, + return widget.bolusRates.isNotEmpty ? ListView.builder( + shrinkWrap: true, + itemCount: widget.bolusRates.length, + itemBuilder: (context, index) { + final bolus = widget.bolusRates[index]; + final error = validateTimePeriod(index); + return ListTile( + isThreeLine: true, + tileColor: error != null ? Colors.red.shade100 : null, + onTap: () { + handleEditAction(bolus); + }, + title: Text( + '${DateTimeUtils.displayTime(bolus.startTime)} - ${DateTimeUtils.displayTime(bolus.endTime)}'), + subtitle: Column( + children: [ + error != null + ? Text(error, + style: const TextStyle(color: Colors.red)) + : const Text(''), + Row( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.start, children: [ - IconButton( - icon: const Icon( - Icons.delete, - color: Colors.blue, + Expanded( + child: Column( + children: (bolus.units > 0 && bolus.carbs > 0) + ? [ + Text((bolus.carbs / bolus.units).toStringAsPrecision(2)), + const Text('carbs per U', + textScaleFactor: 0.75), + ] + : [], ), - onPressed: () => handleDeleteAction(bolus), ), - ], - ), - ); - }, + Expanded( + child: Column( + children: (bolus.units > 0 && bolus.carbs > 0) + ? [ + Text((bolus.units / bolus.carbs * 12).toStringAsPrecision(2)), + const Text('U per bread unit', + textScaleFactor: 0.75), + ] + : [], + ), + ), + Expanded( + child: Column( + children: (bolus.units > 0 && (bolus.mgPerDl ?? bolus.mmolPerL ?? 0) > 0) + ? [ + Text((((Settings.glucoseMeasurement == GlucoseMeasurement.mgPerDl ? bolus.mgPerDl : bolus.mmolPerL)! / bolus.units)).toString()), + Text('${Settings.glucoseMeasurementSuffix} per unit', + textScaleFactor: 0.75), + ] + : [], + ), + ), + ]), + ], ), - ], - ), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + icon: const Icon( + Icons.delete, + color: Colors.blue, + ), + onPressed: () => handleDeleteAction(bolus), + ), + ], + ), + ); + }, ) : const Center( child: Text('You have not created any Bolus Rates yet!'), ); diff --git a/lib/screens/log/log.dart b/lib/screens/log/log.dart index 6979a51..6041fe2 100644 --- a/lib/screens/log/log.dart +++ b/lib/screens/log/log.dart @@ -1,4 +1,5 @@ import 'package:diameter/components/dialogs.dart'; +import 'package:diameter/models/glucose_target.dart'; import 'package:diameter/models/log_bolus.dart'; import 'package:diameter/models/log_entry.dart'; import 'package:diameter/models/log_meal.dart'; @@ -87,6 +88,10 @@ class _LogScreenState extends State { LogBolus.getTotalBolusForEntry(logEntry.id); double carbs = LogMeal.getTotalCarbsForEntry(logEntry.id); + TextStyle glucoseStyle = TextStyle( + color: GlucoseTarget.getColorForGlucose( + mgPerDl: logEntry.mgPerDl ?? 0, + mmolPerL: logEntry.mmolPerL ?? 0)); tiles.add(ListTile( onTap: () { Navigator.push( @@ -117,9 +122,11 @@ class _LogScreenState extends State { GlucoseDisplayMode .bothForList) ? [ - Text(logEntry.mgPerDl.toString()), - const Text( + Text(logEntry.mgPerDl.toString(), + style: glucoseStyle), + Text( 'mg/dl', + style: glucoseStyle, textScaleFactor: 0.75, ), ] @@ -137,9 +144,10 @@ class _LogScreenState extends State { GlucoseDisplayMode .bothForList) ? [ - Text(logEntry.mmolPerL.toString()), - const Text( + Text(logEntry.mmolPerL.toString(), style: glucoseStyle), + Text( 'mmol/l', + style: glucoseStyle, textScaleFactor: 0.75, ), ] @@ -163,8 +171,8 @@ class _LogScreenState extends State { ? [ Text(carbs.toStringAsPrecision(3)), Text( - Settings.nutritionMeasurementSuffix, - textScaleFactor: 0.75), + '${Settings.nutritionMeasurementSuffix} carbs', + textScaleFactor: 0.75), ] : [], ), @@ -182,13 +190,14 @@ class _LogScreenState extends State { ], ), )); - } return ListBody( - children: [Text(DateTimeUtils.displayDate(date))] + tiles, - ); + children: [ + Text(DateTimeUtils.displayDate(date)) + ] + + tiles, + ); }, - ) : const Center( child: Text('You have not created any Log Entries yet!'), diff --git a/lib/screens/log/log_entry/log_bolus_detail.dart b/lib/screens/log/log_entry/log_bolus_detail.dart index 8d2b187..1e505ad 100644 --- a/lib/screens/log/log_entry/log_bolus_detail.dart +++ b/lib/screens/log/log_entry/log_bolus_detail.dart @@ -96,7 +96,7 @@ class _LogBolusDetailScreenState extends State { : 0)) .toString(); _mgPerDlTargetController.text = - (_logBolus?.mgPerDlTarget ?? Settings.get().moderateGlucoseMgPerDl).toString(); + (_logBolus?.mgPerDlTarget ?? Settings.targetMgPerDl()).toString(); _mgPerDlCorrectionController.text = (_logBolus?.mgPerDlCorrection ?? max( (int.tryParse(_mgPerDlCurrentController.text) ?? 0) - @@ -109,7 +109,7 @@ class _LogBolusDetailScreenState extends State { : 0)) .toString(); _mmolPerLTargetController.text = - (_logBolus?.mmolPerLTarget ?? Settings.get().moderateGlucoseMmolPerL).toString(); + (_logBolus?.mmolPerLTarget ?? Settings.targetMmolPerL()).toString(); _mmolPerLCorrectionController.text = (_logBolus?.mmolPerLCorrection ?? max( (double.tryParse(_mmolPerLCurrentController.text) ?? 0) - @@ -349,9 +349,9 @@ class _LogBolusDetailScreenState extends State { _mmolPerLCurrentController.text != (_logEntry?.mmolPerL.toString() ?? ''))) || _mgPerDlTargetController.text != - Settings.get().moderateGlucoseMgPerDl.toString() || + Settings.targetMgPerDl().toString() || _mmolPerLTargetController.text != - Settings.get().moderateGlucoseMmolPerL.toString() || + Settings.targetMmolPerL().toString() || _delayController.text != '' || _setManually || _notesController.text != '')) || @@ -475,9 +475,12 @@ class _LogBolusDetailScreenState extends State { suffixText: 'mg/dl', ), controller: _mgPerDlCurrentController, - onChanged: (_) => onChangeGlucose( + onChanged: (_) async { + await Future.delayed(const Duration(seconds: 1)); + onChangeGlucose( calculateFrom: - GlucoseMeasurement.mgPerDl), + GlucoseMeasurement.mgPerDl); + }, keyboardType: const TextInputType .numberWithOptions(), ), @@ -493,9 +496,12 @@ class _LogBolusDetailScreenState extends State { suffixText: 'mg/dl', ), controller: _mgPerDlTargetController, - onChanged: (_) => onChangeGlucose( + onChanged: (_) async { + await Future.delayed(const Duration(seconds: 1)); + onChangeGlucose( calculateFrom: - GlucoseMeasurement.mgPerDl), + GlucoseMeasurement.mgPerDl); + }, keyboardType: const TextInputType .numberWithOptions(), ), @@ -543,9 +549,12 @@ class _LogBolusDetailScreenState extends State { ), controller: _mmolPerLCurrentController, - onChanged: (_) => onChangeGlucose( + onChanged: (_) async { + await Future.delayed(const Duration(seconds: 1)); + onChangeGlucose( calculateFrom: - GlucoseMeasurement.mmolPerL), + GlucoseMeasurement.mmolPerL); + }, keyboardType: const TextInputType .numberWithOptions(), ), @@ -561,9 +570,12 @@ class _LogBolusDetailScreenState extends State { suffixText: 'mmol/l', ), controller: _mmolPerLTargetController, - onChanged: (_) => onChangeGlucose( + onChanged: (_) async { + await Future.delayed(const Duration(seconds: 1)); + onChangeGlucose( calculateFrom: - GlucoseMeasurement.mmolPerL), + GlucoseMeasurement.mmolPerL); + }, keyboardType: const TextInputType .numberWithOptions(), ), diff --git a/lib/screens/log/log_entry/log_bolus_list.dart b/lib/screens/log/log_entry/log_bolus_list.dart index d23b480..36f3311 100644 --- a/lib/screens/log/log_entry/log_bolus_list.dart +++ b/lib/screens/log/log_entry/log_bolus_list.dart @@ -82,53 +82,47 @@ class _LogBolusListScreenState extends State { @override Widget build(BuildContext context) { - return Column( - children: [ - Expanded( - child: widget.logBoli.isNotEmpty - ? ListView.builder( - shrinkWrap: true, - itemCount: widget.logBoli.length, - itemBuilder: (context, index) { - final bolus = widget.logBoli[index]; - String titleText = '${bolus.units} U ${(bolus.delay ?? 0) != 0 - ? ' (delayed by ${bolus.delay} min)' - : ''}'; - return ListTile( - onTap: () => handleEditAction(bolus), - title: Text(titleText), - subtitle: Text(bolus.carbs != null ? - 'for ${bolus.meal.target.toString()} (${bolus.carbs}${Settings.nutritionMeasurementSuffix} carbs)' - : 'to correct ${Settings.glucoseMeasurement == GlucoseMeasurement.mgPerDl ? bolus.mgPerDlCorrection : bolus.mmolPerLCorrection} ${Settings.glucoseMeasurementSuffix}'), - trailing: Row( - mainAxisSize: MainAxisSize.min, - children: [ - bolus.meal.target != null - ? IconButton( - icon: const Icon(Icons.restaurant), - onPressed: () => - handleEditMealAction(bolus.meal.targetId), - ) - : Container(), - const SizedBox(width: 24), - IconButton( - icon: const Icon( - Icons.delete, - color: Colors.blue, - ), - onPressed: () => handleDeleteAction(bolus), - ), - ], + return widget.logBoli.isNotEmpty + ? ListView.builder( + shrinkWrap: true, + itemCount: widget.logBoli.length, + itemBuilder: (context, index) { + final bolus = widget.logBoli[index]; + String titleText = '${bolus.units} U ${(bolus.delay ?? 0) != 0 + ? ' (delayed by ${bolus.delay} min)' + : ''}'; + return ListTile( + onTap: () => handleEditAction(bolus), + title: Text(titleText), + subtitle: Text(bolus.carbs != null ? + 'for ${bolus.meal.target.toString()} (${bolus.carbs}${Settings.nutritionMeasurementSuffix} carbs)' + : 'to correct ${Settings.glucoseMeasurement == GlucoseMeasurement.mgPerDl ? bolus.mgPerDlCorrection : bolus.mmolPerLCorrection} ${Settings.glucoseMeasurementSuffix}'), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + bolus.meal.target != null + ? IconButton( + icon: const Icon(Icons.restaurant), + onPressed: () => + handleEditMealAction(bolus.meal.targetId), + ) + : Container(), + const SizedBox(width: 24), + IconButton( + icon: const Icon( + Icons.delete, + color: Colors.blue, ), - ); - }, - ) - : const Center( - child: Text( - 'You have not added any Boli to this Log Entry yet!'), + onPressed: () => handleDeleteAction(bolus), + ), + ], ), - ), - ], - ); + ); + }, + ) + : const Center( + child: Text( + 'You have not added any Boli to this Log Entry yet!'), + ); } } diff --git a/lib/screens/log/log_entry/log_entry.dart b/lib/screens/log/log_entry/log_entry.dart index 9f07b91..9b0e63d 100644 --- a/lib/screens/log/log_entry/log_entry.dart +++ b/lib/screens/log/log_entry/log_entry.dart @@ -310,8 +310,12 @@ class _LogEntryScreenState extends State { ), Row( children: [ - Settings.glucoseMeasurement == GlucoseMeasurement.mgPerDl || - [GlucoseDisplayMode.both, GlucoseDisplayMode.bothForDetail].contains(Settings.glucoseDisplayMode) + Settings.glucoseMeasurement == + GlucoseMeasurement.mgPerDl || + [ + GlucoseDisplayMode.both, + GlucoseDisplayMode.bothForDetail + ].contains(Settings.glucoseDisplayMode) ? Expanded( child: TextFormField( decoration: const InputDecoration( @@ -319,10 +323,13 @@ class _LogEntryScreenState extends State { suffixText: 'mg/dl', ), controller: _mgPerDlController, - onChanged: (_) => - convertBetweenMgPerDlAndMmolPerL( - calculateFrom: - GlucoseMeasurement.mgPerDl), + onChanged: (_) async { + await Future.delayed( + const Duration(seconds: 1)); + convertBetweenMgPerDlAndMmolPerL( + calculateFrom: + GlucoseMeasurement.mgPerDl); + }, keyboardType: const TextInputType.numberWithOptions(), validator: (value) { @@ -337,7 +344,8 @@ class _LogEntryScreenState extends State { ), ) : Container(), - Settings.glucoseDisplayMode == GlucoseDisplayMode.both || + Settings.glucoseDisplayMode == + GlucoseDisplayMode.both || Settings.glucoseDisplayMode == GlucoseDisplayMode.bothForDetail ? IconButton( @@ -348,7 +356,8 @@ class _LogEntryScreenState extends State { icon: const Icon(Icons.calculate), ) : Container(), - Settings.glucoseMeasurement == GlucoseMeasurement.mmolPerL || + Settings.glucoseMeasurement == + GlucoseMeasurement.mmolPerL || Settings.glucoseDisplayMode == GlucoseDisplayMode.both || Settings.glucoseDisplayMode == @@ -360,10 +369,13 @@ class _LogEntryScreenState extends State { suffixText: 'mmol/l', ), controller: _mmolPerLController, - onChanged: (_) => - convertBetweenMgPerDlAndMmolPerL( - calculateFrom: - GlucoseMeasurement.mmolPerL), + onChanged: (_) async { + await Future.delayed( + const Duration(seconds: 1)); + convertBetweenMgPerDlAndMmolPerL( + calculateFrom: + GlucoseMeasurement.mmolPerL); + }, keyboardType: const TextInputType.numberWithOptions( decimal: true), @@ -379,7 +391,8 @@ class _LogEntryScreenState extends State { ), ) : Container(), - Settings.glucoseDisplayMode == GlucoseDisplayMode.both || + Settings.glucoseDisplayMode == + GlucoseDisplayMode.both || Settings.glucoseDisplayMode == GlucoseDisplayMode.bothForDetail ? IconButton( diff --git a/lib/screens/log/log_entry/log_meal_detail.dart b/lib/screens/log/log_entry/log_meal_detail.dart index 6e037b4..c023145 100644 --- a/lib/screens/log/log_entry/log_meal_detail.dart +++ b/lib/screens/log/log_entry/log_meal_detail.dart @@ -303,8 +303,10 @@ class _LogMealDetailScreenState extends State { controller: _carbsRatioController, keyboardType: const TextInputType.numberWithOptions( decimal: true), - onChanged: (_) => - calculateThirdMeasurementOfPortionCarbsRelation(), + onChanged: (_) async { + await Future.delayed(const Duration(seconds: 1)); + calculateThirdMeasurementOfPortionCarbsRelation(); + }, ), ), IconButton( @@ -330,8 +332,10 @@ class _LogMealDetailScreenState extends State { controller: _portionSizeController, keyboardType: const TextInputType.numberWithOptions( decimal: true), - onChanged: (_) => - calculateThirdMeasurementOfPortionCarbsRelation(), + onChanged: (_) async { + await Future.delayed(const Duration(seconds: 1)); + calculateThirdMeasurementOfPortionCarbsRelation(); + }, ), ), IconButton( @@ -365,8 +369,10 @@ class _LogMealDetailScreenState extends State { controller: _carbsPerPortionController, keyboardType: const TextInputType.numberWithOptions( decimal: true), - onChanged: (_) => - calculateThirdMeasurementOfPortionCarbsRelation(), + onChanged: (_) async { + await Future.delayed(const Duration(seconds: 1)); + calculateThirdMeasurementOfPortionCarbsRelation(); + }, ), ), IconButton( diff --git a/lib/screens/log/log_entry/log_meal_list.dart b/lib/screens/log/log_entry/log_meal_list.dart index 3e7701b..bc48cd4 100644 --- a/lib/screens/log/log_entry/log_meal_list.dart +++ b/lib/screens/log/log_entry/log_meal_list.dart @@ -66,45 +66,44 @@ class _LogMealListScreenState extends State { @override Widget build(BuildContext context) { - return Column( - children: [ - Expanded( - child: widget.logMeals.isNotEmpty - ? ListView.builder( - shrinkWrap: true, - itemCount: widget.logMeals.length, - itemBuilder: (context, index) { - final meal = widget.logMeals[index]; - return ListTile( - onTap: () => handleEditAction(meal), - title: Row( - children: [ - Expanded(child: Text(meal.value)), - Expanded( - child: Text(meal.carbsPerPortion != null - ? '${meal.carbsPerPortion} g carbs' - : '')), - Expanded( - child: Text( - meal.bolus != null ? '${meal.bolus} U' : '')) - ], - ), - trailing: IconButton( - icon: const Icon( - Icons.delete, - color: Colors.blue, + return widget.logMeals.isNotEmpty + ? ListView.builder( + shrinkWrap: true, + itemCount: widget.logMeals.length, + itemBuilder: (context, index) { + final meal = widget.logMeals[index]; + return ListTile( + onTap: () => handleEditAction(meal), + title: Row( + children: [ + Expanded(child: Text(meal.value)), + Expanded( + child: Column( + children: ((meal.carbsPerPortion ?? 0) > 0) + ? [ + Text(meal.carbsPerPortion!.toStringAsPrecision(3)), + Text( + '${Settings.nutritionMeasurementSuffix} carbs', + textScaleFactor: 0.75), + ] + : [], ), - onPressed: () => handleDeleteAction(meal), ), - ); - }, - ) - : const Center( - child: Text( - 'You have not added any Meals to this Log Entry yet!'), + ], ), - ), - ], - ); + trailing: IconButton( + icon: const Icon( + Icons.delete, + color: Colors.blue, + ), + onPressed: () => handleDeleteAction(meal), + ), + ); + }, + ) + : const Center( + child: Text( + 'You have not added any Meals to this Log Entry yet!'), + ); } } diff --git a/lib/screens/meal/meal_detail.dart b/lib/screens/meal/meal_detail.dart index 8520364..118284a 100644 --- a/lib/screens/meal/meal_detail.dart +++ b/lib/screens/meal/meal_detail.dart @@ -33,10 +33,11 @@ class _MealDetailScreenState extends State { final _carbsRatioController = TextEditingController(text: ''); final _portionSizeController = TextEditingController(text: ''); final _carbsPerPortionController = TextEditingController(text: ''); - final _delayedBolusRateController = TextEditingController(text: ''); final _delayedBolusDurationController = TextEditingController(text: ''); final _notesController = TextEditingController(text: ''); + double _delayedBolusPercentage = 0; + MealSource? _mealSource; MealCategory? _mealCategory; MealPortionType? _mealPortionType; @@ -67,8 +68,8 @@ class _MealDetailScreenState extends State { _portionSizeController.text = (_meal!.portionSize ?? '').toString(); _carbsPerPortionController.text = (_meal!.carbsPerPortion ?? '').toString(); - _delayedBolusRateController.text = - (_meal!.delayedBolusRate ?? '').toString(); + _delayedBolusPercentage = + _meal!.delayedBolusPercentage ?? 0; _delayedBolusDurationController.text = (_meal!.delayedBolusDuration ?? '').toString(); _notesController.text = _meal!.notes ?? ''; @@ -103,7 +104,7 @@ class _MealDetailScreenState extends State { carbsPerPortion: double.tryParse(_carbsPerPortionController.text), delayedBolusDuration: int.tryParse(_delayedBolusDurationController.text), - delayedBolusRate: double.tryParse(_delayedBolusRateController.text), + delayedBolusPercentage: _delayedBolusPercentage, notes: _notesController.text, ); meal.mealSource.target = _mealSource; @@ -134,7 +135,7 @@ class _MealDetailScreenState extends State { _portionSizeAccuracy != null || int.tryParse(_delayedBolusDurationController.text) != null || - double.tryParse(_delayedBolusRateController.text) != null || + _delayedBolusPercentage != 0 || _notesController.text != '')) || (!_isNew && (_valueController.text != _meal!.value || @@ -151,8 +152,8 @@ class _MealDetailScreenState extends State { _portionSizeAccuracy != _meal!.portionSizeAccuracy.target || int.tryParse(_delayedBolusDurationController.text) != _meal!.delayedBolusDuration || - double.tryParse(_delayedBolusRateController.text) != - _meal!.delayedBolusRate || + _delayedBolusPercentage != + _meal!.delayedBolusPercentage || _notesController.text != (_meal!.notes ?? ''))))) { Dialogs.showCancelConfirmationDialog( context: context, @@ -284,15 +285,17 @@ class _MealDetailScreenState extends State { children: [ Expanded( child: TextFormField( - decoration: const InputDecoration( - labelText: 'Carbs ratio', - suffixText: '%', - ), - controller: _carbsRatioController, - keyboardType: const TextInputType.numberWithOptions( - decimal: true), - onChanged: (_) => - calculateThirdMeasurementOfPortionCarbsRelation(), + decoration: const InputDecoration( + labelText: 'Carbs ratio', + suffixText: '%', + ), + controller: _carbsRatioController, + keyboardType: const TextInputType.numberWithOptions( + decimal: true), + onChanged: (_) async { + await Future.delayed(const Duration(seconds: 1)); + calculateThirdMeasurementOfPortionCarbsRelation(); + }, ), ), IconButton( @@ -311,15 +314,16 @@ class _MealDetailScreenState extends State { child: TextFormField( decoration: InputDecoration( labelText: 'Portion size', - suffixText: - Settings.nutritionMeasurementSuffix, + suffixText: Settings.nutritionMeasurementSuffix, alignLabelWithHint: true, ), controller: _portionSizeController, keyboardType: const TextInputType.numberWithOptions( decimal: true), - onChanged: (_) => - calculateThirdMeasurementOfPortionCarbsRelation(), + onChanged: (_) async { + await Future.delayed(const Duration(seconds: 1)); + calculateThirdMeasurementOfPortionCarbsRelation(); + }, ), ), IconButton( @@ -347,14 +351,15 @@ class _MealDetailScreenState extends State { child: TextFormField( decoration: InputDecoration( labelText: 'Carbs per portion', - suffixText: - Settings.nutritionMeasurementSuffix, + suffixText: Settings.nutritionMeasurementSuffix, ), controller: _carbsPerPortionController, keyboardType: const TextInputType.numberWithOptions( decimal: true), - onChanged: (_) => - calculateThirdMeasurementOfPortionCarbsRelation(), + onChanged: (_) async { + await Future.delayed(const Duration(seconds: 1)); + calculateThirdMeasurementOfPortionCarbsRelation(); + }, ), ), IconButton( @@ -386,14 +391,27 @@ class _MealDetailScreenState extends State { controller: _delayedBolusDurationController, keyboardType: const TextInputType.numberWithOptions(), ), - TextFormField( - decoration: const InputDecoration( - labelText: 'Delayed Bolus Units', - suffixText: ' U', + Padding( + padding: const EdgeInsets.symmetric(horizontal: 5.0), + child: Row( + children: [ + const Text('Delayed Bolus Percentage:'), + Expanded( + child: Slider( + label: '${_delayedBolusPercentage.floor().toString()}%', + divisions: 100, + value: _delayedBolusPercentage, + min: 0, + max: 100, + onChanged: (value) { + setState(() { + _delayedBolusPercentage = value; + }); + } + ), + ), + ], ), - controller: _delayedBolusRateController, - keyboardType: - const TextInputType.numberWithOptions(decimal: true), ), TextFormField( controller: _notesController, diff --git a/lib/screens/meal/meal_list.dart b/lib/screens/meal/meal_list.dart index 4bafb7e..af192a8 100644 --- a/lib/screens/meal/meal_list.dart +++ b/lib/screens/meal/meal_list.dart @@ -73,8 +73,9 @@ class _MealListScreenState extends State { itemCount: _meals.length, itemBuilder: (context, index) { final meal = _meals[index]; - + String portionType = meal.mealPortionType.hasValue ? ' per ${meal.mealPortionType.target!.value}' : ''; return ListTile( + isThreeLine: true, onTap: () { Navigator.push( context, @@ -85,7 +86,40 @@ class _MealListScreenState extends State { ).then((message) => reload(message: message)); }, title: Text(meal.value), - subtitle: Text(meal.notes ?? ''), + subtitle: Row( + children: [ + Column( + children: [ + Text(meal.mealSource.target?.value ?? ''), + Text(meal.notes ?? ''), + ], + ), + Expanded( + child: Column( + children: ((meal.carbsPerPortion ?? 0) > 0) + ? [ + Text(meal.carbsPerPortion!.toStringAsPrecision(3)), + Text( + '${Settings.nutritionMeasurementSuffix} carbs', + textScaleFactor: 0.75), + ] + : [], + ), + ), + Expanded( + child: Column( + children: (meal.mealPortionType.hasValue) + ? [ + Text(meal.portionSize!.toStringAsPrecision(3)), + Text( + '${Settings.nutritionMeasurementSuffix}$portionType', + textScaleFactor: 0.75), + ] + : [], + ), + ), + ], + ), trailing: Row( mainAxisSize: MainAxisSize.min, children: [