Improve error handling and presentation
This commit is contained in:
@@ -63,7 +63,7 @@
|
|||||||
"update": {
|
"update": {
|
||||||
"buttonTxt": "Update",
|
"buttonTxt": "Update",
|
||||||
"tooltip": "Update Profile",
|
"tooltip": "Update Profile",
|
||||||
"failureMsg": "Update Failed: ${reason}",
|
"failureMsg": "Update Failed",
|
||||||
"successMsg": "Profile updated successfully"
|
"successMsg": "Profile updated successfully"
|
||||||
},
|
},
|
||||||
"edit": {
|
"edit": {
|
||||||
@@ -241,6 +241,13 @@
|
|||||||
"notFound": "Profile Not Found",
|
"notFound": "Profile Not Found",
|
||||||
"invalidUrl": "Invalid URL",
|
"invalidUrl": "Invalid URL",
|
||||||
"invalidConfig": "Invalid Configs"
|
"invalidConfig": "Invalid Configs"
|
||||||
|
},
|
||||||
|
"connection": {
|
||||||
|
"unexpected": "Unexpected error",
|
||||||
|
"timeout": "Connection timeout",
|
||||||
|
"badCertificate": "Bad certificate",
|
||||||
|
"badResponse": "Bad response",
|
||||||
|
"connectionError": "Connection error"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,7 +63,7 @@
|
|||||||
"update": {
|
"update": {
|
||||||
"buttonTxt": "بروزرسانی",
|
"buttonTxt": "بروزرسانی",
|
||||||
"tooltip": "بروزرسانی پروفایل",
|
"tooltip": "بروزرسانی پروفایل",
|
||||||
"failureMsg": "در بروزرسانی پروفایل خطایی رخ داد: ${reason}",
|
"failureMsg": "در بروزرسانی پروفایل خطایی رخ داد",
|
||||||
"successMsg": "پروفایل با موفقیت بروزرسانی شد"
|
"successMsg": "پروفایل با موفقیت بروزرسانی شد"
|
||||||
},
|
},
|
||||||
"edit": {
|
"edit": {
|
||||||
@@ -241,6 +241,13 @@
|
|||||||
"notFound": "پروفایل یافت نشد",
|
"notFound": "پروفایل یافت نشد",
|
||||||
"invalidUrl": "لینک نامعتبر",
|
"invalidUrl": "لینک نامعتبر",
|
||||||
"invalidConfig": "کانفیگ غیر معتبر"
|
"invalidConfig": "کانفیگ غیر معتبر"
|
||||||
|
},
|
||||||
|
"connection": {
|
||||||
|
"unexpected": "خطای غیرمنتظره",
|
||||||
|
"timeout": "درخواست بیش از حد مجاز زمان برد",
|
||||||
|
"badCertificate": "خطای اعتبار سنجی",
|
||||||
|
"badResponse": "پاسخ نامعتبر",
|
||||||
|
"connectionError": "خطای اتصال"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ extension ErrorPresenter on TranslationsEn {
|
|||||||
final err = error.present(this);
|
final err = error.present(this);
|
||||||
return err.type + (err.message == null ? "" : ": ${err.message}");
|
return err.type + (err.message == null ? "" : ": ${err.message}");
|
||||||
case DioException():
|
case DioException():
|
||||||
return error.toString();
|
return error.present(this);
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -27,8 +27,37 @@ extension ErrorPresenter on TranslationsEn {
|
|||||||
String? mayPrintError(Object? error) =>
|
String? mayPrintError(Object? error) =>
|
||||||
error != null ? _errorToMessage(error) : null;
|
error != null ? _errorToMessage(error) : null;
|
||||||
|
|
||||||
({String type, String? message}) presentError(Object error) {
|
({String type, String? message}) presentError(
|
||||||
if (error case Failure()) return error.present(this);
|
Object error, {
|
||||||
return (type: failure.unexpected, message: null);
|
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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:dartx/dartx.dart';
|
||||||
import 'package:hiddify/core/prefs/general_prefs.dart';
|
import 'package:hiddify/core/prefs/general_prefs.dart';
|
||||||
import 'package:hiddify/features/common/app_update_notifier.dart';
|
import 'package:hiddify/features/common/app_update_notifier.dart';
|
||||||
import 'package:hiddify/features/common/connectivity/connectivity_controller.dart';
|
import 'package:hiddify/features/common/connectivity/connectivity_controller.dart';
|
||||||
@@ -19,7 +20,8 @@ void commonControllers(CommonControllersRef ref) {
|
|||||||
introCompletedProvider,
|
introCompletedProvider,
|
||||||
(_, completed) async {
|
(_, completed) async {
|
||||||
if (completed) {
|
if (completed) {
|
||||||
await ref.read(cronServiceProvider).startScheduler();
|
await Future.delayed(5.seconds)
|
||||||
|
.then((_) async => ref.read(cronServiceProvider).startScheduler());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
fireImmediately: true,
|
fireImmediately: true,
|
||||||
|
|||||||
@@ -176,7 +176,9 @@ class ProfileActionButton extends HookConsumerWidget {
|
|||||||
|
|
||||||
final updateProfileMutation = useMutation(
|
final updateProfileMutation = useMutation(
|
||||||
initialOnFailure: (err) {
|
initialOnFailure: (err) {
|
||||||
CustomAlertDialog.fromErr(t.presentError(err)).show(context);
|
CustomAlertDialog.fromErr(
|
||||||
|
t.presentError(err, action: t.profile.update.failureMsg),
|
||||||
|
).show(context);
|
||||||
},
|
},
|
||||||
initialOnSuccess: () =>
|
initialOnSuccess: () =>
|
||||||
CustomToast.success(t.profile.update.successMsg).show(context),
|
CustomToast.success(t.profile.update.successMsg).show(context),
|
||||||
@@ -241,7 +243,9 @@ class ProfileActionsMenu extends HookConsumerWidget {
|
|||||||
|
|
||||||
final updateProfileMutation = useMutation(
|
final updateProfileMutation = useMutation(
|
||||||
initialOnFailure: (err) {
|
initialOnFailure: (err) {
|
||||||
CustomAlertDialog.fromErr(t.presentError(err)).show(context);
|
CustomAlertDialog.fromErr(
|
||||||
|
t.presentError(err, action: t.profile.update.failureMsg),
|
||||||
|
).show(context);
|
||||||
},
|
},
|
||||||
initialOnSuccess: () =>
|
initialOnSuccess: () =>
|
||||||
CustomToast.success(t.profile.update.successMsg).show(context),
|
CustomToast.success(t.profile.update.successMsg).show(context),
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:fpdart/fpdart.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/data/data_providers.dart';
|
||||||
import 'package:hiddify/domain/failures.dart';
|
|
||||||
import 'package:hiddify/domain/profiles/profiles.dart';
|
import 'package:hiddify/domain/profiles/profiles.dart';
|
||||||
import 'package:hiddify/services/service_providers.dart';
|
import 'package:hiddify/services/service_providers.dart';
|
||||||
import 'package:hiddify/utils/utils.dart';
|
import 'package:hiddify/utils/utils.dart';
|
||||||
@@ -20,22 +16,6 @@ typedef ProfileUpdateResult = ({
|
|||||||
class ProfilesUpdateNotifier extends _$ProfilesUpdateNotifier with AppLogger {
|
class ProfilesUpdateNotifier extends _$ProfilesUpdateNotifier with AppLogger {
|
||||||
@override
|
@override
|
||||||
Stream<ProfileUpdateResult> build() {
|
Stream<ProfileUpdateResult> 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();
|
_schedule();
|
||||||
return const Stream.empty();
|
return const Stream.empty();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user