Merge branch '34-taskprovider-hinzufugen' into 'main'

Resolve "TaskProvider hinzufügen"

Closes #34

See merge request Crondung/hsma_cpd!24
main
Julian Gegner 2023-03-02 19:38:45 +00:00
commit afd5e37ecf
15 changed files with 182 additions and 34 deletions

View File

@ -7,7 +7,7 @@
}, },
"relapse_categories": ["App stresst mich", "langeweile", "lunge braucht es"], "relapse_categories": ["App stresst mich", "langeweile", "lunge braucht es"],
"mood_query": { "mood_query": {
"days": ["Montag", "Freitag"], "days": ["Montag", "Donnerstag"],
"hours": 10, "hours": 10,
"minutes": 30 "minutes": 30
}, },

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:smoke_cess_app/pages/main_page.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:smoke_cess_app/services/notification_service.dart';
import 'package:timezone/data/latest.dart' as tz; import 'package:timezone/data/latest.dart' as tz;
import 'globals.dart' as globals; import 'globals.dart' as globals;
@ -25,8 +26,15 @@ class MyApp extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp( return MaterialApp(
title: _title, title: _title,
home: ChangeNotifierProvider( home: MultiProvider(
create: (context) => SettingsProvider(), providers: [
ChangeNotifierProvider(create: (context) => SettingsProvider()),
ChangeNotifierProxyProvider<SettingsProvider, TasksProvider>(
create: (context) => TasksProvider(null),
update: (context, value, TasksProvider? previous) =>
TasksProvider(value),
),
],
child: const MyHomePage(), child: const MyHomePage(),
)); ));
} }

View File

