Browse Source

refactor(neon): Abstract away custom exceptions

Signed-off-by: jld3103 <jld3103yt@gmail.com>
pull/852/head
jld3103 1 year ago
parent
commit
99a431e702
No known key found for this signature in database
GPG Key ID: 9062417B9E8EB7B3
  1. 1
      packages/neon/neon/lib/l10n/en.arb
  2. 6
      packages/neon/neon/lib/l10n/localizations.dart
  3. 3
      packages/neon/neon/lib/l10n/localizations_en.dart
  4. 6
      packages/neon/neon/lib/src/pages/login_check_account.dart
  5. 2
      packages/neon/neon/lib/src/pages/login_check_server_status.dart
  6. 15
      packages/neon/neon/lib/src/pages/login_qr_code.dart
  7. 2
      packages/neon/neon/lib/src/platform/android.dart
  8. 32
      packages/neon/neon/lib/src/utils/exceptions.dart
  9. 73
      packages/neon/neon/lib/src/widgets/error.dart
  10. 2
      packages/neon/neon/lib/widgets.dart
  11. 12
      packages/neon/neon_files/lib/blocs/files.dart
  12. 1
      packages/neon/neon_files/lib/l10n/en.arb
  13. 6
      packages/neon/neon_files/lib/l10n/localizations.dart
  14. 3
      packages/neon/neon_files/lib/l10n/localizations_en.dart

