Fix bugs
This commit is contained in:
@@ -15,9 +15,11 @@ sealed class ConnectionFailure with _$ConnectionFailure, Failure {
|
||||
StackTrace? stackTrace,
|
||||
]) = UnexpectedConnectionFailure;
|
||||
|
||||
@With<ExpectedMeasuredFailure>()
|
||||
const factory ConnectionFailure.missingVpnPermission([String? message]) =
|
||||
MissingVpnPermission;
|
||||
|
||||
@With<ExpectedMeasuredFailure>()
|
||||
const factory ConnectionFailure.missingNotificationPermission([
|
||||
String? message,
|
||||
]) = MissingNotificationPermission;
|
||||
@@ -28,9 +30,9 @@ sealed class ConnectionFailure with _$ConnectionFailure, Failure {
|
||||
@override
|
||||
({String type, String? message}) present(TranslationsEn t) {
|
||||
return switch (this) {
|
||||
UnexpectedConnectionFailure(:final error) => (
|
||||
UnexpectedConnectionFailure() => (
|
||||
type: t.failure.connectivity.unexpected,
|
||||
message: t.mayPrintError(error),
|
||||
message: null,
|
||||
),
|
||||
MissingVpnPermission(:final message) => (
|
||||
type: t.failure.connectivity.missingVpnPermission,
|
||||
|
||||
@@ -14,7 +14,7 @@ sealed class CoreServiceFailure with _$CoreServiceFailure, Failure {
|
||||
StackTrace? stackTrace,
|
||||
) = UnexpectedCoreServiceFailure;
|
||||
|
||||
@With<ExpectedException>()
|
||||
@With<ExpectedFailure>()
|
||||
const factory CoreServiceFailure.serviceNotRunning([String? message]) =
|
||||
CoreServiceNotRunning;
|
||||
|
||||
@@ -22,6 +22,7 @@ sealed class CoreServiceFailure with _$CoreServiceFailure, Failure {
|
||||
String? message,
|
||||
]) = InvalidConfigOptions;
|
||||
|
||||
@With<ExpectedMeasuredFailure>()
|
||||
const factory CoreServiceFailure.invalidConfig([
|
||||
String? message,
|
||||
]) = InvalidConfig;
|
||||
@@ -51,9 +52,9 @@ sealed class CoreServiceFailure with _$CoreServiceFailure, Failure {
|
||||
@override
|
||||
({String type, String? message}) present(TranslationsEn t) {
|
||||
return switch (this) {
|
||||
UnexpectedCoreServiceFailure(:final error) => (
|
||||
UnexpectedCoreServiceFailure() => (
|
||||
type: t.failure.singbox.unexpected,
|
||||
message: t.mayPrintError(error),
|
||||
message: null,
|
||||
),
|
||||
CoreServiceNotRunning(:final message) => (
|
||||
type: t.failure.singbox.serviceNotRunning,
|
||||
|
||||
@@ -1,68 +1,73 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:hiddify/core/prefs/prefs.dart';
|
||||
|
||||
typedef PresentableError = ({String type, String? message});
|
||||
|
||||
mixin Failure {
|
||||
({String type, String? message}) present(TranslationsEn t);
|
||||
}
|
||||
|
||||
/// failures that are not expected to happen but depending on [error] type might not be relevant (eg network errors)
|
||||
mixin UnexpectedFailure {
|
||||
Object? get error;
|
||||
StackTrace? get stackTrace;
|
||||
}
|
||||
|
||||
/// failures that are expected to happen and should be handled by the app
|
||||
/// and should be logged, eg missing permissions
|
||||
mixin ExpectedMeasuredFailure {}
|
||||
|
||||
/// failures ignored by analytics service etc.
|
||||
mixin ExpectedException {}
|
||||
mixin ExpectedFailure {}
|
||||
|
||||
extension ErrorPresenter on TranslationsEn {
|
||||
String? _errorToMessage(Object error) {
|
||||
switch (error) {
|
||||
case Failure():
|
||||
final err = error.present(this);
|
||||
return err.type + (err.message == null ? "" : ": ${err.message}");
|
||||
case DioException():
|
||||
return error.present(this);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
PresentableError errorToPair(Object error) => switch (error) {
|
||||
UnexpectedFailure(error: final nestedErr?) => errorToPair(nestedErr),
|
||||
Failure() => error.present(this),
|
||||
DioException() => error.present(this),
|
||||
_ => (type: failure.unexpected, message: null),
|
||||
};
|
||||
|
||||
String printError(Object error) =>
|
||||
_errorToMessage(error) ?? failure.unexpected;
|
||||
|
||||
String? mayPrintError(Object? error) =>
|
||||
error != null ? _errorToMessage(error) : null;
|
||||
|
||||
({String type, String? message}) presentError(
|
||||
PresentableError 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);
|
||||
}
|
||||
final pair = errorToPair(error);
|
||||
if (action == null) return pair;
|
||||
return (
|
||||
type: action == null ? presentable.type : "$action: ${presentable.type}",
|
||||
message: presentable.message,
|
||||
type: action,
|
||||
message: pair.type + (pair.message == null ? "" : "\n${pair.message!}"),
|
||||
);
|
||||
}
|
||||
|
||||
String presentShortError(
|
||||
Object error, {
|
||||
String? action,
|
||||
}) {
|
||||
final pair = errorToPair(error);
|
||||
if (action == null) return pair.type;
|
||||
return "$action: ${pair.type}";
|
||||
}
|
||||
}
|
||||
|
||||
extension DioExceptionPresenter on DioException {
|
||||
String presentType(TranslationsEn t) => switch (type) {
|
||||
PresentableError present(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,
|
||||
(type: t.failure.connection.timeout, message: null),
|
||||
DioExceptionType.badCertificate => (
|
||||
type: t.failure.connection.badCertificate,
|
||||
message: message,
|
||||
),
|
||||
DioExceptionType.badResponse => (
|
||||
type: t.failure.connection.badResponse,
|
||||
message: message,
|
||||
),
|
||||
DioExceptionType.connectionError => (
|
||||
type: t.failure.connection.connectionError,
|
||||
message: message,
|
||||
),
|
||||
_ => (type: t.failure.connection.unexpected, message: message),
|
||||
};
|
||||
|
||||
String present(TranslationsEn t) {
|
||||
return presentType(t) + (message == null ? "" : "\n$message");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ sealed class Profile with _$Profile {
|
||||
required DateTime lastUpdate,
|
||||
}) = LocalProfile;
|
||||
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static RemoteProfile fromResponse(
|
||||
String url,
|
||||
Map<String, List<String>> headers,
|
||||
|
||||
@@ -16,18 +16,19 @@ sealed class ProfileFailure with _$ProfileFailure, Failure {
|
||||
|
||||
const factory ProfileFailure.notFound() = ProfileNotFoundFailure;
|
||||
|
||||
@With<ExpectedException>()
|
||||
@With<ExpectedFailure>()
|
||||
const factory ProfileFailure.invalidUrl() = ProfileInvalidUrlFailure;
|
||||
|
||||
@With<ExpectedFailure>()
|
||||
const factory ProfileFailure.invalidConfig([String? message]) =
|
||||
ProfileInvalidConfigFailure;
|
||||
|
||||
@override
|
||||
({String type, String? message}) present(TranslationsEn t) {
|
||||
return switch (this) {
|
||||
ProfileUnexpectedFailure(:final error) => (
|
||||
ProfileUnexpectedFailure() => (
|
||||
type: t.failure.profiles.unexpected,
|
||||
message: t.mayPrintError(error),
|
||||
message: null,
|
||||
),
|
||||
ProfileNotFoundFailure() => (
|
||||
type: t.failure.profiles.notFound,
|
||||
|
||||
Reference in New Issue
Block a user