diff --git a/lib/core/presentation/widgets/copy_dialog.dart b/lib/core/presentation/widgets/copy_dialog.dart new file mode 100644 index 00000000..d450a149 --- /dev/null +++ b/lib/core/presentation/widgets/copy_dialog.dart @@ -0,0 +1,55 @@ +import 'package:flutter/material.dart'; +import 'package:opennutritracker/features/add_meal/presentation/add_meal_type.dart'; +import 'package:opennutritracker/generated/l10n.dart'; + +class CopyDialog extends StatefulWidget { + const CopyDialog({super.key}); + @override + State createState() { + return CopyDialogState(); + } +} + +class CopyDialogState extends State { + AddMealType _selectedValue = AddMealType.breakfastType; + AddMealType get selectedMealType => _selectedValue; + + @override + void initState() { + super.initState(); + } + + @override + Widget build( + BuildContext context, + ) { + return AlertDialog( + title: Text(S.of(context).copyDialogTitle), + content: DropdownButton( + value: _selectedValue, + onChanged: (AddMealType? addMealType) { + if (addMealType != null) { + setState(() { + _selectedValue = addMealType; + }); + } + }, + items: AddMealType.values.map((AddMealType addMealType) { + return DropdownMenuItem( + value: addMealType, + child: Text(addMealType.getTypeName(context))); + }).toList()), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(_selectedValue); + }, + child: Text(S.of(context).dialogOKLabel)), + TextButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: Text(S.of(context).dialogCancelLabel)) + ]); + } +} diff --git a/lib/core/presentation/widgets/copy_or_delete_dialog.dart b/lib/core/presentation/widgets/copy_or_delete_dialog.dart new file mode 100644 index 00000000..bdfda477 --- /dev/null +++ b/lib/core/presentation/widgets/copy_or_delete_dialog.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; +import 'package:opennutritracker/generated/l10n.dart'; + +class CopyOrDeleteDialog extends StatelessWidget { + const CopyOrDeleteDialog({super.key}); + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text(S.of(context).copyOrDeleteTimeDialogTitle), + content: Text(S.of(context).copyOrDeleteTimeDialogContent), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(true); + }, + child: Text(S.of(context).dialogCopyLabel)), + TextButton( + onPressed: () { + Navigator.of(context).pop(false); + }, + child: Text(S.of(context).dialogDeleteLabel)) + ], + ); + } +} diff --git a/lib/features/diary/diary_page.dart b/lib/features/diary/diary_page.dart index fde9c008..0fb66fa7 100644 --- a/lib/features/diary/diary_page.dart +++ b/lib/features/diary/diary_page.dart @@ -5,12 +5,16 @@ import 'package:opennutritracker/core/domain/entity/intake_entity.dart'; import 'package:opennutritracker/core/domain/entity/tracked_day_entity.dart'; import 'package:opennutritracker/core/domain/entity/user_activity_entity.dart'; import 'package:opennutritracker/core/utils/locator.dart'; +import 'package:opennutritracker/features/add_meal/presentation/add_meal_type.dart'; import 'package:opennutritracker/features/diary/presentation/bloc/calendar_day_bloc.dart'; import 'package:opennutritracker/features/diary/presentation/bloc/diary_bloc.dart'; import 'package:opennutritracker/features/diary/presentation/widgets/diary_table_calendar.dart'; import 'package:opennutritracker/features/diary/presentation/widgets/day_info_widget.dart'; +import 'package:opennutritracker/features/meal_detail/presentation/bloc/meal_detail_bloc.dart'; import 'package:opennutritracker/generated/l10n.dart'; +import '../../core/domain/entity/intake_type_entity.dart'; + class DiaryPage extends StatefulWidget { const DiaryPage({super.key}); @@ -23,6 +27,7 @@ class _DiaryPageState extends State with WidgetsBindingObserver { late DiaryBloc _diaryBloc; late CalendarDayBloc _calendarDayBloc; + late MealDetailBloc _mealDetailBloc; static const _calendarDurationDays = Duration(days: 356); final _currentDate = DateTime.now(); @@ -34,6 +39,7 @@ class _DiaryPageState extends State with WidgetsBindingObserver { WidgetsBinding.instance.addObserver(this); _diaryBloc = locator(); _calendarDayBloc = locator(); + _mealDetailBloc = locator(); super.initState(); } @@ -103,6 +109,8 @@ class _DiaryPageState extends State with WidgetsBindingObserver { snackIntake: state.snackIntakeList, onDeleteIntake: _onDeleteIntakeItem, onDeleteActivity: _onDeleteActivityItem, + onCopyIntake: _onCopyIntakeItem, + onCopyActivity: _onCopyActivityItem, ); } return const SizedBox(); @@ -138,6 +146,29 @@ class _DiaryPageState extends State with WidgetsBindingObserver { } } + void _onCopyIntakeItem(IntakeEntity intakeEntity, + TrackedDayEntity? trackedDayEntity, AddMealType? type) async { + IntakeTypeEntity finalType; + if (type == null) { + finalType = intakeEntity.type; + } else { + finalType = type.getIntakeType(); + } + _mealDetailBloc.addIntake( + context, + intakeEntity.unit, + intakeEntity.amount.toString(), + finalType, + intakeEntity.meal, + DateTime.now()); + _diaryBloc.updateHomePage(); + } + + void _onCopyActivityItem(UserActivityEntity userActivityEntity, + TrackedDayEntity? trackedDayEntity) async { + log.info("Should copy activity"); + } + void _onDateSelected( DateTime newDate, Map trackedDaysMap) { setState(() { diff --git a/lib/features/diary/presentation/widgets/day_info_widget.dart b/lib/features/diary/presentation/widgets/day_info_widget.dart index 851c818d..05779c0c 100644 --- a/lib/features/diary/presentation/widgets/day_info_widget.dart +++ b/lib/features/diary/presentation/widgets/day_info_widget.dart @@ -4,12 +4,15 @@ import 'package:opennutritracker/core/domain/entity/intake_entity.dart'; import 'package:opennutritracker/core/domain/entity/tracked_day_entity.dart'; import 'package:opennutritracker/core/domain/entity/user_activity_entity.dart'; import 'package:opennutritracker/core/presentation/widgets/activity_vertial_list.dart'; +import 'package:opennutritracker/core/presentation/widgets/copy_dialog.dart'; import 'package:opennutritracker/core/presentation/widgets/delete_dialog.dart'; import 'package:opennutritracker/core/utils/custom_icons.dart'; import 'package:opennutritracker/features/add_meal/presentation/add_meal_type.dart'; import 'package:opennutritracker/features/home/presentation/widgets/intake_vertical_list.dart'; import 'package:opennutritracker/generated/l10n.dart'; +import '../../../../core/presentation/widgets/copy_or_delete_dialog.dart'; + class DayInfoWidget extends StatelessWidget { final DateTime selectedDay; final TrackedDayEntity? trackedDayEntity; @@ -22,18 +25,25 @@ class DayInfoWidget extends StatelessWidget { onDeleteIntake; final Function(UserActivityEntity userActivityEntity, TrackedDayEntity? trackedDayEntity) onDeleteActivity; + final Function(IntakeEntity intake, TrackedDayEntity? trackedDayEntity, + AddMealType? type) onCopyIntake; + final Function(UserActivityEntity userActivityEntity, + TrackedDayEntity? trackedDayEntity) onCopyActivity; - const DayInfoWidget( - {super.key, - required this.selectedDay, - required this.trackedDayEntity, - required this.userActivities, - required this.breakfastIntake, - required this.lunchIntake, - required this.dinnerIntake, - required this.snackIntake, - required this.onDeleteIntake, - required this.onDeleteActivity}); + const DayInfoWidget({ + super.key, + required this.selectedDay, + required this.trackedDayEntity, + required this.userActivities, + required this.breakfastIntake, + required this.lunchIntake, + required this.dinnerIntake, + required this.snackIntake, + required this.onDeleteIntake, + required this.onDeleteActivity, + required this.onCopyIntake, + required this.onCopyActivity, + }); @override Widget build(BuildContext context) { @@ -171,16 +181,46 @@ class DayInfoWidget extends StatelessWidget { return 'Carbs: $carbsTracked/${carbsGoal}g, Fat: $fatTracked/${fatGoal}g, Protein: $proteinTracked/${proteinGoal}g'; } - void onIntakeItemLongPressed( + void showCopyOrDeleteIntakeDialog( + BuildContext context, IntakeEntity intakeEntity) async { + final copyOrDelete = await showDialog( + context: context, builder: (context) => const CopyOrDeleteDialog()); + if (context.mounted) { + if (copyOrDelete != null && !copyOrDelete) { + showDeleteIntakeDialog(context, intakeEntity); + } else if (copyOrDelete != null && copyOrDelete) { + showCopyDialog(context, intakeEntity); + } + } + } + + void showCopyDialog(BuildContext context, IntakeEntity intakeEntity) async { + const copyDialog = CopyDialog(); + final selectedMealType = await showDialog( + context: context, builder: (context) => copyDialog); + if (selectedMealType != null) { + onCopyIntake(intakeEntity, null, selectedMealType); + } + } + + void showDeleteIntakeDialog( BuildContext context, IntakeEntity intakeEntity) async { final shouldDeleteIntake = await showDialog( context: context, builder: (context) => const DeleteDialog()); - if (shouldDeleteIntake != null) { onDeleteIntake(intakeEntity, trackedDayEntity); } } + void onIntakeItemLongPressed( + BuildContext context, IntakeEntity intakeEntity) async { + if (DateUtils.isSameDay(selectedDay, DateTime.now())) { + showDeleteIntakeDialog(context, intakeEntity); + } else { + showCopyOrDeleteIntakeDialog(context, intakeEntity); + } + } + void onActivityItemLongPressed( BuildContext context, UserActivityEntity activityEntity) async { final shouldDeleteActivity = await showDialog( diff --git a/lib/generated/intl/messages_de.dart b/lib/generated/intl/messages_de.dart index 73f21113..f553d2be 100644 --- a/lib/generated/intl/messages_de.dart +++ b/lib/generated/intl/messages_de.dart @@ -84,6 +84,12 @@ class MessageLookup extends MessageLookupByLibrary { "chooseWeightGoalLabel": MessageLookupByLibrary.simpleMessage("Gewichtsziel wählen"), "cmLabel": MessageLookupByLibrary.simpleMessage("cm"), + "copyDialogTitle": MessageLookupByLibrary.simpleMessage( + "Zu welcher Mahlzeit hinzufügen?"), + "copyOrDeleteTimeDialogContent": MessageLookupByLibrary.simpleMessage( + "Auf \"Nach heute kopieren\" klicken, um die Mahlzeit nach heute zu kopieren. Mit \"Löschen\" kann die Mahlzeit entfernt werden"), + "copyOrDeleteTimeDialogTitle": + MessageLookupByLibrary.simpleMessage("Was soll getan werden?"), "createCustomDialogContent": MessageLookupByLibrary.simpleMessage( "Möchten Sie einen benutzerdefinierte Mahlzeit erstellen?"), "createCustomDialogTitle": MessageLookupByLibrary.simpleMessage( @@ -95,6 +101,9 @@ class MessageLookup extends MessageLookupByLibrary { "deleteTimeDialogTitle": MessageLookupByLibrary.simpleMessage("Eintrag löschen?"), "dialogCancelLabel": MessageLookupByLibrary.simpleMessage("ABBRECHEN"), + "dialogCopyLabel": + MessageLookupByLibrary.simpleMessage("NACH HEUTE KOPIEREN"), + "dialogDeleteLabel": MessageLookupByLibrary.simpleMessage("LÖSCHEN"), "dialogOKLabel": MessageLookupByLibrary.simpleMessage("OK"), "diaryLabel": MessageLookupByLibrary.simpleMessage("Tagebuch"), "dinnerExample": MessageLookupByLibrary.simpleMessage( diff --git a/lib/generated/intl/messages_en.dart b/lib/generated/intl/messages_en.dart index 6867887e..fc1c3717 100644 --- a/lib/generated/intl/messages_en.dart +++ b/lib/generated/intl/messages_en.dart @@ -84,6 +84,12 @@ class MessageLookup extends MessageLookupByLibrary { "chooseWeightGoalLabel": MessageLookupByLibrary.simpleMessage("Choose Weight Goal"), "cmLabel": MessageLookupByLibrary.simpleMessage("cm"), + "copyDialogTitle": MessageLookupByLibrary.simpleMessage( + "Which meal type do you want to copy to?"), + "copyOrDeleteTimeDialogContent": MessageLookupByLibrary.simpleMessage( + "With \"Copy to today\" you can copy the meal to today. With \"Delete\" you can delete the meal."), + "copyOrDeleteTimeDialogTitle": + MessageLookupByLibrary.simpleMessage("What do you want to do?"), "createCustomDialogContent": MessageLookupByLibrary.simpleMessage( "Do you want create a custom meal item?"), "createCustomDialogTitle": @@ -95,6 +101,9 @@ class MessageLookup extends MessageLookupByLibrary { "deleteTimeDialogTitle": MessageLookupByLibrary.simpleMessage("Delete Item?"), "dialogCancelLabel": MessageLookupByLibrary.simpleMessage("CANCEL"), + "dialogCopyLabel": + MessageLookupByLibrary.simpleMessage("COPY TO TODAY"), + "dialogDeleteLabel": MessageLookupByLibrary.simpleMessage("DELETE"), "dialogOKLabel": MessageLookupByLibrary.simpleMessage("OK"), "diaryLabel": MessageLookupByLibrary.simpleMessage("Diary"), "dinnerExample": MessageLookupByLibrary.simpleMessage( diff --git a/lib/generated/intl/messages_tr.dart b/lib/generated/intl/messages_tr.dart index 9d56d446..c4ffea46 100644 --- a/lib/generated/intl/messages_tr.dart +++ b/lib/generated/intl/messages_tr.dart @@ -82,6 +82,12 @@ class MessageLookup extends MessageLookupByLibrary { "chooseWeightGoalLabel": MessageLookupByLibrary.simpleMessage("Kilo Hedefi Seçin"), "cmLabel": MessageLookupByLibrary.simpleMessage("cm"), + "copyDialogTitle": MessageLookupByLibrary.simpleMessage( + "Hangi öğüne eklemek istiyorsunuz?"), + "copyOrDeleteTimeDialogContent": MessageLookupByLibrary.simpleMessage( + "\"Bugüne kopyala\" seçeneğine tıklayarak öğünü bugüne kopyalayabilirsiniz. \"Sil\" seçeneği ile öğün kaldırılabilir."), + "copyOrDeleteTimeDialogTitle": + MessageLookupByLibrary.simpleMessage("Ne yapılmalı?"), "createCustomDialogContent": MessageLookupByLibrary.simpleMessage( "Özel bir yemek öğesi oluşturmak istiyor musunuz?"), "createCustomDialogTitle": MessageLookupByLibrary.simpleMessage( @@ -93,6 +99,9 @@ class MessageLookup extends MessageLookupByLibrary { "deleteTimeDialogTitle": MessageLookupByLibrary.simpleMessage("Öğe Silinsin mi?"), "dialogCancelLabel": MessageLookupByLibrary.simpleMessage("İPTAL"), + "dialogCopyLabel": + MessageLookupByLibrary.simpleMessage("BUGÜNE KOPYALA"), + "dialogDeleteLabel": MessageLookupByLibrary.simpleMessage("SİL"), "dialogOKLabel": MessageLookupByLibrary.simpleMessage("TAMAM"), "diaryLabel": MessageLookupByLibrary.simpleMessage("Günlük"), "dinnerExample": MessageLookupByLibrary.simpleMessage( diff --git a/lib/generated/l10n.dart b/lib/generated/l10n.dart index 12a0372b..98bae115 100644 --- a/lib/generated/l10n.dart +++ b/lib/generated/l10n.dart @@ -921,6 +921,56 @@ class S { ); } + /// `Which meal type do you want to copy to?` + String get copyDialogTitle { + return Intl.message( + 'Which meal type do you want to copy to?', + name: 'copyDialogTitle', + desc: '', + args: [], + ); + } + + /// `What do you want to do?` + String get copyOrDeleteTimeDialogTitle { + return Intl.message( + 'What do you want to do?', + name: 'copyOrDeleteTimeDialogTitle', + desc: '', + args: [], + ); + } + + /// `With "Copy to today" you can copy the meal to today. With "Delete" you can delete the meal.` + String get copyOrDeleteTimeDialogContent { + return Intl.message( + 'With "Copy to today" you can copy the meal to today. With "Delete" you can delete the meal.', + name: 'copyOrDeleteTimeDialogContent', + desc: '', + args: [], + ); + } + + /// `COPY TO TODAY` + String get dialogCopyLabel { + return Intl.message( + 'COPY TO TODAY', + name: 'dialogCopyLabel', + desc: '', + args: [], + ); + } + + /// `DELETE` + String get dialogDeleteLabel { + return Intl.message( + 'DELETE', + name: 'dialogDeleteLabel', + desc: '', + args: [], + ); + } + /// `supplied` String get suppliedLabel { return Intl.message( diff --git a/lib/l10n/intl_de.arb b/lib/l10n/intl_de.arb index ac1bb059..e4f21bb0 100644 --- a/lib/l10n/intl_de.arb +++ b/lib/l10n/intl_de.arb @@ -94,6 +94,13 @@ "deleteTimeDialogContent": "Möchten Sie den ausgewählten Eintrag löschen?", "itemDeletedSnackbar": "Eintrag gelöscht", + "copyDialogTitle": "Zu welcher Mahlzeit hinzufügen?", + + "copyOrDeleteTimeDialogTitle": "Was soll getan werden?", + "copyOrDeleteTimeDialogContent": "Auf \"Nach heute kopieren\" klicken, um die Mahlzeit nach heute zu kopieren. Mit \"Löschen\" kann die Mahlzeit entfernt werden", + "dialogCopyLabel": "NACH HEUTE KOPIEREN", + "dialogDeleteLabel": "LÖSCHEN", + "suppliedLabel": "zugeführt", "burnedLabel": "verbrannt", "kcalLeftLabel": "kcal übrig", diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 477e40d0..9772fcf9 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -94,6 +94,13 @@ "deleteTimeDialogContent": "Do want to delete the selected item?", "itemDeletedSnackbar": "Item deleted", + "copyDialogTitle": "Which meal type do you want to copy to?", + + "copyOrDeleteTimeDialogTitle": "What do you want to do?", + "copyOrDeleteTimeDialogContent": "With \"Copy to today\" you can copy the meal to today. With \"Delete\" you can delete the meal.", + "dialogCopyLabel": "COPY TO TODAY", + "dialogDeleteLabel": "DELETE", + "suppliedLabel": "supplied", "burnedLabel": "burned", "kcalLeftLabel": "kcal left", diff --git a/lib/l10n/intl_tr.arb b/lib/l10n/intl_tr.arb index b8974358..9f82e441 100644 --- a/lib/l10n/intl_tr.arb +++ b/lib/l10n/intl_tr.arb @@ -94,6 +94,13 @@ "deleteTimeDialogContent": "Seçili öğeyi silmek istiyor musunuz?", "itemDeletedSnackbar": "Öğe silindi", + "copyDialogTitle": "Hangi öğüne eklemek istiyorsunuz?", + + "copyOrDeleteTimeDialogTitle": "Ne yapılmalı?", + "copyOrDeleteTimeDialogContent": "\"Bugüne kopyala\" seçeneğine tıklayarak öğünü bugüne kopyalayabilirsiniz. \"Sil\" seçeneği ile öğün kaldırılabilir.", + "dialogCopyLabel": "BUGÜNE KOPYALA", + "dialogDeleteLabel": "SİL", + "suppliedLabel": "alınan", "burnedLabel": "yakılan", "kcalLeftLabel": "kalan kalori",