@ -7,6 +7,8 @@ class Mood implements DatabaseRecord {
Mood(this._moodValue, this._comment, this._date); Mood(this._moodValue, this._comment, this._date);
DateTime get date => _date;
@override @override
factory Mood.fromDatabase(Map<String, dynamic> map) { factory Mood.fromDatabase(Map<String, dynamic> map) {
DateTime date = DateTime.parse(map['date']); DateTime date = DateTime.parse(map['date']);

View File

@ -11,6 +11,8 @@ class Sleep implements DatabaseRecord {
Sleep(this._sleepQualityValue, this._comment, this._date, this._sleptAt, Sleep(this._sleepQualityValue, this._comment, this._date, this._sleptAt,
this._wokeUpAt); this._wokeUpAt);
DateTime get date => _date;
@override @override
factory Sleep.fromDatabase(Map<String, dynamic> map) { factory Sleep.fromDatabase(Map<String, dynamic> map) {
DateTime date = DateTime.parse(map['date']); DateTime date = DateTime.parse(map['date']);

View File

@ -7,6 +7,8 @@ class Workout implements DatabaseRecord {
Workout(this._motivationBefore, this._motivationAfter, this._workoutDate); Workout(this._motivationBefore, this._motivationAfter, this._workoutDate);
DateTime get date => _workoutDate;
@override @override
factory Workout.fromDatabase(Map<String, dynamic> map) { factory Workout.fromDatabase(Map<String, dynamic> map) {
return Workout(map['motivationBefore'], map['motivationAfter'], return Workout(map['motivationBefore'], map['motivationAfter'],

View File

@ -1,9 +1,12 @@
import 'package:awesome_dialog/awesome_dialog.dart'; import 'package:awesome_dialog/awesome_dialog.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.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/services/pages_service.dart';
import 'package:smoke_cess_app/providers/settings_provider.dart'; import 'package:smoke_cess_app/providers/settings_provider.dart';
import '../widgets/todo_icon.dart';
class MyHomePage extends StatefulWidget { class MyHomePage extends StatefulWidget {
const MyHomePage({super.key}); const MyHomePage({super.key});
@ -31,12 +34,12 @@ class MyHomePageState extends State<MyHomePage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var settingsModel = context.watch<SettingsProvider>(); var settingsModel = context.watch<SettingsProvider>();
var group = settingsModel.settings?.group; var tasksModel = context.watch<TasksProvider>();
_isConfigured = settingsModel.initialized; _isConfigured = settingsModel.initialized;
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text( title: Text(
'${pages.keys.elementAt(_selectedIndex)} ${_isConfigured ? "Gruppe $group" : ""}')), '${pages.values.elementAt(_selectedIndex)['title']} ${_isConfigured ? "Gruppe ${settingsModel.settings?.group}" : ""}')),
body: Center( body: Center(
child: SingleChildScrollView( child: SingleChildScrollView(
child: pages.values.elementAt(_selectedIndex)['page'])), child: pages.values.elementAt(_selectedIndex)['page'])),
@ -45,9 +48,10 @@ class MyHomePageState extends State<MyHomePage> {
selectedIndex: _selectedIndex, selectedIndex: _selectedIndex,
destinations: pages.keys.map((key) { destinations: pages.keys.map((key) {
return NavigationDestination( return NavigationDestination(
icon: pages[key]!['icon'] ?? icon: tasksModel.tasks[key] ?? false
const Icon(Icons.disabled_by_default), ? MyToDoIcon(pages[key]?['icon'])
label: key); : pages[key]!['icon'],
label: pages[key]?['title']);
}).toList()), }).toList()),
); );
} }

View File

@ -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<Pages, bool> 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<Mood> 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<Sleep> 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<Workout> workoutList =
await globals.databaseService.getWorkoutRecords();
if (workoutList.isNotEmpty) {
Workout mood = workoutList.last;
tasks[Pages.timer] = !isSameDay(DateTime.now(), mood.date);
}
notifyListeners();
}
}

View File

@ -13,6 +13,12 @@ const weekDays = {
"Sonntag": 7, "Sonntag": 7,
}; };
bool isSameDay(DateTime? dateA, DateTime? dateB) {
return dateA?.year == dateB?.year &&
dateA?.month == dateB?.month &&
dateA?.day == dateB?.day;
}
Future<List<TZDateTime>> getDatesforMood() async { Future<List<TZDateTime>> getDatesforMood() async {
final List<String>? selectedDays = await getMoodQueryDaysCategories(); final List<String>? selectedDays = await getMoodQueryDaysCategories();
final int? selectedHours = await getMoodQueryHours(); final int? selectedHours = await getMoodQueryHours();
@ -27,6 +33,20 @@ Future<List<TZDateTime>> getDatesforSleep() async {
return createTZDateTimes(selectedDays, selectedHours, selectedMinutes); return createTZDateTimes(selectedDays, selectedHours, selectedMinutes);
} }
Future<TZDateTime?> getTodayMood() async {
List<TZDateTime> moodDates = await getDatesforMood();
Iterable<TZDateTime> today =
moodDates.where((element) => isSameDay(element, DateTime.now()));
return today.isNotEmpty ? today.first : null;
}
Future<TZDateTime?> getTodaySleep() async {
List<TZDateTime> sleepDates = await getDatesforSleep();
Iterable<TZDateTime> today =
sleepDates.where((element) => isSameDay(element, DateTime.now()));
return today.isNotEmpty ? today.first : null;
}
List<TZDateTime> createTZDateTimes( List<TZDateTime> createTZDateTimes(
List<String>? selectedDays, int? selectedHours, int? selectedMinutes) { List<String>? selectedDays, int? selectedHours, int? selectedMinutes) {
final List<TZDateTime> tzDateTimes = []; final List<TZDateTime> tzDateTimes = [];

View File

@ -5,25 +5,38 @@ import '../pages/relapse_page.dart';
import '../pages/scanner_page.dart'; import '../pages/scanner_page.dart';
import '../pages/sleep_page.dart'; import '../pages/sleep_page.dart';
const pages = { enum Pages {
'Stimmung': { mood,
sleep,
relapse,
timer,
settings,
}
const Map<Pages, Map<String, dynamic>> pages = {
Pages.mood: {
'title': 'Stimmung',
'page': MoodPage(), '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(), '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(), '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(), 'page': RelapsePage(),
'icon': Icon(Icons.smoke_free_outlined, color: Colors.black), 'icon': Icon(Icons.smoke_free_outlined, color: Colors.black),
}, },
'Scanner': { Pages.settings: {
'title': 'Scanner',
'page': ScannerPage(), 'page': ScannerPage(),
'icon': Icon(Icons.camera_alt_outlined, color: Colors.black) 'icon': Icon(Icons.camera_alt_outlined, color: Colors.black),
}, },
}; };

View File

@ -1,5 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.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/slider.dart';
import 'package:smoke_cess_app/widgets/submit_form_button.dart'; import 'package:smoke_cess_app/widgets/submit_form_button.dart';
import 'package:smoke_cess_app/widgets/text_formfield.dart'; import 'package:smoke_cess_app/widgets/text_formfield.dart';
@ -13,6 +15,7 @@ class MoodForm extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var inputModel = context.watch<InputProvider>(); var inputModel = context.watch<InputProvider>();
var tasksModel = context.watch<TasksProvider>();
return Column( return Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
@ -30,6 +33,7 @@ class MoodForm extends StatelessWidget {
), ),
SubmitFormButton( SubmitFormButton(
submitCallback: inputModel.saveMood, submitCallback: inputModel.saveMood,
updateTasks: () => tasksModel.setTaskDone(Pages.mood),
) )
], ],
); );

View File

@ -1,11 +1,12 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.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/drop_down.dart';
import 'package:smoke_cess_app/widgets/submit_form_button.dart'; import 'package:smoke_cess_app/widgets/submit_form_button.dart';
import 'package:smoke_cess_app/widgets/text_formfield.dart'; import 'package:smoke_cess_app/widgets/text_formfield.dart';
import '../providers/input_provider.dart'; import '../providers/input_provider.dart';
import '../providers/settings_provider.dart'; import '../providers/settings_provider.dart';
import '../services/pages_service.dart';
import 'elevated_card.dart'; import 'elevated_card.dart';
class RelapseForm extends StatelessWidget { class RelapseForm extends StatelessWidget {
@ -15,6 +16,7 @@ class RelapseForm extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
var inputModel = context.watch<InputProvider>(); var inputModel = context.watch<InputProvider>();
var settingsModel = context.watch<SettingsProvider>(); var settingsModel = context.watch<SettingsProvider>();
var tasksModel = context.watch<TasksProvider>();
return Column( return Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
@ -29,7 +31,10 @@ class RelapseForm extends StatelessWidget {
const SizedBox( const SizedBox(
height: 80, height: 80,
), ),
SubmitFormButton(submitCallback: inputModel.saveRelapse) SubmitFormButton(
submitCallback: inputModel.saveRelapse,
updateTasks: () => tasksModel.setTaskDone(Pages.mood),
)
], ],
); );
} }

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.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/elevated_card.dart';
import 'package:smoke_cess_app/widgets/slider.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/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 'package:smoke_cess_app/widgets/timepicker.dart';
import '../providers/input_provider.dart'; import '../providers/input_provider.dart';
import '../services/pages_service.dart';
class SleepForm extends StatelessWidget { class SleepForm extends StatelessWidget {
const SleepForm({Key? key}) : super(key: key); const SleepForm({Key? key}) : super(key: key);
@ -14,6 +16,7 @@ class SleepForm extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
InputProvider inputModel = context.watch<InputProvider>(); InputProvider inputModel = context.watch<InputProvider>();
TasksProvider tasksModel = context.watch<TasksProvider>();
String wokeUpKey = 'wokeUpAt'; String wokeUpKey = 'wokeUpAt';
String sleptKey = 'sleptAt'; String sleptKey = 'sleptAt';
@ -44,6 +47,7 @@ class SleepForm extends StatelessWidget {
), ),
SubmitFormButton( SubmitFormButton(
submitCallback: () => inputModel.saveSleep(wokeUpKey, sleptKey), submitCallback: () => inputModel.saveSleep(wokeUpKey, sleptKey),
updateTasks: () => tasksModel.setTaskDone(Pages.mood),
) )
], ],
); );