1
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": "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" : { "@errorUnsupportedAppVersions" : {
"placeholders": { "placeholders": {

6
packages/neon/neon/lib/l10n/localizations.dart

@ -239,12 +239,6 @@ abstract class AppLocalizations {
/// **'Permission for {name} is missing'** /// **'Permission for {name} is missing'**
String errorMissingPermission(String name); 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. /// No description provided for @errorUnsupportedAppVersions.
/// ///
/// In en, this message translates to: /// In en, this message translates to:

3
packages/neon/neon/lib/l10n/localizations_en.dart

@ -106,9 +106,6 @@ class AppLocalizationsEn extends AppLocalizations {
return 'Permission for $name is missing'; return 'Permission for $name is missing';
} }
@override
String get errorUnableToOpenFile => 'Unable to open the file';
@override @override
String errorUnsupportedAppVersions(String names) { 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.'; 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.';

6
packages/neon/neon/lib/src/pages/login_check_account.dart

@ -68,11 +68,11 @@ class _LoginCheckAccountPageState extends State<LoginCheckAccountPage> {
if (state.hasError) ...[ if (state.hasError) ...[
Builder( Builder(
builder: (final context) { builder: (final context) {
final details = NeonError.getDetails(context, state.error); final details = NeonError.getDetails(state.error);
return NeonValidationTile( return NeonValidationTile(
title: details.isUnauthorized title: details.isUnauthorized
? AppLocalizations.of(context).errorCredentialsForAccountNoLongerMatch ? AppLocalizations.of(context).errorCredentialsForAccountNoLongerMatch
: details.text, : details.getText(context),
state: ValidationState.failure, state: ValidationState.failure,
); );
}, },
@ -91,7 +91,7 @@ class _LoginCheckAccountPageState extends State<LoginCheckAccountPage> {
const HomeRoute().go(context); const HomeRoute().go(context);
} }
: () { : () {
if (state.hasError && NeonError.getDetails(context, state.error).isUnauthorized) { if (state.hasError && NeonError.getDetails(state.error).isUnauthorized) {
Navigator.pop(context); Navigator.pop(context);
return; return;
} }

2
packages/neon/neon/lib/src/pages/login_check_server_status.dart

@ -67,7 +67,7 @@ class _LoginCheckServerStatusPageState extends State<LoginCheckServerStatusPage>
children: [ children: [
if (state.hasError) ...[ if (state.hasError) ...[
NeonValidationTile( NeonValidationTile(
title: NeonError.getDetails(context, state.error).text, title: NeonError.getDetails(state.error).getText(context),
state: ValidationState.failure, state: ValidationState.failure,
), ),
], ],

15
packages/neon/neon/lib/src/pages/login_qr_code.dart

@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_zxing/flutter_zxing.dart'; import 'package:flutter_zxing/flutter_zxing.dart';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:neon/l10n/localizations.dart';
import 'package:neon/src/models/account.dart'; import 'package:neon/src/models/account.dart';
import 'package:neon/src/router.dart'; import 'package:neon/src/router.dart';
import 'package:neon/src/utils/exceptions.dart'; import 'package:neon/src/utils/exceptions.dart';
@ -35,11 +36,11 @@ class _LoginQRcodePageState extends State<LoginQRcodePage> {
try { try {
url = code.text; url = code.text;
if (url == null) { if (url == null) {
throw InvalidQRcodeException(); throw const InvalidQRcodeException();
} }
final match = LoginQRcode.tryParse(url); final match = LoginQRcode.tryParse(url);
if (match == null) { if (match == null) {
throw InvalidQRcodeException(); throw const InvalidQRcodeException();
} }
LoginCheckServerStatusRoute.withCredentials( LoginCheckServerStatusRoute.withCredentials(
@ -60,3 +61,13 @@ class _LoginQRcodePageState extends State<LoginQRcodePage> {
), ),
); );
} }
@immutable
class InvalidQRcodeException extends NeonException {
const InvalidQRcodeException();
@override
NeonExceptionDetails get details => NeonExceptionDetails(
getText: (final context) => AppLocalizations.of(context).errorInvalidQRcode,
);
}

2
packages/neon/neon/lib/src/platform/android.dart

@ -31,7 +31,7 @@ class AndroidNeonPlatform implements NeonPlatform {
@override @override
Future<String> get userAccessibleAppDataPath async { Future<String> get userAccessibleAppDataPath async {
if (!await Permission.storage.request().isGranted) { if (!await Permission.storage.request().isGranted) {
throw MissingPermissionException(Permission.storage); throw const MissingPermissionException(Permission.storage);
} }
return p.join((await getExternalStorageDirectory())!.path); return p.join((await getExternalStorageDirectory())!.path);

32
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'; import 'package:permission_handler/permission_handler.dart';
class MissingPermissionException implements Exception { class NeonExceptionDetails {
MissingPermissionException(this.permission); 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]),
);
}

73
packages/neon/neon/lib/src/widgets/error.dart

@ -28,11 +28,11 @@ class NeonError extends StatelessWidget {
final Color? color; final Color? color;
static void showSnackbar(final BuildContext context, final dynamic error) { static void showSnackbar(final BuildContext context, final dynamic error) {
final details = getDetails(context, error); final details = getDetails(error);
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: Text(details.text), content: Text(details.getText(context)),
action: details.isUnauthorized action: details.isUnauthorized
? SnackBarAction( ? SnackBarAction(
label: AppLocalizations.of(context).loginAgain, label: AppLocalizations.of(context).loginAgain,
@ -49,7 +49,7 @@ class NeonError extends StatelessWidget {
return const SizedBox(); return const SizedBox();
} }
final details = getDetails(context, error); final details = getDetails(error);
final color = this.color ?? Theme.of(context).colorScheme.error; final color = this.color ?? Theme.of(context).colorScheme.error;
final errorIcon = Icon( final errorIcon = Icon(
@ -65,7 +65,7 @@ class NeonError extends StatelessWidget {
if (onlyIcon) { if (onlyIcon) {
return Semantics( return Semantics(
tooltip: details.text, tooltip: details.getText(context),
child: IconButton( child: IconButton(
icon: errorIcon, icon: errorIcon,
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
@ -93,7 +93,7 @@ class NeonError extends StatelessWidget {
), ),
Flexible( Flexible(
child: Text( child: Text(
details.text, details.getText(context),
style: TextStyle( style: TextStyle(
color: color, color: color,
), ),
@ -111,78 +111,64 @@ class NeonError extends StatelessWidget {
} }
@internal @internal
static ExceptionDetails getDetails(final BuildContext context, final dynamic error) { static NeonExceptionDetails getDetails(final dynamic error) {
if (error is String) { if (error is String) {
return ExceptionDetails( return NeonExceptionDetails(
text: error, getText: (final _) => error,
); );
} }
if (error is MissingPermissionException) { if (error is NeonException) {
return ExceptionDetails( return error.details;
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 DynamiteApiException) { if (error is DynamiteApiException) {
if (error.statusCode == 401) { if (error.statusCode == 401) {
return ExceptionDetails( return NeonExceptionDetails(
text: AppLocalizations.of(context).errorCredentialsForAccountNoLongerMatch, getText: (final context) => AppLocalizations.of(context).errorCredentialsForAccountNoLongerMatch,
isUnauthorized: true, isUnauthorized: true,
); );
} }
if (error.statusCode >= 500 && error.statusCode <= 599) { if (error.statusCode >= 500 && error.statusCode <= 599) {
return ExceptionDetails( return NeonExceptionDetails(
text: AppLocalizations.of(context).errorServerHadAProblemProcessingYourRequest, getText: (final context) => AppLocalizations.of(context).errorServerHadAProblemProcessingYourRequest,
); );
} }
} }
if (error is SocketException) { if (error is SocketException) {
return ExceptionDetails( return NeonExceptionDetails(
text: error.address != null getText: (final context) => error.address != null
? AppLocalizations.of(context).errorUnableToReachServerAt(error.address!.host) ? AppLocalizations.of(context).errorUnableToReachServerAt(error.address!.host)
: AppLocalizations.of(context).errorUnableToReachServer, : AppLocalizations.of(context).errorUnableToReachServer,
); );
} }
if (error is ClientException) { if (error is ClientException) {
return ExceptionDetails( return NeonExceptionDetails(
text: error.uri != null getText: (final context) => error.uri != null
? AppLocalizations.of(context).errorUnableToReachServerAt(error.uri!.host) ? AppLocalizations.of(context).errorUnableToReachServerAt(error.uri!.host)
: AppLocalizations.of(context).errorUnableToReachServer, : AppLocalizations.of(context).errorUnableToReachServer,
); );
} }
if (error is HttpException) { if (error is HttpException) {
return ExceptionDetails( return NeonExceptionDetails(
text: error.uri != null getText: (final context) => error.uri != null
? AppLocalizations.of(context).errorUnableToReachServerAt(error.uri!.host) ? AppLocalizations.of(context).errorUnableToReachServerAt(error.uri!.host)
: AppLocalizations.of(context).errorUnableToReachServer, : AppLocalizations.of(context).errorUnableToReachServer,
); );
} }
if (error is TimeoutException) { if (error is TimeoutException) {
return ExceptionDetails( return NeonExceptionDetails(
text: AppLocalizations.of(context).errorConnectionTimedOut, getText: (final context) => AppLocalizations.of(context).errorConnectionTimedOut,
); );
} }
return ExceptionDetails( return NeonExceptionDetails(
text: AppLocalizations.of(context).errorSomethingWentWrongTryAgainLater, 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;
}

2
packages/neon/neon/lib/widgets.dart

@ -1,6 +1,6 @@
export 'package:neon/src/widgets/cached_image.dart'; export 'package:neon/src/widgets/cached_image.dart';
export 'package:neon/src/widgets/dialog.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/image_wrapper.dart';
export 'package:neon/src/widgets/linear_progress_indicator.dart'; export 'package:neon/src/widgets/linear_progress_indicator.dart';
export 'package:neon/src/widgets/list_view.dart'; export 'package:neon/src/widgets/list_view.dart';

12
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); final result = await OpenFile.open(file.path, type: mimeType);
if (result.type != ResultType.done) { if (result.type != ResultType.done) {
throw UnableToOpenFileException(); throw const UnableToOpenFileException();
} }
}, },
disableTimeout: true, disableTimeout: true,
@ -194,3 +194,13 @@ class FilesBloc extends InteractiveBloc implements FilesBlocEvents, FilesBlocSta
_uploadQueue.parallel = options.uploadQueueParallelism.value; _uploadQueue.parallel = options.uploadQueueParallelism.value;
} }
} }
@immutable
class UnableToOpenFileException extends NeonException {
const UnableToOpenFileException();
@override
NeonExceptionDetails get details => NeonExceptionDetails(
getText: (final context) => AppLocalizations.of(context).errorUnableToOpenFile,
);
}

1
packages/neon/neon_files/lib/l10n/en.arb

@ -7,6 +7,7 @@
"actionMove": "Move", "actionMove": "Move",
"actionCopy": "Copy", "actionCopy": "Copy",
"actionSync": "Sync", "actionSync": "Sync",
"errorUnableToOpenFile": "Unable to open the file",
"general": "General", "general": "General",
"goToPath": "Go to /{path}", "goToPath": "Go to /{path}",
"@goToPath": { "@goToPath": {

6
packages/neon/neon_files/lib/l10n/localizations.dart

@ -131,6 +131,12 @@ abstract class AppLocalizations {
/// **'Sync'** /// **'Sync'**
String get actionSync; 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. /// No description provided for @general.
/// ///
/// In en, this message translates to: /// In en, this message translates to:

3
packages/neon/neon_files/lib/l10n/localizations_en.dart

@ -25,6 +25,9 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get actionSync => 'Sync'; String get actionSync => 'Sync';
@override
String get errorUnableToOpenFile => 'Unable to open the file';
@override @override
String get general => 'General'; String get general => 'General';

Loading…
Cancel
Save