TF-2928 Show confirm dialog & handle exception when reload browser or loss of connection
This commit is contained in:
@@ -19,6 +19,7 @@ export 'presentation/extensions/map_extensions.dart';
|
|||||||
export 'domain/exceptions/download_file_exception.dart';
|
export 'domain/exceptions/download_file_exception.dart';
|
||||||
export 'data/extensions/options_extensions.dart';
|
export 'data/extensions/options_extensions.dart';
|
||||||
export 'domain/exceptions/web_session_exception.dart';
|
export 'domain/exceptions/web_session_exception.dart';
|
||||||
|
export 'domain/exceptions/platform_exception.dart';
|
||||||
|
|
||||||
// Utils
|
// Utils
|
||||||
export 'presentation/utils/theme_utils.dart';
|
export 'presentation/utils/theme_utils.dart';
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
|
||||||
|
class PlatformException with EquatableMixin implements Exception {
|
||||||
|
final String message;
|
||||||
|
|
||||||
|
PlatformException(this.message);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [message];
|
||||||
|
}
|
||||||
|
|
||||||
|
class NoSupportPlatformException extends PlatformException {
|
||||||
|
NoSupportPlatformException() : super('This platform is not supported');
|
||||||
|
}
|
||||||
@@ -24,18 +24,19 @@ class ConfirmDialogBuilder {
|
|||||||
double? _radiusButton;
|
double? _radiusButton;
|
||||||
EdgeInsetsGeometry? _paddingTitle;
|
EdgeInsetsGeometry? _paddingTitle;
|
||||||
EdgeInsets? _paddingContent;
|
EdgeInsets? _paddingContent;
|
||||||
EdgeInsets? _paddingButton;
|
EdgeInsetsGeometry? _paddingButton;
|
||||||
|
EdgeInsets? _marginButton;
|
||||||
EdgeInsets? _outsideDialogPadding;
|
EdgeInsets? _outsideDialogPadding;
|
||||||
EdgeInsetsGeometry? _marginIcon;
|
EdgeInsetsGeometry? _marginIcon;
|
||||||
EdgeInsets? _margin;
|
EdgeInsets? _margin;
|
||||||
double? _widthDialog;
|
double? _widthDialog;
|
||||||
double maxWith;
|
final double maxWith;
|
||||||
Alignment? _alignment;
|
Alignment? _alignment;
|
||||||
Color? _backgroundColor;
|
Color? _backgroundColor;
|
||||||
bool showAsBottomSheet;
|
final bool showAsBottomSheet;
|
||||||
List<TextSpan>? listTextSpan;
|
final List<TextSpan>? listTextSpan;
|
||||||
int? titleActionButtonMaxLines;
|
final int? titleActionButtonMaxLines;
|
||||||
bool isArrangeActionButtonsVertical;
|
final bool isArrangeActionButtonsVertical;
|
||||||
|
|
||||||
OnConfirmButtonAction? _onConfirmButtonAction;
|
OnConfirmButtonAction? _onConfirmButtonAction;
|
||||||
OnCancelButtonAction? _onCancelButtonAction;
|
OnCancelButtonAction? _onCancelButtonAction;
|
||||||
@@ -104,10 +105,14 @@ class ConfirmDialogBuilder {
|
|||||||
_paddingContent = value;
|
_paddingContent = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void paddingButton(EdgeInsets? value) {
|
void paddingButton(EdgeInsetsGeometry? value) {
|
||||||
_paddingButton = value;
|
_paddingButton = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void marginButton(EdgeInsets? value) {
|
||||||
|
_marginButton = value;
|
||||||
|
}
|
||||||
|
|
||||||
void marginIcon(EdgeInsetsGeometry? value) {
|
void marginIcon(EdgeInsetsGeometry? value) {
|
||||||
_marginIcon = value;
|
_marginIcon = value;
|
||||||
}
|
}
|
||||||
@@ -253,7 +258,7 @@ class ConfirmDialogBuilder {
|
|||||||
]
|
]
|
||||||
else
|
else
|
||||||
Padding(
|
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(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
if (_cancelText.isNotEmpty)
|
if (_cancelText.isNotEmpty)
|
||||||
@@ -297,7 +302,7 @@ class ConfirmDialogBuilder {
|
|||||||
borderRadius: BorderRadius.circular(radius ?? 8),
|
borderRadius: BorderRadius.circular(radius ?? 8),
|
||||||
side: BorderSide(width: 0, color: bgColor ?? AppColor.colorTextButton),
|
side: BorderSide(width: 0, color: bgColor ?? AppColor.colorTextButton),
|
||||||
),
|
),
|
||||||
padding: const EdgeInsets.all(8),
|
padding: _paddingButton ?? const EdgeInsets.all(8),
|
||||||
elevation: 0
|
elevation: 0
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:contact/contact/model/capability_contact.dart';
|
import 'package:contact/contact/model/capability_contact.dart';
|
||||||
import 'package:core/data/network/config/dynamic_url_interceptors.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/extensions/color_extension.dart';
|
||||||
import 'package:core/presentation/resources/image_paths.dart';
|
import 'package:core/presentation/resources/image_paths.dart';
|
||||||
import 'package:core/presentation/state/failure.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/routes/route_navigation.dart';
|
||||||
import 'package:tmail_ui_user/main/utils/app_config.dart';
|
import 'package:tmail_ui_user/main/utils/app_config.dart';
|
||||||
import 'package:tmail_ui_user/main/utils/app_utils.dart';
|
import 'package:tmail_ui_user/main/utils/app_utils.dart';
|
||||||
|
import 'package:universal_html/html.dart' as html;
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
abstract class BaseController extends GetxController
|
abstract class BaseController extends GetxController
|
||||||
@@ -85,9 +89,42 @@ abstract class BaseController extends GetxController
|
|||||||
GetStoredFirebaseRegistrationInteractor? _getStoredFirebaseRegistrationInteractor;
|
GetStoredFirebaseRegistrationInteractor? _getStoredFirebaseRegistrationInteractor;
|
||||||
DestroyFirebaseRegistrationInteractor? _destroyFirebaseRegistrationInteractor;
|
DestroyFirebaseRegistrationInteractor? _destroyFirebaseRegistrationInteractor;
|
||||||
|
|
||||||
|
StreamSubscription<html.Event>? _subscriptionBrowserOnBeforeUnload;
|
||||||
|
StreamSubscription<html.Event>? _subscriptionBrowserOnUnload;
|
||||||
|
|
||||||
final viewState = Rx<Either<Failure, Success>>(Right(UIState.idle));
|
final viewState = Rx<Either<Failure, Success>>(Right(UIState.idle));
|
||||||
FpsCallback? fpsCallback;
|
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<void> handleBrowserBeforeReloadAction(html.Event event) async {}
|
||||||
|
|
||||||
|
Future<void> handleBrowserReloadAction(html.Event event) async {}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onClose() {
|
||||||
|
if (PlatformInfo.isWeb) {
|
||||||
|
_subscriptionBrowserOnBeforeUnload?.cancel();
|
||||||
|
_subscriptionBrowserOnUnload?.cancel();
|
||||||
|
}
|
||||||
|
super.onClose();
|
||||||
|
}
|
||||||
|
|
||||||
void consumeState(Stream<Either<Failure, Success>> newStateStream) async {
|
void consumeState(Stream<Either<Failure, Success>> newStateStream) async {
|
||||||
newStateStream.listen(onData, onError: onError, onDone: onDone);
|
newStateStream.listen(onData, onError: onError, onDone: onDone);
|
||||||
}
|
}
|
||||||
@@ -105,10 +142,9 @@ abstract class BaseController extends GetxController
|
|||||||
viewState.value.fold(
|
viewState.value.fold(
|
||||||
(failure) {
|
(failure) {
|
||||||
if (failure is FeatureFailure) {
|
if (failure is FeatureFailure) {
|
||||||
final exception = _performFilterExceptionInError(failure.exception);
|
final isUrgentException = _validateUrgentException(failure.exception);
|
||||||
logError('$runtimeType::onData:exception: $exception');
|
if (isUrgentException) {
|
||||||
if (exception != null) {
|
handleUrgentException(failure: failure, exception: failure.exception);
|
||||||
handleExceptionAction(failure: failure, exception: exception);
|
|
||||||
} else {
|
} else {
|
||||||
handleFailureViewState(failure);
|
handleFailureViewState(failure);
|
||||||
}
|
}
|
||||||
@@ -119,11 +155,11 @@ abstract class BaseController extends GetxController
|
|||||||
handleSuccessViewState);
|
handleSuccessViewState);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onError(Object error, StackTrace stackTrace) {
|
void onError(dynamic error, StackTrace stackTrace) {
|
||||||
logError('$runtimeType::onError():error: $error | stackTrace: $stackTrace');
|
logError('$runtimeType::onError():Error: $error | StackTrace: $stackTrace');
|
||||||
final exception = _performFilterExceptionInError(error);
|
final isUrgentException = _validateUrgentException(error);
|
||||||
if (exception != null) {
|
if (isUrgentException) {
|
||||||
handleExceptionAction(exception: exception);
|
handleUrgentException(exception: error);
|
||||||
} else {
|
} else {
|
||||||
handleErrorViewState(error, stackTrace);
|
handleErrorViewState(error, stackTrace);
|
||||||
}
|
}
|
||||||
@@ -131,10 +167,41 @@ abstract class BaseController extends GetxController
|
|||||||
|
|
||||||
void onDone() {}
|
void onDone() {}
|
||||||
|
|
||||||
Exception? _performFilterExceptionInError(dynamic error) {
|
bool _validateUrgentException(dynamic exception) {
|
||||||
logError('$runtimeType::_performFilterExceptionInError(): $error');
|
return exception is NoNetworkError
|
||||||
if (error is NoNetworkError || error is ConnectionTimeout || error is InternalServerError) {
|
|| exception is BadCredentialsException
|
||||||
if (PlatformInfo.isWeb && currentOverlayContext != null && currentContext != null) {
|
|| 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(
|
appToast.showToastMessage(
|
||||||
currentOverlayContext!,
|
currentOverlayContext!,
|
||||||
AppLocalizations.of(currentContext!).no_internet_connection,
|
AppLocalizations.of(currentContext!).no_internet_connection,
|
||||||
@@ -143,43 +210,22 @@ abstract class BaseController extends GetxController
|
|||||||
leadingSVGIcon: imagePaths.icNotConnection,
|
leadingSVGIcon: imagePaths.icNotConnection,
|
||||||
backgroundColor: AppColor.textFieldErrorBorderColor,
|
backgroundColor: AppColor.textFieldErrorBorderColor,
|
||||||
textColor: Colors.white,
|
textColor: Colors.white,
|
||||||
infinityToast: true,
|
infinityToast: true,);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return error;
|
} else if (exception is ConnectionError) {
|
||||||
} 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) {
|
|
||||||
if (currentOverlayContext != null && currentContext != null) {
|
if (currentOverlayContext != null && currentContext != null) {
|
||||||
appToast.showToastErrorMessage(
|
appToast.showToastErrorMessage(
|
||||||
currentOverlayContext!,
|
currentOverlayContext!,
|
||||||
AppLocalizations.of(currentContext!).connectionError
|
AppLocalizations.of(currentContext!).connectionError);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
} else if (exception is BadCredentialsException) {
|
||||||
|
_executeBeforeUnloadAndLogOut();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!authorizationInterceptors.isAppRunning) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_executeBeforeUnloadAndLogOut(exception);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _executeBeforeUnloadAndLogOut(Exception? exception) async {
|
Future<void> _executeBeforeUnloadAndLogOut() async {
|
||||||
if (exception is BadCredentialsException || exception is ConnectionError) {
|
await executeBeforeUnload();
|
||||||
if (PlatformInfo.isWeb) {
|
clearDataAndGoToLoginPage();
|
||||||
await executeBeforeUnload();
|
|
||||||
}
|
|
||||||
clearDataAndGoToLoginPage();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleFailureViewState(Failure failure) async {
|
void handleFailureViewState(Failure failure) async {
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ mixin MessageDialogActionMixin {
|
|||||||
Color? actionButtonColor,
|
Color? actionButtonColor,
|
||||||
Color? cancelButtonColor,
|
Color? cancelButtonColor,
|
||||||
EdgeInsetsGeometry? marginIcon,
|
EdgeInsetsGeometry? marginIcon,
|
||||||
|
EdgeInsetsGeometry? paddingButton,
|
||||||
PopInvokedCallback? onPopInvoked,
|
PopInvokedCallback? onPopInvoked,
|
||||||
bool isArrangeActionButtonsVertical = false,
|
bool isArrangeActionButtonsVertical = false,
|
||||||
int? titleActionButtonMaxLines,
|
int? titleActionButtonMaxLines,
|
||||||
@@ -61,8 +62,9 @@ mixin MessageDialogActionMixin {
|
|||||||
: const EdgeInsetsDirectional.symmetric(horizontal: 24)
|
: const EdgeInsetsDirectional.symmetric(horizontal: 24)
|
||||||
)
|
)
|
||||||
..radiusButton(12)
|
..radiusButton(12)
|
||||||
|
..paddingButton(paddingButton)
|
||||||
..paddingContent(const EdgeInsets.only(left: 24, right: 24, bottom: 24, top: 12))
|
..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))
|
..styleTitle(titleStyle ?? const TextStyle(fontSize: 20, fontWeight: FontWeight.bold, color: Colors.black))
|
||||||
..styleContent(messageStyle ?? const TextStyle(fontSize: 14, fontWeight: FontWeight.normal, color: AppColor.colorContentEmail))
|
..styleContent(messageStyle ?? const TextStyle(fontSize: 14, fontWeight: FontWeight.normal, color: AppColor.colorContentEmail))
|
||||||
..styleTextCancelButton(cancelStyle ?? const TextStyle(fontSize: 17, fontWeight: FontWeight.w500, color: AppColor.colorTextButton))
|
..styleTextCancelButton(cancelStyle ?? const TextStyle(fontSize: 17, fontWeight: FontWeight.w500, color: AppColor.colorTextButton))
|
||||||
@@ -107,6 +109,7 @@ mixin MessageDialogActionMixin {
|
|||||||
..title(title ?? '')
|
..title(title ?? '')
|
||||||
..content(message)
|
..content(message)
|
||||||
..addIcon(icon)
|
..addIcon(icon)
|
||||||
|
..paddingButton(paddingButton)
|
||||||
..margin(const EdgeInsets.only(bottom: 42))
|
..margin(const EdgeInsets.only(bottom: 42))
|
||||||
..widthDialog(responsiveUtils.getSizeScreenWidth(context))
|
..widthDialog(responsiveUtils.getSizeScreenWidth(context))
|
||||||
..colorConfirmButton(actionButtonColor ?? AppColor.colorTextButton)
|
..colorConfirmButton(actionButtonColor ?? AppColor.colorTextButton)
|
||||||
@@ -117,7 +120,7 @@ mixin MessageDialogActionMixin {
|
|||||||
)
|
)
|
||||||
..marginIcon(EdgeInsets.zero)
|
..marginIcon(EdgeInsets.zero)
|
||||||
..paddingContent(const EdgeInsets.only(left: 44, right: 44, bottom: 24, top: 12))
|
..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))
|
..styleTitle(titleStyle ?? const TextStyle(fontSize: 20, fontWeight: FontWeight.bold, color: Colors.black))
|
||||||
..styleContent(messageStyle ?? const TextStyle(fontSize: 14, fontWeight: FontWeight.normal, color: AppColor.colorContentEmail))
|
..styleContent(messageStyle ?? const TextStyle(fontSize: 14, fontWeight: FontWeight.normal, color: AppColor.colorContentEmail))
|
||||||
..styleTextCancelButton(cancelStyle ?? const TextStyle(fontSize: 17, fontWeight: FontWeight.w500, color: AppColor.colorTextButton))
|
..styleTextCancelButton(cancelStyle ?? const TextStyle(fontSize: 17, fontWeight: FontWeight.w500, color: AppColor.colorTextButton))
|
||||||
@@ -187,6 +190,7 @@ mixin MessageDialogActionMixin {
|
|||||||
..title(title ?? '')
|
..title(title ?? '')
|
||||||
..content(message)
|
..content(message)
|
||||||
..addIcon(icon)
|
..addIcon(icon)
|
||||||
|
..paddingButton(paddingButton)
|
||||||
..colorConfirmButton(actionButtonColor ?? AppColor.colorTextButton)
|
..colorConfirmButton(actionButtonColor ?? AppColor.colorTextButton)
|
||||||
..colorCancelButton(cancelButtonColor ?? AppColor.colorCancelButton)
|
..colorCancelButton(cancelButtonColor ?? AppColor.colorCancelButton)
|
||||||
..marginIcon(icon != null ? const EdgeInsets.only(top: 24) : null)
|
..marginIcon(icon != null ? const EdgeInsets.only(top: 24) : null)
|
||||||
@@ -195,7 +199,7 @@ mixin MessageDialogActionMixin {
|
|||||||
: const EdgeInsetsDirectional.symmetric(horizontal: 24))
|
: const EdgeInsetsDirectional.symmetric(horizontal: 24))
|
||||||
..marginIcon(EdgeInsets.zero)
|
..marginIcon(EdgeInsets.zero)
|
||||||
..paddingContent(const EdgeInsets.only(left: 44, right: 44, bottom: 24, top: 12))
|
..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))
|
..styleTitle(titleStyle ?? const TextStyle(fontSize: 20, fontWeight: FontWeight.bold, color: Colors.black))
|
||||||
..styleContent(messageStyle ?? const TextStyle(fontSize: 14, fontWeight: FontWeight.normal, color: AppColor.colorContentEmail))
|
..styleContent(messageStyle ?? const TextStyle(fontSize: 14, fontWeight: FontWeight.normal, color: AppColor.colorContentEmail))
|
||||||
..styleTextCancelButton(cancelStyle ?? const TextStyle(fontSize: 17, fontWeight: FontWeight.w500, color: AppColor.colorTextButton))
|
..styleTextCancelButton(cancelStyle ?? const TextStyle(fontSize: 17, fontWeight: FontWeight.w500, color: AppColor.colorTextButton))
|
||||||
|
|||||||
@@ -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_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/domain/usecases/local_image_picker_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/upload/presentation/controller/upload_controller.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/localizations/app_localizations.dart';
|
||||||
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
||||||
import 'package:universal_html/html.dart' as html;
|
import 'package:universal_html/html.dart' as html;
|
||||||
@@ -164,7 +165,6 @@ class ComposerController extends BaseController with DragDropFileMixin implement
|
|||||||
FocusNode? ccAddressFocusNodeKeyboard;
|
FocusNode? ccAddressFocusNodeKeyboard;
|
||||||
FocusNode? bccAddressFocusNodeKeyboard;
|
FocusNode? bccAddressFocusNodeKeyboard;
|
||||||
|
|
||||||
StreamSubscription<html.Event>? _subscriptionOnBeforeUnload;
|
|
||||||
StreamSubscription<html.Event>? _subscriptionOnDragEnter;
|
StreamSubscription<html.Event>? _subscriptionOnDragEnter;
|
||||||
StreamSubscription<html.Event>? _subscriptionOnDragOver;
|
StreamSubscription<html.Event>? _subscriptionOnDragOver;
|
||||||
StreamSubscription<html.Event>? _subscriptionOnDragLeave;
|
StreamSubscription<html.Event>? _subscriptionOnDragLeave;
|
||||||
@@ -213,7 +213,7 @@ class ComposerController extends BaseController with DragDropFileMixin implement
|
|||||||
super.onInit();
|
super.onInit();
|
||||||
if (PlatformInfo.isWeb) {
|
if (PlatformInfo.isWeb) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
_listenBrowserTabRefresh();
|
_triggerBrowserEventListener();
|
||||||
});
|
});
|
||||||
richTextWebController = getBinding<RichTextWebController>();
|
richTextWebController = getBinding<RichTextWebController>();
|
||||||
} else {
|
} else {
|
||||||
@@ -244,7 +244,6 @@ class ComposerController extends BaseController with DragDropFileMixin implement
|
|||||||
emailContentsViewState.value = Right(UIClosedState());
|
emailContentsViewState.value = Right(UIClosedState());
|
||||||
identitySelected.value = null;
|
identitySelected.value = null;
|
||||||
listFromIdentities.clear();
|
listFromIdentities.clear();
|
||||||
_subscriptionOnBeforeUnload?.cancel();
|
|
||||||
_subscriptionOnDragEnter?.cancel();
|
_subscriptionOnDragEnter?.cancel();
|
||||||
_subscriptionOnDragOver?.cancel();
|
_subscriptionOnDragOver?.cancel();
|
||||||
_subscriptionOnDragLeave?.cancel();
|
_subscriptionOnDragLeave?.cancel();
|
||||||
@@ -353,11 +352,17 @@ class ComposerController extends BaseController with DragDropFileMixin implement
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void handleExceptionAction({Failure? failure, Exception? exception}) {
|
void handleUrgentExceptionOnMobile({Failure? failure, Exception? exception}) {
|
||||||
super.handleExceptionAction(failure: failure, exception: exception);
|
if (failure is GetAllIdentitiesFailure && exception is! BadCredentialsException) {
|
||||||
if (failure is GetAllIdentitiesFailure) {
|
_handleGetAllIdentitiesFailure();
|
||||||
_handleGetAllIdentitiesFailure(failure);
|
|
||||||
}
|
}
|
||||||
|
super.handleUrgentExceptionOnMobile(failure: failure, exception: exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> handleBrowserReloadAction(html.Event event) async {
|
||||||
|
await _removeComposerCacheOnWebInteractor.execute();
|
||||||
|
await _saveComposerCacheOnWebAction();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _listenStreamEvent() {
|
void _listenStreamEvent() {
|
||||||
@@ -377,13 +382,7 @@ class ComposerController extends BaseController with DragDropFileMixin implement
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _listenBrowserTabRefresh() {
|
void _triggerBrowserEventListener() {
|
||||||
_subscriptionOnBeforeUnload = html.window.onBeforeUnload.listen((event) async {
|
|
||||||
await _removeComposerCacheOnWebInteractor.execute();
|
|
||||||
|
|
||||||
await _saveComposerCacheOnWebAction();
|
|
||||||
});
|
|
||||||
|
|
||||||
_subscriptionOnDragEnter = html.window.onDragEnter.listen((event) {
|
_subscriptionOnDragEnter = html.window.onDragEnter.listen((event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
@@ -1966,9 +1965,8 @@ class ComposerController extends BaseController with DragDropFileMixin implement
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleGetAllIdentitiesFailure(GetAllIdentitiesFailure failure) async {
|
Future<void> _handleGetAllIdentitiesFailure() async {
|
||||||
log('ComposerController::_handleGetAllIdentitiesFailure:failure: $failure');
|
if (composerArguments.value?.emailActionType == EmailActionType.editSendingEmail) {
|
||||||
if (composerArguments.value?.emailActionType == EmailActionType.editSendingEmail && PlatformInfo.isMobile) {
|
|
||||||
final signatureContent = await htmlEditorApi?.getSignatureContent();
|
final signatureContent = await htmlEditorApi?.getSignatureContent();
|
||||||
log('ComposerController::_handleGetAllIdentitiesFailure:signatureContent: $signatureContent');
|
log('ComposerController::_handleGetAllIdentitiesFailure:signatureContent: $signatureContent');
|
||||||
if (signatureContent?.isNotEmpty == true) {
|
if (signatureContent?.isNotEmpty == true) {
|
||||||
|
|||||||
@@ -203,17 +203,6 @@ class AuthorizationInterceptors extends QueuedInterceptorsWrapper {
|
|||||||
|
|
||||||
String _getTokenAsBearerHeader(String token) => 'Bearer $token';
|
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<PersonalAccount> _updateCurrentAccount({required TokenOIDC tokenOIDC}) async {
|
Future<PersonalAccount> _updateCurrentAccount({required TokenOIDC tokenOIDC}) async {
|
||||||
final currentAccount = await _accountCacheManager.getCurrentAccount();
|
final currentAccount = await _accountCacheManager.getCurrentAccount();
|
||||||
|
|
||||||
|
|||||||
@@ -172,8 +172,8 @@ class LoginController extends ReloadableController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void handleExceptionAction({Failure? failure, Exception? exception}) {
|
void handleUrgentException({Failure? failure, Exception? exception}) {
|
||||||
logError('LoginController::handleExceptionAction:exception: $exception | failure: ${failure.runtimeType}');
|
logError('LoginController::handleUrgentException:Exception: $exception | Failure: $failure');
|
||||||
if (failure is CheckOIDCIsAvailableFailure ||
|
if (failure is CheckOIDCIsAvailableFailure ||
|
||||||
failure is GetStoredOidcConfigurationFailure ||
|
failure is GetStoredOidcConfigurationFailure ||
|
||||||
failure is GetOIDCConfigurationFailure ||
|
failure is GetOIDCConfigurationFailure ||
|
||||||
@@ -185,7 +185,7 @@ class LoginController extends ReloadableController {
|
|||||||
} else if (failure is GetSessionFailure) {
|
} else if (failure is GetSessionFailure) {
|
||||||
clearAllData();
|
clearAllData();
|
||||||
} else {
|
} else {
|
||||||
super.handleExceptionAction(failure: failure, exception: exception);
|
super.handleUrgentException(failure: failure, exception: exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+13
-7
@@ -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/routes/route_utils.dart';
|
||||||
import 'package:tmail_ui_user/main/utils/email_receive_manager.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:tmail_ui_user/main/utils/ios_notification_manager.dart';
|
||||||
|
import 'package:universal_html/html.dart' as html;
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
class MailboxDashBoardController extends ReloadableController {
|
class MailboxDashBoardController extends ReloadableController {
|
||||||
@@ -407,14 +408,19 @@ class MailboxDashBoardController extends ReloadableController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void handleExceptionAction({Failure? failure, Exception? exception}) {
|
void handleUrgentExceptionOnMobile({Failure? failure, Exception? exception}) {
|
||||||
super.handleExceptionAction(failure: failure, exception: exception);
|
if (failure is SendEmailFailure && exception is NoNetworkError) {
|
||||||
if (failure is SendEmailFailure &&
|
|
||||||
exception is NoNetworkError &&
|
|
||||||
PlatformInfo.isMobile
|
|
||||||
) {
|
|
||||||
log('MailboxDashBoardController::handleExceptionAction(): $failure');
|
|
||||||
_storeSendingEmailInCaseOfSendingFailureInMobile(failure);
|
_storeSendingEmailInCaseOfSendingFailureInMobile(failure);
|
||||||
|
} else {
|
||||||
|
super.handleUrgentExceptionOnMobile(failure: failure, exception: exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> handleBrowserBeforeReloadAction(html.Event event) async {
|
||||||
|
if (event is html.BeforeUnloadEvent
|
||||||
|
&& composerOverlayState.value == ComposerOverlayState.active) {
|
||||||
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -70,7 +70,7 @@ class DeleteIdentityDialogBuilder extends StatelessWidget {
|
|||||||
fontSize: 17,
|
fontSize: 17,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
color: Colors.white))
|
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)
|
..backgroundColor(Colors.black26)
|
||||||
..onConfirmButtonAction(AppLocalizations.of(context).delete, () =>
|
..onConfirmButtonAction(AppLocalizations.of(context).delete, () =>
|
||||||
onDeleteIdentityAction.call())
|
onDeleteIdentityAction.call())
|
||||||
|
|||||||
@@ -220,11 +220,10 @@ class ThreadController extends BaseController with EmailActionController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void handleExceptionAction({Failure? failure, Exception? exception}) {
|
void handleUrgentException({Failure? failure, Exception? exception}) {
|
||||||
super.handleExceptionAction(failure: failure, exception: exception);
|
|
||||||
logError('ThreadController::handleExceptionAction(): failure: $failure | exception: $exception');
|
|
||||||
_resetLoadingMore();
|
_resetLoadingMore();
|
||||||
clearState();
|
clearState();
|
||||||
|
super.handleUrgentException(failure: failure, exception: exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _resetLoadingMore() {
|
void _resetLoadingMore() {
|
||||||
|
|||||||
@@ -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": "Initializing data...",
|
||||||
"@initializing_data": {
|
"@initializing_data": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
@@ -3933,5 +3933,23 @@
|
|||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders_order": [],
|
"placeholders_order": [],
|
||||||
"placeholders": {}
|
"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": {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4113,4 +4113,25 @@ class AppLocalizations {
|
|||||||
name: 'requestReadReceiptHasBeenDisabled'
|
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',
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user