diff --git a/assets/translations/en-US.json b/assets/translations/en-US.json index c929bf0..72be06a 100644 --- a/assets/translations/en-US.json +++ b/assets/translations/en-US.json @@ -40,6 +40,8 @@ "default_tb_view": "Default View", "view": "View", "grid": "Grid", + "default_subject_duration": "Default Subject Duration", + "choose_duration": "Choose a Duration", "delete_timetable_dialog": "Deleting a timetable will delete its corresponding subjects.", "reset_timetables_dialog": "Resetting timetables will also delete all subjects.", diff --git a/assets/translations/fr-FR.json b/assets/translations/fr-FR.json index a2c4002..bd72e52 100644 --- a/assets/translations/fr-FR.json +++ b/assets/translations/fr-FR.json @@ -40,6 +40,8 @@ "default_tb_view": "Vue par défaut", "view": "Vue", "grid": "Grille", + "default_subject_duration": "Durée par défaut d'un matière", + "choose_duration": "Choisir une Durée", "delete_timetable_dialog": "La suppression d'un tableau supprimera ses sujets correspondants.", "reset_timetables_dialog": "La réinitialisation des emplois de temps supprimera toutes les matières aussi.", diff --git a/lib/components/settings/general.dart b/lib/components/settings/general.dart index 4dfb849..d1b900f 100644 --- a/lib/components/settings/general.dart +++ b/lib/components/settings/general.dart @@ -80,13 +80,9 @@ class GeneralOptions extends ConsumerWidget { size: 20, ), horizontalTitleGap: 8, - title: Row( - children: [ - const Text("app_color").tr(), - const Spacer(), + title: const Text("app_color").tr(), + trailing: ColorIndicator(color: appThemeColor, monetTheming: monetTheming), - ], - ), onTap: () { Navigator.push( context, diff --git a/lib/components/settings/timetable_features.dart b/lib/components/settings/timetable_features.dart index 8f97548..e4d7a87 100644 --- a/lib/components/settings/timetable_features.dart +++ b/lib/components/settings/timetable_features.dart @@ -1,12 +1,14 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:timetable/components/settings/screens/timetable_management.dart'; +import 'package:timetable/components/widgets/bottom_sheets/subject_duration_modal_bottom_sheet.dart'; import 'package:timetable/provider/settings.dart'; import 'package:timetable/components/settings/screens/timetable_period.dart'; /// All the settings for changing some timetable features. -class TimetableFeaturesOptions extends ConsumerWidget { +class TimetableFeaturesOptions extends HookConsumerWidget { const TimetableFeaturesOptions({super.key}); @override @@ -14,6 +16,9 @@ class TimetableFeaturesOptions extends ConsumerWidget { final settings = ref.read(settingsProvider.notifier); final rotationWeeks = ref.watch(settingsProvider).rotationWeeks; final autoCompleteColor = ref.watch(settingsProvider).autoCompleteColor; + final defaultSubjectDuration = + ref.watch(settingsProvider).defaultSubjectDuration; + final duration = useState(defaultSubjectDuration); return Column( children: [ @@ -49,6 +54,34 @@ class TimetableFeaturesOptions extends ConsumerWidget { }, title: const Text("manage_timetables").tr(), ), + ListTile( + leading: const Icon( + Icons.timer_outlined, + size: 20, + ), + horizontalTitleGap: 8, + title: const Text("default_subject_duration").tr(), + trailing: Text("${defaultSubjectDuration.inMinutes}min"), + onTap: () { + showModalBottomSheet( + showDragHandle: true, + enableDrag: true, + isDismissible: true, + context: context, + builder: (context) { + return Wrap( + children: [ + SubjectDurationModalBottomSheet( + duration: duration, + ), + ], + ); + }, + ).then( + (_) => settings.updateDefaultSubjectDuration(duration.value), + ); + }, + ), SwitchListTile( secondary: const Icon( Icons.rotate_90_degrees_ccw_outlined, diff --git a/lib/components/subject_management/subject_screen.dart b/lib/components/subject_management/subject_screen.dart index 0aa1d33..7904f50 100644 --- a/lib/components/subject_management/subject_screen.dart +++ b/lib/components/subject_management/subject_screen.dart @@ -41,6 +41,8 @@ class SubjectScreen extends HookConsumerWidget { final tfHours = ref.watch(settingsProvider).twentyFourHours; final customStartTimeHour = ref.watch(settingsProvider).customStartTime.hour; + final defaultSubjectDuration = + ref.watch(settingsProvider).defaultSubjectDuration; final bool isSubjectNull = (subject == null); final bool isCTimetableNull = (currentTimetable == null); @@ -65,7 +67,9 @@ class SubjectScreen extends HookConsumerWidget { final ValueNotifier endTime = useState( TimeOfDay( hour: subject?.endTime.hour ?? - (rowIndex! + (tfHours ? 0 : customStartTimeHour) + 1), + (rowIndex! + + (tfHours ? 0 : customStartTimeHour) + + defaultSubjectDuration.inHours), minute: 0, ), ); diff --git a/lib/components/widgets/bottom_sheets/subject_duration_modal_bottom_sheet.dart b/lib/components/widgets/bottom_sheets/subject_duration_modal_bottom_sheet.dart new file mode 100644 index 0000000..5d25d0e --- /dev/null +++ b/lib/components/widgets/bottom_sheets/subject_duration_modal_bottom_sheet.dart @@ -0,0 +1,51 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:timetable/components/widgets/bottom_sheets/subject_data_bottom_sheet.dart'; +import 'package:timetable/constants/durations.dart'; + +/// Bottom Sheet Modal Widget used to select the default subject duration. +class SubjectDurationModalBottomSheet extends StatelessWidget { + final ValueNotifier duration; + + const SubjectDurationModalBottomSheet({ + super.key, + required this.duration, + }); + + @override + Widget build(BuildContext context) { + return SubjectDataBottomSheet( + title: "choose_duration".tr(), + children: List.generate( + durations.length, + (i) { + final d = durations[i]; + bool isSelected = (d == duration.value); + + return ListTile( + title: Row( + children: [ + // writing minutes directly here will only work for now lol + Text( + "${durations[i].inMinutes} minutes", + ).tr(), + const Spacer(), + Visibility( + visible: isSelected, + child: const Icon( + Icons.check, + ), + ), + ], + ), + visualDensity: VisualDensity.compact, + onTap: () { + duration.value = d; + Navigator.pop(context); + }, + ); + }, + ), + ); + } +} diff --git a/lib/constants/durations.dart b/lib/constants/durations.dart new file mode 100644 index 0000000..e835b47 --- /dev/null +++ b/lib/constants/durations.dart @@ -0,0 +1,6 @@ +const List durations = [ + Duration(minutes: 60), + Duration(minutes: 120), + Duration(minutes: 180), + Duration(minutes: 240), +]; diff --git a/lib/db/models/settings.dart b/lib/db/models/settings.dart index 83e2cc4..acdd19b 100644 --- a/lib/db/models/settings.dart +++ b/lib/db/models/settings.dart @@ -19,6 +19,7 @@ class Settings { final bool twentyFourHours; final TbViews defaultTimetableView; final Color appThemeColor; + final Duration defaultSubjectDuration; // settings defaults Settings({ @@ -38,6 +39,7 @@ class Settings { this.twentyFourHours = false, this.defaultTimetableView = TbViews.grid, this.appThemeColor = Colors.deepPurple, + this.defaultSubjectDuration = const Duration(minutes: 60), }); Settings copyWith({ @@ -57,6 +59,7 @@ class Settings { bool? twentyFourHours, TbViews? defaultTimetableView, Color? appThemeColor, + Duration? defaultSubjectDuration, }) => Settings( customTimePeriod: customTimePeriod ?? this.customTimePeriod, @@ -76,6 +79,8 @@ class Settings { twentyFourHours: twentyFourHours ?? this.twentyFourHours, defaultTimetableView: defaultTimetableView ?? this.defaultTimetableView, appThemeColor: appThemeColor ?? this.appThemeColor, + defaultSubjectDuration: + defaultSubjectDuration ?? this.defaultSubjectDuration, ); Map toJson() => { @@ -97,6 +102,7 @@ class Settings { 'twentyFourHours': twentyFourHours, 'defaultTimetableView': defaultTimetableView.name, 'appThemeColorValue': appThemeColor.value, + 'defaultSubjectDuration': defaultSubjectDuration.inMinutes, }; factory Settings.fromJson(Map json) { @@ -137,6 +143,9 @@ class Settings { appThemeColor: json['appThemeColorValue'] != null ? Color(json['appThemeColorValue'] as int) : null, + defaultSubjectDuration: json['defaultSubjectDuration'] != null + ? Duration(minutes: json['defaultSubjectDuration'] as int) + : null, ); } } diff --git a/lib/provider/settings.dart b/lib/provider/settings.dart index 356497c..ca60e9c 100644 --- a/lib/provider/settings.dart +++ b/lib/provider/settings.dart @@ -11,6 +11,14 @@ class SettingsNotifier extends StateNotifier { loadSettings(); } + void updateDefaultSubjectDuration(Duration defaultSubjectDuration) { + final newState = state.copyWith( + defaultSubjectDuration: defaultSubjectDuration, + ); + state = newState; + saveSettings(); + } + void updateAppThemeColor(Color appThemeColor) { final newState = state.copyWith( appThemeColor: appThemeColor,