View File

@ -3,7 +3,9 @@ import 'package:flutter/material.dart';
class SubmitFormButton extends StatelessWidget { class SubmitFormButton extends StatelessWidget {
final Future<int> Function() submitCallback; final Future<int> Function() submitCallback;
const SubmitFormButton({super.key, required this.submitCallback}); final void Function() updateTasks;
const SubmitFormButton(
{super.key, required this.submitCallback, required this.updateTasks});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -12,19 +14,22 @@ class SubmitFormButton extends StatelessWidget {
child: ElevatedButton( child: ElevatedButton(
onPressed: () async { onPressed: () async {
int success = await submitCallback(); int success = await submitCallback();
success != 0 if (success != 0) {
? AwesomeDialog( AwesomeDialog(
context: context, context: context,
dialogType: DialogType.success, dialogType: DialogType.success,
title: 'Gespeichert', title: 'Gespeichert',
desc: 'Der Eintrag wurde erfolgreich gespeichert', desc: 'Der Eintrag wurde erfolgreich gespeichert',
).show() ).show();
: AwesomeDialog( updateTasks();
context: context, } else {
dialogType: DialogType.error, AwesomeDialog(
title: 'Fehler', context: context,
desc: 'Der Eintrag konnte nicht gespeichert werden', dialogType: DialogType.error,
).show(); title: 'Fehler',
desc: 'Der Eintrag konnte nicht gespeichert werden',
).show();
}
}, },
child: const Text('Speichern'), child: const Text('Speichern'),
), ),

View File

@ -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: <Widget>[
_icon,
const Positioned(
// draw a red marble
top: 0.0,
right: 0.0,
child: Icon(Icons.brightness_1, size: 10.0, color: Colors.redAccent),
)
]);
}
}

