From fe93a94fc657665e1d9d9087dbf448d0f98e9503 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20M=C3=BCller?= Date: Wed, 12 Jun 2024 15:53:13 +0200 Subject: [PATCH] bloc for holidays --- .../view/loadable_state_consumer.dart | 3 +- .../utilityWidgets/bloc_module.dart | 9 +- lib/state/app/modules/app_modules.dart | 2 +- .../modules/holidays/bloc/holidays_bloc.dart | 9 +- .../modules/holidays/view/holidays_view.dart | 25 +-- .../pages/more/feedback/feedbackDialog.dart | 7 +- lib/view/pages/more/holidays/holidays.dart | 157 ------------------ lib/widget/string_extensions.dart | 3 + 8 files changed, 37 insertions(+), 178 deletions(-) delete mode 100644 lib/view/pages/more/holidays/holidays.dart create mode 100644 lib/widget/string_extensions.dart diff --git a/lib/state/app/infrastructure/loadableState/view/loadable_state_consumer.dart b/lib/state/app/infrastructure/loadableState/view/loadable_state_consumer.dart index 7d9244e..238ab94 100644 --- a/lib/state/app/infrastructure/loadableState/view/loadable_state_consumer.dart +++ b/lib/state/app/infrastructure/loadableState/view/loadable_state_consumer.dart @@ -15,8 +15,9 @@ import 'loadable_state_primary_loading.dart'; class LoadableStateConsumer, LoadableState>, TState> extends StatelessWidget { final Widget Function(TState state, bool loading) child; + final void Function(TState state)? onLoad; final bool wrapWithScrollView; - const LoadableStateConsumer({required this.child, this.wrapWithScrollView = false, super.key}); + const LoadableStateConsumer({required this.child, this.onLoad, this.wrapWithScrollView = false, super.key}); static Duration animationDuration = const Duration(milliseconds: 200); diff --git a/lib/state/app/infrastructure/utilityWidgets/bloc_module.dart b/lib/state/app/infrastructure/utilityWidgets/bloc_module.dart index 35297b7..fa1bee7 100644 --- a/lib/state/app/infrastructure/utilityWidgets/bloc_module.dart +++ b/lib/state/app/infrastructure/utilityWidgets/bloc_module.dart @@ -5,14 +5,19 @@ class BlocModule, TState> extends St final TBloc Function(BuildContext context) create; final Widget Function(BuildContext context, TBloc bloc, TState state) child; final bool autoRebuild; - const BlocModule({required this.create, required this.child, this.autoRebuild = false, super.key}); + final void Function(BuildContext context, TBloc bloc)? onInitialisation; + const BlocModule({required this.create, required this.child, this.autoRebuild = false, this.onInitialisation, super.key}); Widget rebuildChild(BuildContext context) => child(context, context.watch(), context.watch().state); Widget staticChild(BuildContext context) => child(context, context.read(), context.read().state); @override Widget build(BuildContext context) => BlocProvider( - create: create, + create: (context) { + var bloc = create(context); + this.onInitialisation != null ? this.onInitialisation!(context, bloc) : null; + return bloc; + }, child: Builder( builder: (context) => autoRebuild ? rebuildChild(context) diff --git a/lib/state/app/modules/app_modules.dart b/lib/state/app/modules/app_modules.dart index 5e53b32..df84651 100644 --- a/lib/state/app/modules/app_modules.dart +++ b/lib/state/app/modules/app_modules.dart @@ -26,7 +26,7 @@ class AppModule { Modules.marianumMessage: AppModule('Marianum Message', Icons.newspaper, MarianumMessageListView.new), Modules.roomPlan: AppModule('Raumplan', Icons.location_pin, Roomplan.new), Modules.gradeAveragesCalculator: AppModule('Notendurschnittsrechner', Icons.calculate, GradeAveragesView.new), - Modules.holidays: AppModule('Schulferien', Icons.holiday_village, HolidaysView.new), + Modules.holidays: AppModule('Schulferien', Icons.time_to_leave, HolidaysView.new), }; static AppModule getModule(Modules module) => modules()[module]!; diff --git a/lib/state/app/modules/holidays/bloc/holidays_bloc.dart b/lib/state/app/modules/holidays/bloc/holidays_bloc.dart index 033be9e..248a080 100644 --- a/lib/state/app/modules/holidays/bloc/holidays_bloc.dart +++ b/lib/state/app/modules/holidays/bloc/holidays_bloc.dart @@ -1,5 +1,3 @@ -import 'dart:developer'; - import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc.dart'; import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart'; import '../repository/holidays_repository.dart'; @@ -9,7 +7,6 @@ import 'holidays_state.dart'; class HolidaysBloc extends LoadableHydratedBloc { HolidaysBloc() { on((event, emit) { - log('SetPastHolidaysVisible: ${event.shouldBeVisible.toString()}'); add(Emit((state) => state.copyWith(showPastHolidays: event.shouldBeVisible))); }); @@ -19,9 +16,9 @@ class HolidaysBloc extends LoadableHydratedBloc innerState?.showPastHolidays ?? false; - List? getHolidays() => innerState?.holidays.where( - (element) => showPastHolidays() || DateTime.parse(element.end).isAfter(DateTime.now()) - ).toList(); + List? getHolidays() => innerState?.holidays + .where((element) => showPastHolidays() || DateTime.parse(element.end).isAfter(DateTime.now())) + .toList() ?? []; @override fromNothing() => const HolidaysState(showPastHolidays: false, holidays: [], showDisclaimer: true); diff --git a/lib/state/app/modules/holidays/view/holidays_view.dart b/lib/state/app/modules/holidays/view/holidays_view.dart index 45b0b7f..d0da804 100644 --- a/lib/state/app/modules/holidays/view/holidays_view.dart +++ b/lib/state/app/modules/holidays/view/holidays_view.dart @@ -1,11 +1,11 @@ import 'package:flutter/material.dart'; import 'package:jiffy/jiffy.dart'; -import '../../../../../view/pages/more/holidays/holidays.dart'; import '../../../../../widget/animatedTime.dart'; import '../../../../../widget/list_view_util.dart'; import '../../../../../widget/centeredLeading.dart'; import '../../../../../widget/debug/debugTile.dart'; +import '../../../../../widget/string_extensions.dart'; import '../../../infrastructure/loadableState/view/loadable_state_consumer.dart'; import '../../../infrastructure/utilityWidgets/bloc_module.dart'; import '../bloc/holidays_bloc.dart'; @@ -41,12 +41,12 @@ class HolidaysView extends StatelessWidget { title: const Text('Schulferien in Hessen'), actions: [ IconButton( - icon: const Icon(Icons.warning_amber_outlined), + icon: const Icon(Icons.info_outline), onPressed: showDisclaimer, ), PopupMenuButton( initialValue: bloc.showPastHolidays(), - icon: const Icon(Icons.manage_history_outlined), + icon: const Icon(Icons.history), itemBuilder: (context) => [true, false].map((e) => PopupMenuItem( value: e, enabled: e != bloc.showPastHolidays(), @@ -65,26 +65,31 @@ class HolidaysView extends StatelessWidget { body: LoadableStateConsumer( child: (state, loading) => ListViewUtil.fromList(bloc.getHolidays(), (holiday) { var holidayType = holiday.name.split(' ').first.capitalize(); - String formatDate(String enDate) => Jiffy.parse(enDate).format(pattern: 'dd.MM.yyyy'); + String formatDate(String date) => Jiffy.parse(date).format(pattern: 'dd.MM.yyyy'); + String getYear(String date, {String format = 'yyyy'}) => Jiffy.parse(date).format(pattern: format); + + String getHolidayYear(String startDate, String endDate) => getYear(startDate) == getYear(endDate) + ? getYear(startDate) + : '${getYear(startDate)}/${getYear(endDate, format: 'yy')}'; return ListTile( leading: const CenteredLeading(Icon(Icons.calendar_month)), - title: Text('${state.showPastHolidays}$holidayType ab ${formatDate(holiday.start)}'), - subtitle: Text('bis ${formatDate(holiday.end)}'), + title: Text('$holidayType ${getHolidayYear(holiday.start, holiday.end)}'), + subtitle: Text('${formatDate(holiday.start)} - ${formatDate(holiday.end)}'), onTap: () => showDialog(context: context, builder: (context) => SimpleDialog( title: Text('$holidayType ${holiday.year} in Hessen'), children: [ ListTile( leading: const CenteredLeading(Icon(Icons.signpost_outlined)), - title: Text(holiday.name), - subtitle: Text(holiday.slug), + title: Text(holiday.name.capitalize()), + subtitle: Text(holiday.slug.capitalize()), ), ListTile( - leading: const Icon(Icons.arrow_forward), + leading: const Icon(Icons.date_range_outlined), title: Text('vom ${formatDate(holiday.start)}'), ), ListTile( - leading: const Icon(Icons.arrow_back), + leading: const Icon(Icons.date_range_outlined), title: Text('bis zum ${formatDate(holiday.end)}'), ), Visibility( diff --git a/lib/view/pages/more/feedback/feedbackDialog.dart b/lib/view/pages/more/feedback/feedbackDialog.dart index c3b6d15..f78eea2 100644 --- a/lib/view/pages/more/feedback/feedbackDialog.dart +++ b/lib/view/pages/more/feedback/feedbackDialog.dart @@ -60,12 +60,17 @@ class _FeedbackDialogState extends State { Padding( padding: const EdgeInsets.all(10), child: TextField( + onChanged: (value) { + if(value.trim().toLowerCase() == 'ranzig') { + _feedbackInput.text = 'selber'; + } + }, controller: _feedbackInput, autofocus: true, decoration: InputDecoration( border: const OutlineInputBorder(), label: const Text('Feedback und Verbesserungen'), - errorText: _textFieldEmpty ? 'Bitte gib eine Beschreibung an' : null, + errorText: _textFieldEmpty ? 'Bitte gib eine Beschreibung an???' : null, ), minLines: 4, maxLines: 7, diff --git a/lib/view/pages/more/holidays/holidays.dart b/lib/view/pages/more/holidays/holidays.dart deleted file mode 100644 index 6d5df2a..0000000 --- a/lib/view/pages/more/holidays/holidays.dart +++ /dev/null @@ -1,157 +0,0 @@ -import 'dart:core'; - -import 'package:flutter/material.dart'; -import 'package:jiffy/jiffy.dart'; -import 'package:provider/provider.dart'; - -import '../../../../model/holidays/holidaysProps.dart'; -import '../../../../storage/base/settingsProvider.dart'; -import '../../../../widget/centeredLeading.dart'; -import '../../../../widget/confirmDialog.dart'; -import '../../../../widget/debug/debugTile.dart'; -import '../../../../widget/loadingSpinner.dart'; -import '../../../../widget/placeholderView.dart'; -import '../../../../widget/animatedTime.dart'; - -class Holidays extends StatefulWidget { - const Holidays({super.key}); - - @override - State createState() => _HolidaysState(); -} - -extension StringExtension on String { - String capitalize() => '${this[0].toUpperCase()}${substring(1).toLowerCase()}'; -} - -class _HolidaysState extends State { - late SettingsProvider settings = Provider.of(context, listen: false); - late bool showPastEvents = settings.val().holidaysSettings.showPastEvents; - - @override - void initState() { - WidgetsBinding.instance.addPostFrameCallback((timeStamp) { - Provider.of(context, listen: false).run(); - if(!settings.val().holidaysSettings.dismissedDisclaimer) showDisclaimer(); - }); - - super.initState(); - } - - String parseString(String enDate) => Jiffy.parse(enDate).format(pattern: 'dd.MM.yyyy'); - - void showDisclaimer() { - showDialog(context: context, builder: (context) => AlertDialog( - title: const Text('Richtigkeit und Bereitstellung der Daten'), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - const Text('' - 'Sämtliche Datumsangaben sind ohne Gewähr.\n' - 'Ich übernehme weder Verantwortung für die Richtigkeit der Daten noch hafte ich für wirtschaftliche Schäden die aus der Verwendung dieser Daten entstehen können.\n\n' - 'Die Daten stammen von https://ferien-api.de/'), - const SizedBox(height: 30), - ListTile( - title: const Text('Diese Meldung nicht mehr anzeigen'), - trailing: Checkbox( - value: settings.val().holidaysSettings.dismissedDisclaimer, - onChanged: (value) => settings.val(write: true).holidaysSettings.dismissedDisclaimer = value!, - ), - ) - ], - ), - actions: [ - TextButton(child: const Text('ferien-api.de besuchen'), onPressed: () => ConfirmDialog.openBrowser(context, 'https://ferien-api.de/')), - TextButton(child: const Text('Okay'), onPressed: () => Navigator.of(context).pop()), - ], - )); - } - - @override - Widget build(BuildContext context) => Scaffold( - appBar: AppBar( - title: const Text('Schulferien in Hessen'), - actions: [ - IconButton( - icon: const Icon(Icons.warning_amber_outlined), - onPressed: showDisclaimer, - ), - PopupMenuButton( - initialValue: settings.val().holidaysSettings.showPastEvents, - icon: const Icon(Icons.manage_history_outlined), - itemBuilder: (context) => [true, false].map((e) => PopupMenuItem( - value: e, - enabled: e != showPastEvents, - child: Row( - children: [ - Icon(e ? Icons.history_outlined : Icons.history_toggle_off_outlined, color: Theme.of(context).colorScheme.onSurface), - const SizedBox(width: 15), - Text(e ? 'Alle anzeigen' : 'Nur zukünftige anzeigen') - ], - ) - )).toList(), - onSelected: (e) { - setState(() { - showPastEvents = e; - settings.val(write: true).holidaysSettings.showPastEvents = e; - }); - }, - ), - ], - ), - body: Consumer(builder: (context, value, child) { - if(value.primaryLoading()) return const LoadingSpinner(); - - var holidays = value.getHolidaysResponse.data; - if(!showPastEvents) holidays = holidays.where((element) => DateTime.parse(element.end).isAfter(DateTime.now())).toList(); - - if(holidays.isEmpty) return const PlaceholderView(icon: Icons.search_off, text: 'Es wurden keine Ferieneinträge gefunden!'); - - return ListView.builder( - itemCount: holidays.length, - itemBuilder: (context, index) { - var holiday = holidays[index]; - var holidayType = holiday.name.split(' ').first.capitalize(); - return ListTile( - leading: const CenteredLeading(Icon(Icons.calendar_month)), - title: Text('$holidayType ab ${parseString(holiday.start)}'), - subtitle: Text('bis ${parseString(holiday.end)}'), - onTap: () => showDialog(context: context, builder: (context) => SimpleDialog( - title: Text('$holidayType ${holiday.year} in Hessen'), - children: [ - ListTile( - leading: const CenteredLeading(Icon(Icons.signpost_outlined)), - title: Text(holiday.name), - subtitle: Text(holiday.slug), - ), - ListTile( - leading: const Icon(Icons.arrow_forward), - title: Text('vom ${parseString(holiday.start)}'), - ), - ListTile( - leading: const Icon(Icons.arrow_back), - title: Text('bis zum ${parseString(holiday.end)}'), - ), - Visibility( - visible: !DateTime.parse(holiday.start).difference(DateTime.now()).isNegative, - replacement: ListTile( - leading: const CenteredLeading(Icon(Icons.content_paste_search_outlined)), - title: Text(Jiffy.parse(holiday.start).fromNow()), - ), - child: ListTile( - leading: const CenteredLeading(Icon(Icons.timer_outlined)), - title: AnimatedTime(callback: () => DateTime.parse(holiday.start).difference(DateTime.now())), - subtitle: Text(Jiffy.parse(holiday.start).fromNow()), - ), - ), - DebugTile(context).jsonData(holiday.toJson()), - ], - )), - trailing: const Icon(Icons.arrow_right), - ); - }, - ); - }, - ) - ); -} diff --git a/lib/widget/string_extensions.dart b/lib/widget/string_extensions.dart new file mode 100644 index 0000000..246906a --- /dev/null +++ b/lib/widget/string_extensions.dart @@ -0,0 +1,3 @@ +extension StringExtensions on String { + String capitalize() => '${this[0].toUpperCase()}${substring(1).toLowerCase()}'; +} \ No newline at end of file