Skip to content

Commit

Permalink
feat(sks-menu): improve error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
simon-the-shark committed Dec 9, 2024
1 parent 3736a3d commit 2e33cb2
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 28 deletions.
12 changes: 9 additions & 3 deletions lib/api_base_rest/cache/cache.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
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 {
Expand All @@ -13,6 +14,8 @@ extension DataCachingX on Ref {
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));

Expand All @@ -26,8 +29,11 @@ extension DataCachingX on Ref {
return data;
}
}
final dio = watch(restClientProvider);
final response = await dio.get(fullUrl);
final response = await safeGetWatch(
fullUrl,
localizedMessage: localizedOfflineMessage,
onRetry: onRetry,
);
final sksData = fromJson(response.data as Map<String, dynamic>);
if (extraValidityCheck(sksData)) {
await cacheManager.putFile(
Expand Down
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,
);
}
}
13 changes: 9 additions & 4 deletions lib/features/sks-menu/data/repository/sks_menu_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ 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 "../models/dish_category_enum.dart";
import "../models/sks_menu_response.dart";
Expand All @@ -19,7 +20,13 @@ Future<ExtendedSksMenuResponse> getSksMenuData(Ref ref) async {
ttlDays,
SksMenuResponse.fromJson,
extraValidityCheck: (data) =>
data.isMenuOnline && DateTime.now().date == data.lastUpdate.date,
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
Expand All @@ -35,8 +42,6 @@ Future<ExtendedSksMenuResponse> getSksMenuData(Ref ref) async {
isMenuOnline: sksMenuResponse.isMenuOnline,
lastUpdate: sksMenuResponse.lastUpdate,
meals: trueMeals,
technicalInfos: [
...technicalInfos,
],
technicalInfos: technicalInfos,
);
}
33 changes: 13 additions & 20 deletions lib/features/sks-menu/presentation/sks_menu_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import "package:auto_route/annotations.dart";
import "package:flutter/material.dart";
import "package:flutter_hooks/flutter_hooks.dart";
import "package:hooks_riverpod/hooks_riverpod.dart";
import "package:logger/logger.dart";
import "package:lottie/lottie.dart";

import "../../../../theme/app_theme.dart";
import "../../../config/ui_config.dart";
import "../../../gen/assets.gen.dart";
import "../../../utils/context_extensions.dart";
import "../../../widgets/detail_views/detail_view_app_bar.dart";
import "../../../widgets/my_error_widget.dart";
import "../../../widgets/my_text_button.dart";
import "../../sks_people_live/presentation/widgets/sks_user_data_button.dart";
import "../data/models/sks_menu_response.dart";
Expand All @@ -34,7 +34,7 @@ class SksMenuView extends HookConsumerWidget {
return asyncSksMenuData.when(
data: (sksMenuData) {
if (!sksMenuData.isMenuOnline && !isLastMenuButtonClicked.value) {
return _SKSMenuLottieAnimation(
return _SKSMenuUnavailableAnimation(
onShowLastMenuTap: () {
isLastMenuButtonClicked.value = true;
},
Expand All @@ -45,7 +45,14 @@ class SksMenuView extends HookConsumerWidget {
isLastMenuButtonClicked: isLastMenuButtonClicked.value,
);
},
error: (error, stackTrace) => _SKSMenuLottieAnimation(error: error),
error: (error, stackTrace) => Scaffold(
appBar: DetailViewAppBar(
actions: const [
SksUserDataButton(),
],
),
body: MyErrorWidget(error),
),
loading: () => const Scaffold(
body: Center(
child: SksMenuViewLoading(),
Expand All @@ -67,7 +74,7 @@ class _SksMenuView extends StatelessWidget {
@override
Widget build(BuildContext context) {
if (!isLastMenuButtonClicked && !sksMenuData.isMenuOnline) {
return const _SKSMenuLottieAnimation();
return const _SKSMenuUnavailableAnimation();
}
return Scaffold(
appBar: DetailViewAppBar(
Expand Down Expand Up @@ -102,22 +109,14 @@ class _SksMenuView extends StatelessWidget {
}
}

class _SKSMenuLottieAnimation extends HookWidget {
const _SKSMenuLottieAnimation({
this.error,
this.onShowLastMenuTap,
});
class _SKSMenuUnavailableAnimation extends HookWidget {
const _SKSMenuUnavailableAnimation({this.onShowLastMenuTap});

final Object? error;
final VoidCallback? onShowLastMenuTap;

@override
Widget build(BuildContext context) {
final isAnimationCompleted = useState(false);

if (error != null) {
Logger().e(error.toString());
}
final animationSize = MediaQuery.sizeOf(context).width * 0.6;

return Scaffold(
Expand Down Expand Up @@ -167,12 +166,6 @@ class _SKSMenuLottieAnimation extends HookWidget {
),
textAlign: TextAlign.center,
),
if (error != null)
Text(
error.toString(),
style: context.textTheme.titleGrey,
textAlign: TextAlign.center,
),
if (onShowLastMenuTap != null)
Padding(
padding: const EdgeInsets.only(top: 12),
Expand Down
12 changes: 11 additions & 1 deletion lib/l10n/app_pl.arb
Original file line number Diff line number Diff line change
Expand Up @@ -191,5 +191,15 @@
"push_notifications_dialog_info": "Obecnie nie korzystamy z powiadomień push, ale planujemy dodać je w przyszłości. Możesz wyrazić na nie zgodę już teraz.",
"sks_menu_technical_info": "KOMUNIKAT",
"sks_note": "UWAGA",
"sks_menu_you_see_last_menu": "Aktualne menu SKS jest niedostępne. Przeglądasz ostatnio dostępną wersję."
"sks_menu_you_see_last_menu": "Aktualne menu SKS jest niedostępne. Przeglądasz ostatnio dostępną wersję.",
"my_offline_error_message": "Wystąpił błąd podczas pobierania danych dotyczących {data_type}",
"@my_offline_error_message": {
"description": "An error message with a single parameter",
"placeholders": {
"data_type": {
"type": "String",
"example": "wydziałów"
}
}
}
}
7 changes: 7 additions & 0 deletions lib/widgets/my_error_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import "package:logger/logger.dart";
import "package:lottie/lottie.dart";

import "../api_base/query_adapter.dart";
import "../api_base_rest/client/offline_error.dart";
import "../config/ui_config.dart";
import "../features/offline_messages/widgets/general_offline_message.dart";
import "../features/offline_messages/widgets/grapgql_offline_message.dart";
import "../features/parkings_view/api_client/iparking_commands.dart";
import "../features/parkings_view/widgets/offline_parkings_view.dart";
Expand All @@ -32,6 +34,11 @@ class MyErrorWidget extends HookWidget {
return switch (error) {
ParkingsOfflineException() => const OfflineParkingsView(),
GqlOfflineException(:final ttlKey) => OfflineGraphQLMessage(ttlKey),
RestFrameworkOfflineException(:final localizedMessage, :final onRetry) =>
OfflineMessage(
localizedMessage(context),
onRefresh: onRetry,
),
_ => Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
Expand Down

0 comments on commit 2e33cb2

Please sign in to comment.