From eca11b3506284f77c16e72793f85094e4cc8026e Mon Sep 17 00:00:00 2001 From: dab246 Date: Fri, 12 Jul 2024 18:58:50 +0700 Subject: [PATCH] TF-2928 Show confirm dialog & handle exception when reload browser or loss of connection --- core/lib/core.dart | 1 + .../domain/exceptions/platform_exception.dart | 14 ++ .../dialog/confirmation_dialog_builder.dart | 23 ++-- lib/features/base/base_controller.dart | 130 ++++++++++++------ .../mixin/message_dialog_action_mixin.dart | 10 +- .../presentation/composer_controller.dart | 32 ++--- .../authorization_interceptors.dart | 11 -- .../login/presentation/login_controller.dart | 6 +- .../mailbox_dashboard_controller.dart | 20 ++- .../delete_identity_dialog_builder.dart | 2 +- .../presentation/thread_controller.dart | 5 +- lib/l10n/intl_messages.arb | 20 ++- lib/main/localizations/app_localizations.dart | 21 +++ 13 files changed, 198 insertions(+), 97 deletions(-) create mode 100644 core/lib/domain/exceptions/platform_exception.dart diff --git a/core/lib/core.dart b/core/lib/core.dart index 9bc6ca392..944f522d6 100644 --- a/core/lib/core.dart +++ b/core/lib/core.dart @@ -19,6 +19,7 @@ export 'presentation/extensions/map_extensions.dart'; export 'domain/exceptions/download_file_exception.dart'; export 'data/extensions/options_extensions.dart'; export 'domain/exceptions/web_session_exception.dart'; +export 'domain/exceptions/platform_exception.dart'; // Utils export 'presentation/utils/theme_utils.dart'; diff --git a/core/lib/domain/exceptions/platform_exception.dart b/core/lib/domain/exceptions/platform_exception.dart new file mode 100644 index 000000000..9569df122 --- /dev/null +++ b/core/lib/domain/exceptions/platform_exception.dart @@ -0,0 +1,14 @@ +import 'package:equatable/equatable.dart'; + +class PlatformException with EquatableMixin implements Exception { + final String message; + + PlatformException(this.message); + + @override + List get props => [message]; +} + +class NoSupportPlatformException extends PlatformException { + NoSupportPlatformException() : super('This platform is not supported'); +} \ No newline at end of file diff --git a/core/lib/presentation/views/dialog/confirmation_dialog_builder.dart b/core/lib/presentation/views/dialog/confirmation_dialog_builder.dart index 18dbcf258..667274418 100644 --- a/core/lib/presentation/views/dialog/confirmation_dialog_builder.dart +++ b/core/lib/presentation/views/dialog/confirmation_dialog_builder.dart @@ -24,18 +24,19 @@ class ConfirmDialogBuilder { double? _radiusButton; EdgeInsetsGeometry? _paddingTitle; EdgeInsets? _paddingContent; - EdgeInsets? _paddingButton; + EdgeInsetsGeometry? _paddingButton; + EdgeInsets? _marginButton; EdgeInsets? _outsideDialogPadding; EdgeInsetsGeometry? _marginIcon; EdgeInsets? _margin; double? _widthDialog; - double maxWith; + final double maxWith; Alignment? _alignment; Color? _backgroundColor; - bool showAsBottomSheet; - List? listTextSpan; - int? titleActionButtonMaxLines; - bool isArrangeActionButtonsVertical; + final bool showAsBottomSheet; + final List? listTextSpan; + final int? titleActionButtonMaxLines; + final bool isArrangeActionButtonsVertical; OnConfirmButtonAction? _onConfirmButtonAction; OnCancelButtonAction? _onCancelButtonAction; @@ -104,10 +105,14 @@ class ConfirmDialogBuilder { _paddingContent = value; } - void paddingButton(EdgeInsets? value) { + void paddingButton(EdgeInsetsGeometry? value) { _paddingButton = value; } + void marginButton(EdgeInsets? value) { + _marginButton = value; + } + void marginIcon(EdgeInsetsGeometry? value) { _marginIcon = value; } @@ -253,7 +258,7 @@ class ConfirmDialogBuilder { ] else Padding( - padding: _paddingButton ?? const EdgeInsetsDirectional.only(bottom: 16, start: 16, end: 16), + padding: _marginButton ?? const EdgeInsetsDirectional.only(bottom: 16, start: 16, end: 16), child: Row( children: [ if (_cancelText.isNotEmpty) @@ -297,7 +302,7 @@ class ConfirmDialogBuilder { borderRadius: BorderRadius.circular(radius ?? 8), side: BorderSide(width: 0, color: bgColor ?? AppColor.colorTextButton), ), - padding: const EdgeInsets.all(8), + padding: _paddingButton ?? const EdgeInsets.all(8), elevation: 0 ), child: Text( diff --git a/lib/features/base/base_controller.dart b/lib/features/base/base_controller.dart index 9ca39a37e..0c670ae15 100644 --- a/lib/features/base/base_controller.dart +++ b/lib/features/base/base_controller.dart @@ -1,5 +1,8 @@ +import 'dart:async'; + import 'package:contact/contact/model/capability_contact.dart'; import 'package:core/data/network/config/dynamic_url_interceptors.dart'; +import 'package:core/domain/exceptions/platform_exception.dart'; import 'package:core/presentation/extensions/color_extension.dart'; import 'package:core/presentation/resources/image_paths.dart'; import 'package:core/presentation/state/failure.dart'; @@ -60,6 +63,7 @@ import 'package:tmail_ui_user/main/routes/app_routes.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart'; import 'package:tmail_ui_user/main/utils/app_config.dart'; import 'package:tmail_ui_user/main/utils/app_utils.dart'; +import 'package:universal_html/html.dart' as html; import 'package:uuid/uuid.dart'; abstract class BaseController extends GetxController @@ -85,9 +89,42 @@ abstract class BaseController extends GetxController GetStoredFirebaseRegistrationInteractor? _getStoredFirebaseRegistrationInteractor; DestroyFirebaseRegistrationInteractor? _destroyFirebaseRegistrationInteractor; + StreamSubscription? _subscriptionBrowserOnBeforeUnload; + StreamSubscription? _subscriptionBrowserOnUnload; + final viewState = Rx>(Right(UIState.idle)); FpsCallback? fpsCallback; + @override + void onInit() { + super.onInit(); + if (PlatformInfo.isWeb) { + WidgetsBinding.instance.addPostFrameCallback((_) { + _triggerBrowserReloadListener(); + }); + } + } + + void _triggerBrowserReloadListener() { + _subscriptionBrowserOnBeforeUnload = + html.window.onBeforeUnload.listen(handleBrowserBeforeReloadAction); + _subscriptionBrowserOnUnload = + html.window.onUnload.listen(handleBrowserReloadAction); + } + + Future handleBrowserBeforeReloadAction(html.Event event) async {} + + Future handleBrowserReloadAction(html.Event event) async {} + + @override + void onClose() { + if (PlatformInfo.isWeb) { + _subscriptionBrowserOnBeforeUnload?.cancel(); + _subscriptionBrowserOnUnload?.cancel(); + } + super.onClose(); + } + void consumeState(Stream> newStateStream) async { newStateStream.listen(onData, onError: onError, onDone: onDone); } @@ -105,10 +142,9 @@ abstract class BaseController extends GetxController viewState.value.fold( (failure) { if (failure is FeatureFailure) { - final exception = _performFilterExceptionInError(failure.exception); - logError('$runtimeType::onData:exception: $exception'); - if (exception != null) { - handleExceptionAction(failure: failure, exception: exception); + final isUrgentException = _validateUrgentException(failure.exception); + if (isUrgentException) { + handleUrgentException(failure: failure, exception: failure.exception); } else { handleFailureViewState(failure); } @@ -119,11 +155,11 @@ abstract class BaseController extends GetxController handleSuccessViewState); } - void onError(Object error, StackTrace stackTrace) { - logError('$runtimeType::onError():error: $error | stackTrace: $stackTrace'); - final exception = _performFilterExceptionInError(error); - if (exception != null) { - handleExceptionAction(exception: exception); + void onError(dynamic error, StackTrace stackTrace) { + logError('$runtimeType::onError():Error: $error | StackTrace: $stackTrace'); + final isUrgentException = _validateUrgentException(error); + if (isUrgentException) { + handleUrgentException(exception: error); } else { handleErrorViewState(error, stackTrace); } @@ -131,10 +167,41 @@ abstract class BaseController extends GetxController void onDone() {} - Exception? _performFilterExceptionInError(dynamic error) { - logError('$runtimeType::_performFilterExceptionInError(): $error'); - if (error is NoNetworkError || error is ConnectionTimeout || error is InternalServerError) { - if (PlatformInfo.isWeb && currentOverlayContext != null && currentContext != null) { + bool _validateUrgentException(dynamic exception) { + return exception is NoNetworkError + || exception is BadCredentialsException + || exception is ConnectionError; + } + + void handleErrorViewState(Object error, StackTrace stackTrace) {} + + void handleUrgentException({Failure? failure, Exception? exception}) { + if (PlatformInfo.isWeb) { + handleUrgentExceptionOnWeb(failure: failure, exception: exception); + } else if (PlatformInfo.isMobile) { + handleUrgentExceptionOnMobile(failure: failure, exception: exception); + } else { + throw NoSupportPlatformException(); + } + } + + void handleUrgentExceptionOnMobile({Failure? failure, Exception? exception}) { + logError('$runtimeType::handleUrgentExceptionOnMobile():Failure: $failure | Exception: $exception'); + if (exception is ConnectionError) { + if (currentOverlayContext != null && currentContext != null) { + appToast.showToastErrorMessage( + currentOverlayContext!, + AppLocalizations.of(currentContext!).connectionError); + } + } else if (exception is BadCredentialsException) { + _executeBeforeUnloadAndLogOut(); + } + } + + void handleUrgentExceptionOnWeb({Failure? failure, Exception? exception}) { + logError('$runtimeType::handleUrgentExceptionOnWeb():Failure: $failure | Exception: $exception'); + if (exception is NoNetworkError) { + if (currentOverlayContext != null && currentContext != null) { appToast.showToastMessage( currentOverlayContext!, AppLocalizations.of(currentContext!).no_internet_connection, @@ -143,43 +210,22 @@ abstract class BaseController extends GetxController leadingSVGIcon: imagePaths.icNotConnection, backgroundColor: AppColor.textFieldErrorBorderColor, textColor: Colors.white, - infinityToast: true, - ); + infinityToast: true,); } - return error; - } else if (error is BadCredentialsException || error is ConnectionError) { - return error; - } - - return null; - } - - void handleErrorViewState(Object error, StackTrace stackTrace) {} - - void handleExceptionAction({Failure? failure, Exception? exception}) { - logError('$runtimeType::handleExceptionAction():failure: $failure | exception: $exception'); - if (exception is ConnectionError) { + } else if (exception is ConnectionError) { if (currentOverlayContext != null && currentContext != null) { appToast.showToastErrorMessage( currentOverlayContext!, - AppLocalizations.of(currentContext!).connectionError - ); + AppLocalizations.of(currentContext!).connectionError); } + } else if (exception is BadCredentialsException) { + _executeBeforeUnloadAndLogOut(); } - - if (!authorizationInterceptors.isAppRunning) { - return; - } - _executeBeforeUnloadAndLogOut(exception); } - Future _executeBeforeUnloadAndLogOut(Exception? exception) async { - if (exception is BadCredentialsException || exception is ConnectionError) { - if (PlatformInfo.isWeb) { - await executeBeforeUnload(); - } - clearDataAndGoToLoginPage(); - } + Future _executeBeforeUnloadAndLogOut() async { + await executeBeforeUnload(); + clearDataAndGoToLoginPage(); } void handleFailureViewState(Failure failure) async { diff --git a/lib/features/base/mixin/message_dialog_action_mixin.dart b/lib/features/base/mixin/message_dialog_action_mixin.dart index 69c2337c4..3756683cb 100644 --- a/lib/features/base/mixin/message_dialog_action_mixin.dart +++ b/lib/features/base/mixin/message_dialog_action_mixin.dart @@ -33,6 +33,7 @@ mixin MessageDialogActionMixin { Color? actionButtonColor, Color? cancelButtonColor, EdgeInsetsGeometry? marginIcon, + EdgeInsetsGeometry? paddingButton, PopInvokedCallback? onPopInvoked, bool isArrangeActionButtonsVertical = false, int? titleActionButtonMaxLines, @@ -61,8 +62,9 @@ mixin MessageDialogActionMixin { : const EdgeInsetsDirectional.symmetric(horizontal: 24) ) ..radiusButton(12) + ..paddingButton(paddingButton) ..paddingContent(const EdgeInsets.only(left: 24, right: 24, bottom: 24, top: 12)) - ..paddingButton(hasCancelButton ? null : const EdgeInsets.only(bottom: 24, left: 24, right: 24)) + ..marginButton(hasCancelButton ? null : const EdgeInsets.only(bottom: 24, left: 24, right: 24)) ..styleTitle(titleStyle ?? const TextStyle(fontSize: 20, fontWeight: FontWeight.bold, color: Colors.black)) ..styleContent(messageStyle ?? const TextStyle(fontSize: 14, fontWeight: FontWeight.normal, color: AppColor.colorContentEmail)) ..styleTextCancelButton(cancelStyle ?? const TextStyle(fontSize: 17, fontWeight: FontWeight.w500, color: AppColor.colorTextButton)) @@ -107,6 +109,7 @@ mixin MessageDialogActionMixin { ..title(title ?? '') ..content(message) ..addIcon(icon) + ..paddingButton(paddingButton) ..margin(const EdgeInsets.only(bottom: 42)) ..widthDialog(responsiveUtils.getSizeScreenWidth(context)) ..colorConfirmButton(actionButtonColor ?? AppColor.colorTextButton) @@ -117,7 +120,7 @@ mixin MessageDialogActionMixin { ) ..marginIcon(EdgeInsets.zero) ..paddingContent(const EdgeInsets.only(left: 44, right: 44, bottom: 24, top: 12)) - ..paddingButton(hasCancelButton ? null : const EdgeInsets.only(bottom: 16, left: 44, right: 44)) + ..marginButton(hasCancelButton ? null : const EdgeInsets.only(bottom: 16, left: 44, right: 44)) ..styleTitle(titleStyle ?? const TextStyle(fontSize: 20, fontWeight: FontWeight.bold, color: Colors.black)) ..styleContent(messageStyle ?? const TextStyle(fontSize: 14, fontWeight: FontWeight.normal, color: AppColor.colorContentEmail)) ..styleTextCancelButton(cancelStyle ?? const TextStyle(fontSize: 17, fontWeight: FontWeight.w500, color: AppColor.colorTextButton)) @@ -187,6 +190,7 @@ mixin MessageDialogActionMixin { ..title(title ?? '') ..content(message) ..addIcon(icon) + ..paddingButton(paddingButton) ..colorConfirmButton(actionButtonColor ?? AppColor.colorTextButton) ..colorCancelButton(cancelButtonColor ?? AppColor.colorCancelButton) ..marginIcon(icon != null ? const EdgeInsets.only(top: 24) : null) @@ -195,7 +199,7 @@ mixin MessageDialogActionMixin { : const EdgeInsetsDirectional.symmetric(horizontal: 24)) ..marginIcon(EdgeInsets.zero) ..paddingContent(const EdgeInsets.only(left: 44, right: 44, bottom: 24, top: 12)) - ..paddingButton(hasCancelButton ? null : const EdgeInsets.only(bottom: 16, left: 44, right: 44)) + ..marginButton(hasCancelButton ? null : const EdgeInsets.only(bottom: 16, left: 44, right: 44)) ..styleTitle(titleStyle ?? const TextStyle(fontSize: 20, fontWeight: FontWeight.bold, color: Colors.black)) ..styleContent(messageStyle ?? const TextStyle(fontSize: 14, fontWeight: FontWeight.normal, color: AppColor.colorContentEmail)) ..styleTextCancelButton(cancelStyle ?? const TextStyle(fontSize: 17, fontWeight: FontWeight.w500, color: AppColor.colorTextButton)) diff --git a/lib/features/composer/presentation/composer_controller.dart b/lib/features/composer/presentation/composer_controller.dart index 5f702e4a9..a390f3d91 100644 --- a/lib/features/composer/presentation/composer_controller.dart +++ b/lib/features/composer/presentation/composer_controller.dart @@ -92,6 +92,7 @@ import 'package:tmail_ui_user/features/upload/domain/state/local_image_picker_st import 'package:tmail_ui_user/features/upload/domain/usecases/local_file_picker_interactor.dart'; import 'package:tmail_ui_user/features/upload/domain/usecases/local_image_picker_interactor.dart'; import 'package:tmail_ui_user/features/upload/presentation/controller/upload_controller.dart'; +import 'package:tmail_ui_user/main/exceptions/remote_exception.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart'; import 'package:universal_html/html.dart' as html; @@ -164,7 +165,6 @@ class ComposerController extends BaseController with DragDropFileMixin implement FocusNode? ccAddressFocusNodeKeyboard; FocusNode? bccAddressFocusNodeKeyboard; - StreamSubscription? _subscriptionOnBeforeUnload; StreamSubscription? _subscriptionOnDragEnter; StreamSubscription? _subscriptionOnDragOver; StreamSubscription? _subscriptionOnDragLeave; @@ -213,7 +213,7 @@ class ComposerController extends BaseController with DragDropFileMixin implement super.onInit(); if (PlatformInfo.isWeb) { WidgetsBinding.instance.addPostFrameCallback((_) { - _listenBrowserTabRefresh(); + _triggerBrowserEventListener(); }); richTextWebController = getBinding(); } else { @@ -244,7 +244,6 @@ class ComposerController extends BaseController with DragDropFileMixin implement emailContentsViewState.value = Right(UIClosedState()); identitySelected.value = null; listFromIdentities.clear(); - _subscriptionOnBeforeUnload?.cancel(); _subscriptionOnDragEnter?.cancel(); _subscriptionOnDragOver?.cancel(); _subscriptionOnDragLeave?.cancel(); @@ -353,11 +352,17 @@ class ComposerController extends BaseController with DragDropFileMixin implement } @override - void handleExceptionAction({Failure? failure, Exception? exception}) { - super.handleExceptionAction(failure: failure, exception: exception); - if (failure is GetAllIdentitiesFailure) { - _handleGetAllIdentitiesFailure(failure); + void handleUrgentExceptionOnMobile({Failure? failure, Exception? exception}) { + if (failure is GetAllIdentitiesFailure && exception is! BadCredentialsException) { + _handleGetAllIdentitiesFailure(); } + super.handleUrgentExceptionOnMobile(failure: failure, exception: exception); + } + + @override + Future handleBrowserReloadAction(html.Event event) async { + await _removeComposerCacheOnWebInteractor.execute(); + await _saveComposerCacheOnWebAction(); } void _listenStreamEvent() { @@ -377,13 +382,7 @@ class ComposerController extends BaseController with DragDropFileMixin implement } } - void _listenBrowserTabRefresh() { - _subscriptionOnBeforeUnload = html.window.onBeforeUnload.listen((event) async { - await _removeComposerCacheOnWebInteractor.execute(); - - await _saveComposerCacheOnWebAction(); - }); - + void _triggerBrowserEventListener() { _subscriptionOnDragEnter = html.window.onDragEnter.listen((event) { event.preventDefault(); @@ -1966,9 +1965,8 @@ class ComposerController extends BaseController with DragDropFileMixin implement ); } - void _handleGetAllIdentitiesFailure(GetAllIdentitiesFailure failure) async { - log('ComposerController::_handleGetAllIdentitiesFailure:failure: $failure'); - if (composerArguments.value?.emailActionType == EmailActionType.editSendingEmail && PlatformInfo.isMobile) { + Future _handleGetAllIdentitiesFailure() async { + if (composerArguments.value?.emailActionType == EmailActionType.editSendingEmail) { final signatureContent = await htmlEditorApi?.getSignatureContent(); log('ComposerController::_handleGetAllIdentitiesFailure:signatureContent: $signatureContent'); if (signatureContent?.isNotEmpty == true) { diff --git a/lib/features/login/data/network/interceptors/authorization_interceptors.dart b/lib/features/login/data/network/interceptors/authorization_interceptors.dart index 85b2078c3..8699df8b2 100644 --- a/lib/features/login/data/network/interceptors/authorization_interceptors.dart +++ b/lib/features/login/data/network/interceptors/authorization_interceptors.dart @@ -203,17 +203,6 @@ class AuthorizationInterceptors extends QueuedInterceptorsWrapper { String _getTokenAsBearerHeader(String token) => 'Bearer $token'; - bool get isAppRunning { - switch(_authenticationType) { - case AuthenticationType.basic: - return _authorization != null; - case AuthenticationType.oidc: - return _configOIDC != null && _token != null; - case AuthenticationType.none: - return false; - } - } - Future _updateCurrentAccount({required TokenOIDC tokenOIDC}) async { final currentAccount = await _accountCacheManager.getCurrentAccount(); diff --git a/lib/features/login/presentation/login_controller.dart b/lib/features/login/presentation/login_controller.dart index 561ad3448..fb2c44986 100644 --- a/lib/features/login/presentation/login_controller.dart +++ b/lib/features/login/presentation/login_controller.dart @@ -172,8 +172,8 @@ class LoginController extends ReloadableController { } @override - void handleExceptionAction({Failure? failure, Exception? exception}) { - logError('LoginController::handleExceptionAction:exception: $exception | failure: ${failure.runtimeType}'); + void handleUrgentException({Failure? failure, Exception? exception}) { + logError('LoginController::handleUrgentException:Exception: $exception | Failure: $failure'); if (failure is CheckOIDCIsAvailableFailure || failure is GetStoredOidcConfigurationFailure || failure is GetOIDCConfigurationFailure || @@ -185,7 +185,7 @@ class LoginController extends ReloadableController { } else if (failure is GetSessionFailure) { clearAllData(); } else { - super.handleExceptionAction(failure: failure, exception: exception); + super.handleUrgentException(failure: failure, exception: exception); } } diff --git a/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart b/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart index ae3e63f8a..5e7837de5 100644 --- a/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart @@ -153,6 +153,7 @@ import 'package:tmail_ui_user/main/routes/route_navigation.dart'; import 'package:tmail_ui_user/main/routes/route_utils.dart'; import 'package:tmail_ui_user/main/utils/email_receive_manager.dart'; import 'package:tmail_ui_user/main/utils/ios_notification_manager.dart'; +import 'package:universal_html/html.dart' as html; import 'package:uuid/uuid.dart'; class MailboxDashBoardController extends ReloadableController { @@ -407,14 +408,19 @@ class MailboxDashBoardController extends ReloadableController { } @override - void handleExceptionAction({Failure? failure, Exception? exception}) { - super.handleExceptionAction(failure: failure, exception: exception); - if (failure is SendEmailFailure && - exception is NoNetworkError && - PlatformInfo.isMobile - ) { - log('MailboxDashBoardController::handleExceptionAction(): $failure'); + void handleUrgentExceptionOnMobile({Failure? failure, Exception? exception}) { + if (failure is SendEmailFailure && exception is NoNetworkError) { _storeSendingEmailInCaseOfSendingFailureInMobile(failure); + } else { + super.handleUrgentExceptionOnMobile(failure: failure, exception: exception); + } + } + + @override + Future handleBrowserBeforeReloadAction(html.Event event) async { + if (event is html.BeforeUnloadEvent + && composerOverlayState.value == ComposerOverlayState.active) { + event.preventDefault(); } } diff --git a/lib/features/manage_account/presentation/profiles/identities/widgets/delete_identity_dialog_builder.dart b/lib/features/manage_account/presentation/profiles/identities/widgets/delete_identity_dialog_builder.dart index a6e307f76..89ca63cf4 100644 --- a/lib/features/manage_account/presentation/profiles/identities/widgets/delete_identity_dialog_builder.dart +++ b/lib/features/manage_account/presentation/profiles/identities/widgets/delete_identity_dialog_builder.dart @@ -70,7 +70,7 @@ class DeleteIdentityDialogBuilder extends StatelessWidget { fontSize: 17, fontWeight: FontWeight.w500, color: Colors.white)) - ..paddingButton(const EdgeInsets.only(bottom: 24, left: 24, right: 24)) + ..marginButton(const EdgeInsets.only(bottom: 24, left: 24, right: 24)) ..backgroundColor(Colors.black26) ..onConfirmButtonAction(AppLocalizations.of(context).delete, () => onDeleteIdentityAction.call()) diff --git a/lib/features/thread/presentation/thread_controller.dart b/lib/features/thread/presentation/thread_controller.dart index 9c02a2af4..37e74ead8 100644 --- a/lib/features/thread/presentation/thread_controller.dart +++ b/lib/features/thread/presentation/thread_controller.dart @@ -220,11 +220,10 @@ class ThreadController extends BaseController with EmailActionController { } @override - void handleExceptionAction({Failure? failure, Exception? exception}) { - super.handleExceptionAction(failure: failure, exception: exception); - logError('ThreadController::handleExceptionAction(): failure: $failure | exception: $exception'); + void handleUrgentException({Failure? failure, Exception? exception}) { _resetLoadingMore(); clearState(); + super.handleUrgentException(failure: failure, exception: exception); } void _resetLoadingMore() { diff --git a/lib/l10n/intl_messages.arb b/lib/l10n/intl_messages.arb index 9aa8d658a..4d6bd53f3 100644 --- a/lib/l10n/intl_messages.arb +++ b/lib/l10n/intl_messages.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2024-07-10T13:53:40.940363", + "@@last_modified": "2024-07-12T12:54:25.065342", "initializing_data": "Initializing data...", "@initializing_data": { "type": "text", @@ -3933,5 +3933,23 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "reconnect": "Reconnect", + "@reconnect": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "sessionExpired": "Session expired", + "@sessionExpired": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "dialogMessageSessionHasExpired": "The current session has expired. Please reconnect to the server", + "@dialogMessageSessionHasExpired": { + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/lib/main/localizations/app_localizations.dart b/lib/main/localizations/app_localizations.dart index 47be4e141..29fc74925 100644 --- a/lib/main/localizations/app_localizations.dart +++ b/lib/main/localizations/app_localizations.dart @@ -4113,4 +4113,25 @@ class AppLocalizations { name: 'requestReadReceiptHasBeenDisabled' ); } + + String get reconnect { + return Intl.message( + 'Reconnect', + name: 'reconnect', + ); + } + + String get sessionExpired { + return Intl.message( + 'Session expired', + name: 'sessionExpired', + ); + } + + String get dialogMessageSessionHasExpired { + return Intl.message( + 'The current session has expired. Please reconnect to the server', + name: 'dialogMessageSessionHasExpired', + ); + } } \ No newline at end of file