View File

@ -2,7 +2,9 @@ import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.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/providers/workout_provider.dart';
import 'package:smoke_cess_app/services/pages_service.dart';
import 'package:smoke_cess_app/widgets/timer_widget.dart'; import 'package:smoke_cess_app/widgets/timer_widget.dart';
import '../providers/timer_provider.dart'; import '../providers/timer_provider.dart';
@ -15,6 +17,7 @@ class WorkoutTimerWidget extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
TimerProvider timerProvider = context.watch<TimerProvider>(); TimerProvider timerProvider = context.watch<TimerProvider>();
WorkoutProvider workoutProvider = context.watch<WorkoutProvider>(); WorkoutProvider workoutProvider = context.watch<WorkoutProvider>();
TasksProvider tasksProvider = context.read<TasksProvider>();
if (workoutProvider.isPhaseComplete && !workoutProvider.isWorkoutComplete) { if (workoutProvider.isPhaseComplete && !workoutProvider.isWorkoutComplete) {
Timer(const Duration(milliseconds: 1), () => workoutProvider.nextPhase()); Timer(const Duration(milliseconds: 1), () => workoutProvider.nextPhase());
@ -26,6 +29,7 @@ class WorkoutTimerWidget extends StatelessWidget {
() => showMotivationPopup(context, (double value) { () => showMotivationPopup(context, (double value) {
workoutProvider.motivationAfter = value.toInt(); workoutProvider.motivationAfter = value.toInt();
workoutProvider.saveWorkout(); workoutProvider.saveWorkout();
tasksProvider.setTaskDone(Pages.timer);
}, 'Motivation nach dem Training')); }, 'Motivation nach dem Training'));
} }