diff --git a/lib/api/holidays/getHolidays.dart b/lib/api/holidays/getHolidays.dart index 1d7be0b..8ce3325 100644 --- a/lib/api/holidays/getHolidays.dart +++ b/lib/api/holidays/getHolidays.dart @@ -1,5 +1,4 @@ import 'dart:convert'; - import 'package:http/http.dart' as http; import 'getHolidaysResponse.dart'; @@ -7,11 +6,10 @@ import 'getHolidaysResponse.dart'; class GetHolidays { Future query() async { var response = (await http.get(Uri.parse('https://ferien-api.de/api/v1/holidays/HE'))).body; + var data = jsonDecode(response) as List; return GetHolidaysResponse( List.from( - jsonDecode(response).map( - GetHolidaysResponseObject.fromJson - ) + data.map((e) => GetHolidaysResponseObject.fromJson(e as Map)) ) ); } diff --git a/lib/api/holidays/getHolidaysCache.dart b/lib/api/holidays/getHolidaysCache.dart index d27a9ed..49e04e1 100644 --- a/lib/api/holidays/getHolidaysCache.dart +++ b/lib/api/holidays/getHolidaysCache.dart @@ -13,12 +13,11 @@ class GetHolidaysCache extends RequestCache { GetHolidaysResponse onLocalData(String json) { List parsedListJson = jsonDecode(json)['data']; return GetHolidaysResponse( - List.from( - parsedListJson.map( - // ignore: unnecessary_lambdas - (dynamic i) => GetHolidaysResponseObject.fromJson(i) - ) + List.from( + parsedListJson.map( + (i) => GetHolidaysResponseObject.fromJson(i as Map) ) + ) ); } diff --git a/lib/state/app/basis/dataloader/holiday_data_loader.dart b/lib/state/app/basis/dataloader/holiday_data_loader.dart new file mode 100644 index 0000000..19c345f --- /dev/null +++ b/lib/state/app/basis/dataloader/holiday_data_loader.dart @@ -0,0 +1,9 @@ +import 'package:dio/dio.dart'; + +import '../../infrastructure/dataLoader/data_loader.dart'; + +abstract class HolidayDataLoader extends DataLoader { + HolidayDataLoader() : super(Dio(BaseOptions( + baseUrl: 'https://ferien-api.de/api/v1/', + ))); +} diff --git a/lib/state/app/infrastructure/dataLoader/mhsl_data_loader.dart b/lib/state/app/basis/dataloader/mhsl_data_loader.dart similarity index 78% rename from lib/state/app/infrastructure/dataLoader/mhsl_data_loader.dart rename to lib/state/app/basis/dataloader/mhsl_data_loader.dart index 0825587..6b4baab 100644 --- a/lib/state/app/infrastructure/dataLoader/mhsl_data_loader.dart +++ b/lib/state/app/basis/dataloader/mhsl_data_loader.dart @@ -1,6 +1,6 @@ import 'package:dio/dio.dart'; -import 'data_loader.dart'; +import '../../infrastructure/dataLoader/data_loader.dart'; abstract class MhslDataLoader extends DataLoader { MhslDataLoader() : super(Dio(BaseOptions( diff --git a/lib/state/app/infrastructure/dataLoader/data_loader.dart b/lib/state/app/infrastructure/dataLoader/data_loader.dart index cb4a3fe..64c1aa7 100644 --- a/lib/state/app/infrastructure/dataLoader/data_loader.dart +++ b/lib/state/app/infrastructure/dataLoader/data_loader.dart @@ -35,8 +35,12 @@ abstract class DataLoader { } class DataLoaderResult { - final Map json; + final dynamic json; final Map headers; + Map asMap() => json as Map; + List asList() => json as List; + List> asListOfMaps() => asList().map((e) => e as Map).toList(); + DataLoaderResult({required this.json, required this.headers}); } diff --git a/lib/state/app/infrastructure/loadableState/loadable_state.dart b/lib/state/app/infrastructure/loadableState/loadable_state.dart index ac1626a..b163e41 100644 --- a/lib/state/app/infrastructure/loadableState/loadable_state.dart +++ b/lib/state/app/infrastructure/loadableState/loadable_state.dart @@ -9,11 +9,11 @@ class LoadableState with _$LoadableState { const LoadableState._(); const factory LoadableState({ - @Default(true) bool isLoading, - @Default(null) TState? data, - @Default(null) int? lastFetch, - @Default(null) void Function()? reFetch, - @Default(null) LoadingError? error, + required bool isLoading, + required TState? data, + required int? lastFetch, + required void Function()? reFetch, + required LoadingError? error, }) = _LoadableState; bool _hasError() => error != null; diff --git a/lib/state/app/infrastructure/loadableState/view/loadable_state_error_bar.dart b/lib/state/app/infrastructure/loadableState/view/loadable_state_error_bar.dart index bc5353c..fd27b2b 100644 --- a/lib/state/app/infrastructure/loadableState/view/loadable_state_error_bar.dart +++ b/lib/state/app/infrastructure/loadableState/view/loadable_state_error_bar.dart @@ -89,4 +89,3 @@ class _LoadableStateErrorBarTextState extends State { super.dispose(); } } - diff --git a/lib/state/app/infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc.dart b/lib/state/app/infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc.dart index 116bad2..cdcedf7 100644 --- a/lib/state/app/infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc.dart +++ b/lib/state/app/infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc.dart @@ -17,23 +17,40 @@ abstract class LoadableHydratedBloc< LoadableState > { late TRepository _repository; - LoadableHydratedBloc() : super(const LoadableState()) { + LoadableHydratedBloc() : super(const LoadableState( + error: null, + data: null, + isLoading: true, + lastFetch: null, + reFetch: null, + )) { - on>((event, emit) => emit(LoadableState( - isLoading: event.loading, + on>((event, emit) { + emit(LoadableState( + isLoading: state.isLoading, data: event.state(innerState ?? fromNothing()), - lastFetch: DateTime.now().millisecondsSinceEpoch, - reFetch: retry + lastFetch: state.lastFetch, + reFetch: retry, + error: state.error, + )); + }); + + on>((event, emit) => emit(LoadableState( + isLoading: false, + data: event.state(innerState ?? fromNothing()), + lastFetch: DateTime.now().millisecondsSinceEpoch, + reFetch: retry, + error: null, ))); on>((event, emit) => emit(LoadableState( isLoading: true, data: innerState, - lastFetch: state.lastFetch + lastFetch: state.lastFetch, + reFetch: null, + error: null, ))); - on>((event, emit) => emit(const LoadableState())); - on>((event, emit) => emit(LoadableState( isLoading: false, data: innerState, @@ -61,7 +78,7 @@ abstract class LoadableHydratedBloc< (e) { log('Error while fetching ${TState.toString()}: ${e.toString()}'); add(Error(LoadingError( - message: e.message, + message: e.message ?? e.toString(), allowRetry: true, ))); }, @@ -73,14 +90,30 @@ abstract class LoadableHydratedBloc< @override fromJson(Map json) { var rawData = LoadableSaveContext.unwrap(json); - return LoadableState(isLoading: true, lastFetch: rawData.meta.timestamp, data: fromStorage(rawData.data)); + return LoadableState( + isLoading: true, + data: fromStorage(rawData.data), + lastFetch: rawData.meta.timestamp, + reFetch: null, + error: null, + ); } @override - Map? toJson(LoadableState state) => LoadableSaveContext.wrap( - toStorage(state.data), + Map? toJson(LoadableState state) { + Map? data; + try { + data = toStorage(state.data); + } catch(e) { + log('Failed to save state ${TState.toString()}: ${e.toString()}'); + data = null; + } + + return LoadableSaveContext.wrap( + data, state.lastFetch ?? DateTime.now().millisecondsSinceEpoch - ); + ); + } Future gatherData(); TRepository repository(); diff --git a/lib/state/app/infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart b/lib/state/app/infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart index 06462de..55b8e6a 100644 --- a/lib/state/app/infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart +++ b/lib/state/app/infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart @@ -3,10 +3,12 @@ import '../../loadableState/loading_error.dart'; class LoadableHydratedBlocEvent {} class Emit extends LoadableHydratedBlocEvent { final TState Function(TState state) state; - final bool loading; - Emit(this.state, {this.loading = false}); + Emit(this.state); +} +class DataGathered extends LoadableHydratedBlocEvent { + final TState Function(TState state) state; + DataGathered(this.state); } -class ClearState extends LoadableHydratedBlocEvent {} class Error extends LoadableHydratedBlocEvent { final LoadingError error; Error(this.error); diff --git a/lib/state/app/infrastructure/utilityWidgets/loadableHydratedBloc/loadable_save_context.dart b/lib/state/app/infrastructure/utilityWidgets/loadableHydratedBloc/loadable_save_context.dart index e4d68d5..0dea5cd 100644 --- a/lib/state/app/infrastructure/utilityWidgets/loadableHydratedBloc/loadable_save_context.dart +++ b/lib/state/app/infrastructure/utilityWidgets/loadableHydratedBloc/loadable_save_context.dart @@ -21,4 +21,3 @@ class LoadableSaveContext with _$LoadableSaveContext { static ({Map data, LoadableSaveContext meta}) unwrap(Map data) => (data: data[dataKey] as Map, meta: LoadableSaveContext.fromJson(data[metaKey])); } - diff --git a/lib/state/app/modules/app_modules.dart b/lib/state/app/modules/app_modules.dart index 83b93b5..5e53b32 100644 --- a/lib/state/app/modules/app_modules.dart +++ b/lib/state/app/modules/app_modules.dart @@ -4,12 +4,12 @@ import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart'; import '../../../api/mhsl/breaker/getBreakers/getBreakersResponse.dart'; import '../../../model/breakers/Breaker.dart'; import '../../../view/pages/files/files.dart'; -import '../../../view/pages/more/holidays/holidays.dart'; import '../../../view/pages/more/roomplan/roomplan.dart'; import '../../../view/pages/talk/chatList.dart'; import '../../../view/pages/timetable/timetable.dart'; import '../../../widget/centeredLeading.dart'; import 'gradeAverages/view/grade_averages_view.dart'; +import 'holidays/view/holidays_view.dart'; import 'marianumMessage/view/marianum_message_list_view.dart'; class AppModule { @@ -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, Holidays.new), + Modules.holidays: AppModule('Schulferien', Icons.holiday_village, 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 new file mode 100644 index 0000000..cd369f0 --- /dev/null +++ b/lib/state/app/modules/holidays/bloc/holidays_bloc.dart @@ -0,0 +1,36 @@ +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'; +import 'holidays_event.dart'; +import 'holidays_state.dart'; + +class HolidaysBloc extends LoadableHydratedBloc { + HolidaysBloc() { + on((event, emit) { + log('set pastholidays: ${event.shouldBeVisible.toString()}'); + add(Emit((state) => state.copyWith(showPastHolidays: state.showPastHolidays))); + }); + + on((event, emit) => add( + Emit((state) => state.copyWith(showDisclaimer: false)) + )); + } + + bool showPastHolidays() => innerState?.showPastHolidays ?? false; + + @override + fromNothing() => const HolidaysState(showPastHolidays: false, holidays: [], showDisclaimer: true); + @override + fromStorage(Map json) => HolidaysState.fromJson(json); + @override + Future gatherData() async { + var holidays = await repo.getHolidays(); + add(DataGathered((state) => state.copyWith(holidays: holidays))); + } + @override + repository() => HolidaysRepository(); + @override + Map? toStorage(state) => state.toJson(); +} diff --git a/lib/state/app/modules/holidays/bloc/holidays_event.dart b/lib/state/app/modules/holidays/bloc/holidays_event.dart new file mode 100644 index 0000000..8565250 --- /dev/null +++ b/lib/state/app/modules/holidays/bloc/holidays_event.dart @@ -0,0 +1,9 @@ +import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart'; +import 'holidays_state.dart'; + +sealed class HolidaysEvent extends LoadableHydratedBlocEvent {} +class SetPastHolidaysVisible extends HolidaysEvent { + final bool shouldBeVisible; + SetPastHolidaysVisible(this.shouldBeVisible); +} +class DisclaimerDismissed extends HolidaysEvent {} diff --git a/lib/state/app/modules/holidays/bloc/holidays_state.dart b/lib/state/app/modules/holidays/bloc/holidays_state.dart new file mode 100644 index 0000000..05c0cd2 --- /dev/null +++ b/lib/state/app/modules/holidays/bloc/holidays_state.dart @@ -0,0 +1,30 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:flutter/foundation.dart'; + +part 'holidays_state.freezed.dart'; +part 'holidays_state.g.dart'; + +@freezed +class HolidaysState with _$HolidaysState { + const factory HolidaysState({ + required bool showPastHolidays, + required bool showDisclaimer, + required List holidays, + }) = _HolidaysState; + + factory HolidaysState.fromJson(Map json) => _$HolidaysStateFromJson(json); +} + +@freezed +class Holiday with _$Holiday { + const factory Holiday({ + required String start, + required String end, + required int year, + required String stateCode, + required String name, + required String slug, + }) = _Holiday; + + factory Holiday.fromJson(Map json) => _$HolidayFromJson(json); +} diff --git a/lib/state/app/modules/holidays/bloc/holidays_state.freezed.dart b/lib/state/app/modules/holidays/bloc/holidays_state.freezed.dart new file mode 100644 index 0000000..6659f45 --- /dev/null +++ b/lib/state/app/modules/holidays/bloc/holidays_state.freezed.dart @@ -0,0 +1,463 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'holidays_state.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +HolidaysState _$HolidaysStateFromJson(Map json) { + return _HolidaysState.fromJson(json); +} + +/// @nodoc +mixin _$HolidaysState { + bool get showPastHolidays => throw _privateConstructorUsedError; + bool get showDisclaimer => throw _privateConstructorUsedError; + List get holidays => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $HolidaysStateCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $HolidaysStateCopyWith<$Res> { + factory $HolidaysStateCopyWith( + HolidaysState value, $Res Function(HolidaysState) then) = + _$HolidaysStateCopyWithImpl<$Res, HolidaysState>; + @useResult + $Res call( + {bool showPastHolidays, bool showDisclaimer, List holidays}); +} + +/// @nodoc +class _$HolidaysStateCopyWithImpl<$Res, $Val extends HolidaysState> + implements $HolidaysStateCopyWith<$Res> { + _$HolidaysStateCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? showPastHolidays = null, + Object? showDisclaimer = null, + Object? holidays = null, + }) { + return _then(_value.copyWith( + showPastHolidays: null == showPastHolidays + ? _value.showPastHolidays + : showPastHolidays // ignore: cast_nullable_to_non_nullable + as bool, + showDisclaimer: null == showDisclaimer + ? _value.showDisclaimer + : showDisclaimer // ignore: cast_nullable_to_non_nullable + as bool, + holidays: null == holidays + ? _value.holidays + : holidays // ignore: cast_nullable_to_non_nullable + as List, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$HolidaysStateImplCopyWith<$Res> + implements $HolidaysStateCopyWith<$Res> { + factory _$$HolidaysStateImplCopyWith( + _$HolidaysStateImpl value, $Res Function(_$HolidaysStateImpl) then) = + __$$HolidaysStateImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {bool showPastHolidays, bool showDisclaimer, List holidays}); +} + +/// @nodoc +class __$$HolidaysStateImplCopyWithImpl<$Res> + extends _$HolidaysStateCopyWithImpl<$Res, _$HolidaysStateImpl> + implements _$$HolidaysStateImplCopyWith<$Res> { + __$$HolidaysStateImplCopyWithImpl( + _$HolidaysStateImpl _value, $Res Function(_$HolidaysStateImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? showPastHolidays = null, + Object? showDisclaimer = null, + Object? holidays = null, + }) { + return _then(_$HolidaysStateImpl( + showPastHolidays: null == showPastHolidays + ? _value.showPastHolidays + : showPastHolidays // ignore: cast_nullable_to_non_nullable + as bool, + showDisclaimer: null == showDisclaimer + ? _value.showDisclaimer + : showDisclaimer // ignore: cast_nullable_to_non_nullable + as bool, + holidays: null == holidays + ? _value._holidays + : holidays // ignore: cast_nullable_to_non_nullable + as List, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$HolidaysStateImpl + with DiagnosticableTreeMixin + implements _HolidaysState { + const _$HolidaysStateImpl( + {required this.showPastHolidays, + required this.showDisclaimer, + required final List holidays}) + : _holidays = holidays; + + factory _$HolidaysStateImpl.fromJson(Map json) => + _$$HolidaysStateImplFromJson(json); + + @override + final bool showPastHolidays; + @override + final bool showDisclaimer; + final List _holidays; + @override + List get holidays { + if (_holidays is EqualUnmodifiableListView) return _holidays; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_holidays); + } + + @override + String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { + return 'HolidaysState(showPastHolidays: $showPastHolidays, showDisclaimer: $showDisclaimer, holidays: $holidays)'; + } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(DiagnosticsProperty('type', 'HolidaysState')) + ..add(DiagnosticsProperty('showPastHolidays', showPastHolidays)) + ..add(DiagnosticsProperty('showDisclaimer', showDisclaimer)) + ..add(DiagnosticsProperty('holidays', holidays)); + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$HolidaysStateImpl && + (identical(other.showPastHolidays, showPastHolidays) || + other.showPastHolidays == showPastHolidays) && + (identical(other.showDisclaimer, showDisclaimer) || + other.showDisclaimer == showDisclaimer) && + const DeepCollectionEquality().equals(other._holidays, _holidays)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => Object.hash(runtimeType, showPastHolidays, showDisclaimer, + const DeepCollectionEquality().hash(_holidays)); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$HolidaysStateImplCopyWith<_$HolidaysStateImpl> get copyWith => + __$$HolidaysStateImplCopyWithImpl<_$HolidaysStateImpl>(this, _$identity); + + @override + Map toJson() { + return _$$HolidaysStateImplToJson( + this, + ); + } +} + +abstract class _HolidaysState implements HolidaysState { + const factory _HolidaysState( + {required final bool showPastHolidays, + required final bool showDisclaimer, + required final List holidays}) = _$HolidaysStateImpl; + + factory _HolidaysState.fromJson(Map json) = + _$HolidaysStateImpl.fromJson; + + @override + bool get showPastHolidays; + @override + bool get showDisclaimer; + @override + List get holidays; + @override + @JsonKey(ignore: true) + _$$HolidaysStateImplCopyWith<_$HolidaysStateImpl> get copyWith => + throw _privateConstructorUsedError; +} + +Holiday _$HolidayFromJson(Map json) { + return _Holiday.fromJson(json); +} + +/// @nodoc +mixin _$Holiday { + String get start => throw _privateConstructorUsedError; + String get end => throw _privateConstructorUsedError; + int get year => throw _privateConstructorUsedError; + String get stateCode => throw _privateConstructorUsedError; + String get name => throw _privateConstructorUsedError; + String get slug => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $HolidayCopyWith get copyWith => throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $HolidayCopyWith<$Res> { + factory $HolidayCopyWith(Holiday value, $Res Function(Holiday) then) = + _$HolidayCopyWithImpl<$Res, Holiday>; + @useResult + $Res call( + {String start, + String end, + int year, + String stateCode, + String name, + String slug}); +} + +/// @nodoc +class _$HolidayCopyWithImpl<$Res, $Val extends Holiday> + implements $HolidayCopyWith<$Res> { + _$HolidayCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? start = null, + Object? end = null, + Object? year = null, + Object? stateCode = null, + Object? name = null, + Object? slug = null, + }) { + return _then(_value.copyWith( + start: null == start + ? _value.start + : start // ignore: cast_nullable_to_non_nullable + as String, + end: null == end + ? _value.end + : end // ignore: cast_nullable_to_non_nullable + as String, + year: null == year + ? _value.year + : year // ignore: cast_nullable_to_non_nullable + as int, + stateCode: null == stateCode + ? _value.stateCode + : stateCode // ignore: cast_nullable_to_non_nullable + as String, + name: null == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + slug: null == slug + ? _value.slug + : slug // ignore: cast_nullable_to_non_nullable + as String, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$HolidayImplCopyWith<$Res> implements $HolidayCopyWith<$Res> { + factory _$$HolidayImplCopyWith( + _$HolidayImpl value, $Res Function(_$HolidayImpl) then) = + __$$HolidayImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {String start, + String end, + int year, + String stateCode, + String name, + String slug}); +} + +/// @nodoc +class __$$HolidayImplCopyWithImpl<$Res> + extends _$HolidayCopyWithImpl<$Res, _$HolidayImpl> + implements _$$HolidayImplCopyWith<$Res> { + __$$HolidayImplCopyWithImpl( + _$HolidayImpl _value, $Res Function(_$HolidayImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? start = null, + Object? end = null, + Object? year = null, + Object? stateCode = null, + Object? name = null, + Object? slug = null, + }) { + return _then(_$HolidayImpl( + start: null == start + ? _value.start + : start // ignore: cast_nullable_to_non_nullable + as String, + end: null == end + ? _value.end + : end // ignore: cast_nullable_to_non_nullable + as String, + year: null == year + ? _value.year + : year // ignore: cast_nullable_to_non_nullable + as int, + stateCode: null == stateCode + ? _value.stateCode + : stateCode // ignore: cast_nullable_to_non_nullable + as String, + name: null == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + slug: null == slug + ? _value.slug + : slug // ignore: cast_nullable_to_non_nullable + as String, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$HolidayImpl with DiagnosticableTreeMixin implements _Holiday { + const _$HolidayImpl( + {required this.start, + required this.end, + required this.year, + required this.stateCode, + required this.name, + required this.slug}); + + factory _$HolidayImpl.fromJson(Map json) => + _$$HolidayImplFromJson(json); + + @override + final String start; + @override + final String end; + @override + final int year; + @override + final String stateCode; + @override + final String name; + @override + final String slug; + + @override + String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { + return 'Holiday(start: $start, end: $end, year: $year, stateCode: $stateCode, name: $name, slug: $slug)'; + } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(DiagnosticsProperty('type', 'Holiday')) + ..add(DiagnosticsProperty('start', start)) + ..add(DiagnosticsProperty('end', end)) + ..add(DiagnosticsProperty('year', year)) + ..add(DiagnosticsProperty('stateCode', stateCode)) + ..add(DiagnosticsProperty('name', name)) + ..add(DiagnosticsProperty('slug', slug)); + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$HolidayImpl && + (identical(other.start, start) || other.start == start) && + (identical(other.end, end) || other.end == end) && + (identical(other.year, year) || other.year == year) && + (identical(other.stateCode, stateCode) || + other.stateCode == stateCode) && + (identical(other.name, name) || other.name == name) && + (identical(other.slug, slug) || other.slug == slug)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => + Object.hash(runtimeType, start, end, year, stateCode, name, slug); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$HolidayImplCopyWith<_$HolidayImpl> get copyWith => + __$$HolidayImplCopyWithImpl<_$HolidayImpl>(this, _$identity); + + @override + Map toJson() { + return _$$HolidayImplToJson( + this, + ); + } +} + +abstract class _Holiday implements Holiday { + const factory _Holiday( + {required final String start, + required final String end, + required final int year, + required final String stateCode, + required final String name, + required final String slug}) = _$HolidayImpl; + + factory _Holiday.fromJson(Map json) = _$HolidayImpl.fromJson; + + @override + String get start; + @override + String get end; + @override + int get year; + @override + String get stateCode; + @override + String get name; + @override + String get slug; + @override + @JsonKey(ignore: true) + _$$HolidayImplCopyWith<_$HolidayImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/state/app/modules/holidays/bloc/holidays_state.g.dart b/lib/state/app/modules/holidays/bloc/holidays_state.g.dart new file mode 100644 index 0000000..1d0f3f0 --- /dev/null +++ b/lib/state/app/modules/holidays/bloc/holidays_state.g.dart @@ -0,0 +1,43 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'holidays_state.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$HolidaysStateImpl _$$HolidaysStateImplFromJson(Map json) => + _$HolidaysStateImpl( + showPastHolidays: json['showPastHolidays'] as bool, + showDisclaimer: json['showDisclaimer'] as bool, + holidays: (json['holidays'] as List) + .map((e) => Holiday.fromJson(e as Map)) + .toList(), + ); + +Map _$$HolidaysStateImplToJson(_$HolidaysStateImpl instance) => + { + 'showPastHolidays': instance.showPastHolidays, + 'showDisclaimer': instance.showDisclaimer, + 'holidays': instance.holidays, + }; + +_$HolidayImpl _$$HolidayImplFromJson(Map json) => + _$HolidayImpl( + start: json['start'] as String, + end: json['end'] as String, + year: json['year'] as int, + stateCode: json['stateCode'] as String, + name: json['name'] as String, + slug: json['slug'] as String, + ); + +Map _$$HolidayImplToJson(_$HolidayImpl instance) => + { + 'start': instance.start, + 'end': instance.end, + 'year': instance.year, + 'stateCode': instance.stateCode, + 'name': instance.name, + 'slug': instance.slug, + }; diff --git a/lib/state/app/modules/holidays/dataProvider/holidays_get_holidays.dart b/lib/state/app/modules/holidays/dataProvider/holidays_get_holidays.dart new file mode 100644 index 0000000..cd52128 --- /dev/null +++ b/lib/state/app/modules/holidays/dataProvider/holidays_get_holidays.dart @@ -0,0 +1,13 @@ +import 'package:dio/dio.dart'; + +import '../../../basis/dataloader/holiday_data_loader.dart'; +import '../../../infrastructure/dataLoader/data_loader.dart'; +import '../bloc/holidays_state.dart'; + +class HolidaysGetHolidays extends HolidayDataLoader> { + @override + List assemble(DataLoaderResult data) => data.asListOfMaps().map(Holiday.fromJson).toList(); + + @override + Future> fetch() => dio.get('/holidays/HE'); +} diff --git a/lib/state/app/modules/holidays/repository/holidays_repository.dart b/lib/state/app/modules/holidays/repository/holidays_repository.dart new file mode 100644 index 0000000..72ec949 --- /dev/null +++ b/lib/state/app/modules/holidays/repository/holidays_repository.dart @@ -0,0 +1,7 @@ +import '../../../infrastructure/repository/repository.dart'; +import '../bloc/holidays_state.dart'; +import '../dataProvider/holidays_get_holidays.dart'; + +class HolidaysRepository extends Repository { + Future> getHolidays() => HolidaysGetHolidays().run(); +} diff --git a/lib/state/app/modules/holidays/view/holidays_view.dart b/lib/state/app/modules/holidays/view/holidays_view.dart new file mode 100644 index 0000000..3b40f4e --- /dev/null +++ b/lib/state/app/modules/holidays/view/holidays_view.dart @@ -0,0 +1,120 @@ +import 'dart:developer'; + +import 'package:flutter/material.dart'; +import 'package:jiffy/jiffy.dart'; + +import '../../../../../view/pages/more/holidays/holidays.dart'; +import '../../../../../widget/animatedTime.dart'; +import '../../../../../widget/centeredLeading.dart'; +import '../../../../../widget/debug/debugTile.dart'; +import '../../../infrastructure/loadableState/loadable_state.dart'; +import '../../../infrastructure/loadableState/view/loadable_state_consumer.dart'; +import '../../../infrastructure/utilityWidgets/bloc_module.dart'; +import '../bloc/holidays_bloc.dart'; +import '../bloc/holidays_event.dart'; +import '../bloc/holidays_state.dart'; + +class HolidaysView extends StatelessWidget { + const HolidaysView({super.key}); + + @override + Widget build(BuildContext context) => BlocModule>( + create: (contet) => HolidaysBloc(), + autoRebuild: true, + child: (context, bloc, state) { + log(state.toString()); + void showDisclaimer() { + showDialog(context: context, builder: (context) => AlertDialog( + title: const Text('Richtigkeit und Bereitstellung der Daten'), + content: 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/'), + actions: [ + TextButton(child: const Text('Okay'), onPressed: () { + bloc.add(DisclaimerDismissed()); + Navigator.of(context).pop(); + }), + ], + )); + } + + return Scaffold( + appBar: AppBar( + title: const Text('Schulferien in Hessen'), + actions: [ + IconButton( + icon: const Icon(Icons.warning_amber_outlined), + onPressed: showDisclaimer, + ), + PopupMenuButton( + initialValue: bloc.showPastHolidays(), + icon: const Icon(Icons.manage_history_outlined), + itemBuilder: (context) => [true, false].map((e) => PopupMenuItem( + value: e, + enabled: e != bloc.showPastHolidays(), + 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) => bloc.add(SetPastHolidaysVisible(e)), + ), + ], + ), + body: LoadableStateConsumer( + child: (state, loading) => ListView.builder( + itemCount: state.holidays.length, + itemBuilder: (context, index) { + var holiday = state.holidays[index]; + var holidayType = holiday.name.split(' ').first.capitalize(); + + String formatDate(String enDate) => Jiffy.parse(enDate).format(pattern: 'dd.MM.yyyy'); + + return ListTile( + leading: const CenteredLeading(Icon(Icons.calendar_month)), + title: Text('$holidayType ab ${formatDate(holiday.start)}'), + subtitle: Text('bis ${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), + ), + ListTile( + leading: const Icon(Icons.arrow_forward), + title: Text('vom ${formatDate(holiday.start)}'), + ), + ListTile( + leading: const Icon(Icons.arrow_back), + title: Text('bis zum ${formatDate(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/state/app/modules/marianumMessage/bloc/marianum_message_bloc.dart b/lib/state/app/modules/marianumMessage/bloc/marianum_message_bloc.dart index 7876351..97c3b95 100644 --- a/lib/state/app/modules/marianumMessage/bloc/marianum_message_bloc.dart +++ b/lib/state/app/modules/marianumMessage/bloc/marianum_message_bloc.dart @@ -8,7 +8,7 @@ class MarianumMessageBloc extends LoadableHydratedBloc gatherData() async { var messages = await repo.getMessages(); - add(Emit((state) => state.copyWith(messageList: messages))); + add(DataGathered((state) => state.copyWith(messageList: messages))); } @override diff --git a/lib/state/app/modules/marianumMessage/dataProvider/marianum_message_get_messages.dart b/lib/state/app/modules/marianumMessage/dataProvider/marianum_message_get_messages.dart index cb7f9db..f8c4b24 100644 --- a/lib/state/app/modules/marianumMessage/dataProvider/marianum_message_get_messages.dart +++ b/lib/state/app/modules/marianumMessage/dataProvider/marianum_message_get_messages.dart @@ -1,7 +1,7 @@ import 'package:dio/dio.dart'; import '../../../infrastructure/dataLoader/data_loader.dart'; -import '../../../infrastructure/dataLoader/mhsl_data_loader.dart'; +import '../../../basis/dataloader/mhsl_data_loader.dart'; import '../bloc/marianum_message_state.dart'; class MarianumMessageGetMessages extends MhslDataLoader { diff --git a/lib/view/pages/talk/components/chatBubble.dart b/lib/view/pages/talk/components/chatBubble.dart index 8110c1c..0539f19 100644 --- a/lib/view/pages/talk/components/chatBubble.dart +++ b/lib/view/pages/talk/components/chatBubble.dart @@ -2,7 +2,6 @@ import 'package:better_open_file/better_open_file.dart'; import 'package:bubble/bubble.dart'; import 'package:emoji_picker_flutter/emoji_picker_flutter.dart' as emojis; import 'package:flowder/flowder.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; diff --git a/lib/view/pages/talk/components/chatBubbleStyles.dart b/lib/view/pages/talk/components/chatBubbleStyles.dart index c640ca2..5cf527a 100644 --- a/lib/view/pages/talk/components/chatBubbleStyles.dart +++ b/lib/view/pages/talk/components/chatBubbleStyles.dart @@ -51,4 +51,4 @@ class ChatBubbleStyles { alignment: Alignment.topRight, ); } -} \ No newline at end of file +}