From 99a431e702d8fb17e4293e1e99c8c15dc0f7ce81 Mon Sep 17 00:00:00 2001 From: jld3103 Date: Tue, 26 Sep 2023 17:32:51 +0200 Subject: [PATCH] refactor(neon): Abstract away custom exceptions Signed-off-by: jld3103 --- packages/neon/neon/lib/l10n/en.arb | 1 - .../neon/neon/lib/l10n/localizations.dart | 6 -- .../neon/neon/lib/l10n/localizations_en.dart | 3 - .../lib/src/pages/login_check_account.dart | 6 +- .../src/pages/login_check_server_status.dart | 2 +- .../neon/lib/src/pages/login_qr_code.dart | 15 +++- .../neon/neon/lib/src/platform/android.dart | 2 +- .../neon/neon/lib/src/utils/exceptions.dart | 32 ++++++-- packages/neon/neon/lib/src/widgets/error.dart | 73 ++++++------------- packages/neon/neon/lib/widgets.dart | 2 +- packages/neon/neon_files/lib/blocs/files.dart | 12 ++- packages/neon/neon_files/lib/l10n/en.arb | 1 + .../neon_files/lib/l10n/localizations.dart | 6 ++ .../neon_files/lib/l10n/localizations_en.dart | 3 + 14 files changed, 91 insertions(+), 73 deletions(-) diff --git a/packages/neon/neon/lib/l10n/en.arb b/packages/neon/neon/lib/l10n/en.arb index a5a715f9..5bd46a6c 100644 --- a/packages/neon/neon/lib/l10n/en.arb +++ b/packages/neon/neon/lib/l10n/en.arb @@ -58,7 +58,6 @@ } } }, - "errorUnableToOpenFile": "Unable to open the file", "errorUnsupportedAppVersions": "Sorry, the version of the following apps on your Nextcloud instance are not supported. \n {names} \n Please contact your administrator to resolve the issues.", "@errorUnsupportedAppVersions" : { "placeholders": { diff --git a/packages/neon/neon/lib/l10n/localizations.dart b/packages/neon/neon/lib/l10n/localizations.dart index f95d6212..d287e80a 100644 --- a/packages/neon/neon/lib/l10n/localizations.dart +++ b/packages/neon/neon/lib/l10n/localizations.dart @@ -239,12 +239,6 @@ abstract class AppLocalizations { /// **'Permission for {name} is missing'** String errorMissingPermission(String name); - /// No description provided for @errorUnableToOpenFile. - /// - /// In en, this message translates to: - /// **'Unable to open the file'** - String get errorUnableToOpenFile; - /// No description provided for @errorUnsupportedAppVersions. /// /// In en, this message translates to: diff --git a/packages/neon/neon/lib/l10n/localizations_en.dart b/packages/neon/neon/lib/l10n/localizations_en.dart index 071828f9..45a8ccc8 100644 --- a/packages/neon/neon/lib/l10n/localizations_en.dart +++ b/packages/neon/neon/lib/l10n/localizations_en.dart @@ -106,9 +106,6 @@ class AppLocalizationsEn extends AppLocalizations { return 'Permission for $name is missing'; } - @override - String get errorUnableToOpenFile => 'Unable to open the file'; - @override String errorUnsupportedAppVersions(String names) { return 'Sorry, the version of the following apps on your Nextcloud instance are not supported. \n $names \n Please contact your administrator to resolve the issues.'; diff --git a/packages/neon/neon/lib/src/pages/login_check_account.dart b/packages/neon/neon/lib/src/pages/login_check_account.dart index 0cb672f4..08a9d0bf 100644 --- a/packages/neon/neon/lib/src/pages/login_check_account.dart +++ b/packages/neon/neon/lib/src/pages/login_check_account.dart @@ -68,11 +68,11 @@ class _LoginCheckAccountPageState extends State { if (state.hasError) ...[ Builder( builder: (final context) { - final details = NeonError.getDetails(context, state.error); + final details = NeonError.getDetails(state.error); return NeonValidationTile( title: details.isUnauthorized ? AppLocalizations.of(context).errorCredentialsForAccountNoLongerMatch - : details.text, + : details.getText(context), state: ValidationState.failure, ); }, @@ -91,7 +91,7 @@ class _LoginCheckAccountPageState extends State { const HomeRoute().go(context); } : () { - if (state.hasError && NeonError.getDetails(context, state.error).isUnauthorized) { + if (state.hasError && NeonError.getDetails(state.error).isUnauthorized) { Navigator.pop(context); return; } diff --git a/packages/neon/neon/lib/src/pages/login_check_server_status.dart b/packages/neon/neon/lib/src/pages/login_check_server_status.dart index 5c59c2ea..c9af2a0c 100644 --- a/packages/neon/neon/lib/src/pages/login_check_server_status.dart +++ b/packages/neon/neon/lib/src/pages/login_check_server_status.dart @@ -67,7 +67,7 @@ class _LoginCheckServerStatusPageState extends State children: [ if (state.hasError) ...[ NeonValidationTile( - title: NeonError.getDetails(context, state.error).text, + title: NeonError.getDetails(state.error).getText(context), state: ValidationState.failure, ), ], diff --git a/packages/neon/neon/lib/src/pages/login_qr_code.dart b/packages/neon/neon/lib/src/pages/login_qr_code.dart index 27bd88ba..d4293427 100644 --- a/packages/neon/neon/lib/src/pages/login_qr_code.dart +++ b/packages/neon/neon/lib/src/pages/login_qr_code.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_zxing/flutter_zxing.dart'; import 'package:meta/meta.dart'; +import 'package:neon/l10n/localizations.dart'; import 'package:neon/src/models/account.dart'; import 'package:neon/src/router.dart'; import 'package:neon/src/utils/exceptions.dart'; @@ -35,11 +36,11 @@ class _LoginQRcodePageState extends State { try { url = code.text; if (url == null) { - throw InvalidQRcodeException(); + throw const InvalidQRcodeException(); } final match = LoginQRcode.tryParse(url); if (match == null) { - throw InvalidQRcodeException(); + throw const InvalidQRcodeException(); } LoginCheckServerStatusRoute.withCredentials( @@ -60,3 +61,13 @@ class _LoginQRcodePageState extends State { ), ); } + +@immutable +class InvalidQRcodeException extends NeonException { + const InvalidQRcodeException(); + + @override + NeonExceptionDetails get details => NeonExceptionDetails( + getText: (final context) => AppLocalizations.of(context).errorInvalidQRcode, + ); +} diff --git a/packages/neon/neon/lib/src/platform/android.dart b/packages/neon/neon/lib/src/platform/android.dart index 253ec6bd..93c27089 100644 --- a/packages/neon/neon/lib/src/platform/android.dart +++ b/packages/neon/neon/lib/src/platform/android.dart @@ -31,7 +31,7 @@ class AndroidNeonPlatform implements NeonPlatform { @override Future get userAccessibleAppDataPath async { if (!await Permission.storage.request().isGranted) { - throw MissingPermissionException(Permission.storage); + throw const MissingPermissionException(Permission.storage); } return p.join((await getExternalStorageDirectory())!.path); diff --git a/packages/neon/neon/lib/src/utils/exceptions.dart b/packages/neon/neon/lib/src/utils/exceptions.dart index 394627e4..6fb6c3c9 100644 --- a/packages/neon/neon/lib/src/utils/exceptions.dart +++ b/packages/neon/neon/lib/src/utils/exceptions.dart @@ -1,11 +1,33 @@ +import 'package:flutter/material.dart'; +import 'package:neon/l10n/localizations.dart'; +import 'package:neon/src/models/label_builder.dart'; import 'package:permission_handler/permission_handler.dart'; -class MissingPermissionException implements Exception { - MissingPermissionException(this.permission); +class NeonExceptionDetails { + const NeonExceptionDetails({ + required this.getText, + this.isUnauthorized = false, + }); - final Permission permission; + final LabelBuilder getText; + final bool isUnauthorized; +} + +@immutable +abstract class NeonException implements Exception { + const NeonException(); + + NeonExceptionDetails get details; } -class UnableToOpenFileException implements Exception {} +class MissingPermissionException extends NeonException { + const MissingPermissionException(this.permission); -class InvalidQRcodeException implements Exception {} + final Permission permission; + + @override + NeonExceptionDetails get details => NeonExceptionDetails( + getText: (final context) => + AppLocalizations.of(context).errorMissingPermission(permission.toString().split('.')[1]), + ); +} diff --git a/packages/neon/neon/lib/src/widgets/error.dart b/packages/neon/neon/lib/src/widgets/error.dart index 5fe1d0ba..fe445ba6 100644 --- a/packages/neon/neon/lib/src/widgets/error.dart +++ b/packages/neon/neon/lib/src/widgets/error.dart @@ -28,11 +28,11 @@ class NeonError extends StatelessWidget { final Color? color; static void showSnackbar(final BuildContext context, final dynamic error) { - final details = getDetails(context, error); + final details = getDetails(error); ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: Text(details.text), + content: Text(details.getText(context)), action: details.isUnauthorized ? SnackBarAction( label: AppLocalizations.of(context).loginAgain, @@ -49,7 +49,7 @@ class NeonError extends StatelessWidget { return const SizedBox(); } - final details = getDetails(context, error); + final details = getDetails(error); final color = this.color ?? Theme.of(context).colorScheme.error; final errorIcon = Icon( @@ -65,7 +65,7 @@ class NeonError extends StatelessWidget { if (onlyIcon) { return Semantics( - tooltip: details.text, + tooltip: details.getText(context), child: IconButton( icon: errorIcon, padding: EdgeInsets.zero, @@ -93,7 +93,7 @@ class NeonError extends StatelessWidget { ), Flexible( child: Text( - details.text, + details.getText(context), style: TextStyle( color: color, ), @@ -111,78 +111,64 @@ class NeonError extends StatelessWidget { } @internal - static ExceptionDetails getDetails(final BuildContext context, final dynamic error) { + static NeonExceptionDetails getDetails(final dynamic error) { if (error is String) { - return ExceptionDetails( - text: error, + return NeonExceptionDetails( + getText: (final _) => error, ); } - if (error is MissingPermissionException) { - return ExceptionDetails( - text: AppLocalizations.of(context).errorMissingPermission(error.permission.toString().split('.')[1]), - ); - } - - if (error is UnableToOpenFileException) { - return ExceptionDetails( - text: AppLocalizations.of(context).errorUnableToOpenFile, - ); - } - - if (error is InvalidQRcodeException) { - return ExceptionDetails( - text: AppLocalizations.of(context).errorInvalidQRcode, - ); + if (error is NeonException) { + return error.details; } if (error is DynamiteApiException) { if (error.statusCode == 401) { - return ExceptionDetails( - text: AppLocalizations.of(context).errorCredentialsForAccountNoLongerMatch, + return NeonExceptionDetails( + getText: (final context) => AppLocalizations.of(context).errorCredentialsForAccountNoLongerMatch, isUnauthorized: true, ); } if (error.statusCode >= 500 && error.statusCode <= 599) { - return ExceptionDetails( - text: AppLocalizations.of(context).errorServerHadAProblemProcessingYourRequest, + return NeonExceptionDetails( + getText: (final context) => AppLocalizations.of(context).errorServerHadAProblemProcessingYourRequest, ); } } if (error is SocketException) { - return ExceptionDetails( - text: error.address != null + return NeonExceptionDetails( + getText: (final context) => error.address != null ? AppLocalizations.of(context).errorUnableToReachServerAt(error.address!.host) : AppLocalizations.of(context).errorUnableToReachServer, ); } if (error is ClientException) { - return ExceptionDetails( - text: error.uri != null + return NeonExceptionDetails( + getText: (final context) => error.uri != null ? AppLocalizations.of(context).errorUnableToReachServerAt(error.uri!.host) : AppLocalizations.of(context).errorUnableToReachServer, ); } if (error is HttpException) { - return ExceptionDetails( - text: error.uri != null + return NeonExceptionDetails( + getText: (final context) => error.uri != null ? AppLocalizations.of(context).errorUnableToReachServerAt(error.uri!.host) : AppLocalizations.of(context).errorUnableToReachServer, ); } if (error is TimeoutException) { - return ExceptionDetails( - text: AppLocalizations.of(context).errorConnectionTimedOut, + return NeonExceptionDetails( + getText: (final context) => AppLocalizations.of(context).errorConnectionTimedOut, ); } - return ExceptionDetails( - text: AppLocalizations.of(context).errorSomethingWentWrongTryAgainLater, + return NeonExceptionDetails( + getText: (final context) => AppLocalizations.of(context).errorSomethingWentWrongTryAgainLater, ); } @@ -194,14 +180,3 @@ class NeonError extends StatelessWidget { ); } } - -@internal -class ExceptionDetails { - ExceptionDetails({ - required this.text, - this.isUnauthorized = false, - }); - - final String text; - final bool isUnauthorized; -} diff --git a/packages/neon/neon/lib/widgets.dart b/packages/neon/neon/lib/widgets.dart index bbd2556b..34bee9ed 100644 --- a/packages/neon/neon/lib/widgets.dart +++ b/packages/neon/neon/lib/widgets.dart @@ -1,6 +1,6 @@ export 'package:neon/src/widgets/cached_image.dart'; export 'package:neon/src/widgets/dialog.dart'; -export 'package:neon/src/widgets/error.dart' hide ExceptionDetails; +export 'package:neon/src/widgets/error.dart'; export 'package:neon/src/widgets/image_wrapper.dart'; export 'package:neon/src/widgets/linear_progress_indicator.dart'; export 'package:neon/src/widgets/list_view.dart'; diff --git a/packages/neon/neon_files/lib/blocs/files.dart b/packages/neon/neon_files/lib/blocs/files.dart index c61f730d..b8aae030 100644 --- a/packages/neon/neon_files/lib/blocs/files.dart +++ b/packages/neon/neon_files/lib/blocs/files.dart @@ -95,7 +95,7 @@ class FilesBloc extends InteractiveBloc implements FilesBlocEvents, FilesBlocSta } final result = await OpenFile.open(file.path, type: mimeType); if (result.type != ResultType.done) { - throw UnableToOpenFileException(); + throw const UnableToOpenFileException(); } }, disableTimeout: true, @@ -194,3 +194,13 @@ class FilesBloc extends InteractiveBloc implements FilesBlocEvents, FilesBlocSta _uploadQueue.parallel = options.uploadQueueParallelism.value; } } + +@immutable +class UnableToOpenFileException extends NeonException { + const UnableToOpenFileException(); + + @override + NeonExceptionDetails get details => NeonExceptionDetails( + getText: (final context) => AppLocalizations.of(context).errorUnableToOpenFile, + ); +} diff --git a/packages/neon/neon_files/lib/l10n/en.arb b/packages/neon/neon_files/lib/l10n/en.arb index 07661702..8a347043 100644 --- a/packages/neon/neon_files/lib/l10n/en.arb +++ b/packages/neon/neon_files/lib/l10n/en.arb @@ -7,6 +7,7 @@ "actionMove": "Move", "actionCopy": "Copy", "actionSync": "Sync", + "errorUnableToOpenFile": "Unable to open the file", "general": "General", "goToPath": "Go to /{path}", "@goToPath": { diff --git a/packages/neon/neon_files/lib/l10n/localizations.dart b/packages/neon/neon_files/lib/l10n/localizations.dart index f0372a5e..1914ad2c 100644 --- a/packages/neon/neon_files/lib/l10n/localizations.dart +++ b/packages/neon/neon_files/lib/l10n/localizations.dart @@ -131,6 +131,12 @@ abstract class AppLocalizations { /// **'Sync'** String get actionSync; + /// No description provided for @errorUnableToOpenFile. + /// + /// In en, this message translates to: + /// **'Unable to open the file'** + String get errorUnableToOpenFile; + /// No description provided for @general. /// /// In en, this message translates to: diff --git a/packages/neon/neon_files/lib/l10n/localizations_en.dart b/packages/neon/neon_files/lib/l10n/localizations_en.dart index 622b0ae2..adb1bd35 100644 --- a/packages/neon/neon_files/lib/l10n/localizations_en.dart +++ b/packages/neon/neon_files/lib/l10n/localizations_en.dart @@ -25,6 +25,9 @@ class AppLocalizationsEn extends AppLocalizations { @override String get actionSync => 'Sync'; + @override + String get errorUnableToOpenFile => 'Unable to open the file'; + @override String get general => 'General';