Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/adjust sks menu #479

Merged
merged 6 commits into from
Dec 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 32 additions & 14 deletions lib/api_base_rest/cache/cache.dart
Original file line number Diff line number Diff line change
@@ -1,37 +1,55 @@
import "dart:convert";
import "dart:typed_data";

import "package:flutter/widgets.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";
import "../client/dio_client.dart";

import "../client/offline_error.dart";
import "cache_manager.dart";

extension DataCachingX on Ref {
Future<T> getAndCacheData<T>(
Future<void> clearCache(
String fullUrl,
int ttlDays,
T Function(Map<String, dynamic> json) fromJson,
bool Function() extraValidityCheck,
) async {
final cacheManager = watch(restCacheManagerProvider(ttlDays));
await cacheManager.removeFile(fullUrl);
}

Future<T> getAndCacheData<T>(
String fullUrl,
int ttlDays,
T Function(Map<String, dynamic> json) fromJson, {
// returns true if the data is still valid
required bool Function(T cachedData) extraValidityCheck,
required String Function(BuildContext context) localizedOfflineMessage,
VoidCallback? onRetry,
}) async {
final cacheManager = watch(restCacheManagerProvider(ttlDays));

final cachedFile = await cacheManager.getFileFromCache(fullUrl);
if (cachedFile != null && extraValidityCheck()) {
if (cachedFile != null) {
final cachedData = await cachedFile.file.readAsString();
final data = fromJson(
jsonDecode(cachedData) as Map<String, dynamic>,
);
return data;
if (extraValidityCheck(data)) {
return data;
}
}
final dio = watch(restClientProvider);
final response = await dio.get(fullUrl);
final sksData = fromJson(response.data as Map<String, dynamic>);

await cacheManager.putFile(
final response = await safeGetWatch(
fullUrl,
Uint8List.fromList(jsonEncode(response.data).codeUnits),
fileExtension: CacheManagerConfig.jsonExtesion,
localizedMessage: localizedOfflineMessage,
onRetry: onRetry,
);

final sksData = fromJson(response.data as Map<String, dynamic>);
if (extraValidityCheck(sksData)) {
await cacheManager.putFile(
fullUrl,
Uint8List.fromList(utf8.encode(jsonEncode(response.data))),
fileExtension: CacheManagerConfig.jsonExtesion,
);
}
return sksData;
}
}
2 changes: 1 addition & 1 deletion lib/api_base_rest/cache/cache_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ CacheManager restCacheManager(Ref ref, int ttlDays) {
}

abstract class CacheManagerConfig {
static const jsonExtesion = ".json";
static const jsonExtesion = "json";
}
50 changes: 50 additions & 0 deletions lib/api_base_rest/client/offline_error.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import "package:dio/dio.dart";
import "package:flutter/widgets.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";

import "dio_client.dart";

class RestFrameworkOfflineException implements Exception {
final String message;
final String Function(BuildContext context) localizedMessage;
final VoidCallback? onRetry;

RestFrameworkOfflineException({
required this.localizedMessage,
this.onRetry,
this.message = "No Internet connection",
});

@override
String toString() => "RestFrameworkOfflineException: $message";
}

extension DioSafeRequestsX on Ref {
Future<Response<T>> safeRequest<T>(
Future<Response<T>> Function() request, {
required String Function(BuildContext context) localizedMessage,
VoidCallback? onRetry,
}) async {
try {
return await request();
} on DioException catch (_) {
throw RestFrameworkOfflineException(
localizedMessage: localizedMessage,
onRetry: onRetry,
);
}
}

Future<Response<T>> safeGetWatch<T>(
String url, {
required String Function(BuildContext context) localizedMessage,
VoidCallback? onRetry,
}) async {
final dio = watch(restClientProvider);
return safeRequest(
() => dio.get(url),
localizedMessage: localizedMessage,
onRetry: onRetry,
);
}
}
6 changes: 4 additions & 2 deletions lib/features/sks-menu/data/models/dish_category_enum.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ enum DishCategory {
vegetarianDish,
meatDish,
sideDish,
drink;
drink,
technicalInfo;

@override
String toString() => name;
Expand All @@ -25,7 +26,8 @@ extension GetLocalizedNameX on DishCategory {
context.localize.sks_menu_vegetarian_dishes,
DishCategory.meatDish => context.localize.sks_menu_meat_dishes,
DishCategory.sideDish => context.localize.sks_menu_side_dishes,
DishCategory.drink => context.localize.sks_menu_drinks
DishCategory.drink => context.localize.sks_menu_drinks,
DishCategory.technicalInfo => context.localize.sks_menu_technical_info,
};
}
}
14 changes: 14 additions & 0 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 @@ -16,3 +17,16 @@ class SksMenuResponse with _$SksMenuResponse {
factory SksMenuResponse.fromJson(Map<String, dynamic> json) =>
simon-the-shark marked this conversation as resolved.
Show resolved Hide resolved
_$SksMenuResponseFromJson(json);
}

@freezed
class ExtendedSksMenuResponse with _$ExtendedSksMenuResponse {
const factory ExtendedSksMenuResponse({
required bool isMenuOnline,
required DateTime lastUpdate,
required IList<SksMenuDish> meals,
required IList<String> technicalInfos,
}) = _ExtendedSksMenuResponse;

factory ExtendedSksMenuResponse.fromJson(Map<String, dynamic> json) =>
_$ExtendedSksMenuResponseFromJson(json);
}
50 changes: 41 additions & 9 deletions lib/features/sks-menu/data/repository/sks_menu_repository.dart
Original file line number Diff line number Diff line change
@@ -1,20 +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/client/dio_client.dart";
import "../../../../api_base_rest/cache/cache.dart";
import "../../../../config/env.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<SksMenuResponse> getSksMenuData(Ref ref) async {
final mealsUrl = "${Env.sksUrl}/meals/current";
class SksMenuRepository extends _$SksMenuRepository {
static final _mealsUrl = "${Env.sksUrl}/meals/current";
static const _ttlDays = 1;

final dio = ref.read(restClientProvider);
final response = await dio.get(mealsUrl);
final SksMenuResponse sksMenuResponse =
SksMenuResponse.fromJson(response.data as Map<String, dynamic>);
Future<void> clearCache() async {
return ref.clearCache(_mealsUrl, _ttlDays);
}

return sksMenuResponse;
@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,
);
}
}
Loading
Loading