From 81658a1c8670dfd2c3137460bac01088fa7fd52c Mon Sep 17 00:00:00 2001 From: problematicconsumer Date: Mon, 2 Oct 2023 23:57:20 +0330 Subject: [PATCH] Improve error handling and presentation --- assets/translations/strings.i18n.json | 9 ++++- assets/translations/strings_fa.i18n.json | 9 ++++- lib/domain/failures.dart | 37 +++++++++++++++++-- lib/features/common/common_controllers.dart | 4 +- lib/features/common/profile_tile.dart | 8 +++- .../notifier/profiles_update_notifier.dart | 20 ---------- 6 files changed, 58 insertions(+), 29 deletions(-) diff --git a/assets/translations/strings.i18n.json b/assets/translations/strings.i18n.json index 8614e94b..7da6be8c 100644 --- a/assets/translations/strings.i18n.json +++ b/assets/translations/strings.i18n.json @@ -63,7 +63,7 @@ "update": { "buttonTxt": "Update", "tooltip": "Update Profile", - "failureMsg": "Update Failed: ${reason}", + "failureMsg": "Update Failed", "successMsg": "Profile updated successfully" }, "edit": { @@ -241,6 +241,13 @@ "notFound": "Profile Not Found", "invalidUrl": "Invalid URL", "invalidConfig": "Invalid Configs" + }, + "connection": { + "unexpected": "Unexpected error", + "timeout": "Connection timeout", + "badCertificate": "Bad certificate", + "badResponse": "Bad response", + "connectionError": "Connection error" } } } diff --git a/assets/translations/strings_fa.i18n.json b/assets/translations/strings_fa.i18n.json index 1a1f29db..832cdb39 100644 --- a/assets/translations/strings_fa.i18n.json +++ b/assets/translations/strings_fa.i18n.json @@ -63,7 +63,7 @@ "update": { "buttonTxt": "بروزرسانی", "tooltip": "بروزرسانی پروفایل", - "failureMsg": "در بروزرسانی پروفایل خطایی رخ داد: ${reason}", + "failureMsg": "در بروزرسانی پروفایل خطایی رخ داد", "successMsg": "پروفایل با موفقیت بروزرسانی شد" }, "edit": { @@ -241,6 +241,13 @@ "notFound": "پروفایل یافت نشد", "invalidUrl": "لینک نامعتبر", "invalidConfig": "کانفیگ غیر معتبر" + }, + "connection": { + "unexpected": "خطای غیرمنتظره", + "timeout": "درخواست بیش از حد مجاز زمان برد", + "badCertificate": "خطای اعتبار سنجی", + "badResponse": "پاسخ نامعتبر", + "connectionError": "خطای اتصال" } } } diff --git a/lib/domain/failures.dart b/lib/domain/failures.dart index 4a2973d4..b8a8ea8e 100644 --- a/lib/domain/failures.dart +++ b/lib/domain/failures.dart @@ -15,7 +15,7 @@ extension ErrorPresenter on TranslationsEn { final err = error.present(this); return err.type + (err.message == null ? "" : ": ${err.message}"); case DioException(): - return error.toString(); + return error.present(this); default: return null; } @@ -27,8 +27,37 @@ extension ErrorPresenter on TranslationsEn { String? mayPrintError(Object? error) => error != null ? _errorToMessage(error) : null; - ({String type, String? message}) presentError(Object error) { - if (error case Failure()) return error.present(this); - return (type: failure.unexpected, message: null); + ({String type, String? message}) presentError( + Object error, { + String? action, + }) { + final ({String type, String? message}) presentable; + if (error case Failure()) { + presentable = error.present(this); + } else { + presentable = (type: failure.unexpected, message: null); + } + return ( + type: action == null ? presentable.type : "$action: ${presentable.type}", + message: presentable.message, + ); + } +} + +extension DioExceptionPresenter on DioException { + String presentType(TranslationsEn t) => switch (type) { + DioExceptionType.connectionTimeout || + DioExceptionType.sendTimeout || + DioExceptionType.receiveTimeout => + t.failure.connection.timeout, + DioExceptionType.badCertificate => t.failure.connection.badCertificate, + DioExceptionType.badResponse => t.failure.connection.badResponse, + DioExceptionType.connectionError => + t.failure.connection.connectionError, + _ => t.failure.unexpected, + }; + + String present(TranslationsEn t) { + return presentType(t) + (message == null ? "" : "\n$message"); } } diff --git a/lib/features/common/common_controllers.dart b/lib/features/common/common_controllers.dart index 8cfbb413..07ace23b 100644 --- a/lib/features/common/common_controllers.dart +++ b/lib/features/common/common_controllers.dart @@ -1,3 +1,4 @@ +import 'package:dartx/dartx.dart'; import 'package:hiddify/core/prefs/general_prefs.dart'; import 'package:hiddify/features/common/app_update_notifier.dart'; import 'package:hiddify/features/common/connectivity/connectivity_controller.dart'; @@ -19,7 +20,8 @@ void commonControllers(CommonControllersRef ref) { introCompletedProvider, (_, completed) async { if (completed) { - await ref.read(cronServiceProvider).startScheduler(); + await Future.delayed(5.seconds) + .then((_) async => ref.read(cronServiceProvider).startScheduler()); } }, fireImmediately: true, diff --git a/lib/features/common/profile_tile.dart b/lib/features/common/profile_tile.dart index 805134ce..564b99b3 100644 --- a/lib/features/common/profile_tile.dart +++ b/lib/features/common/profile_tile.dart @@ -176,7 +176,9 @@ class ProfileActionButton extends HookConsumerWidget { final updateProfileMutation = useMutation( initialOnFailure: (err) { - CustomAlertDialog.fromErr(t.presentError(err)).show(context); + CustomAlertDialog.fromErr( + t.presentError(err, action: t.profile.update.failureMsg), + ).show(context); }, initialOnSuccess: () => CustomToast.success(t.profile.update.successMsg).show(context), @@ -241,7 +243,9 @@ class ProfileActionsMenu extends HookConsumerWidget { final updateProfileMutation = useMutation( initialOnFailure: (err) { - CustomAlertDialog.fromErr(t.presentError(err)).show(context); + CustomAlertDialog.fromErr( + t.presentError(err, action: t.profile.update.failureMsg), + ).show(context); }, initialOnSuccess: () => CustomToast.success(t.profile.update.successMsg).show(context), diff --git a/lib/features/profiles/notifier/profiles_update_notifier.dart b/lib/features/profiles/notifier/profiles_update_notifier.dart index 5b3d2da7..cccc9588 100644 --- a/lib/features/profiles/notifier/profiles_update_notifier.dart +++ b/lib/features/profiles/notifier/profiles_update_notifier.dart @@ -1,9 +1,5 @@ -import 'package:flutter/material.dart'; import 'package:fpdart/fpdart.dart'; -import 'package:hiddify/core/core_providers.dart'; -import 'package:hiddify/core/router/router.dart'; import 'package:hiddify/data/data_providers.dart'; -import 'package:hiddify/domain/failures.dart'; import 'package:hiddify/domain/profiles/profiles.dart'; import 'package:hiddify/services/service_providers.dart'; import 'package:hiddify/utils/utils.dart'; @@ -20,22 +16,6 @@ typedef ProfileUpdateResult = ({ class ProfilesUpdateNotifier extends _$ProfilesUpdateNotifier with AppLogger { @override Stream build() { - ref.listenSelf( - (previous, next) { - if (next case AsyncData(value: final result)) { - final t = ref.read(translationsProvider); - final context = rootNavigatorKey.currentContext; - if (context == null || !context.mounted) return; - SnackBar(content: Text(t.profile.update.successMsg)); - switch (result.failureOrSuccess) { - case Right(): - CustomToast.success(t.profile.update.successMsg).show(context); - case Left(value: final err): - CustomToast.error(t.printError(err)).show(context); - } - } - }, - ); _schedule(); return const Stream.empty(); }