translations, set name props as unique, set child data as deleted on deleting parents

This commit is contained in:
spinel 2022-07-08 04:08:50 +02:00
parent 38f43a48ec
commit 0182bf463b
81 changed files with 4199 additions and 1215 deletions

54
TODO
View File

@ -1,48 +1,45 @@
MAIN TASKS: MAIN TASKS:
Database: Database:
☐ set name properties as unique (and add checks to forms)
☐ implement users
☐ check objectbox docs on how to make users
☐ tie all data to users
☐ add user filters to all getters
☐ change settings to not be a singleton, but only one settings instance per user and one default entry
☐ add login and authentification
☐ enable restoring data from sync
☐ find a solution for storage
☐ hosting
☐ objectbox sync (commercial use is not free)
☐ implement alternative data export for now?
☐ create default datasets for configuration (meal categories, portion types, accuracies, event types, possibly meal source) ☐ create default datasets for configuration (meal categories, portion types, accuracies, event types, possibly meal source)
☐ cleanup unneeded models (and make sure the app still runs)
Features: Features:
☐ app icon
☐ add explanations to each section
☐ german language support
☐ indicate nested creation process (creating from dropdown etc)
☐ indicate read only fields ☐ indicate read only fields
☐ indicate nested creation process (creating from dropdown etc)
☐ app info/credits screen ☐ app info/credits screen
☐ add explanations to each section
☐ app icon
Components/Framework: Components/Framework:
☐ check through all detail forms and set required fields/according messages
☐ show indicator and make all fields readonly if user somehow gets to a deleted record detail view
☐ check for changes before navigating as well (not just on cancel)
☐ fix bug when navigating while creating/editing a record
☐ change placement of delete and floating button because its very easy to accidentally hit delete
☐ dropdown tweaks
☐ edit item -> cancel: shouldn't clear dropdwon
☐ account for deleted/disabled elements
☐ change app id from "com.example.diameter" to "at.sarahziesel.diameter"
☐ come up with new concept for duration component ☐ come up with new concept for duration component
☐ update duration fields to use corresponding component ☐ update duration fields to use corresponding component
☐ log event type detail (reminder duration) ☐ log event type detail (reminder duration)
☐ log event detail (reminder duration) ☐ log event detail (reminder duration)
☐ meal (bolus delay) ☐ meal (bolus delay)
☐ log bolus (delay) ☐ log bolus (delay)
☐ check through all detail forms and set required fields/according messages
☐ change placement of delete and floating button because its very easy to accidentally hit delete
☐ check for changes before navigating as well (not just on cancel)
☐ show indicator and make all fields readonly if user somehow gets to a deleted record detail view
☐ dropdown tweaks
☐ edit item -> cancel: shouldn't clear dropdwon
☐ account for deleted/disabled elements
Log: Log:
☐ add filters ☐ add filters
☐ check if there is still an active bolus when suggesting glucose bolus ☐ check if there is still an active bolus when suggesting glucose bolus
☐ remember/display setting for correction boli correctly
Log Events: Log Events:
☐ add filters ☐ add filters
☐ don't show warning for existing event if it's the one being edited
☐ add create button to event type dropdown
Categorization: Categorization:
☐ add colors to event types as indicators for log entries and graphs in reports ☐ add colors to event types as indicators for log entries and graphs in reports
☐ implement reminders as push notifications ☐ implement reminders as push notifications
Settings: Settings:
☐ export functionality
☐ implement loading from google drive
☐ find out pricing for google apis
☐ add some options (which data to export, including dead records...)
☐ option to switch theme ☐ option to switch theme
☐ add fields for glucose target tiers (as map of cutoff glucose and colors) ☐ add fields for glucose target tiers (as map of cutoff glucose and colors)
☐ add field for active insulin duration ☐ add field for active insulin duration
@ -50,6 +47,15 @@ MAIN TASKS:
☐ add functionality to delete dead records (meaning: set deleted flag and no relations to undeleted records) ☐ add functionality to delete dead records (meaning: set deleted flag and no relations to undeleted records)
Archive: Archive:
✔ set child data as deleted on deleting parent (ie. log boli for log entry etc) @done(22-06-19 23:44) @project(MAIN TASKS.Database)
✔ set name properties as unique @done(22-06-17 01:34) @project(MAIN TASKS.Database)
✔ german language support @done(22-06-17 01:19) @project(MAIN TASKS.Features)
✔ add translations for all sections @done(22-06-17 01:19) @project(MAIN TASKS.Features)
✔ add data source field to all models @done(22-04-15 00:24) @project(MAIN TASKS.Database)
✔ add export methods to all models @done(22-04-12 21:38) @project(MAIN TASKS.Database)
✔ add import methods to all models @done(22-04-15 00:24) @project(MAIN TASKS.Database)
✔ implement export to json @done(22-04-12 21:43) @project(MAIN TASKS.Settings)
✔ implement saving export to google drive @done(22-04-13 15:01) @project(MAIN TASKS.Settings)
✔ switch day on swipe @done(22-03-19 23:20) @project(MAIN TASKS.Log) ✔ switch day on swipe @done(22-03-19 23:20) @project(MAIN TASKS.Log)
✔ switch day on swipe @done(22-03-19 23:23) @project(MAIN TASKS.Log Events) ✔ switch day on swipe @done(22-03-19 23:23) @project(MAIN TASKS.Log Events)
✔ switch day on swipe @done(22-03-19 23:20) @project(MAIN TASKS.Reports) ✔ switch day on swipe @done(22-03-19 23:20) @project(MAIN TASKS.Reports)

View File

@ -0,0 +1 @@
{"installed":{"client_id":"988592836243-pmdkvghnvd6fdeo4qm0sjgstcqvrbhqs.apps.googleusercontent.com","project_id":"coastal-fiber-347020","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://oauth2.googleapis.com/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs"}}

501
assets/i18n/de.json Normal file
View File

@ -0,0 +1,501 @@
{
"accuracy": {
"confirmDelete": "Willst du diese Präzision wirklich löschen?",
"deleted": "Präzision gelöscht",
"detail": {
"title": "{status} Präzision"
},
"empty": "Du hast noch keine Präzisionen erstellt!",
"fields": {
"confidenceRating": "Reihung",
"fürCarbsRatio": "für KH-Verhältnis",
"fürPortionSize": "für Portionsgröße",
"name": "Name",
"notes": "Bemerkung",
"validators": {
"name": "Name ist leer"
}
},
"new": "Neue",
"saved": {
"1": "{status}Präzision gespeichert",
"else": "{status}Präzisionen gespeichert"
},
"title": "Präzision"
},
"basal": {
"confirmDelete": "Willst du diese Basalrate wirklich löschen?",
"deleted": "Basalrate gelöscht",
"empty": "Du hast noch keine Basalraten erstellt!",
"fields": {
"endTime": "Endzeit",
"startTime": "Startzeit",
"units": "Einheiten"
},
"new": "Neue",
"saved": {
"1": "{status}Basalrate gespeichert",
"else": "{status}Basalraten gespeichert"
},
"title": "{status} Basalrate für {profileName}",
"warnings": {
"duplicate": "Es gibt bereits eine Rate mit dieser Startzeit.",
"endTimeLast": "Letzter Basal des Tages muss um 00:00 enden",
"gap": "Lücke zwischen dieser und der vorigen Rate",
"overlap": "Zeitraum der Rate überlappt mit einer anderen",
"startTimeFirst": "Erster Basal des Tages muss um 00:00 beginnen"
}
},
"basalProfile": {
"activated": "{profileName} wurde als aktives Profil ausgewählt",
"active": "Derzeit aktives Profil",
"confirmDelete": "Willst du dieseBasal Profile?",
"copied": "Kopie von {profileName} hinzugefügt",
"copyOf": "Kopie von {profileName}",
"default": "Standard-Profil",
"deleted": "Basalprofil gelöscht",
"detail": {
"tabs": {
"profile": "Profil",
"rates": "Raten"
},
"title": "{status} Basalprofil {profileName}"
},
"empty": "Du hast noch kein Basalprofil erstellt!",
"fields": {
"active": "aktiv",
"name": "Name",
"notes": "Bemerkung",
"validators": {
"name": "Name ist leer"
}
},
"new": "Neues",
"saved": "{status}Basalprofil gespeichert",
"title": "Basalprofile",
"warnings": {
"activeAlreadySet": "Es gibt bereits ein oder mehrere aktive Profile. Was möchtest du tun?",
"multipleActive": "Mehr als ein aktives Profil.",
"noActive": "Kein aktives Basalprofil.",
"noActiveOnCreate": "Im Moment ist kein Profil aktiv. Möchtest du dieses aktivieren?",
"resolve": {
"activate": "Profil aktivieren",
"activateCurrent": "Dieses Profil aktivieren",
"create": "Profil erstellen",
"createInstead": "Stattdessen neues Profil erstellen",
"deactivateOthers": "Alle anderen deaktivieren",
"deactivateProfile": "{profileName} deaktivieren",
"ignore": "Ignorierem",
"pick": "Wähle ein Profil"
}
}
},
"bolus": {
"confirmDelete": "Willst du diese Bolusrate wirklich löschen?",
"deleted": "Bolusrate gelöscht",
"empty": "Du hast noch keine Bolusraten erstellt!",
"fields": {
"endTime": "Endzeit",
"perCarbs": "pro KH",
"perGlucose": "pro {glucoseMeasurementSuffix}",
"startTime": "Startzeit",
"units": "Einheiten"
},
"new": "Neue",
"saved": {
"1": "{status}Bolusrate gespeichert",
"else": "{status}Bolusraten gespeichert"
},
"title": "{status} Bolusrate für {profileName}",
"warnings": {
"duplicate": "Es gibt bereits eine Rate mit dieser Startzeit.",
"endTimeLast": "Letzter Bolus des Tages muss um 00:00 enden",
"gap": "Lücke zwischen dieser und der vorigen Rate",
"overlap": "Zeitraum der Rate überlappt mit einer anderen",
"startTimeFirst": "Erster Bolus des Tages muss um 00:00 beginnen"
}
},
"bolusProfile": {
"activated": "{profileName} wurde als aktives Profil ausgewählt",
"active": "Derzeit aktives Profil",
"confirmDelete": "Willst du dieses Boluspofil wirklich löschen?",
"copied": "Kopie von {profileName} hinzugefügt",
"copyOf": "Kopie von {profileName}",
"default": "Standardprofil",
"deleted": "Bolusprofil gelöscht",
"detail": {
"tabs": {
"profile": "Profil",
"rates": "Raten"
},
"title": "{status} Bolusprofil {profileName}"
},
"empty": "Du hast noch keine Bolusprofile erstellt!",
"fields": {
"active": "aktiv",
"name": "Name",
"notes": "Bemerkung",
"validators": {
"name": "Bezeichnung ist leer"
}
},
"new": "Neues",
"saved": "{status}Bolusprofil gespeichert",
"title": "Bolusprofile",
"warnings": {
"activeAlreadySet": "Es gibt bereits ein oder mehrere aktive Profile. Was möchtest du tun?",
"multipleActive": "Mehr als ein aktives Profil.",
"noActive": "Kein aktives Bolusprofil.",
"noActiveOnCreate": "Im Moment ist kein Profil aktiv. Möchtest du dieses aktivieren?",
"resolve": {
"activate": "Profil aktivieren",
"activateCurrent": "Dieses Profil aktivieren",
"create": "Profil erstellen",
"createInstead": "Stattdessen neues Profil erstellen",
"deactivateOthers": "Alle anderen deaktivieren",
"deactivateProfile": "{profileName} deaktivieren",
"ignore": "Ignorierem",
"pick": "Wähle ein Profil"
}
}
},
"categories": "Kategorien",
"event": {
"confirmDelete": "Willst du dieses Ereignis wirklich löschen?",
"confirmEnd": "Willst du dieses Ereignis wirklich beenden?",
"deleted": "Log-Ereignis gelöscht",
"detail": {
"title": "{status} Log-Ereignis {name}"
},
"empty": "Kkeine Ereigniss für dieses Datum!",
"emptyActive": "Keine aktiven Ereignisse!",
"end": "Ereignis beenden",
"ended": "Log-Ereignis beendet",
"fields": {
"basalProfile": "Basalprofil",
"bolusProfile": "Bolusprofil",
"date": "Datum",
"endDate": "Enddatum",
"endTime": "Endzeit",
"eventType": "Ereignis-Typ",
"hasEndTime": "hat Endzeitpunkt",
"notes": "Bemerkung",
"reminderDuration": "Dauer für Erinnerung",
"startDate": "Startdatum",
"startTime": "Startzeit",
"time": "Zeit"
},
"new": "Neues",
"saved": "{status}Log-Ereignis gespeichert",
"title": "Log-Ereignisse",
"titleActive": "Aktive Ereignisse",
"warnings": {
"duplicate": "Im angegebenen Zeitraum gibt es bereits ein aktives Ereignis vom selben Typ. Was möchtest du tun?"
}
},
"eventType": {
"deleted": "Log-Ereignis-Typ gelöscht",
"detail": {
"title": "{status} Log-Ereignis-Typ {name}"
},
"empty": "Du hast noch keine Log-Ereignis-Typen erstellt!",
"fields": {
"basalProfile": "Basalprofil",
"bolusProfile": "Bolusprofil",
"defaultReminderDuration": "Standarddauer für Erinnerung",
"hasEndTime": "hat Endzeitpunkt",
"name": "Name",
"notes": "Bemerkung",
"validators": {
"name": "Name ist leer"
}
},
"new": "Neuer",
"saved": "{status}Log-Ereignis-Typ gespeichert",
"title": "Log-Ereignis-Typen"
},
"export": {
"error": "Error beim Import der folgenden Daten: {data}"
},
"general": {
"apply": "Anwenden",
"cancel": "Abbrechen",
"close": "Schließen",
"confirm": "Bestätigen",
"confirmDelete": "Willst du diesen Eintrag wirklich löschen?",
"confirmDiscard": "Änderungen wurden bereits vorgenommen. Eingaben verwerfen?",
"discard": "Verwerfen",
"edit": "Bearbeiten",
"example": "Beispiel",
"keepEditing": "Weiter bearbeiten",
"next": "Weiter",
"per": "pro",
"save": "Speichern",
"saveAndClose": "Speichern & Schließen",
"saveAsIs": "Aktuellen Stand speichern",
"suffixes": {
"carbs": "{nutritionMeasurement} KH",
"carbsPerU": "{nutritionMeasurementSuffix} KH pro E",
"mins": " min",
"perDay": "am Tag",
"units": "E",
"uPerBreadUnit": "E pro BE",
"uPerGlucose": "{glucoseMeasurementSuffix} pro Einheit"
}
},
"log": {
"confirmDelete": "Willst du diesen Logeintrag wirklich löschen?",
"deleted": "Logeintrag gelöscht",
"empty": "Du hast noch keine Logeinträge für dieses Datum erstellt!",
"fields": {
"date": "Datum",
"glucose": "Blutzucker",
"time": "Zeit"
},
"filter": {
"endDate": "Endzeit",
"maxGlucose": "max. {glucoseMeasurement}",
"meal": "Mahlzeit",
"mealNameContains": "Name der Mahlzeit enthält",
"minGlucose": "min. {glucoseMeasurement}",
"noteContains": "Bemerkung enthält",
"startDate": "Startzeit"
},
"saved": "{status}Logeintrag gespeichert",
"tabs": {
"bolus": {
"confirmDelete": "Willst du diesen Bolus wirklich löschen?",
"delayedBy": "(um {delay} verzögert)",
"deleted": "Bolus gelöscht",
"detail": {
"fields": {
"carbs": "KH",
"correction": "Korrektur",
"current": "Aktuell",
"delayedBolus": "Verzögerter Bolus",
"delayedBolusDuration": "Dauer Verzögerter Bolus",
"forGlucose": "für Blutzucker",
"forMeal": "für Mahlzeit",
"immediateBolus": "Sofortiger Bolus",
"meal": "Mahlzeit",
"setManually": "manuall einstellen",
"target": "Zielwert",
"units": "Bolus-Einheiten"
},
"title": "{status} Bolus"
},
"empty": "Du hast noch keine Boli für diesen Logeintrag erstellt!",
"forMeal": "für {meal}",
"new": "Neuer",
"saved": "{status}Bolus gespeichert",
"title": "Boli",
"toCorrect": "zur Korrektur von {correction}"
},
"general": "Allgemein",
"meal": {
"confirmDelete": "Willst du diese Mahlzeit wirklich löschen?",
"deleted": "Mahlzeit gelöscht",
"detail": {
"fields": {
"amount": "Menge",
"carbsRatio": "KH-Verhältnis",
"carbsRatioAccuracy": "Präzision KH-Verhältnis",
"meal": "Mahlzeit",
"mealCategory": "Kategorie",
"mealPortionType": "Portionstyp",
"mealSource": "Herkunft",
"name": "Name",
"notes": "Bemerkung",
"portionSize": "Portionsgröße",
"portionSizeAccuracy": "Präzision Portionsgröße",
"setManually": "KH-Verhältnis manuell einstellen",
"validators": {
"name": "Name ist leer"
}
},
"title": "{status} Mahlzeit"
},
"empty": "Du hast noch keine Mahlzeiten für diesen Logeintrag erstellt!",
"new": "Neue",
"saved": "{status}Mahlzeit gespeichert",
"title": "Mahlzeiten"
}
},
"title": "Logeinträge"
},
"meal": {
"confirmDelete": "Willst du diese Mahlzeit wirklich löschen?",
"deleted": "Mahlzeit gelöscht",
"detail": {
"title": "{status} Mahlzeit {name}"
},
"empty": "Du hast noch keine Mahlzeiten erstellt!",
"fields": {
"additional": {
"carbsRatioAccuracy": "Präzision KH-Verhältnis",
"mealCategory": "Kategorie",
"portionSizeAccuracy": "Präzision Portionsgröße",
"title": "Zusätzliche Felder"
},
"carbsPerPortion": "KH pro Portion",
"carbsRatio": "KH-Verhältnis",
"delay": {
"duration": "Dauer",
"title": "Verzögerter Bolus"
},
"mealPortionType": "Portionstyp",
"mealSource": "Herkunft",
"name": "Name",
"notes": "Bemerkung",
"portionSize": "Portionsgröße",
"setManually": "KH-Verhältnis manuell einstellen",
"validators": {
"name": "Name ist leer"
}
},
"new": "Neue",
"saved": "{status}Mahlzeit gespeichert",
"title": "Mahlzeiten"
},
"mealCategory": {
"deleted": "Mahlzeiten-Kategorie gelöscht",
"detail": {
"title": "{status} Mahlzeiten-Kategorie {name}"
},
"empty": "Du hast noch keine Mahlzeiten-Kategorien erstellt!",
"fields": {
"name": "Name",
"notes": "Bemerkung",
"validators": {
"name": "Name ist leer"
}
},
"new": "New",
"saved": "{status}Mahlzeiten-Kategorie gespeichert",
"title": "Mahlzeiten-Kategorien"
},
"mealSource": {
"deleted": "Mahlzeiten-Herkunft gelöscht",
"detail": {
"title": "{status} Mahlzeiten-Herkunft {name}"
},
"empty": "Du hast noch keine Mahlzeiten-Herkünfte erstellt!",
"fields": {
"defaultCarbsRatioAccuracy": "Standard-Präzision KH-Verhältnis",
"defaultMealCategory": "Standard Mahlzeiten-Kategorie",
"defaultMealPortionType": "Standard-Portionstyp",
"defaultPortionSizeAccuracy": "Standard-Präzision Portionsgröße",
"name": "Name",
"notes": "Bemerkung",
"validators": {
"name": "Name ist leer"
}
},
"new": "New",
"saved": "{status}Mahlzeiten-Herkunft gespeichert",
"title": "Mahlzeiten-Herkünfte"
},
"navigation": {
"basalProfiles": "Basalprofile",
"bolusProfiles": "Bolusprofile",
"categorization": "Kategorisierung",
"log": "Log",
"logEvent": "Log-Ereignisse",
"meals": "Mahlzeiten",
"reports": "Berichte",
"settings": "Einstellungen"
},
"portionType": {
"deleted": "Mahlzeiten-Portionstyp gelöscht",
"detail": {
"title": "{status} Mahlzeiten-Portionstyp {name}"
},
"empty": "Du hast noch keine Mahlzeiten-Portionstypen erstellt!",
"fields": {
"name": "Name",
"notes": "Bemerkung",
"validators": {
"name": "Name ist leer"
}
},
"new": "New",
"saved": "{status}Mahlzeiten-Portionstyp gespeichert",
"title": "Mahlzeiten-Portionstypen"
},
"reports": {
"dailyChart": {
"empty": "Du hast noch keine Logeinträge für dieses Datum erstellt!",
"showBasal": "zeige Basal",
"showBolus": "zeige Bolus",
"showChart": "zeige Grafik",
"showMeals": "zeige Mahlzeiten",
"title": "Tagesgrafik"
},
"export": {
"date": "Datum",
"delayedBy": "(um {delay} verzögert)",
"endDate": "Enddatum",
"export": "exportieren",
"range": "Zeitraum",
"showBasal": "zeige Basal",
"showBolus": "zeige Bolus",
"showChart": "zeige Grafik",
"showMahlzeits": "zeige Mahlzeiten",
"showTable": "zeige Tabelle",
"singleDate": "Bestimmtes Datum",
"tableHeaders": {
"bolus": "Bolus",
"carbs": "Kohlenhydrate",
"glucose": "Blutzucker",
"mealBolus": "Mahlzeit Bolus",
"mealBemerkung": "Mahlzeit Bemerkung",
"meals": "Mahlzeit",
"notes": "Bemerkung",
"portionSize": "Portionsgröße",
"time": "Zeit"
},
"title": "Log-Übersicht"
},
"ids": {
"basal": "Basal",
"bolus": "Bolus",
"carbs": "Kohlenhydrate",
"glucose": "Blutzucker"
},
"sections": {
"dailyReport": "Tagesbericht",
"pdfReport": "PDF-Übersicht"
},
"title": "Berichte"
},
"settings": {
"confirmReset": "Bist du sicher, dass du alle Einstellungen zurücksetzen möchtest?",
"fields": {
"confirmOnCancel": "beim Abbrechen der Bearbeitung eines Eintrags, wenn bereits Änderungen vorgenommen wurden",
"confirmOnDelete": "beim Löschen eines Eintrags",
"confirmOnEndEreignis": "beim Beenden eines Ereignisses",
"dateformat": "Datumsformat",
"displayBothDetail": "zeige beide Blutzucker-Maßeinheiten in der Detailansicht",
"displayBothList": "display both glucose measurements in list view",
"glucoseMeasurement": "Bevorzugte Maßeinheit für Blutzuckerwerte",
"insulinIncrement": "Insulin-Dosierungsschritte",
"longDateformat": "Datumsformat lang",
"longTimeformat": "Zeitformat lang",
"mmolLIncrement": "Schritte Mmol/l",
"nutritionIncrement": "Schritte Maßeinheit für Mahlzeiten",
"nutritionMeasurement": "Bevorzugte Maßeinheit für Mahlzeiten",
"onlyDisplayActive": "zeige nur aktive Blutzucker-Maßeinheit",
"targetGlucose": "Ziel-Blutzucker",
"timeformat": "Zeitformat"
},
"reset": "Einstellungen wurden auf Standardeinstellungen zurückgesetzt",
"resetAll": "Alle zurücksetzen",
"sections": {
"confirmation": "Bestätigungen",
"dateTimeformat": "Zeit- und Datumsformat",
"measurements": "Maßeinheiten"
},
"title": "Applikationseinstellungen",
"updated": "Einstellungen gespeichert"
}
}

501
assets/i18n/en.json Normal file
View File

@ -0,0 +1,501 @@
{
"accuracy": {
"confirmDelete": "Are you sure you want to delete this Accuracy?",
"deleted": "Accuracy deleted",
"detail": {
"title": "{status} Accuracy"
},
"empty": "You have not created any Accuracies yet!",
"fields": {
"confidenceRating": "Confidence Rating",
"forCarbsRatio": "for Carbs Ratio",
"forPortionSize": "for Portion Size",
"name": "Name",
"notes": "Notes",
"validators": {
"name": "Empty Name"
}
},
"new": "New",
"saved": {
"1": "{status}Accuracy saved",
"else": "{status}Accuracy saved"
},
"title": "Accuracies"
},
"basal": {
"confirmDelete": "Are you sure you want to delete this Basal Rate?",
"deleted": "Basal Rate deleted",
"empty": "You have not created any Basal Rates yet!",
"fields": {
"endTime": "End Time",
"startTime": "Start Time",
"units": "Units"
},
"new": "New",
"saved": {
"1": "{status}Basal Rate saved",
"else": "{status}Basal Rates saved"
},
"title": "{status} Basal Rate for {profileName}",
"warnings": {
"duplicate": "There's already a rate with this start time.",
"endTimeLast": "Last Basal of the day needs to end at 00:00",
"gap": "There's a time gap between this and the previous rate",
"overlap": "This rate's time period overlaps with another one.",
"startTimeFirst": "First Basal of the day needs to start at 00:00"
}
},
"basalProfile": {
"activated": "{profileName} has been set as your active Profile",
"active": "Current Active Profile",
"confirmDelete": "Are you sure you want to delete this Basal Profile?",
"copied": "Added copy of {profileName}",
"copyOf": "Copy of {profileName}",
"default": "Default Profile",
"deleted": "Basal Profile deleted",
"detail": {
"tabs": {
"profile": "Profile",
"rates": "Rates"
},
"title": "{status} Basal Profile {profileName}"
},
"empty": "You have not created any Basal Profiles yet!",
"fields": {
"active": "active",
"name": "Name",
"notes": "Notes",
"validators": {
"name": "Empty title"
}
},
"new": "New",
"saved": "{status}Basal Profile saved",
"title": "Basal Profiles",
"warnings": {
"activeAlreadySet": "There are already one or more active profiles. What would you like to do?",
"multipleActive": "More than one active Basal Profile has been found.",
"noActive": "You currently do not have an active Basal Profile.",
"noActiveOnCreate": "There is currently no active profile. Would you like to set this one as active?",
"resolve": {
"activate": "Activate a Profile",
"activateCurrent": "Activate This Profile",
"create": "Create a Profile",
"createInstead": "Create a New Profile Instead",
"deactivateOthers": "Deactivate All Others",
"deactivateProfile": "Deactivate {profileName}",
"ignore": "Ignore",
"pick": "Pick a Profile"
}
}
},
"bolus": {
"confirmDelete": "Are you sure you want to delete this Bolus Rate?",
"deleted": "Bolus Rate deleted",
"empty": "You have not created any Bolus Rates yet!",
"fields": {
"endTime": "End Time",
"perCarbs": "per carns",
"perGlucose": "per {glucoseMeasurementSuffix}",
"startTime": "Start Time",
"units": "Units"
},
"new": "New",
"saved": {
"1": "{status}Bolus Rate saved",
"else": "{status}Bolus Rates saved"
},
"title": "{status} Bolus Rate for {profileName}",
"warnings": {
"duplicate": "There's already a rate with this start time.",
"endTimeLast": "Last Bolus of the day needs to end at 00:00",
"gap": "There's a time gap between this and the previous rate",
"overlap": "This rate's time period overlaps with another one.",
"startTimeFirst": "First Bolus of the day needs to start at 00:00"
}
},
"bolusProfile": {
"activated": "{profileName} has been set as your active Profile",
"active": "Current Active Profile",
"confirmDelete": "Are you sure you want to delete this Bolus Profile?",
"copied": "Added copy of {profileName}",
"copyOf": "Copy of {profileName}",
"default": "Default Profile",
"deleted": "Bolus Profile deleted",
"detail": {
"tabs": {
"profile": "Profile",
"rates": "Rates"
},
"title": "{status} Bolus Profile {profileName}"
},
"empty": "You have not created any Bolus Profiles yet!",
"fields": {
"active": "active",
"name": "Name",
"notes": "Notes",
"validators": {
"name": "Empty title"
}
},
"new": "New",
"saved": "{status}Bolus Profile saved",
"title": "Bolus Profiles",
"warnings": {
"activeAlreadySet": "There are already one or more active profiles. What would you like to do?",
"multipleActive": "More than one active Bolus Profile has been found.",
"noActive": "You currently do not have an active Bolus Profile.",
"noActiveOnCreate": "There is currently no active profile. Would you like to set this one as active?",
"resolve": {
"activate": "Activate a Profile",
"activateCurrent": "Activate This Profile",
"create": "Create a Profile",
"createInstead": "Create a New Profile Instead",
"deactivateOthers": "Deactivate All Others",
"deactivateProfile": "Deactivate {profileName}",
"ignore": "Ignore",
"pick": "Pick a Profile"
}
}
},
"categories": "Categories",
"event": {
"confirmDelete": "Are you sure you want to delete this Log Event?",
"confirmEnd": "Are you sure you want to end this Event?",
"deleted": "Log Event deleted",
"detail": {
"title": "{status} Log Event {name}"
},
"empty": "There are no Events for that date!",
"emptyActive": "There are no Active Events!",
"end": "End Event",
"ended": "Log Event ended",
"fields": {
"basalProfile": "Basal Profile",
"bolusProfile": "Bolus Profile",
"date": "Date",
"endDate": "End Date",
"endTime": "End Time",
"eventType": "Event Type",
"hasEndTime": "has end time",
"notes": "Notes",
"reminderDuration": "Reminder Duration",
"startDate": "Start Date",
"startTime": "Start Time",
"time": "Time"
},
"new": "New",
"saved": "{status}Log Event saved",
"title": "Log Events",
"titleActive": "Active Events",
"warnings": {
"duplicate": "An Event of this type is already active within the set time frame. What would you like to do?"
}
},
"eventType": {
"deleted": "Log Event Type deleted",
"detail": {
"title": "{status} Log Event Type {name}"
},
"empty": "You have not created any Log Event Types yet!",
"fields": {
"basalProfile": "Basal Profile",
"bolusProfile": "Bolus Profile",
"defaultReminderDuration": "Default Reminder Duration",
"hasEndTime": "has end time",
"name": "Name",
"notes": "Notes",
"validators": {
"name": "Empty name"
}
},
"new": "New",
"saved": "{status}Log Event Type saved",
"title": "Log Event Types"
},
"export": {
"error": "Error importing the following data: {data}"
},
"general": {
"apply": "Apply",
"cancel": "Cancel",
"close": "Close",
"confirm": "Confirm",
"confirmDelete": "Are you sure you want to delete this record?",
"confirmDiscard": "You already made some changes. Discard your input?",
"discard": "Discard",
"edit": "Edit",
"example": "Example",
"keepEditing": "Keep Editing",
"next": "Next",
"per": "per",
"save": "Save",
"saveAndClose": "Save & Close",
"saveAsIs": "Save As Is",
"suffixes": {
"carbs": "{nutritionMeasurement} carbs",
"carbsPerU": "{nutritionMeasurementSuffix} carbs per U",
"mins": " min",
"perDay": "per day",
"units": "U",
"uPerBreadUnit": "U per bread unit",
"uPerGlucose": "{glucoseMeasurementSuffix} per unit"
}
},
"log": {
"confirmDelete": "Are you sure you want to delete this Log Entry?",
"deleted": "Log Entry deleted",
"empty": "You have not created any Log Entries for this date yet!",
"fields": {
"date": "Date",
"glucose": "Blood Glucose",
"time": "Time"
},
"filter": {
"endDate": "End Date",
"maxGlucose": "max {glucoseMeasurement}",
"meal": "Meal",
"mealNameContains": "Meal Name Contains",
"minGlucose": "min {glucoseMeasurement}",
"noteContains": "Note Contains",
"startDate": "Start Date"
},
"saved": "{status}Log Entry saved",
"tabs": {
"bolus": {
"confirmDelete": "Are you sure you want to delete this Bolus?",
"delayedBy": "(delayed by {delay})",
"deleted": "Bolus deleted",
"detail": {
"fields": {
"carbs": "Carbs",
"correction": "Correction",
"current": "Current",
"delayedBolus": "Delayed Bolus",
"delayedBolusDuration": "Delayed Bolus Duration",
"forGlucose": "for glucose",
"forMeal": "for meal",
"immediateBolus": "Immediate Bolus",
"meal": "Meal",
"setManually": "set manually",
"target": "Target",
"units": "Bolus Units"
},
"title": "{status} Bolus"
},
"empty": "You have not added any Boli to this Log Entry yet!",
"forMeal": "for {meal}",
"new": "New",
"saved": "{status}Bolus saved",
"title": "Boli",
"toCorrect": "to correct {correction}"
},
"general": "General",
"meal": {
"confirmDelete": "Are you sure you want to delete this Meal?",
"deleted": "Meal deleted",
"detail": {
"fields": {
"amount": "Amount",
"carbsRatio": "Carbs Ratio",
"carbsRatioAccuracy": "Carbs Ratio Accuracy",
"meal": "Meal",
"mealCategory": "Meal Category",
"mealPortionType": "Meal Portion Type",
"mealSource": "Meal Source",
"name": "Name",
"notes": "Notes",
"portionSize": "Portion Size",
"portionSizeAccuracy": "Portion Size Accuracy",
"setManually": "set carbs ratio manually",
"validators": {
"name": "Empty Name"
}
},
"title": "{status} Meal"
},
"empty": "You have not added any Meals to this Log Entry yet!",
"new": "New",
"saved": "{status}Meal saved",
"title": "Meals"
}
},
"title": "Log Entries"
},
"meal": {
"confirmDelete": "Are you sure you want to delete this Meal?",
"deleted": "Meal deleted",
"detail": {
"title": "{status} Meal {name}"
},
"empty": "You have not created any Meals yet!",
"fields": {
"additional": {
"carbsRatioAccuracy": "Carbs Ratio Accuracy",
"mealCategory": "Meal Category",
"portionSizeAccuracy": "Portion Size Accuracy",
"title": "Additional Fields"
},
"carbsPerPortion": "Carbs Per Portion",
"carbsRatio": "Carbs Ratio",
"delay": {
"duration": "Duration",
"title": "Bolus Delay"
},
"mealPortionType": "Meal Portion Type",
"mealSource": "Meal Source",
"name": "Name",
"notes": "Notes",
"portionSize": "Portion Size",
"setManually": "set carbs ratio manually",
"validators": {
"name": "Empty Name"
}
},
"new": "New",
"saved": "{status}Meal saved",
"title": "Meals"
},
"mealCategory": {
"deleted": "Meal Category deleted",
"detail": {
"title": "{status} Meal Category {name}"
},
"empty": "You have not created any Meal Categories yet!",
"fields": {
"name": "Name",
"notes": "Notes",
"validators": {
"name": "Empty name"
}
},
"new": "New",
"saved": "{status}Meal Category saved",
"title": "Meal Categories"
},
"mealSource": {
"deleted": "Meal Source deleted",
"detail": {
"title": "{status} Meal Source {name}"
},
"empty": "You have not created any Meal Sources yet!",
"fields": {
"defaultCarbsRatioAccuracy": "Default Carbs Ratio Accuracy",
"defaultMealCategory": "Default Meal Category",
"defaultMealPortionType": "Default Meal Portion Type",
"defaultPortionSizeAccuracy": "Default Portion Size Accuracy",
"name": "Name",
"notes": "Notes",
"validators": {
"name": "Empty name"
}
},
"new": "New",
"saved": "{status}Meal Source saved",
"title": "Meal Sources"
},
"navigation": {
"basalProfiles": "Basal Profiles",
"bolusProfiles": "Bolus Profiles",
"categorization": "Categorization",
"log": "Log",
"logEvents": "Log Events",
"meals": "Meals",
"reports": "Reports",
"settings": "Settings"
},
"portionType": {
"deleted": "Meal Portion Type deleted",
"detail": {
"title": "{status} Meal Portion Type {name}"
},
"empty": "You have not created any Meal Portion Types yet!",
"fields": {
"name": "Name",
"notes": "Notes",
"validators": {
"name": "Empty name"
}
},
"new": "New",
"saved": "{status}Meal Portion Type saved",
"title": "Meal Portion Types"
},
"reports": {
"dailyChart": {
"empty": "You have not created any Log Entries for this date yet!",
"showBasal": "show Basal",
"showBolus": "show Bolus",
"showChart": "show Chart",
"showMeals": "show Meals",
"title": "Daily Chart"
},
"export": {
"date": "Date",
"delayedBy": "(delayed by {delay})",
"endDate": "End Date",
"export": "export",
"range": "Range",
"showBasal": "show Basal",
"showBolus": "show Bolus",
"showChart": "show Chart",
"showMeals": "show Meals",
"showTable": "show Table",
"singleDate": "Single Date",
"tableHeaders": {
"bolus": "Bolus",
"carbs": "Carbohydrates",
"glucose": "Glucose",
"mealBolus": "Meal Bolus",
"mealNotes": "Meal Notes",
"meals": "Meals",
"notes": "Notes",
"portionSize": "Portion Size",
"time": "Time"
},
"title": "Log Report"
},
"ids": {
"basal": "Basal",
"bolus": "Bolus",
"carbs": "Carbohydrates",
"glucose": "Glucose"
},
"sections": {
"dailyReport": "Daily Report",
"pdfReport": "PDF Report"
},
"title": "Reports"
},
"settings": {
"confirmReset": "Are you sure you want to reset all settings?",
"fields": {
"confirmOnCancel": "on cancelling edit or creation of a record if changes have already been made",
"confirmOnDelete": "on deleting a record",
"confirmOnEndEvent": "on stopping (ending) an event",
"dateFormat": "Date Format",
"displayBothDetail": "display both glucose measurements in detail view",
"displayBothList": "display both glucose measurements in list view",
"glucoseMeasurement": "Preferred Glucose Measurement",
"insulinIncrement": "Insulin Increment",
"longDateFormat": "Long Date Format",
"longTimeFormat": "Long Time Format",
"mmolLIncrement": "Mmol/l Increment",
"nutritionIncrement": "Nutrition Increment",
"nutritionMeasurement": "Preferred Nutrition Measurement",
"onlyDisplayActive": "only display active glucose measurement",
"targetGlucose": "Target Glucose",
"timeFormat": "Time Format"
},
"reset": "Settings have been reset to default",
"resetAll": "Reset All",
"sections": {
"confirmation": "Confirmation Prompts",
"dateTimeFormat": "Time & Date Format",
"measurements": "Measurements"
},
"title": "Application Settings",
"updated": "Settings updated"
}
}

6
build.yaml Normal file
View File

@ -0,0 +1,6 @@
targets:
$default:
sources:
include:
- assets/i18n/**
- lib/**

View File

@ -291,7 +291,7 @@
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.example.tide; PRODUCT_BUNDLE_IDENTIFIER = com.example.diameter;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
@ -415,7 +415,7 @@
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.example.tide; PRODUCT_BUNDLE_IDENTIFIER = com.example.diameter;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@ -434,7 +434,7 @@
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.example.tide; PRODUCT_BUNDLE_IDENTIFIER = com.example.diameter;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;

View File

@ -11,7 +11,7 @@
<key>CFBundleInfoDictionaryVersion</key> <key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string> <string>6.0</string>
<key>CFBundleName</key> <key>CFBundleName</key>
<string>tide</string> <string>diameter</string>
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>

View File

@ -5,7 +5,7 @@ class AppTheme {
AppTheme._(); AppTheme._();
static ThemeData lightTheme = FlexColorScheme.light( static ThemeData lightTheme = FlexColorScheme.light(
surfaceStyle: FlexSurface.medium, // surfaceMode: FlexSurfaceMode.level,
scheme: FlexScheme.aquaBlue, scheme: FlexScheme.aquaBlue,
fontFamily: 'Roboto', fontFamily: 'Roboto',
).toTheme; ).toTheme;

View File

@ -1,11 +1,13 @@
import 'package:diameter/localization_keys.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
class DetailBottomRow extends StatefulWidget { class DetailBottomRow extends StatefulWidget {
final void Function()? onCancel; final void Function()? onCancel;
final void Function()? onAction; final void Function()? onAction;
final void Function()? onMiddleAction; final void Function()? onMiddleAction;
final String actionText; final String actionTextKey;
final String middleActionText; final String middleActionTextKey;
final IconData actionIcon; final IconData actionIcon;
final IconData middleActionIcon; final IconData middleActionIcon;
@ -14,9 +16,9 @@ class DetailBottomRow extends StatefulWidget {
required this.onCancel, required this.onCancel,
required this.onAction, required this.onAction,
this.onMiddleAction, this.onMiddleAction,
this.actionText = 'SAVE', this.actionTextKey = LocalizationKeys.general_save,
this.actionIcon = Icons.save, this.actionIcon = Icons.save,
this.middleActionText = 'SAVE & CLOSE', this.middleActionTextKey = LocalizationKeys.general_saveAndClose,
this.middleActionIcon = Icons.done}) this.middleActionIcon = Icons.done})
: super(key: key); : super(key: key);
@ -39,7 +41,7 @@ class _DetailBottomRowState<T> extends State<DetailBottomRow> {
Icons.close, Icons.close,
size: 18.0, size: 18.0,
), ),
label: const Text('CANCEL'), label: Text(translate(LocalizationKeys.general_cancel).toUpperCase()),
), ),
widget.onMiddleAction != null widget.onMiddleAction != null
? ElevatedButton.icon( ? ElevatedButton.icon(
@ -48,7 +50,7 @@ class _DetailBottomRowState<T> extends State<DetailBottomRow> {
widget.middleActionIcon, widget.middleActionIcon,
size: 18.0, size: 18.0,
), ),
label: Text(widget.middleActionText), label: Text(translate(widget.middleActionTextKey)),
) )
: const Spacer(), : const Spacer(),
ElevatedButton.icon( ElevatedButton.icon(
@ -57,7 +59,7 @@ class _DetailBottomRowState<T> extends State<DetailBottomRow> {
widget.actionIcon, widget.actionIcon,
size: 18.0, size: 18.0,
), ),
label: Text(widget.actionText), label: Text(translate(widget.actionTextKey)),
), ),
], ],
), ),

View File

@ -6,13 +6,15 @@ class TimeOfDayFormField extends StatefulWidget {
final TextEditingController controller; final TextEditingController controller;
final String label; final String label;
final void Function(TimeOfDay?) onChanged; final void Function(TimeOfDay?) onChanged;
final String? Function(String?)? validator;
const TimeOfDayFormField( const TimeOfDayFormField(
{Key? key, {Key? key,
required this.time, required this.time,
required this.controller, required this.controller,
required this.label, required this.label,
required this.onChanged}) required this.onChanged,
this.validator})
: super(key: key); : super(key: key);
@override @override
@ -35,6 +37,7 @@ class _TimeOfDayFormFieldState extends State<TimeOfDayFormField> {
); );
widget.onChanged(newTime); widget.onChanged(newTime);
}, },
validator: widget.validator,
); );
} }
} }

366
lib/data_export.dart Normal file
View File

@ -0,0 +1,366 @@
import 'dart:convert';
import 'dart:io';
import 'package:diameter/localization_keys.dart';
import 'package:diameter/models/accuracy.dart';
import 'package:diameter/models/basal.dart';
import 'package:diameter/models/basal_profile.dart';
import 'package:diameter/models/bolus.dart';
import 'package:diameter/models/bolus_profile.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_event.dart';
import 'package:diameter/models/log_event_type.dart';
import 'package:diameter/models/log_meal.dart';
import 'package:diameter/models/meal.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/models/settings.dart';
import 'package:flutter_translate/flutter_translate.dart';
// import 'package:flutter/material.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:intl/intl.dart';
import 'package:path_provider/path_provider.dart';
import 'package:http/http.dart' as http;
import 'package:googleapis/drive/v3.dart' as drive;
class GoogleAuthClient extends http.BaseClient {
final Map<String, String> _headers;
final http.Client _client = http.Client();
GoogleAuthClient(this._headers);
@override
Future<http.StreamedResponse> send(http.BaseRequest request) {
return _client.send(request..headers.addAll(_headers));
}
}
class DataExport {
static Map<String, dynamic> appDataToJson(DateTime timestamp) {
final Map<String, dynamic> data = <String, dynamic>{};
data['exportDate'] = timestamp.toString();
data['accuracies'] =
Accuracy.box.getAll().map((accuracy) => accuracy.toJson()).toList();
data['basalProfiles'] = BasalProfile.box
.getAll()
.map((basalProfile) => basalProfile.toJson())
.toList();
data['basalRates'] =
Basal.box.getAll().map((basal) => basal.toJson()).toList();
data['bolusProfiles'] = BolusProfile.box
.getAll()
.map((bolusProfile) => bolusProfile.toJson())
.toList();
data['bolusRates'] =
Bolus.box.getAll().map((bolusRates) => bolusRates.toJson()).toList();
data['glucoseTargets'] = GlucoseTarget.box
.getAll()
.map((glucoseTarget) => glucoseTarget.toJson())
.toList();
data['logBoli'] =
LogBolus.box.getAll().map((logBolus) => logBolus.toJson()).toList();
data['logEntries'] =
LogEntry.box.getAll().map((logEntry) => logEntry.toJson()).toList();
data['logEventTypes'] = LogEventType.box
.getAll()
.map((logEventType) => logEventType.toJson())
.toList();
data['logEvents'] =
LogEvent.box.getAll().map((logEvent) => logEvent.toJson()).toList();
data['logMeals'] =
LogMeal.box.getAll().map((logMeal) => logMeal.toJson()).toList();
data['mealCategories'] = MealCategory.box
.getAll()
.map((mealCategory) => mealCategory.toJson())
.toList();
data['mealPortionTypes'] = MealPortionType.box
.getAll()
.map((mealPortionType) => mealPortionType.toJson())
.toList();
data['mealSources'] = MealSource.box
.getAll()
.map((mealSource) => mealSource.toJson())
.toList();
data['meals'] = Meal.box.getAll().map((meal) => meal.toJson()).toList();
data['settings'] = Settings.toJson();
return data;
}
List<String> appDataFromJson(File file,
{String source = '', bool overrideExisting = true, bool skipDeleted = false}) {
final Map<String, dynamic> data = json.decode(file.openRead().toString());
final List<String> errors = [];
final exportDate = DateTime.tryParse(data['exportDate']);
if (exportDate != null && Settings.lastExportTimeStamp != null && exportDate.isAfter(Settings.lastExportTimeStamp!)) {
if (data.keys.contains('accuracies')) {
for (var entry in data['accuracies']) {
if (!skipDeleted || entry['deleted'] == false) {
final error = Accuracy.putFromJson(entry, overrideExisting, source);
if (error != null) {
errors.add(translate(LocalizationKeys.export_error, args: {"data": entry}));
}
}
}
}
if (data.keys.contains('basalProfiles')) {
for (var entry in data['basalProfiles']) {
if (!skipDeleted || entry['deleted'] == false) {
final error = BasalProfile.putFromJson(entry, overrideExisting, source);
if (error != null) {
errors.add(translate(LocalizationKeys.export_error, args: {"data": entry}));
}
}
}
}
if (data.keys.contains('basalRates')) {
for (var entry in data['basalRates']) {
if (!skipDeleted || entry['deleted'] == false) {
final error = Basal.putFromJson(entry, overrideExisting, source);
if (error != null) {
errors.add(translate(LocalizationKeys.export_error, args: {"data": entry}));
}
}
}
}
if (data.keys.contains('bolusRates')) {
for (var entry in data['bolusRates']) {
if (!skipDeleted || entry['deleted'] == false) {
final error = Bolus.putFromJson(entry, overrideExisting, source);
if (error != null) {
errors.add(translate(LocalizationKeys.export_error, args: {"data": entry}));
}
}
}
}
if (data.keys.contains('glucoseTargets')) {
for (var entry in data['glucoseTargets']) {
if (!skipDeleted || entry['deleted'] == false) {
final error = GlucoseTarget.putFromJson(entry, overrideExisting, source);
if (error != null) {
errors.add(translate(LocalizationKeys.export_error, args: {"data": entry}));
}
}
}
}
if (data.keys.contains('logBoli')) {
for (var entry in data['logBoli']) {
if (!skipDeleted || entry['deleted'] == false) {
final error = LogBolus.putFromJson(entry, overrideExisting, source);
if (error != null) {
errors.add(translate(LocalizationKeys.export_error, args: {"data": entry}));
}
}
}
}
if (data.keys.contains('logEntries')) {
for (var entry in data['logEntries']) {
if (!skipDeleted || entry['deleted'] == false) {
final error = LogEntry.putFromJson(entry, overrideExisting, source);
if (error != null) {
errors.add(translate(LocalizationKeys.export_error, args: {"data": entry}));
}
}
}
}
if (data.keys.contains('logEvents')) {
for (var entry in data['logEvents']) {
if (!skipDeleted || entry['deleted'] == false) {
final error = LogEvent.putFromJson(entry, overrideExisting, source);
if (error != null) {
errors.add(translate(LocalizationKeys.export_error, args: {"data": entry}));
}
}
}
}
if (data.keys.contains('logMeals')) {
for (var entry in data['logMeals']) {
if (!skipDeleted || entry['deleted'] == false) {
final error = LogMeal.putFromJson(entry, overrideExisting, source);
if (error != null) {
errors.add(translate(LocalizationKeys.export_error, args: {"data": entry}));
}
}
}
}
if (data.keys.contains('mealCategories')) {
for (var entry in data['mealCategories']) {
if (!skipDeleted || entry['deleted'] == false) {
final error = MealCategory.putFromJson(entry, overrideExisting, source);
if (error != null) {
errors.add(translate(LocalizationKeys.export_error, args: {"data": entry}));
}
}
}
}
if (data.keys.contains('mealPortionTypes')) {
for (var entry in data['mealPortionTypes']) {
if (!skipDeleted || entry['deleted'] == false) {
final error = MealPortionType.putFromJson(entry, overrideExisting, source);
if (error != null) {
errors.add(translate(LocalizationKeys.export_error, args: {"data": entry}));
}
}
}
}
if (data.keys.contains('mealSources')) {
for (var entry in data['mealSources']) {
if (!skipDeleted || entry['deleted'] == false) {
final error = MealSource.putFromJson(entry, overrideExisting, source);
if (error != null) {
errors.add(translate(LocalizationKeys.export_error, args: {"data": entry}));
}
}
}
}
if (data.keys.contains('meals')) {
for (var entry in data['meals']) {
if (!skipDeleted || entry['deleted'] == false) {
final error = Meal.putFromJson(entry, overrideExisting, source);
if (error != null) {
errors.add(translate(LocalizationKeys.export_error, args: {"data": entry}));
}
}
}
}
} else {
// show confirmation dialog because export is newer than last saved timestamp
}
return errors;
}
static Future<File> exportDataToFile(DateTime timestamp) async {
final appDocDir = await getApplicationDocumentsDirectory();
final appDocPath = appDocDir.path;
final DateFormat formatter = DateFormat(DateFormat.YEAR_MONTH_DAY);
final date = DateTime.now();
final file = File('$appDocPath/diameter_${formatter.format(date)}.json');
file.writeAsStringSync(json.encode(appDataToJson(timestamp)));
return file;
}
static Future<void> exportToGoogleDrive() async {
final login = GoogleSignIn.standard(scopes: [drive.DriveApi.driveScope]);
final GoogleSignInAccount? account = await login.signIn();
if (account != null) {
final authenticateClient = GoogleAuthClient(await account.authHeaders);
final driveApi = drive.DriveApi(authenticateClient);
final timestamp = DateTime.now();
var localFile = await DataExport.exportDataToFile(timestamp);
var media = drive.Media(localFile.openRead(), localFile.lengthSync());
final settings = Settings.get();
settings.lastExportTimestamp = timestamp;
Settings.put(settings);
drive.File driveFile = drive.File()..name = 'diameter.json';
await driveApi.files.create(driveFile, uploadMedia: media);
}
}
static Future<void> importFromGoogleDrive() async {
final login = GoogleSignIn.standard(scopes: [drive.DriveApi.driveScope]);
final GoogleSignInAccount? account = await login.signIn();
if (account != null) {
final authenticateClient = GoogleAuthClient(await account.authHeaders);
final driveApi = drive.DriveApi(authenticateClient);
final timestamp = DateTime.now();
var localFile = await DataExport.exportDataToFile(timestamp);
var media = drive.Media(localFile.openRead(), localFile.lengthSync());
final settings = Settings.get();
settings.lastExportTimestamp = timestamp;
Settings.put(settings);
drive.File driveFile = drive.File()..name = 'diameter.json';
await driveApi.files.create(driveFile, uploadMedia: media);
}
}
}
// class DataExportDialog extends StatefulWidget {
// static const String routeName = '/data-export';
// const DataExportDialog({Key? key}) : super(key: key);
// @override
// _DataExportDialogState createState() => _DataExportDialogState();
// }
// class _DataExportDialogState extends State<DataExportDialog> {
// final ScrollController _scrollController = ScrollController();
// bool _isSaving = false;
// @override
// void dispose() {
// _scrollController.dispose();
// super.dispose();
// }
// @override
// void initState() {
// super.initState();
// }
// void onExport(BuildContext context) async {
// setState(() {
// _isSaving = true;
// });
// Navigator.pop(context);
// setState(() {
// _isSaving = false;
// });
// }
// @override
// Widget build(BuildContext context) {
// return AlertDialog(
// content: Column(
// mainAxisSize: MainAxisSize.min,
// crossAxisAlignment: CrossAxisAlignment.center,
// children: const [
// // CheckboxListTile(
// // value: showChart,
// // onChanged: (_) => setState(() => showChart = !showChart),
// // title: const Text('show Chart'),
// // controlAffinity: ListTileControlAffinity.leading,
// // ),
// ],
// ),
// actions: <Widget>[
// TextButton(
// onPressed: () => Navigator.pop(context),
// child: const Text('CANCEL'),
// ),
// ElevatedButton(
// onPressed: !_isSaving ? () => onExport(context) : null,
// child: const Text('EXPORT'),
// ),
// ]);
// }
// }

453
lib/localization_keys.dart Normal file
View File

@ -0,0 +1,453 @@
// ignore_for_file: constant_identifier_names
class LocalizationKeys {
static const String general = "general";
static const String general_apply = "$general.apply";
static const String general_cancel = "$general.cancel";
static const String general_confirm = "$general.confirm";
static const String general_discard = "$general.discard";
static const String general_confirmDiscard = "$general.confirmDiscard";
static const String general_delete = "$general.delete";
static const String general_confirmDelete = "$general.confirmDelete";
static const String general_save = "$general.save";
static const String general_close = "$general.close";
static const String general_saveAndClose = "$general.saveAndClose";
static const String general_saveAsIs = "$general.saveAsIs";
static const String general_keepEditing = "$general.keepEditing";
static const String general_next = "$general.next";
static const String general_edit = "$general.edit";
static const String general_per = "$general.per";
static const String general_example = "$general.example";
static const String general_suffixes = "$general.suffixes";
static const String general_suffixes_units = "$general_suffixes.units";
static const String general_suffixes_carbs = "$general_suffixes.carbs";
static const String general_suffixes_mins = "$general_suffixes.mins";
static const String general_suffixes_carbsPerU = "$general_suffixes.carbsPerU";
static const String general_suffixes_uPerBreadUnit = "$general_suffixes.uPerBreadUnit";
static const String general_suffixes_uPerGlucose = "$general_suffixes.uPerGlucose";
static const String general_suffixes_perDay = "$general_suffixes.perDay";
static const String navigation = "navigation";
static const String navigation_log = "$navigation.log";
static const String navigation_logEvents = "$navigation.logEvents";
static const String navigation_reports = "$navigation.reports";
static const String navigation_meals = "$navigation.meals";
static const String navigation_basalProfiles = "$navigation.basalProfiles";
static const String navigation_bolusProfiles = "$navigation.bolusProfiles";
static const String navigation_categorization = "$navigation.categorization";
static const String navigation_settings = "$navigation.settings";
static const String accuracy = "accuracy";
static const String accuracy_empty = "$accuracy.empty";
static const String accuracy_saved = "$accuracy.saved";
static const String accuracy_saved_1 = "$accuracy_saved.1";
static const String accuracy_saved_else = "$accuracy_saved.else";
static const String accuracy_deleted = "$accuracy.deleted";
static const String accuracy_confirmDelete = "$accuracy.confirmDelete";
static const String accuracy_new = "$accuracy.new";
static const String accuracy_fields = "$accuracy.fields";
static const String accuracy_fields_name = "$accuracy_fields.name";
static const String accuracy_fields_forPortionSize = "$accuracy_fields.forPortionSize";
static const String accuracy_fields_forCarbsRatio = "$accuracy_fields.forCarbsRatio";
static const String accuracy_fields_confidenceRating = "$accuracy_fields.confidenceRating";
static const String accuracy_fields_notes = "$accuracy_fields.notes";
static const String accuracy_fields_validators = "$accuracy_fields.validators";
static const String accuracy_fields_validators_name = "$accuracy_fields_validators.name";
static const String accuracy_title = "$accuracy.title";
static const String accuracy_detail = "$accuracy.detail";
static const String accuracy_detail_title = "$accuracy_detail.title";
static const String basal = "basal";
static const String basal_empty = "$basal.empty";
static const String basal_warnings = "$basal.warnings";
static const String basal_warnings_duplicate = "$basal_warnings.duplicate";
static const String basal_warnings_overlap = "$basal_warnings.overlap";
static const String basal_warnings_startTimeFirst = "$basal_warnings.startTimeFirst";
static const String basal_warnings_gap = "$basal_warnings.gap";
static const String basal_warnings_endTimeLast = "$basal_warnings.endTimeLast";
static const String basal_saved = "$basal.saved";
static const String basal_saved_1 = "$basal_saved.1";
static const String basal_saved_else = "$basal_saved.else";
static const String basal_deleted = "$basal.deleted";
static const String basal_confirmDelete = "$basal.confirmDelete";
static const String basal_new = "$basal.new";
static const String basal_fields = "$basal.fields";
static const String basal_fields_startTime = "$basal_fields.startTime";
static const String basal_fields_endTime = "$basal_fields.endTime";
static const String basal_fields_units = "$basal_fields.units";
static const String basal_title = "$basal.title";
static const String basalProfile = "basalProfile";
static const String basalProfile_empty = "$basalProfile.empty";
static const String basalProfile_warnings = "$basalProfile.warnings";
static const String basalProfile_warnings_noActive = "$basalProfile_warnings.noActive";
static const String basalProfile_warnings_noActiveOnCreate = "$basalProfile_warnings.noActiveOnCreate";
static const String basalProfile_warnings_multipleActive = "$basalProfile_warnings.multipleActive";
static const String basalProfile_warnings_activeAlreadySet = "$basalProfile_warnings.activeAlreadySet";
static const String basalProfile_warnings_resolve = "$basalProfile_warnings.resolve";
static const String basalProfile_warnings_resolve_activate = "$basalProfile_warnings_resolve.activate";
static const String basalProfile_warnings_resolve_activateCurrent = "$basalProfile_warnings_resolve.activateCurrent";
static const String basalProfile_warnings_resolve_create = "$basalProfile_warnings_resolve.create";
static const String basalProfile_warnings_resolve_createInstead = "$basalProfile_warnings_resolve.createInstead";
static const String basalProfile_warnings_resolve_pick = "$basalProfile_warnings_resolve.pick";
static const String basalProfile_warnings_resolve_ignore = "$basalProfile_warnings_resolve.ignore";
static const String basalProfile_warnings_resolve_deactivateProfile = "$basalProfile_warnings_resolve.deactivateProfile";
static const String basalProfile_warnings_resolve_deactivateOthers = "$basalProfile_warnings_resolve.deactivateOthers";
static const String basalProfile_saved = "$basalProfile.saved";
static const String basalProfile_default = "$basalProfile.default";
static const String basalProfile_active = "$basalProfile.active";
static const String basalProfile_copied = "$basalProfile.copied";
static const String basalProfile_copyOf = "$basalProfile.copyOf";
static const String basalProfile_deleted = "$basalProfile.deleted";
static const String basalProfile_confirmDelete = "$basalProfile.confirmDelete";
static const String basalProfile_activated = "$basalProfile.activated";
static const String basalProfile_new = "$basalProfile.new";
static const String basalProfile_fields = "$basalProfile.fields";
static const String basalProfile_fields_name = "$basalProfile_fields.name";
static const String basalProfile_fields_notes = "$basalProfile_fields.notes";
static const String basalProfile_fields_active = "$basalProfile_fields.active";
static const String basalProfile_fields_validators = "$basalProfile_fields.validators";
static const String basalProfile_fields_validators_name = "$basalProfile_fields_validators.name";
static const String basalProfile_title = "$basalProfile.title";
static const String basalProfile_detail = "$basalProfile.detail";
static const String basalProfile_detail_title = "$basalProfile_detail.title";
static const String basalProfile_detail_tabs = "$basalProfile_detail.tabs";
static const String basalProfile_detail_tabs_profile = "$basalProfile_detail_tabs.profile";
static const String basalProfile_detail_tabs_rates = "$basalProfile_detail_tabs.rates";
static const String bolus = "bolus";
static const String bolus_empty = "$bolus.empty";
static const String bolus_warnings = "$bolus.warnings";
static const String bolus_warnings_duplicate = "$bolus_warnings.duplicate";
static const String bolus_warnings_overlap = "$bolus_warnings.overlap";
static const String bolus_warnings_startTimeFirst = "$bolus_warnings.startTimeFirst";
static const String bolus_warnings_gap = "$bolus_warnings.gap";
static const String bolus_warnings_endTimeLast = "$bolus_warnings.endTimeLast";
static const String bolus_saved = "$bolus.saved";
static const String bolus_saved_1 = "$bolus_saved.1";
static const String bolus_saved_else = "$bolus_saved.else";
static const String bolus_deleted = "$bolus.deleted";
static const String bolus_confirmDelete = "$bolus.confirmDelete";
static const String bolus_new = "$bolus.new";
static const String bolus_fields = "$bolus.fields";
static const String bolus_fields_startTime = "$bolus_fields.startTime";
static const String bolus_fields_endTime = "$bolus_fields.endTime";
static const String bolus_fields_units = "$bolus_fields.units";
static const String bolus_fields_perCarbs = "$bolus_fields.perCarbs";
static const String bolus_fields_perGlucose = "$bolus_fields.perGlucose";
static const String bolus_title = "$bolus.title";
static const String bolusProfile = "bolusProfile";
static const String bolusProfile_empty = "$bolusProfile.empty";
static const String bolusProfile_warnings = "$bolusProfile.warnings";
static const String bolusProfile_warnings_noActive = "$bolusProfile_warnings.noActive";
static const String bolusProfile_warnings_noActiveOnCreate = "$bolusProfile_warnings.noActiveOnCreate";
static const String bolusProfile_warnings_multipleActive = "$bolusProfile_warnings.multipleActive";
static const String bolusProfile_warnings_activeAlreadySet = "$bolusProfile_warnings.activeAlreadySet";
static const String bolusProfile_warnings_resolve = "$bolusProfile_warnings.resolve";
static const String bolusProfile_warnings_resolve_activate = "$bolusProfile_warnings_resolve.activate";
static const String bolusProfile_warnings_resolve_activateCurrent = "$bolusProfile_warnings_resolve.activateCurrent";
static const String bolusProfile_warnings_resolve_create = "$bolusProfile_warnings_resolve.create";
static const String bolusProfile_warnings_resolve_createInstead = "$bolusProfile_warnings_resolve.createInstead";
static const String bolusProfile_warnings_resolve_pick = "$bolusProfile_warnings_resolve.pick";
static const String bolusProfile_warnings_resolve_ignore = "$bolusProfile_warnings_resolve.ignore";
static const String bolusProfile_warnings_resolve_deactivateProfile = "$bolusProfile_warnings_resolve.deactivateProfile";
static const String bolusProfile_warnings_resolve_deactivateOthers = "$bolusProfile_warnings_resolve.deactivateOthers";
static const String bolusProfile_saved = "$bolusProfile.saved";
static const String bolusProfile_default = "$bolusProfile.default";
static const String bolusProfile_active = "$bolusProfile.active";
static const String bolusProfile_copied = "$bolusProfile.copied";
static const String bolusProfile_copyOf = "$bolusProfile.copyOf";
static const String bolusProfile_deleted = "$bolusProfile.deleted";
static const String bolusProfile_confirmDelete = "$bolusProfile.confirmDelete";
static const String bolusProfile_activated = "$bolusProfile.activated";
static const String bolusProfile_new = "$bolusProfile.new";
static const String bolusProfile_fields = "$bolusProfile.fields";
static const String bolusProfile_fields_name = "$bolusProfile_fields.name";
static const String bolusProfile_fields_notes = "$bolusProfile_fields.notes";
static const String bolusProfile_fields_active = "$bolusProfile_fields.active";
static const String bolusProfile_fields_validators = "$bolusProfile_fields.validators";
static const String bolusProfile_fields_validators_name = "$bolusProfile_fields_validators.name";
static const String bolusProfile_title = "$bolusProfile.title";
static const String bolusProfile_detail = "$bolusProfile.detail";
static const String bolusProfile_detail_title = "$bolusProfile_detail.title";
static const String bolusProfile_detail_tabs = "$bolusProfile_detail.tabs";
static const String bolusProfile_detail_tabs_profile = "$bolusProfile_detail_tabs.profile";
static const String bolusProfile_detail_tabs_rates = "$bolusProfile_detail_tabs.rates";
static const String categories = "categories";
static const String log = "log";
static const String log_title = "$log.title";
static const String log_empty = "$log.empty";
static const String log_confirmDelete = "$log.confirmDelete";
static const String log_saved = "$log.saved";
static const String log_deleted = "$log.deleted";
static const String log_new = "$log.new";
static const String log_fields = "$log.fields";
static const String log_fields_date = "$log_fields.date";
static const String log_fields_time = "$log_fields.time";
static const String log_fields_glucose = "$log_fields.glucose";
static const String log_fields_notes = "$log_fields.notes";
static const String log_filter = "$log.filter";
static const String log_filter_startDate = "$log_filter.startDate";
static const String log_filter_endDate = "$log_filter.endDate";
static const String log_filter_minGlucose = "$log_filter.minGlucose";
static const String log_filter_maxGlucose = "$log_filter.maxGlucose";
static const String log_filter_meal = "$log_filter.meal";
static const String log_filter_mealNameContains = "$log_filter.mealNameContains";
static const String log_filter_noteContains = "$log_filter.noteContains";
static const String log_detail = "$log.detail";
static const String log_detail_title = "$log_detail.title";
static const String log_detail_tabs = "$log_detail.tabs";
static const String log_detail_tabs_general = "$log_detail_tabs.general";
static const String log_detail_tabs_meal = "$log_detail_tabs.meal";
static const String log_detail_tabs_meal_title = "$log_detail_tabs_meal.title";
static const String log_detail_tabs_meal_saved = "$log_detail_tabs_meal.saved";
static const String log_detail_tabs_meal_new = "$log_detail_tabs_meal.new";
static const String log_detail_tabs_meal_empty = "$log_detail_tabs_meal.empty";
static const String log_detail_tabs_meal_confirmDelete = "$log_detail_tabs_meal.confirmDelete";
static const String log_detail_tabs_meal_deleted = "$log_detail_tabs_meal.deleted";
static const String log_detail_tabs_meal_detail = "$log_detail_tabs_meal.detail";
static const String log_detail_tabs_meal_detail_title = "$log_detail_tabs_meal_detail.title";
static const String log_detail_tabs_meal_detail_additionalFields = "$log_detail_tabs_meal_detail.additionalFields";
static const String log_detail_tabs_meal_detail_fields = "$log_detail_tabs_meal_detail.fields";
static const String log_detail_tabs_meal_detail_fields_meal = "$log_detail_tabs_meal_detail_fields.meal";
static const String log_detail_tabs_meal_detail_fields_name = "$log_detail_tabs_meal_detail_fields.name";
static const String log_detail_tabs_meal_detail_fields_amount = "$log_detail_tabs_meal_detail_fields.amount";
static const String log_detail_tabs_meal_detail_fields_portionSize = "$log_detail_tabs_meal_detail_fields.portionSize";
static const String log_detail_tabs_meal_detail_fields_carbsRatio = "$log_detail_tabs_meal_detail_fields.carbsRatio";
static const String log_detail_tabs_meal_detail_fields_totalCarbs = "$log_detail_tabs_meal_detail_fields.totalCarbs";
static const String log_detail_tabs_meal_detail_fields_setManually = "$log_detail_tabs_meal_detail_fields.setManually";
static const String log_detail_tabs_meal_detail_fields_notes = "$log_detail_tabs_meal_detail_fields.notes";
static const String log_detail_tabs_meal_detail_fields_mealSource = "$log_detail_tabs_meal_detail_fields.mealSource";
static const String log_detail_tabs_meal_detail_fields_mealCategory = "$log_detail_tabs_meal_detail_fields.mealCategory";
static const String log_detail_tabs_meal_detail_fields_mealPortionType = "$log_detail_tabs_meal_detail_fields.mealPortionType";
static const String log_detail_tabs_meal_detail_fields_portionSizeAccuracy = "$log_detail_tabs_meal_detail_fields.portionSizeAccuracy";
static const String log_detail_tabs_meal_detail_fields_carbsRatioAccuracy = "$log_detail_tabs_meal_detail_fields.carbsRatioAccuracy";
static const String log_detail_tabs_meal_detail_fields_validators = "$log_detail_tabs_meal_detail_fields.validators";
static const String log_detail_tabs_meal_detail_fields_validators_name = "$log_detail_tabs_meal_detail_fields_validators.name";
static const String log_detail_tabs_bolus = "$log_detail_tabs.bolus";
static const String log_detail_tabs_bolus_title = "$log_detail_tabs_bolus.title";
static const String log_detail_tabs_bolus_saved = "$log_detail_tabs_bolus.saved";
static const String log_detail_tabs_bolus_new = "$log_detail_tabs_bolus.new";
static const String log_detail_tabs_bolus_deleted = "$log_detail_tabs_bolus.deleted";
static const String log_detail_tabs_bolus_confirmDelete = "$log_detail_tabs_bolus.confirmDelete";
static const String log_detail_tabs_bolus_delayedBy = "$log_detail_tabs_bolus.delayedBy";
static const String log_detail_tabs_bolus_forMeal = "$log_detail_tabs_bolus.forMeal";
static const String log_detail_tabs_bolus_toCorrect = "$log_detail_tabs_bolus.toCorrect";
static const String log_detail_tabs_bolus_empty = "$log_detail_tabs_bolus.empty";
static const String log_detail_tabs_bolus_detail = "$log_detail_tabs_bolus.detail";
static const String log_detail_tabs_bolus_detail_title = "$log_detail_tabs_bolus_detail.title";
static const String log_detail_tabs_bolus_detail_fields = "$log_detail_tabs_bolus_detail.fields";
static const String log_detail_tabs_bolus_detail_fields_units = "$log_detail_tabs_bolus_detail_fields.units";
static const String log_detail_tabs_bolus_detail_fields_setManually = "$log_detail_tabs_bolus_detail_fields.setManually";
static const String log_detail_tabs_bolus_detail_fields_forGlucose = "$log_detail_tabs_bolus_detail_fields.forGlucose";
static const String log_detail_tabs_bolus_detail_fields_forMeal = "$log_detail_tabs_bolus_detail_fields.forMeal";
static const String log_detail_tabs_bolus_detail_fields_current = "$log_detail_tabs_bolus_detail_fields.current";
static const String log_detail_tabs_bolus_detail_fields_target = "$log_detail_tabs_bolus_detail_fields.target";
static const String log_detail_tabs_bolus_detail_fields_correction = "$log_detail_tabs_bolus_detail_fields.correction";
static const String log_detail_tabs_bolus_detail_fields_meal = "$log_detail_tabs_bolus_detail_fields.meal";
static const String log_detail_tabs_bolus_detail_fields_carbs = "$log_detail_tabs_bolus_detail_fields.carbs";
static const String log_detail_tabs_bolus_detail_fields_delayedBolusDuration = "$log_detail_tabs_bolus_detail_fields.delayedBolusDuration";
static const String log_detail_tabs_bolus_detail_fields_immediateBolus = "$log_detail_tabs_bolus_detail_fields.immediateBolus";
static const String log_detail_tabs_bolus_detail_fields_delayedBolus = "$log_detail_tabs_bolus_detail_fields.delayedBolus";
static const String meal = "meal";
static const String meal_title = "$meal.title";
static const String meal_empty = "$meal.empty";
static const String meal_confirmDelete = "$meal.confirmDelete";
static const String meal_saved = "$meal.saved";
static const String meal_deleted = "$meal.deleted";
static const String meal_new = "$meal.new";
static const String meal_fields = "$meal.fields";
static const String meal_fields_name = "$meal_fields.name";
static const String meal_fields_mealSource = "$meal_fields.mealSource";
static const String meal_fields_mealPortionType = "$meal_fields.mealPortionType";
static const String meal_fields_notes = "$meal_fields.notes";
static const String meal_fields_carbsRatio = "$meal_fields.carbsRatio";
static const String meal_fields_setManually = "$meal_fields.setManually";
static const String meal_fields_portionSize = "$meal_fields.portionSize";
static const String meal_fields_carbsPerPortion = "$meal_fields.carbsPerPortion";
static const String meal_fields_delay = "$meal_fields.delay";
static const String meal_fields_delay_title = "$meal_fields_delay.title";
static const String meal_fields_delay_duration = "$meal_fields_delay.duration";
static const String meal_fields_additional = "$meal_fields.additional";
static const String meal_fields_additional_title = "$meal_fields_additional.title";
static const String meal_fields_additional_mealCategory = "$meal_fields_additional.mealCategory";
static const String meal_fields_additional_portionSizeAccuracy = "$meal_fields_additional.portionSizeAccuracy";
static const String meal_fields_additional_carbsRatioAccuracy = "$meal_fields_additional.carbsRatioAccuracy";
static const String meal_fields_validators = "$meal_fields.validators";
static const String meal_fields_validators_name = "$meal_fields_validators.name";
static const String meal_detail = "$meal.detail";
static const String meal_detail_title = "$meal_detail.title";
static const String mealSource = "mealSource";
static const String mealSource_title = "$mealSource.title";
static const String mealSource_empty = "$mealSource.empty";
static const String mealSource_confirmDelete = "$mealSource.confirmDelete";
static const String mealSource_saved = "$mealSource.saved";
static const String mealSource_deleted = "$mealSource.deleted";
static const String mealSource_new = "$mealSource.new";
static const String mealSource_fields = "$mealSource.fields";
static const String mealSource_fields_name = "$mealSource_fields.name";
static const String mealSource_fields_notes = "$mealSource_fields.notes";
static const String mealSource_fields_defaultCarbsRatioAccuracy = "$mealSource_fields.defaultCarbsRatioAccuracy";
static const String mealSource_fields_defaultPortionSizeAccuracy = "$mealSource_fields.defaultPortionSizeAccuracy";
static const String mealSource_fields_defaultMealCategory = "$mealSource_fields.defaultMealCategory";
static const String mealSource_fields_defaultMealPortionType = "$mealSource_fields.defaultMealPortionType";
static const String mealSource_fields_validators = "$mealSource_fields.validators";
static const String mealSource_fields_validators_name = "$mealSource_fields_validators.name";
static const String mealSource_detail = "$mealSource.detail";
static const String mealSource_detail_title = "$mealSource_detail.title";
static const String mealCategory = "mealCategory";
static const String mealCategory_title = "$mealCategory.title";
static const String mealCategory_empty = "$mealCategory.empty";
static const String mealCategory_confirmDelete = "$mealCategory.confirmDelete";
static const String mealCategory_saved = "$mealCategory.saved";
static const String mealCategory_deleted = "$mealCategory.deleted";
static const String mealCategory_new = "$mealCategory.new";
static const String mealCategory_fields = "$mealCategory.fields";
static const String mealCategory_fields_name = "$mealCategory_fields.name";
static const String mealCategory_fields_notes = "$mealCategory_fields.notes";
static const String mealCategory_fields_validators = "$mealCategory_fields.validators";
static const String mealCategory_fields_validators_name = "$mealCategory_fields_validators.name";
static const String mealCategory_detail = "$mealCategory.detail";
static const String mealCategory_detail_title = "$mealCategory_detail.title";
static const String portionType = "portionType";
static const String portionType_title = "$portionType.title";
static const String portionType_empty = "$portionType.empty";
static const String portionType_confirmDelete = "$portionType.confirmDelete";
static const String portionType_saved = "$portionType.saved";
static const String portionType_deleted = "$portionType.deleted";
static const String portionType_new = "$portionType.new";
static const String portionType_fields = "$portionType.fields";
static const String portionType_fields_name = "$portionType_fields.name";
static const String portionType_fields_notes = "$portionType_fields.notes";
static const String portionType_fields_validators = "$portionType_fields.validators";
static const String portionType_fields_validators_name = "$portionType_fields_validators.name";
static const String portionType_detail = "$portionType.detail";
static const String portionType_detail_title = "$portionType_detail.title";
static const String eventType = "eventType";
static const String eventType_title = "$eventType.title";
static const String eventType_empty = "$eventType.empty";
static const String eventType_saved = "$eventType.saved";
static const String eventType_deleted = "$eventType.deleted";
static const String eventType_new = "$eventType.new";
static const String eventType_fields = "$eventType.fields";
static const String eventType_fields_name = "$eventType_fields.name";
static const String eventType_fields_notes = "$eventType_fields.notes";
static const String eventType_fields_hasEndTime = "$eventType_fields.hasEndTime";
static const String eventType_fields_defaultReminderDuration = "$eventType_fields.defaultReminderDuration";
static const String eventType_fields_bolusProfile = "$eventType_fields.bolusProfile";
static const String eventType_fields_basalProfile = "$eventType_fields.basalProfile";
static const String eventType_fields_validators = "$eventType_fields.validators";
static const String eventType_fields_validators_name = "$eventType_fields_validators.name";
static const String eventType_detail = "$eventType.detail";
static const String eventType_detail_title = "$eventType_detail.title";
static const String event = "event";
static const String event_warnings = "$event.warnings";
static const String event_warnings_duplicate = "$event_warnings.duplicate";
static const String event_title = "$event.title";
static const String event_titleActive = "$event.titleAcive";
static const String event_empty = "$event.empty";
static const String event_emptyActive = "$event.emptyActive";
static const String event_saved = "$event.saved";
static const String event_deleted = "$event.deleted";
static const String event_confirmDelete = "$event.confirmDelete";
static const String event_end = "$event.end";
static const String event_ended = "$event.ended";
static const String event_confirmEnd = "$event.confirmEnd";
static const String event_new = "$event.new";
static const String event_fields = "$event.fields";
static const String event_fields_eventType = "$event_fields.eventType";
static const String event_fields_startDate = "$event_fields.startDate";
static const String event_fields_endDate = "$event_fields.endDate";
static const String event_fields_date = "$event_fields.date";
static const String event_fields_startTime = "$event_fields.startTime";
static const String event_fields_endTime = "$event_fields.endTime";
static const String event_fields_time = "$event_fields.time";
static const String event_fields_notes = "$event_fields.notes";
static const String event_fields_hasEndTime = "$event_fields.hasEndTime";
static const String event_fields_reminderDuration = "$event_fields.reminderDuration";
static const String event_fields_bolusProfile = "$event_fields.bolusProfile";
static const String event_fields_basalProfile = "$event_fields.basalProfile";
static const String event_detail = "$event.detail";
static const String event_detail_title = "$event_detail.title";
static const String settings = "settings";
static const String settings_title = "$settings.title";
static const String settings_reset = "$settings.reset";
static const String settings_resetAll = "$settings.resetAll";
static const String settings_confirmReset = "$settings.confirmReset";
static const String settings_updated = "$settings.updated";
static const String settings_sections = "$settings.sections";
static const String settings_sections_measurements = "$settings_sections.measurements";
static const String settings_sections_confirmation = "$settings_sections.confirmation";
static const String settings_sections_dateTimeFormat = "$settings_sections.dateTimeFormat";
static const String settings_fields = "$settings.fields";
static const String settings_fields_nutritionMeasurement = "$settings_fields.nutritionMeasurement";
static const String settings_fields_glucoseMeasurement = "$settings_fields.glucoseMeasurement";
static const String settings_fields_targetGlucose = "$settings_fields.targetGlucose";
static const String settings_fields_insulinIncrement = "$settings_fields.insulinIncrement";
static const String settings_fields_nutritionIncrement = "$settings_fields.nutritionIncrement";
static const String settings_fields_mmolLIncrement = "$settings_fields.mmolLIncrement";
static const String settings_fields_onlyDisplayActive = "$settings_fields.onlyDisplayActive";
static const String settings_fields_displayBothDetail = "$settings_fields.displayBothDetail";
static const String settings_fields_displayBothList = "$settings_fields.displayBothList";
static const String settings_fields_confirmOnCancel = "$settings_fields.confirmOnCancel";
static const String settings_fields_confirmOnDelete = "$settings_fields.confirmOnDelete";
static const String settings_fields_confirmOnEndEvent = "$settings_fields.confirmOnEndEvent";
static const String settings_fields_dateFormat = "$settings_fields.dateFormat";
static const String settings_fields_longDateFormat = "$settings_fields.longDateFormat";
static const String settings_fields_timeFormat = "$settings_fields.timeFormat";
static const String settings_fields_longTimeFormat = "$settings_fields.longTimeFormat";
static const String reports = "reports";
static const String reports_title = "$reports.title";
static const String reports_ids = "$reports.ids";
static const String reports_ids_glucose = "$reports_ids.glucose";
static const String reports_ids_carbs = "$reports_ids.carbs";
static const String reports_ids_basal = "$reports_ids.basal";
static const String reports_ids_bolus = "$reports_ids.bolus";
static const String reports_dailyCharts = "$reports.dailyCharts";
static const String reports_dailyCharts_showChart = "$reports_dailyCharts.showChart";
static const String reports_dailyCharts_showBolus = "$reports_dailyCharts.showBolus";
static const String reports_dailyCharts_showBasal = "$reports_dailyCharts.showBasal";
static const String reports_dailyCharts_showMeals = "$reports_dailyCharts.showMeals";
static const String reports_dailyCharts_empty = "$reports_dailyCharts.empty";
static const String reports_sections = "$reports.sections";
static const String reports_sections_dailyReport = "$reports_sections.dailyReport";
static const String reports_sections_pdfReport = "$reports_sections.pdfReport";
static const String reports_export = "$reports.export";
static const String reports_export_title = "$reports_export.title";
static const String reports_export_singleDate = "$reports_export.singleDate";
static const String reports_export_range = "$reports_export.range";
static const String reports_export_date = "$reports_export.date";
static const String reports_export_endDate = "$reports_export.endDate";
static const String reports_export_showChart = "$reports_export.showChart";
static const String reports_export_showBolus = "$reports_export.showBolus";
static const String reports_export_showBasal = "$reports_export.showBasal";
static const String reports_export_showMeals = "$reports_export.showMeals";
static const String reports_export_showTable = "$reports_export.showTable";
static const String reports_export_export = "$reports_export.export";
static const String reports_export_tableHeaders = "$reports_export.tableHeaders";
static const String reports_export_tableHeaders_time = "$reports_export_tableHeaders.time";
static const String reports_export_tableHeaders_glucose = "$reports_export_tableHeaders.glucose";
static const String reports_export_tableHeaders_bolus = "$reports_export_tableHeaders.bolus";
static const String reports_export_tableHeaders_notes = "$reports_export_tableHeaders.notes";
static const String reports_export_tableHeaders_meals = "$reports_export_tableHeaders.meals";
static const String reports_export_tableHeaders_portionSize = "$reports_export_tableHeaders.portionSize";
static const String reports_export_tableHeaders_carbs = "$reports_export_tableHeaders.carbs";
static const String reports_export_tableHeaders_mealBolus = "$reports_export_tableHeaders.mealBolus";
static const String reports_export_tableHeaders_mealNotes = "$reports_export_tableHeaders.mealNotes";
static const String export = "export";
static const String export_error = "$export.error";
}

View File

@ -25,65 +25,98 @@ import 'package:diameter/screens/meal/meal_list.dart';
import 'package:diameter/screens/reports/export.dart'; import 'package:diameter/screens/reports/export.dart';
import 'package:diameter/screens/reports/reports.dart'; import 'package:diameter/screens/reports/reports.dart';
import 'package:diameter/settings.dart'; import 'package:diameter/settings.dart';
import 'package:diameter/data_export.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:diameter/screens/basal/basal_profile_list.dart'; import 'package:diameter/screens/basal/basal_profile_list.dart';
import 'package:diameter/screens/bolus/bolus_profile_list.dart'; import 'package:diameter/screens/bolus/bolus_profile_list.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:objectbox/objectbox.dart'; import 'package:objectbox/objectbox.dart';
import 'package:flutter_translate/flutter_translate.dart';
late ObjectBox objectBox; late ObjectBox objectBox;
Future<void> main() async { Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
DataExport.exportToGoogleDrive();
objectBox = await ObjectBox.create(); objectBox = await ObjectBox.create();
Sync.isAvailable(); Sync.isAvailable();
SyncClient syncClient = Sync.client( SyncClient syncClient = Sync.client(objectBox.store,
objectBox.store, 'ws://192.168.1.184:9999', SyncCredentials.sharedSecretString(secret));
'ws://192.168.1.184:9999',
SyncCredentials.sharedSecretString(secret)
);
syncClient.start(); syncClient.start();
syncClient.requestUpdates(subscribeForFuturePushes: false); syncClient.requestUpdates(subscribeForFuturePushes: false);
DataExport.exportToGoogleDrive();
var delegate = await LocalizationDelegate.create(
fallbackLocale: 'en_US',
supportedLocales: ['en_US', 'de'],
);
runApp( runApp(
GestureDetector( LocalizedApp(delegate, const App()),
onTap: () => FocusManager.instance.primaryFocus?.unfocus(),
child: MaterialApp(
theme: AppTheme.makeTheme(AppTheme.lightTheme),
darkTheme: AppTheme.makeTheme(AppTheme.darkTheme),
themeMode: Settings.themeMode,
initialRoute: '/',
routes: {
'/': (context) => const LogScreen(),
Routes.log: (context) => const LogScreen(),
Routes.logEntry: (context) => const LogEntryScreen(),
Routes.logEvent: (context) => const LogEventDetailScreen(),
Routes.eventTypes: (context) => const EventTypeListScreen(),
Routes.eventType: (context) => const EventTypeDetailScreen(),
Routes.logEvents: (context) => const LogEventListScreen(),
Routes.reports: (context) => const ReportsOverviewScreen(),
Routes.export: (context) => const ExportDialog(),
Routes.dailyChart: (context) => const DailyChart(),
Routes.meals: (context) => const MealListScreen(),
Routes.meal: (context) => const MealDetailScreen(),
Routes.category: (context) => const CategoryOverviewScreen(),
Routes.mealCategories: (context) => const MealCategoryListScreen(),
Routes.mealCategory: (context) => const MealCategoryDetailScreen(),
Routes.mealPortionTypes: (context) =>
const MealPortionTypeListScreen(),
Routes.mealPortionType: (context) =>
const MealPortionTypeDetailScreen(),
Routes.mealSources: (context) => const MealSourceListScreen(),
Routes.mealSource: (context) => const MealSourceDetailScreen(),
Routes.accuracies: (context) => const AccuracyListScreen(),
Routes.accuracy: (context) => const AccuracyDetailScreen(),
Routes.bolusProfiles: (context) => const BolusProfileListScreen(),
Routes.bolusProfile: (context) => const BolusProfileDetailScreen(),
Routes.basalProfiles: (context) => const BasalProfileListScreen(),
Routes.basalProfile: (context) => const BasalProfileDetailScreen(),
Routes.settings: (context) => const SettingsScreen(),
},
),
),
); );
} }
class App extends StatelessWidget {
const App({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
var localizationDelegate = LocalizedApp.of(context).delegate;
return LocalizationProvider(
state: LocalizationProvider.of(context).state,
child: GestureDetector(
onTap: () => FocusManager.instance.primaryFocus?.unfocus(),
child: MaterialApp(
localizationsDelegates: [
localizationDelegate,
],
supportedLocales: const [
Locale('en', 'US'),
Locale('de', 'DE'),
],
// localizationDelegate.supportedLocales,
locale: localizationDelegate.currentLocale,
theme: AppTheme.makeTheme(AppTheme.lightTheme),
darkTheme: AppTheme.makeTheme(AppTheme.darkTheme),
themeMode: Settings.themeMode,
initialRoute: '/',
routes: {
'/': (context) => const LogScreen(),
Routes.log: (context) => const LogScreen(),
Routes.logEntry: (context) => const LogEntryScreen(),
Routes.logEvent: (context) => const LogEventDetailScreen(),
Routes.eventTypes: (context) => const EventTypeListScreen(),
Routes.eventType: (context) => const EventTypeDetailScreen(),
Routes.logEvents: (context) => const LogEventListScreen(),
Routes.reports: (context) => const ReportsOverviewScreen(),
Routes.export: (context) => const ExportDialog(),
Routes.dailyChart: (context) => const DailyChart(),
Routes.meals: (context) => const MealListScreen(),
Routes.meal: (context) => const MealDetailScreen(),
Routes.category: (context) => const CategoryOverviewScreen(),
Routes.mealCategories: (context) => const MealCategoryListScreen(),
Routes.mealCategory: (context) => const MealCategoryDetailScreen(),
Routes.mealPortionTypes: (context) =>
const MealPortionTypeListScreen(),
Routes.mealPortionType: (context) =>
const MealPortionTypeDetailScreen(),
Routes.mealSources: (context) => const MealSourceListScreen(),
Routes.mealSource: (context) => const MealSourceDetailScreen(),
Routes.accuracies: (context) => const AccuracyListScreen(),
Routes.accuracy: (context) => const AccuracyDetailScreen(),
Routes.bolusProfiles: (context) => const BolusProfileListScreen(),
Routes.bolusProfile: (context) => const BolusProfileDetailScreen(),
Routes.basalProfiles: (context) => const BasalProfileListScreen(),
Routes.basalProfile: (context) => const BasalProfileDetailScreen(),
Routes.settings: (context) => const SettingsScreen(),
},
),
),
);
}
}

View File

@ -10,11 +10,13 @@ class Accuracy {
// properties // properties
int id; int id;
bool deleted; bool deleted;
@Unique()
String value; String value;
bool forCarbsRatio; bool forCarbsRatio;
bool forPortionSize; bool forPortionSize;
int? confidenceRating; int? confidenceRating;
String? notes; String? notes;
String? source;
// constructor // constructor
Accuracy({ Accuracy({
@ -25,6 +27,7 @@ class Accuracy {
this.forPortionSize = false, this.forPortionSize = false,
this.confidenceRating, this.confidenceRating,
this.notes, this.notes,
this.source,
}); });
// methods // methods
@ -60,10 +63,14 @@ class Accuracy {
} }
static void reorder(Accuracy accuracy, int? newPosition) { static void reorder(Accuracy accuracy, int? newPosition) {
QueryBuilder<Accuracy> all = box.query(Accuracy_.deleted.equals(false).and(Accuracy_.id.notEquals(accuracy.id))) QueryBuilder<Accuracy> all = box.query(Accuracy_.deleted
.equals(false)
.and(Accuracy_.id.notEquals(accuracy.id)))
..order(Accuracy_.confidenceRating); ..order(Accuracy_.confidenceRating);
List<Accuracy> accuracies = all.build().find(); List<Accuracy> accuracies = all.build().find();
newPosition == null || newPosition >= accuracies.length ? accuracies.add(accuracy) : accuracies.insert(newPosition, accuracy); newPosition == null || newPosition >= accuracies.length
? accuracies.add(accuracy)
: accuracies.insert(newPosition, accuracy);
box.putMany(accuracies.map((item) { box.putMany(accuracies.map((item) {
item.confidenceRating = accuracies.indexOf(item); item.confidenceRating = accuracies.indexOf(item);
return item; return item;
@ -74,4 +81,31 @@ class Accuracy {
String toString() { String toString() {
return value; return value;
} }
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['deleted'] = deleted;
data['value'] = value;
data['forCarbsRatio'] = forCarbsRatio;
data['forPortionSize'] = forPortionSize;
data['confidenceRating'] = confidenceRating;
data['notes'] = notes;
return data;
}
static String? putFromJson(Map<String, dynamic> json, bool overrideExisting, String? source) {
final accuracy = Accuracy(
id: overrideExisting ? json['id'] : 0,
deleted: json['deleted'],
value: json['value'],
forCarbsRatio: json['forCarbsRatio'],
forPortionSize: json['forPortionSize'],
confidenceRating: json['confidenceRating'],
notes: json['notes'],
source: source,
);
Accuracy.put(accuracy);
return null;
}
} }

View File

@ -18,6 +18,7 @@ class Basal {
@Property(type: PropertyType.date) @Property(type: PropertyType.date)
DateTime endTime; DateTime endTime;
double units; double units;
String? source;
// relations // relations
final basalProfile = ToOne<BasalProfile>(); final basalProfile = ToOne<BasalProfile>();
@ -29,6 +30,7 @@ class Basal {
required this.startTime, required this.startTime,
required this.endTime, required this.endTime,
this.units = 0, this.units = 0,
this.source,
}); });
// methods // methods
@ -43,6 +45,13 @@ class Basal {
} }
} }
static void removeAllForProfile(int id) {
box.putMany(getAllForProfile(id).map((item) {
item.deleted = true;
return item;
}).toList());
}
static List<Basal> getAllForProfile(int id) { static List<Basal> getAllForProfile(int id) {
QueryBuilder<Basal> builder = box.query(Basal_.deleted.equals(false)) QueryBuilder<Basal> builder = box.query(Basal_.deleted.equals(false))
..order(Basal_.startTime); ..order(Basal_.startTime);
@ -94,4 +103,42 @@ class Basal {
String toString() { String toString() {
return DateTimeUtils.displayTime(startTime); return DateTimeUtils.displayTime(startTime);
} }
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['deleted'] = deleted;
data['startTime'] = startTime.toIso8601String();
data['endTime'] = endTime.toIso8601String();
data['units'] = units;
data['basalProfile'] = basalProfile.targetId;
return data;
}
static String? putFromJson(Map<String, dynamic> json, bool overrideExisting, String? source) {
DateTime? startTime = DateTime.tryParse(json['startTime']);
DateTime? endTime = DateTime.tryParse(json['endTime']);
if (startTime == null || endTime == null) {
return startTime == null
? endTime == null
? 'start and end time are missing'
: 'start time is missing'
: 'end time is missing';
}
final basal = Basal(
id: overrideExisting ? json['id'] : 0,
deleted: json['deleted'],
startTime: startTime,
endTime: endTime,
units: json['units'],
source: source,
);
basal.basalProfile.targetId = json['basalProfile'];
Basal.put(basal);
return null;
}
} }

View File

@ -1,4 +1,5 @@
import 'package:diameter/main.dart'; import 'package:diameter/main.dart';
import 'package:diameter/models/basal.dart';
import 'package:diameter/models/log_event.dart'; import 'package:diameter/models/log_event.dart';
import 'package:objectbox/objectbox.dart'; import 'package:objectbox/objectbox.dart';
import 'package:diameter/objectbox.g.dart' show BasalProfile_; import 'package:diameter/objectbox.g.dart' show BasalProfile_;
@ -11,9 +12,11 @@ class BasalProfile {
// properties // properties
int id; int id;
bool deleted; bool deleted;
@Unique()
String name; String name;
bool active; bool active;
String? notes; String? notes;
String? source;
// constructor // constructor
BasalProfile({ BasalProfile({
@ -22,6 +25,7 @@ class BasalProfile {
this.name = '', this.name = '',
this.active = false, this.active = false,
this.notes, this.notes,
this.source,
}); });
// methods // methods
@ -29,7 +33,8 @@ class BasalProfile {
static void put(BasalProfile basalProfile) => box.put(basalProfile); static void put(BasalProfile basalProfile) => box.put(basalProfile);
static List<BasalProfile> getAll() { static List<BasalProfile> getAll() {
QueryBuilder<BasalProfile> all = box.query(BasalProfile_.deleted.equals(false)) QueryBuilder<BasalProfile> all = box
.query(BasalProfile_.deleted.equals(false))
..order(BasalProfile_.name); ..order(BasalProfile_.name);
return all.build().find(); return all.build().find();
} }
@ -38,13 +43,16 @@ class BasalProfile {
final item = box.get(id); final item = box.get(id);
if (item != null) { if (item != null) {
item.deleted = true; item.deleted = true;
Basal.removeAllForProfile(id);
box.put(item); box.put(item);
} }
} }
static int activeCount() { static int activeCount() {
Query<BasalProfile> query = box Query<BasalProfile> query = box
.query(BasalProfile_.active.equals(true) & BasalProfile_.deleted.equals(false)).build(); .query(BasalProfile_.active.equals(true) &
BasalProfile_.deleted.equals(false))
.build();
return query.find().length; return query.find().length;
} }
@ -58,13 +66,15 @@ class BasalProfile {
static BasalProfile? getActive(DateTime? dateTime) { static BasalProfile? getActive(DateTime? dateTime) {
if (dateTime != null) { if (dateTime != null) {
List<LogEvent> activeEvents = LogEvent.getAllActiveForTime(dateTime) List<LogEvent> activeEvents = LogEvent.getAllActiveForTime(dateTime)
.where((event) => event.basalProfile.target != null).toList(); .where((event) => event.basalProfile.target != null)
.toList();
if (activeEvents.length > 1) { if (activeEvents.length > 1) {
final now = DateTime.now(); final now = DateTime.now();
activeEvents = activeEvents = activeEvents
activeEvents.where((item) => !activeEvents.any((other) => .where((item) => !activeEvents.any((other) =>
item.time.isBefore(other.time) || (item.endTime ?? now).isAfter(other.endTime ?? now) item.time.isBefore(other.time) ||
)).toList(); (item.endTime ?? now).isAfter(other.endTime ?? now)))
.toList();
} }
if (activeEvents.length == 1) { if (activeEvents.length == 1) {
return activeEvents.single.basalProfile.target; return activeEvents.single.basalProfile.target;
@ -84,4 +94,28 @@ class BasalProfile {
String toString() { String toString() {
return name; return name;
} }
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['deleted'] = deleted;
data['name'] = name;
data['active'] = active;
data['notes'] = notes;
return data;
}
static String? putFromJson(
Map<String, dynamic> json, bool overrideExisting, String? source) {
final basalProfile = BasalProfile(
id: overrideExisting ? json['id'] : 0,
deleted: json['deleted'] == 'true',
name: json['name'],
active: json['active'] == 'true',
notes: json['notes'],
source: source,
);
BasalProfile.put(basalProfile);
return null;
}
} }

View File

@ -21,6 +21,7 @@ class Bolus {
double carbs; double carbs;
int? mgPerDl; int? mgPerDl;
double? mmolPerL; double? mmolPerL;
String? source;
// relations // relations
final bolusProfile = ToOne<BolusProfile>(); final bolusProfile = ToOne<BolusProfile>();
@ -35,6 +36,7 @@ class Bolus {
this.carbs = 0, this.carbs = 0,
this.mgPerDl, this.mgPerDl,
this.mmolPerL, this.mmolPerL,
this.source,
}); });
// methods // methods
@ -56,6 +58,13 @@ class Bolus {
} }
} }
static void removeAllForProfile(int id) {
box.putMany(getAllForProfile(id).map((item) {
item.deleted = true;
return item;
}).toList());
}
static Bolus? getRateForTime(DateTime? dateTime) { static Bolus? getRateForTime(DateTime? dateTime) {
if (dateTime != null) { if (dateTime != null) {
final bolusProfile = BolusProfile.getActive(dateTime); final bolusProfile = BolusProfile.getActive(dateTime);
@ -81,4 +90,48 @@ class Bolus {
String toString() { String toString() {
return DateTimeUtils.displayTime(startTime); return DateTimeUtils.displayTime(startTime);
} }
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['deleted'] = deleted;
data['startTime'] = startTime.toIso8601String();
data['endTime'] = endTime.toIso8601String();
data['units'] = units;
data['carbs'] = carbs;
data['mgPerDl'] = mgPerDl;
data['mmolPerL'] = mmolPerL;
data['bolusProfile'] = bolusProfile.targetId;
return data;
}
static String? putFromJson(Map<String, dynamic> json, bool overrideExisting, String? source) {
DateTime? startTime = DateTime.tryParse(json['startTime']);
DateTime? endTime = DateTime.tryParse(json['endTime']);
if (startTime == null || endTime == null) {
return startTime == null
? endTime == null
? 'start and end time are missing'
: 'start time is missing'
: 'end time is missing';
}
final bolus = Bolus(
id: overrideExisting ? json['id'] : 0,
deleted: json['deleted'],
startTime: startTime,
endTime: endTime,
units: json['units'],
carbs: json['carbs'],
mgPerDl: json['mgPerDl'],
mmolPerL: json['mmolPerL'],
source: source,
);
bolus.bolusProfile.targetId = json['bolusProfile'];
Bolus.put(bolus);
return null;
}
} }

View File

@ -1,4 +1,5 @@
import 'package:diameter/main.dart'; import 'package:diameter/main.dart';
import 'package:diameter/models/bolus.dart';
import 'package:diameter/models/log_event.dart'; import 'package:diameter/models/log_event.dart';
import 'package:objectbox/objectbox.dart'; import 'package:objectbox/objectbox.dart';
import 'package:diameter/objectbox.g.dart' show BolusProfile_; import 'package:diameter/objectbox.g.dart' show BolusProfile_;
@ -11,9 +12,11 @@ class BolusProfile {
// properties // properties
int id; int id;
bool deleted; bool deleted;
@Unique()
String name; String name;
bool active; bool active;
String? notes; String? notes;
String? source;
// constructor // constructor
BolusProfile({ BolusProfile({
@ -22,6 +25,7 @@ class BolusProfile {
this.name = '', this.name = '',
this.active = false, this.active = false,
this.notes, this.notes,
this.source,
}); });
// methods // methods
@ -38,6 +42,7 @@ class BolusProfile {
final item = box.get(id); final item = box.get(id);
if (item != null) { if (item != null) {
item.deleted = true; item.deleted = true;
Bolus.removeAllForProfile(id);
box.put(item); box.put(item);
} }
} }
@ -87,4 +92,29 @@ class BolusProfile {
String toString() { String toString() {
return name; return name;
} }
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['deleted'] = deleted;
data['name'] = name;
data['active'] = active;
data['notes'] = notes;
return data;
}
static String? putFromJson(
Map<String, dynamic> json, bool overrideExisting, String? source) {
final bolusProfile = BolusProfile(
id: overrideExisting ? json['id'] : 0,
deleted: json['deleted'] == 'true',
name: json['name'],
active: json['active'],
notes: json['notes'],
source: source,
);
BolusProfile.put(bolusProfile);
return null;
}
} }

View File

@ -17,6 +17,7 @@ class GlucoseTarget {
double fromMmolPerL; double fromMmolPerL;
double toMmolPerL; double toMmolPerL;
int color; int color;
String? source;
// constructor // constructor
GlucoseTarget({ GlucoseTarget({
@ -27,6 +28,7 @@ class GlucoseTarget {
required this.fromMmolPerL, required this.fromMmolPerL,
required this.toMmolPerL, required this.toMmolPerL,
required this.color, required this.color,
this.source,
}); });
// methods // methods
@ -44,16 +46,20 @@ class GlucoseTarget {
if (box.getAll().isEmpty) { if (box.getAll().isEmpty) {
reset(); reset();
} }
Condition<GlucoseTarget> condition; Condition<GlucoseTarget> condition;
if (mgPerDl > 0 && if (mgPerDl > 0 &&
(mmolPerL == 0 || (mmolPerL == 0 ||
Settings.glucoseMeasurement == GlucoseMeasurement.mgPerDl)) { Settings.glucoseMeasurement == GlucoseMeasurement.mgPerDl)) {
condition = GlucoseTarget_.fromMgPerDL.lessOrEqual(mgPerDl).and(GlucoseTarget_.toMgPerDl.greaterOrEqual(mgPerDl)); condition = GlucoseTarget_.fromMgPerDL
.lessOrEqual(mgPerDl)
.and(GlucoseTarget_.toMgPerDl.greaterOrEqual(mgPerDl));
} else if (mmolPerL > 0 && } else if (mmolPerL > 0 &&
(mgPerDl == 0 || (mgPerDl == 0 ||
Settings.glucoseMeasurement == GlucoseMeasurement.mmolPerL)) { Settings.glucoseMeasurement == GlucoseMeasurement.mmolPerL)) {
condition = GlucoseTarget_.fromMmolPerL.lessOrEqual(mmolPerL).and(GlucoseTarget_.toMmolPerL.greaterOrEqual(mmolPerL)); condition = GlucoseTarget_.fromMmolPerL
.lessOrEqual(mmolPerL)
.and(GlucoseTarget_.toMmolPerL.greaterOrEqual(mmolPerL));
} else { } else {
return Colors.black; return Colors.black;
} }
@ -119,4 +125,33 @@ class GlucoseTarget {
]; ];
box.putMany(defaultTargets); box.putMany(defaultTargets);
} }
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['deleted'] = deleted;
data['fromMgPerDL'] = fromMgPerDL;
data['toMgPerDl'] = toMgPerDl;
data['fromMmolPerL'] = fromMmolPerL;
data['toMmolPerL'] = toMmolPerL;
data['color'] = color;
return data;
}
static String? putFromJson(Map<String, dynamic> json, bool overrideExisting, String? source) {
final glucoseTarget = GlucoseTarget(
id: overrideExisting ? json['id'] : 0,
deleted: json['deleted'],
fromMgPerDL: json['fromMgPerDL'],
toMgPerDl: json['toMgPerDl'],
fromMmolPerL: json['fromMmolPerL'],
toMmolPerL: json['toMmolPerL'],
color: json['color'],
source: source,
);
GlucoseTarget.put(glucoseTarget);
return null;
}
} }

View File

@ -24,6 +24,7 @@ class LogBolus {
double? mmolPerLCorrection; double? mmolPerLCorrection;
bool setManually; bool setManually;
String? notes; String? notes;
String? source;
// relations // relations
final logEntry = ToOne<LogEntry>(); final logEntry = ToOne<LogEntry>();
@ -45,6 +46,7 @@ class LogBolus {
this.mmolPerLCorrection, this.mmolPerLCorrection,
this.setManually = false, this.setManually = false,
this.notes, this.notes,
this.source,
}); });
// methods // methods
@ -72,8 +74,7 @@ class LogBolus {
} }
static bool bolusForMealExists(int id) { static bool bolusForMealExists(int id) {
QueryBuilder<LogBolus> builder = box.query(LogBolus_.deleted QueryBuilder<LogBolus> builder = box.query(LogBolus_.deleted.equals(false));
.equals(false));
builder.link(LogBolus_.meal, LogMeal_.id.equals(id)); builder.link(LogBolus_.meal, LogMeal_.id.equals(id));
return builder.build().find().isNotEmpty; return builder.build().find().isNotEmpty;
} }
@ -86,8 +87,63 @@ class LogBolus {
} }
} }
static void removeAllForEntry(int id) {
box.putMany(getAllForEntry(id).map((item) {
item.deleted = true;
return item;
}).toList());
}
@override @override
String toString() { String toString() {
return units.toString(); return units.toString();
} }
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['deleted'] = deleted;
data['units'] = units;
data['carbs'] = carbs;
data['delay'] = delay;
data['mgPerDlCurrent'] = mgPerDlCurrent;
data['mgPerDlTarget'] = mgPerDlTarget;
data['mgPerDlCorrection'] = mgPerDlCorrection;
data['mmolPerLCurrent'] = mmolPerLCurrent;
data['mmolPerLTarget'] = mmolPerLTarget;
data['mmolPerLCorrection'] = mmolPerLCorrection;
data['setManually'] = setManually;
data['notes'] = notes;
data['logEntry'] = logEntry.targetId;
data['rate'] = rate.targetId;
data['meal'] = meal.targetId;
return data;
}
static String? putFromJson(Map<String, dynamic> json, bool overrideExisting, String? source) {
final logBolus = LogBolus(
id: overrideExisting ? json['id'] : 0,
deleted: json['deleted'],
units: json['units'],
carbs: json['carbs'],
delay: json['delay'],
mgPerDlCurrent: json['mgPerDlCurrent'],
mgPerDlTarget: json['mgPerDlTarget'],
mgPerDlCorrection: json['mgPerDlCorrection'],
mmolPerLCurrent: json['mmolPerLCurrent'],
mmolPerLTarget: json['mmolPerLTarget'],
mmolPerLCorrection: json['mmolPerLCorrection'],
setManually: json['setManually'],
notes: json['notes'],
source: source,
);
logBolus.logEntry.targetId = json['logEntry'];
logBolus.rate.targetId = json['rate'];
logBolus.meal.targetId = json['meal'];
LogBolus.put(logBolus);
return null;
}
} }

View File

@ -1,5 +1,6 @@
import 'package:diameter/main.dart'; import 'package:diameter/main.dart';
import 'package:diameter/models/log_bolus.dart'; import 'package:diameter/models/log_bolus.dart';
import 'package:diameter/models/log_meal.dart';
import 'package:diameter/models/settings.dart'; import 'package:diameter/models/settings.dart';
import 'package:diameter/utils/date_time_utils.dart'; import 'package:diameter/utils/date_time_utils.dart';
import 'package:objectbox/objectbox.dart'; import 'package:objectbox/objectbox.dart';
@ -19,6 +20,7 @@ class LogEntry {
double? mmolPerL; double? mmolPerL;
double? glucoseTrend; double? glucoseTrend;
String? notes; String? notes;
String? source;
// constructor // constructor
LogEntry({ LogEntry({
@ -29,6 +31,7 @@ class LogEntry {
this.mmolPerL, this.mmolPerL,
this.glucoseTrend, this.glucoseTrend,
this.notes, this.notes,
this.source,
}); });
// methods // methods
@ -40,6 +43,8 @@ class LogEntry {
final item = box.get(id); final item = box.get(id);
if (item != null) { if (item != null) {
item.deleted = true; item.deleted = true;
LogMeal.removeAllForEntry(id);
LogBolus.removeAllForEntry(id);
box.put(item); box.put(item);
} }
} }
@ -63,9 +68,78 @@ class LogEntry {
entry.time.isBefore(endOfDay)); entry.time.isBefore(endOfDay));
}).toList(); }).toList();
} }
static List<LogEntry> getAllByFilter({
DateTime? startDate,
DateTime? endDate,
int? minMgPerDl,
int? maxMgPerDl,
double? minMmolPerL,
double? maxMmolPerL,
int? mealId,
String? mealName,
String? note,
}) {
DateTime start = startDate ?? DateTime(2000, 1, 1);
DateTime end = endDate ?? DateTime.now();
QueryBuilder<LogEntry> builder = box.query(LogEntry_.deleted.equals(false) &
(Settings.glucoseMeasurement == GlucoseMeasurement.mgPerDl
? LogEntry_.mgPerDl.between(
minMgPerDl ?? 0, maxMgPerDl ?? double.maxFinite.toInt())
: LogEntry_.mmolPerL
.between(minMmolPerL ?? 0, maxMmolPerL ?? double.maxFinite)))
..order(LogEntry_.time, flags: Order.descending);
return builder.build().find().where((entry) {
return (note == null || (entry.notes ?? '').contains(note)) &&
(mealId == null ||
LogMeal.getAllForEntry(entry.id)
.any((LogMeal logMeal) => logMeal.meal.targetId == mealId)) &&
(mealName == null ||
LogMeal.getAllForEntry(entry.id).any(
(LogMeal logMeal) => logMeal.value.contains(mealName))) &&
(entry.time.compareTo(start) >= 0 && entry.time.isBefore(end));
}).toList();
}
@override @override
String toString() { String toString() {
return DateTimeUtils.displayDateTime(time); return DateTimeUtils.displayDateTime(time);
} }
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['deleted'] = deleted;
data['time'] = time.toIso8601String();
data['mgPerDl'] = mgPerDl;
data['mmolPerL'] = mmolPerL;
data['glucoseTrend'] = glucoseTrend;
data['notes'] = notes;
return data;
}
static String? putFromJson(
Map<String, dynamic> json, bool overrideExisting, String? source) {
DateTime? time = DateTime.tryParse(json['time']);
if (time == null) {
return 'time is missing';
}
final logEntry = LogEntry(
id: overrideExisting ? json['id'] : 0,
deleted: json['deleted'],
time: time,
mgPerDl: json['mgPerDl'],
mmolPerL: json['mmolPerL'],
glucoseTrend: json['glucoseTrend'],
notes: json['notes'],
source: source,
);
LogEntry.put(logEntry);
return null;
}
} }

View File

@ -20,6 +20,7 @@ class LogEvent {
bool hasEndTime; bool hasEndTime;
int? reminderDuration; int? reminderDuration;
String? notes; String? notes;
String? source;
@Transient() @Transient()
String? title; String? title;
@ -40,6 +41,7 @@ class LogEvent {
this.hasEndTime = false, this.hasEndTime = false,
this.reminderDuration, this.reminderDuration,
this.notes, this.notes,
this.source,
}); });
// methods // methods
@ -143,4 +145,46 @@ class LogEvent {
String toString() { String toString() {
return eventType.target?.value ?? ''; return eventType.target?.value ?? '';
} }
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['deleted'] = deleted;
data['time'] = time.toIso8601String();
data['endTime'] = endTime?.toIso8601String();
data['hasEndTime'] = hasEndTime;
data['reminderDuration'] = reminderDuration;
data['notes'] = notes;
data['eventType'] = eventType.targetId;
data['bolusProfile'] = bolusProfile.targetId;
data['basalProfile'] = basalProfile.targetId;
return data;
}
static String? putFromJson(Map<String, dynamic> json, bool overrideExisting, String? source) {
DateTime? time = DateTime.tryParse(json['time']);
if (time == null) {
return 'time is missing';
}
final logEvent = LogEvent(
id: overrideExisting ? json['id'] : 0,
deleted: json['deleted'],
time: time,
endTime: DateTime.tryParse(json['endTime']),
hasEndTime: json['hasEndTime'],
reminderDuration: json['reminderDuration'],
notes: json['notes'],
source: source,
);
logEvent.eventType.targetId = json['eventType'];
logEvent.bolusProfile.targetId = json['bolusProfile'];
logEvent.basalProfile.targetId = json['basalProfile'];
LogEvent.put(logEvent);
return null;
}
} }

View File

@ -12,10 +12,12 @@ class LogEventType {
// properties // properties
int id; int id;
bool deleted; bool deleted;
@Unique()
String value; String value;
bool hasEndTime; bool hasEndTime;
int? defaultReminderDuration; int? defaultReminderDuration;
String? notes; String? notes;
String? source;
// constructor // constructor
LogEventType({ LogEventType({
@ -25,6 +27,7 @@ class LogEventType {
this.hasEndTime = false, this.hasEndTime = false,
this.defaultReminderDuration, this.defaultReminderDuration,
this.notes, this.notes,
this.source,
}); });
// relations // relations
@ -52,4 +55,32 @@ class LogEventType {
String toString() { String toString() {
return value; return value;
} }
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['deleted'] = deleted;
data['value'] = value;
data['hasEndTime'] = hasEndTime;
data['defaultReminderDuration'] = defaultReminderDuration;
data['notes'] = notes;
return data;
}
static String? putFromJson(Map<String, dynamic> json, bool overrideExisting, String? source) {
final logEventType = LogEventType(
id: overrideExisting ? json['id'] : 0,
deleted: json['deleted'],
value: json['value'],
hasEndTime: json['hasEndTime'],
defaultReminderDuration: json['defaultReminderDuration'],
notes: json['notes'],
source: source,
);
LogEventType.put(logEventType);
return null;
}
} }

View File

@ -22,8 +22,8 @@ class LogMeal {
double? portionSize; double? portionSize;
double? totalCarbs; double? totalCarbs;
String? notes; String? notes;
double? bolus;
double amount; double amount;
String? source;
// relations // relations
final logEntry = ToOne<LogEntry>(); final logEntry = ToOne<LogEntry>();
@ -44,6 +44,7 @@ class LogMeal {
this.portionSize, this.portionSize,
this.totalCarbs, this.totalCarbs,
this.notes, this.notes,
this.source,
}); });
// methods // methods
@ -57,6 +58,13 @@ class LogMeal {
} }
} }
static void removeAllForEntry(int id) {
box.putMany(getAllForEntry(id).map((item) {
item.deleted = true;
return item;
}).toList());
}
static List<LogMeal> getAllForEntry(int id) { static List<LogMeal> getAllForEntry(int id) {
QueryBuilder<LogMeal> builder = box.query(LogMeal_.deleted.equals(false)); QueryBuilder<LogMeal> builder = box.query(LogMeal_.deleted.equals(false));
builder.link(LogMeal_.logEntry, LogEntry_.id.equals(id)); builder.link(LogMeal_.logEntry, LogEntry_.id.equals(id));
@ -86,4 +94,50 @@ class LogMeal {
String toString() { String toString() {
return value; return value;
} }
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['deleted'] = deleted;
data['value'] = value;
data['carbsRatio'] = carbsRatio;
data['portionSize'] = portionSize;
data['totalCarbs'] = totalCarbs;
data['notes'] = notes;
data['amount'] = amount;
data['logEntry'] = logEntry.targetId;
data['meal'] = meal.targetId;
data['mealSource'] = mealSource.targetId;
data['mealCategory'] = mealCategory.targetId;
data['mealPortionType'] = mealPortionType.targetId;
data['portionSizeAccuracy'] = portionSizeAccuracy.targetId;
data['carbsRatioAccuracy'] = carbsRatioAccuracy.targetId;
return data;
}
static String? putFromJson(
Map<String, dynamic> json, bool overrideExisting, String? source) {
final logMeal = LogMeal(
id: overrideExisting ? json['id'] : 0,
deleted: json['deleted'] == 'true',
value: json['value'],
carbsRatio: json['carbsRatio'],
portionSize: json['portionSize'],
totalCarbs: json['totalCarbs'],
amount: json['amount'],
notes: json['notes'],
source: source,
);
logMeal.logEntry.targetId = json['logEntry'];
logMeal.meal.targetId = json['meal'];
logMeal.mealSource.targetId = json['mealSource'];
logMeal.mealCategory.targetId = json['mealCategory'];
logMeal.mealPortionType.targetId = json['mealPortionType'];
logMeal.portionSizeAccuracy.targetId = json['portionSizeAccuracy'];
logMeal.carbsRatioAccuracy.targetId = json['carbsRatioAccuracy'];
LogMeal.put(logMeal);
return null;
}
} }

View File

@ -16,6 +16,7 @@ class Meal {
// properties // properties
int id; int id;
bool deleted; bool deleted;
@Unique()
String value; String value;
double? carbsRatio; double? carbsRatio;
double? portionSize; double? portionSize;
@ -23,6 +24,7 @@ class Meal {
int? delayedBolusDuration; int? delayedBolusDuration;
double? delayedBolusPercentage; double? delayedBolusPercentage;
String? notes; String? notes;
String? source;
// relations // relations
final mealSource = ToOne<MealSource>(); final mealSource = ToOne<MealSource>();
@ -42,6 +44,7 @@ class Meal {
this.delayedBolusDuration, this.delayedBolusDuration,
this.delayedBolusPercentage, this.delayedBolusPercentage,
this.notes, this.notes,
this.source,
}); });
// methods // methods
@ -49,7 +52,7 @@ class Meal {
static void put(Meal meal) => box.put(meal); static void put(Meal meal) => box.put(meal);
static List<Meal> getAll() { static List<Meal> getAll() {
QueryBuilder<Meal> builder = box.query(Meal_.deleted.equals(false))..order(Meal_.value); QueryBuilder<Meal> builder = box.query(Meal_.deleted.equals(false))..order(Meal_.value);
return builder.build().find(); return builder.build().find();
} }
@ -65,4 +68,48 @@ class Meal {
String toString() { String toString() {
return value; return value;
} }
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['deleted'] = deleted;
data['value'] = value;
data['carbsRatio'] = carbsRatio;
data['portionSize'] = portionSize;
data['carbsPerPortion'] = carbsPerPortion;
data['delayedBolusDuration'] = delayedBolusDuration;
data['delayedBolusPercentage'] = delayedBolusPercentage;
data['notes'] = notes;
data['mealSource'] = mealSource.targetId;
data['mealCategory'] = mealCategory.targetId;
data['mealPortionType'] = mealPortionType.targetId;
data['portionSizeAccuracy'] = portionSizeAccuracy.targetId;
data['carbsRatioAccuracy'] = carbsRatioAccuracy.targetId;
return data;
}
static String? putFromJson(
Map<String, dynamic> json, bool overrideExisting, String? source) {
final meal = Meal(
id: overrideExisting ? json['id'] : 0,
deleted: json['deleted'] == 'true',
value: json['value'],
carbsRatio: json['carbsRatio'],
portionSize: json['portionSize'],
carbsPerPortion: json['carbsPerPortion'],
delayedBolusDuration: json['delayedBolusDuration'],
delayedBolusPercentage: json['delayedBolusPercentage'],
notes: json['notes'],
source: source,
);
meal.mealSource.targetId = json['mealSource'];
meal.mealCategory.targetId = json['mealCategory'];
meal.mealPortionType.targetId = json['mealPortionType'];
meal.portionSizeAccuracy.targetId = json['portionSizeAccuracy'];
meal.carbsRatioAccuracy.targetId = json['carbsRatioAccuracy'];
Meal.put(meal);
return null;
}
} }

View File

@ -10,8 +10,10 @@ class MealCategory {
// properties // properties
int id; int id;
bool deleted; bool deleted;
@Unique()
String value; String value;
String? notes; String? notes;
String? source;
// constructor // constructor
MealCategory({ MealCategory({
@ -19,6 +21,7 @@ class MealCategory {
this.deleted = false, this.deleted = false,
this.value = '', this.value = '',
this.notes, this.notes,
this.source,
}); });
// methods // methods
@ -42,4 +45,27 @@ class MealCategory {
String toString() { String toString() {
return value; return value;
} }
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['deleted'] = deleted;
data['value'] = value;
data['notes'] = notes;
return data;
}
static String? putFromJson(
Map<String, dynamic> json, bool overrideExisting, String? source) {
final mealCategory = MealCategory(
id: overrideExisting ? json['id'] : 0,
deleted: json['deleted'] == 'true',
value: json['value'],
notes: json['notes'],
source: source,
);
MealCategory.put(mealCategory);
return null;
}
} }

View File

@ -5,13 +5,16 @@ import 'package:diameter/objectbox.g.dart' show MealPortionType_;
@Entity(uid: 2111511899235985637) @Entity(uid: 2111511899235985637)
@Sync() @Sync()
class MealPortionType { class MealPortionType {
static final Box<MealPortionType> box = objectBox.store.box<MealPortionType>(); static final Box<MealPortionType> box =
objectBox.store.box<MealPortionType>();
// properties // properties
int id; int id;
bool deleted; bool deleted;
@Unique()
String value; String value;
String? notes; String? notes;
String? source;
// constructor // constructor
MealPortionType({ MealPortionType({
@ -19,17 +22,20 @@ class MealPortionType {
this.deleted = false, this.deleted = false,
this.value = '', this.value = '',
this.notes, this.notes,
this.source,
}); });
// methods // methods
static MealPortionType? get(int id) => box.get(id); static MealPortionType? get(int id) => box.get(id);
static void put(MealPortionType mealPortionType) => box.put(mealPortionType); static void put(MealPortionType mealPortionType) => box.put(mealPortionType);
static List<MealPortionType> getAll() { static List<MealPortionType> getAll() {
QueryBuilder<MealPortionType> builder = box.query(MealPortionType_.deleted.equals(false))..order(MealPortionType_.value); QueryBuilder<MealPortionType> builder = box
.query(MealPortionType_.deleted.equals(false))
..order(MealPortionType_.value);
return builder.build().find(); return builder.build().find();
} }
static void remove(int id) { static void remove(int id) {
final item = box.get(id); final item = box.get(id);
if (item != null) { if (item != null) {
@ -42,4 +48,27 @@ class MealPortionType {
String toString() { String toString() {
return value; return value;
} }
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['deleted'] = deleted;
data['value'] = value;
data['notes'] = notes;
return data;
}
static String? putFromJson(
Map<String, dynamic> json, bool overrideExisting, String? source) {
final mealPortionType = MealPortionType(
id: overrideExisting ? json['id'] : 0,
deleted: json['deleted'] == 'true',
value: json['value'],
notes: json['notes'],
source: source,
);
MealPortionType.put(mealPortionType);
return null;
}
} }

View File

@ -13,8 +13,10 @@ class MealSource {
// properties // properties
int id; int id;
bool deleted; bool deleted;
@Unique()
String value; String value;
String? notes; String? notes;
String? source;
// relations // relations
final defaultMealCategory = ToOne<MealCategory>(); final defaultMealCategory = ToOne<MealCategory>();
@ -28,17 +30,19 @@ class MealSource {
this.deleted = false, this.deleted = false,
this.value = '', this.value = '',
this.notes, this.notes,
this.source,
}); });
// methods // methods
static MealSource? get(int id) => box.get(id); static MealSource? get(int id) => box.get(id);
static void put(MealSource mealSource) => box.put(mealSource); static void put(MealSource mealSource) => box.put(mealSource);
static List<MealSource> getAll() { static List<MealSource> getAll() {
QueryBuilder<MealSource> builder = box.query(MealSource_.deleted.equals(false))..order(MealSource_.value); QueryBuilder<MealSource> builder =
box.query(MealSource_.deleted.equals(false))..order(MealSource_.value);
return builder.build().find(); return builder.build().find();
} }
static void remove(int id) { static void remove(int id) {
final item = box.get(id); final item = box.get(id);
if (item != null) { if (item != null) {
@ -51,4 +55,36 @@ class MealSource {
String toString() { String toString() {
return value; return value;
} }
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['deleted'] = deleted;
data['value'] = value;
data['notes'] = notes;
data['defaultMealCategory'] = defaultMealCategory.targetId;
data['defaultMealPortionType'] = defaultMealPortionType.targetId;
data['defaultCarbsRatioAccuracy'] = defaultCarbsRatioAccuracy.targetId;
data['defaultPortionSizeAccuracy'] = defaultPortionSizeAccuracy.targetId;
return data;
}
static String? putFromJson(
Map<String, dynamic> json, bool overrideExisting, String? source) {
final mealSource = MealSource(
id: overrideExisting ? json['id'] : 0,
deleted: json['deleted'] == 'true',
value: json['value'],
notes: json['notes'],
source: source,
);
mealSource.defaultMealCategory.targetId = json['defaultMealCategory'];
mealSource.defaultMealPortionType.targetId = json['defaultMealPortionType'];
mealSource.defaultCarbsRatioAccuracy.targetId = json['defaultCarbsRatioAccuracy'];
mealSource.defaultPortionSizeAccuracy.targetId = json['defaultPortionSizeAccuracy'];
MealSource.put(mealSource);
return null;
}
} }

View File

@ -69,6 +69,8 @@ class Settings {
bool useDarkTheme; bool useDarkTheme;
DateTime? lastExportTimestamp;
// constructor // constructor
Settings({ Settings({
this.id = 0, this.id = 0,
@ -89,6 +91,7 @@ class Settings {
this.targetGlucoseMgPerDl = 100, this.targetGlucoseMgPerDl = 100,
this.targetGlucoseMmolPerL = 5.5, this.targetGlucoseMmolPerL = 5.5,
this.useDarkTheme = false, this.useDarkTheme = false,
this.lastExportTimestamp,
}); });
// methods // methods
@ -101,7 +104,8 @@ class Settings {
static NutritionMeasurement get nutritionMeasurement => static NutritionMeasurement get nutritionMeasurement =>
NutritionMeasurement.values[get().nutritionMeasurementIndex]; NutritionMeasurement.values[get().nutritionMeasurementIndex];
static GlucoseMeasurement get glucoseMeasurement => GlucoseMeasurement.values[get().glucoseMeasurementIndex]; static GlucoseMeasurement get glucoseMeasurement =>
GlucoseMeasurement.values[get().glucoseMeasurementIndex];
static GlucoseDisplayMode get glucoseDisplayMode => static GlucoseDisplayMode get glucoseDisplayMode =>
GlucoseDisplayMode.values[get().glucoseDisplayModeIndex]; GlucoseDisplayMode.values[get().glucoseDisplayModeIndex];
@ -120,10 +124,39 @@ class Settings {
static ThemeMode get themeMode => static ThemeMode get themeMode =>
get().useDarkTheme ? ThemeMode.dark : ThemeMode.light; get().useDarkTheme ? ThemeMode.dark : ThemeMode.light;
static DateTime? get lastExportTimeStamp => get().lastExportTimestamp;
static void put(Settings settings) => box.put(settings); static void put(Settings settings) => box.put(settings);
static void reset() { static void reset() {
box.removeAll(); box.removeAll();
box.put(Settings(useDarkTheme: ThemeMode.system == ThemeMode.dark)); box.put(Settings(useDarkTheme: ThemeMode.system == ThemeMode.dark));
} }
static Map<String, dynamic> toJson() {
Settings settings = get();
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = settings.id;
data['nutritionMeasurementIndex'] = settings.nutritionMeasurementIndex;
data['glucoseDisplayModeIndex'] = settings.glucoseDisplayModeIndex;
data['glucoseMeasurementIndex'] = settings.glucoseMeasurementIndex;
data['targetGlucoseMgPerDl'] = settings.targetGlucoseMgPerDl;
data['targetGlucoseMmolPerL'] = settings.targetGlucoseMmolPerL;
data['insulinIncrements'] = settings.insulinIncrements;
data['nutritionIncrements'] = settings.nutritionIncrements;
data['mmolPerLIncrements'] = settings.mmolPerLIncrements;
data['amountIncrements'] = settings.amountIncrements;
data['dateFormat'] = settings.dateFormat;
data['longDateFormat'] = settings.longDateFormat;
data['timeFormat'] = settings.timeFormat;
data['longTimeFormat'] = settings.longTimeFormat;
data['showConfirmationDialogOnCancel'] =
settings.showConfirmationDialogOnCancel;
data['showConfirmationDialogOnDelete'] =
settings.showConfirmationDialogOnDelete;
data['showConfirmationDialogOnStopEvent'] =
settings.showConfirmationDialogOnStopEvent;
data['useDarkTheme'] = settings.useDarkTheme;
return data;
}
} }

View File

@ -1,12 +1,12 @@
import 'package:diameter/main.dart'; import 'package:diameter/main.dart';
import 'package:diameter/models/meal.dart'; import 'package:diameter/models/meal.dart';
import 'package:diameter/models/recipe.dart'; import 'package:diameter/models/x_recipe.dart';
import 'package:diameter/utils/utils.dart'; import 'package:diameter/utils/utils.dart';
import 'package:objectbox/objectbox.dart'; import 'package:objectbox/objectbox.dart';
import 'package:diameter/objectbox.g.dart' show Ingredient_, Recipe_; import 'package:diameter/objectbox.g.dart' show Ingredient_, Recipe_;
@Entity(uid: 6950311793136068892) // @Entity(uid: 6950311793136068892)
@Sync() // @Sync()
class Ingredient { class Ingredient {
static final Box<Ingredient> box = objectBox.store.box<Ingredient>(); static final Box<Ingredient> box = objectBox.store.box<Ingredient>();

View File

@ -1,12 +1,12 @@
import 'package:diameter/main.dart'; import 'package:diameter/main.dart';
import 'package:diameter/models/ingredient.dart'; import 'package:diameter/models/x_ingredient.dart';
import 'package:diameter/models/meal.dart'; import 'package:diameter/models/meal.dart';
import 'package:diameter/utils/utils.dart'; import 'package:diameter/utils/utils.dart';
import 'package:objectbox/objectbox.dart'; import 'package:objectbox/objectbox.dart';
import 'package:diameter/objectbox.g.dart' show Recipe_; import 'package:diameter/objectbox.g.dart' show Recipe_;
@Entity(uid: 6497942314956341514) // @Entity(uid: 6497942314956341514)
@Sync() // @Sync()
class Recipe { class Recipe {
static final Box<Recipe> box = objectBox.store.box<Recipe>(); static final Box<Recipe> box = objectBox.store.box<Recipe>();

View File

@ -1,8 +1,8 @@
import 'package:diameter/main.dart'; import 'package:diameter/main.dart';
import 'package:objectbox/objectbox.dart'; import 'package:objectbox/objectbox.dart';
@Entity() // @Entity()
@Sync() // @Sync()
class User { class User {
static final Box<User> box = objectBox.store.box<User>(); static final Box<User> box = objectBox.store.box<User>();
// properties // properties

View File

@ -1,3 +1,4 @@
import 'package:diameter/localization_keys.dart';
import 'package:diameter/screens/category/categories.dart'; import 'package:diameter/screens/category/categories.dart';
import 'package:diameter/screens/category/accuracy_detail.dart'; import 'package:diameter/screens/category/accuracy_detail.dart';
import 'package:diameter/screens/category/accuracy_list.dart'; import 'package:diameter/screens/category/accuracy_list.dart';
@ -28,6 +29,7 @@ import 'package:diameter/screens/reports/export.dart';
import 'package:diameter/screens/reports/reports.dart'; import 'package:diameter/screens/reports/reports.dart';
import 'package:diameter/settings.dart'; import 'package:diameter/settings.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
class Routes { class Routes {
static const String basal = BasalDetailScreen.routeName; static const String basal = BasalDetailScreen.routeName;
@ -119,7 +121,7 @@ class _NavigationState extends State<Navigation> {
), ),
), ),
ListTile( ListTile(
title: const Text('Log'), title: Text(translate(LocalizationKeys.navigation_log)),
leading: const Icon(Icons.dashboard), leading: const Icon(Icons.dashboard),
onTap: () { onTap: () {
selectDestination(Routes.log); selectDestination(Routes.log);
@ -127,7 +129,7 @@ class _NavigationState extends State<Navigation> {
selected: widget.currentLocation == Routes.log, selected: widget.currentLocation == Routes.log,
), ),
ListTile( ListTile(
title: const Text('Log Events'), title: Text(translate(LocalizationKeys.navigation_logEvents)),
leading: const Icon(Icons.event), leading: const Icon(Icons.event),
onTap: () { onTap: () {
selectDestination(Routes.logEvents); selectDestination(Routes.logEvents);
@ -135,7 +137,7 @@ class _NavigationState extends State<Navigation> {
selected: widget.currentLocation == Routes.logEvents, selected: widget.currentLocation == Routes.logEvents,
), ),
ListTile( ListTile(
title: const Text('Reports'), title: Text(translate(LocalizationKeys.navigation_reports)),
leading: const Icon(Icons.show_chart), leading: const Icon(Icons.show_chart),
onTap: () { onTap: () {
selectDestination(Routes.reports); selectDestination(Routes.reports);
@ -143,7 +145,7 @@ class _NavigationState extends State<Navigation> {
selected: Routes.reportRoutes.contains(widget.currentLocation), selected: Routes.reportRoutes.contains(widget.currentLocation),
), ),
ListTile( ListTile(
title: const Text('Meals'), title: Text(translate(LocalizationKeys.navigation_meals)),
leading: const Icon(Icons.dinner_dining), leading: const Icon(Icons.dinner_dining),
onTap: () { onTap: () {
selectDestination(Routes.meals); selectDestination(Routes.meals);
@ -151,7 +153,7 @@ class _NavigationState extends State<Navigation> {
selected: Routes.mealRoutes.contains(widget.currentLocation), selected: Routes.mealRoutes.contains(widget.currentLocation),
), ),
ListTile( ListTile(
title: const Text('Basal Profiles'), title: Text(translate(LocalizationKeys.navigation_basalProfiles)),
leading: const Icon(Icons.access_time), leading: const Icon(Icons.access_time),
onTap: () { onTap: () {
selectDestination(Routes.basalProfiles); selectDestination(Routes.basalProfiles);
@ -159,7 +161,7 @@ class _NavigationState extends State<Navigation> {
selected: Routes.basalRoutes.contains(widget.currentLocation), selected: Routes.basalRoutes.contains(widget.currentLocation),
), ),
ListTile( ListTile(
title: const Text('Bolus Profiles'), title: Text(translate(LocalizationKeys.navigation_bolusProfiles)),
leading: const Icon(Icons.medication), leading: const Icon(Icons.medication),
onTap: () { onTap: () {
selectDestination(Routes.bolusProfiles); selectDestination(Routes.bolusProfiles);
@ -167,7 +169,7 @@ class _NavigationState extends State<Navigation> {
selected: Routes.bolusRoutes.contains(widget.currentLocation), selected: Routes.bolusRoutes.contains(widget.currentLocation),
), ),
ListTile( ListTile(
title: const Text('Categorization'), title: Text(translate(LocalizationKeys.navigation_categorization)),
leading: const Icon(Icons.category), leading: const Icon(Icons.category),
onTap: () { onTap: () {
selectDestination(Routes.category); selectDestination(Routes.category);
@ -175,7 +177,7 @@ class _NavigationState extends State<Navigation> {
selected: Routes.categoryRoutes.contains(widget.currentLocation), selected: Routes.categoryRoutes.contains(widget.currentLocation),
), ),
ListTile( ListTile(
title: const Text('Settings'), title: Text(translate(LocalizationKeys.navigation_settings)),
leading: const Icon(Icons.settings), leading: const Icon(Icons.settings),
onTap: () { onTap: () {
selectDestination(Routes.settings); selectDestination(Routes.settings);

View File

@ -5,7 +5,7 @@
"entities": [ "entities": [
{ {
"id": "2:1467758525778521891", "id": "2:1467758525778521891",
"lastPropertyId": "6:3409466778841164684", "lastPropertyId": "7:3194552447464951410",
"name": "Basal", "name": "Basal",
"flags": 2, "flags": 2,
"properties": [ "properties": [
@ -42,13 +42,18 @@
"id": "6:3409466778841164684", "id": "6:3409466778841164684",
"name": "deleted", "name": "deleted",
"type": 1 "type": 1
},
{
"id": "7:3194552447464951410",
"name": "source",
"type": 9
} }
], ],
"relations": [] "relations": []
}, },
{ {
"id": "3:3613736032926903785", "id": "3:3613736032926903785",
"lastPropertyId": "5:8140071977687660397", "lastPropertyId": "6:3153627070507809184",
"name": "BasalProfile", "name": "BasalProfile",
"flags": 2, "flags": 2,
"properties": [ "properties": [
@ -77,13 +82,18 @@
"id": "5:8140071977687660397", "id": "5:8140071977687660397",
"name": "deleted", "name": "deleted",
"type": 1 "type": 1
},
{
"id": "6:3153627070507809184",
"name": "source",
"type": 9
} }
], ],
"relations": [] "relations": []
}, },
{ {
"id": "4:3417770529060202389", "id": "4:3417770529060202389",
"lastPropertyId": "9:7440090146687096977", "lastPropertyId": "10:6138236789530596038",
"name": "Bolus", "name": "Bolus",
"flags": 2, "flags": 2,
"properties": [ "properties": [
@ -135,13 +145,18 @@
"id": "9:7440090146687096977", "id": "9:7440090146687096977",
"name": "deleted", "name": "deleted",
"type": 1 "type": 1
},
{
"id": "10:6138236789530596038",
"name": "source",
"type": 9
} }
], ],
"relations": [] "relations": []
}, },
{ {
"id": "5:8812452529027052317", "id": "5:8812452529027052317",
"lastPropertyId": "5:8082994824481464395", "lastPropertyId": "6:8746199841403936632",
"name": "BolusProfile", "name": "BolusProfile",
"flags": 2, "flags": 2,
"properties": [ "properties": [
@ -170,13 +185,18 @@
"id": "5:8082994824481464395", "id": "5:8082994824481464395",
"name": "deleted", "name": "deleted",
"type": 1 "type": 1
},
{
"id": "6:8746199841403936632",
"name": "source",
"type": 9
} }
], ],
"relations": [] "relations": []
}, },
{ {
"id": "6:752131069307970560", "id": "6:752131069307970560",
"lastPropertyId": "10:2505303363495348118", "lastPropertyId": "11:8454251731096762477",
"name": "LogEntry", "name": "LogEntry",
"flags": 2, "flags": 2,
"properties": [ "properties": [
@ -215,13 +235,18 @@
"id": "10:2505303363495348118", "id": "10:2505303363495348118",
"name": "glucoseTrend", "name": "glucoseTrend",
"type": 8 "type": 8
},
{
"id": "11:8454251731096762477",
"name": "source",
"type": 9
} }
], ],
"relations": [] "relations": []
}, },
{ {
"id": "7:4303325892753185970", "id": "7:4303325892753185970",
"lastPropertyId": "12:3041952167628926163", "lastPropertyId": "13:9206766629540069341",
"name": "LogEvent", "name": "LogEvent",
"flags": 2, "flags": 2,
"properties": [ "properties": [
@ -284,13 +309,18 @@
"id": "12:3041952167628926163", "id": "12:3041952167628926163",
"name": "reminderDuration", "name": "reminderDuration",
"type": 6 "type": 6
},
{
"id": "13:9206766629540069341",
"name": "source",
"type": 9
} }
], ],
"relations": [] "relations": []
}, },
{ {
"id": "8:8362795406595606110", "id": "8:8362795406595606110",
"lastPropertyId": "8:1869014400856897151", "lastPropertyId": "9:7377683740652293217",
"name": "LogEventType", "name": "LogEventType",
"flags": 2, "flags": 2,
"properties": [ "properties": [
@ -340,13 +370,18 @@
"flags": 520, "flags": 520,
"indexId": "28:4563029809754152081", "indexId": "28:4563029809754152081",
"relationTarget": "BasalProfile" "relationTarget": "BasalProfile"
},
{
"id": "9:7377683740652293217",
"name": "source",
"type": 9
} }
], ],
"relations": [] "relations": []
}, },
{ {
"id": "9:411177866700467286", "id": "9:411177866700467286",
"lastPropertyId": "19:8965198821438347033", "lastPropertyId": "20:3296734778930341954",
"name": "LogMeal", "name": "LogMeal",
"flags": 2, "flags": 2,
"properties": [ "properties": [
@ -371,11 +406,6 @@
"name": "portionSize", "name": "portionSize",
"type": 8 "type": 8
}, },
{
"id": "6:8074052538574863399",
"name": "bolus",
"type": 8
},
{ {
"id": "9:1920579694098037947", "id": "9:1920579694098037947",
"name": "notes", "name": "notes",
@ -451,13 +481,18 @@
"id": "19:8965198821438347033", "id": "19:8965198821438347033",
"name": "totalCarbs", "name": "totalCarbs",
"type": 8 "type": 8
},
{
"id": "20:3296734778930341954",
"name": "source",
"type": 9
} }
], ],
"relations": [] "relations": []
}, },
{ {
"id": "10:382130101578692012", "id": "10:382130101578692012",
"lastPropertyId": "15:8283810711091063880", "lastPropertyId": "16:8612850434699732822",
"name": "Meal", "name": "Meal",
"flags": 2, "flags": 2,
"properties": [ "properties": [
@ -546,13 +581,18 @@
"id": "15:8283810711091063880", "id": "15:8283810711091063880",
"name": "delayedBolusPercentage", "name": "delayedBolusPercentage",
"type": 8 "type": 8
},
{
"id": "16:8612850434699732822",
"name": "source",
"type": 9
} }
], ],
"relations": [] "relations": []
}, },
{ {
"id": "11:3158200688796904913", "id": "11:3158200688796904913",
"lastPropertyId": "4:824435977543069541", "lastPropertyId": "5:2221810999445049020",
"name": "MealCategory", "name": "MealCategory",
"flags": 2, "flags": 2,
"properties": [ "properties": [
@ -576,13 +616,18 @@
"id": "4:824435977543069541", "id": "4:824435977543069541",
"name": "deleted", "name": "deleted",
"type": 1 "type": 1
},
{
"id": "5:2221810999445049020",
"name": "source",
"type": 9
} }
], ],
"relations": [] "relations": []
}, },
{ {
"id": "12:2111511899235985637", "id": "12:2111511899235985637",
"lastPropertyId": "4:5680236937391945907", "lastPropertyId": "5:6807468448392267469",
"name": "MealPortionType", "name": "MealPortionType",
"flags": 2, "flags": 2,
"properties": [ "properties": [
@ -606,13 +651,18 @@
"id": "4:5680236937391945907", "id": "4:5680236937391945907",
"name": "deleted", "name": "deleted",
"type": 1 "type": 1
},
{
"id": "5:6807468448392267469",
"name": "source",
"type": 9
} }
], ],
"relations": [] "relations": []
}, },
{ {
"id": "13:1283034494527412242", "id": "13:1283034494527412242",
"lastPropertyId": "8:4547899751779962180", "lastPropertyId": "9:6873017370661053783",
"name": "MealSource", "name": "MealSource",
"flags": 2, "flags": 2,
"properties": [ "properties": [
@ -668,13 +718,18 @@
"id": "8:4547899751779962180", "id": "8:4547899751779962180",
"name": "deleted", "name": "deleted",
"type": 1 "type": 1
},
{
"id": "9:6873017370661053783",
"name": "source",
"type": 9
} }
], ],
"relations": [] "relations": []
}, },
{ {
"id": "14:8033487006694871160", "id": "14:8033487006694871160",
"lastPropertyId": "18:7503231998671134983", "lastPropertyId": "19:3683854670538916324",
"name": "LogBolus", "name": "LogBolus",
"flags": 2, "flags": 2,
"properties": [ "properties": [
@ -767,13 +822,18 @@
"id": "18:7503231998671134983", "id": "18:7503231998671134983",
"name": "mmolPerLCorrection", "name": "mmolPerLCorrection",
"type": 8 "type": 8
},
{
"id": "19:3683854670538916324",
"name": "source",
"type": 9
} }
], ],
"relations": [] "relations": []
}, },
{ {
"id": "15:291512798403320400", "id": "15:291512798403320400",
"lastPropertyId": "7:6675647182186603076", "lastPropertyId": "8:1800866627092978542",
"name": "Accuracy", "name": "Accuracy",
"flags": 2, "flags": 2,
"properties": [ "properties": [
@ -812,13 +872,18 @@
"id": "7:6675647182186603076", "id": "7:6675647182186603076",
"name": "deleted", "name": "deleted",
"type": 1 "type": 1
},
{
"id": "8:1800866627092978542",
"name": "source",
"type": 9
} }
], ],
"relations": [] "relations": []
}, },
{ {
"id": "16:3989341091218179227", "id": "16:3989341091218179227",
"lastPropertyId": "27:3553639710779248831", "lastPropertyId": "28:7196743106880728684",
"name": "Settings", "name": "Settings",
"flags": 2, "flags": 2,
"properties": [ "properties": [
@ -912,13 +977,18 @@
"id": "27:3553639710779248831", "id": "27:3553639710779248831",
"name": "amountIncrements", "name": "amountIncrements",
"type": 8 "type": 8
},
{
"id": "28:7196743106880728684",
"name": "lastExportTimestamp",
"type": 10
} }
], ],
"relations": [] "relations": []
}, },
{ {
"id": "17:5041265995704044399", "id": "17:5041265995704044399",
"lastPropertyId": "7:1333487551279074696", "lastPropertyId": "8:1091046738846336945",
"name": "GlucoseTarget", "name": "GlucoseTarget",
"flags": 2, "flags": 2,
"properties": [ "properties": [
@ -957,6 +1027,11 @@
"id": "7:1333487551279074696", "id": "7:1333487551279074696",
"name": "color", "name": "color",
"type": 6 "type": 6
},
{
"id": "8:1091046738846336945",
"name": "source",
"type": 9
} }
], ],
"relations": [] "relations": []
@ -1125,7 +1200,8 @@
4678123663117222609, 4678123663117222609,
780211923138281722, 780211923138281722,
763575433624979013, 763575433624979013,
1225271130099322691 1225271130099322691,
8074052538574863399
], ],
"retiredRelationUids": [], "retiredRelationUids": [],
"version": 1 "version": 1

View File

@ -19,7 +19,7 @@ import 'models/basal_profile.dart';
import 'models/bolus.dart'; import 'models/bolus.dart';
import 'models/bolus_profile.dart'; import 'models/bolus_profile.dart';
import 'models/glucose_target.dart'; import 'models/glucose_target.dart';
import 'models/ingredient.dart'; import 'models/x_ingredient.dart';
import 'models/log_bolus.dart'; import 'models/log_bolus.dart';
import 'models/log_entry.dart'; import 'models/log_entry.dart';
import 'models/log_event.dart'; import 'models/log_event.dart';
@ -29,9 +29,9 @@ import 'models/meal.dart';
import 'models/meal_category.dart'; import 'models/meal_category.dart';
import 'models/meal_portion_type.dart'; import 'models/meal_portion_type.dart';
import 'models/meal_source.dart'; import 'models/meal_source.dart';
import 'models/recipe.dart'; import 'models/x_recipe.dart';
import 'models/settings.dart'; import 'models/settings.dart';
import 'models/user.dart'; import 'models/x_user.dart';
export 'package:objectbox/objectbox.dart'; // so that callers only have to import this file export 'package:objectbox/objectbox.dart'; // so that callers only have to import this file
@ -39,7 +39,7 @@ final _entities = <ModelEntity>[
ModelEntity( ModelEntity(
id: const IdUid(2, 1467758525778521891), id: const IdUid(2, 1467758525778521891),
name: 'Basal', name: 'Basal',
lastPropertyId: const IdUid(6, 3409466778841164684), lastPropertyId: const IdUid(7, 3194552447464951410),
flags: 2, flags: 2,
properties: <ModelProperty>[ properties: <ModelProperty>[
ModelProperty( ModelProperty(
@ -73,6 +73,11 @@ final _entities = <ModelEntity>[
id: const IdUid(6, 3409466778841164684), id: const IdUid(6, 3409466778841164684),
name: 'deleted', name: 'deleted',
type: 1, type: 1,
flags: 0),
ModelProperty(
id: const IdUid(7, 3194552447464951410),
name: 'source',
type: 9,
flags: 0) flags: 0)
], ],
relations: <ModelRelation>[], relations: <ModelRelation>[],
@ -80,7 +85,7 @@ final _entities = <ModelEntity>[
ModelEntity( ModelEntity(
id: const IdUid(3, 3613736032926903785), id: const IdUid(3, 3613736032926903785),
name: 'BasalProfile', name: 'BasalProfile',
lastPropertyId: const IdUid(5, 8140071977687660397), lastPropertyId: const IdUid(6, 3153627070507809184),
flags: 2, flags: 2,
properties: <ModelProperty>[ properties: <ModelProperty>[
ModelProperty( ModelProperty(
@ -107,6 +112,11 @@ final _entities = <ModelEntity>[
id: const IdUid(5, 8140071977687660397), id: const IdUid(5, 8140071977687660397),
name: 'deleted', name: 'deleted',
type: 1, type: 1,
flags: 0),
ModelProperty(
id: const IdUid(6, 3153627070507809184),
name: 'source',
type: 9,
flags: 0) flags: 0)
], ],
relations: <ModelRelation>[], relations: <ModelRelation>[],
@ -114,7 +124,7 @@ final _entities = <ModelEntity>[
ModelEntity( ModelEntity(
id: const IdUid(4, 3417770529060202389), id: const IdUid(4, 3417770529060202389),
name: 'Bolus', name: 'Bolus',
lastPropertyId: const IdUid(9, 7440090146687096977), lastPropertyId: const IdUid(10, 6138236789530596038),
flags: 2, flags: 2,
properties: <ModelProperty>[ properties: <ModelProperty>[
ModelProperty( ModelProperty(
@ -163,6 +173,11 @@ final _entities = <ModelEntity>[
id: const IdUid(9, 7440090146687096977), id: const IdUid(9, 7440090146687096977),
name: 'deleted', name: 'deleted',
type: 1, type: 1,
flags: 0),
ModelProperty(
id: const IdUid(10, 6138236789530596038),
name: 'source',
type: 9,
flags: 0) flags: 0)
], ],
relations: <ModelRelation>[], relations: <ModelRelation>[],
@ -170,7 +185,7 @@ final _entities = <ModelEntity>[
ModelEntity( ModelEntity(
id: const IdUid(5, 8812452529027052317), id: const IdUid(5, 8812452529027052317),
name: 'BolusProfile', name: 'BolusProfile',
lastPropertyId: const IdUid(5, 8082994824481464395), lastPropertyId: const IdUid(6, 8746199841403936632),
flags: 2, flags: 2,
properties: <ModelProperty>[ properties: <ModelProperty>[
ModelProperty( ModelProperty(
@ -197,6 +212,11 @@ final _entities = <ModelEntity>[
id: const IdUid(5, 8082994824481464395), id: const IdUid(5, 8082994824481464395),
name: 'deleted', name: 'deleted',
type: 1, type: 1,
flags: 0),
ModelProperty(
id: const IdUid(6, 8746199841403936632),
name: 'source',
type: 9,
flags: 0) flags: 0)
], ],
relations: <ModelRelation>[], relations: <ModelRelation>[],
@ -204,7 +224,7 @@ final _entities = <ModelEntity>[
ModelEntity( ModelEntity(
id: const IdUid(6, 752131069307970560), id: const IdUid(6, 752131069307970560),
name: 'LogEntry', name: 'LogEntry',
lastPropertyId: const IdUid(10, 2505303363495348118), lastPropertyId: const IdUid(11, 8454251731096762477),
flags: 2, flags: 2,
properties: <ModelProperty>[ properties: <ModelProperty>[
ModelProperty( ModelProperty(
@ -241,6 +261,11 @@ final _entities = <ModelEntity>[
id: const IdUid(10, 2505303363495348118), id: const IdUid(10, 2505303363495348118),
name: 'glucoseTrend', name: 'glucoseTrend',
type: 8, type: 8,
flags: 0),
ModelProperty(
id: const IdUid(11, 8454251731096762477),
name: 'source',
type: 9,
flags: 0) flags: 0)
], ],
relations: <ModelRelation>[], relations: <ModelRelation>[],
@ -248,7 +273,7 @@ final _entities = <ModelEntity>[
ModelEntity( ModelEntity(
id: const IdUid(7, 4303325892753185970), id: const IdUid(7, 4303325892753185970),
name: 'LogEvent', name: 'LogEvent',
lastPropertyId: const IdUid(12, 3041952167628926163), lastPropertyId: const IdUid(13, 9206766629540069341),
flags: 2, flags: 2,
properties: <ModelProperty>[ properties: <ModelProperty>[
ModelProperty( ModelProperty(
@ -306,6 +331,11 @@ final _entities = <ModelEntity>[
id: const IdUid(12, 3041952167628926163), id: const IdUid(12, 3041952167628926163),
name: 'reminderDuration', name: 'reminderDuration',
type: 6, type: 6,
flags: 0),
ModelProperty(
id: const IdUid(13, 9206766629540069341),
name: 'source',
type: 9,
flags: 0) flags: 0)
], ],
relations: <ModelRelation>[], relations: <ModelRelation>[],
@ -313,7 +343,7 @@ final _entities = <ModelEntity>[
ModelEntity( ModelEntity(
id: const IdUid(8, 8362795406595606110), id: const IdUid(8, 8362795406595606110),
name: 'LogEventType', name: 'LogEventType',
lastPropertyId: const IdUid(8, 1869014400856897151), lastPropertyId: const IdUid(9, 7377683740652293217),
flags: 2, flags: 2,
properties: <ModelProperty>[ properties: <ModelProperty>[
ModelProperty( ModelProperty(
@ -359,14 +389,19 @@ final _entities = <ModelEntity>[
type: 11, type: 11,
flags: 520, flags: 520,
indexId: const IdUid(28, 4563029809754152081), indexId: const IdUid(28, 4563029809754152081),
relationTarget: 'BasalProfile') relationTarget: 'BasalProfile'),
ModelProperty(
id: const IdUid(9, 7377683740652293217),
name: 'source',
type: 9,
flags: 0)
], ],
relations: <ModelRelation>[], relations: <ModelRelation>[],
backlinks: <ModelBacklink>[]), backlinks: <ModelBacklink>[]),
ModelEntity( ModelEntity(
id: const IdUid(9, 411177866700467286), id: const IdUid(9, 411177866700467286),
name: 'LogMeal', name: 'LogMeal',
lastPropertyId: const IdUid(19, 8965198821438347033), lastPropertyId: const IdUid(20, 3296734778930341954),
flags: 2, flags: 2,
properties: <ModelProperty>[ properties: <ModelProperty>[
ModelProperty( ModelProperty(
@ -389,11 +424,6 @@ final _entities = <ModelEntity>[
name: 'portionSize', name: 'portionSize',
type: 8, type: 8,
flags: 0), flags: 0),
ModelProperty(
id: const IdUid(6, 8074052538574863399),
name: 'bolus',
type: 8,
flags: 0),
ModelProperty( ModelProperty(
id: const IdUid(9, 1920579694098037947), id: const IdUid(9, 1920579694098037947),
name: 'notes', name: 'notes',
@ -462,6 +492,11 @@ final _entities = <ModelEntity>[
id: const IdUid(19, 8965198821438347033), id: const IdUid(19, 8965198821438347033),
name: 'totalCarbs', name: 'totalCarbs',
type: 8, type: 8,
flags: 0),
ModelProperty(
id: const IdUid(20, 3296734778930341954),
name: 'source',
type: 9,
flags: 0) flags: 0)
], ],
relations: <ModelRelation>[], relations: <ModelRelation>[],
@ -469,7 +504,7 @@ final _entities = <ModelEntity>[
ModelEntity( ModelEntity(
id: const IdUid(10, 382130101578692012), id: const IdUid(10, 382130101578692012),
name: 'Meal', name: 'Meal',
lastPropertyId: const IdUid(15, 8283810711091063880), lastPropertyId: const IdUid(16, 8612850434699732822),
flags: 2, flags: 2,
properties: <ModelProperty>[ properties: <ModelProperty>[
ModelProperty( ModelProperty(
@ -551,6 +586,11 @@ final _entities = <ModelEntity>[
id: const IdUid(15, 8283810711091063880), id: const IdUid(15, 8283810711091063880),
name: 'delayedBolusPercentage', name: 'delayedBolusPercentage',
type: 8, type: 8,
flags: 0),
ModelProperty(
id: const IdUid(16, 8612850434699732822),
name: 'source',
type: 9,
flags: 0) flags: 0)
], ],
relations: <ModelRelation>[], relations: <ModelRelation>[],
@ -558,7 +598,7 @@ final _entities = <ModelEntity>[
ModelEntity( ModelEntity(
id: const IdUid(11, 3158200688796904913), id: const IdUid(11, 3158200688796904913),
name: 'MealCategory', name: 'MealCategory',
lastPropertyId: const IdUid(4, 824435977543069541), lastPropertyId: const IdUid(5, 2221810999445049020),
flags: 2, flags: 2,
properties: <ModelProperty>[ properties: <ModelProperty>[
ModelProperty( ModelProperty(
@ -580,6 +620,11 @@ final _entities = <ModelEntity>[
id: const IdUid(4, 824435977543069541), id: const IdUid(4, 824435977543069541),
name: 'deleted', name: 'deleted',
type: 1, type: 1,
flags: 0),
ModelProperty(
id: const IdUid(5, 2221810999445049020),
name: 'source',
type: 9,
flags: 0) flags: 0)
], ],
relations: <ModelRelation>[], relations: <ModelRelation>[],
@ -587,7 +632,7 @@ final _entities = <ModelEntity>[
ModelEntity( ModelEntity(
id: const IdUid(12, 2111511899235985637), id: const IdUid(12, 2111511899235985637),
name: 'MealPortionType', name: 'MealPortionType',
lastPropertyId: const IdUid(4, 5680236937391945907), lastPropertyId: const IdUid(5, 6807468448392267469),
flags: 2, flags: 2,
properties: <ModelProperty>[ properties: <ModelProperty>[
ModelProperty( ModelProperty(
@ -609,6 +654,11 @@ final _entities = <ModelEntity>[
id: const IdUid(4, 5680236937391945907), id: const IdUid(4, 5680236937391945907),
name: 'deleted', name: 'deleted',
type: 1, type: 1,
flags: 0),
ModelProperty(
id: const IdUid(5, 6807468448392267469),
name: 'source',
type: 9,
flags: 0) flags: 0)
], ],
relations: <ModelRelation>[], relations: <ModelRelation>[],
@ -616,7 +666,7 @@ final _entities = <ModelEntity>[
ModelEntity( ModelEntity(
id: const IdUid(13, 1283034494527412242), id: const IdUid(13, 1283034494527412242),
name: 'MealSource', name: 'MealSource',
lastPropertyId: const IdUid(8, 4547899751779962180), lastPropertyId: const IdUid(9, 6873017370661053783),
flags: 2, flags: 2,
properties: <ModelProperty>[ properties: <ModelProperty>[
ModelProperty( ModelProperty(
@ -666,6 +716,11 @@ final _entities = <ModelEntity>[
id: const IdUid(8, 4547899751779962180), id: const IdUid(8, 4547899751779962180),
name: 'deleted', name: 'deleted',
type: 1, type: 1,
flags: 0),
ModelProperty(
id: const IdUid(9, 6873017370661053783),
name: 'source',
type: 9,
flags: 0) flags: 0)
], ],
relations: <ModelRelation>[], relations: <ModelRelation>[],
@ -673,7 +728,7 @@ final _entities = <ModelEntity>[
ModelEntity( ModelEntity(
id: const IdUid(14, 8033487006694871160), id: const IdUid(14, 8033487006694871160),
name: 'LogBolus', name: 'LogBolus',
lastPropertyId: const IdUid(18, 7503231998671134983), lastPropertyId: const IdUid(19, 3683854670538916324),
flags: 2, flags: 2,
properties: <ModelProperty>[ properties: <ModelProperty>[
ModelProperty( ModelProperty(
@ -761,6 +816,11 @@ final _entities = <ModelEntity>[
id: const IdUid(18, 7503231998671134983), id: const IdUid(18, 7503231998671134983),
name: 'mmolPerLCorrection', name: 'mmolPerLCorrection',
type: 8, type: 8,
flags: 0),
ModelProperty(
id: const IdUid(19, 3683854670538916324),
name: 'source',
type: 9,
flags: 0) flags: 0)
], ],
relations: <ModelRelation>[], relations: <ModelRelation>[],
@ -768,7 +828,7 @@ final _entities = <ModelEntity>[
ModelEntity( ModelEntity(
id: const IdUid(15, 291512798403320400), id: const IdUid(15, 291512798403320400),
name: 'Accuracy', name: 'Accuracy',
lastPropertyId: const IdUid(7, 6675647182186603076), lastPropertyId: const IdUid(8, 1800866627092978542),
flags: 2, flags: 2,
properties: <ModelProperty>[ properties: <ModelProperty>[
ModelProperty( ModelProperty(
@ -805,6 +865,11 @@ final _entities = <ModelEntity>[
id: const IdUid(7, 6675647182186603076), id: const IdUid(7, 6675647182186603076),
name: 'deleted', name: 'deleted',
type: 1, type: 1,
flags: 0),
ModelProperty(
id: const IdUid(8, 1800866627092978542),
name: 'source',
type: 9,
flags: 0) flags: 0)
], ],
relations: <ModelRelation>[], relations: <ModelRelation>[],
@ -812,7 +877,7 @@ final _entities = <ModelEntity>[
ModelEntity( ModelEntity(
id: const IdUid(16, 3989341091218179227), id: const IdUid(16, 3989341091218179227),
name: 'Settings', name: 'Settings',
lastPropertyId: const IdUid(27, 3553639710779248831), lastPropertyId: const IdUid(28, 7196743106880728684),
flags: 2, flags: 2,
properties: <ModelProperty>[ properties: <ModelProperty>[
ModelProperty( ModelProperty(
@ -904,6 +969,11 @@ final _entities = <ModelEntity>[
id: const IdUid(27, 3553639710779248831), id: const IdUid(27, 3553639710779248831),
name: 'amountIncrements', name: 'amountIncrements',
type: 8, type: 8,
flags: 0),
ModelProperty(
id: const IdUid(28, 7196743106880728684),
name: 'lastExportTimestamp',
type: 10,
flags: 0) flags: 0)
], ],
relations: <ModelRelation>[], relations: <ModelRelation>[],
@ -911,7 +981,7 @@ final _entities = <ModelEntity>[
ModelEntity( ModelEntity(
id: const IdUid(17, 5041265995704044399), id: const IdUid(17, 5041265995704044399),
name: 'GlucoseTarget', name: 'GlucoseTarget',
lastPropertyId: const IdUid(7, 1333487551279074696), lastPropertyId: const IdUid(8, 1091046738846336945),
flags: 2, flags: 2,
properties: <ModelProperty>[ properties: <ModelProperty>[
ModelProperty( ModelProperty(
@ -948,6 +1018,11 @@ final _entities = <ModelEntity>[
id: const IdUid(7, 1333487551279074696), id: const IdUid(7, 1333487551279074696),
name: 'color', name: 'color',
type: 6, type: 6,
flags: 0),
ModelProperty(
id: const IdUid(8, 1091046738846336945),
name: 'source',
type: 9,
flags: 0) flags: 0)
], ],
relations: <ModelRelation>[], relations: <ModelRelation>[],
@ -1124,7 +1199,8 @@ ModelDefinition getObjectBoxModel() {
4678123663117222609, 4678123663117222609,
780211923138281722, 780211923138281722,
763575433624979013, 763575433624979013,
1225271130099322691 1225271130099322691,
8074052538574863399
], ],
retiredRelationUids: const [], retiredRelationUids: const [],
modelVersion: 5, modelVersion: 5,
@ -1141,13 +1217,16 @@ ModelDefinition getObjectBoxModel() {
object.id = id; object.id = id;
}, },
objectToFB: (Basal object, fb.Builder fbb) { objectToFB: (Basal object, fb.Builder fbb) {
fbb.startTable(7); final sourceOffset =
object.source == null ? null : fbb.writeString(object.source!);
fbb.startTable(8);
fbb.addInt64(0, object.id); fbb.addInt64(0, object.id);
fbb.addInt64(1, object.startTime.millisecondsSinceEpoch); fbb.addInt64(1, object.startTime.millisecondsSinceEpoch);
fbb.addInt64(2, object.endTime.millisecondsSinceEpoch); fbb.addInt64(2, object.endTime.millisecondsSinceEpoch);
fbb.addFloat64(3, object.units); fbb.addFloat64(3, object.units);
fbb.addInt64(4, object.basalProfile.targetId); fbb.addInt64(4, object.basalProfile.targetId);
fbb.addBool(5, object.deleted); fbb.addBool(5, object.deleted);
fbb.addOffset(6, sourceOffset);
fbb.finish(fbb.endTable()); fbb.finish(fbb.endTable());
return object.id; return object.id;
}, },
@ -1163,8 +1242,10 @@ ModelDefinition getObjectBoxModel() {
const fb.Int64Reader().vTableGet(buffer, rootOffset, 6, 0)), const fb.Int64Reader().vTableGet(buffer, rootOffset, 6, 0)),
endTime: DateTime.fromMillisecondsSinceEpoch( endTime: DateTime.fromMillisecondsSinceEpoch(
const fb.Int64Reader().vTableGet(buffer, rootOffset, 8, 0)), const fb.Int64Reader().vTableGet(buffer, rootOffset, 8, 0)),
units: const fb.Float64Reader() units:
.vTableGet(buffer, rootOffset, 10, 0)); const fb.Float64Reader().vTableGet(buffer, rootOffset, 10, 0),
source: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 16));
object.basalProfile.targetId = object.basalProfile.targetId =
const fb.Int64Reader().vTableGet(buffer, rootOffset, 12, 0); const fb.Int64Reader().vTableGet(buffer, rootOffset, 12, 0);
object.basalProfile.attach(store); object.basalProfile.attach(store);
@ -1182,12 +1263,15 @@ ModelDefinition getObjectBoxModel() {
final nameOffset = fbb.writeString(object.name); final nameOffset = fbb.writeString(object.name);
final notesOffset = final notesOffset =
object.notes == null ? null : fbb.writeString(object.notes!); object.notes == null ? null : fbb.writeString(object.notes!);
fbb.startTable(6); final sourceOffset =
object.source == null ? null : fbb.writeString(object.source!);
fbb.startTable(7);
fbb.addInt64(0, object.id); fbb.addInt64(0, object.id);
fbb.addOffset(1, nameOffset); fbb.addOffset(1, nameOffset);
fbb.addBool(2, object.active); fbb.addBool(2, object.active);
fbb.addOffset(3, notesOffset); fbb.addOffset(3, notesOffset);
fbb.addBool(4, object.deleted); fbb.addBool(4, object.deleted);
fbb.addOffset(5, sourceOffset);
fbb.finish(fbb.endTable()); fbb.finish(fbb.endTable());
return object.id; return object.id;
}, },
@ -1204,7 +1288,9 @@ ModelDefinition getObjectBoxModel() {
active: active:
const fb.BoolReader().vTableGet(buffer, rootOffset, 8, false), const fb.BoolReader().vTableGet(buffer, rootOffset, 8, false),
notes: const fb.StringReader(asciiOptimization: true) notes: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 10)); .vTableGetNullable(buffer, rootOffset, 10),
source: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 14));
return object; return object;
}), }),
@ -1217,7 +1303,9 @@ ModelDefinition getObjectBoxModel() {
object.id = id; object.id = id;
}, },
objectToFB: (Bolus object, fb.Builder fbb) { objectToFB: (Bolus object, fb.Builder fbb) {
fbb.startTable(10); final sourceOffset =
object.source == null ? null : fbb.writeString(object.source!);
fbb.startTable(11);
fbb.addInt64(0, object.id); fbb.addInt64(0, object.id);
fbb.addInt64(1, object.startTime.millisecondsSinceEpoch); fbb.addInt64(1, object.startTime.millisecondsSinceEpoch);
fbb.addInt64(2, object.endTime.millisecondsSinceEpoch); fbb.addInt64(2, object.endTime.millisecondsSinceEpoch);
@ -1227,6 +1315,7 @@ ModelDefinition getObjectBoxModel() {
fbb.addFloat64(6, object.mmolPerL); fbb.addFloat64(6, object.mmolPerL);
fbb.addInt64(7, object.bolusProfile.targetId); fbb.addInt64(7, object.bolusProfile.targetId);
fbb.addBool(8, object.deleted); fbb.addBool(8, object.deleted);
fbb.addOffset(9, sourceOffset);
fbb.finish(fbb.endTable()); fbb.finish(fbb.endTable());
return object.id; return object.id;
}, },
@ -1249,7 +1338,9 @@ ModelDefinition getObjectBoxModel() {
mgPerDl: const fb.Int64Reader() mgPerDl: const fb.Int64Reader()
.vTableGetNullable(buffer, rootOffset, 14), .vTableGetNullable(buffer, rootOffset, 14),
mmolPerL: const fb.Float64Reader() mmolPerL: const fb.Float64Reader()
.vTableGetNullable(buffer, rootOffset, 16)); .vTableGetNullable(buffer, rootOffset, 16),
source: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 22));
object.bolusProfile.targetId = object.bolusProfile.targetId =
const fb.Int64Reader().vTableGet(buffer, rootOffset, 18, 0); const fb.Int64Reader().vTableGet(buffer, rootOffset, 18, 0);
object.bolusProfile.attach(store); object.bolusProfile.attach(store);
@ -1267,12 +1358,15 @@ ModelDefinition getObjectBoxModel() {
final nameOffset = fbb.writeString(object.name); final nameOffset = fbb.writeString(object.name);
final notesOffset = final notesOffset =
object.notes == null ? null : fbb.writeString(object.notes!); object.notes == null ? null : fbb.writeString(object.notes!);
fbb.startTable(6); final sourceOffset =
object.source == null ? null : fbb.writeString(object.source!);
fbb.startTable(7);
fbb.addInt64(0, object.id); fbb.addInt64(0, object.id);
fbb.addOffset(1, nameOffset); fbb.addOffset(1, nameOffset);
fbb.addBool(2, object.active); fbb.addBool(2, object.active);
fbb.addOffset(3, notesOffset); fbb.addOffset(3, notesOffset);
fbb.addBool(4, object.deleted); fbb.addBool(4, object.deleted);
fbb.addOffset(5, sourceOffset);
fbb.finish(fbb.endTable()); fbb.finish(fbb.endTable());
return object.id; return object.id;
}, },
@ -1289,7 +1383,9 @@ ModelDefinition getObjectBoxModel() {
active: active:
const fb.BoolReader().vTableGet(buffer, rootOffset, 8, false), const fb.BoolReader().vTableGet(buffer, rootOffset, 8, false),
notes: const fb.StringReader(asciiOptimization: true) notes: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 10)); .vTableGetNullable(buffer, rootOffset, 10),
source: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 14));
return object; return object;
}), }),
@ -1304,7 +1400,9 @@ ModelDefinition getObjectBoxModel() {
objectToFB: (LogEntry object, fb.Builder fbb) { objectToFB: (LogEntry object, fb.Builder fbb) {
final notesOffset = final notesOffset =
object.notes == null ? null : fbb.writeString(object.notes!); object.notes == null ? null : fbb.writeString(object.notes!);
fbb.startTable(11); final sourceOffset =
object.source == null ? null : fbb.writeString(object.source!);
fbb.startTable(12);
fbb.addInt64(0, object.id); fbb.addInt64(0, object.id);
fbb.addInt64(1, object.time.millisecondsSinceEpoch); fbb.addInt64(1, object.time.millisecondsSinceEpoch);
fbb.addInt64(2, object.mgPerDl); fbb.addInt64(2, object.mgPerDl);
@ -1312,6 +1410,7 @@ ModelDefinition getObjectBoxModel() {
fbb.addOffset(7, notesOffset); fbb.addOffset(7, notesOffset);
fbb.addBool(8, object.deleted); fbb.addBool(8, object.deleted);
fbb.addFloat64(9, object.glucoseTrend); fbb.addFloat64(9, object.glucoseTrend);
fbb.addOffset(10, sourceOffset);
fbb.finish(fbb.endTable()); fbb.finish(fbb.endTable());
return object.id; return object.id;
}, },
@ -1332,7 +1431,9 @@ ModelDefinition getObjectBoxModel() {
glucoseTrend: const fb.Float64Reader() glucoseTrend: const fb.Float64Reader()
.vTableGetNullable(buffer, rootOffset, 22), .vTableGetNullable(buffer, rootOffset, 22),
notes: const fb.StringReader(asciiOptimization: true) notes: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 18)); .vTableGetNullable(buffer, rootOffset, 18),
source: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 24));
return object; return object;
}), }),
@ -1348,7 +1449,9 @@ ModelDefinition getObjectBoxModel() {
objectToFB: (LogEvent object, fb.Builder fbb) { objectToFB: (LogEvent object, fb.Builder fbb) {
final notesOffset = final notesOffset =
object.notes == null ? null : fbb.writeString(object.notes!); object.notes == null ? null : fbb.writeString(object.notes!);
fbb.startTable(13); final sourceOffset =
object.source == null ? null : fbb.writeString(object.source!);
fbb.startTable(14);
fbb.addInt64(0, object.id); fbb.addInt64(0, object.id);
fbb.addInt64(1, object.time.millisecondsSinceEpoch); fbb.addInt64(1, object.time.millisecondsSinceEpoch);
fbb.addInt64(2, object.endTime?.millisecondsSinceEpoch); fbb.addInt64(2, object.endTime?.millisecondsSinceEpoch);
@ -1359,6 +1462,7 @@ ModelDefinition getObjectBoxModel() {
fbb.addInt64(9, object.bolusProfile.targetId); fbb.addInt64(9, object.bolusProfile.targetId);
fbb.addInt64(10, object.basalProfile.targetId); fbb.addInt64(10, object.basalProfile.targetId);
fbb.addInt64(11, object.reminderDuration); fbb.addInt64(11, object.reminderDuration);
fbb.addOffset(12, sourceOffset);
fbb.finish(fbb.endTable()); fbb.finish(fbb.endTable());
return object.id; return object.id;
}, },
@ -1381,7 +1485,9 @@ ModelDefinition getObjectBoxModel() {
reminderDuration: const fb.Int64Reader() reminderDuration: const fb.Int64Reader()
.vTableGetNullable(buffer, rootOffset, 26), .vTableGetNullable(buffer, rootOffset, 26),
notes: const fb.StringReader(asciiOptimization: true) notes: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 12)); .vTableGetNullable(buffer, rootOffset, 12),
source: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 28));
object.eventType.targetId = object.eventType.targetId =
const fb.Int64Reader().vTableGet(buffer, rootOffset, 18, 0); const fb.Int64Reader().vTableGet(buffer, rootOffset, 18, 0);
object.eventType.attach(store); object.eventType.attach(store);
@ -1406,7 +1512,9 @@ ModelDefinition getObjectBoxModel() {
final valueOffset = fbb.writeString(object.value); final valueOffset = fbb.writeString(object.value);
final notesOffset = final notesOffset =
object.notes == null ? null : fbb.writeString(object.notes!); object.notes == null ? null : fbb.writeString(object.notes!);
fbb.startTable(9); final sourceOffset =
object.source == null ? null : fbb.writeString(object.source!);
fbb.startTable(10);
fbb.addInt64(0, object.id); fbb.addInt64(0, object.id);
fbb.addOffset(1, valueOffset); fbb.addOffset(1, valueOffset);
fbb.addBool(2, object.hasEndTime); fbb.addBool(2, object.hasEndTime);
@ -1415,6 +1523,7 @@ ModelDefinition getObjectBoxModel() {
fbb.addBool(5, object.deleted); fbb.addBool(5, object.deleted);
fbb.addInt64(6, object.bolusProfile.targetId); fbb.addInt64(6, object.bolusProfile.targetId);
fbb.addInt64(7, object.basalProfile.targetId); fbb.addInt64(7, object.basalProfile.targetId);
fbb.addOffset(8, sourceOffset);
fbb.finish(fbb.endTable()); fbb.finish(fbb.endTable());
return object.id; return object.id;
}, },
@ -1433,7 +1542,9 @@ ModelDefinition getObjectBoxModel() {
defaultReminderDuration: const fb.Int64Reader() defaultReminderDuration: const fb.Int64Reader()
.vTableGetNullable(buffer, rootOffset, 10), .vTableGetNullable(buffer, rootOffset, 10),
notes: const fb.StringReader(asciiOptimization: true) notes: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 12)); .vTableGetNullable(buffer, rootOffset, 12),
source: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 20));
object.bolusProfile.targetId = object.bolusProfile.targetId =
const fb.Int64Reader().vTableGet(buffer, rootOffset, 16, 0); const fb.Int64Reader().vTableGet(buffer, rootOffset, 16, 0);
object.bolusProfile.attach(store); object.bolusProfile.attach(store);
@ -1462,12 +1573,13 @@ ModelDefinition getObjectBoxModel() {
final valueOffset = fbb.writeString(object.value); final valueOffset = fbb.writeString(object.value);
final notesOffset = final notesOffset =
object.notes == null ? null : fbb.writeString(object.notes!); object.notes == null ? null : fbb.writeString(object.notes!);
fbb.startTable(20); final sourceOffset =
object.source == null ? null : fbb.writeString(object.source!);
fbb.startTable(21);
fbb.addInt64(0, object.id); fbb.addInt64(0, object.id);
fbb.addOffset(1, valueOffset); fbb.addOffset(1, valueOffset);
fbb.addFloat64(2, object.carbsRatio); fbb.addFloat64(2, object.carbsRatio);
fbb.addFloat64(3, object.portionSize); fbb.addFloat64(3, object.portionSize);
fbb.addFloat64(5, object.bolus);
fbb.addOffset(8, notesOffset); fbb.addOffset(8, notesOffset);
fbb.addInt64(9, object.logEntry.targetId); fbb.addInt64(9, object.logEntry.targetId);
fbb.addInt64(10, object.meal.targetId); fbb.addInt64(10, object.meal.targetId);
@ -1479,6 +1591,7 @@ ModelDefinition getObjectBoxModel() {
fbb.addBool(16, object.deleted); fbb.addBool(16, object.deleted);
fbb.addFloat64(17, object.amount); fbb.addFloat64(17, object.amount);
fbb.addFloat64(18, object.totalCarbs); fbb.addFloat64(18, object.totalCarbs);
fbb.addOffset(19, sourceOffset);
fbb.finish(fbb.endTable()); fbb.finish(fbb.endTable());
return object.id; return object.id;
}, },
@ -1501,9 +1614,9 @@ ModelDefinition getObjectBoxModel() {
totalCarbs: const fb.Float64Reader() totalCarbs: const fb.Float64Reader()
.vTableGetNullable(buffer, rootOffset, 40), .vTableGetNullable(buffer, rootOffset, 40),
notes: const fb.StringReader(asciiOptimization: true) notes: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 20)) .vTableGetNullable(buffer, rootOffset, 20),
..bolus = const fb.Float64Reader() source: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 14); .vTableGetNullable(buffer, rootOffset, 42));
object.logEntry.targetId = object.logEntry.targetId =
const fb.Int64Reader().vTableGet(buffer, rootOffset, 22, 0); const fb.Int64Reader().vTableGet(buffer, rootOffset, 22, 0);
object.logEntry.attach(store); object.logEntry.attach(store);
@ -1545,7 +1658,9 @@ ModelDefinition getObjectBoxModel() {
final valueOffset = fbb.writeString(object.value); final valueOffset = fbb.writeString(object.value);
final notesOffset = final notesOffset =
object.notes == null ? null : fbb.writeString(object.notes!); object.notes == null ? null : fbb.writeString(object.notes!);
fbb.startTable(16); final sourceOffset =
object.source == null ? null : fbb.writeString(object.source!);
fbb.startTable(17);
fbb.addInt64(0, object.id); fbb.addInt64(0, object.id);
fbb.addOffset(1, valueOffset); fbb.addOffset(1, valueOffset);
fbb.addFloat64(2, object.carbsRatio); fbb.addFloat64(2, object.carbsRatio);
@ -1560,6 +1675,7 @@ ModelDefinition getObjectBoxModel() {
fbb.addInt64(12, object.carbsRatioAccuracy.targetId); fbb.addInt64(12, object.carbsRatioAccuracy.targetId);
fbb.addBool(13, object.deleted); fbb.addBool(13, object.deleted);
fbb.addFloat64(14, object.delayedBolusPercentage); fbb.addFloat64(14, object.delayedBolusPercentage);
fbb.addOffset(15, sourceOffset);
fbb.finish(fbb.endTable()); fbb.finish(fbb.endTable());
return object.id; return object.id;
}, },
@ -1584,7 +1700,9 @@ ModelDefinition getObjectBoxModel() {
delayedBolusPercentage: const fb.Float64Reader() delayedBolusPercentage: const fb.Float64Reader()
.vTableGetNullable(buffer, rootOffset, 32), .vTableGetNullable(buffer, rootOffset, 32),
notes: const fb.StringReader(asciiOptimization: true) notes: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 18)); .vTableGetNullable(buffer, rootOffset, 18),
source: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 34));
object.mealSource.targetId = object.mealSource.targetId =
const fb.Int64Reader().vTableGet(buffer, rootOffset, 20, 0); const fb.Int64Reader().vTableGet(buffer, rootOffset, 20, 0);
object.mealSource.attach(store); object.mealSource.attach(store);
@ -1614,11 +1732,14 @@ ModelDefinition getObjectBoxModel() {
final valueOffset = fbb.writeString(object.value); final valueOffset = fbb.writeString(object.value);
final notesOffset = final notesOffset =
object.notes == null ? null : fbb.writeString(object.notes!); object.notes == null ? null : fbb.writeString(object.notes!);
fbb.startTable(5); final sourceOffset =
object.source == null ? null : fbb.writeString(object.source!);
fbb.startTable(6);
fbb.addInt64(0, object.id); fbb.addInt64(0, object.id);
fbb.addOffset(1, valueOffset); fbb.addOffset(1, valueOffset);
fbb.addOffset(2, notesOffset); fbb.addOffset(2, notesOffset);
fbb.addBool(3, object.deleted); fbb.addBool(3, object.deleted);
fbb.addOffset(4, sourceOffset);
fbb.finish(fbb.endTable()); fbb.finish(fbb.endTable());
return object.id; return object.id;
}, },
@ -1633,7 +1754,9 @@ ModelDefinition getObjectBoxModel() {
value: const fb.StringReader(asciiOptimization: true) value: const fb.StringReader(asciiOptimization: true)
.vTableGet(buffer, rootOffset, 6, ''), .vTableGet(buffer, rootOffset, 6, ''),
notes: const fb.StringReader(asciiOptimization: true) notes: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 8)); .vTableGetNullable(buffer, rootOffset, 8),
source: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 12));
return object; return object;
}), }),
@ -1649,11 +1772,14 @@ ModelDefinition getObjectBoxModel() {
final valueOffset = fbb.writeString(object.value); final valueOffset = fbb.writeString(object.value);
final notesOffset = final notesOffset =
object.notes == null ? null : fbb.writeString(object.notes!); object.notes == null ? null : fbb.writeString(object.notes!);
fbb.startTable(5); final sourceOffset =
object.source == null ? null : fbb.writeString(object.source!);
fbb.startTable(6);
fbb.addInt64(0, object.id); fbb.addInt64(0, object.id);
fbb.addOffset(1, valueOffset); fbb.addOffset(1, valueOffset);
fbb.addOffset(2, notesOffset); fbb.addOffset(2, notesOffset);
fbb.addBool(3, object.deleted); fbb.addBool(3, object.deleted);
fbb.addOffset(4, sourceOffset);
fbb.finish(fbb.endTable()); fbb.finish(fbb.endTable());
return object.id; return object.id;
}, },
@ -1668,7 +1794,9 @@ ModelDefinition getObjectBoxModel() {
value: const fb.StringReader(asciiOptimization: true) value: const fb.StringReader(asciiOptimization: true)
.vTableGet(buffer, rootOffset, 6, ''), .vTableGet(buffer, rootOffset, 6, ''),
notes: const fb.StringReader(asciiOptimization: true) notes: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 8)); .vTableGetNullable(buffer, rootOffset, 8),
source: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 12));
return object; return object;
}), }),
@ -1689,7 +1817,9 @@ ModelDefinition getObjectBoxModel() {
final valueOffset = fbb.writeString(object.value); final valueOffset = fbb.writeString(object.value);
final notesOffset = final notesOffset =
object.notes == null ? null : fbb.writeString(object.notes!); object.notes == null ? null : fbb.writeString(object.notes!);
fbb.startTable(9); final sourceOffset =
object.source == null ? null : fbb.writeString(object.source!);
fbb.startTable(10);
fbb.addInt64(0, object.id); fbb.addInt64(0, object.id);
fbb.addOffset(1, valueOffset); fbb.addOffset(1, valueOffset);
fbb.addOffset(2, notesOffset); fbb.addOffset(2, notesOffset);
@ -1698,6 +1828,7 @@ ModelDefinition getObjectBoxModel() {
fbb.addInt64(5, object.defaultCarbsRatioAccuracy.targetId); fbb.addInt64(5, object.defaultCarbsRatioAccuracy.targetId);
fbb.addInt64(6, object.defaultPortionSizeAccuracy.targetId); fbb.addInt64(6, object.defaultPortionSizeAccuracy.targetId);
fbb.addBool(7, object.deleted); fbb.addBool(7, object.deleted);
fbb.addOffset(8, sourceOffset);
fbb.finish(fbb.endTable()); fbb.finish(fbb.endTable());
return object.id; return object.id;
}, },
@ -1712,7 +1843,9 @@ ModelDefinition getObjectBoxModel() {
value: const fb.StringReader(asciiOptimization: true) value: const fb.StringReader(asciiOptimization: true)
.vTableGet(buffer, rootOffset, 6, ''), .vTableGet(buffer, rootOffset, 6, ''),
notes: const fb.StringReader(asciiOptimization: true) notes: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 8)); .vTableGetNullable(buffer, rootOffset, 8),
source: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 20));
object.defaultMealCategory.targetId = object.defaultMealCategory.targetId =
const fb.Int64Reader().vTableGet(buffer, rootOffset, 10, 0); const fb.Int64Reader().vTableGet(buffer, rootOffset, 10, 0);
object.defaultMealCategory.attach(store); object.defaultMealCategory.attach(store);
@ -1739,7 +1872,9 @@ ModelDefinition getObjectBoxModel() {
objectToFB: (LogBolus object, fb.Builder fbb) { objectToFB: (LogBolus object, fb.Builder fbb) {
final notesOffset = final notesOffset =
object.notes == null ? null : fbb.writeString(object.notes!); object.notes == null ? null : fbb.writeString(object.notes!);
fbb.startTable(19); final sourceOffset =
object.source == null ? null : fbb.writeString(object.source!);
fbb.startTable(20);
fbb.addInt64(0, object.id); fbb.addInt64(0, object.id);
fbb.addFloat64(1, object.units); fbb.addFloat64(1, object.units);
fbb.addFloat64(2, object.carbs); fbb.addFloat64(2, object.carbs);
@ -1756,6 +1891,7 @@ ModelDefinition getObjectBoxModel() {
fbb.addFloat64(15, object.mmolPerLCurrent); fbb.addFloat64(15, object.mmolPerLCurrent);
fbb.addFloat64(16, object.mmolPerLTarget); fbb.addFloat64(16, object.mmolPerLTarget);
fbb.addFloat64(17, object.mmolPerLCorrection); fbb.addFloat64(17, object.mmolPerLCorrection);
fbb.addOffset(18, sourceOffset);
fbb.finish(fbb.endTable()); fbb.finish(fbb.endTable());
return object.id; return object.id;
}, },
@ -1788,7 +1924,9 @@ ModelDefinition getObjectBoxModel() {
setManually: const fb.BoolReader() setManually: const fb.BoolReader()
.vTableGet(buffer, rootOffset, 16, false), .vTableGet(buffer, rootOffset, 16, false),
notes: const fb.StringReader(asciiOptimization: true) notes: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 18)); .vTableGetNullable(buffer, rootOffset, 18),
source:
const fb.StringReader(asciiOptimization: true).vTableGetNullable(buffer, rootOffset, 40));
object.logEntry.targetId = object.logEntry.targetId =
const fb.Int64Reader().vTableGet(buffer, rootOffset, 20, 0); const fb.Int64Reader().vTableGet(buffer, rootOffset, 20, 0);
object.logEntry.attach(store); object.logEntry.attach(store);
@ -1812,7 +1950,9 @@ ModelDefinition getObjectBoxModel() {
final valueOffset = fbb.writeString(object.value); final valueOffset = fbb.writeString(object.value);
final notesOffset = final notesOffset =
object.notes == null ? null : fbb.writeString(object.notes!); object.notes == null ? null : fbb.writeString(object.notes!);
fbb.startTable(8); final sourceOffset =
object.source == null ? null : fbb.writeString(object.source!);
fbb.startTable(9);
fbb.addInt64(0, object.id); fbb.addInt64(0, object.id);
fbb.addOffset(1, valueOffset); fbb.addOffset(1, valueOffset);
fbb.addBool(2, object.forCarbsRatio); fbb.addBool(2, object.forCarbsRatio);
@ -1820,6 +1960,7 @@ ModelDefinition getObjectBoxModel() {
fbb.addInt64(4, object.confidenceRating); fbb.addInt64(4, object.confidenceRating);
fbb.addOffset(5, notesOffset); fbb.addOffset(5, notesOffset);
fbb.addBool(6, object.deleted); fbb.addBool(6, object.deleted);
fbb.addOffset(7, sourceOffset);
fbb.finish(fbb.endTable()); fbb.finish(fbb.endTable());
return object.id; return object.id;
}, },
@ -1840,7 +1981,9 @@ ModelDefinition getObjectBoxModel() {
confidenceRating: const fb.Int64Reader() confidenceRating: const fb.Int64Reader()
.vTableGetNullable(buffer, rootOffset, 12), .vTableGetNullable(buffer, rootOffset, 12),
notes: const fb.StringReader(asciiOptimization: true) notes: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 14)); .vTableGetNullable(buffer, rootOffset, 14),
source: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 18));
return object; return object;
}), }),
@ -1861,7 +2004,7 @@ ModelDefinition getObjectBoxModel() {
final longTimeFormatOffset = object.longTimeFormat == null final longTimeFormatOffset = object.longTimeFormat == null
? null ? null
: fbb.writeString(object.longTimeFormat!); : fbb.writeString(object.longTimeFormat!);
fbb.startTable(28); fbb.startTable(29);
fbb.addInt64(0, object.id); fbb.addInt64(0, object.id);
fbb.addOffset(1, dateFormatOffset); fbb.addOffset(1, dateFormatOffset);
fbb.addOffset(2, longDateFormatOffset); fbb.addOffset(2, longDateFormatOffset);
@ -1880,13 +2023,15 @@ ModelDefinition getObjectBoxModel() {
fbb.addFloat64(24, object.nutritionIncrements); fbb.addFloat64(24, object.nutritionIncrements);
fbb.addFloat64(25, object.mmolPerLIncrements); fbb.addFloat64(25, object.mmolPerLIncrements);
fbb.addFloat64(26, object.amountIncrements); fbb.addFloat64(26, object.amountIncrements);
fbb.addInt64(27, object.lastExportTimestamp?.millisecondsSinceEpoch);
fbb.finish(fbb.endTable()); fbb.finish(fbb.endTable());
return object.id; return object.id;
}, },
objectFromFB: (Store store, ByteData fbData) { objectFromFB: (Store store, ByteData fbData) {
final buffer = fb.BufferContext(fbData); final buffer = fb.BufferContext(fbData);
final rootOffset = buffer.derefObject(0); final rootOffset = buffer.derefObject(0);
final lastExportTimestampValue =
const fb.Int64Reader().vTableGetNullable(buffer, rootOffset, 58);
final object = Settings( final object = Settings(
id: const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0), id: const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0),
nutritionMeasurementIndex: nutritionMeasurementIndex:
@ -1916,7 +2061,8 @@ ModelDefinition getObjectBoxModel() {
showConfirmationDialogOnStopEvent: const fb.BoolReader().vTableGet(buffer, rootOffset, 18, false), showConfirmationDialogOnStopEvent: const fb.BoolReader().vTableGet(buffer, rootOffset, 18, false),
targetGlucoseMgPerDl: const fb.Int64Reader().vTableGet(buffer, rootOffset, 44, 0), targetGlucoseMgPerDl: const fb.Int64Reader().vTableGet(buffer, rootOffset, 44, 0),
targetGlucoseMmolPerL: const fb.Float64Reader().vTableGet(buffer, rootOffset, 46, 0), targetGlucoseMmolPerL: const fb.Float64Reader().vTableGet(buffer, rootOffset, 46, 0),
useDarkTheme: const fb.BoolReader().vTableGet(buffer, rootOffset, 48, false)); useDarkTheme: const fb.BoolReader().vTableGet(buffer, rootOffset, 48, false),
lastExportTimestamp: lastExportTimestampValue == null ? null : DateTime.fromMillisecondsSinceEpoch(lastExportTimestampValue));
return object; return object;
}), }),
@ -1929,7 +2075,9 @@ ModelDefinition getObjectBoxModel() {
object.id = id; object.id = id;
}, },
objectToFB: (GlucoseTarget object, fb.Builder fbb) { objectToFB: (GlucoseTarget object, fb.Builder fbb) {
fbb.startTable(8); final sourceOffset =
object.source == null ? null : fbb.writeString(object.source!);
fbb.startTable(9);
fbb.addInt64(0, object.id); fbb.addInt64(0, object.id);
fbb.addBool(1, object.deleted); fbb.addBool(1, object.deleted);
fbb.addInt64(2, object.fromMgPerDL); fbb.addInt64(2, object.fromMgPerDL);
@ -1937,6 +2085,7 @@ ModelDefinition getObjectBoxModel() {
fbb.addFloat64(4, object.fromMmolPerL); fbb.addFloat64(4, object.fromMmolPerL);
fbb.addFloat64(5, object.toMmolPerL); fbb.addFloat64(5, object.toMmolPerL);
fbb.addInt64(6, object.color); fbb.addInt64(6, object.color);
fbb.addOffset(7, sourceOffset);
fbb.finish(fbb.endTable()); fbb.finish(fbb.endTable());
return object.id; return object.id;
}, },
@ -1957,7 +2106,9 @@ ModelDefinition getObjectBoxModel() {
toMmolPerL: toMmolPerL:
const fb.Float64Reader().vTableGet(buffer, rootOffset, 14, 0), const fb.Float64Reader().vTableGet(buffer, rootOffset, 14, 0),
color: color:
const fb.Int64Reader().vTableGet(buffer, rootOffset, 16, 0)); const fb.Int64Reader().vTableGet(buffer, rootOffset, 16, 0),
source: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 18));
return object; return object;
}), }),
@ -2105,6 +2256,9 @@ class Basal_ {
/// see [Basal.deleted] /// see [Basal.deleted]
static final deleted = static final deleted =
QueryBooleanProperty<Basal>(_entities[0].properties[5]); QueryBooleanProperty<Basal>(_entities[0].properties[5]);
/// see [Basal.source]
static final source = QueryStringProperty<Basal>(_entities[0].properties[6]);
} }
/// [BasalProfile] entity fields to define ObjectBox queries. /// [BasalProfile] entity fields to define ObjectBox queries.
@ -2128,6 +2282,10 @@ class BasalProfile_ {
/// see [BasalProfile.deleted] /// see [BasalProfile.deleted]
static final deleted = static final deleted =
QueryBooleanProperty<BasalProfile>(_entities[1].properties[4]); QueryBooleanProperty<BasalProfile>(_entities[1].properties[4]);
/// see [BasalProfile.source]
static final source =
QueryStringProperty<BasalProfile>(_entities[1].properties[5]);
} }
/// [Bolus] entity fields to define ObjectBox queries. /// [Bolus] entity fields to define ObjectBox queries.
@ -2164,6 +2322,9 @@ class Bolus_ {
/// see [Bolus.deleted] /// see [Bolus.deleted]
static final deleted = static final deleted =
QueryBooleanProperty<Bolus>(_entities[2].properties[8]); QueryBooleanProperty<Bolus>(_entities[2].properties[8]);
/// see [Bolus.source]
static final source = QueryStringProperty<Bolus>(_entities[2].properties[9]);
} }
/// [BolusProfile] entity fields to define ObjectBox queries. /// [BolusProfile] entity fields to define ObjectBox queries.
@ -2187,6 +2348,10 @@ class BolusProfile_ {
/// see [BolusProfile.deleted] /// see [BolusProfile.deleted]
static final deleted = static final deleted =
QueryBooleanProperty<BolusProfile>(_entities[3].properties[4]); QueryBooleanProperty<BolusProfile>(_entities[3].properties[4]);
/// see [BolusProfile.source]
static final source =
QueryStringProperty<BolusProfile>(_entities[3].properties[5]);
} }
/// [LogEntry] entity fields to define ObjectBox queries. /// [LogEntry] entity fields to define ObjectBox queries.
@ -2217,6 +2382,10 @@ class LogEntry_ {
/// see [LogEntry.glucoseTrend] /// see [LogEntry.glucoseTrend]
static final glucoseTrend = static final glucoseTrend =
QueryDoubleProperty<LogEntry>(_entities[4].properties[6]); QueryDoubleProperty<LogEntry>(_entities[4].properties[6]);
/// see [LogEntry.source]
static final source =
QueryStringProperty<LogEntry>(_entities[4].properties[7]);
} }
/// [LogEvent] entity fields to define ObjectBox queries. /// [LogEvent] entity fields to define ObjectBox queries.
@ -2259,6 +2428,10 @@ class LogEvent_ {
/// see [LogEvent.reminderDuration] /// see [LogEvent.reminderDuration]
static final reminderDuration = static final reminderDuration =
QueryIntegerProperty<LogEvent>(_entities[5].properties[9]); QueryIntegerProperty<LogEvent>(_entities[5].properties[9]);
/// see [LogEvent.source]
static final source =
QueryStringProperty<LogEvent>(_entities[5].properties[10]);
} }
/// [LogEventType] entity fields to define ObjectBox queries. /// [LogEventType] entity fields to define ObjectBox queries.
@ -2294,6 +2467,10 @@ class LogEventType_ {
/// see [LogEventType.basalProfile] /// see [LogEventType.basalProfile]
static final basalProfile = QueryRelationToOne<LogEventType, BasalProfile>( static final basalProfile = QueryRelationToOne<LogEventType, BasalProfile>(
_entities[6].properties[7]); _entities[6].properties[7]);
/// see [LogEventType.source]
static final source =
QueryStringProperty<LogEventType>(_entities[6].properties[8]);
} }
/// [LogMeal] entity fields to define ObjectBox queries. /// [LogMeal] entity fields to define ObjectBox queries.
@ -2312,51 +2489,52 @@ class LogMeal_ {
static final portionSize = static final portionSize =
QueryDoubleProperty<LogMeal>(_entities[7].properties[3]); QueryDoubleProperty<LogMeal>(_entities[7].properties[3]);
/// see [LogMeal.bolus]
static final bolus = QueryDoubleProperty<LogMeal>(_entities[7].properties[4]);
/// see [LogMeal.notes] /// see [LogMeal.notes]
static final notes = QueryStringProperty<LogMeal>(_entities[7].properties[5]); static final notes = QueryStringProperty<LogMeal>(_entities[7].properties[4]);
/// see [LogMeal.logEntry] /// see [LogMeal.logEntry]
static final logEntry = static final logEntry =
QueryRelationToOne<LogMeal, LogEntry>(_entities[7].properties[6]); QueryRelationToOne<LogMeal, LogEntry>(_entities[7].properties[5]);
/// see [LogMeal.meal] /// see [LogMeal.meal]
static final meal = static final meal =
QueryRelationToOne<LogMeal, Meal>(_entities[7].properties[7]); QueryRelationToOne<LogMeal, Meal>(_entities[7].properties[6]);
/// see [LogMeal.mealSource] /// see [LogMeal.mealSource]
static final mealSource = static final mealSource =
QueryRelationToOne<LogMeal, MealSource>(_entities[7].properties[8]); QueryRelationToOne<LogMeal, MealSource>(_entities[7].properties[7]);
/// see [LogMeal.mealCategory] /// see [LogMeal.mealCategory]
static final mealCategory = static final mealCategory =
QueryRelationToOne<LogMeal, MealCategory>(_entities[7].properties[9]); QueryRelationToOne<LogMeal, MealCategory>(_entities[7].properties[8]);
/// see [LogMeal.mealPortionType] /// see [LogMeal.mealPortionType]
static final mealPortionType = static final mealPortionType =
QueryRelationToOne<LogMeal, MealPortionType>(_entities[7].properties[10]); QueryRelationToOne<LogMeal, MealPortionType>(_entities[7].properties[9]);
/// see [LogMeal.portionSizeAccuracy] /// see [LogMeal.portionSizeAccuracy]
static final portionSizeAccuracy = static final portionSizeAccuracy =
QueryRelationToOne<LogMeal, Accuracy>(_entities[7].properties[11]); QueryRelationToOne<LogMeal, Accuracy>(_entities[7].properties[10]);
/// see [LogMeal.carbsRatioAccuracy] /// see [LogMeal.carbsRatioAccuracy]
static final carbsRatioAccuracy = static final carbsRatioAccuracy =
QueryRelationToOne<LogMeal, Accuracy>(_entities[7].properties[12]); QueryRelationToOne<LogMeal, Accuracy>(_entities[7].properties[11]);
/// see [LogMeal.deleted] /// see [LogMeal.deleted]
static final deleted = static final deleted =
QueryBooleanProperty<LogMeal>(_entities[7].properties[13]); QueryBooleanProperty<LogMeal>(_entities[7].properties[12]);
/// see [LogMeal.amount] /// see [LogMeal.amount]
static final amount = static final amount =
QueryDoubleProperty<LogMeal>(_entities[7].properties[14]); QueryDoubleProperty<LogMeal>(_entities[7].properties[13]);
/// see [LogMeal.totalCarbs] /// see [LogMeal.totalCarbs]
static final totalCarbs = static final totalCarbs =
QueryDoubleProperty<LogMeal>(_entities[7].properties[15]); QueryDoubleProperty<LogMeal>(_entities[7].properties[14]);
/// see [LogMeal.source]
static final source =
QueryStringProperty<LogMeal>(_entities[7].properties[15]);
} }
/// [Meal] entity fields to define ObjectBox queries. /// [Meal] entity fields to define ObjectBox queries.
@ -2413,6 +2591,9 @@ class Meal_ {
/// see [Meal.delayedBolusPercentage] /// see [Meal.delayedBolusPercentage]
static final delayedBolusPercentage = static final delayedBolusPercentage =
QueryDoubleProperty<Meal>(_entities[8].properties[13]); QueryDoubleProperty<Meal>(_entities[8].properties[13]);
/// see [Meal.source]
static final source = QueryStringProperty<Meal>(_entities[8].properties[14]);
} }
/// [MealCategory] entity fields to define ObjectBox queries. /// [MealCategory] entity fields to define ObjectBox queries.
@ -2432,6 +2613,10 @@ class MealCategory_ {
/// see [MealCategory.deleted] /// see [MealCategory.deleted]
static final deleted = static final deleted =
QueryBooleanProperty<MealCategory>(_entities[9].properties[3]); QueryBooleanProperty<MealCategory>(_entities[9].properties[3]);
/// see [MealCategory.source]
static final source =
QueryStringProperty<MealCategory>(_entities[9].properties[4]);
} }
/// [MealPortionType] entity fields to define ObjectBox queries. /// [MealPortionType] entity fields to define ObjectBox queries.
@ -2451,6 +2636,10 @@ class MealPortionType_ {
/// see [MealPortionType.deleted] /// see [MealPortionType.deleted]
static final deleted = static final deleted =
QueryBooleanProperty<MealPortionType>(_entities[10].properties[3]); QueryBooleanProperty<MealPortionType>(_entities[10].properties[3]);
/// see [MealPortionType.source]
static final source =
QueryStringProperty<MealPortionType>(_entities[10].properties[4]);
} }
/// [MealSource] entity fields to define ObjectBox queries. /// [MealSource] entity fields to define ObjectBox queries.
@ -2487,6 +2676,10 @@ class MealSource_ {
/// see [MealSource.deleted] /// see [MealSource.deleted]
static final deleted = static final deleted =
QueryBooleanProperty<MealSource>(_entities[11].properties[7]); QueryBooleanProperty<MealSource>(_entities[11].properties[7]);
/// see [MealSource.source]
static final source =
QueryStringProperty<MealSource>(_entities[11].properties[8]);
} }
/// [LogBolus] entity fields to define ObjectBox queries. /// [LogBolus] entity fields to define ObjectBox queries.
@ -2553,6 +2746,10 @@ class LogBolus_ {
/// see [LogBolus.mmolPerLCorrection] /// see [LogBolus.mmolPerLCorrection]
static final mmolPerLCorrection = static final mmolPerLCorrection =
QueryDoubleProperty<LogBolus>(_entities[12].properties[15]); QueryDoubleProperty<LogBolus>(_entities[12].properties[15]);
/// see [LogBolus.source]
static final source =
QueryStringProperty<LogBolus>(_entities[12].properties[16]);
} }
/// [Accuracy] entity fields to define ObjectBox queries. /// [Accuracy] entity fields to define ObjectBox queries.
@ -2583,6 +2780,10 @@ class Accuracy_ {
/// see [Accuracy.deleted] /// see [Accuracy.deleted]
static final deleted = static final deleted =
QueryBooleanProperty<Accuracy>(_entities[13].properties[6]); QueryBooleanProperty<Accuracy>(_entities[13].properties[6]);
/// see [Accuracy.source]
static final source =
QueryStringProperty<Accuracy>(_entities[13].properties[7]);
} }
/// [Settings] entity fields to define ObjectBox queries. /// [Settings] entity fields to define ObjectBox queries.
@ -2657,6 +2858,10 @@ class Settings_ {
/// see [Settings.amountIncrements] /// see [Settings.amountIncrements]
static final amountIncrements = static final amountIncrements =
QueryDoubleProperty<Settings>(_entities[14].properties[17]); QueryDoubleProperty<Settings>(_entities[14].properties[17]);
/// see [Settings.lastExportTimestamp]
static final lastExportTimestamp =
QueryIntegerProperty<Settings>(_entities[14].properties[18]);
} }
/// [GlucoseTarget] entity fields to define ObjectBox queries. /// [GlucoseTarget] entity fields to define ObjectBox queries.
@ -2688,6 +2893,10 @@ class GlucoseTarget_ {
/// see [GlucoseTarget.color] /// see [GlucoseTarget.color]
static final color = static final color =
QueryIntegerProperty<GlucoseTarget>(_entities[15].properties[6]); QueryIntegerProperty<GlucoseTarget>(_entities[15].properties[6]);
/// see [GlucoseTarget.source]
static final source =
QueryStringProperty<GlucoseTarget>(_entities[15].properties[7]);
} }
/// [Recipe] entity fields to define ObjectBox queries. /// [Recipe] entity fields to define ObjectBox queries.

View File

@ -1,6 +1,7 @@
import 'package:diameter/components/detail.dart'; import 'package:diameter/components/detail.dart';
import 'package:diameter/components/forms/number_form_field.dart'; import 'package:diameter/components/forms/number_form_field.dart';
import 'package:diameter/components/forms/time_of_day_form_field.dart'; import 'package:diameter/components/forms/time_of_day_form_field.dart';
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart'; import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/models/settings.dart'; import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
@ -10,6 +11,7 @@ import 'package:flutter/material.dart';
import 'package:diameter/components/forms/form_wrapper.dart'; import 'package:diameter/components/forms/form_wrapper.dart';
import 'package:diameter/models/basal.dart'; import 'package:diameter/models/basal.dart';
import 'package:diameter/models/basal_profile.dart'; import 'package:diameter/models/basal_profile.dart';
import 'package:flutter_translate/flutter_translate.dart';
class BasalDetailScreen extends StatefulWidget { class BasalDetailScreen extends StatefulWidget {
static const String routeName = '/basal'; static const String routeName = '/basal';
@ -130,7 +132,7 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
_startTime.hour == other.startTime.hour && _startTime.hour == other.startTime.hour &&
_startTime.minute == other.startTime.minute) _startTime.minute == other.startTime.minute)
.isNotEmpty) { .isNotEmpty) {
error = 'There\'s already a rate with this start time.'; error = translate(LocalizationKeys.basal_warnings_duplicate);
} }
if (basalRates if (basalRates
@ -141,7 +143,7 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
DateTimeUtils.convertTimeOfDayToDateTime(_endTime) DateTimeUtils.convertTimeOfDayToDateTime(_endTime)
.isAfter(other.startTime)) .isAfter(other.startTime))
.isNotEmpty) { .isNotEmpty) {
error = 'This rate\'s time period overlaps with another one.'; error = translate(LocalizationKeys.basal_warnings_overlap);
} }
return error == null return error == null
@ -154,11 +156,11 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
actions: <Widget>[ actions: <Widget>[
TextButton( TextButton(
onPressed: () => Navigator.pop(context, 'CANCEL'), onPressed: () => Navigator.pop(context, 'CANCEL'),
child: const Text('GO BACK TO EDITING'), child: Text(translate(LocalizationKeys.general_keepEditing).toUpperCase()),
), ),
ElevatedButton( ElevatedButton(
onPressed: () => Navigator.pop(context, 'CONFIRM'), onPressed: () => Navigator.pop(context, 'CONFIRM'),
child: const Text('SAVE AS IS'), child: Text(translate(LocalizationKeys.general_saveAsIs).toUpperCase()),
), ),
], ],
); );
@ -196,13 +198,26 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
).then((result) { ).then((result) {
Navigator.pop( Navigator.pop(
context, context,
['New Basal Rate${result[1] != null ? 's' : ''} saved', basal] + [
[result[1]], translatePlural(
LocalizationKeys.basal_saved,
result.length,
args: {
"status": _isNew ? '${LocalizationKeys.basal_new} ' : ''
},
),
basal] + [result[1]],
); );
}); });
} else { } else {
Navigator.pop( Navigator.pop(
context, ['${_isNew ? 'New' : ''} Basal Rate saved', basal]); context, [translatePlural(
LocalizationKeys.basal_saved,
1,
args: {
"status": _isNew ? '${LocalizationKeys.basal_new} ' : ''
},
), basal]);
} }
} }
}); });
@ -241,7 +256,14 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text( title: Text(
'${_isNew ? 'New' : 'Edit'} Basal Rate for ${BasalProfile.get(widget.basalProfileId)?.name}'), translate(
LocalizationKeys.basal_title,
args: {
"status": _isNew ? LocalizationKeys.basal_new : LocalizationKeys.general_edit,
"profileName": BasalProfile.get(widget.basalProfileId)?.name,
}
),
),
), ),
drawer: const Navigation(currentLocation: BasalDetailScreen.routeName), drawer: const Navigation(currentLocation: BasalDetailScreen.routeName),
body: Scrollbar( body: Scrollbar(
@ -259,7 +281,7 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
child: Padding( child: Padding(
padding: const EdgeInsets.only(right: 5), padding: const EdgeInsets.only(right: 5),
child: TimeOfDayFormField( child: TimeOfDayFormField(
label: 'Start Time', label: translate(LocalizationKeys.basal_fields_startTime),
controller: _startTimeController, controller: _startTimeController,
time: _startTime, time: _startTime,
onChanged: updateStartTime, onChanged: updateStartTime,
@ -270,7 +292,7 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
child: Padding( child: Padding(
padding: const EdgeInsets.only(left: 5), padding: const EdgeInsets.only(left: 5),
child: TimeOfDayFormField( child: TimeOfDayFormField(
label: 'End Time', label: translate(LocalizationKeys.basal_fields_endTime),
controller: _endTimeController, controller: _endTimeController,
time: _endTime, time: _endTime,
onChanged: updateEndTime, onChanged: updateEndTime,
@ -281,8 +303,8 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
), ),
NumberFormField( NumberFormField(
controller: _unitsController, controller: _unitsController,
label: 'Units', label: translate(LocalizationKeys.basal_fields_units),
suffix: 'U', suffix: translate(LocalizationKeys.general_suffixes_units),
autoRoundToMultipleOfStep: true, autoRoundToMultipleOfStep: true,
step: Settings.insulinSteps, step: Settings.insulinSteps,
onChanged: (value) { onChanged: (value) {
@ -291,7 +313,7 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
Utils.toStringMatchingTemplateFractionPrecision( Utils.toStringMatchingTemplateFractionPrecision(
value, Settings.insulinSteps); value, Settings.insulinSteps);
} }
}), })
], ],
), ),
], ],
@ -305,8 +327,8 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
onMiddleAction: _isSaving || _isFinalRate onMiddleAction: _isSaving || _isFinalRate
? null ? null
: () => handleSaveAction(next: false), : () => handleSaveAction(next: false),
actionText: _isFinalRate ? 'SAVE & CLOSE' : 'NEXT', actionTextKey: translate(_isFinalRate ? LocalizationKeys.general_saveAndClose : LocalizationKeys.general_next).toUpperCase(),
middleActionText: 'SAVE & CLOSE', middleActionTextKey: translate(LocalizationKeys.general_saveAndClose).toUpperCase(),
), ),
); );
} }

View File

@ -1,3 +1,4 @@
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart'; import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/models/settings.dart'; import 'package:diameter/models/settings.dart';
import 'package:diameter/utils/date_time_utils.dart'; import 'package:diameter/utils/date_time_utils.dart';
@ -5,6 +6,7 @@ import 'package:flutter/material.dart';
import 'package:diameter/models/basal.dart'; import 'package:diameter/models/basal.dart';
import 'package:diameter/models/basal_profile.dart'; import 'package:diameter/models/basal_profile.dart';
import 'package:diameter/screens/basal/basal_detail.dart'; import 'package:diameter/screens/basal/basal_detail.dart';
import 'package:flutter_translate/flutter_translate.dart';
class BasalListScreen extends StatefulWidget { class BasalListScreen extends StatefulWidget {
final BasalProfile basalProfile; final BasalProfile basalProfile;
@ -61,7 +63,7 @@ class _BasalListScreenState extends State<BasalListScreen> {
void onDelete(Basal basal) { void onDelete(Basal basal) {
Basal.remove(basal.id); Basal.remove(basal.id);
reload(message: 'Basal Rate deleted'); reload(message: translate(LocalizationKeys.basal_deleted));
} }
void handleDeleteAction(Basal basal) async { void handleDeleteAction(Basal basal) async {
@ -69,7 +71,7 @@ class _BasalListScreenState extends State<BasalListScreen> {
DialogUtils.showConfirmationDialog( DialogUtils.showConfirmationDialog(
context: context, context: context,
onConfirm: () => onDelete(basal), onConfirm: () => onDelete(basal),
message: 'Are you sure you want to delete this Basal Rate?', message: translate(LocalizationKeys.basal_confirmDelete),
); );
} else { } else {
onDelete(basal); onDelete(basal);
@ -83,26 +85,26 @@ class _BasalListScreenState extends State<BasalListScreen> {
// check for gaps // check for gaps
if (index == 0 && if (index == 0 &&
(basal.startTime.hour != 0 || basal.startTime.minute != 0)) { (basal.startTime.hour != 0 || basal.startTime.minute != 0)) {
return 'First Basal of the day needs to start at 00:00'; return translate(LocalizationKeys.basal_warnings_startTimeFirst);
} }
if (index > 0) { if (index > 0) {
var lastEndTime = basalRates[index - 1].endTime; var lastEndTime = basalRates[index - 1].endTime;
if (basal.startTime.isAfter(lastEndTime)) { if (basal.startTime.isAfter(lastEndTime)) {
return 'There\'s a time gap between this and the previous rate'; return translate(LocalizationKeys.basal_warnings_gap);
} }
} }
if (index == basalRates.length - 1 && if (index == basalRates.length - 1 &&
(basal.endTime.hour != 0 || basal.endTime.minute != 0)) { (basal.endTime.hour != 0 || basal.endTime.minute != 0)) {
return 'Last Basal of the day needs to end at 00:00'; return translate(LocalizationKeys.basal_warnings_endTimeLast);
} }
// check for duplicates // check for duplicates
if (basalRates if (basalRates
.where((other) => basal != other && basal.startTime == other.startTime) .where((other) => basal != other && basal.startTime == other.startTime)
.isNotEmpty) { .isNotEmpty) {
return 'There are multiple rates with this start time'; return translate(LocalizationKeys.basal_warnings_duplicate);
} }
if (basalRates if (basalRates
@ -110,7 +112,7 @@ class _BasalListScreenState extends State<BasalListScreen> {
basal.startTime.isBefore(other.startTime) && basal.startTime.isBefore(other.startTime) &&
basal.endTime.isAfter(other.startTime)) basal.endTime.isAfter(other.startTime))
.isNotEmpty) { .isNotEmpty) {
return 'This rate\'s time period overlaps with another one'; return translate(LocalizationKeys.basal_warnings_overlap);
} }
return null; return null;
@ -158,7 +160,7 @@ class _BasalListScreenState extends State<BasalListScreen> {
child: Text( child: Text(
'${DateTimeUtils.displayTime(basal.startTime)} - ${DateTimeUtils.displayTime(basal.endTime)}')), '${DateTimeUtils.displayTime(basal.startTime)} - ${DateTimeUtils.displayTime(basal.endTime)}')),
const Spacer(), const Spacer(),
Expanded(child: Text('${basal.units} U')), Expanded(child: Text('${basal.units} ${translate(LocalizationKeys.general_suffixes_units)}')),
], ],
), ),
trailing: Row( trailing: Row(
@ -180,8 +182,8 @@ class _BasalListScreenState extends State<BasalListScreen> {
}, },
), ),
) )
: const Center( : Center(
child: Text('You have not created any Basal Rates yet!'), child: Text(translate(LocalizationKeys.basal_empty)),
); );
} }
} }

View File

@ -1,5 +1,6 @@
import 'package:diameter/components/detail.dart'; import 'package:diameter/components/detail.dart';
import 'package:diameter/components/forms/boolean_form_field.dart'; import 'package:diameter/components/forms/boolean_form_field.dart';
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart'; import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/models/basal.dart'; import 'package:diameter/models/basal.dart';
import 'package:diameter/models/settings.dart'; import 'package:diameter/models/settings.dart';
@ -9,6 +10,7 @@ import 'package:flutter/material.dart';
import 'package:diameter/components/forms/form_wrapper.dart'; import 'package:diameter/components/forms/form_wrapper.dart';
import 'package:diameter/models/basal_profile.dart'; import 'package:diameter/models/basal_profile.dart';
import 'package:diameter/screens/basal/basal_list.dart'; import 'package:diameter/screens/basal/basal_list.dart';
import 'package:flutter_translate/flutter_translate.dart';
class BasalProfileDetailScreen extends StatefulWidget { class BasalProfileDetailScreen extends StatefulWidget {
static const String routeName = '/basal-profile'; static const String routeName = '/basal-profile';
@ -133,21 +135,30 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return AlertDialog( return AlertDialog(
content: const Text( content: Text(translate(LocalizationKeys.basalProfile_warnings_activeAlreadySet)),
'There are already one or more active profiles. What would you like to do?'),
actions: [ actions: [
TextButton( TextButton(
onPressed: () => Navigator.pop(context, 0), onPressed: () => Navigator.pop(context, 0),
child: const Text('IGNORE'), child: Text(translate(LocalizationKeys.basalProfile_warnings_resolve_ignore).toUpperCase()),
), ),
TextButton( TextButton(
onPressed: () => Navigator.pop(context, 1), onPressed: () => Navigator.pop(context, 1),
child: child:
Text('DEACTIVATE ${_nameController.text.toUpperCase()}'), Text(
translate(
LocalizationKeys.basalProfile_warnings_resolve_deactivateProfile,
args: {
"profileName": _nameController.text,
}
).toUpperCase()
),
), ),
ElevatedButton( ElevatedButton(
onPressed: () => Navigator.pop(context, 2), onPressed: () => Navigator.pop(context, 2),
child: const Text('DEACTIVATE ALL OTHERS'), child: Text(
translate(
LocalizationKeys.basalProfile_warnings_resolve_deactivateOthers,
).toUpperCase()),
) )
], ],
); );
@ -167,16 +178,25 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return AlertDialog( return AlertDialog(
content: const Text( content: Text(
'There is currently no active profile. Would you like to set this one as active?'), translate(
LocalizationKeys.basalProfile_warnings_noActiveOnCreate,
).toUpperCase()),
actions: [ actions: [
TextButton( TextButton(
onPressed: () => Navigator.pop(context, 0), onPressed: () => Navigator.pop(context, 0),
child: const Text('IGNORE'), child: Text(
translate(
LocalizationKeys.basalProfile_warnings_resolve_ignore,
).toUpperCase()),
), ),
TextButton( TextButton(
onPressed: () => Navigator.pop(context, 1), onPressed: () => Navigator.pop(context, 1),
child: const Text('ACTIVATE THIS PROFILE'), child: Text(
translate(
LocalizationKeys.basalProfile_warnings_resolve_activateCurrent,
).toUpperCase()
),
), ),
], ],
); );
@ -249,7 +269,11 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
if (close) { if (close) {
Navigator.pop(context, Navigator.pop(context,
['${_isNew ? 'New' : ''} Basal Profile saved', basalProfile]); [
translate(LocalizationKeys.basalProfile_saved, args: {
"status": _isNew ? '${translate(LocalizationKeys.basalProfile_new)} ' : ''
}),
]);
} else { } else {
if (_isNew) { if (_isNew) {
Navigator.push( Navigator.push(
@ -260,7 +284,7 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
), ),
).then((result) => Navigator.pop(context, result)); ).then((result) => Navigator.pop(context, result));
} else { } else {
reload(message: 'Basal Profile saved'); reload(message: translate(LocalizationKeys.basalProfile_saved));
} }
} }
} }
@ -329,12 +353,12 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
fields: [ fields: [
TextFormField( TextFormField(
controller: _nameController, controller: _nameController,
decoration: const InputDecoration( decoration: InputDecoration(
labelText: 'Name', labelText: translate(LocalizationKeys.basalProfile_fields_name),
), ),
validator: (value) { validator: (value) {
if (value!.trim().isEmpty) { if (value!.trim().isEmpty) {
return 'Empty title'; return translate(LocalizationKeys.basalProfile_fields_validators_name);
} }
return null; return null;
}, },
@ -342,8 +366,8 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
TextFormField( TextFormField(
keyboardType: TextInputType.multiline, keyboardType: TextInputType.multiline,
controller: _notesController, controller: _notesController,
decoration: const InputDecoration( decoration: InputDecoration(
labelText: 'Notes', labelText: translate(LocalizationKeys.basalProfile_fields_notes),
), ),
minLines: 2, minLines: 2,
maxLines: 5, maxLines: 5,
@ -355,7 +379,7 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
_active = value; _active = value;
}); });
}, },
label: 'active', label: translate(LocalizationKeys.basalProfile_fields_active),
), ),
], ],
), ),
@ -374,13 +398,24 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(_isNew ? 'New Basal Profile' : _basalProfile!.name), title: Text(translate(LocalizationKeys.basalProfile_detail_title, args: {
"status": _isNew ? '${translate(LocalizationKeys.basalProfile_new)} ' : '',
"profileName": _basalProfile!.name,
})),
bottom: _isNew bottom: _isNew
? PreferredSize(child: Container(), preferredSize: Size.zero) ? PreferredSize(child: Container(), preferredSize: Size.zero)
: const TabBar( : TabBar(
tabs: [ tabs: [
Tab(text: 'PROFILE'), Tab(
Tab(text: 'RATES'), text: translate(
LocalizationKeys.basalProfile_detail_tabs_profile,
).toUpperCase(),
),
Tab(
text: translate(
LocalizationKeys.basalProfile_detail_tabs_rates,
).toUpperCase(),
),
], ],
), ),
actions: appBarActions, actions: appBarActions,

View File

@ -1,3 +1,4 @@
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart'; import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/components/forms/auto_complete_dropdown_button.dart'; import 'package:diameter/components/forms/auto_complete_dropdown_button.dart';
import 'package:diameter/models/basal.dart'; import 'package:diameter/models/basal.dart';
@ -6,6 +7,7 @@ import 'package:diameter/navigation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:diameter/models/basal_profile.dart'; import 'package:diameter/models/basal_profile.dart';
import 'package:diameter/screens/basal/basal_profile_detail.dart'; import 'package:diameter/screens/basal/basal_profile_detail.dart';
import 'package:flutter_translate/flutter_translate.dart';
class BasalProfileListScreen extends StatefulWidget { class BasalProfileListScreen extends StatefulWidget {
static const String routeName = '/basal-profiles'; static const String routeName = '/basal-profiles';
@ -59,26 +61,33 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
banner = activeProfileCount != 1 banner = activeProfileCount != 1
? MaterialBanner( ? MaterialBanner(
content: Text(activeProfileCount == 0 content: Text(activeProfileCount == 0
? 'You currently do not have an active Basal Profile.' ? translate(LocalizationKeys.basalProfile_warnings_noActive)
: 'More than one active Basal Profile has been found.'), : translate(
LocalizationKeys.basalProfile_warnings_multipleActive)),
leading: const CircleAvatar(child: Icon(Icons.warning)), leading: const CircleAvatar(child: Icon(Icons.warning)),
forceActionsBelow: true, forceActionsBelow: true,
actions: activeProfileCount == 0 actions: activeProfileCount == 0
? [ ? [
_basalProfiles.isNotEmpty _basalProfiles.isNotEmpty
? TextButton( ? TextButton(
child: const Text('ACTIVATE A PROFILE'), child: Text(translate(LocalizationKeys
.basalProfile_warnings_resolve_activate)
.toUpperCase()),
onPressed: handlePickActiveProfileAction, onPressed: handlePickActiveProfileAction,
) )
: Container(), : Container(),
TextButton( TextButton(
child: const Text('CREATE A NEW PROFILE'), child: Text(translate(LocalizationKeys
.basalProfile_warnings_resolve_create)
.toUpperCase()),
onPressed: () => onNew(true), onPressed: () => onNew(true),
), ),
] ]
: [ : [
TextButton( TextButton(
child: const Text('PICK A PROFILE'), child: Text(translate(LocalizationKeys
.basalProfile_warnings_resolve_pick)
.toUpperCase()),
onPressed: handlePickActiveProfileAction, onPressed: handlePickActiveProfileAction,
), ),
], ],
@ -89,9 +98,10 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
void handleDuplicateAction(BasalProfile basalProfile) async { void handleDuplicateAction(BasalProfile basalProfile) async {
final copy = BasalProfile( final copy = BasalProfile(
active: false, active: false,
name: 'Copy of ${basalProfile.name}', name: translate(LocalizationKeys.basalProfile_copyOf, args: {
); "profileName": basalProfile.name,
}));
BasalProfile.put(copy); BasalProfile.put(copy);
final rates = Basal.getAllForProfile(basalProfile.id); final rates = Basal.getAllForProfile(basalProfile.id);
@ -105,12 +115,19 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
Basal.put(basal); Basal.put(basal);
} }
reload(message: 'Added copy of ${basalProfile.name}'); reload(
message: translate(
LocalizationKeys.basalProfile_warnings_resolve_ignore,
args: {"profileName": basalProfile.name},
));
} }
void onDelete(BasalProfile basalProfile) { void onDelete(BasalProfile basalProfile) {
BasalProfile.remove(basalProfile.id); BasalProfile.remove(basalProfile.id);
reload(message: 'Basal Profile deleted'); reload(
message: translate(
LocalizationKeys.basalProfile_deleted,
));
} }
void handleDeleteAction(BasalProfile basalProfile) async { void handleDeleteAction(BasalProfile basalProfile) async {
@ -118,7 +135,7 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
DialogUtils.showConfirmationDialog( DialogUtils.showConfirmationDialog(
context: context, context: context,
onConfirm: () => onDelete(basalProfile), onConfirm: () => onDelete(basalProfile),
message: 'Are you sure you want to delete this Basal Profile?', message: translate(LocalizationKeys.basalProfile_confirmDelete),
); );
} else { } else {
onDelete(basalProfile); onDelete(basalProfile);
@ -131,7 +148,8 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
basalProfile.active = true; basalProfile.active = true;
BasalProfile.put(basalProfile); BasalProfile.put(basalProfile);
reload( reload(
message: '${basalProfile.name} has been set as your active Profile'); message: translate(LocalizationKeys.basalProfile_activated,
args: {"profileName": basalProfile.name}));
} }
} }
@ -141,14 +159,16 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
content: AutoCompleteDropdownButton( content: AutoCompleteDropdownButton(
controller: TextEditingController(text: ''), controller: TextEditingController(text: ''),
items: _basalProfiles, items: _basalProfiles,
label: 'Default Basal Profile', label: translate(LocalizationKeys.basalProfile_default),
onChanged: onPickActive, onChanged: onPickActive,
), ),
leading: const CircleAvatar(child: Icon(Icons.info)), leading: const CircleAvatar(child: Icon(Icons.info)),
forceActionsBelow: true, forceActionsBelow: true,
actions: [ actions: [
TextButton( TextButton(
child: const Text('CREATE A NEW PROFILE INSTEAD'), child: Text(translate(LocalizationKeys
.basalProfile_warnings_resolve_createInstead)
.toUpperCase()),
onPressed: () => onNew(true), onPressed: () => onNew(true),
), ),
], ],
@ -178,7 +198,7 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: const Text('Basal Profiles'), title: Text(translate(LocalizationKeys.basalProfile_title)),
actions: <Widget>[ actions: <Widget>[
IconButton(onPressed: reload, icon: const Icon(Icons.refresh)) IconButton(onPressed: reload, icon: const Icon(Icons.refresh))
], ],
@ -202,9 +222,9 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
double dailyTotal = double dailyTotal =
Basal.getDailyTotalForProfile(basalProfile.id); Basal.getDailyTotalForProfile(basalProfile.id);
String activeProfileText = basalProfile.active String activeProfileText = basalProfile.active
? ' (Default Profile)' ? '(${translate(LocalizationKeys.basalProfile_default)})'
: basalProfile.id == _activeProfile?.id : basalProfile.id == _activeProfile?.id
? ' (Current Active Profile)' ? ' (${translate(LocalizationKeys.basalProfile_active)})'
: ''; : '';
return Card( return Card(
child: ListTile( child: ListTile(
@ -228,7 +248,9 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
? [ ? [
Text(dailyTotal Text(dailyTotal
.toStringAsPrecision(3)), .toStringAsPrecision(3)),
const Text('U/day', Text(
translate(LocalizationKeys
.general_suffixes_perDay),
textScaleFactor: 0.75), textScaleFactor: 0.75),
] ]
: [], : [],
@ -263,8 +285,8 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
}, },
), ),
) )
: const Center( : Center(
child: Text('You have not created any Basal Profiles yet!'), child: Text(translate(LocalizationKeys.basalProfile_empty)),
), ),
), ),
], ],

View File

@ -1,6 +1,7 @@
import 'package:diameter/components/detail.dart'; import 'package:diameter/components/detail.dart';
import 'package:diameter/components/forms/number_form_field.dart'; import 'package:diameter/components/forms/number_form_field.dart';
import 'package:diameter/components/forms/time_of_day_form_field.dart'; import 'package:diameter/components/forms/time_of_day_form_field.dart';
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart'; import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/models/settings.dart'; import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
@ -10,6 +11,7 @@ import 'package:flutter/material.dart';
import 'package:diameter/components/forms/form_wrapper.dart'; import 'package:diameter/components/forms/form_wrapper.dart';
import 'package:diameter/models/bolus.dart'; import 'package:diameter/models/bolus.dart';
import 'package:diameter/models/bolus_profile.dart'; import 'package:diameter/models/bolus_profile.dart';
import 'package:flutter_translate/flutter_translate.dart';
class BolusDetailScreen extends StatefulWidget { class BolusDetailScreen extends StatefulWidget {
static const String routeName = '/bolus'; static const String routeName = '/bolus';
@ -140,7 +142,7 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
_startTime.hour == other.startTime.hour && _startTime.hour == other.startTime.hour &&
_startTime.minute == other.startTime.minute) _startTime.minute == other.startTime.minute)
.isNotEmpty) { .isNotEmpty) {
error = 'There\'s already a rate with this start time.'; error = translate(LocalizationKeys.bolus_warnings_duplicate);
} }
if (bolusRates if (bolusRates
@ -151,7 +153,7 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
DateTimeUtils.convertTimeOfDayToDateTime(_endTime) DateTimeUtils.convertTimeOfDayToDateTime(_endTime)
.isAfter(other.startTime)) .isAfter(other.startTime))
.isNotEmpty) { .isNotEmpty) {
error = 'This rate\'s time period overlaps with another one.'; error = translate(LocalizationKeys.bolus_warnings_overlap);
} }
return error == null return error == null
@ -164,11 +166,11 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
actions: <Widget>[ actions: <Widget>[
TextButton( TextButton(
onPressed: () => Navigator.pop(context, 'CANCEL'), onPressed: () => Navigator.pop(context, 'CANCEL'),
child: const Text('GO BACK TO EDITING'), child: Text(translate(LocalizationKeys.general_keepEditing).toUpperCase()),
), ),
ElevatedButton( ElevatedButton(
onPressed: () => Navigator.pop(context, 'CONFIRM'), onPressed: () => Navigator.pop(context, 'CONFIRM'),
child: const Text('SAVE AS IS'), child: Text(translate(LocalizationKeys.general_saveAsIs).toUpperCase()),
), ),
], ],
); );
@ -210,13 +212,27 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
).then((result) { ).then((result) {
Navigator.pop( Navigator.pop(
context, context,
['New Bolus Rate${result[1] != null ? 's' : ''} saved', bolus] + [
translatePlural(
LocalizationKeys.bolus_saved,
result.length,
args: {
"status": _isNew ? '${LocalizationKeys.bolus_new} ' : ''
},
),
bolus] +
[result[1]], [result[1]],
); );
}); });
} else { } else {
Navigator.pop( Navigator.pop(
context, ['${_isNew ? 'New' : ''} Bolus Rate saved', bolus]); context, [translatePlural(
LocalizationKeys.bolus_saved,
1,
args: {
"status": _isNew ? '${LocalizationKeys.bolus_new} ' : ''
},
), bolus]);
} }
} }
}); });
@ -288,7 +304,14 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text( title: Text(
'${_isNew ? 'New' : 'Edit'} Bolus Rate for ${BolusProfile.get(widget.bolusProfileId)?.name}'), translate(
LocalizationKeys.basal_title,
args: {
"status": _isNew ? LocalizationKeys.bolus_new : LocalizationKeys.general_edit,
"profileName": BolusProfile.get(widget.bolusProfileId)?.name,
}
),
),
), ),
drawer: const Navigation(currentLocation: BolusDetailScreen.routeName), drawer: const Navigation(currentLocation: BolusDetailScreen.routeName),
body: Scrollbar( body: Scrollbar(
@ -306,7 +329,7 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
child: Padding( child: Padding(
padding: const EdgeInsets.only(right: 5), padding: const EdgeInsets.only(right: 5),
child: TimeOfDayFormField( child: TimeOfDayFormField(
label: 'Start Time', label: translate(LocalizationKeys.bolus_fields_startTime),
controller: _startTimeController, controller: _startTimeController,
time: _startTime, time: _startTime,
onChanged: updateStartTime, onChanged: updateStartTime,
@ -317,7 +340,7 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
child: Padding( child: Padding(
padding: const EdgeInsets.only(left: 5), padding: const EdgeInsets.only(left: 5),
child: TimeOfDayFormField( child: TimeOfDayFormField(
label: 'End Time', label: translate(LocalizationKeys.bolus_fields_endTime),
controller: _endTimeController, controller: _endTimeController,
time: _endTime, time: _endTime,
onChanged: updateEndTime, onChanged: updateEndTime,
@ -328,8 +351,8 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
), ),
NumberFormField( NumberFormField(
controller: _unitsController, controller: _unitsController,
label: 'Units', label: translate(LocalizationKeys.bolus_fields_units),
suffix: 'U', suffix: translate(LocalizationKeys.general_suffixes_units),
autoRoundToMultipleOfStep: true, autoRoundToMultipleOfStep: true,
step: Settings.insulinSteps, step: Settings.insulinSteps,
onChanged: (value) { onChanged: (value) {
@ -342,7 +365,7 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
), ),
NumberFormField( NumberFormField(
controller: _carbsController, controller: _carbsController,
label: 'per carbs', label: translate(LocalizationKeys.bolus_fields_perCarbs),
suffix: Settings.nutritionMeasurementSuffix, suffix: Settings.nutritionMeasurementSuffix,
autoRoundToMultipleOfStep: true, autoRoundToMultipleOfStep: true,
step: Settings.nutritionSteps, step: Settings.nutritionSteps,
@ -365,8 +388,10 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
? Expanded( ? Expanded(
flex: Settings.glucoseMeasurement == GlucoseMeasurement.mgPerDl ? 2 : 1, flex: Settings.glucoseMeasurement == GlucoseMeasurement.mgPerDl ? 2 : 1,
child: NumberFormField( child: NumberFormField(
label: 'per mg/dl', label: translate(LocalizationKeys.bolus_fields_perGlucose, args: {
suffix: 'mg/dl', "glucoseMeasurement": Settings.glucoseMeasurementSuffix
}),
suffix: Settings.glucoseMeasurementSuffix,
readOnly: Settings.glucoseMeasurement == readOnly: Settings.glucoseMeasurement ==
GlucoseMeasurement.mmolPerL, GlucoseMeasurement.mmolPerL,
showSteppers: Settings.glucoseMeasurement == GlucoseMeasurement.mgPerDl, showSteppers: Settings.glucoseMeasurement == GlucoseMeasurement.mgPerDl,
@ -384,8 +409,10 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
? Expanded( ? Expanded(
flex: Settings.glucoseMeasurement == GlucoseMeasurement.mmolPerL ? 2 : 1, flex: Settings.glucoseMeasurement == GlucoseMeasurement.mmolPerL ? 2 : 1,
child: NumberFormField( child: NumberFormField(
label: 'per mmol/l', label: translate(LocalizationKeys.bolus_fields_perGlucose, args: {
suffix: 'mmol/l', "glucoseMeasurement": Settings.glucoseMeasurementSuffix
}),
suffix: Settings.glucoseMeasurementSuffix,
readOnly: Settings.glucoseMeasurement == readOnly: Settings.glucoseMeasurement ==
GlucoseMeasurement.mgPerDl, GlucoseMeasurement.mgPerDl,
showSteppers: Settings.glucoseMeasurement == GlucoseMeasurement.mmolPerL, showSteppers: Settings.glucoseMeasurement == GlucoseMeasurement.mmolPerL,
@ -410,8 +437,8 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
onMiddleAction: _isSaving || _isFinalRate onMiddleAction: _isSaving || _isFinalRate
? null ? null
: () => handleSaveAction(next: false), : () => handleSaveAction(next: false),
actionText: _isFinalRate ? 'SAVE & CLOSE' : 'NEXT', actionTextKey: _isFinalRate ? translate(LocalizationKeys.general_saveAndClose) : translate(LocalizationKeys.general_next),
middleActionText: 'SAVE & CLOSE', middleActionTextKey: translate(LocalizationKeys.general_saveAndClose),
), ),
); );
} }

View File

@ -1,3 +1,4 @@
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart'; import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/models/settings.dart'; import 'package:diameter/models/settings.dart';
import 'package:diameter/utils/date_time_utils.dart'; import 'package:diameter/utils/date_time_utils.dart';
@ -5,6 +6,7 @@ import 'package:flutter/material.dart';
import 'package:diameter/models/bolus.dart'; import 'package:diameter/models/bolus.dart';
import 'package:diameter/models/bolus_profile.dart'; import 'package:diameter/models/bolus_profile.dart';
import 'package:diameter/screens/bolus/bolus_detail.dart'; import 'package:diameter/screens/bolus/bolus_detail.dart';
import 'package:flutter_translate/flutter_translate.dart';
class BolusListScreen extends StatefulWidget { class BolusListScreen extends StatefulWidget {
final BolusProfile bolusProfile; final BolusProfile bolusProfile;
@ -61,7 +63,7 @@ class _BolusListScreenState extends State<BolusListScreen> {
void onDelete(Bolus bolus) { void onDelete(Bolus bolus) {
Bolus.remove(bolus.id); Bolus.remove(bolus.id);
reload(message: 'Bolus Rate deleted'); reload(message: translate(LocalizationKeys.bolus_deleted));
} }
void handleDeleteAction(Bolus bolus) async { void handleDeleteAction(Bolus bolus) async {
@ -69,7 +71,7 @@ class _BolusListScreenState extends State<BolusListScreen> {
DialogUtils.showConfirmationDialog( DialogUtils.showConfirmationDialog(
context: context, context: context,
onConfirm: () => onDelete(bolus), onConfirm: () => onDelete(bolus),
message: 'Are you sure you want to delete this Bolus Rate?', message: translate(LocalizationKeys.bolus_confirmDelete),
); );
} else { } else {
onDelete(bolus); onDelete(bolus);
@ -82,26 +84,26 @@ class _BolusListScreenState extends State<BolusListScreen> {
if (index == 0 && if (index == 0 &&
(bolus.startTime.toLocal().hour != 0 || bolus.startTime.minute != 0)) { (bolus.startTime.toLocal().hour != 0 || bolus.startTime.minute != 0)) {
return 'First Bolus of the day needs to start at 00:00'; return translate(LocalizationKeys.bolus_warnings_startTimeFirst);
} }
if (index > 0) { if (index > 0) {
var lastEndTime = bolusRates[index - 1].endTime; var lastEndTime = bolusRates[index - 1].endTime;
if (bolus.startTime.isAfter(lastEndTime)) { if (bolus.startTime.isAfter(lastEndTime)) {
return 'There\'s a time gap between this and the previous rate'; return translate(LocalizationKeys.bolus_warnings_gap);
} }
} }
if (index == bolusRates.length - 1 && if (index == bolusRates.length - 1 &&
(bolus.endTime.toLocal().hour != 0 || bolus.endTime.minute != 0)) { (bolus.endTime.toLocal().hour != 0 || bolus.endTime.minute != 0)) {
return 'Last Bolus of the day needs to end at 00:00'; return translate(LocalizationKeys.bolus_warnings_endTimeLast);
} }
// check for duplicates // check for duplicates
if (bolusRates if (bolusRates
.where((other) => bolus != other && bolus.startTime == other.startTime) .where((other) => bolus != other && bolus.startTime == other.startTime)
.isNotEmpty) { .isNotEmpty) {
return 'There are multiple rates with this start time'; return translate(LocalizationKeys.bolus_warnings_duplicate);
} }
if (bolusRates if (bolusRates
@ -109,7 +111,7 @@ class _BolusListScreenState extends State<BolusListScreen> {
bolus.startTime.isBefore(other.startTime) && bolus.startTime.isBefore(other.startTime) &&
bolus.endTime.isAfter(other.startTime)) bolus.endTime.isAfter(other.startTime))
.isNotEmpty) { .isNotEmpty) {
return 'This rate\'s time period overlaps with another one'; return translate(LocalizationKeys.bolus_warnings_overlap);
} }
return null; return null;
@ -162,8 +164,9 @@ class _BolusListScreenState extends State<BolusListScreen> {
? [ ? [
Text((bolus.carbs / bolus.units) Text((bolus.carbs / bolus.units)
.toStringAsPrecision(2)), .toStringAsPrecision(2)),
Text( Text(translate(LocalizationKeys.general_suffixes_carbsPerU, args: {
'${Settings.nutritionMeasurementSuffix} carbs per U', "nutritionMeasurementSuffix": Settings.nutritionMeasurementSuffix
}),
textAlign: TextAlign.center, textAlign: TextAlign.center,
textScaleFactor: 0.75), textScaleFactor: 0.75),
] ]
@ -176,7 +179,7 @@ class _BolusListScreenState extends State<BolusListScreen> {
? [ ? [
Text((bolus.units / bolus.carbs * 12) Text((bolus.units / bolus.carbs * 12)
.toStringAsPrecision(2)), .toStringAsPrecision(2)),
const Text('U per bread unit', Text(translate(LocalizationKeys.general_suffixes_uPerBreadUnit),
textAlign: TextAlign.center, textAlign: TextAlign.center,
textScaleFactor: 0.75), textScaleFactor: 0.75),
] ]
@ -196,8 +199,9 @@ class _BolusListScreenState extends State<BolusListScreen> {
: bolus.mmolPerL ?? 0)! / : bolus.mmolPerL ?? 0)! /
bolus.units)) bolus.units))
.toString()), .toString()),
Text( Text(translate(LocalizationKeys.general_suffixes_uPerGlucose, args: {
'${Settings.glucoseMeasurementSuffix} per unit', "glucoseMeasurementSuffix": Settings.glucoseMeasurementSuffix
}),
textAlign: TextAlign.center, textAlign: TextAlign.center,
textScaleFactor: 0.75), textScaleFactor: 0.75),
] ]
@ -225,8 +229,8 @@ class _BolusListScreenState extends State<BolusListScreen> {
}, },
), ),
) )
: const Center( : Center(
child: Text('You have not created any Bolus Rates yet!'), child: Text(translate(LocalizationKeys.bolus_title)),
); );
} }
} }

View File

@ -1,5 +1,6 @@
import 'package:diameter/components/detail.dart'; import 'package:diameter/components/detail.dart';
import 'package:diameter/components/forms/boolean_form_field.dart'; import 'package:diameter/components/forms/boolean_form_field.dart';
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart'; import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/models/bolus.dart'; import 'package:diameter/models/bolus.dart';
import 'package:diameter/models/settings.dart'; import 'package:diameter/models/settings.dart';
@ -9,6 +10,7 @@ import 'package:flutter/material.dart';
import 'package:diameter/components/forms/form_wrapper.dart'; import 'package:diameter/components/forms/form_wrapper.dart';
import 'package:diameter/models/bolus_profile.dart'; import 'package:diameter/models/bolus_profile.dart';
import 'package:diameter/screens/bolus/bolus_list.dart'; import 'package:diameter/screens/bolus/bolus_list.dart';
import 'package:flutter_translate/flutter_translate.dart';
class BolusProfileDetailScreen extends StatefulWidget { class BolusProfileDetailScreen extends StatefulWidget {
static const String routeName = '/bolus-profile'; static const String routeName = '/bolus-profile';
@ -131,20 +133,19 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return AlertDialog( return AlertDialog(
content: const Text( content: Text(translate(LocalizationKeys.bolusProfile_warnings_activeAlreadySet)),
'There are already one or more active profiles. What would you like to do?'),
actions: [ actions: [
TextButton( TextButton(
onPressed: () => Navigator.pop(context, 0), onPressed: () => Navigator.pop(context, 0),
child: const Text('IGNORE'), child: Text(translate(LocalizationKeys.bolusProfile_warnings_resolve_ignore).toUpperCase()),
), ),
TextButton( TextButton(
onPressed: () => Navigator.pop(context, 1), onPressed: () => Navigator.pop(context, 1),
child: const Text('DEACTIVATE THIS PROFILE'), child: Text(translate(LocalizationKeys.bolusProfile_warnings_resolve_deactivateProfile).toUpperCase()),
), ),
ElevatedButton( ElevatedButton(
onPressed: () => Navigator.pop(context, 2), onPressed: () => Navigator.pop(context, 2),
child: const Text('DEACTIVATE ALL OTHERS'), child: Text(translate(LocalizationKeys.bolusProfile_warnings_resolve_deactivateOthers).toUpperCase()),
) )
], ],
); );
@ -164,16 +165,15 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return AlertDialog( return AlertDialog(
content: const Text( content: Text(translate(LocalizationKeys.bolusProfile_warnings_noActiveOnCreate)),
'There is currently no active profile. Would you like to set this one as active?'),
actions: [ actions: [
TextButton( TextButton(
onPressed: () => Navigator.pop(context, 0), onPressed: () => Navigator.pop(context, 0),
child: const Text('IGNORE'), child: Text(translate(LocalizationKeys.bolusProfile_warnings_resolve_ignore).toUpperCase()),
), ),
TextButton( TextButton(
onPressed: () => Navigator.pop(context, 1), onPressed: () => Navigator.pop(context, 1),
child: const Text('ACTIVATE THIS PROFILE'), child: Text(translate(LocalizationKeys.bolusProfile_warnings_resolve_activateCurrent).toUpperCase()),
), ),
], ],
); );
@ -246,8 +246,15 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
BolusProfile.put(bolusProfile); BolusProfile.put(bolusProfile);
if (close) { if (close) {
Navigator.pop(context, Navigator.pop(context, [
['${_isNew ? 'New' : ''} Bolus Profile saved', bolusProfile]); translate(
LocalizationKeys.bolusProfile_saved,
args: {
"status": _isNew ? '${translate(LocalizationKeys.bolusProfile_new)} ' : ''
},
), bolusProfile
],
);
} else { } else {
if (_isNew) { if (_isNew) {
Navigator.push( Navigator.push(
@ -258,7 +265,7 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
), ),
).then((result) => Navigator.pop(context, result)); ).then((result) => Navigator.pop(context, result));
} else { } else {
reload(message: 'Bolus Profile saved'); reload(message: translate(LocalizationKeys.bolusProfile_saved));
} }
} }
} }
@ -329,19 +336,19 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
fields: [ fields: [
TextFormField( TextFormField(
controller: _nameController, controller: _nameController,
decoration: const InputDecoration( decoration: InputDecoration(
labelText: 'Name', labelText: translate(LocalizationKeys.bolusProfile_fields_name),
), ),
validator: (value) { validator: (value) {
if (value!.trim().isEmpty) { if (value!.trim().isEmpty) {
return 'Empty title'; return translate(LocalizationKeys.bolusProfile_fields_validators_name);
} }
return null; return null;
}, },
), ),
TextFormField( TextFormField(
decoration: const InputDecoration( decoration: InputDecoration(
labelText: 'Notes', labelText: translate(LocalizationKeys.bolusProfile_fields_notes),
), ),
controller: _notesController, controller: _notesController,
keyboardType: TextInputType.multiline, keyboardType: TextInputType.multiline,
@ -355,7 +362,7 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
_active = value; _active = value;
}); });
}, },
label: 'active', label: translate(LocalizationKeys.bolusProfile_fields_active),
), ),
], ],
), ),
@ -374,13 +381,16 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(_isNew ? 'New Bolus Profile' : _bolusProfile!.name), title: Text(translate(LocalizationKeys.bolusProfile_detail_title, args: {
"status": _isNew ? '${translate(LocalizationKeys.bolusProfile_new)} ' : '',
"profileName": _bolusProfile!.name,
})),
bottom: _isNew bottom: _isNew
? PreferredSize(child: Container(), preferredSize: Size.zero) ? PreferredSize(child: Container(), preferredSize: Size.zero)
: const TabBar( : TabBar(
tabs: [ tabs: [
Tab(text: 'PROFILE'), Tab(text: translate(LocalizationKeys.bolusProfile_detail_tabs_profile).toUpperCase()),
Tab(text: 'RATES'), Tab(text: translate(LocalizationKeys.bolusProfile_detail_tabs_rates).toUpperCase()),
], ],
), ),
actions: appBarActions, actions: appBarActions,

View File

@ -1,3 +1,4 @@
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart'; import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/components/forms/auto_complete_dropdown_button.dart'; import 'package:diameter/components/forms/auto_complete_dropdown_button.dart';
import 'package:diameter/models/bolus.dart'; import 'package:diameter/models/bolus.dart';
@ -6,6 +7,7 @@ import 'package:diameter/navigation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:diameter/models/bolus_profile.dart'; import 'package:diameter/models/bolus_profile.dart';
import 'package:diameter/screens/bolus/bolus_profile_detail.dart'; import 'package:diameter/screens/bolus/bolus_profile_detail.dart';
import 'package:flutter_translate/flutter_translate.dart';
class BolusProfileListScreen extends StatefulWidget { class BolusProfileListScreen extends StatefulWidget {
static const String routeName = '/bolus-profiles'; static const String routeName = '/bolus-profiles';
@ -62,26 +64,26 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
banner = activeProfileCount != 1 banner = activeProfileCount != 1
? MaterialBanner( ? MaterialBanner(
content: Text(activeProfileCount == 0 content: Text(activeProfileCount == 0
? 'You currently do not have an active Bolus Profile.' ? translate(LocalizationKeys.bolusProfile_warnings_noActive)
: 'More than one active Bolus Profile has been found.'), : translate(LocalizationKeys.bolusProfile_warnings_multipleActive)),
leading: const CircleAvatar(child: Icon(Icons.warning)), leading: const CircleAvatar(child: Icon(Icons.warning)),
forceActionsBelow: true, forceActionsBelow: true,
actions: activeProfileCount == 0 actions: activeProfileCount == 0
? [ ? [
_bolusProfiles.isNotEmpty _bolusProfiles.isNotEmpty
? TextButton( ? TextButton(
child: const Text('ACTIVATE A PROFILE'), child: Text(translate(LocalizationKeys.bolusProfile_warnings_resolve_activate).toUpperCase()),
onPressed: handlePickActiveProfileAction, onPressed: handlePickActiveProfileAction,
) )
: Container(), : Container(),
TextButton( TextButton(
child: const Text('CREATE A NEW PROFILE'), child: Text(translate(LocalizationKeys.bolusProfile_warnings_resolve_create).toUpperCase()),
onPressed: () => onNew(true), onPressed: () => onNew(true),
), ),
] ]
: [ : [
TextButton( TextButton(
child: const Text('PICK A PROFILE'), child: Text(translate(LocalizationKeys.bolusProfile_warnings_resolve_pick).toUpperCase()),
onPressed: handlePickActiveProfileAction, onPressed: handlePickActiveProfileAction,
), ),
], ],
@ -93,7 +95,12 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
void handleDuplicateAction(BolusProfile bolusProfile) async { void handleDuplicateAction(BolusProfile bolusProfile) async {
final copy = BolusProfile( final copy = BolusProfile(
active: false, active: false,
name: 'Copy of ${bolusProfile.name}', name: translate(
LocalizationKeys.bolusProfile_copyOf,
args: {
"profileName": bolusProfile.name
},
),
); );
BolusProfile.put(copy); BolusProfile.put(copy);
@ -111,12 +118,19 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
Bolus.put(bolus); Bolus.put(bolus);
} }
reload(message: 'Added copy of ${bolusProfile.name}'); reload(
message: translate(
LocalizationKeys.bolusProfile_copied,
args: {
"profileName": bolusProfile.name
},
),
);
} }
void onDelete(BolusProfile bolusProfile) { void onDelete(BolusProfile bolusProfile) {
BolusProfile.remove(bolusProfile.id); BolusProfile.remove(bolusProfile.id);
reload(message: 'Bolus Profile deleted'); reload(message: translate(LocalizationKeys.bolusProfile_deleted));
} }
void handleDeleteAction(BolusProfile bolusProfile) async { void handleDeleteAction(BolusProfile bolusProfile) async {
@ -124,7 +138,7 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
DialogUtils.showConfirmationDialog( DialogUtils.showConfirmationDialog(
context: context, context: context,
onConfirm: () => onDelete(bolusProfile), onConfirm: () => onDelete(bolusProfile),
message: 'Are you sure you want to delete this Bolus Profile?', message: translate(LocalizationKeys.bolusProfile_confirmDelete),
); );
} else { } else {
onDelete(bolusProfile); onDelete(bolusProfile);
@ -137,7 +151,13 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
bolusProfile.active = true; bolusProfile.active = true;
BolusProfile.put(bolusProfile); BolusProfile.put(bolusProfile);
reload( reload(
message: '${bolusProfile.name} has been set as your active Profile'); message: translate(
LocalizationKeys.bolusProfile_activated,
args: {
"profileName": bolusProfile.name
},
),
);
} }
} }
@ -147,14 +167,14 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
content: AutoCompleteDropdownButton( content: AutoCompleteDropdownButton(
controller: TextEditingController(text: ''), controller: TextEditingController(text: ''),
items: _bolusProfiles, items: _bolusProfiles,
label: 'Default Basal Profile', label: translate(LocalizationKeys.bolusProfile_default),
onChanged: onPickActive, onChanged: onPickActive,
), ),
leading: const CircleAvatar(child: Icon(Icons.info)), leading: const CircleAvatar(child: Icon(Icons.info)),
forceActionsBelow: true, forceActionsBelow: true,
actions: [ actions: [
TextButton( TextButton(
child: const Text('CREATE A NEW PROFILE INSTEAD'), child: Text(translate(LocalizationKeys.bolusProfile_warnings_resolve_createInstead).toUpperCase()),
onPressed: () => onNew(true), onPressed: () => onNew(true),
), ),
], ],
@ -184,7 +204,7 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: const Text('Bolus Profiles'), title: Text(translate(LocalizationKeys.bolusProfile_title)),
actions: <Widget>[ actions: <Widget>[
IconButton(onPressed: reload, icon: const Icon(Icons.refresh)) IconButton(onPressed: reload, icon: const Icon(Icons.refresh))
], ],
@ -206,9 +226,9 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
itemBuilder: (context, index) { itemBuilder: (context, index) {
final bolusProfile = _bolusProfiles[index]; final bolusProfile = _bolusProfiles[index];
String activeProfileText = bolusProfile.active String activeProfileText = bolusProfile.active
? ' (Default Profile)' ? ' (${translate(LocalizationKeys.bolusProfile_default)})'
: bolusProfile.id == _activeProfile?.id : bolusProfile.id == _activeProfile?.id
? ' (Current Active Profile)' ? ' (${translate(LocalizationKeys.bolusProfile_active)})'
: ''; : '';
return Card( return Card(
child: ListTile( child: ListTile(
@ -250,8 +270,8 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
}, },
), ),
) )
: const Center( : Center(
child: Text('You have not created any Bolus Profiles yet!'), child: Text(translate(LocalizationKeys.bolusProfile_empty)),
), ),
), ),
], ],

View File

@ -1,12 +1,14 @@
import 'package:diameter/components/detail.dart'; import 'package:diameter/components/detail.dart';
import 'package:diameter/components/forms/boolean_form_field.dart'; import 'package:diameter/components/forms/boolean_form_field.dart';
import 'package:diameter/components/forms/number_form_field.dart'; import 'package:diameter/components/forms/number_form_field.dart';
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart'; import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/models/settings.dart'; import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:diameter/components/forms/form_wrapper.dart'; import 'package:diameter/components/forms/form_wrapper.dart';
import 'package:diameter/models/accuracy.dart'; import 'package:diameter/models/accuracy.dart';
import 'package:flutter_translate/flutter_translate.dart';
class AccuracyDetailScreen extends StatefulWidget { class AccuracyDetailScreen extends StatefulWidget {
static const String routeName = '/accuracy'; static const String routeName = '/accuracy';
@ -93,7 +95,12 @@ class _AccuracyDetailScreenState extends State<AccuracyDetailScreen> {
Accuracy.reorder( Accuracy.reorder(
accuracy, int.tryParse(_confidenceRatingController.text)); accuracy, int.tryParse(_confidenceRatingController.text));
Navigator.pop( Navigator.pop(
context, ['${_isNew ? 'New' : ''} Accuracy saved', accuracy]); context, [translate(
LocalizationKeys.accuracy_saved,
args: {
"status": _isNew ? '${translate(LocalizationKeys.accuracy_new)} ' : ''
},
), accuracy]);
} }
setState(() { setState(() {
_isSaving = false; _isSaving = false;
@ -130,7 +137,9 @@ class _AccuracyDetailScreenState extends State<AccuracyDetailScreen> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(_isNew ? 'New Accuracy' : _accuracy!.value), title: Text(translate(LocalizationKeys.accuracy_detail_title, args: {
"status": _isNew ? '${translate(LocalizationKeys.accuracy_new)} ' : '',
})),
), ),
drawer: const Navigation(currentLocation: AccuracyDetailScreen.routeName), drawer: const Navigation(currentLocation: AccuracyDetailScreen.routeName),
body: Scrollbar( body: Scrollbar(
@ -145,12 +154,12 @@ class _AccuracyDetailScreenState extends State<AccuracyDetailScreen> {
fields: [ fields: [
TextFormField( TextFormField(
controller: _valueController, controller: _valueController,
decoration: const InputDecoration( decoration: InputDecoration(
labelText: 'Name', labelText: translate(LocalizationKeys.accuracy_fields_name),
), ),
validator: (value) { validator: (value) {
if (value!.trim().isEmpty) { if (value!.trim().isEmpty) {
return 'Empty name'; return translate(LocalizationKeys.accuracy_fields_validators_name);
} }
return null; return null;
}, },
@ -158,7 +167,7 @@ class _AccuracyDetailScreenState extends State<AccuracyDetailScreen> {
BooleanFormField( BooleanFormField(
icon: const Icon(Icons.square_foot), icon: const Icon(Icons.square_foot),
value: _forPortionSize, value: _forPortionSize,
label: 'for portion size', label: translate(LocalizationKeys.accuracy_fields_forPortionSize),
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
_forPortionSize = value; _forPortionSize = value;
@ -168,7 +177,7 @@ class _AccuracyDetailScreenState extends State<AccuracyDetailScreen> {
BooleanFormField( BooleanFormField(
icon: const Icon(Icons.pie_chart), icon: const Icon(Icons.pie_chart),
value: _forCarbsRatio, value: _forCarbsRatio,
label: 'for carbs ratio', label: translate(LocalizationKeys.accuracy_fields_forCarbsRatio),
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
_forCarbsRatio = value; _forCarbsRatio = value;
@ -177,7 +186,7 @@ class _AccuracyDetailScreenState extends State<AccuracyDetailScreen> {
), ),
NumberFormField( NumberFormField(
controller: _confidenceRatingController, controller: _confidenceRatingController,
label: 'Confidence Rating', label: translate(LocalizationKeys.accuracy_fields_confidenceRating),
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
_confidenceRatingController.text = _confidenceRatingController.text =
@ -188,8 +197,8 @@ class _AccuracyDetailScreenState extends State<AccuracyDetailScreen> {
TextFormField( TextFormField(
controller: _notesController, controller: _notesController,
keyboardType: TextInputType.multiline, keyboardType: TextInputType.multiline,
decoration: const InputDecoration( decoration: InputDecoration(
labelText: 'Notes', labelText: translate(LocalizationKeys.accuracy_fields_notes),
), ),
minLines: 2, minLines: 2,
maxLines: 5, maxLines: 5,

View File

@ -1,9 +1,11 @@
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart'; import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/models/settings.dart'; import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:diameter/screens/category/accuracy_detail.dart'; import 'package:diameter/screens/category/accuracy_detail.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:diameter/models/accuracy.dart'; import 'package:diameter/models/accuracy.dart';
import 'package:flutter_translate/flutter_translate.dart';
class AccuracyListScreen extends StatefulWidget { class AccuracyListScreen extends StatefulWidget {
static const String routeName = '/accuracies'; static const String routeName = '/accuracies';
@ -58,7 +60,7 @@ class _AccuracyListScreenState extends State<AccuracyListScreen> {
DialogUtils.showConfirmationDialog( DialogUtils.showConfirmationDialog(
context: context, context: context,
onConfirm: () => onDelete(accuracy), onConfirm: () => onDelete(accuracy),
message: 'Are you sure you want to delete this Accuracy?', message: translate(LocalizationKeys.accuracy_confirmDelete),
); );
} else { } else {
onDelete(accuracy); onDelete(accuracy);
@ -81,7 +83,7 @@ class _AccuracyListScreenState extends State<AccuracyListScreen> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: const Text('Accuracies'), title: Text(translate(LocalizationKeys.accuracy_detail)),
actions: <Widget>[ actions: <Widget>[
IconButton(onPressed: reload, icon: const Icon(Icons.refresh)) IconButton(onPressed: reload, icon: const Icon(Icons.refresh))
], ],
@ -164,8 +166,8 @@ class _AccuracyListScreenState extends State<AccuracyListScreen> {
); );
}), }),
) )
: const Center( : Center(
child: Text('You have not created any Accuracies yet!'), child: Text(translate(LocalizationKeys.accuracy_empty)),
), ),
), ),
], ],

View File

@ -1,5 +1,7 @@
import 'package:diameter/localization_keys.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
class CategoryOverviewScreen extends StatefulWidget { class CategoryOverviewScreen extends StatefulWidget {
static const String routeName = '/category'; static const String routeName = '/category';
@ -22,7 +24,7 @@ class _CategoryOverviewScreenState extends State<CategoryOverviewScreen> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: const Text('Reports'), title: Text(translate(LocalizationKeys.categories)),
), ),
drawer: drawer:
const Navigation(currentLocation: CategoryOverviewScreen.routeName), const Navigation(currentLocation: CategoryOverviewScreen.routeName),
@ -45,7 +47,7 @@ class _CategoryOverviewScreenState extends State<CategoryOverviewScreen> {
child: Icon(Icons.local_grocery_store, size: 50, color: Theme.of(context).textTheme.subtitle2?.color), child: Icon(Icons.local_grocery_store, size: 50, color: Theme.of(context).textTheme.subtitle2?.color),
), ),
Text( Text(
'MEAL SOURCES', translate(LocalizationKeys.mealSource_title).toUpperCase(),
style: Theme.of(context).textTheme.subtitle2, style: Theme.of(context).textTheme.subtitle2,
textAlign: TextAlign.center textAlign: TextAlign.center
), ),
@ -66,7 +68,7 @@ class _CategoryOverviewScreenState extends State<CategoryOverviewScreen> {
child: Icon(Icons.category, size: 50, color: Theme.of(context).textTheme.subtitle2?.color), child: Icon(Icons.category, size: 50, color: Theme.of(context).textTheme.subtitle2?.color),
), ),
Text( Text(
'MEAL CATEGORIES', translate(LocalizationKeys.mealCategory_title).toUpperCase(),
style: Theme.of(context).textTheme.subtitle2, style: Theme.of(context).textTheme.subtitle2,
textAlign: TextAlign.center textAlign: TextAlign.center
), ),
@ -86,7 +88,7 @@ class _CategoryOverviewScreenState extends State<CategoryOverviewScreen> {
child: Icon(Icons.pie_chart, size: 50, color: Theme.of(context).textTheme.subtitle2?.color), child: Icon(Icons.pie_chart, size: 50, color: Theme.of(context).textTheme.subtitle2?.color),
), ),
Text( Text(
'PORTION TYPES', translate(LocalizationKeys.portionType_title).toUpperCase(),
style: Theme.of(context).textTheme.subtitle2, style: Theme.of(context).textTheme.subtitle2,
textAlign: TextAlign.center textAlign: TextAlign.center
), ),
@ -107,7 +109,7 @@ class _CategoryOverviewScreenState extends State<CategoryOverviewScreen> {
child: Icon(Icons.architecture, size: 50, color: Theme.of(context).textTheme.subtitle2?.color), child: Icon(Icons.architecture, size: 50, color: Theme.of(context).textTheme.subtitle2?.color),
), ),
Text( Text(
'ACCURACIES', translate(LocalizationKeys.accuracy_title).toUpperCase(),
style: Theme.of(context).textTheme.subtitle2, style: Theme.of(context).textTheme.subtitle2,
textAlign: TextAlign.center textAlign: TextAlign.center
), ),
@ -127,7 +129,7 @@ class _CategoryOverviewScreenState extends State<CategoryOverviewScreen> {
child: Icon(Icons.event, size: 50, color: Theme.of(context).textTheme.subtitle2?.color), child: Icon(Icons.event, size: 50, color: Theme.of(context).textTheme.subtitle2?.color),
), ),
Text( Text(
'EVENT TYPES', translate(LocalizationKeys.eventType_title).toUpperCase(),
style: Theme.of(context).textTheme.subtitle2, style: Theme.of(context).textTheme.subtitle2,
textAlign: TextAlign.center textAlign: TextAlign.center
), ),

View File

@ -1,6 +1,7 @@
import 'package:diameter/components/detail.dart'; import 'package:diameter/components/detail.dart';
import 'package:diameter/components/forms/boolean_form_field.dart'; import 'package:diameter/components/forms/boolean_form_field.dart';
import 'package:diameter/components/forms/duration_form_field.dart'; import 'package:diameter/components/forms/duration_form_field.dart';
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart'; import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/components/forms/auto_complete_dropdown_button.dart'; import 'package:diameter/components/forms/auto_complete_dropdown_button.dart';
import 'package:diameter/components/forms/form_wrapper.dart'; import 'package:diameter/components/forms/form_wrapper.dart';
@ -12,6 +13,7 @@ import 'package:diameter/navigation.dart';
import 'package:diameter/screens/basal/basal_profile_detail.dart'; import 'package:diameter/screens/basal/basal_profile_detail.dart';
import 'package:diameter/screens/bolus/bolus_profile_detail.dart'; import 'package:diameter/screens/bolus/bolus_profile_detail.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
class EventTypeDetailScreen extends StatefulWidget { class EventTypeDetailScreen extends StatefulWidget {
static const String routeName = '/log-event-type'; static const String routeName = '/log-event-type';
@ -126,7 +128,9 @@ class _EventTypeDetailScreenState extends State<EventTypeDetailScreen> {
eventType.bolusProfile.target = _bolusProfile; eventType.bolusProfile.target = _bolusProfile;
LogEventType.put(eventType); LogEventType.put(eventType);
Navigator.pop( Navigator.pop(
context, ['${_isNew ? 'New' : ''} Log Event Type Saved', eventType]); context, [translate(LocalizationKeys.eventType_saved, args: {
"status": _isNew ? LocalizationKeys.eventType_new : '',
}), eventType]);
} }
setState(() { setState(() {
_isSaving = false; _isSaving = false;
@ -161,7 +165,10 @@ class _EventTypeDetailScreenState extends State<EventTypeDetailScreen> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(_isNew ? 'New Log Event Type' : _logEventType!.value), title: Text(translate(LocalizationKeys.eventType_detail_title, args: {
"status": _isNew ? LocalizationKeys.eventType_new : LocalizationKeys.general_edit,
"name": _isNew ? '' : _logEventType!.value,
}))
), ),
drawer: drawer:
const Navigation(currentLocation: EventTypeDetailScreen.routeName), const Navigation(currentLocation: EventTypeDetailScreen.routeName),
@ -175,19 +182,19 @@ class _EventTypeDetailScreenState extends State<EventTypeDetailScreen> {
FormWrapper(formState: _logEventTypeForm, fields: [ FormWrapper(formState: _logEventTypeForm, fields: [
TextFormField( TextFormField(
controller: _valueController, controller: _valueController,
decoration: const InputDecoration( decoration: InputDecoration(
labelText: 'Name', labelText: translate(LocalizationKeys.eventType_fields_name),
), ),
validator: (value) { validator: (value) {
if (value!.trim().isEmpty) { if (value!.trim().isEmpty) {
return 'Empty name'; return translate(LocalizationKeys.eventType_fields_validators_name);
} }
return null; return null;
}, },
), ),
BooleanFormField( BooleanFormField(
value: _hasEndTime, value: _hasEndTime,
label: 'has end time', label: translate(LocalizationKeys.eventType_fields_hasEndTime),
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
_hasEndTime = value; _hasEndTime = value;
@ -201,7 +208,7 @@ class _EventTypeDetailScreenState extends State<EventTypeDetailScreen> {
padding: const EdgeInsets.only(bottom: 10.0), padding: const EdgeInsets.only(bottom: 10.0),
child: DurationFormField( child: DurationFormField(
minutes: _defaultReminderDuration, minutes: _defaultReminderDuration,
label: 'Default Reminder Duration', label: translate(LocalizationKeys.eventType_fields_defaultReminderDuration),
onChanged: (value) => _defaultReminderDuration = value ?? 0, onChanged: (value) => _defaultReminderDuration = value ?? 0,
showSteppers: true, showSteppers: true,
), ),
@ -215,7 +222,7 @@ class _EventTypeDetailScreenState extends State<EventTypeDetailScreen> {
BolusProfile>( BolusProfile>(
selectedItem: _bolusProfile, selectedItem: _bolusProfile,
controller: _bolusProfileController, controller: _bolusProfileController,
label: 'Bolus Profile', label: translate(LocalizationKeys.eventType_fields_bolusProfile),
items: _bolusProfiles, items: _bolusProfiles,
onChanged: updateBolusProfile, onChanged: updateBolusProfile,
), ),
@ -252,7 +259,7 @@ class _EventTypeDetailScreenState extends State<EventTypeDetailScreen> {
AutoCompleteDropdownButton<BasalProfile>( AutoCompleteDropdownButton<BasalProfile>(
controller: _basalProfileController, controller: _basalProfileController,
selectedItem: _basalProfile, selectedItem: _basalProfile,
label: 'Basal Profile', label: translate(LocalizationKeys.eventType_fields_basalProfile),
items: _basalProfiles, items: _basalProfiles,
onChanged: updateBasalProfile, onChanged: updateBasalProfile,
), ),
@ -283,8 +290,8 @@ class _EventTypeDetailScreenState extends State<EventTypeDetailScreen> {
: []), : []),
TextFormField( TextFormField(
controller: _notesController, controller: _notesController,
decoration: const InputDecoration( decoration: InputDecoration(
labelText: 'Notes', labelText: translate(LocalizationKeys.eventType_fields_notes),
), ),
keyboardType: TextInputType.multiline, keyboardType: TextInputType.multiline,
minLines: 2, minLines: 2,

View File

@ -1,7 +1,9 @@
import 'package:diameter/localization_keys.dart';
import 'package:diameter/models/log_event_type.dart'; import 'package:diameter/models/log_event_type.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:diameter/screens/category/event_type_detail.dart'; import 'package:diameter/screens/category/event_type_detail.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
class EventTypeListScreen extends StatefulWidget { class EventTypeListScreen extends StatefulWidget {
static const String routeName = '/log-event-types'; static const String routeName = '/log-event-types';
@ -48,7 +50,7 @@ class _EventTypeListScreenState extends State<EventTypeListScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar(title: const Text('Log Event Types'), actions: <Widget>[ appBar: AppBar(title: Text(translate(LocalizationKeys.eventType_title)), actions: <Widget>[
IconButton(onPressed: reload, icon: const Icon(Icons.refresh)) IconButton(onPressed: reload, icon: const Icon(Icons.refresh))
]), ]),
drawer: drawer:
@ -88,7 +90,7 @@ class _EventTypeListScreenState extends State<EventTypeListScreen> {
IconButton( IconButton(
onPressed: () async { onPressed: () async {
LogEventType.remove(logEventType.id); LogEventType.remove(logEventType.id);
reload(message: 'Log Event Type deleted'); reload(message: translate(LocalizationKeys.eventType_deleted));
}, },
icon: const Icon(Icons.delete, icon: const Icon(Icons.delete,
color: Colors.blue), color: Colors.blue),
@ -100,9 +102,9 @@ class _EventTypeListScreenState extends State<EventTypeListScreen> {
}, },
), ),
) )
: const Center( : Center(
child: child:
Text('You have not created any Log Event Types yet!'), Text(translate(LocalizationKeys.eventType_empty)),
), ),
), ),
], ],

View File

@ -1,10 +1,12 @@
import 'package:diameter/components/detail.dart'; import 'package:diameter/components/detail.dart';
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart'; import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/components/forms/form_wrapper.dart'; import 'package:diameter/components/forms/form_wrapper.dart';
import 'package:diameter/models/settings.dart'; import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:diameter/models/meal_category.dart'; import 'package:diameter/models/meal_category.dart';
import 'package:flutter_translate/flutter_translate.dart';
class MealCategoryDetailScreen extends StatefulWidget { class MealCategoryDetailScreen extends StatefulWidget {
static const String routeName = '/meal-category'; static const String routeName = '/meal-category';
@ -76,7 +78,9 @@ class _MealCategoryDetailScreenState extends State<MealCategoryDetailScreen> {
); );
MealCategory.put(mealCategory); MealCategory.put(mealCategory);
Navigator.pop(context, [ Navigator.pop(context, [
'${_isNew ? 'New' : ''} Meal Category saved', mealCategory translate(LocalizationKeys.mealCategory_saved, args: {
"status": _isNew ? LocalizationKeys.mealCategory_new : '',
}), mealCategory
]); ]);
} }
} }
@ -102,7 +106,11 @@ class _MealCategoryDetailScreenState extends State<MealCategoryDetailScreen> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(_isNew ? 'New Meal Category' : _mealCategory!.value), title: Text(translate(LocalizationKeys.mealCategory_detail_title, args: {
"status": _isNew ? LocalizationKeys.mealCategory_new : '',
"name": _mealCategory!.value
}),
),
), ),
drawer: drawer:
const Navigation(currentLocation: MealCategoryDetailScreen.routeName), const Navigation(currentLocation: MealCategoryDetailScreen.routeName),
@ -118,20 +126,20 @@ class _MealCategoryDetailScreenState extends State<MealCategoryDetailScreen> {
fields: [ fields: [
TextFormField( TextFormField(
controller: _valueController, controller: _valueController,
decoration: const InputDecoration( decoration: InputDecoration(
labelText: 'Name', labelText: translate(LocalizationKeys.mealCategory_fields_name),
), ),
validator: (value) { validator: (value) {
if (value!.trim().isEmpty) { if (value!.trim().isEmpty) {
return 'Empty name'; return translate(LocalizationKeys.mealCategory_fields_validators_name);
} }
return null; return null;
}, },
), ),
TextFormField( TextFormField(
controller: _notesController, controller: _notesController,
decoration: const InputDecoration( decoration: InputDecoration(
labelText: 'Notes', labelText: translate(LocalizationKeys.mealCategory_fields_notes),
), ),
keyboardType: TextInputType.multiline, keyboardType: TextInputType.multiline,
minLines: 2, minLines: 2,

View File

@ -1,9 +1,11 @@
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart'; import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/models/settings.dart'; import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:diameter/screens/category/meal_category_detail.dart'; import 'package:diameter/screens/category/meal_category_detail.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:diameter/models/meal_category.dart'; import 'package:diameter/models/meal_category.dart';
import 'package:flutter_translate/flutter_translate.dart';
class MealCategoryListScreen extends StatefulWidget { class MealCategoryListScreen extends StatefulWidget {
static const String routeName = '/meal-categories'; static const String routeName = '/meal-categories';
@ -50,7 +52,7 @@ class _MealCategoryListScreenState extends State<MealCategoryListScreen> {
void onDelete(MealCategory mealCategory) { void onDelete(MealCategory mealCategory) {
MealCategory.remove(mealCategory.id); MealCategory.remove(mealCategory.id);
reload(message: 'Meal Category deleted'); reload(message: translate(LocalizationKeys.mealCategory_deleted));
} }
void handleDeleteAction(MealCategory mealCategory) async { void handleDeleteAction(MealCategory mealCategory) async {
@ -58,7 +60,7 @@ class _MealCategoryListScreenState extends State<MealCategoryListScreen> {
DialogUtils.showConfirmationDialog( DialogUtils.showConfirmationDialog(
context: context, context: context,
onConfirm: () => onDelete(mealCategory), onConfirm: () => onDelete(mealCategory),
message: 'Are you sure you want to delete this Meal Category?', message: translate(LocalizationKeys.mealCategory_confirmDelete),
); );
} else { } else {
onDelete(mealCategory); onDelete(mealCategory);
@ -69,7 +71,7 @@ class _MealCategoryListScreenState extends State<MealCategoryListScreen> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: const Text('Meal Categories'), title: Text(translate(LocalizationKeys.mealCategory_title)),
actions: <Widget>[ actions: <Widget>[
IconButton( IconButton(
onPressed: reload, onPressed: reload,
@ -125,8 +127,8 @@ class _MealCategoryListScreenState extends State<MealCategoryListScreen> {
); );
}, },
), ),
): const Center( ): Center(
child: Text('You have not created any Meal Categories yet!'), child: Text(translate(LocalizationKeys.mealCategory_empty)),
), ),
), ),
], ],

View File

@ -1,10 +1,12 @@
import 'package:diameter/components/detail.dart'; import 'package:diameter/components/detail.dart';
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart'; import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/components/forms/form_wrapper.dart'; import 'package:diameter/components/forms/form_wrapper.dart';
import 'package:diameter/models/settings.dart'; import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:diameter/models/meal_portion_type.dart'; import 'package:diameter/models/meal_portion_type.dart';
import 'package:flutter_translate/flutter_translate.dart';
class MealPortionTypeDetailScreen extends StatefulWidget { class MealPortionTypeDetailScreen extends StatefulWidget {
static const String routeName = '/meal-portion-type'; static const String routeName = '/meal-portion-type';
@ -77,8 +79,10 @@ class _MealPortionTypeDetailScreenState
notes: _notesController.text, notes: _notesController.text,
); );
MealPortionType.put(mealPortionType); MealPortionType.put(mealPortionType);
Navigator.pop(context, Navigator.pop(context,
['${_isNew ? 'New' : ''} Meal Portion Type saved', mealPortionType]); [translate(LocalizationKeys.portionType_saved, args: {
"status": _isNew ? LocalizationKeys.portionType_new : '',
}), mealPortionType]);
} }
} }
@ -102,10 +106,12 @@ class _MealPortionTypeDetailScreenState
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
bool isNew = _mealPortionType == null;
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(isNew ? 'New Meal Portion Type' : _mealPortionType!.value), title: Text(translate(LocalizationKeys.portionType_detail_title, args: {
"status": translate(_isNew ? LocalizationKeys.portionType_new : LocalizationKeys.general_edit),
"name": _mealPortionType!.value,
})),
), ),
drawer: const Navigation( drawer: const Navigation(
currentLocation: MealPortionTypeDetailScreen.routeName), currentLocation: MealPortionTypeDetailScreen.routeName),
@ -121,20 +127,20 @@ class _MealPortionTypeDetailScreenState
fields: [ fields: [
TextFormField( TextFormField(
controller: _valueController, controller: _valueController,
decoration: const InputDecoration( decoration: InputDecoration(
labelText: 'Name', labelText: translate(LocalizationKeys.portionType_fields_name),
), ),
validator: (value) { validator: (value) {
if (value!.trim().isEmpty) { if (value!.trim().isEmpty) {
return 'Empty name'; return translate(LocalizationKeys.portionType_fields_validators_name);
} }
return null; return null;
}, },
), ),
TextFormField( TextFormField(
controller: _notesController, controller: _notesController,
decoration: const InputDecoration( decoration: InputDecoration(
labelText: 'Notes', labelText: translate(LocalizationKeys.portionType_fields_notes),
), ),
keyboardType: TextInputType.multiline, keyboardType: TextInputType.multiline,
minLines: 2, minLines: 2,

View File

@ -1,9 +1,11 @@
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart'; import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/models/settings.dart'; import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:diameter/screens/category/meal_portion_type_detail.dart'; import 'package:diameter/screens/category/meal_portion_type_detail.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:diameter/models/meal_portion_type.dart'; import 'package:diameter/models/meal_portion_type.dart';
import 'package:flutter_translate/flutter_translate.dart';
class MealPortionTypeListScreen extends StatefulWidget { class MealPortionTypeListScreen extends StatefulWidget {
static const String routeName = '/meal-portion-types'; static const String routeName = '/meal-portion-types';
@ -51,7 +53,7 @@ class _MealPortionTypeListScreenState extends State<MealPortionTypeListScreen> {
void onDelete(MealPortionType mealPortionType) { void onDelete(MealPortionType mealPortionType) {
MealPortionType.remove(mealPortionType.id); MealPortionType.remove(mealPortionType.id);
reload(message: 'Meal Portion Type deleted'); reload(message: translate(LocalizationKeys.portionType_deleted));
} }
void handleDeleteAction(MealPortionType mealPortionType) async { void handleDeleteAction(MealPortionType mealPortionType) async {
@ -59,7 +61,7 @@ class _MealPortionTypeListScreenState extends State<MealPortionTypeListScreen> {
DialogUtils.showConfirmationDialog( DialogUtils.showConfirmationDialog(
context: context, context: context,
onConfirm: () => onDelete(mealPortionType), onConfirm: () => onDelete(mealPortionType),
message: 'Are you sure you want to delete this Meal Portion Type?', message: translate(LocalizationKeys.portionType_confirmDelete),
); );
} else { } else {
onDelete(mealPortionType); onDelete(mealPortionType);
@ -70,7 +72,7 @@ class _MealPortionTypeListScreenState extends State<MealPortionTypeListScreen> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: const Text('Meal Portion Types'), title: Text(translate(LocalizationKeys.portionType_title)),
actions: <Widget>[ actions: <Widget>[
IconButton(onPressed: reload, icon: const Icon(Icons.refresh)) IconButton(onPressed: reload, icon: const Icon(Icons.refresh))
], ],
@ -124,8 +126,8 @@ class _MealPortionTypeListScreenState extends State<MealPortionTypeListScreen> {
); );
}, },
), ),
) : const Center( ) : Center(
child: Text('You have not created any Meal Portion Types yet!'), child: Text(translate(LocalizationKeys.portionType_empty)),
), ),
), ),
], ],

View File

@ -1,4 +1,5 @@
import 'package:diameter/components/detail.dart'; import 'package:diameter/components/detail.dart';
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart'; import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/components/forms/auto_complete_dropdown_button.dart'; import 'package:diameter/components/forms/auto_complete_dropdown_button.dart';
import 'package:diameter/components/forms/form_wrapper.dart'; import 'package:diameter/components/forms/form_wrapper.dart';
@ -12,6 +13,7 @@ import 'package:diameter/screens/category/accuracy_detail.dart';
import 'package:diameter/screens/category/meal_category_detail.dart'; import 'package:diameter/screens/category/meal_category_detail.dart';
import 'package:diameter/screens/category/meal_portion_type_detail.dart'; import 'package:diameter/screens/category/meal_portion_type_detail.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
class MealSourceDetailScreen extends StatefulWidget { class MealSourceDetailScreen extends StatefulWidget {
static const String routeName = '/meal-source'; static const String routeName = '/meal-source';
@ -121,7 +123,9 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
mealSource.defaultMealCategory.target = _defaultMealCategory; mealSource.defaultMealCategory.target = _defaultMealCategory;
mealSource.defaultMealPortionType.target = _defaultMealPortionType; mealSource.defaultMealPortionType.target = _defaultMealPortionType;
MealSource.put(mealSource); MealSource.put(mealSource);
Navigator.pop(context, ['${_isNew ? 'New' : ''} Meal Source saved', mealSource]); Navigator.pop(context, [translate(LocalizationKeys.mealSource_saved, args: {
"status": _isNew ? LocalizationKeys.mealSource_new : ''
}), mealSource]);
} }
void handleCancelAction() { void handleCancelAction() {
@ -158,7 +162,12 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(_isNew ? 'New Meal Source' : _mealSource!.value), title: Text(
translate(LocalizationKeys.mealSource_saved, args: {
"status": _isNew ? LocalizationKeys.mealSource_new : LocalizationKeys.general_edit,
"name": _mealSource!.value,
})
),
), ),
drawer: drawer:
const Navigation(currentLocation: MealSourceDetailScreen.routeName), const Navigation(currentLocation: MealSourceDetailScreen.routeName),
@ -174,12 +183,12 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
fields: [ fields: [
TextFormField( TextFormField(
controller: _valueController, controller: _valueController,
decoration: const InputDecoration( decoration: InputDecoration(
labelText: 'Name', labelText: translate(LocalizationKeys.mealSource_fields_name),
), ),
validator: (value) { validator: (value) {
if (value!.trim().isEmpty) { if (value!.trim().isEmpty) {
return 'Empty name'; return translate(LocalizationKeys.mealSource_fields_validators_name);
} }
return null; return null;
}, },
@ -190,7 +199,7 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
child: AutoCompleteDropdownButton<Accuracy>( child: AutoCompleteDropdownButton<Accuracy>(
selectedItem: _defaultCarbsRatioAccuracy, selectedItem: _defaultCarbsRatioAccuracy,
controller: _defaultCarbsRatioAccuracyController, controller: _defaultCarbsRatioAccuracyController,
label: 'Default Carbs Ratio Accuracy', label: translate(LocalizationKeys.mealSource_fields_defaultCarbsRatioAccuracy),
items: _carbsRatioAccuracies, items: _carbsRatioAccuracies,
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
@ -233,7 +242,7 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
child: AutoCompleteDropdownButton<Accuracy>( child: AutoCompleteDropdownButton<Accuracy>(
selectedItem: _defaultPortionSizeAccuracy, selectedItem: _defaultPortionSizeAccuracy,
controller: _defaultPortionSizeAccuracyController, controller: _defaultPortionSizeAccuracyController,
label: 'Default Portion Size Accuracy', label: translate(LocalizationKeys.mealSource_fields_defaultPortionSizeAccuracy),
items: _portionSizeAccuracies, items: _portionSizeAccuracies,
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
@ -278,7 +287,7 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
child: AutoCompleteDropdownButton<MealCategory>( child: AutoCompleteDropdownButton<MealCategory>(
selectedItem: _defaultMealCategory, selectedItem: _defaultMealCategory,
controller: _defaultMealCategoryController, controller: _defaultMealCategoryController,
label: 'Default Meal Category', label: translate(LocalizationKeys.mealSource_fields_defaultMealCategory),
items: _mealCategories, items: _mealCategories,
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
@ -320,7 +329,7 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
child: AutoCompleteDropdownButton<MealPortionType>( child: AutoCompleteDropdownButton<MealPortionType>(
selectedItem: _defaultMealPortionType, selectedItem: _defaultMealPortionType,
controller: _defaultMealPortionTypeController, controller: _defaultMealPortionTypeController,
label: 'Default Meal Portion Type', label: translate(LocalizationKeys.mealSource_fields_defaultMealPortionType),
items: _mealPortionTypes, items: _mealPortionTypes,
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
@ -359,8 +368,8 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
), ),
TextFormField( TextFormField(
controller: _notesController, controller: _notesController,
decoration: const InputDecoration( decoration: InputDecoration(
labelText: 'Notes', labelText: translate(LocalizationKeys.mealSource_fields_notes),
), ),
keyboardType: TextInputType.multiline, keyboardType: TextInputType.multiline,
minLines: 2, minLines: 2,

View File

@ -1,9 +1,11 @@
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart'; import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/models/meal_source.dart'; import 'package:diameter/models/meal_source.dart';
import 'package:diameter/models/settings.dart'; import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:diameter/screens/category/meal_source_detail.dart'; import 'package:diameter/screens/category/meal_source_detail.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
class MealSourceListScreen extends StatefulWidget { class MealSourceListScreen extends StatefulWidget {
static const String routeName = '/meal-sources'; static const String routeName = '/meal-sources';
@ -50,7 +52,7 @@ class _MealSourceListScreenState extends State<MealSourceListScreen> {
void onDelete(MealSource mealSource) { void onDelete(MealSource mealSource) {
MealSource.remove(mealSource.id); MealSource.remove(mealSource.id);
reload(message: 'Meal Source deleted'); reload(message: translate(LocalizationKeys.mealSource_deleted));
} }
void handleDeleteAction(MealSource mealSource) async { void handleDeleteAction(MealSource mealSource) async {
@ -58,7 +60,7 @@ class _MealSourceListScreenState extends State<MealSourceListScreen> {
DialogUtils.showConfirmationDialog( DialogUtils.showConfirmationDialog(
context: context, context: context,
onConfirm: () => onDelete(mealSource), onConfirm: () => onDelete(mealSource),
message: 'Are you sure you want to delete this Meal Source?', message: translate(LocalizationKeys.mealSource_confirmDelete),
); );
} else { } else {
onDelete(mealSource); onDelete(mealSource);
@ -69,7 +71,7 @@ class _MealSourceListScreenState extends State<MealSourceListScreen> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: const Text('Meal Sources'), title: Text(translate(LocalizationKeys.mealSource_title)),
actions: <Widget>[ actions: <Widget>[
IconButton(onPressed: reload, icon: const Icon(Icons.refresh)) IconButton(onPressed: reload, icon: const Icon(Icons.refresh))
], ],
@ -122,8 +124,8 @@ class _MealSourceListScreenState extends State<MealSourceListScreen> {
); );
} }
), ),
) : const Center( ) : Center(
child: Text('You have not created any Meal Sources yet!'), child: Text(translate(LocalizationKeys.mealSource_empty)),
), ),
), ),
], ],

View File

@ -3,6 +3,7 @@ import 'dart:math';
import 'package:diameter/components/detail.dart'; import 'package:diameter/components/detail.dart';
import 'package:diameter/components/forms/boolean_form_field.dart'; import 'package:diameter/components/forms/boolean_form_field.dart';
import 'package:diameter/components/forms/number_form_field.dart'; import 'package:diameter/components/forms/number_form_field.dart';
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart'; import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/components/forms/auto_complete_dropdown_button.dart'; import 'package:diameter/components/forms/auto_complete_dropdown_button.dart';
import 'package:diameter/components/forms/form_wrapper.dart'; import 'package:diameter/components/forms/form_wrapper.dart';
@ -15,6 +16,7 @@ import 'package:diameter/navigation.dart';
import 'package:diameter/screens/log/log_entry/log_meal_detail.dart'; import 'package:diameter/screens/log/log_entry/log_meal_detail.dart';
import 'package:diameter/utils/utils.dart'; import 'package:diameter/utils/utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
enum BolusType { enum BolusType {
meal, meal,
@ -453,7 +455,9 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
} }
Navigator.pop(context, Navigator.pop(context,
['${_isNew ? 'New' : ''} Bolus Saved', logBolus, delayedBolus]); [translate(LocalizationKeys.log_detail_tabs_bolus_saved, args: {
"status": _isNew ? LocalizationKeys.log_detail_tabs_bolus_new : ''
}), logBolus, delayedBolus]);
} }
setState(() { setState(() {
_isSaving = false; _isSaving = false;
@ -509,7 +513,9 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(_isNew ? 'New Bolus' : 'Edit Bolus'), title: Text(translate(LocalizationKeys.log_detail_tabs_bolus_detail_title, args: {
"status": _isNew ? LocalizationKeys.log_detail_tabs_bolus_new : LocalizationKeys.general_edit
})),
), ),
drawer: const Navigation(currentLocation: LogBolusDetailScreen.routeName), drawer: const Navigation(currentLocation: LogBolusDetailScreen.routeName),
body: Scrollbar( body: Scrollbar(
@ -544,7 +550,7 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
contentPadding: const EdgeInsets.only( contentPadding: const EdgeInsets.only(
left: 10.0, right: 10.0, top: 10.0), left: 10.0, right: 10.0, top: 10.0),
value: _setManually, value: _setManually,
label: 'set manually', label: translate(LocalizationKeys.log_detail_tabs_bolus_detail_fields_setManually),
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
_setManually = value; _setManually = value;
@ -559,7 +565,7 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
children: [ children: [
Expanded( Expanded(
child: RadioListTile( child: RadioListTile(
title: const Text('for glucose'), title: Text(translate(LocalizationKeys.log_detail_tabs_bolus_detail_fields_forGlucose)),
groupValue: _bolusType, groupValue: _bolusType,
value: BolusType.glucose, value: BolusType.glucose,
onChanged: (_) { onChanged: (_) {
@ -571,7 +577,7 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
), ),
Expanded( Expanded(
child: RadioListTile( child: RadioListTile(
title: const Text('for meal'), title: Text(translate(LocalizationKeys.log_detail_tabs_bolus_detail_fields_forMeal)),
groupValue: _bolusType, groupValue: _bolusType,
value: BolusType.meal, value: BolusType.meal,
onChanged: (value) { onChanged: (value) {
@ -599,8 +605,8 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
padding: padding:
const EdgeInsets.only(right: 5.0), const EdgeInsets.only(right: 5.0),
child: NumberFormField( child: NumberFormField(
label: 'Current', label: translate(LocalizationKeys.log_detail_tabs_bolus_detail_fields_current),
suffix: 'mg/dl', suffix: Settings.glucoseMeasurementSuffix,
controller: controller:
_mgPerDlCurrentController, _mgPerDlCurrentController,
onChanged: (_) => onChangeGlucose(), onChanged: (_) => onChangeGlucose(),
@ -613,8 +619,8 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 5.0), horizontal: 5.0),
child: NumberFormField( child: NumberFormField(
label: 'Target', label: translate(LocalizationKeys.log_detail_tabs_bolus_detail_fields_target),
suffix: 'mg/dl', suffix: Settings.glucoseMeasurementSuffix,
controller: controller:
_mgPerDlTargetController, _mgPerDlTargetController,
onChanged: (_) => onChangeGlucose(), onChanged: (_) => onChangeGlucose(),
@ -627,9 +633,9 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
padding: padding:
const EdgeInsets.only(left: 5.0), const EdgeInsets.only(left: 5.0),
child: TextFormField( child: TextFormField(
decoration: const InputDecoration( decoration: InputDecoration(
labelText: 'Correction', labelText: translate(LocalizationKeys.log_detail_tabs_bolus_detail_fields_correction),
suffixText: 'mg/dl', suffixText: Settings.glucoseMeasurementSuffix,
), ),
controller: controller:
_mgPerDlCorrectionController, _mgPerDlCorrectionController,
@ -661,8 +667,8 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
padding: const EdgeInsets.only( padding: const EdgeInsets.only(
right: 5.0), right: 5.0),
child: NumberFormField( child: NumberFormField(
label: 'Current', label: translate(LocalizationKeys.log_detail_tabs_bolus_detail_fields_current),
suffix: 'mmol/l', suffix: Settings.glucoseMeasurementSuffix,
controller: controller:
_mmolPerLCurrentController, _mmolPerLCurrentController,
onChanged: (_) => onChanged: (_) =>
@ -676,8 +682,8 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 5.0), horizontal: 5.0),
child: NumberFormField( child: NumberFormField(
label: 'Target', label: translate(LocalizationKeys.log_detail_tabs_bolus_detail_fields_target),
suffix: 'mmol/l', suffix: Settings.glucoseMeasurementSuffix,
controller: controller:
_mmolPerLTargetController, _mmolPerLTargetController,
onChanged: (_) => onChanged: (_) =>
@ -691,9 +697,9 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
padding: const EdgeInsets.only( padding: const EdgeInsets.only(
left: 5.0), left: 5.0),
child: TextFormField( child: TextFormField(
decoration: const InputDecoration( decoration: InputDecoration(
labelText: 'Correction', labelText: translate(LocalizationKeys.log_detail_tabs_bolus_detail_fields_correction),
suffixText: 'mmol/l', suffixText: Settings.glucoseMeasurementSuffix,
), ),
controller: controller:
_mmolPerLCorrectionController, _mmolPerLCorrectionController,
@ -713,7 +719,7 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
child: AutoCompleteDropdownButton<LogMeal>( child: AutoCompleteDropdownButton<LogMeal>(
controller: _mealController, controller: _mealController,
selectedItem: _meal, selectedItem: _meal,
label: 'Meal', label: translate(LocalizationKeys.log_detail_tabs_bolus_detail_fields_meal),
items: _logMeals, items: _logMeals,
onChanged: onSelectMeal, onChanged: onSelectMeal,
), ),
@ -758,9 +764,9 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
children: [ children: [
Expanded( Expanded(
child: TextFormField( child: TextFormField(
decoration: const InputDecoration( decoration: InputDecoration(
labelText: 'Delayed Bolus Duration', labelText: translate(LocalizationKeys.log_detail_tabs_bolus_detail_fields_delayedBolusDuration),
suffixText: ' min', suffixText: translate(LocalizationKeys.general_suffixes_mins),
), ),
controller: _delayController, controller: _delayController,
onChanged: (value) => setState(() {}), onChanged: (value) => setState(() {}),
@ -789,8 +795,8 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
child: Padding( child: Padding(
padding: const EdgeInsets.only(right: 5.0), padding: const EdgeInsets.only(right: 5.0),
child: NumberFormField( child: NumberFormField(
label: 'Immediate Bolus', label: translate(LocalizationKeys.log_detail_tabs_bolus_detail_fields_immediateBolus),
suffix: ' U', suffix: translate(LocalizationKeys.general_suffixes_units),
controller: _immediateUnitsController, controller: _immediateUnitsController,
max: double.tryParse(_unitsController.text), max: double.tryParse(_unitsController.text),
step: Settings.insulinSteps, step: Settings.insulinSteps,
@ -804,8 +810,8 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
child: Padding( child: Padding(
padding: const EdgeInsets.only(left: 5.0), padding: const EdgeInsets.only(left: 5.0),
child: NumberFormField( child: NumberFormField(
label: 'Delayed Bolus', label: translate(LocalizationKeys.log_detail_tabs_bolus_detail_fields_delayedBolus),
suffix: ' U', suffix: translate(LocalizationKeys.general_suffixes_units),
controller: _delayedUnitsController, controller: _delayedUnitsController,
max: double.tryParse(_unitsController.text), max: double.tryParse(_unitsController.text),
step: Settings.insulinSteps, step: Settings.insulinSteps,

View File

@ -1,3 +1,4 @@
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart'; import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/models/log_bolus.dart'; import 'package:diameter/models/log_bolus.dart';
import 'package:diameter/models/log_entry.dart'; import 'package:diameter/models/log_entry.dart';
@ -5,6 +6,7 @@ import 'package:diameter/models/settings.dart';
import 'package:diameter/screens/log/log_entry/log_bolus_detail.dart'; import 'package:diameter/screens/log/log_entry/log_bolus_detail.dart';
import 'package:diameter/screens/log/log_entry/log_meal_detail.dart'; import 'package:diameter/screens/log/log_entry/log_meal_detail.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
class LogBolusListScreen extends StatefulWidget { class LogBolusListScreen extends StatefulWidget {
final LogEntry logEntry; final LogEntry logEntry;
@ -61,7 +63,7 @@ class _LogBolusListScreenState extends State<LogBolusListScreen> {
void onDelete(LogBolus logBolus) { void onDelete(LogBolus logBolus) {
LogBolus.remove(logBolus.id); LogBolus.remove(logBolus.id);
reload(message: 'Bolus deleted'); reload(message: translate(LocalizationKeys.log_detail_tabs_bolus_deleted));
} }
void handleDeleteAction(LogBolus logBolus) async { void handleDeleteAction(LogBolus logBolus) async {
@ -69,7 +71,8 @@ class _LogBolusListScreenState extends State<LogBolusListScreen> {
DialogUtils.showConfirmationDialog( DialogUtils.showConfirmationDialog(
context: context, context: context,
onConfirm: () => onDelete(logBolus), onConfirm: () => onDelete(logBolus),
message: 'Are you sure you want to delete this Bolus?', message:
translate(LocalizationKeys.log_detail_tabs_bolus_confirmDelete),
); );
} else { } else {
onDelete(logBolus); onDelete(logBolus);
@ -92,17 +95,22 @@ class _LogBolusListScreenState extends State<LogBolusListScreen> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return widget.logBoli.isNotEmpty return widget.logBoli.isNotEmpty
? Scrollbar( ? Scrollbar(
controller: _scrollController, controller: _scrollController,
child: ListView.builder( child: ListView.builder(
padding: const EdgeInsets.all(10.0), padding: const EdgeInsets.all(10.0),
controller: _scrollController, controller: _scrollController,
shrinkWrap: true, shrinkWrap: true,
itemCount: widget.logBoli.length, itemCount: widget.logBoli.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final bolus = widget.logBoli[index]; final bolus = widget.logBoli[index];
String titleText = '${bolus.units} U ${(bolus.delay ?? 0) != 0 String titleText =
? ' (delayed by ${bolus.delay} min)' '${bolus.units} ${translate(LocalizationKeys.general_suffixes_units)} ${(bolus.delay ?? 0) != 0 ?
: ''}'; translate(
LocalizationKeys.log_detail_tabs_bolus_delayedBy,
args: {
"delay": '${bolus.delay} ${translate(LocalizationKeys.general_suffixes_mins)}'
}
) : ''}';
return Card( return Card(
child: ListTile( child: ListTile(
onTap: () => handleEditAction(bolus), onTap: () => handleEditAction(bolus),
@ -110,8 +118,8 @@ class _LogBolusListScreenState extends State<LogBolusListScreen> {
titleText.toUpperCase(), titleText.toUpperCase(),
style: Theme.of(context).textTheme.subtitle2, style: Theme.of(context).textTheme.subtitle2,
), ),
subtitle: Text(bolus.carbs != null ? subtitle: Text(bolus.carbs != null
'for ${(bolus.meal.target ?? '').toString()} (${bolus.carbs}${Settings.nutritionMeasurementSuffix} carbs)' ? 'for ${(bolus.meal.target ?? '').toString()} (${bolus.carbs}${Settings.nutritionMeasurementSuffix} carbs)'
: 'to correct ${Settings.glucoseMeasurement == GlucoseMeasurement.mgPerDl ? bolus.mgPerDlCorrection : bolus.mmolPerLCorrection} ${Settings.glucoseMeasurementSuffix}'), : 'to correct ${Settings.glucoseMeasurement == GlucoseMeasurement.mgPerDl ? bolus.mgPerDlCorrection : bolus.mmolPerLCorrection} ${Settings.glucoseMeasurementSuffix}'),
trailing: Row( trailing: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
@ -137,10 +145,10 @@ class _LogBolusListScreenState extends State<LogBolusListScreen> {
); );
}, },
), ),
) )
: const Center( : Center(
child: Text( child:
'You have not added any Boli to this Log Entry yet!'), Text(translate(LocalizationKeys.log_detail_tabs_bolus_empty)),
); );
} }
} }

View File

@ -2,6 +2,7 @@ import 'package:diameter/components/detail.dart';
import 'package:diameter/components/forms/date_time_form_field.dart'; import 'package:diameter/components/forms/date_time_form_field.dart';
import 'package:diameter/components/forms/number_form_field.dart'; import 'package:diameter/components/forms/number_form_field.dart';
import 'package:diameter/components/forms/time_of_day_form_field.dart'; import 'package:diameter/components/forms/time_of_day_form_field.dart';
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart'; import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/components/forms/form_wrapper.dart'; import 'package:diameter/components/forms/form_wrapper.dart';
import 'package:diameter/models/log_bolus.dart'; import 'package:diameter/models/log_bolus.dart';
@ -18,6 +19,8 @@ import 'package:diameter/utils/utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'dart:math' as math; import 'dart:math' as math;
import 'package:flutter_translate/flutter_translate.dart';
class LogEntryScreen extends StatefulWidget { class LogEntryScreen extends StatefulWidget {
static const String routeName = '/log-entry'; static const String routeName = '/log-entry';
final int id; final int id;
@ -192,7 +195,9 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
if (close) { if (close) {
Navigator.pop( Navigator.pop(
context, ['${_isNew ? 'New' : ''} Log Entry Saved', logEntry]); context, [translate(LocalizationKeys.log_saved, args: {
"status": _isNew ? LocalizationKeys.log_new : ''
}), logEntry]);
} else { } else {
if (_isNew) { if (_isNew) {
Navigator.push( Navigator.push(
@ -202,7 +207,7 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
), ),
).then((result) => Navigator.pop(context, result)); ).then((result) => Navigator.pop(context, result));
} else { } else {
reload(message: 'Log Entry Saved'); reload(message: translate(LocalizationKeys.log_saved));
} }
} }
} }
@ -306,7 +311,7 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
padding: const EdgeInsets.only(right: 5), padding: const EdgeInsets.only(right: 5),
child: DateTimeFormField( child: DateTimeFormField(
date: _time, date: _time,
label: 'Date', label: translate(LocalizationKeys.log_fields_date),
controller: _dateController, controller: _dateController,
onChanged: (newTime) { onChanged: (newTime) {
if (newTime != null) { if (newTime != null) {
@ -329,7 +334,7 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
padding: const EdgeInsets.only(left: 5), padding: const EdgeInsets.only(left: 5),
child: TimeOfDayFormField( child: TimeOfDayFormField(
time: TimeOfDay.fromDateTime(_time), time: TimeOfDay.fromDateTime(_time),
label: 'Time', label: translate(LocalizationKeys.log_fields_time),
controller: _timeController, controller: _timeController,
onChanged: (newTime) { onChanged: (newTime) {
if (newTime != null) { if (newTime != null) {
@ -363,8 +368,8 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
? 2 ? 2
: 1, : 1,
child: NumberFormField( child: NumberFormField(
label: 'Blood Glucose', label: translate(LocalizationKeys.log_fields_glucose),
suffix: 'mg/dl', suffix: Settings.glucoseMeasurementSuffix,
readOnly: Settings.glucoseMeasurement == readOnly: Settings.glucoseMeasurement ==
GlucoseMeasurement.mmolPerL, GlucoseMeasurement.mmolPerL,
showSteppers: showSteppers:
@ -388,8 +393,8 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
? 2 ? 2
: 1, : 1,
child: NumberFormField( child: NumberFormField(
label: 'Blood Glucose', label: translate(LocalizationKeys.log_fields_glucose),
suffix: 'mmol/l', suffix: Settings.glucoseMeasurementSuffix,
readOnly: Settings.glucoseMeasurement == readOnly: Settings.glucoseMeasurement ==
GlucoseMeasurement.mgPerDl, GlucoseMeasurement.mgPerDl,
showSteppers: showSteppers:
@ -421,8 +426,8 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
), ),
TextFormField( TextFormField(
controller: _notesController, controller: _notesController,
decoration: const InputDecoration( decoration: InputDecoration(
labelText: 'Notes', labelText: translate(LocalizationKeys.log_fields_notes),
), ),
keyboardType: TextInputType.multiline, keyboardType: TextInputType.multiline,
minLines: 2, minLines: 2,
@ -444,14 +449,16 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(_isNew ? 'New Log Entry' : 'Edit Log Entry'), title: Text(translate(LocalizationKeys.log_detail_title, args: {
"status": _isNew ? LocalizationKeys.log_new : LocalizationKeys.general_edit
})),
bottom: _isNew bottom: _isNew
? PreferredSize(child: Container(), preferredSize: Size.zero) ? PreferredSize(child: Container(), preferredSize: Size.zero)
: const TabBar( : TabBar(
tabs: [ tabs: [
Tab(text: 'GENERAL'), Tab(text: translate(LocalizationKeys.log_detail_tabs_general).toUpperCase()),
Tab(text: 'MEALS'), Tab(text: translate(LocalizationKeys.log_detail_tabs_meal_title).toUpperCase()),
Tab(text: 'BOLI'), Tab(text: translate(LocalizationKeys.log_detail_tabs_bolus_title).toUpperCase()),
], ],
), ),
actions: appBarActions, actions: appBarActions,

View File

@ -1,6 +1,7 @@
import 'package:diameter/components/detail.dart'; import 'package:diameter/components/detail.dart';
import 'package:diameter/components/forms/boolean_form_field.dart'; import 'package:diameter/components/forms/boolean_form_field.dart';
import 'package:diameter/components/forms/number_form_field.dart'; import 'package:diameter/components/forms/number_form_field.dart';
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart'; import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/components/forms/auto_complete_dropdown_button.dart'; import 'package:diameter/components/forms/auto_complete_dropdown_button.dart';
import 'package:diameter/components/forms/form_wrapper.dart'; import 'package:diameter/components/forms/form_wrapper.dart';
@ -19,6 +20,7 @@ import 'package:diameter/screens/category/meal_source_detail.dart';
import 'package:diameter/screens/meal/meal_detail.dart'; import 'package:diameter/screens/meal/meal_detail.dart';
import 'package:diameter/utils/utils.dart'; import 'package:diameter/utils/utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
class LogMealDetailScreen extends StatefulWidget { class LogMealDetailScreen extends StatefulWidget {
static const String routeName = '/log-meal'; static const String routeName = '/log-meal';
@ -230,7 +232,9 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
logMeal.carbsRatioAccuracy.target = _carbsRatioAccuracy; logMeal.carbsRatioAccuracy.target = _carbsRatioAccuracy;
LogMeal.put(logMeal); LogMeal.put(logMeal);
Navigator.pop(context, ['${_isNew ? 'New' : ''} Meal Saved', logMeal]); Navigator.pop(context, [translate(LocalizationKeys.log_detail_tabs_meal_saved, args: {
"status": _isNew ? translate(LocalizationKeys.log_detail_tabs_bolus_new) : ''
}), logMeal]);
} }
setState(() { setState(() {
_isSaving = false; _isSaving = false;
@ -405,7 +409,9 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(_isNew ? 'New Meal for Log Entry' : _logMeal!.value), title: Text(translate(LocalizationKeys.log_detail_tabs_meal_detail_title, args: {
"status": _isNew ? translate(LocalizationKeys.log_detail_tabs_bolus_new) : LocalizationKeys.general_edit
})),
), ),
drawer: const Navigation(currentLocation: LogMealDetailScreen.routeName), drawer: const Navigation(currentLocation: LogMealDetailScreen.routeName),
body: Scrollbar( body: Scrollbar(
@ -424,7 +430,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
child: AutoCompleteDropdownButton<Meal>( child: AutoCompleteDropdownButton<Meal>(
controller: _mealController, controller: _mealController,
selectedItem: _meal, selectedItem: _meal,
label: 'Meal', label: translate(LocalizationKeys.log_detail_tabs_meal_detail_fields_meal),
items: _meals, items: _meals,
onChanged: onSelectMeal, onChanged: onSelectMeal,
), ),
@ -449,12 +455,12 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
), ),
TextFormField( TextFormField(
controller: _valueController, controller: _valueController,
decoration: const InputDecoration( decoration: InputDecoration(
labelText: 'Name', labelText: translate(LocalizationKeys.log_detail_tabs_meal_detail_fields_name),
), ),
validator: (value) { validator: (value) {
if (value!.trim().isEmpty) { if (value!.trim().isEmpty) {
return 'Empty name'; return translate(LocalizationKeys.log_detail_tabs_meal_detail_fields_validators_name);
} }
return null; return null;
}, },
@ -465,7 +471,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
flex: 10, flex: 10,
child: NumberFormField( child: NumberFormField(
controller: _amountController, controller: _amountController,
label: 'Amount', label: translate(LocalizationKeys.log_detail_tabs_meal_detail_fields_amount),
suffix: _mealPortionType?.value, suffix: _mealPortionType?.value,
onChanged: updateAmount, onChanged: updateAmount,
), ),
@ -524,7 +530,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
children: [ children: [
Expanded( Expanded(
child: NumberFormField( child: NumberFormField(
label: 'Portion size', label: translate(LocalizationKeys.log_detail_tabs_meal_detail_fields_portionSize),
suffix: Settings.nutritionMeasurementSuffix, suffix: Settings.nutritionMeasurementSuffix,
controller: _portionSizeController, controller: _portionSizeController,
showSteppers: false, showSteppers: false,
@ -548,7 +554,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
const SizedBox(width: 10), const SizedBox(width: 10),
Expanded( Expanded(
child: NumberFormField( child: NumberFormField(
label: 'Carbs ratio', label: translate(LocalizationKeys.log_detail_tabs_meal_detail_fields_carbsRatio),
suffix: '%', suffix: '%',
controller: _carbsRatioController, controller: _carbsRatioController,
showSteppers: false, showSteppers: false,
@ -570,7 +576,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
const SizedBox(width: 10), const SizedBox(width: 10),
Expanded( Expanded(
child: NumberFormField( child: NumberFormField(
label: 'Total carbs', label: translate(LocalizationKeys.log_detail_tabs_meal_detail_fields_totalCarbs),
suffix: Settings.nutritionMeasurementSuffix, suffix: Settings.nutritionMeasurementSuffix,
controller: _totalCarbsController, controller: _totalCarbsController,
showSteppers: false, showSteppers: false,
@ -595,7 +601,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
), ),
BooleanFormField( BooleanFormField(
value: _setManually, value: _setManually,
label: 'set carbs ratio manually', label: translate(LocalizationKeys.log_detail_tabs_meal_detail_fields_setManually),
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
_setManually = value; _setManually = value;
@ -605,8 +611,8 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
), ),
TextFormField( TextFormField(
controller: _notesController, controller: _notesController,
decoration: const InputDecoration( decoration: InputDecoration(
labelText: 'Notes', labelText: translate(LocalizationKeys.log_detail_tabs_meal_detail_fields_notes),
), ),
keyboardType: TextInputType.multiline, keyboardType: TextInputType.multiline,
minLines: 2, minLines: 2,
@ -621,7 +627,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
mainAxisSize: MainAxisSize.max, mainAxisSize: MainAxisSize.max,
children: [ children: [
Text( Text(
'ADDITIONAL FIELDS', translate(LocalizationKeys.log_detail_tabs_meal_detail_additionalFields).toUpperCase(),
style: Theme.of(context).textTheme.subtitle2, style: Theme.of(context).textTheme.subtitle2,
), ),
const Spacer(), const Spacer(),
@ -642,7 +648,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
AutoCompleteDropdownButton<MealSource>( AutoCompleteDropdownButton<MealSource>(
controller: _mealSourceController, controller: _mealSourceController,
selectedItem: _mealSource, selectedItem: _mealSource,
label: 'Meal Source', label: translate(LocalizationKeys.log_detail_tabs_meal_detail_fields_mealSource),
items: _mealSources, items: _mealSources,
onChanged: updateMealSource, onChanged: updateMealSource,
), ),
@ -677,7 +683,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
AutoCompleteDropdownButton<MealCategory>( AutoCompleteDropdownButton<MealCategory>(
controller: _mealCategoryController, controller: _mealCategoryController,
selectedItem: _mealCategory, selectedItem: _mealCategory,
label: 'Meal Category', label: translate(LocalizationKeys.log_detail_tabs_meal_detail_fields_mealCategory),
items: _mealCategories, items: _mealCategories,
onChanged: updateMealCategory, onChanged: updateMealCategory,
), ),
@ -712,7 +718,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
AutoCompleteDropdownButton<MealPortionType>( AutoCompleteDropdownButton<MealPortionType>(
controller: _mealPortionTypeController, controller: _mealPortionTypeController,
selectedItem: _mealPortionType, selectedItem: _mealPortionType,
label: 'Meal Portion Type', label: translate(LocalizationKeys.log_detail_tabs_meal_detail_fields_mealPortionType),
items: _mealPortionTypes, items: _mealPortionTypes,
onChanged: updateMealPortionType, onChanged: updateMealPortionType,
), ),
@ -747,7 +753,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
AutoCompleteDropdownButton<Accuracy>( AutoCompleteDropdownButton<Accuracy>(
controller: _portionSizeAccuracyController, controller: _portionSizeAccuracyController,
selectedItem: _portionSizeAccuracy, selectedItem: _portionSizeAccuracy,
label: 'Portion Size Accuracy', label: translate(LocalizationKeys.log_detail_tabs_meal_detail_fields_portionSizeAccuracy),
items: _portionSizeAccuracies, items: _portionSizeAccuracies,
onChanged: updatePortionSizeAccuracy, onChanged: updatePortionSizeAccuracy,
), ),
@ -783,7 +789,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
AutoCompleteDropdownButton<Accuracy>( AutoCompleteDropdownButton<Accuracy>(
controller: _carbsRatioAccuracyController, controller: _carbsRatioAccuracyController,
selectedItem: _carbsRatioAccuracy, selectedItem: _carbsRatioAccuracy,
label: 'Carbs Ratio Accuracy', label: translate(LocalizationKeys.log_detail_tabs_meal_detail_fields_carbsRatioAccuracy),
items: _carbsRatioAccuracies, items: _carbsRatioAccuracies,
onChanged: updateCarbsRatioAccuracy, onChanged: updateCarbsRatioAccuracy,
), ),

View File

@ -1,9 +1,11 @@
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart'; import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/models/log_entry.dart'; import 'package:diameter/models/log_entry.dart';
import 'package:diameter/models/log_meal.dart'; import 'package:diameter/models/log_meal.dart';
import 'package:diameter/models/settings.dart'; import 'package:diameter/models/settings.dart';
import 'package:diameter/screens/log/log_entry/log_meal_detail.dart'; import 'package:diameter/screens/log/log_entry/log_meal_detail.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
class LogMealListScreen extends StatefulWidget { class LogMealListScreen extends StatefulWidget {
final LogEntry logEntry; final LogEntry logEntry;
@ -57,7 +59,7 @@ class _LogMealListScreenState extends State<LogMealListScreen> {
void onDelete(LogMeal logMeal) { void onDelete(LogMeal logMeal) {
LogMeal.remove(logMeal.id); LogMeal.remove(logMeal.id);
reload(message: 'Meal deleted'); reload(message: translate(LocalizationKeys.log_detail_tabs_meal_deleted));
} }
void handleDeleteAction(LogMeal meal) async { void handleDeleteAction(LogMeal meal) async {
@ -65,7 +67,7 @@ class _LogMealListScreenState extends State<LogMealListScreen> {
DialogUtils.showConfirmationDialog( DialogUtils.showConfirmationDialog(
context: context, context: context,
onConfirm: () => onDelete(meal), onConfirm: () => onDelete(meal),
message: 'Are you sure you want to delete this Meal?', message: translate(LocalizationKeys.log_detail_tabs_meal_confirmDelete),
); );
} else { } else {
onDelete(meal); onDelete(meal);
@ -99,7 +101,7 @@ class _LogMealListScreenState extends State<LogMealListScreen> {
? [ ? [
Text(meal.totalCarbs!.toStringAsPrecision(3)), Text(meal.totalCarbs!.toStringAsPrecision(3)),
Text( Text(
'${Settings.nutritionMeasurementSuffix} carbs', '${Settings.nutritionMeasurementSuffix} ${translate(LocalizationKeys.general_suffixes_carbs)}',
textScaleFactor: 0.75), textScaleFactor: 0.75),
] ]
: [], : [],
@ -119,9 +121,9 @@ class _LogMealListScreenState extends State<LogMealListScreen> {
}, },
), ),
) )
: const Center( : Center(
child: Text( child: Text(
'You have not added any Meals to this Log Entry yet!'), translate(LocalizationKeys.log_detail_tabs_meal_empty)),
); );
} }
} }

View File

@ -2,6 +2,7 @@ import 'package:diameter/components/detail.dart';
import 'package:diameter/components/forms/boolean_form_field.dart'; import 'package:diameter/components/forms/boolean_form_field.dart';
import 'package:diameter/components/forms/date_time_form_field.dart'; import 'package:diameter/components/forms/date_time_form_field.dart';
import 'package:diameter/components/forms/time_of_day_form_field.dart'; import 'package:diameter/components/forms/time_of_day_form_field.dart';
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart'; import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/components/forms/auto_complete_dropdown_button.dart'; import 'package:diameter/components/forms/auto_complete_dropdown_button.dart';
import 'package:diameter/components/forms/form_wrapper.dart'; import 'package:diameter/components/forms/form_wrapper.dart';
@ -15,6 +16,7 @@ import 'package:diameter/screens/basal/basal_profile_detail.dart';
import 'package:diameter/screens/bolus/bolus_profile_detail.dart'; import 'package:diameter/screens/bolus/bolus_profile_detail.dart';
import 'package:diameter/utils/date_time_utils.dart'; import 'package:diameter/utils/date_time_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
class LogEventDetailScreen extends StatefulWidget { class LogEventDetailScreen extends StatefulWidget {
static const String routeName = '/log-event'; static const String routeName = '/log-event';
@ -194,20 +196,19 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return AlertDialog( return AlertDialog(
content: const Text( content: Text(translate(LocalizationKeys.event_warnings_duplicate)),
'An Event of this type is already active within the set time frame. What would you like to do?'),
actions: [ actions: [
TextButton( TextButton(
onPressed: () => Navigator.pop(context, 'DISCARD'), onPressed: () => Navigator.pop(context, 'DISCARD'),
child: const Text('DISCARD'), child: Text(translate(LocalizationKeys.general_discard).toUpperCase()),
), ),
TextButton( TextButton(
onPressed: () => Navigator.pop(context, 'EDIT'), onPressed: () => Navigator.pop(context, 'EDIT'),
child: const Text('KEEP EDITING'), child: Text(translate(LocalizationKeys.general_keepEditing).toUpperCase()),
), ),
ElevatedButton( ElevatedButton(
onPressed: () => Navigator.pop(context, 'SAVE'), onPressed: () => Navigator.pop(context, 'SAVE'),
child: const Text('SAVE'), child: Text(translate(LocalizationKeys.general_save).toUpperCase()),
) )
], ],
); );
@ -236,7 +237,9 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
event.basalProfile.target = _basalProfile; event.basalProfile.target = _basalProfile;
event.bolusProfile.target = _bolusProfile; event.bolusProfile.target = _bolusProfile;
LogEvent.put(event); LogEvent.put(event);
Navigator.pop(context, ['${_isNew ? 'New' : ''} Event Saved', event]); Navigator.pop(context, [translate(LocalizationKeys.event_saved, args: {
"status": _isNew ? LocalizationKeys.event_new : "",
}), event]);
} }
void handleSaveAction() async { void handleSaveAction() async {
@ -276,7 +279,10 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
final now = DateTime.now(); final now = DateTime.now();
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(_isNew ? 'New Event' : 'Edit Event'), title: Text(translate(LocalizationKeys.event_saved, args: {
"status": _isNew ? LocalizationKeys.event_new : LocalizationKeys.general_edit,
"name": _isNew ? '' : _logEvent?.eventType.target?.value
})),
), ),
drawer: const Navigation(currentLocation: LogEventDetailScreen.routeName), drawer: const Navigation(currentLocation: LogEventDetailScreen.routeName),
body: Scrollbar( body: Scrollbar(
@ -292,7 +298,7 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
AutoCompleteDropdownButton<LogEventType>( AutoCompleteDropdownButton<LogEventType>(
controller: _eventTypeController, controller: _eventTypeController,
selectedItem: _eventType, selectedItem: _eventType,
label: 'Event Type', label: translate(LocalizationKeys.event_fields_eventType),
items: _logEventTypes, items: _logEventTypes,
onChanged: onSelectEventType, onChanged: onSelectEventType,
), ),
@ -303,7 +309,7 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
padding: const EdgeInsets.only(right: 5), padding: const EdgeInsets.only(right: 5),
child: DateTimeFormField( child: DateTimeFormField(
date: _time, date: _time,
label: _hasEndTime ? 'Start Date' : 'Date', label: translate(_hasEndTime ? LocalizationKeys.event_fields_startDate : LocalizationKeys.event_fields_date),
controller: _dateController, controller: _dateController,
onChanged: (newTime) { onChanged: (newTime) {
if (newTime != null) { if (newTime != null) {
@ -322,7 +328,7 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
padding: const EdgeInsets.only(left: 5), padding: const EdgeInsets.only(left: 5),
child: TimeOfDayFormField( child: TimeOfDayFormField(
time: TimeOfDay.fromDateTime(_time), time: TimeOfDay.fromDateTime(_time),
label: _hasEndTime ? 'Start Time' : 'Time', label: translate(_hasEndTime ? LocalizationKeys.event_fields_startTime : LocalizationKeys.event_fields_time),
controller: _timeController, controller: _timeController,
onChanged: (newTime) { onChanged: (newTime) {
if (newTime != null) { if (newTime != null) {
@ -345,7 +351,7 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
_hasEndTime = value; _hasEndTime = value;
}); });
}, },
label: 'has end time', label: translate(LocalizationKeys.event_fields_hasEndTime),
), ),
Column( Column(
children: _hasEndTime children: _hasEndTime
@ -357,7 +363,7 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
padding: const EdgeInsets.only(right: 5), padding: const EdgeInsets.only(right: 5),
child: DateTimeFormField( child: DateTimeFormField(
date: _endTime ?? now, date: _endTime ?? now,
label: 'End Date', label: translate(LocalizationKeys.event_fields_endDate),
controller: _endDateController, controller: _endDateController,
onChanged: (newTime) { onChanged: (newTime) {
if (newTime != null) { if (newTime != null) {
@ -381,7 +387,7 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
child: TimeOfDayFormField( child: TimeOfDayFormField(
time: TimeOfDay.fromDateTime( time: TimeOfDay.fromDateTime(
_endTime ?? now), _endTime ?? now),
label: 'End Time', label: translate(LocalizationKeys.event_fields_endTime),
controller: _endTimeController, controller: _endTimeController,
onChanged: (newTime) { onChanged: (newTime) {
if (newTime != null) { if (newTime != null) {
@ -408,8 +414,8 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
keyboardType: keyboardType:
const TextInputType.numberWithOptions(), const TextInputType.numberWithOptions(),
decoration: InputDecoration( decoration: InputDecoration(
labelText: 'Default Reminder Duration', labelText: translate(LocalizationKeys.event_fields_reminderDuration),
suffixText: ' min', suffixText: translate(LocalizationKeys.general_suffixes_mins),
enabled: _hasEndTime, enabled: _hasEndTime,
), ),
), ),
@ -421,7 +427,7 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
BolusProfile>( BolusProfile>(
controller: _bolusProfileController, controller: _bolusProfileController,
selectedItem: _bolusProfile, selectedItem: _bolusProfile,
label: 'Bolus Profile', label: translate(LocalizationKeys.event_fields_bolusProfile),
items: _bolusProfiles, items: _bolusProfiles,
onChanged: updateBolusProfile, onChanged: updateBolusProfile,
), ),
@ -457,7 +463,7 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
BasalProfile>( BasalProfile>(
controller: _basalProfileController, controller: _basalProfileController,
selectedItem: _basalProfile, selectedItem: _basalProfile,
label: 'Basal Profile', label: translate(LocalizationKeys.event_fields_basalProfile),
items: _basalProfiles, items: _basalProfiles,
onChanged: updateBasalProfile, onChanged: updateBasalProfile,
), ),
@ -489,8 +495,8 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
: []), : []),
TextFormField( TextFormField(
controller: _notesController, controller: _notesController,
decoration: const InputDecoration( decoration: InputDecoration(
labelText: 'Notes', labelText: translate(LocalizationKeys.event_fields_notes),
), ),
keyboardType: TextInputType.multiline, keyboardType: TextInputType.multiline,
minLines: 2, minLines: 2,

View File

@ -1,3 +1,4 @@
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart'; import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/models/log_event.dart'; import 'package:diameter/models/log_event.dart';
import 'package:diameter/models/settings.dart'; import 'package:diameter/models/settings.dart';
@ -5,6 +6,7 @@ import 'package:diameter/screens/log/log_event/log_event_detail.dart';
import 'package:diameter/utils/date_time_utils.dart'; import 'package:diameter/utils/date_time_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:flutter_translate/flutter_translate.dart';
class LogEventListScreen extends StatefulWidget { class LogEventListScreen extends StatefulWidget {
static const String routeName = '/log-events'; static const String routeName = '/log-events';
@ -88,7 +90,7 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
void onDelete(LogEvent logEvent) { void onDelete(LogEvent logEvent) {
LogEvent.remove(logEvent.id); LogEvent.remove(logEvent.id);
reload(message: 'Event deleted'); reload(message: translate(LocalizationKeys.event_deleted));
} }
void handleDeleteAction(LogEvent logEvent) async { void handleDeleteAction(LogEvent logEvent) async {
@ -96,7 +98,7 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
DialogUtils.showConfirmationDialog( DialogUtils.showConfirmationDialog(
context: context, context: context,
onConfirm: () => onDelete(logEvent), onConfirm: () => onDelete(logEvent),
message: 'Are you sure you want to delete this Event?', message: translate(LocalizationKeys.event_confirmDelete),
); );
} else { } else {
onDelete(logEvent); onDelete(logEvent);
@ -106,7 +108,7 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
void onStop(LogEvent event) async { void onStop(LogEvent event) async {
event.endTime = DateTime.now(); event.endTime = DateTime.now();
LogEvent.put(event); LogEvent.put(event);
reload(message: 'Event ended'); reload(message: translate(LocalizationKeys.event_ended));
} }
void handleStopAction(LogEvent event) async { void handleStopAction(LogEvent event) async {
@ -114,8 +116,8 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
DialogUtils.showConfirmationDialog( DialogUtils.showConfirmationDialog(
context: context, context: context,
onConfirm: () => onStop(event), onConfirm: () => onStop(event),
message: 'Are you sure you want to end this Event?', message: translate(LocalizationKeys.event_confirmEnd),
confirmationLabel: 'END EVENT', confirmationLabel: translate(LocalizationKeys.event_end),
); );
} else { } else {
onStop(event); onStop(event);
@ -136,7 +138,7 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: const Text('Log Events'), title: Text(translate(LocalizationKeys.event_title)),
actions: <Widget>[ actions: <Widget>[
IconButton( IconButton(
onPressed: () => onChangeDate(DateTime.now()), onPressed: () => onChangeDate(DateTime.now()),
@ -159,7 +161,7 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
children: [ children: [
Expanded( Expanded(
child: Text( child: Text(
'ACTIVE EVENTS', translate(LocalizationKeys.event_titleActive).toUpperCase(),
style: Theme.of(context).textTheme.subtitle2, style: Theme.of(context).textTheme.subtitle2,
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
@ -235,8 +237,8 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
); );
}, },
) )
: const Center( : Center(
child: Text('There are no Active Events!'), child: Text(translate(LocalizationKeys.event_emptyActive)),
), ),
const Padding( const Padding(
padding: EdgeInsets.all(10.0), padding: EdgeInsets.all(10.0),
@ -351,8 +353,8 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
); );
}, },
)) ))
: const Center( : Center(
child: Text('There are no Events for that date!'), child: Text(translate(LocalizationKeys.event_empty)),
), ),
), ),
], ],

View File

@ -1,304 +0,0 @@
import 'package:diameter/components/detail.dart';
import 'package:diameter/components/forms/boolean_form_field.dart';
import 'package:diameter/components/forms/duration_form_field.dart';
import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/components/forms/auto_complete_dropdown_button.dart';
import 'package:diameter/components/forms/form_wrapper.dart';
import 'package:diameter/models/basal_profile.dart';
import 'package:diameter/models/bolus_profile.dart';
import 'package:diameter/models/log_event_type.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart';
import 'package:diameter/screens/basal/basal_profile_detail.dart';
import 'package:diameter/screens/bolus/bolus_profile_detail.dart';
import 'package:flutter/material.dart';
class EventTypeDetailScreen extends StatefulWidget {
static const String routeName = '/log-event-type';
final int id;
const EventTypeDetailScreen({Key? key, this.id = 0}) : super(key: key);
@override
_EventTypeDetailScreenState createState() => _EventTypeDetailScreenState();
}
class _EventTypeDetailScreenState extends State<EventTypeDetailScreen> {
LogEventType? _logEventType;
bool _isNew = true;
bool _isSaving = false;
List<BolusProfile> _bolusProfiles = [];
List<BasalProfile> _basalProfiles = [];
final GlobalKey<FormState> _logEventTypeForm = GlobalKey<FormState>();
final ScrollController _scrollController = ScrollController();
final _valueController = TextEditingController(text: '');
final _notesController = TextEditingController(text: '');
bool _hasEndTime = false;
int _defaultReminderDuration = 0;
BolusProfile? _bolusProfile;
BasalProfile? _basalProfile;
final _bolusProfileController = TextEditingController(text: '');
final _basalProfileController = TextEditingController(text: '');
@override
void initState() {
super.initState();
reload();
_bolusProfiles = BolusProfile.getAll();
_basalProfiles = BasalProfile.getAll();
if (_logEventType != null) {
_valueController.text = _logEventType!.value;
_defaultReminderDuration =
_logEventType!.defaultReminderDuration ?? 0;
_hasEndTime = _logEventType!.hasEndTime;
_notesController.text = _logEventType!.notes ?? '';
_basalProfile = _logEventType!.basalProfile.target;
_basalProfileController.text = (_basalProfile ?? '').toString();
_bolusProfile = _logEventType!.bolusProfile.target;
_bolusProfileController.text = (_bolusProfile ?? '').toString();
}
}
@override
void dispose() {
_scrollController.dispose();
_valueController.dispose();
_notesController.dispose();
_bolusProfileController.dispose();
_basalProfileController.dispose();
super.dispose();
}
void reload({String? message}) {
if (widget.id != 0) {
setState(() {
_logEventType = LogEventType.get(widget.id);
});
}
_isNew = _logEventType == null;
setState(() {
if (message != null) {
var snackBar = SnackBar(
content: Text(message),
duration: const Duration(seconds: 2),
);
ScaffoldMessenger.of(context)
..removeCurrentSnackBar()
..showSnackBar(snackBar);
}
});
}
void updateBasalProfile(BasalProfile? value) {
setState(() {
_basalProfile = value;
_basalProfileController.text = (_basalProfile ?? '').toString();
});
}
void updateBolusProfile(BolusProfile? value) {
setState(() {
_bolusProfile = value;
_bolusProfileController.text = (_bolusProfile ?? '').toString();
});
}
void handleSaveAction() async {
setState(() {
_isSaving = true;
});
if (_logEventTypeForm.currentState!.validate()) {
LogEventType eventType = LogEventType(
id: widget.id,
value: _valueController.text,
notes: _notesController.text,
defaultReminderDuration: _defaultReminderDuration,
hasEndTime: _hasEndTime,
);
eventType.basalProfile.target = _basalProfile;
eventType.bolusProfile.target = _bolusProfile;
LogEventType.put(eventType);
Navigator.pop(
context, ['${_isNew ? 'New' : ''} Log Event Type Saved', eventType]);
}
setState(() {
_isSaving = false;
});
}
void handleCancelAction() {
bool isNew = _logEventType == null;
if (Settings.get().showConfirmationDialogOnCancel &&
((isNew &&
(_valueController.text != '' ||
_defaultReminderDuration != 0 ||
_notesController.text != '' ||
_hasEndTime)) ||
(!isNew &&
(_valueController.text != _logEventType!.value ||
_defaultReminderDuration !=
_logEventType!.defaultReminderDuration ||
_notesController.text != (_logEventType!.notes ?? '') ||
_hasEndTime != _logEventType!.hasEndTime)))) {
DialogUtils.showCancelConfirmationDialog(
context: context,
isNew: isNew,
onSave: handleSaveAction,
);
} else {
Navigator.pop(context);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_isNew ? 'New Log Event Type' : _logEventType!.value),
),
drawer:
const Navigation(currentLocation: EventTypeDetailScreen.routeName),
body: Scrollbar(
controller: _scrollController,
child: SingleChildScrollView(
controller: _scrollController,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
FormWrapper(formState: _logEventTypeForm, fields: [
TextFormField(
controller: _valueController,
decoration: const InputDecoration(
labelText: 'Name',
),
validator: (value) {
if (value!.trim().isEmpty) {
return 'Empty name';
}
return null;
},
),
BooleanFormField(
value: _hasEndTime,
label: 'has end time',
onChanged: (value) {
setState(() {
_hasEndTime = value;
});
},
),
Column(
children: _hasEndTime
? [
Padding(
padding: const EdgeInsets.only(bottom: 10.0),
child: DurationFormField(
minutes: _defaultReminderDuration,
label: 'Default Reminder Duration',
onChanged: (value) => _defaultReminderDuration = value ?? 0,
showSteppers: true,
),
),
Padding(
padding: const EdgeInsets.only(bottom: 10.0),
child: Row(
children: [
Expanded(
child: AutoCompleteDropdownButton<
BolusProfile>(
selectedItem: _bolusProfile,
controller: _bolusProfileController,
label: 'Bolus Profile',
items: _bolusProfiles,
onChanged: updateBolusProfile,
),
),
IconButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => _bolusProfile ==
null
? const BolusProfileDetailScreen()
: BolusProfileDetailScreen(
id: _bolusProfile!.id),
),
).then((result) {
setState(() {
updateBolusProfile(result?[1]);
});
reload(message: result?[0]);
});
},
icon: Icon(_bolusProfile == null
? Icons.add
: Icons.edit),
),
],
),
),
Row(
children: [
Expanded(
child:
AutoCompleteDropdownButton<BasalProfile>(
controller: _basalProfileController,
selectedItem: _basalProfile,
label: 'Basal Profile',
items: _basalProfiles,
onChanged: updateBasalProfile,
),
),
IconButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => _basalProfile ==
null
? const BasalProfileDetailScreen()
: BasalProfileDetailScreen(
id: _basalProfile!.id),
),
).then((result) {
updateBasalProfile(result?[1]);
reload(message: result?[0]);
});
},
icon: Icon(_basalProfile == null
? Icons.add
: Icons.edit),
),
],
),
]
: []),
TextFormField(
controller: _notesController,
decoration: const InputDecoration(
labelText: 'Notes',
),
keyboardType: TextInputType.multiline,
minLines: 2,
maxLines: 5,
),
]),
],
),
),
),
bottomNavigationBar: DetailBottomRow(
onCancel: handleCancelAction,
onAction: _isSaving ? null : handleSaveAction,
),
);
}
}

View File

@ -1,123 +0,0 @@
import 'package:diameter/models/log_event_type.dart';
import 'package:diameter/navigation.dart';
import 'package:diameter/screens/log/log_event/log_event_type_detail.dart';
import 'package:flutter/material.dart';
class LogEventTypeListScreen extends StatefulWidget {
static const String routeName = '/log-event-types';
const LogEventTypeListScreen({Key? key}) : super(key: key);
@override
_LogEventTypeListScreenState createState() => _LogEventTypeListScreenState();
}
class _LogEventTypeListScreenState extends State<LogEventTypeListScreen> {
List<LogEventType> _logEventTypes = [];
final ScrollController _scrollController = ScrollController();
@override
void initState() {
super.initState();
reload();
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
void reload({String? message}) {
setState(() {
_logEventTypes = LogEventType.getAll();
});
setState(() {
if (message != null) {
var snackBar = SnackBar(
content: Text(message),
duration: const Duration(seconds: 2),
);
ScaffoldMessenger.of(context)
..removeCurrentSnackBar()
..showSnackBar(snackBar);
}
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Log Event Types'), actions: <Widget>[
IconButton(onPressed: reload, icon: const Icon(Icons.refresh))
]),
drawer:
const Navigation(currentLocation: LogEventTypeListScreen.routeName),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: _logEventTypes.isNotEmpty
? Scrollbar(
controller: _scrollController,
child: ListView.builder(
controller: _scrollController,
padding: const EdgeInsets.all(10.0),
itemCount: _logEventTypes.length,
itemBuilder: (context, index) {
final logEventType = _logEventTypes[index];
return Card(
child: ListTile(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => EventTypeDetailScreen(
id: logEventType.id),
),
).then((result) => reload(message: result?[0]));
},
title: Text(
logEventType.value.toUpperCase(),
style: Theme.of(context).textTheme.subtitle2,
),
subtitle: Text(logEventType.notes ?? ''),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
onPressed: () async {
LogEventType.remove(logEventType.id);
reload(message: 'Log Event Type deleted');
},
icon: const Icon(Icons.delete,
color: Colors.blue),
)
],
),
),
);
},
),
)
: const Center(
child:
Text('You have not created any Log Event Types yet!'),
),
),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const EventTypeDetailScreen(),
),
).then((result) => reload(message: result?[0]));
},
child: const Icon(Icons.add),
),
);
}
}

View File

@ -0,0 +1,230 @@
import 'package:diameter/components/forms/auto_complete_dropdown_button.dart';
import 'package:diameter/components/forms/date_time_form_field.dart';
import 'package:diameter/components/forms/number_form_field.dart';
import 'package:diameter/localization_keys.dart';
import 'package:diameter/models/meal.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/utils/date_time_utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
class LogFilterDialog extends StatefulWidget {
static const String routeName = '/log/filter';
final DateTime? date;
final void Function(
{DateTime? startDate,
DateTime? endDate,
int? minMgPerDl,
int? maxMgPerDl,
double? minMmolPerL,
double? maxMmolPerL,
int? mealId,
String? mealName,
String? note}) onApplyFilter;
const LogFilterDialog({Key? key, this.date, required this.onApplyFilter})
: super(key: key);
@override
_LogFilterDialogState createState() => _LogFilterDialogState();
}
class _LogFilterDialogState extends State<LogFilterDialog> {
final ScrollController _scrollController = ScrollController();
late DateTime filterStartDate;
late DateTime filterEndDate;
Meal? meal;
List<Meal> _meals = [];
TextEditingController filterStartDateController =
TextEditingController(text: '');
TextEditingController filterEndDateController =
TextEditingController(text: '');
TextEditingController mealController = TextEditingController(text: '');
TextEditingController minMgPerDlController = TextEditingController(text: '');
TextEditingController maxMgPerDlController = TextEditingController(text: '');
TextEditingController minMmolPerLController = TextEditingController(text: '');
TextEditingController maxMmolPerLController = TextEditingController(text: '');
TextEditingController mealNameController = TextEditingController(text: '');
TextEditingController noteController = TextEditingController(text: '');
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
@override
void initState() {
super.initState();
filterStartDate = widget.date ?? DateTimeUtils.today();
filterEndDate = DateTimeUtils.today();
filterStartDateController.text = DateTimeUtils.displayDate(filterStartDate);
filterEndDateController.text = DateTimeUtils.displayDate(filterEndDate);
_meals = Meal.getAll();
}
@override
Widget build(BuildContext context) {
return AlertDialog(
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Row(
children: [
Expanded(
child: DateTimeFormField(
date: filterStartDate,
label: translate(LocalizationKeys.log_filter_startDate),
controller: filterStartDateController,
onChanged: (newDate) {
if (newDate != null) {
filterStartDate =
DateTime(newDate.year, newDate.month, newDate.day);
filterStartDateController.text =
DateTimeUtils.displayDate(filterStartDate);
}
},
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(left: 5.0),
child: DateTimeFormField(
date: filterEndDate,
label: translate(LocalizationKeys.log_filter_endDate),
controller: filterEndDateController,
onChanged: (newDate) {
if (newDate != null) {
filterEndDate = DateTime(
newDate.year, newDate.month, newDate.day);
filterEndDateController.text =
DateTimeUtils.displayDate(filterEndDate);
}
},
),
),
)
],
),
Row(
children: Settings.glucoseMeasurement ==
GlucoseMeasurement.mgPerDl
? [
Expanded(
child: NumberFormField(
label: translate(LocalizationKeys.log_filter_minGlucose, args: {
"glucoseMeasurement": Settings.glucoseMeasurementSuffix
}),
controller: minMgPerDlController,
onChanged: (value) {
if (value != null) {
minMgPerDlController.text = value.toString();
}
},
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(left: 5.0),
child: NumberFormField(
label: translate(LocalizationKeys.log_filter_maxGlucose, args: {
"glucoseMeasurement": Settings.glucoseMeasurementSuffix
}),
controller: maxMgPerDlController,
onChanged: (value) {
if (value != null) {
maxMgPerDlController.text = value.toString();
}
},
),
),
)
]
: [
Expanded(
child: NumberFormField(
label: translate(LocalizationKeys.log_filter_minGlucose, args: {
"glucoseMeasurement": Settings.glucoseMeasurementSuffix
}),
controller: minMmolPerLController,
onChanged: (value) {
if (value != null) {
minMmolPerLController.text = value.toString();
}
},
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(left: 5.0),
child: NumberFormField(
label: translate(LocalizationKeys.log_filter_maxGlucose, args: {
"glucoseMeasurement": Settings.glucoseMeasurementSuffix
}),
controller: maxMmolPerLController,
onChanged: (value) {
if (value != null) {
maxMmolPerLController.text = value.toString();
}
},
),
),
)
],
),
Expanded(
child: AutoCompleteDropdownButton<Meal>(
controller: mealController,
selectedItem: meal,
label: translate(LocalizationKeys.log_filter_meal),
items: _meals,
onChanged: (value) {
setState(() {
meal = value;
});
},
),
),
TextFormField(
controller: mealNameController,
decoration: InputDecoration(
labelText: translate(LocalizationKeys.log_filter_mealNameContains),
),
),
TextFormField(
controller: noteController,
decoration: InputDecoration(
labelText: translate(LocalizationKeys.log_filter_noteContains),
),
),
],
),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.pop(context),
child: Text(translate(LocalizationKeys.general_cancel).toUpperCase()),
),
ElevatedButton(
onPressed: () => widget.onApplyFilter(
startDate: filterStartDate,
endDate: filterEndDate,
minMgPerDl: int.tryParse(minMgPerDlController.text),
maxMgPerDl: int.tryParse(maxMgPerDlController.text),
minMmolPerL: double.tryParse(minMmolPerLController.text),
maxMmolPerL: double.tryParse(maxMmolPerLController.text),
mealId: meal?.id,
mealName: mealNameController.text,
note: noteController.text,
),
child: Text(translate(LocalizationKeys.general_apply).toUpperCase()),
),
]);
}
}

View File

@ -1,3 +1,5 @@
import 'package:diameter/localization_keys.dart';
import 'package:diameter/screens/log/log_filter_dialog.dart';
import 'package:diameter/screens/reports/export.dart'; import 'package:diameter/screens/reports/export.dart';
import 'package:diameter/utils/dialog_utils.dart'; import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/models/glucose_target.dart'; import 'package:diameter/models/glucose_target.dart';
@ -11,6 +13,9 @@ import 'package:diameter/utils/date_time_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'dart:math' as math; import 'dart:math' as math;
import 'package:flutter_translate/flutter_translate.dart';
class LogScreen extends StatefulWidget { class LogScreen extends StatefulWidget {
static const String routeName = '/log'; static const String routeName = '/log';
const LogScreen({Key? key}) : super(key: key); const LogScreen({Key? key}) : super(key: key);
@ -65,7 +70,7 @@ class _LogScreenState extends State<LogScreen> {
void onDelete(LogEntry logEntry) { void onDelete(LogEntry logEntry) {
LogEntry.remove(logEntry.id); LogEntry.remove(logEntry.id);
reload(message: 'Log Entry deleted'); reload(message: translate(LocalizationKeys.log_deleted));
} }
void handleDeleteAction(LogEntry logEntry) async { void handleDeleteAction(LogEntry logEntry) async {
@ -73,7 +78,7 @@ class _LogScreenState extends State<LogScreen> {
DialogUtils.showConfirmationDialog( DialogUtils.showConfirmationDialog(
context: context, context: context,
onConfirm: () => onDelete(logEntry), onConfirm: () => onDelete(logEntry),
message: 'Are you sure you want to delete this Log Entry?', message: translate(LocalizationKeys.log_confirmDelete),
); );
} else { } else {
onDelete(logEntry); onDelete(logEntry);
@ -90,15 +95,48 @@ class _LogScreenState extends State<LogScreen> {
} }
} }
void onChangeFilter(
{DateTime? startDate,
DateTime? endDate,
int? minMgPerDl,
int? maxMgPerDl,
double? minMmolPerL,
double? maxMmolPerL,
int? mealId,
String? mealName,
String? note}) {
setState(() {
_logEntries = LogEntry.getAllByFilter(
startDate: startDate,
endDate: endDate,
minMgPerDl: minMgPerDl,
maxMgPerDl: maxMgPerDl,
minMmolPerL: minMmolPerL,
maxMmolPerL: maxMmolPerL,
mealId: mealId,
mealName: mealName,
note: note,
);
});
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: const Text('Log Entries'), title: Text(translate(LocalizationKeys.log_title)),
actions: <Widget>[ actions: <Widget>[
IconButton( IconButton(
onPressed: () => onChangeDate(DateTime.now()), onPressed: () => onChangeDate(DateTime.now()),
icon: const Icon(Icons.today)), icon: const Icon(Icons.today)),
IconButton(
onPressed: () => showDialog(
context: context,
builder: (context) => LogFilterDialog(
date: _date,
onApplyFilter: onChangeFilter,
)),
icon: const Icon(Icons.filter_list)),
IconButton( IconButton(
onPressed: () => showDialog( onPressed: () => showDialog(
context: context, context: context,
@ -297,7 +335,7 @@ class _LogScreenState extends State<LogScreen> {
? [ ? [
Text( Text(
bolus.toStringAsPrecision(3)), bolus.toStringAsPrecision(3)),
const Text('U', Text(translate(LocalizationKeys.general_suffixes_units),
textScaleFactor: 0.75), textScaleFactor: 0.75),
] ]
: [], : [],
@ -309,9 +347,13 @@ class _LogScreenState extends State<LogScreen> {
? [ ? [
Text( Text(
carbs.toStringAsPrecision(3)), carbs.toStringAsPrecision(3)),
Text( Text(translate(
'${Settings.nutritionMeasurementSuffix} carbs', LocalizationKeys.general_suffixes_carbs,
textScaleFactor: 0.75), args: {
"nutritionMeasurementSuffix": Settings.nutritionMeasurementSuffix,
}
),
textScaleFactor: 0.75),
] ]
: [], : [],
), ),
@ -334,9 +376,10 @@ class _LogScreenState extends State<LogScreen> {
}, },
), ),
) )
: const Center( : Center(
child: Text( child: Text(
'You have not created any Log Entries for this date yet!'), translate(LocalizationKeys.log_empty)
),
), ),
), ),
], ],

View File

@ -1,6 +1,7 @@
import 'package:diameter/components/detail.dart'; import 'package:diameter/components/detail.dart';
import 'package:diameter/components/forms/boolean_form_field.dart'; import 'package:diameter/components/forms/boolean_form_field.dart';
import 'package:diameter/components/forms/number_form_field.dart'; import 'package:diameter/components/forms/number_form_field.dart';
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart'; import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/components/forms/auto_complete_dropdown_button.dart'; import 'package:diameter/components/forms/auto_complete_dropdown_button.dart';
import 'package:diameter/components/forms/form_wrapper.dart'; import 'package:diameter/components/forms/form_wrapper.dart';
@ -17,6 +18,7 @@ import 'package:diameter/screens/category/meal_portion_type_detail.dart';
import 'package:diameter/screens/category/meal_source_detail.dart'; import 'package:diameter/screens/category/meal_source_detail.dart';
import 'package:diameter/utils/utils.dart'; import 'package:diameter/utils/utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
class MealDetailScreen extends StatefulWidget { class MealDetailScreen extends StatefulWidget {
static const String routeName = '/meal'; static const String routeName = '/meal';
@ -195,7 +197,9 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
meal.carbsRatioAccuracy.target = _carbsRatioAccuracy; meal.carbsRatioAccuracy.target = _carbsRatioAccuracy;
Meal.put(meal); Meal.put(meal);
Navigator.pop(context, ['${_isNew ? 'New' : ''} Meal Saved', meal]); Navigator.pop(context, [translate(LocalizationKeys.meal_saved, args: {
"status": _isNew ? translate(LocalizationKeys.meal_new) : '',
}), meal]);
} }
setState(() { setState(() {
_isSaving = false; _isSaving = false;
@ -340,7 +344,10 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(_isNew ? 'New Meal' : _meal!.value), title: Text(translate(LocalizationKeys.meal_saved, args: {
"status": _isNew ? translate(LocalizationKeys.meal_new) : '',
"name": _meal?.value,
})),
), ),
drawer: const Navigation(currentLocation: MealDetailScreen.routeName), drawer: const Navigation(currentLocation: MealDetailScreen.routeName),
body: Scrollbar( body: Scrollbar(
@ -354,12 +361,12 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
fields: [ fields: [
TextFormField( TextFormField(
controller: _valueController, controller: _valueController,
decoration: const InputDecoration( decoration: InputDecoration(
labelText: 'Name', labelText: translate(LocalizationKeys.meal_fields_name),
), ),
validator: (value) { validator: (value) {
if (value!.trim().isEmpty) { if (value!.trim().isEmpty) {
return 'Empty name'; return translate(LocalizationKeys.meal_fields_validators_name);
} }
return null; return null;
}, },
@ -370,7 +377,7 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
child: AutoCompleteDropdownButton<MealSource>( child: AutoCompleteDropdownButton<MealSource>(
controller: _mealSourceController, controller: _mealSourceController,
selectedItem: _mealSource, selectedItem: _mealSource,
label: 'Meal Source', label: translate(LocalizationKeys.meal_fields_mealSource),
items: _mealSources, items: _mealSources,
onChanged: onSelectMealSource, onChanged: onSelectMealSource,
), ),
@ -400,7 +407,7 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
child: AutoCompleteDropdownButton<MealPortionType>( child: AutoCompleteDropdownButton<MealPortionType>(
controller: _mealPortionTypeController, controller: _mealPortionTypeController,
selectedItem: _mealPortionType, selectedItem: _mealPortionType,
label: 'Meal Portion Type', label: translate(LocalizationKeys.meal_fields_mealPortionType),
items: _mealPortionTypes, items: _mealPortionTypes,
onChanged: updateMealPortionType, onChanged: updateMealPortionType,
), ),
@ -429,7 +436,7 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
children: [ children: [
Expanded( Expanded(
child: NumberFormField( child: NumberFormField(
label: 'Carbs ratio', label: translate(LocalizationKeys.meal_fields_carbsRatio),
suffix: '%', suffix: '%',
controller: _carbsRatioController, controller: _carbsRatioController,
showSteppers: false, showSteppers: false,
@ -445,7 +452,7 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
const SizedBox(width: 10), const SizedBox(width: 10),
Expanded( Expanded(
child: NumberFormField( child: NumberFormField(
label: 'Portion size', label: translate(LocalizationKeys.meal_fields_portionSize),
suffix: Settings.nutritionMeasurementSuffix, suffix: Settings.nutritionMeasurementSuffix,
controller: _portionSizeController, controller: _portionSizeController,
showSteppers: false, showSteppers: false,
@ -461,7 +468,7 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
const SizedBox(width: 10), const SizedBox(width: 10),
Expanded( Expanded(
child: NumberFormField( child: NumberFormField(
label: 'Carbs per portion', label: translate(LocalizationKeys.meal_fields_carbsPerPortion),
suffix: Settings.nutritionMeasurementSuffix, suffix: Settings.nutritionMeasurementSuffix,
controller: _carbsPerPortionController, controller: _carbsPerPortionController,
showSteppers: false, showSteppers: false,
@ -479,7 +486,7 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
Expanded( Expanded(
child: BooleanFormField( child: BooleanFormField(
value: _setManually, value: _setManually,
label: 'set carbs ratio manually', label: translate(LocalizationKeys.meal_fields_setManually),
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
_setManually = value; _setManually = value;
@ -490,8 +497,8 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
), ),
TextFormField( TextFormField(
controller: _notesController, controller: _notesController,
decoration: const InputDecoration( decoration: InputDecoration(
labelText: 'Notes', labelText: translate(LocalizationKeys.meal_fields_notes),
), ),
keyboardType: TextInputType.multiline, keyboardType: TextInputType.multiline,
minLines: 2, minLines: 2,
@ -503,7 +510,7 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
child: Row( child: Row(
children: [ children: [
Text( Text(
'BOLUS DELAY', translate(LocalizationKeys.meal_fields_delay_title).toUpperCase(),
style: Theme.of(context).textTheme.subtitle2, style: Theme.of(context).textTheme.subtitle2,
), ),
const Spacer(), const Spacer(),
@ -514,9 +521,9 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
children: [ children: [
Expanded( Expanded(
child: TextFormField( child: TextFormField(
decoration: const InputDecoration( decoration: InputDecoration(
labelText: 'Duration', labelText: translate(LocalizationKeys.meal_fields_delay_duration),
suffixText: ' min', suffixText: translate(LocalizationKeys.general_suffixes_mins),
), ),
controller: _delayedBolusDurationController, controller: _delayedBolusDurationController,
onChanged: (value) => setState(() {}), onChanged: (value) => setState(() {}),
@ -553,7 +560,7 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
children: [ children: [
Expanded( Expanded(
child: Text( child: Text(
'ADDITIONAL FIELDS', translate(LocalizationKeys.meal_fields_additional_title).toUpperCase(),
style: Theme.of(context).textTheme.subtitle2, style: Theme.of(context).textTheme.subtitle2,
), ),
), ),
@ -574,7 +581,7 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
AutoCompleteDropdownButton<MealCategory>( AutoCompleteDropdownButton<MealCategory>(
controller: _mealCategoryController, controller: _mealCategoryController,
selectedItem: _mealCategory, selectedItem: _mealCategory,
label: 'Meal Category', label: translate(LocalizationKeys.meal_fields_additional_mealCategory),
items: _mealCategories, items: _mealCategories,
onChanged: updateMealCategory, onChanged: updateMealCategory,
), ),
@ -609,7 +616,7 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
AutoCompleteDropdownButton<Accuracy>( AutoCompleteDropdownButton<Accuracy>(
controller: _portionSizeAccuracyController, controller: _portionSizeAccuracyController,
selectedItem: _portionSizeAccuracy, selectedItem: _portionSizeAccuracy,
label: 'Portion Size Accuracy', label: translate(LocalizationKeys.meal_fields_additional_portionSizeAccuracy),
items: _portionSizeAccuracies, items: _portionSizeAccuracies,
onChanged: updatePortionSizeAccuracy, onChanged: updatePortionSizeAccuracy,
), ),
@ -645,7 +652,7 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
AutoCompleteDropdownButton<Accuracy>( AutoCompleteDropdownButton<Accuracy>(
controller: _carbsRatioAccuracyController, controller: _carbsRatioAccuracyController,
selectedItem: _carbsRatioAccuracy, selectedItem: _carbsRatioAccuracy,
label: 'Carbs Ratio Accuracy', label: translate(LocalizationKeys.meal_fields_additional_carbsRatioAccuracy),
items: _carbsRatioAccuracies, items: _carbsRatioAccuracies,
onChanged: updateCarbsRatioAccuracy, onChanged: updateCarbsRatioAccuracy,
), ),

View File

@ -1,9 +1,11 @@
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart'; import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/models/meal.dart'; import 'package:diameter/models/meal.dart';
import 'package:diameter/models/settings.dart'; import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:diameter/screens/meal/meal_detail.dart'; import 'package:diameter/screens/meal/meal_detail.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
class MealListScreen extends StatefulWidget { class MealListScreen extends StatefulWidget {
static const String routeName = '/meals'; static const String routeName = '/meals';
@ -50,7 +52,7 @@ class _MealListScreenState extends State<MealListScreen> {
void onDelete(Meal meal) { void onDelete(Meal meal) {
Meal.remove(meal.id); Meal.remove(meal.id);
reload(message: 'Meal deleted'); reload(message: translate(LocalizationKeys.meal_deleted));
} }
void handleDeleteAction(Meal meal) async { void handleDeleteAction(Meal meal) async {
@ -58,7 +60,7 @@ class _MealListScreenState extends State<MealListScreen> {
DialogUtils.showConfirmationDialog( DialogUtils.showConfirmationDialog(
context: context, context: context,
onConfirm: () => onDelete(meal), onConfirm: () => onDelete(meal),
message: 'Are you sure you want to delete this Meal?', message: translate(LocalizationKeys.meal_confirmDelete),
); );
} else { } else {
onDelete(meal); onDelete(meal);
@ -68,8 +70,12 @@ class _MealListScreenState extends State<MealListScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar(title: const Text('Meals'), actions: <Widget>[ appBar: AppBar(
IconButton(onPressed: reload, icon: const Icon(Icons.refresh)) title: Text(
translate(LocalizationKeys.meal_title)
),
actions: <Widget>[
IconButton(onPressed: reload, icon: const Icon(Icons.refresh))
]), ]),
drawer: const Navigation(currentLocation: MealListScreen.routeName), drawer: const Navigation(currentLocation: MealListScreen.routeName),
body: Column( body: Column(
@ -84,7 +90,7 @@ class _MealListScreenState extends State<MealListScreen> {
itemCount: _meals.length, itemCount: _meals.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final meal = _meals[index]; final meal = _meals[index];
String portionType = meal.mealPortionType.hasValue ? ' per ${meal.mealPortionType.target!.value}' : ''; String portionType = meal.mealPortionType.hasValue ? ' ${translate(LocalizationKeys.general_per)} ${meal.mealPortionType.target!.value}' : '';
return Card( return Card(
child: ListTile( child: ListTile(
isThreeLine: true, isThreeLine: true,
@ -118,7 +124,7 @@ class _MealListScreenState extends State<MealListScreen> {
? [ ? [
Text(meal.carbsPerPortion!.toStringAsPrecision(3)), Text(meal.carbsPerPortion!.toStringAsPrecision(3)),
Text( Text(
'${Settings.nutritionMeasurementSuffix} carbs', '${Settings.nutritionMeasurementSuffix} ${translate(LocalizationKeys.general_suffixes_carbs)}',
textScaleFactor: 0.75), textScaleFactor: 0.75),
] ]
: [], : [],
@ -166,8 +172,8 @@ class _MealListScreenState extends State<MealListScreen> {
); );
}, },
), ),
): const Center( ): Center(
child: Text('You have not created any Meals yet!'), child: Text(translate(LocalizationKeys.meal_empty)),
), ),
), ),
], ],

View File

@ -1,4 +1,5 @@
import 'package:charts_flutter/flutter.dart' as charts; import 'package:charts_flutter/flutter.dart' as charts;
import 'package:diameter/localization_keys.dart';
import 'package:diameter/models/basal.dart'; import 'package:diameter/models/basal.dart';
import 'package:diameter/models/glucose_target.dart'; import 'package:diameter/models/glucose_target.dart';
import 'package:diameter/models/log_bolus.dart'; import 'package:diameter/models/log_bolus.dart';
@ -9,6 +10,7 @@ import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:diameter/utils/date_time_utils.dart'; import 'package:diameter/utils/date_time_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
charts.TimeSeriesChart generateChart( charts.TimeSeriesChart generateChart(
DateTime date, DateTime date,
@ -48,7 +50,7 @@ charts.TimeSeriesChart generateChart(
return charts.TimeSeriesChart( return charts.TimeSeriesChart(
[ [
charts.Series<LogEntry, DateTime>( charts.Series<LogEntry, DateTime>(
id: 'Glucose', id: translate(LocalizationKeys.reports_ids_glucose),
colorFn: (LogEntry entry, _) => charts.Color.black, colorFn: (LogEntry entry, _) => charts.Color.black,
domainFn: (LogEntry entry, _) => entry.time, domainFn: (LogEntry entry, _) => entry.time,
measureFn: (LogEntry entry, _) => measureFn: (LogEntry entry, _) =>
@ -58,7 +60,7 @@ charts.TimeSeriesChart generateChart(
data: logEntries, data: logEntries,
)..setAttribute(charts.rendererIdKey, 'glucoseRenderer'), )..setAttribute(charts.rendererIdKey, 'glucoseRenderer'),
charts.Series<LogEntry, DateTime>( charts.Series<LogEntry, DateTime>(
id: 'Carbohydrates', id: translate(LocalizationKeys.reports_ids_carbs),
colorFn: (LogEntry entry, _) => colorFn: (LogEntry entry, _) =>
charts.MaterialPalette.yellow.shadeDefault, charts.MaterialPalette.yellow.shadeDefault,
domainFn: (LogEntry entry, _) => entry.time, domainFn: (LogEntry entry, _) => entry.time,
@ -67,7 +69,7 @@ charts.TimeSeriesChart generateChart(
data: showMeals ? logEntries : [], data: showMeals ? logEntries : [],
)..setAttribute(charts.rendererIdKey, 'carbsRenderer'), )..setAttribute(charts.rendererIdKey, 'carbsRenderer'),
charts.Series<Basal, DateTime>( charts.Series<Basal, DateTime>(
id: 'Basal', id: translate(LocalizationKeys.reports_ids_basal),
colorFn: (Basal basal, _) => charts.Color.black.lighter, colorFn: (Basal basal, _) => charts.Color.black.lighter,
domainFn: (Basal basal, _) => basal.startTime, domainFn: (Basal basal, _) => basal.startTime,
measureFn: (Basal basal, _) => basal.units, measureFn: (Basal basal, _) => basal.units,
@ -77,7 +79,7 @@ charts.TimeSeriesChart generateChart(
charts.measureAxisIdKey, charts.Axis.secondaryMeasureAxisId) charts.measureAxisIdKey, charts.Axis.secondaryMeasureAxisId)
..setAttribute(charts.rendererIdKey, 'basalRenderer'), ..setAttribute(charts.rendererIdKey, 'basalRenderer'),
charts.Series<LogEntry, DateTime>( charts.Series<LogEntry, DateTime>(
id: 'Bolus', id: translate(LocalizationKeys.reports_ids_bolus),
colorFn: (LogEntry entry, _) => colorFn: (LogEntry entry, _) =>
charts.MaterialPalette.blue.shadeDefault, charts.MaterialPalette.blue.shadeDefault,
domainFn: (LogEntry entry, _) => entry.time, domainFn: (LogEntry entry, _) => entry.time,
@ -260,7 +262,7 @@ class _DailyChartState extends State<DailyChart> {
value: showChart, value: showChart,
onChanged: (_) => onChanged: (_) =>
setState(() => showChart = !showChart), setState(() => showChart = !showChart),
title: const Text('show Chart'), title: Text(translate(LocalizationKeys.reports_dailyCharts_showChart)),
controlAffinity: ListTileControlAffinity.leading, controlAffinity: ListTileControlAffinity.leading,
), ),
Padding( Padding(
@ -271,7 +273,7 @@ class _DailyChartState extends State<DailyChart> {
? (_) => ? (_) =>
setState(() => showBolus = !showBolus) setState(() => showBolus = !showBolus)
: null, : null,
title: const Text('show Bolus'), title: Text(translate(LocalizationKeys.reports_dailyCharts_showBolus)),
controlAffinity: controlAffinity:
ListTileControlAffinity.leading, ListTileControlAffinity.leading,
), ),
@ -284,7 +286,7 @@ class _DailyChartState extends State<DailyChart> {
? (_) => ? (_) =>
setState(() => showBasal = !showBasal) setState(() => showBasal = !showBasal)
: null, : null,
title: const Text('show Basal'), title: Text(translate(LocalizationKeys.reports_dailyCharts_showBasal)),
controlAffinity: controlAffinity:
ListTileControlAffinity.leading, ListTileControlAffinity.leading,
), ),
@ -297,7 +299,7 @@ class _DailyChartState extends State<DailyChart> {
? (_) => ? (_) =>
setState(() => showMeals = !showMeals) setState(() => showMeals = !showMeals)
: null, : null,
title: const Text('show Meals'), title: Text(translate(LocalizationKeys.reports_dailyCharts_showMeals)),
controlAffinity: controlAffinity:
ListTileControlAffinity.leading, ListTileControlAffinity.leading,
), ),
@ -307,7 +309,7 @@ class _DailyChartState extends State<DailyChart> {
actions: <Widget>[ actions: <Widget>[
ElevatedButton( ElevatedButton(
onPressed: () => Navigator.pop(context), onPressed: () => Navigator.pop(context),
child: const Text('CLOSE'), child: Text(translate(LocalizationKeys.general_close).toUpperCase()),
), ),
]), ]),
), ),
@ -378,9 +380,8 @@ class _DailyChartState extends State<DailyChart> {
child: logEntries.isNotEmpty child: logEntries.isNotEmpty
? generateChart(date, logEntries, logEvents, targets, showMeals, ? generateChart(date, logEntries, logEvents, targets, showMeals,
showBasal, showBolus) showBasal, showBolus)
: const Center( : Center(
child: Text( child: Text(translate(LocalizationKeys.reports_dailyCharts_empty)),
'You have not created any Log Entries for this date yet!'),
), ),
), ),
], ],

View File

@ -2,6 +2,7 @@ import 'dart:io';
import 'dart:math'; import 'dart:math';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:diameter/components/forms/date_time_form_field.dart'; import 'package:diameter/components/forms/date_time_form_field.dart';
import 'package:diameter/localization_keys.dart';
import 'package:diameter/models/glucose_target.dart'; import 'package:diameter/models/glucose_target.dart';
import 'package:diameter/models/log_bolus.dart'; import 'package:diameter/models/log_bolus.dart';
import 'package:diameter/models/log_entry.dart'; import 'package:diameter/models/log_entry.dart';
@ -12,6 +13,7 @@ import 'package:diameter/screens/reports/daily_chart.dart';
import 'package:diameter/utils/date_time_utils.dart'; import 'package:diameter/utils/date_time_utils.dart';
import 'package:diameter/utils/utils.dart'; import 'package:diameter/utils/utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:open_file/open_file.dart'; import 'package:open_file/open_file.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
@ -121,7 +123,7 @@ class _ExportDialogState extends State<ExportDialog> {
children: [ children: [
Expanded( Expanded(
child: RadioListTile( child: RadioListTile(
title: const Text('single date'), title: Text(translate(LocalizationKeys.reports_export_singleDate)),
groupValue: exportRange, groupValue: exportRange,
value: false, value: false,
onChanged: (_) { onChanged: (_) {
@ -132,7 +134,7 @@ class _ExportDialogState extends State<ExportDialog> {
), ),
Expanded( Expanded(
child: RadioListTile( child: RadioListTile(
title: const Text('range'), title: Text(translate(LocalizationKeys.reports_export_range)),
groupValue: exportRange, groupValue: exportRange,
value: true, value: true,
onChanged: (value) { onChanged: (value) {
@ -148,7 +150,7 @@ class _ExportDialogState extends State<ExportDialog> {
Expanded( Expanded(
child: DateTimeFormField( child: DateTimeFormField(
date: exportStartDate, date: exportStartDate,
label: 'Date', label: translate(LocalizationKeys.reports_export_date),
controller: exportStartDateController, controller: exportStartDateController,
onChanged: (newDate) { onChanged: (newDate) {
if (newDate != null) { if (newDate != null) {
@ -166,7 +168,7 @@ class _ExportDialogState extends State<ExportDialog> {
padding: const EdgeInsets.only(left: 5.0), padding: const EdgeInsets.only(left: 5.0),
child: DateTimeFormField( child: DateTimeFormField(
date: exportEndDate, date: exportEndDate,
label: 'End Date', label: translate(LocalizationKeys.reports_export_endDate),
controller: exportEndDateController, controller: exportEndDateController,
onChanged: (newDate) { onChanged: (newDate) {
if (newDate != null) { if (newDate != null) {
@ -185,7 +187,7 @@ class _ExportDialogState extends State<ExportDialog> {
CheckboxListTile( CheckboxListTile(
value: showChart, value: showChart,
onChanged: (_) => setState(() => showChart = !showChart), onChanged: (_) => setState(() => showChart = !showChart),
title: const Text('show Chart'), title: Text(translate(LocalizationKeys.reports_export_showChart)),
controlAffinity: ListTileControlAffinity.leading, controlAffinity: ListTileControlAffinity.leading,
), ),
Padding( Padding(
@ -195,7 +197,7 @@ class _ExportDialogState extends State<ExportDialog> {
onChanged: showChart onChanged: showChart
? (_) => setState(() => showBolus = !showBolus) ? (_) => setState(() => showBolus = !showBolus)
: null, : null,
title: const Text('show Bolus'), title: Text(translate(LocalizationKeys.reports_export_showBolus)),
controlAffinity: ListTileControlAffinity.leading, controlAffinity: ListTileControlAffinity.leading,
), ),
), ),
@ -206,7 +208,7 @@ class _ExportDialogState extends State<ExportDialog> {
onChanged: showChart onChanged: showChart
? (_) => setState(() => showBasal = !showBasal) ? (_) => setState(() => showBasal = !showBasal)
: null, : null,
title: const Text('show Basal'), title: Text(translate(LocalizationKeys.reports_export_showBasal)),
controlAffinity: ListTileControlAffinity.leading, controlAffinity: ListTileControlAffinity.leading,
), ),
), ),
@ -217,14 +219,14 @@ class _ExportDialogState extends State<ExportDialog> {
onChanged: showChart onChanged: showChart
? (_) => setState(() => showMeals = !showMeals) ? (_) => setState(() => showMeals = !showMeals)
: null, : null,
title: const Text('show Meals'), title: Text(translate(LocalizationKeys.reports_export_showMeals)),
controlAffinity: ListTileControlAffinity.leading, controlAffinity: ListTileControlAffinity.leading,
), ),
), ),
CheckboxListTile( CheckboxListTile(
value: showTable, value: showTable,
onChanged: (_) => setState(() => showTable = !showTable), onChanged: (_) => setState(() => showTable = !showTable),
title: const Text('show Table'), title: Text(translate(LocalizationKeys.reports_export_showTable)),
controlAffinity: ListTileControlAffinity.leading, controlAffinity: ListTileControlAffinity.leading,
), ),
], ],
@ -232,13 +234,13 @@ class _ExportDialogState extends State<ExportDialog> {
actions: <Widget>[ actions: <Widget>[
TextButton( TextButton(
onPressed: () => Navigator.pop(context), onPressed: () => Navigator.pop(context),
child: const Text('CANCEL'), child: Text(translate(LocalizationKeys.general_cancel).toUpperCase()),
), ),
ElevatedButton( ElevatedButton(
onPressed: !_isSaving && (showChart || showTable) onPressed: !_isSaving && (showChart || showTable)
? () => onExport(context) ? () => onExport(context)
: null, : null,
child: const Text('EXPORT'), child: Text(translate(LocalizationKeys.reports_export_export).toUpperCase()),
), ),
]); ]);
} }
@ -273,15 +275,14 @@ pw.Table generateTable(List<LogEntry> logEntries) {
const small = pw.TextStyle(fontSize: 8.0); const small = pw.TextStyle(fontSize: 8.0);
final tableHeaders = [ final tableHeaders = [
'Time', translate(LocalizationKeys.reports_export_tableHeaders_time),
'Glucose', translate(LocalizationKeys.reports_export_tableHeaders_glucose),
'Bolus', translate(LocalizationKeys.reports_export_tableHeaders_notes),
'Notes', translate(LocalizationKeys.reports_export_tableHeaders_meals),
'Meals', translate(LocalizationKeys.reports_export_tableHeaders_portionSize),
'Portion Size', translate(LocalizationKeys.reports_export_tableHeaders_carbs),
'Carbohydrates', translate(LocalizationKeys.reports_export_tableHeaders_mealBolus),
'Meal Bolus', translate(LocalizationKeys.reports_export_tableHeaders_mealNotes),
'Meal Notes',
]; ];
final List<pw.TableRow> data = logEntries.map((logEntry) { final List<pw.TableRow> data = logEntries.map((logEntry) {
@ -304,7 +305,9 @@ pw.Table generateTable(List<LogEntry> logEntries) {
for (var bolus in boli for (var bolus in boli
.where((bolus) => bolus.meal.targetId == meal.id) .where((bolus) => bolus.meal.targetId == meal.id)
.toList()) .toList())
'${bolus.units} U${bolus.delay != null ? ' (over ${bolus.delay} min)' : ''}' '${bolus.units} ${translate(LocalizationKeys.general_suffixes_units)} ${bolus.delay != null ? translate(LocalizationKeys.log_detail_tabs_bolus_delayedBy, args: {
"delay": '${bolus.delay} ${translate(LocalizationKeys.general_suffixes_mins)}'
}) : ''}'
].join(' + '), ].join(' + '),
notes: meal.notes ?? '', notes: meal.notes ?? '',
)) ))
@ -320,7 +323,9 @@ pw.Table generateTable(List<LogEntry> logEntries) {
bolus.meal.targetId != 0 && bolus.meal.targetId != 0 &&
!meals.any((meal) => meal.id == bolus.meal.targetId)) !meals.any((meal) => meal.id == bolus.meal.targetId))
.map((bolus) => .map((bolus) =>
'${bolus.units} U${bolus.delay != null ? ' (over ${bolus.delay} min)' : ''}${bolus.meal.target != null ? ' (for ${bolus.meal.target!.value})' : ''}') '${bolus.units} ${translate(LocalizationKeys.general_suffixes_units)} ${bolus.delay != null ? translate(LocalizationKeys.log_detail_tabs_bolus_delayedBy, args: {
"delay": '${bolus.delay} ${translate(LocalizationKeys.general_suffixes_mins)}'
}) : ''}')
.toList(); .toList();
return pw.TableRow( return pw.TableRow(
@ -358,7 +363,7 @@ pw.Table generateTable(List<LogEntry> logEntries) {
), ),
// Bolus // Bolus
pw.Text( pw.Text(
[for (var bolus in glucoseBoli) '${bolus.units} U'].join(' + ')), [for (var bolus in glucoseBoli) '${bolus.units} ${translate(LocalizationKeys.general_suffixes_units)}'].join(' + ')),
// Notes // Notes
pw.Text(logEntry.notes ?? '', style: small), pw.Text(logEntry.notes ?? '', style: small),
// Meals // Meals
@ -449,7 +454,7 @@ Future<Uint8List> generateLogReport(
return pw.Column( return pw.Column(
children: [ children: [
pw.Text( pw.Text(
'LOG REPORT', translate(LocalizationKeys.reports_export_title).toUpperCase(),
style: pw.TextStyle( style: pw.TextStyle(
fontWeight: pw.FontWeight.bold, fontWeight: pw.FontWeight.bold,
fontSize: 12, fontSize: 12,

View File

@ -1,6 +1,8 @@
import 'package:diameter/localization_keys.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:diameter/screens/reports/export.dart'; import 'package:diameter/screens/reports/export.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
class ReportsOverviewScreen extends StatefulWidget { class ReportsOverviewScreen extends StatefulWidget {
static const String routeName = '/reports'; static const String routeName = '/reports';
@ -23,7 +25,7 @@ class _ReportsOverviewScreenState extends State<ReportsOverviewScreen> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: const Text('Reports'), title: Text(translate(LocalizationKeys.reports_title)),
), ),
drawer: drawer:
const Navigation(currentLocation: ReportsOverviewScreen.routeName), const Navigation(currentLocation: ReportsOverviewScreen.routeName),
@ -47,7 +49,7 @@ class _ReportsOverviewScreenState extends State<ReportsOverviewScreen> {
child: Icon(Icons.today, size: 50, color: Theme.of(context).textTheme.subtitle2?.color), child: Icon(Icons.today, size: 50, color: Theme.of(context).textTheme.subtitle2?.color),
), ),
Text( Text(
'DAILY REPORT', translate(LocalizationKeys.reports_sections_dailyReport).toUpperCase(),
style: Theme.of(context).textTheme.subtitle2, style: Theme.of(context).textTheme.subtitle2,
textAlign: TextAlign.center textAlign: TextAlign.center
), ),
@ -72,7 +74,7 @@ class _ReportsOverviewScreenState extends State<ReportsOverviewScreen> {
child: Icon(Icons.today, size: 50, color: Theme.of(context).textTheme.subtitle2?.color), child: Icon(Icons.today, size: 50, color: Theme.of(context).textTheme.subtitle2?.color),
), ),
Text( Text(
'PDF REPORT', translate(LocalizationKeys.reports_sections_pdfReport).toUpperCase(),
style: Theme.of(context).textTheme.subtitle2, style: Theme.of(context).textTheme.subtitle2,
textAlign: TextAlign.center textAlign: TextAlign.center
), ),

View File

@ -2,9 +2,9 @@ import 'package:diameter/components/detail.dart';
import 'package:diameter/utils/dialog_utils.dart'; import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/components/forms/auto_complete_dropdown_button.dart'; import 'package:diameter/components/forms/auto_complete_dropdown_button.dart';
import 'package:diameter/components/forms/form_wrapper.dart'; import 'package:diameter/components/forms/form_wrapper.dart';
import 'package:diameter/models/ingredient.dart'; import 'package:diameter/models/x_ingredient.dart';
import 'package:diameter/models/meal.dart'; import 'package:diameter/models/meal.dart';
import 'package:diameter/models/recipe.dart'; import 'package:diameter/models/x_recipe.dart';
import 'package:diameter/models/settings.dart'; import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:diameter/screens/meal/meal_detail.dart'; import 'package:diameter/screens/meal/meal_detail.dart';

View File

@ -1,9 +1,9 @@
import 'package:diameter/screens/x_recipe/recipe_detail.dart';
import 'package:diameter/utils/dialog_utils.dart'; import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/models/ingredient.dart'; import 'package:diameter/models/x_ingredient.dart';
import 'package:diameter/models/recipe.dart'; import 'package:diameter/models/x_recipe.dart';
import 'package:diameter/models/settings.dart'; import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:diameter/screens/recipe/recipe_detail.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class RecipeListScreen extends StatefulWidget { class RecipeListScreen extends StatefulWidget {

View File

@ -1,11 +1,13 @@
import 'package:diameter/components/forms/boolean_form_field.dart'; import 'package:diameter/components/forms/boolean_form_field.dart';
import 'package:diameter/components/forms/number_form_field.dart'; import 'package:diameter/components/forms/number_form_field.dart';
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart'; import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/components/forms/auto_complete_dropdown_button.dart'; import 'package:diameter/components/forms/auto_complete_dropdown_button.dart';
import 'package:diameter/models/settings.dart'; import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:diameter/utils/utils.dart'; import 'package:diameter/utils/utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
class SettingsScreen extends StatefulWidget { class SettingsScreen extends StatefulWidget {
@ -152,19 +154,19 @@ class _SettingsScreenState extends State<SettingsScreen> {
showConfirmationDialogOnDelete: _showConfirmationDialogOnDelete, showConfirmationDialogOnDelete: _showConfirmationDialogOnDelete,
showConfirmationDialogOnStopEvent: _showConfirmationDialogOnStopEvent, showConfirmationDialogOnStopEvent: _showConfirmationDialogOnStopEvent,
)); ));
reload(message: 'Settings updated'); reload(message: translate(LocalizationKeys.settings_updated));
} }
void onReset() { void onReset() {
Settings.reset(); Settings.reset();
reload(message: 'Settings have been reset to default'); reload(message: translate(LocalizationKeys.settings_reset));
} }
void handleResetAction() async { void handleResetAction() async {
DialogUtils.showConfirmationDialog( DialogUtils.showConfirmationDialog(
context: context, context: context,
onConfirm: onReset, onConfirm: onReset,
message: 'Are you sure you want to reset all settings?', message: translate(LocalizationKeys.settings_confirmReset),
); );
} }
@ -172,7 +174,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: const Text('Application Settings'), title: Text(translate(LocalizationKeys.settings_title)),
), ),
drawer: const Navigation(currentLocation: SettingsScreen.routeName), drawer: const Navigation(currentLocation: SettingsScreen.routeName),
body: SingleChildScrollView( body: SingleChildScrollView(
@ -191,7 +193,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
children: [ children: [
Expanded( Expanded(
child: Text( child: Text(
'MEASUREMENTS', translate(LocalizationKeys.settings_sections_measurements).toUpperCase(),
style: Theme.of(context).textTheme.subtitle2, style: Theme.of(context).textTheme.subtitle2,
), ),
), ),
@ -208,7 +210,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
AutoCompleteDropdownButton<String>( AutoCompleteDropdownButton<String>(
controller: _nutritionMeasurementLabelController, controller: _nutritionMeasurementLabelController,
selectedItem: _nutritionMeasurementLabelController.text, selectedItem: _nutritionMeasurementLabelController.text,
label: 'Preferred Nutrition Measurement', label: translate(LocalizationKeys.settings_fields_nutritionMeasurement),
items: nutritionMeasurementLabels, items: nutritionMeasurementLabels,
onChanged: (value) { onChanged: (value) {
_nutritionMeasurementLabelController.text = _nutritionMeasurementLabelController.text =
@ -221,7 +223,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
child: AutoCompleteDropdownButton<String>( child: AutoCompleteDropdownButton<String>(
controller: _glucoseMeasurementLabelController, controller: _glucoseMeasurementLabelController,
selectedItem: _glucoseMeasurementLabelController.text, selectedItem: _glucoseMeasurementLabelController.text,
label: 'Preferred Glucose Measurement', label: translate(LocalizationKeys.settings_fields_glucoseMeasurement),
items: glucoseMeasurementLabels, items: glucoseMeasurementLabels,
onChanged: (value) { onChanged: (value) {
_glucoseMeasurementLabelController.text = _glucoseMeasurementLabelController.text =
@ -232,8 +234,8 @@ class _SettingsScreenState extends State<SettingsScreen> {
), ),
Settings.glucoseMeasurement == GlucoseMeasurement.mgPerDl Settings.glucoseMeasurement == GlucoseMeasurement.mgPerDl
? NumberFormField( ? NumberFormField(
label: 'Target glucose', label: translate(LocalizationKeys.settings_fields_targetGlucose),
suffix: 'mg/dl', suffix: Settings.glucoseMeasurementSuffix,
controller: _targetGlucoseMgPerDlController, controller: _targetGlucoseMgPerDlController,
showSteppers: false, showSteppers: false,
onChanged: (_) async { onChanged: (_) async {
@ -254,8 +256,8 @@ class _SettingsScreenState extends State<SettingsScreen> {
}, },
) )
: NumberFormField( : NumberFormField(
label: 'Target glucose', label: translate(LocalizationKeys.settings_fields_targetGlucose),
suffix: 'mmol/l', suffix: Settings.glucoseMeasurementSuffix,
controller: _targetGlucoseMmolPerLController, controller: _targetGlucoseMmolPerLController,
showSteppers: false, showSteppers: false,
onChanged: (_) async { onChanged: (_) async {
@ -277,7 +279,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
child: NumberFormField( child: NumberFormField(
controller: _insulinIncrementsController, controller: _insulinIncrementsController,
showSteppers: false, showSteppers: false,
label: 'Insulin increment', label: translate(LocalizationKeys.settings_fields_insulinIncrement),
onChanged: (value) { onChanged: (value) {
_insulinIncrementsController.text = _insulinIncrementsController.text =
(value ?? 0).toString(); (value ?? 0).toString();
@ -287,7 +289,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
NumberFormField( NumberFormField(
controller: _nutritionIncrementsController, controller: _nutritionIncrementsController,
showSteppers: false, showSteppers: false,
label: 'Nutrition increment', label: translate(LocalizationKeys.settings_fields_nutritionIncrement),
onChanged: (value) { onChanged: (value) {
_nutritionIncrementsController.text = _nutritionIncrementsController.text =
(value ?? 0).toString(); (value ?? 0).toString();
@ -298,7 +300,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
child: NumberFormField( child: NumberFormField(
controller: _mmolPerLIncrementsController, controller: _mmolPerLIncrementsController,
showSteppers: false, showSteppers: false,
label: 'Mmol/L increment', label: translate(LocalizationKeys.settings_fields_mmolLIncrement),
onChanged: (value) { onChanged: (value) {
_mmolPerLIncrementsController.text = _mmolPerLIncrementsController.text =
(value ?? 0).toString(); (value ?? 0).toString();
@ -307,7 +309,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
), ),
BooleanFormField( BooleanFormField(
value: _onlyDisplayActiveGlucoseMeasurement, value: _onlyDisplayActiveGlucoseMeasurement,
label: 'only display active glucose measurement', label: translate(LocalizationKeys.settings_fields_onlyDisplayActive),
onChanged: (value) { onChanged: (value) {
_onlyDisplayActiveGlucoseMeasurement = value; _onlyDisplayActiveGlucoseMeasurement = value;
saveSettings(); saveSettings();
@ -317,7 +319,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
value: _displayBothGlucoseMeasurementsInDetailView, value: _displayBothGlucoseMeasurementsInDetailView,
enabled: !_onlyDisplayActiveGlucoseMeasurement, enabled: !_onlyDisplayActiveGlucoseMeasurement,
label: label:
'display both glucose measurements in detail view', translate(LocalizationKeys.settings_fields_displayBothDetail),
onChanged: (value) { onChanged: (value) {
_displayBothGlucoseMeasurementsInDetailView = value; _displayBothGlucoseMeasurementsInDetailView = value;
saveSettings(); saveSettings();
@ -326,7 +328,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
BooleanFormField( BooleanFormField(
value: _displayBothGlucoseMeasurementsInListView, value: _displayBothGlucoseMeasurementsInListView,
enabled: !_onlyDisplayActiveGlucoseMeasurement, enabled: !_onlyDisplayActiveGlucoseMeasurement,
label: 'display both glucose measurements in list view', label: translate(LocalizationKeys.settings_fields_displayBothList),
onChanged: (value) { onChanged: (value) {
_displayBothGlucoseMeasurementsInListView = value; _displayBothGlucoseMeasurementsInListView = value;
saveSettings(); saveSettings();
@ -347,7 +349,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
children: [ children: [
Expanded( Expanded(
child: Text( child: Text(
'CONFIRMATION PROMPTS', translate(LocalizationKeys.settings_sections_confirmation).toUpperCase(),
style: Theme.of(context).textTheme.subtitle2, style: Theme.of(context).textTheme.subtitle2,
), ),
), ),
@ -364,7 +366,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
BooleanFormField( BooleanFormField(
value: _showConfirmationDialogOnCancel, value: _showConfirmationDialogOnCancel,
label: label:
'on cancelling edit or creation of a record if changes have already been made', translate(LocalizationKeys.settings_fields_confirmOnCancel),
onChanged: (value) { onChanged: (value) {
_showConfirmationDialogOnCancel = value; _showConfirmationDialogOnCancel = value;
saveSettings(); saveSettings();
@ -372,7 +374,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
), ),
BooleanFormField( BooleanFormField(
value: _showConfirmationDialogOnDelete, value: _showConfirmationDialogOnDelete,
label: 'on deleting a record', label: translate(LocalizationKeys.settings_fields_confirmOnDelete),
onChanged: (value) { onChanged: (value) {
_showConfirmationDialogOnDelete = value; _showConfirmationDialogOnDelete = value;
saveSettings(); saveSettings();
@ -380,7 +382,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
), ),
BooleanFormField( BooleanFormField(
value: _showConfirmationDialogOnStopEvent, value: _showConfirmationDialogOnStopEvent,
label: 'on stopping (ending) an event', label: translate(LocalizationKeys.settings_fields_confirmOnEndEvent),
onChanged: (value) { onChanged: (value) {
_showConfirmationDialogOnStopEvent = value; _showConfirmationDialogOnStopEvent = value;
saveSettings(); saveSettings();
@ -401,7 +403,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
children: [ children: [
Expanded( Expanded(
child: Text( child: Text(
'TIME & DATE FORMAT', translate(LocalizationKeys.settings_sections_dateTimeFormat).toUpperCase(),
style: Theme.of(context).textTheme.subtitle2, style: Theme.of(context).textTheme.subtitle2,
), ),
), ),
@ -421,15 +423,9 @@ class _SettingsScreenState extends State<SettingsScreen> {
Expanded( Expanded(
child: TextFormField( child: TextFormField(
controller: _dateFormatController, controller: _dateFormatController,
decoration: const InputDecoration( decoration: InputDecoration(
labelText: 'Date Format', labelText: translate(LocalizationKeys.settings_fields_dateFormat),
), ),
validator: (value) {
if (value!.trim().isEmpty) {
return 'Empty title';
}
return null;
},
), ),
), ),
Expanded( Expanded(
@ -439,7 +435,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Text('Example', textScaleFactor: 0.75), Text(translate(LocalizationKeys.general_example), textScaleFactor: 0.75),
Text( Text(
DateFormat(_dateFormatController.text) DateFormat(_dateFormatController.text)
.format(DateTime.now()), .format(DateTime.now()),
@ -459,15 +455,9 @@ class _SettingsScreenState extends State<SettingsScreen> {
Expanded( Expanded(
child: TextFormField( child: TextFormField(
controller: _longDateFormatController, controller: _longDateFormatController,
decoration: const InputDecoration( decoration: InputDecoration(
labelText: 'Long Date Format', labelText: translate(LocalizationKeys.settings_fields_longDateFormat),
), ),
validator: (value) {
if (value!.trim().isEmpty) {
return 'Empty title';
}
return null;
},
), ),
), ),
Expanded( Expanded(
@ -477,7 +467,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Text('Example', Text(translate(LocalizationKeys.general_example),
textScaleFactor: 0.75), textScaleFactor: 0.75),
Text( Text(
DateFormat(_longDateFormatController.text) DateFormat(_longDateFormatController.text)
@ -497,15 +487,9 @@ class _SettingsScreenState extends State<SettingsScreen> {
Expanded( Expanded(
child: TextFormField( child: TextFormField(
controller: _timeFormatController, controller: _timeFormatController,
decoration: const InputDecoration( decoration: InputDecoration(
labelText: 'Time Format', labelText: translate(LocalizationKeys.settings_fields_timeFormat),
), ),
validator: (value) {
if (value!.trim().isEmpty) {
return 'Empty title';
}
return null;
},
), ),
), ),
Expanded( Expanded(
@ -515,7 +499,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Text('Example', textScaleFactor: 0.75), Text(translate(LocalizationKeys.general_example), textScaleFactor: 0.75),
Text( Text(
DateFormat(_timeFormatController.text) DateFormat(_timeFormatController.text)
.format(DateTime.now()), .format(DateTime.now()),
@ -535,15 +519,9 @@ class _SettingsScreenState extends State<SettingsScreen> {
Expanded( Expanded(
child: TextFormField( child: TextFormField(
controller: _longTimeFormatController, controller: _longTimeFormatController,
decoration: const InputDecoration( decoration: InputDecoration(
labelText: 'Long Time Format', labelText: translate(LocalizationKeys.settings_fields_longTimeFormat),
), ),
validator: (value) {
if (value!.trim().isEmpty) {
return 'Empty title';
}
return null;
},
), ),
), ),
Expanded( Expanded(
@ -553,7 +531,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Text('Example', Text(translate(LocalizationKeys.general_example),
textScaleFactor: 0.75), textScaleFactor: 0.75),
Text( Text(
DateFormat(_longTimeFormatController.text) DateFormat(_longTimeFormatController.text)
@ -584,7 +562,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
Icons.settings_backup_restore, Icons.settings_backup_restore,
size: 18.0, size: 18.0,
), ),
label: const Text('RESET ALL'), label: Text(translate(LocalizationKeys.settings_resetAll).toUpperCase()),
), ),
const Spacer(), const Spacer(),
], ],

View File

@ -1,4 +1,6 @@
import 'package:diameter/localization_keys.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
class DialogUtils { class DialogUtils {
static void showCancelConfirmationDialog( static void showCancelConfirmationDialog(
@ -13,30 +15,29 @@ class DialogUtils {
List<Widget> actions = [ List<Widget> actions = [
TextButton( TextButton(
onPressed: () => Navigator.pop(context, 'CANCEL'), onPressed: () => Navigator.pop(context, 'CANCEL'),
child: const Text('CANCEL'), child: Text(translate(LocalizationKeys.general_cancel).toUpperCase()),
), ),
]; ];
actions.add(isNew actions.add(isNew
? ElevatedButton( ? ElevatedButton(
onPressed: () => Navigator.pop(context, 'DISCARD'), onPressed: () => Navigator.pop(context, 'DISCARD'),
child: const Text('DISCARD'), child: Text(translate(LocalizationKeys.general_discard).toUpperCase()),
) )
: TextButton( : TextButton(
onPressed: () => Navigator.pop(context, 'DISCARD'), onPressed: () => Navigator.pop(context, 'DISCARD'),
child: const Text('DISCARD'), child: Text(translate(LocalizationKeys.general_discard).toUpperCase()),
)); ));
if (!isNew) { if (!isNew) {
actions.add(ElevatedButton( actions.add(ElevatedButton(
onPressed: () => Navigator.pop(context, 'SAVE'), onPressed: () => Navigator.pop(context, 'SAVE'),
child: const Text('SAVE'), child: Text(translate(LocalizationKeys.general_save).toUpperCase()),
)); ));
} }
return AlertDialog( return AlertDialog(
content: Text(message ?? content: Text(message ?? translate(LocalizationKeys.general_confirmDiscard)),
'You already made some changes. Discard your input?'),
actions: actions, actions: actions,
); );
}).then((value) { }).then((value) {
@ -52,21 +53,21 @@ class DialogUtils {
static void showConfirmationDialog( static void showConfirmationDialog(
{required BuildContext context, {required BuildContext context,
required void Function() onConfirm, required void Function() onConfirm,
String message = 'Are you sure you want to delete this record?', String? message,
String confirmationLabel = 'DELETE'}) { String? confirmationLabel}) {
showDialog( showDialog(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return AlertDialog( return AlertDialog(
content: Text(message), content: Text(message ?? translate(LocalizationKeys.general_confirmDelete)),
actions: <Widget>[ actions: <Widget>[
TextButton( TextButton(
onPressed: () => Navigator.pop(context, 'CANCEL'), onPressed: () => Navigator.pop(context, 'CANCEL'),
child: const Text('CANCEL'), child: Text(translate(LocalizationKeys.general_cancel).toUpperCase()),
), ),
ElevatedButton( ElevatedButton(
onPressed: () => Navigator.pop(context, 'CONFIRM'), onPressed: () => Navigator.pop(context, 'CONFIRM'),
child: Text(confirmationLabel), child: Text(confirmationLabel ?? translate(LocalizationKeys.general_confirm).toUpperCase()),
), ),
], ],
); );

Binary file not shown.

Binary file not shown.

View File

@ -1,27 +1,34 @@
# Generated by pub # Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile # See https://dart.dev/tools/pub/glossary#lockfile
packages: packages:
_discoveryapis_commons:
dependency: transitive
description:
name: _discoveryapis_commons
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
_fe_analyzer_shared: _fe_analyzer_shared:
dependency: transitive dependency: transitive
description: description:
name: _fe_analyzer_shared name: _fe_analyzer_shared
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "36.0.0" version: "38.0.0"
analyzer: analyzer:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: analyzer name: analyzer
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.3.1" version: "3.4.1"
archive: archive:
dependency: transitive dependency: transitive
description: description:
name: archive name: archive
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.2.2" version: "3.3.0"
args: args:
dependency: transitive dependency: transitive
description: description:
@ -231,7 +238,7 @@ packages:
name: flex_color_scheme name: flex_color_scheme
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.0.1" version: "4.2.0"
flutter: flutter:
dependency: "direct main" dependency: "direct main"
description: flutter description: flutter
@ -244,11 +251,23 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.4" version: "1.0.4"
flutter_localizations:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_test: flutter_test:
dependency: "direct dev" dependency: "direct dev"
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
flutter_translate:
dependency: "direct main"
description:
name: flutter_translate
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1"
flutter_web_plugins: flutter_web_plugins:
dependency: transitive dependency: transitive
description: flutter description: flutter
@ -268,6 +287,34 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.2" version: "2.0.2"
google_sign_in:
dependency: "direct main"
description:
name: google_sign_in
url: "https://pub.dartlang.org"
source: hosted
version: "5.2.4"
google_sign_in_platform_interface:
dependency: transitive
description:
name: google_sign_in_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.2"
google_sign_in_web:
dependency: transitive
description:
name: google_sign_in_web
url: "https://pub.dartlang.org"
source: hosted
version: "0.10.0+5"
googleapis:
dependency: "direct main"
description:
name: googleapis
url: "https://pub.dartlang.org"
source: hosted
version: "7.0.0"
graphs: graphs:
dependency: transitive dependency: transitive
description: description:
@ -519,7 +566,7 @@ packages:
name: printing name: printing
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "5.7.4" version: "5.7.5"
process: process:
dependency: transitive dependency: transitive
description: description:
@ -555,13 +602,20 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.0.0" version: "3.0.0"
quiver:
dependency: transitive
description:
name: quiver
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1+1"
shelf: shelf:
dependency: transitive dependency: transitive
description: description:
name: shelf name: shelf
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.2.0" version: "1.3.0"
shelf_web_socket: shelf_web_socket:
dependency: transitive dependency: transitive
description: description:
@ -644,6 +698,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.3.0" version: "1.3.0"
universal_io:
dependency: transitive
description:
name: universal_io
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.4"
vector_math: vector_math:
dependency: transitive dependency: transitive
description: description:
@ -671,7 +732,7 @@ packages:
name: win32 name: win32
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.4.2" version: "2.5.1"
xdg_directories: xdg_directories:
dependency: transitive dependency: transitive
description: description:
@ -694,5 +755,5 @@ packages:
source: hosted source: hosted
version: "3.1.0" version: "3.1.0"
sdks: sdks:
dart: ">=2.15.0 <3.0.0" dart: ">=2.16.0 <3.0.0"
flutter: ">=2.8.0" flutter: ">=2.8.0"

View File

@ -11,16 +11,21 @@ environment:
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
flutter_localizations:
sdk: flutter
path_provider: ^2.0.9 path_provider: ^2.0.9
cupertino_icons: ^1.0.2 cupertino_icons: ^1.0.2
flex_color_scheme: ^3.0.1 flex_color_scheme: ^4.2.0
intl: ^0.17.0 intl: ^0.17.0
objectbox: ^1.2.0 objectbox: ^1.2.0
objectbox_sync_flutter_libs: any objectbox_sync_flutter_libs: any
charts_flutter: ^0.12.0
printing: ^5.7.2 printing: ^5.7.2
pdf: ^3.7.1 pdf: ^3.7.1
open_file: ^3.2.1 open_file: ^3.2.1
flutter_translate: ^3.0.1
googleapis: ^7.0.0
google_sign_in: ^5.2.1
charts_flutter: ^0.12.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
@ -33,3 +38,5 @@ dev_dependencies:
flutter: flutter:
uses-material-design: true uses-material-design: true
assets:
- assets/i18n/

View File

@ -8,7 +8,7 @@
// import 'package:flutter/material.dart'; // import 'package:flutter/material.dart';
// import 'package:flutter_test/flutter_test.dart'; // import 'package:flutter_test/flutter_test.dart';
// import 'package:tide/main.dart'; // import 'package:diameter/main.dart';
void main() { void main() {
// testWidgets('Counter increments smoke test', (WidgetTester tester) async { // testWidgets('Counter increments smoke test', (WidgetTester tester) async {

View File

@ -1,7 +1,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<!-- <!--
If you are serving your web app in a path other than the root, change the If you are serving your web app in a path other than the root, change the
href value below to reflect the base path you are serving from. href value below to reflect the base path you are serving from.
@ -14,88 +14,106 @@
This is a placeholder for base href that will be replaced by the value of This is a placeholder for base href that will be replaced by the value of
the `--base-href` argument provided to `flutter build`. the `--base-href` argument provided to `flutter build`.
--> -->
<base href="$FLUTTER_BASE_HREF"> <base href="$FLUTTER_BASE_HREF" />
<meta charset="UTF-8"> <meta charset="UTF-8" />
<meta content="IE=Edge" http-equiv="X-UA-Compatible"> <meta content="IE=Edge" http-equiv="X-UA-Compatible" />
<meta name="description" content="A new Flutter project."> <meta name="description" content="A new Flutter project." />
<!-- iOS meta tags & icons --> <!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black"> <meta name="apple-mobile-web-app-status-bar-style" content="black" />
<meta name="apple-mobile-web-app-title" content="tide"> <meta name="apple-mobile-web-app-title" content="diameter" />
<link rel="apple-touch-icon" href="icons/Icon-192.png"> <link rel="apple-touch-icon" href="icons/Icon-192.png" />
<title>tide</title> <title>diameter</title>
<link rel="manifest" href="manifest.json"> <link rel="manifest" href="manifest.json" />
</head> </head>
<body> <body>
<!-- This script installs service_worker.js to provide PWA functionality to <!-- This script installs service_worker.js to provide PWA functionality to
application. For more information, see: application. For more information, see:
https://developers.google.com/web/fundamentals/primers/service-workers --> https://developers.google.com/web/fundamentals/primers/service-workers -->
<script> <script>
var serviceWorkerVersion = null; var serviceWorkerVersion = null;
var scriptLoaded = false; var scriptLoaded = false;
function loadMainDartJs() { function loadMainDartJs() {
if (scriptLoaded) { if (scriptLoaded) {
return; return;
}
scriptLoaded = true;
var scriptTag = document.createElement('script');
scriptTag.src = 'main.dart.js';
scriptTag.type = 'application/javascript';
document.body.append(scriptTag);
}
if ('serviceWorker' in navigator) {
// Service workers are supported. Use them.
window.addEventListener('load', function () {
// Wait for registration to finish before dropping the <script> tag.
// Otherwise, the browser will load the script multiple times,
// potentially different versions.
var serviceWorkerUrl = 'flutter_service_worker.js?v=' + serviceWorkerVersion;
navigator.serviceWorker.register(serviceWorkerUrl)
.then((reg) => {
function waitForActivation(serviceWorker) {
serviceWorker.addEventListener('statechange', () => {
if (serviceWorker.state == 'activated') {
console.log('Installed new service worker.');
loadMainDartJs();
} }
}); scriptLoaded = true;
var scriptTag = document.createElement("script");
scriptTag.src = "main.dart.js";
scriptTag.type = "application/javascript";
document.body.append(scriptTag);
} }
if (!reg.active && (reg.installing || reg.waiting)) {
// No active web worker and we have installed or are installing
// one for the first time. Simply wait for it to activate.
waitForActivation(reg.installing || reg.waiting);
} else if (!reg.active.scriptURL.endsWith(serviceWorkerVersion)) {
// When the app updates the serviceWorkerVersion changes, so we
// need to ask the service worker to update.
console.log('New service worker available.');
reg.update();
waitForActivation(reg.installing);
} else {
// Existing service worker is still good.
console.log('Loading app from service worker.');
loadMainDartJs();
}
});
// If service worker doesn't succeed in a reasonable amount of time, if ("serviceWorker" in navigator) {
// fallback to plaint <script> tag. // Service workers are supported. Use them.
setTimeout(() => { window.addEventListener("load", function () {
if (!scriptLoaded) { // Wait for registration to finish before dropping the <script> tag.
console.warn( // Otherwise, the browser will load the script multiple times,
'Failed to load app from service worker. Falling back to plain <script> tag.', // potentially different versions.
); var serviceWorkerUrl =
loadMainDartJs(); "flutter_service_worker.js?v=" + serviceWorkerVersion;
} navigator.serviceWorker
}, 4000); .register(serviceWorkerUrl)
}); .then((reg) => {
} else { function waitForActivation(serviceWorker) {
// Service workers not supported. Just drop the <script> tag. serviceWorker.addEventListener(
loadMainDartJs(); "statechange",
} () => {
</script> if (
</body> serviceWorker.state == "activated"
) {
console.log(
"Installed new service worker."
);
loadMainDartJs();
}
}
);
}
if (
!reg.active &&
(reg.installing || reg.waiting)
) {
// No active web worker and we have installed or are installing
// one for the first time. Simply wait for it to activate.
waitForActivation(
reg.installing || reg.waiting
);
} else if (
!reg.active.scriptURL.endsWith(
serviceWorkerVersion
)
) {
// When the app updates the serviceWorkerVersion changes, so we
// need to ask the service worker to update.
console.log("New service worker available.");
reg.update();
waitForActivation(reg.installing);
} else {
// Existing service worker is still good.
console.log("Loading app from service worker.");
loadMainDartJs();
}
});
// If service worker doesn't succeed in a reasonable amount of time,
// fallback to plaint <script> tag.
setTimeout(() => {
if (!scriptLoaded) {
console.warn(
"Failed to load app from service worker. Falling back to plain <script> tag."
);
loadMainDartJs();
}
}, 4000);
});
} else {
// Service workers not supported. Just drop the <script> tag.
loadMainDartJs();
}
</script>
</body>
</html> </html>

View File

@ -89,7 +89,7 @@ BEGIN
BEGIN BEGIN
BLOCK "040904e4" BLOCK "040904e4"
BEGIN BEGIN
VALUE "CompanyName", "com.example" "\0" VALUE "CompanyName", "at.sarahziesel" "\0"
VALUE "FileDescription", "diameter" "\0" VALUE "FileDescription", "diameter" "\0"
VALUE "FileVersion", VERSION_AS_STRING "\0" VALUE "FileVersion", VERSION_AS_STRING "\0"
VALUE "InternalName", "diameter" "\0" VALUE "InternalName", "diameter" "\0"