Skip to content

Commit

Permalink
feat(sks-menu): add pull to refresh
Browse files Browse the repository at this point in the history
  • Loading branch information
simon-the-shark committed Dec 10, 2024
1 parent 38a712e commit 316c35d
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 66 deletions.
8 changes: 8 additions & 0 deletions lib/api_base_rest/cache/cache.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ import "../client/offline_error.dart";
import "cache_manager.dart";

extension DataCachingX on Ref {
Future<void> clearCache(
String fullUrl,
int ttlDays,
) async {
final cacheManager = watch(restCacheManagerProvider(ttlDays));
await cacheManager.removeFile(fullUrl);
}

Future<T> getAndCacheData<T>(
String fullUrl,
int ttlDays,
Expand Down
5 changes: 3 additions & 2 deletions lib/features/sks-menu/data/models/sks_menu_response.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import "package:fast_immutable_collections/fast_immutable_collections.dart";
import "package:freezed_annotation/freezed_annotation.dart";

import "sks_menu_data.dart";
Expand All @@ -22,8 +23,8 @@ class ExtendedSksMenuResponse with _$ExtendedSksMenuResponse {
const factory ExtendedSksMenuResponse({
required bool isMenuOnline,
required DateTime lastUpdate,
required List<SksMenuDish> meals,
required List<String> technicalInfos,
required IList<SksMenuDish> meals,
required IList<String> technicalInfos,
}) = _ExtendedSksMenuResponse;

factory ExtendedSksMenuResponse.fromJson(Map<String, dynamic> json) =>
Expand Down
75 changes: 40 additions & 35 deletions lib/features/sks-menu/data/repository/sks_menu_repository.dart
Original file line number Diff line number Diff line change
@@ -1,47 +1,52 @@
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:fast_immutable_collections/fast_immutable_collections.dart";
import "package:riverpod_annotation/riverpod_annotation.dart";

import "../../../../api_base_rest/cache/cache.dart";
import "../../../../config/env.dart";
import "../../../../utils/context_extensions.dart";
import "../../../../utils/datetime_utils.dart";
import "../../presentation/sks_menu_screen.dart";
import "../models/dish_category_enum.dart";
import "../models/sks_menu_response.dart";

part "sks_menu_repository.g.dart";

@riverpod
Future<ExtendedSksMenuResponse> getSksMenuData(Ref ref) async {
final mealsUrl = "${Env.sksUrl}/meals/current";
const ttlDays = 1;

final sksMenuResponse = await ref.getAndCacheData(
mealsUrl,
ttlDays,
SksMenuResponse.fromJson,
extraValidityCheck: (data) =>
data.isMenuOnline &&
DateTime.now().date.isSameDay(data.lastUpdate.date),
localizedOfflineMessage: (context) =>
context.localize.my_offline_error_message(
context.localize.sks_menu,
),
onRetry: () => ref.invalidateSelf(),
);

final trueMeals = sksMenuResponse.meals
.where((e) => e.category != DishCategory.technicalInfo)
.toList();

final technicalInfos = sksMenuResponse.meals
.where((e) => e.category == DishCategory.technicalInfo)
.map((e) => e.name)
.toList();

return ExtendedSksMenuResponse(
isMenuOnline: sksMenuResponse.isMenuOnline,
lastUpdate: sksMenuResponse.lastUpdate,
meals: trueMeals,
technicalInfos: technicalInfos,
);
class SksMenuRepository extends _$SksMenuRepository {
static final _mealsUrl = "${Env.sksUrl}/meals/current";
static const _ttlDays = 1;

Future<void> clearCache() async {
return ref.clearCache(_mealsUrl, _ttlDays);
}

@override
Future<ExtendedSksMenuResponse> build() async {
final sksMenuResponse = await ref.getAndCacheData(
_mealsUrl,
_ttlDays,
SksMenuResponse.fromJson,
extraValidityCheck: (data) {
return data.isMenuOnline &&
DateTime.now().date.isSameDay(data.lastUpdate.date);
},
localizedOfflineMessage: SksMenuView.localizedOfflineMessage,
onRetry: () => ref.invalidateSelf(),
);

final trueMeals = sksMenuResponse.meals
.where((e) => e.category != DishCategory.technicalInfo)
.toIList();

final technicalInfos = sksMenuResponse.meals
.where((e) => e.category == DishCategory.technicalInfo)
.map((e) => e.name)
.toIList();

return ExtendedSksMenuResponse(
isMenuOnline: sksMenuResponse.isMenuOnline,
lastUpdate: sksMenuResponse.lastUpdate,
meals: trueMeals,
technicalInfos: technicalInfos,
);
}
}
61 changes: 37 additions & 24 deletions lib/features/sks-menu/presentation/sks_menu_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,15 @@ import "widgets/technical_message.dart";
class SksMenuView extends HookConsumerWidget {
const SksMenuView({super.key});

static String localizedOfflineMessage(BuildContext context) {
return context.localize.my_offline_error_message(
context.localize.sks_menu,
);
}

@override
Widget build(BuildContext context, WidgetRef ref) {
final asyncSksMenuData = ref.watch(getSksMenuDataProvider);
final asyncSksMenuData = ref.watch(sksMenuRepositoryProvider);
final isLastMenuButtonClicked = useState(false);

return asyncSksMenuData.when(
Expand Down Expand Up @@ -62,7 +68,7 @@ class SksMenuView extends HookConsumerWidget {
}
}

class _SksMenuView extends StatelessWidget {
class _SksMenuView extends ConsumerWidget {
const _SksMenuView({
required this.sksMenuData,
required this.isLastMenuButtonClicked,
Expand All @@ -72,7 +78,7 @@ class _SksMenuView extends StatelessWidget {
final bool isLastMenuButtonClicked;

@override
Widget build(BuildContext context) {
Widget build(BuildContext context, WidgetRef ref) {
if (!isLastMenuButtonClicked && !sksMenuData.isMenuOnline) {
return const _SKSMenuUnavailableAnimation();
}
Expand All @@ -82,28 +88,35 @@ class _SksMenuView extends StatelessWidget {
SksUserDataButton(),
],
),
body: ListView(
children: [
if (!sksMenuData.isMenuOnline)
TechnicalMessage(
color: context.colorTheme.blueAzure,
title: context.localize.sks_note,
message: context.localize.sks_menu_you_see_last_menu,
body: RefreshIndicator(
onRefresh: () async {
await ref.read(sksMenuRepositoryProvider.notifier).clearCache();
return ref.refresh(sksMenuRepositoryProvider.future);
},
color: context.colorTheme.orangePomegranade,
child: ListView(
children: [
if (!sksMenuData.isMenuOnline)
TechnicalMessage(
alertType: AlertType.info,
title: context.localize.sks_note,
message: context.localize.sks_menu_you_see_last_menu,
),
for (final technicalInfo in sksMenuData.technicalInfos)
TechnicalMessage(message: technicalInfo),
SksMenuHeader(
dateTimeOfLastUpdate: sksMenuData.lastUpdate.toIso8601String(),
),
for (final technicalInfo in sksMenuData.technicalInfos)
TechnicalMessage(message: technicalInfo),
SksMenuHeader(
dateTimeOfLastUpdate: sksMenuData.lastUpdate.toIso8601String(),
),
Padding(
padding: const EdgeInsets.all(HomeViewConfig.paddingMedium),
child: SksMenuSection(sksMenuData.meals),
),
const SksMenuDataSourceLink(),
const SizedBox(
height: ScienceClubsViewConfig.mediumPadding,
),
],
Padding(
padding: const EdgeInsets.all(HomeViewConfig.paddingMedium),
child: SksMenuSection(sksMenuData.meals),
),
const SksMenuDataSourceLink(),
const SizedBox(
height: ScienceClubsViewConfig.mediumPadding,
),
],
),
),
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import "sks_menu_tiles.dart";
class SksMenuSection extends StatelessWidget {
const SksMenuSection(this.data, {super.key});

final List<SksMenuDish> data;
final IList<SksMenuDish> data;

@override
Widget build(BuildContext context) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,31 @@ import "../../../../config/ui_config.dart";
import "../../../../theme/app_theme.dart";
import "../../data/models/dish_category_enum.dart";

enum AlertType { info, error }

class TechnicalMessage extends StatelessWidget {
const TechnicalMessage({
super.key,
required this.message,
this.title,
this.color,
this.alertType = AlertType.error,
});
final String message;
final String? title;
final Color? color;
final AlertType alertType;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(
HomeViewConfig.paddingMedium,
),
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
borderRadius:
BorderRadius.circular(AppWidgetsConfig.borderRadiusMedium),
child: ColoredBox(
color: color ?? context.colorTheme.orangePomegranade,
color: alertType == AlertType.error
? context.colorTheme.orangePomegranade
: context.colorTheme.blueAzure,
child: ListTile(
title: Text(
title ?? DishCategory.technicalInfo.getLocalizedName(context),
Expand Down

0 comments on commit 316c35d

Please sign in to comment.