diff --git a/assets/group3.json b/assets/group3.json index 55ce9e0..30e9886 100644 --- a/assets/group3.json +++ b/assets/group3.json @@ -7,7 +7,7 @@ }, "relapse_categories": ["App stresst mich", "langeweile", "lunge braucht es"], "mood_query": { - "days": ["Montag", "Freitag"], + "days": ["Montag", "Donnerstag"], "hours": 10, "minutes": 30 }, diff --git a/lib/main.dart b/lib/main.dart index a1cf5ac..c5a19b7 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:smoke_cess_app/pages/main_page.dart'; +import 'package:smoke_cess_app/providers/tasks_provider.dart'; import 'package:smoke_cess_app/services/notification_service.dart'; import 'package:timezone/data/latest.dart' as tz; import 'globals.dart' as globals; @@ -25,8 +26,15 @@ class MyApp extends StatelessWidget { Widget build(BuildContext context) { return MaterialApp( title: _title, - home: ChangeNotifierProvider( - create: (context) => SettingsProvider(), + home: MultiProvider( + providers: [ + ChangeNotifierProvider(create: (context) => SettingsProvider()), + ChangeNotifierProxyProvider( + create: (context) => TasksProvider(null), + update: (context, value, TasksProvider? previous) => + TasksProvider(value), + ), + ], child: const MyHomePage(), )); } diff --git a/lib/models/mood.dart b/lib/models/mood.dart index 562b2b4..7f5e7a6 100644 --- a/lib/models/mood.dart +++ b/lib/models/mood.dart @@ -7,6 +7,8 @@ class Mood implements DatabaseRecord { Mood(this._moodValue, this._comment, this._date); + DateTime get date => _date; + @override factory Mood.fromDatabase(Map map) { DateTime date = DateTime.parse(map['date']); diff --git a/lib/models/sleep.dart b/lib/models/sleep.dart index 3474b03..74fd6cb 100644 --- a/lib/models/sleep.dart +++ b/lib/models/sleep.dart @@ -11,6 +11,8 @@ class Sleep implements DatabaseRecord { Sleep(this._sleepQualityValue, this._comment, this._date, this._sleptAt, this._wokeUpAt); + DateTime get date => _date; + @override factory Sleep.fromDatabase(Map map) { DateTime date = DateTime.parse(map['date']); diff --git a/lib/models/workout.dart b/lib/models/workout.dart index 671dfc8..187b7f6 100644 --- a/lib/models/workout.dart +++ b/lib/models/workout.dart @@ -7,6 +7,8 @@ class Workout implements DatabaseRecord { Workout(this._motivationBefore, this._motivationAfter, this._workoutDate); + DateTime get date => _workoutDate; + @override factory Workout.fromDatabase(Map map) { return Workout(map['motivationBefore'], map['motivationAfter'], diff --git a/lib/pages/main_page.dart b/lib/pages/main_page.dart index b45b479..ce6a1ff 100644 --- a/lib/pages/main_page.dart +++ b/lib/pages/main_page.dart @@ -1,9 +1,12 @@ import 'package:awesome_dialog/awesome_dialog.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import 'package:smoke_cess_app/providers/tasks_provider.dart'; import 'package:smoke_cess_app/services/pages_service.dart'; import 'package:smoke_cess_app/providers/settings_provider.dart'; +import '../widgets/todo_icon.dart'; + class MyHomePage extends StatefulWidget { const MyHomePage({super.key}); @@ -31,12 +34,12 @@ class MyHomePageState extends State { @override Widget build(BuildContext context) { var settingsModel = context.watch(); - var group = settingsModel.settings?.group; + var tasksModel = context.watch(); _isConfigured = settingsModel.initialized; return Scaffold( appBar: AppBar( title: Text( - '${pages.keys.elementAt(_selectedIndex)} ${_isConfigured ? "Gruppe $group" : ""}')), + '${pages.values.elementAt(_selectedIndex)['title']} ${_isConfigured ? "Gruppe ${settingsModel.settings?.group}" : ""}')), body: Center( child: SingleChildScrollView( child: pages.values.elementAt(_selectedIndex)['page'])), @@ -45,9 +48,10 @@ class MyHomePageState extends State { selectedIndex: _selectedIndex, destinations: pages.keys.map((key) { return NavigationDestination( - icon: pages[key]!['icon'] ?? - const Icon(Icons.disabled_by_default), - label: key); + icon: tasksModel.tasks[key] ?? false + ? MyToDoIcon(pages[key]?['icon']) + : pages[key]!['icon'], + label: pages[key]?['title']); }).toList()), ); } diff --git a/lib/providers/tasks_provider.dart b/lib/providers/tasks_provider.dart new file mode 100644 index 0000000..f7cd673 --- /dev/null +++ b/lib/providers/tasks_provider.dart @@ -0,0 +1,56 @@ +import 'package:flutter/material.dart'; +import 'package:smoke_cess_app/models/sleep.dart'; +import 'package:smoke_cess_app/models/workout.dart'; +import 'package:smoke_cess_app/providers/settings_provider.dart'; +import 'package:smoke_cess_app/services/date_service.dart'; +import 'package:smoke_cess_app/services/pages_service.dart'; +import 'package:timezone/browser.dart'; +import '../globals.dart' as globals; +import '../models/mood.dart'; + +class TasksProvider extends ChangeNotifier { + Map tasks = { + Pages.mood: true, + Pages.sleep: true, + Pages.timer: true, + }; + + TasksProvider(SettingsProvider? settingsProvider) { + initTasks(); + } + + void setTaskDone(Pages taskName) { + tasks[taskName] = false; + notifyListeners(); + } + + void initTasks() async { + TZDateTime? moodToday = await getTodayMood(); + if (moodToday != null) { + List moodList = await globals.databaseService.getMoodRecords(); + if (moodList.isNotEmpty) { + Mood mood = moodList.last; + tasks[Pages.mood] = !isSameDay(moodToday, mood.date); + } + } else { + tasks[Pages.mood] = false; + } + TZDateTime? sleepToday = await getTodaySleep(); + if (sleepToday != null) { + List sleepList = await globals.databaseService.getSleepRecords(); + if (sleepList.isNotEmpty) { + Sleep sleep = sleepList.last; + tasks[Pages.sleep] = !isSameDay(sleepToday, sleep.date); + } + } else { + tasks[Pages.sleep] = false; + } + List workoutList = + await globals.databaseService.getWorkoutRecords(); + if (workoutList.isNotEmpty) { + Workout mood = workoutList.last; + tasks[Pages.timer] = !isSameDay(DateTime.now(), mood.date); + } + notifyListeners(); + } +} diff --git a/lib/services/date_service.dart b/lib/services/date_service.dart index 0fd108e..b5e254f 100644 --- a/lib/services/date_service.dart +++ b/lib/services/date_service.dart @@ -13,6 +13,12 @@ const weekDays = { "Sonntag": 7, }; +bool isSameDay(DateTime? dateA, DateTime? dateB) { + return dateA?.year == dateB?.year && + dateA?.month == dateB?.month && + dateA?.day == dateB?.day; +} + Future> getDatesforMood() async { final List? selectedDays = await getMoodQueryDaysCategories(); final int? selectedHours = await getMoodQueryHours(); @@ -27,6 +33,20 @@ Future> getDatesforSleep() async { return createTZDateTimes(selectedDays, selectedHours, selectedMinutes); } +Future getTodayMood() async { + List moodDates = await getDatesforMood(); + Iterable today = + moodDates.where((element) => isSameDay(element, DateTime.now())); + return today.isNotEmpty ? today.first : null; +} + +Future getTodaySleep() async { + List sleepDates = await getDatesforSleep(); + Iterable today = + sleepDates.where((element) => isSameDay(element, DateTime.now())); + return today.isNotEmpty ? today.first : null; +} + List createTZDateTimes( List? selectedDays, int? selectedHours, int? selectedMinutes) { final List tzDateTimes = []; diff --git a/lib/services/pages_service.dart b/lib/services/pages_service.dart index 8f3ab37..0c19383 100644 --- a/lib/services/pages_service.dart +++ b/lib/services/pages_service.dart @@ -5,25 +5,38 @@ import '../pages/relapse_page.dart'; import '../pages/scanner_page.dart'; import '../pages/sleep_page.dart'; -const pages = { - 'Stimmung': { +enum Pages { + mood, + sleep, + relapse, + timer, + settings, +} + +const Map> pages = { + Pages.mood: { + 'title': 'Stimmung', 'page': MoodPage(), - 'icon': Icon(Icons.mood_outlined, color: Colors.black) + 'icon': Icon(Icons.mood_outlined, color: Colors.black), }, - 'Schlaf': { + Pages.sleep: { + 'title': 'Schlaf', 'page': SleepPage(), - 'icon': Icon(Icons.bedtime_outlined, color: Colors.black) + 'icon': Icon(Icons.bedtime_outlined, color: Colors.black), }, - 'Timer': { + Pages.timer: { + 'title': 'Timer', 'page': IntervalTimerPage(), - 'icon': Icon(Icons.timer_outlined, color: Colors.black) + 'icon': Icon(Icons.timer_outlined, color: Colors.black), }, - 'Rückfall': { + Pages.relapse: { + 'title': 'Rückfall', 'page': RelapsePage(), 'icon': Icon(Icons.smoke_free_outlined, color: Colors.black), }, - 'Scanner': { + Pages.settings: { + 'title': 'Scanner', 'page': ScannerPage(), - 'icon': Icon(Icons.camera_alt_outlined, color: Colors.black) + 'icon': Icon(Icons.camera_alt_outlined, color: Colors.black), }, }; diff --git a/lib/widgets/mood_form.dart b/lib/widgets/mood_form.dart index b461ce7..e43e97d 100644 --- a/lib/widgets/mood_form.dart +++ b/lib/widgets/mood_form.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import 'package:smoke_cess_app/providers/tasks_provider.dart'; +import 'package:smoke_cess_app/services/pages_service.dart'; import 'package:smoke_cess_app/widgets/slider.dart'; import 'package:smoke_cess_app/widgets/submit_form_button.dart'; import 'package:smoke_cess_app/widgets/text_formfield.dart'; @@ -13,6 +15,7 @@ class MoodForm extends StatelessWidget { @override Widget build(BuildContext context) { var inputModel = context.watch(); + var tasksModel = context.watch(); return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ @@ -30,6 +33,7 @@ class MoodForm extends StatelessWidget { ), SubmitFormButton( submitCallback: inputModel.saveMood, + updateTasks: () => tasksModel.setTaskDone(Pages.mood), ) ], ); diff --git a/lib/widgets/relapse_form.dart b/lib/widgets/relapse_form.dart index 88e555c..9451277 100644 --- a/lib/widgets/relapse_form.dart +++ b/lib/widgets/relapse_form.dart @@ -1,11 +1,12 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import 'package:smoke_cess_app/providers/tasks_provider.dart'; import 'package:smoke_cess_app/widgets/drop_down.dart'; import 'package:smoke_cess_app/widgets/submit_form_button.dart'; import 'package:smoke_cess_app/widgets/text_formfield.dart'; - import '../providers/input_provider.dart'; import '../providers/settings_provider.dart'; +import '../services/pages_service.dart'; import 'elevated_card.dart'; class RelapseForm extends StatelessWidget { @@ -15,6 +16,7 @@ class RelapseForm extends StatelessWidget { Widget build(BuildContext context) { var inputModel = context.watch(); var settingsModel = context.watch(); + var tasksModel = context.watch(); return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ @@ -29,7 +31,10 @@ class RelapseForm extends StatelessWidget { const SizedBox( height: 80, ), - SubmitFormButton(submitCallback: inputModel.saveRelapse) + SubmitFormButton( + submitCallback: inputModel.saveRelapse, + updateTasks: () => tasksModel.setTaskDone(Pages.mood), + ) ], ); } diff --git a/lib/widgets/sleep_form.dart b/lib/widgets/sleep_form.dart index 8b4389c..ad033ba 100644 --- a/lib/widgets/sleep_form.dart +++ b/lib/widgets/sleep_form.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import 'package:smoke_cess_app/providers/tasks_provider.dart'; import 'package:smoke_cess_app/widgets/elevated_card.dart'; import 'package:smoke_cess_app/widgets/slider.dart'; import 'package:smoke_cess_app/widgets/submit_form_button.dart'; @@ -7,6 +8,7 @@ import 'package:smoke_cess_app/widgets/text_formfield.dart'; import 'package:smoke_cess_app/widgets/timepicker.dart'; import '../providers/input_provider.dart'; +import '../services/pages_service.dart'; class SleepForm extends StatelessWidget { const SleepForm({Key? key}) : super(key: key); @@ -14,6 +16,7 @@ class SleepForm extends StatelessWidget { @override Widget build(BuildContext context) { InputProvider inputModel = context.watch(); + TasksProvider tasksModel = context.watch(); String wokeUpKey = 'wokeUpAt'; String sleptKey = 'sleptAt'; @@ -44,6 +47,7 @@ class SleepForm extends StatelessWidget { ), SubmitFormButton( submitCallback: () => inputModel.saveSleep(wokeUpKey, sleptKey), + updateTasks: () => tasksModel.setTaskDone(Pages.mood), ) ], ); diff --git a/lib/widgets/submit_form_button.dart b/lib/widgets/submit_form_button.dart index 3242b18..b5bb593 100644 --- a/lib/widgets/submit_form_button.dart +++ b/lib/widgets/submit_form_button.dart @@ -3,7 +3,9 @@ import 'package:flutter/material.dart'; class SubmitFormButton extends StatelessWidget { final Future Function() submitCallback; - const SubmitFormButton({super.key, required this.submitCallback}); + final void Function() updateTasks; + const SubmitFormButton( + {super.key, required this.submitCallback, required this.updateTasks}); @override Widget build(BuildContext context) { @@ -12,19 +14,22 @@ class SubmitFormButton extends StatelessWidget { child: ElevatedButton( onPressed: () async { int success = await submitCallback(); - success != 0 - ? AwesomeDialog( - context: context, - dialogType: DialogType.success, - title: 'Gespeichert', - desc: 'Der Eintrag wurde erfolgreich gespeichert', - ).show() - : AwesomeDialog( - context: context, - dialogType: DialogType.error, - title: 'Fehler', - desc: 'Der Eintrag konnte nicht gespeichert werden', - ).show(); + if (success != 0) { + AwesomeDialog( + context: context, + dialogType: DialogType.success, + title: 'Gespeichert', + desc: 'Der Eintrag wurde erfolgreich gespeichert', + ).show(); + updateTasks(); + } else { + AwesomeDialog( + context: context, + dialogType: DialogType.error, + title: 'Fehler', + desc: 'Der Eintrag konnte nicht gespeichert werden', + ).show(); + } }, child: const Text('Speichern'), ), diff --git a/lib/widgets/todo_icon.dart b/lib/widgets/todo_icon.dart new file mode 100644 index 0000000..e5c82b6 --- /dev/null +++ b/lib/widgets/todo_icon.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; + +class MyToDoIcon extends StatelessWidget { + final Icon _icon; + const MyToDoIcon(this._icon, {super.key}); + + @override + Widget build(BuildContext context) { + return Stack(children: [ + _icon, + const Positioned( + // draw a red marble + top: 0.0, + right: 0.0, + child: Icon(Icons.brightness_1, size: 10.0, color: Colors.redAccent), + ) + ]); + } +} diff --git a/lib/widgets/workout_timer_widget.dart b/lib/widgets/workout_timer_widget.dart index 59f67d4..a85a09e 100644 --- a/lib/widgets/workout_timer_widget.dart +++ b/lib/widgets/workout_timer_widget.dart @@ -2,7 +2,9 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import 'package:smoke_cess_app/providers/tasks_provider.dart'; import 'package:smoke_cess_app/providers/workout_provider.dart'; +import 'package:smoke_cess_app/services/pages_service.dart'; import 'package:smoke_cess_app/widgets/timer_widget.dart'; import '../providers/timer_provider.dart'; @@ -15,6 +17,7 @@ class WorkoutTimerWidget extends StatelessWidget { Widget build(BuildContext context) { TimerProvider timerProvider = context.watch(); WorkoutProvider workoutProvider = context.watch(); + TasksProvider tasksProvider = context.read(); if (workoutProvider.isPhaseComplete && !workoutProvider.isWorkoutComplete) { Timer(const Duration(milliseconds: 1), () => workoutProvider.nextPhase()); @@ -26,6 +29,7 @@ class WorkoutTimerWidget extends StatelessWidget { () => showMotivationPopup(context, (double value) { workoutProvider.motivationAfter = value.toInt(); workoutProvider.saveWorkout(); + tasksProvider.setTaskDone(Pages.timer); }, 'Motivation nach dem Training')); }