diff --git a/lib/features/identity_creator/data/datasource/identity_creator_data_source.dart b/lib/features/identity_creator/data/datasource/identity_creator_data_source.dart index f9fc955c2..f29b893f4 100644 --- a/lib/features/identity_creator/data/datasource/identity_creator_data_source.dart +++ b/lib/features/identity_creator/data/datasource/identity_creator_data_source.dart @@ -7,4 +7,10 @@ abstract class IdentityCreatorDataSource { AccountId accountId, UserName userName, {required IdentityCache identityCache}); + + Future getIdentityCacheOnWeb( + AccountId accountId, + UserName userName); + + Future removeIdentityCacheOnWeb(); } \ No newline at end of file diff --git a/lib/features/identity_creator/data/datasource_impl/local_identity_creator_data_source_impl.dart b/lib/features/identity_creator/data/datasource_impl/local_identity_creator_data_source_impl.dart index e6d343c6c..78a306dd8 100644 --- a/lib/features/identity_creator/data/datasource_impl/local_identity_creator_data_source_impl.dart +++ b/lib/features/identity_creator/data/datasource_impl/local_identity_creator_data_source_impl.dart @@ -1,5 +1,7 @@ import 'dart:convert'; +import 'package:collection/collection.dart'; +import 'package:core/domain/exceptions/web_session_exception.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/user_name.dart'; import 'package:model/extensions/account_id_extensions.dart'; @@ -31,6 +33,31 @@ class LocalIdentityCreatorDataSourceImpl implements IdentityCreatorDataSource { window.sessionStorage.addAll(entries); }).catchError(_exceptionThrower.throwException); } + + @override + Future getIdentityCacheOnWeb( + AccountId accountId, + UserName userName + ) async { + return Future.sync(() { + final cacheKey = _generateTupleKey(accountId, userName); + final result = window.sessionStorage.entries.firstWhereOrNull( + (entry) => entry.key == cacheKey); + if (result != null) { + return IdentityCacheModel.fromJson(jsonDecode(result.value)); + } else { + throw NotFoundInWebSessionException(); + } + }).catchError(_exceptionThrower.throwException); + } + + @override + Future removeIdentityCacheOnWeb() async { + return Future.sync(() { + window.sessionStorage.removeWhere( + (key, value) => key.startsWith(LocalIdentityCreatorDataSourceImpl.sessionStorageKeyword)); + }).catchError(_exceptionThrower.throwException); + } String _generateTupleKey(AccountId accountId, UserName userName) { return TupleKey( diff --git a/lib/features/identity_creator/data/repository/identity_creator_repository_impl.dart b/lib/features/identity_creator/data/repository/identity_creator_repository_impl.dart index f75620929..56cfe9869 100644 --- a/lib/features/identity_creator/data/repository/identity_creator_repository_impl.dart +++ b/lib/features/identity_creator/data/repository/identity_creator_repository_impl.dart @@ -18,4 +18,16 @@ class IdentityCreatorRepositoryImpl implements IdentityCreatorRepository { accountId, userName, identityCache: identityCache); + + @override + Future getIdentityCacheOnWeb( + AccountId accountId, + UserName userName + ) => _identityCreatorDataSource.getIdentityCacheOnWeb( + accountId, + userName); + + @override + Future removeIdentityCacheOnWeb() + => _identityCreatorDataSource.removeIdentityCacheOnWeb(); } \ No newline at end of file diff --git a/lib/features/identity_creator/domain/repository/identity_creator_repository.dart b/lib/features/identity_creator/domain/repository/identity_creator_repository.dart index 06c606d12..fced6a67b 100644 --- a/lib/features/identity_creator/domain/repository/identity_creator_repository.dart +++ b/lib/features/identity_creator/domain/repository/identity_creator_repository.dart @@ -7,4 +7,10 @@ abstract class IdentityCreatorRepository { AccountId accountId, UserName userName, {required IdentityCache identityCache}); + + Future getIdentityCacheOnWeb( + AccountId accountId, + UserName userName); + + Future removeIdentityCacheOnWeb(); } \ No newline at end of file diff --git a/lib/features/identity_creator/domain/state/get_identity_cache_on_web_state.dart b/lib/features/identity_creator/domain/state/get_identity_cache_on_web_state.dart new file mode 100644 index 000000000..b455c4c53 --- /dev/null +++ b/lib/features/identity_creator/domain/state/get_identity_cache_on_web_state.dart @@ -0,0 +1,18 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:tmail_ui_user/features/identity_creator/domain/model/identity_cache.dart'; + +class GettingIdentityCacheOnWeb extends LoadingState {} + +class GetIdentityCacheOnWebSuccess extends UIState { + GetIdentityCacheOnWebSuccess(this.identityCache); + + final IdentityCache? identityCache; + + @override + List get props => [identityCache]; +} + +class GetIdentityCacheOnWebFailure extends FeatureFailure { + GetIdentityCacheOnWebFailure({super.exception}); +} \ No newline at end of file diff --git a/lib/features/identity_creator/domain/state/remove_identity_cache_on_web_state.dart b/lib/features/identity_creator/domain/state/remove_identity_cache_on_web_state.dart new file mode 100644 index 000000000..8e932b945 --- /dev/null +++ b/lib/features/identity_creator/domain/state/remove_identity_cache_on_web_state.dart @@ -0,0 +1,10 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; + +class RemovingIdentityCacheOnWeb extends LoadingState {} + +class RemoveIdentityCacheOnWebSuccess extends UIState {} + +class RemoveIdentityCacheOnWebFailure extends FeatureFailure { + RemoveIdentityCacheOnWebFailure({super.exception}); +} \ No newline at end of file diff --git a/lib/features/identity_creator/domain/usecase/get_identity_cache_on_web_interactor.dart b/lib/features/identity_creator/domain/usecase/get_identity_cache_on_web_interactor.dart new file mode 100644 index 000000000..9df1e8e26 --- /dev/null +++ b/lib/features/identity_creator/domain/usecase/get_identity_cache_on_web_interactor.dart @@ -0,0 +1,31 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:core/utils/app_logger.dart'; +import 'package:dartz/dartz.dart'; +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/core/user_name.dart'; +import 'package:tmail_ui_user/features/identity_creator/domain/repository/identity_creator_repository.dart'; +import 'package:tmail_ui_user/features/identity_creator/domain/state/get_identity_cache_on_web_state.dart'; + +class GetIdentityCacheOnWebInteractor { + GetIdentityCacheOnWebInteractor(this._identityCreatorRepository); + + final IdentityCreatorRepository _identityCreatorRepository; + + Stream> execute( + AccountId accountId, + UserName userName + ) async* { + try { + yield Right(GettingIdentityCacheOnWeb()); + final result = await _identityCreatorRepository.getIdentityCacheOnWeb( + accountId, + userName); + + yield Right(GetIdentityCacheOnWebSuccess(result)); + } catch (exception) { + logError("$runtimeType::execute: $exception"); + yield Left(GetIdentityCacheOnWebFailure(exception: exception)); + } + } +} \ No newline at end of file diff --git a/lib/features/identity_creator/domain/usecase/remove_identity_cache_on_web_interactor.dart b/lib/features/identity_creator/domain/usecase/remove_identity_cache_on_web_interactor.dart new file mode 100644 index 000000000..c26a026fb --- /dev/null +++ b/lib/features/identity_creator/domain/usecase/remove_identity_cache_on_web_interactor.dart @@ -0,0 +1,24 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:core/utils/app_logger.dart'; +import 'package:dartz/dartz.dart'; +import 'package:tmail_ui_user/features/identity_creator/domain/repository/identity_creator_repository.dart'; +import 'package:tmail_ui_user/features/identity_creator/domain/state/remove_identity_cache_on_web_state.dart'; + +class RemoveIdentityCacheOnWebInteractor { + RemoveIdentityCacheOnWebInteractor(this._identityCreatorRepository); + + final IdentityCreatorRepository _identityCreatorRepository; + + Stream> execute() async* { + try { + yield Right(RemovingIdentityCacheOnWeb()); + await _identityCreatorRepository.removeIdentityCacheOnWeb(); + + yield Right(RemoveIdentityCacheOnWebSuccess()); + } catch (exception) { + logError("$runtimeType::execute: $exception"); + yield Left(RemoveIdentityCacheOnWebFailure(exception: exception)); + } + } +} \ No newline at end of file diff --git a/lib/features/identity_creator/presentation/identity_creator_controller.dart b/lib/features/identity_creator/presentation/identity_creator_controller.dart index d90fbc76c..216c1097e 100644 --- a/lib/features/identity_creator/presentation/identity_creator_controller.dart +++ b/lib/features/identity_creator/presentation/identity_creator_controller.dart @@ -352,43 +352,31 @@ class IdentityCreatorController extends BaseController with DragDropFileMixin im listEmailAddressOfReplyTo.value = listEmailAddressOfReplyTo.toSet().toList(); listEmailAddressDefault.value = listEmailAddressDefault.toSet().toList(); - if (actionType.value == IdentityActionType.edit && identity != null) { - if (identity?.replyTo?.isNotEmpty == true) { - try { - replyToOfIdentity.value = listEmailAddressOfReplyTo - .firstWhere((emailAddress) => emailAddress == identity?.replyTo!.first); - } catch(e) { - replyToOfIdentity.value = noneEmailAddress; - } - } else { + if (identity?.replyTo?.isNotEmpty == true) { + replyToOfIdentity.value = listEmailAddressOfReplyTo + .firstWhereOrNull((emailAddress) => emailAddress == identity!.replyTo!.first); + + if (replyToOfIdentity.value == null && identity!.replyTo!.first == noneEmailAddress) { replyToOfIdentity.value = noneEmailAddress; } - - if (identity?.bcc?.isNotEmpty == true) { - bccOfIdentity.value = identity?.bcc!.first; - inputBccIdentityController.text = identity?.bcc!.first.emailAddress ?? ''; - } else { - bccOfIdentity.value = null; - } - - if (identity?.email?.isNotEmpty == true) { - try { - emailOfIdentity.value = listEmailAddressDefault - .firstWhere((emailAddress) => emailAddress.email == identity?.email); - } catch(e) { - emailOfIdentity.value = null; - } - } else { - emailOfIdentity.value = listEmailAddressDefault.isNotEmpty - ? listEmailAddressDefault.first - : null; - } + } else if (actionType.value == IdentityActionType.edit) { + replyToOfIdentity.value = noneEmailAddress; } else { replyToOfIdentity.value = null; + } + + if (identity?.bcc?.isNotEmpty == true) { + bccOfIdentity.value = identity!.bcc!.first; + inputBccIdentityController.text = identity!.bcc!.first.emailAddress; + } else { bccOfIdentity.value = null; - emailOfIdentity.value = listEmailAddressDefault.isNotEmpty - ? listEmailAddressDefault.first - : null; + } + + if (identity?.email?.isNotEmpty == true) { + emailOfIdentity.value = listEmailAddressDefault + .firstWhereOrNull((emailAddress) => emailAddress.email == identity!.email); + } else { + emailOfIdentity.value = listEmailAddressDefault.firstOrNull; } } @@ -486,16 +474,19 @@ class IdentityCreatorController extends BaseController with DragDropFileMixin im Future<({ Identity identity, PublicAssetsInIdentityArguments publicAssetsInIdentityArguments - })> _generateIdentityAndPublicAssetArguments() async { + })> _generateIdentityAndPublicAssetArguments({bool forCache = false}) async { final signatureHtmlText = PlatformInfo.isWeb ? contentHtmlEditor : await _getSignatureHtmlText(); final bccAddress = bccOfIdentity.value != null && bccOfIdentity.value != noneEmailAddress ? {bccOfIdentity.value!} : {}; - final replyToAddress = replyToOfIdentity.value != null && replyToOfIdentity.value != noneEmailAddress - ? {replyToOfIdentity.value!} - : {}; + Set replyToAddress; + if (replyToOfIdentity.value != null && (replyToOfIdentity.value != noneEmailAddress || forCache)) { + replyToAddress = {replyToOfIdentity.value!}; + } else { + replyToAddress = {}; + } final sortOrder = isDefaultIdentitySupported.isTrue ? UnsignedInt(isDefaultIdentity.value ? 0 : 100) @@ -924,7 +915,8 @@ class IdentityCreatorController extends BaseController with DragDropFileMixin im Future _saveIdentityCacheOnWebAction() async { if (accountId != null && session?.username != null) { - final cacheArguments = await _generateIdentityAndPublicAssetArguments(); + final cacheArguments = await _generateIdentityAndPublicAssetArguments( + forCache: true); final identityCache = IdentityCache( identity: cacheArguments.identity, identityActionType: actionType.value, diff --git a/lib/features/identity_creator/presentation/restore_identity_cache_interactor_bindings.dart b/lib/features/identity_creator/presentation/restore_identity_cache_interactor_bindings.dart new file mode 100644 index 000000000..4642f3760 --- /dev/null +++ b/lib/features/identity_creator/presentation/restore_identity_cache_interactor_bindings.dart @@ -0,0 +1,61 @@ +import 'package:get/get.dart'; +import 'package:tmail_ui_user/features/base/interactors_bindings.dart'; +import 'package:tmail_ui_user/features/identity_creator/data/datasource/identity_creator_data_source.dart'; +import 'package:tmail_ui_user/features/identity_creator/data/datasource_impl/local_identity_creator_data_source_impl.dart'; +import 'package:tmail_ui_user/features/identity_creator/data/repository/identity_creator_repository_impl.dart'; +import 'package:tmail_ui_user/features/identity_creator/domain/repository/identity_creator_repository.dart'; +import 'package:tmail_ui_user/features/identity_creator/domain/usecase/get_identity_cache_on_web_interactor.dart'; +import 'package:tmail_ui_user/features/identity_creator/domain/usecase/remove_identity_cache_on_web_interactor.dart'; +import 'package:tmail_ui_user/main/bindings/network/binding_tag.dart'; +import 'package:tmail_ui_user/main/exceptions/cache_exception_thrower.dart'; + +class RestoreIdentityCacheInteractorBindings extends InteractorsBindings { + static const _tag = BindingTag.restoreIdentityCacheInteractorBindingsTag; + + @override + void bindingsDataSource() { + Get.lazyPut( + () => Get.find(tag: _tag), + tag: _tag); + } + + @override + void bindingsDataSourceImpl() { + Get.lazyPut( + () => LocalIdentityCreatorDataSourceImpl( + Get.find()), + tag: _tag); + } + + @override + void bindingsInteractor() { + Get.lazyPut( + () => GetIdentityCacheOnWebInteractor( + Get.find(tag: _tag)), + tag: _tag); + Get.lazyPut( + () => RemoveIdentityCacheOnWebInteractor( + Get.find(tag: _tag)), + tag: _tag); + } + + @override + void bindingsRepository() { + Get.lazyPut( + () => Get.find(tag: _tag), + tag: _tag); + } + + @override + void bindingsRepositoryImpl() { + Get.lazyPut( + () => IdentityCreatorRepositoryImpl( + Get.find(tag: _tag)), + tag: _tag); + } + + void close() { + Get.delete(tag: _tag); + Get.delete(tag: _tag); + } +} \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart b/lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart index ace76dc1e..61f04d8fc 100644 --- a/lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart +++ b/lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart @@ -32,6 +32,11 @@ import 'package:tmail_ui_user/features/email/domain/usecases/unsubscribe_email_i import 'package:tmail_ui_user/features/email/presentation/bindings/email_bindings.dart'; import 'package:tmail_ui_user/features/home/domain/repository/session_repository.dart'; import 'package:tmail_ui_user/features/home/domain/usecases/store_session_interactor.dart'; +import 'package:tmail_ui_user/features/identity_creator/data/datasource/identity_creator_data_source.dart'; +import 'package:tmail_ui_user/features/identity_creator/data/datasource_impl/local_identity_creator_data_source_impl.dart'; +import 'package:tmail_ui_user/features/identity_creator/data/repository/identity_creator_repository_impl.dart'; +import 'package:tmail_ui_user/features/identity_creator/domain/repository/identity_creator_repository.dart'; +import 'package:tmail_ui_user/features/identity_creator/domain/usecase/get_identity_cache_on_web_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/data/datasource/mailbox_datasource.dart'; import 'package:tmail_ui_user/features/mailbox/data/datasource/state_datasource.dart'; import 'package:tmail_ui_user/features/mailbox/data/datasource_impl/mailbox_cache_datasource_impl.dart'; @@ -158,6 +163,7 @@ class MailboxDashBoardBindings extends BaseBindings { Get.find(), Get.find(), Get.find(), + Get.find(), Get.find(), Get.find(), Get.find(), @@ -197,6 +203,7 @@ class MailboxDashBoardBindings extends BaseBindings { Get.lazyPut(() => Get.find()); Get.lazyPut( () => Get.find()); + Get.lazyPut(() => Get.find()); } @override @@ -260,6 +267,9 @@ class MailboxDashBoardBindings extends BaseBindings { Get.find(), Get.find() )); + Get.lazyPut(() => LocalIdentityCreatorDataSourceImpl( + Get.find() + )); } @override @@ -346,6 +356,9 @@ class MailboxDashBoardBindings extends BaseBindings { Get.find(), Get.find() )); + Get.lazyPut(() => GetIdentityCacheOnWebInteractor( + Get.find() + )); } @override @@ -359,6 +372,7 @@ class MailboxDashBoardBindings extends BaseBindings { Get.lazyPut(() => Get.find()); Get.lazyPut(() => Get.find()); Get.lazyPut(() => Get.find()); + Get.lazyPut(() => Get.find()); } @override @@ -403,5 +417,8 @@ class MailboxDashBoardBindings extends BaseBindings { Get.find(), )); Get.lazyPut(() => ServerSettingsRepositoryImpl(Get.find())); + Get.lazyPut(() => IdentityCreatorRepositoryImpl( + Get.find() + )); } } \ No newline at end of file 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 e0726a858..0589bf6d8 100644 --- a/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart @@ -72,6 +72,8 @@ import 'package:tmail_ui_user/features/email/presentation/utils/email_utils.dart import 'package:tmail_ui_user/features/email_recovery/presentation/model/email_recovery_arguments.dart'; import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.dart'; import 'package:tmail_ui_user/features/home/domain/usecases/store_session_interactor.dart'; +import 'package:tmail_ui_user/features/identity_creator/domain/state/get_identity_cache_on_web_state.dart'; +import 'package:tmail_ui_user/features/identity_creator/domain/usecase/get_identity_cache_on_web_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/domain/exceptions/mailbox_exception.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart'; import 'package:tmail_ui_user/features/mailbox/domain/state/mark_as_mailbox_read_state.dart'; @@ -174,6 +176,7 @@ class MailboxDashBoardController extends ReloadableController with UserSettingPo final DeleteEmailPermanentlyInteractor _deleteEmailPermanentlyInteractor; final MarkAsMailboxReadInteractor _markAsMailboxReadInteractor; final GetComposerCacheOnWebInteractor _getEmailCacheOnWebInteractor; + final GetIdentityCacheOnWebInteractor _getIdentityCacheOnWebInteractor; final MarkAsEmailReadInteractor _markAsEmailReadInteractor; final MarkAsStarEmailInteractor _markAsStarEmailInteractor; final MarkAsMultipleEmailReadInteractor _markAsMultipleEmailReadInteractor; @@ -257,6 +260,7 @@ class MailboxDashBoardController extends ReloadableController with UserSettingPo this._deleteEmailPermanentlyInteractor, this._markAsMailboxReadInteractor, this._getEmailCacheOnWebInteractor, + this._getIdentityCacheOnWebInteractor, this._markAsEmailReadInteractor, this._markAsStarEmailInteractor, this._markAsMultipleEmailReadInteractor, @@ -310,6 +314,15 @@ class MailboxDashBoardController extends ReloadableController with UserSettingPo sessionCurrent!.username)); } + void _handleIdentityCache() async { + if (accountId.value == null || sessionCurrent == null) return; + + consumeState( + _getIdentityCacheOnWebInteractor.execute( + accountId.value!, + sessionCurrent!.username)); + } + @override void handleSuccessViewState(Success success) { super.handleSuccessViewState(success); @@ -384,6 +397,8 @@ class MailboxDashBoardController extends ReloadableController with UserSettingPo _handleGetAllIdentitiesSuccess(success); } else if (success is GetComposerCacheSuccess) { goToComposer(ComposerArguments.fromSessionStorageBrowser(success.composerCache)); + } else if (success is GetIdentityCacheOnWebSuccess) { + goToSettings(); } } @@ -411,6 +426,8 @@ class MailboxDashBoardController extends ReloadableController with UserSettingPo } else if (failure is EmptySpamFolderFailure || failure is MoveMultipleEmailToMailboxFailure) { toastManager.showMessageFailure(failure); + } else if (failure is GetComposerCacheFailure) { + _handleIdentityCache(); } } diff --git a/lib/features/manage_account/presentation/profiles/identities/identities_controller.dart b/lib/features/manage_account/presentation/profiles/identities/identities_controller.dart index 77ee0ef25..9d8f66de4 100644 --- a/lib/features/manage_account/presentation/profiles/identities/identities_controller.dart +++ b/lib/features/manage_account/presentation/profiles/identities/identities_controller.dart @@ -13,8 +13,17 @@ import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/capability/capability_identifier.dart'; import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:jmap_dart_client/jmap/identities/identity.dart'; -import 'package:tmail_ui_user/features/base/base_controller.dart'; +import 'package:model/extensions/identity_request_dto_extension.dart'; +import 'package:tmail_ui_user/features/base/before_reconnect_handler.dart'; +import 'package:tmail_ui_user/features/base/before_reconnect_manager.dart'; +import 'package:tmail_ui_user/features/base/reloadable/reloadable_controller.dart'; +import 'package:tmail_ui_user/features/identity_creator/domain/model/identity_cache.dart'; +import 'package:tmail_ui_user/features/identity_creator/domain/state/get_identity_cache_on_web_state.dart'; +import 'package:tmail_ui_user/features/identity_creator/domain/usecase/get_identity_cache_on_web_interactor.dart'; +import 'package:tmail_ui_user/features/identity_creator/domain/usecase/remove_identity_cache_on_web_interactor.dart'; +import 'package:tmail_ui_user/features/identity_creator/domain/usecase/save_identity_cache_on_web_interactor.dart'; import 'package:tmail_ui_user/features/identity_creator/presentation/model/identity_creator_arguments.dart'; +import 'package:tmail_ui_user/features/identity_creator/presentation/restore_identity_cache_interactor_bindings.dart'; import 'package:tmail_ui_user/features/manage_account/domain/model/create_new_identity_request.dart'; import 'package:tmail_ui_user/features/manage_account/domain/model/edit_identity_request.dart'; import 'package:tmail_ui_user/features/manage_account/domain/state/create_new_default_identity_state.dart'; @@ -47,7 +56,7 @@ import 'package:tmail_ui_user/main/routes/app_routes.dart'; import 'package:tmail_ui_user/main/routes/dialog_router.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart'; -class IdentitiesController extends BaseController { +class IdentitiesController extends ReloadableController implements BeforeReconnectHandler { final accountDashBoardController = Get.find(); @@ -58,11 +67,16 @@ class IdentitiesController extends BaseController { final EditIdentityInteractor _editIdentityInteractor; final EditDefaultIdentityInteractor _editDefaultIdentityInteractor; final TransformHtmlSignatureInteractor _transformHtmlSignatureInteractor; + final SaveIdentityCacheOnWebInteractor _saveIdentityCacheOnWebInteractor; final identitySelected = Rxn(); final signatureSelected = Rxn(); final listAllIdentities = [].obs; + dynamic newIdentityArguments; + + final _beforeReconnectManager = Get.find(); + IdentitiesController( this._getAllIdentitiesInteractor, this._deleteIdentityInteractor, @@ -70,12 +84,15 @@ class IdentitiesController extends BaseController { this._editIdentityInteractor, this._createNewDefaultIdentityInteractor, this._editDefaultIdentityInteractor, - this._transformHtmlSignatureInteractor + this._transformHtmlSignatureInteractor, + this._saveIdentityCacheOnWebInteractor ); @override void onInit() { _registerObxStreamListener(); + RestoreIdentityCacheInteractorBindings().dependencies(); + _beforeReconnectManager.addListener(onBeforeReconnect); super.onInit(); } @@ -96,6 +113,8 @@ class IdentitiesController extends BaseController { signatureSelected.value = success.signature; } else if (success is RemoveIdentityFromPublicAssetsSuccessState) { _deleteIdentityAction(success.identityId); + } else if (success is GetIdentityCacheOnWebSuccess) { + _openIdentityEditorFromCache(success); } } @@ -108,6 +127,8 @@ class IdentitiesController extends BaseController { _deleteIdentityAction(failure.identityId); } else if (failure is NotFoundAnyPublicAssetsFailureState) { _deleteIdentityAction(failure.identityId); + } else if (failure is GetIdentityCacheOnWebFailure) { + _removeIdentityCache(); } } @@ -117,6 +138,7 @@ class IdentitiesController extends BaseController { if (accountId != null && session != null) { _getAllIdentities(session, accountId); _injectCleanUpPublicAssetsInteractorBindings(session, accountId); + _handleIdentityCache(); } }); } @@ -164,7 +186,7 @@ class IdentitiesController extends BaseController { if (accountId != null && session != null) { final arguments = IdentityCreatorArguments(accountId, session); - final newIdentityArguments = PlatformInfo.isWeb + newIdentityArguments = PlatformInfo.isWeb ? await DialogRouter.pushGeneralDialog(routeName: AppRoutes.identityCreator, arguments: arguments) : await push(AppRoutes.identityCreator, arguments: arguments); @@ -189,6 +211,8 @@ class IdentitiesController extends BaseController { } void _createNewIdentitySuccess(CreateNewIdentitySuccess success) { + _removeIdentityCache(); + if (currentOverlayContext != null && currentContext != null) { appToast.showToastSuccessMessage( currentOverlayContext!, @@ -204,6 +228,8 @@ class IdentitiesController extends BaseController { } void _createNewDefaultIdentitySuccess(CreateNewDefaultIdentitySuccess success) { + _removeIdentityCache(); + if (currentOverlayContext != null && currentContext != null) { appToast.showToastSuccessMessage( currentOverlayContext!, @@ -318,7 +344,7 @@ class IdentitiesController extends BaseController { identity: identity, actionType: IdentityActionType.edit); - final newIdentityArguments = PlatformInfo.isWeb + newIdentityArguments = PlatformInfo.isWeb ? await DialogRouter.pushGeneralDialog(routeName: AppRoutes.identityCreator, arguments: arguments) : await push(AppRoutes.identityCreator, arguments: arguments); @@ -343,6 +369,8 @@ class IdentitiesController extends BaseController { } void _editIdentitySuccess(EditIdentitySuccess success) { + _removeIdentityCache(); + if (currentOverlayContext != null && currentContext != null) { appToast.showToastSuccessMessage( currentOverlayContext!, @@ -392,9 +420,108 @@ class IdentitiesController extends BaseController { publicAssetsInIdentityArguments: publicAssetsInIdentityArguments)); } + @override + void handleReloaded(Session session) { + log('IdentitiesController::handleReloaded:'); + _handleIdentityCache(); + } + + Future _openIdentityEditorFromCache(GetIdentityCacheOnWebSuccess success) async { + final identityCache = success.identityCache; + final accountId = accountDashBoardController.accountId.value; + final session = accountDashBoardController.sessionCurrent; + if (identityCache == null || accountId == null || session == null) return; + + final arguments = IdentityCreatorArguments( + accountId, + session, + identity: identityCache.identity, + isDefault: identityCache.isDefault, + publicAssetsInIdentityArguments: identityCache.publicAssetsInIdentityArguments, + actionType: identityCache.identityActionType); + + newIdentityArguments = PlatformInfo.isWeb + ? await DialogRouter.pushGeneralDialog(routeName: AppRoutes.identityCreator, arguments: arguments) + : await push(AppRoutes.identityCreator, arguments: arguments); + + if (newIdentityArguments == null) { + _removeIdentityCache(); + } + + if (newIdentityArguments is CreateNewIdentityRequest) { + _createNewIdentityAction(session, accountId, newIdentityArguments); + } else if (newIdentityArguments is EditIdentityRequest) { + _editIdentityAction(session, accountId, newIdentityArguments); + } + } + + void _handleIdentityCache() { + final session = accountDashBoardController.sessionCurrent; + final accountId = accountDashBoardController.accountId; + if (session == null + || accountId.value == null + || !PlatformInfo.isWeb) return; + + final getIdentityCacheOnWebInteractor = getBinding( + tag: BindingTag.restoreIdentityCacheInteractorBindingsTag); + if (getIdentityCacheOnWebInteractor == null) return; + + consumeState(getIdentityCacheOnWebInteractor.execute( + accountId.value!, + session.username)); + } + + void _removeIdentityCache() { + final removeIdentityCacheOnWebInteractor = getBinding( + tag: BindingTag.restoreIdentityCacheInteractorBindingsTag); + if (removeIdentityCacheOnWebInteractor == null) return; + consumeState(removeIdentityCacheOnWebInteractor.execute()); + } + + Future _saveIdentityCacheOnWebAction(dynamic requestArgument) async { + final session = accountDashBoardController.sessionCurrent; + final accountId = accountDashBoardController.accountId; + if (accountId.value == null || session?.username == null) return; + + IdentityCache? identityCache; + + switch (requestArgument) { + case (CreateNewIdentityRequest createNewIdentityRequest): + identityCache = IdentityCache( + identity: createNewIdentityRequest.newIdentity, + identityActionType: IdentityActionType.create, + isDefault: createNewIdentityRequest.isDefaultIdentity, + publicAssetsInIdentityArguments: createNewIdentityRequest.publicAssetsInIdentityArguments); + break; + case (EditIdentityRequest editIdentityRequest): + identityCache = IdentityCache( + identity: editIdentityRequest.identityRequest.toIdentityWithId(editIdentityRequest.identityId), + identityActionType: IdentityActionType.edit, + isDefault: editIdentityRequest.isDefaultIdentity, + publicAssetsInIdentityArguments: editIdentityRequest.publicAssetsInIdentityArguments); + break; + default: + break; + } + + if (identityCache == null) return; + + consumeState(_saveIdentityCacheOnWebInteractor.execute( + accountId.value!, + session!.username, + identityCache: identityCache + )); + } + + @override + Future onBeforeReconnect() => _saveIdentityCacheOnWebAction(newIdentityArguments); + @override void onClose() { CleanUpPublicAssetsInteractorBindings().close(); + RestoreIdentityCacheInteractorBindings().close(); + newIdentityArguments = null; + _beforeReconnectManager.removeListener(onBeforeReconnect); super.onClose(); } } \ No newline at end of file diff --git a/lib/features/manage_account/presentation/profiles/identities/identity_bindings.dart b/lib/features/manage_account/presentation/profiles/identities/identity_bindings.dart index cf1f63c4c..f7a2c70e5 100644 --- a/lib/features/manage_account/presentation/profiles/identities/identity_bindings.dart +++ b/lib/features/manage_account/presentation/profiles/identities/identity_bindings.dart @@ -1,4 +1,5 @@ import 'package:get/get.dart'; +import 'package:tmail_ui_user/features/identity_creator/domain/usecase/save_identity_cache_on_web_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/create_new_default_identity_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/create_new_identity_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/delete_identity_interactor.dart'; @@ -22,7 +23,8 @@ class IdentityBindings extends Bindings { Get.find(), Get.find(), Get.find(), - Get.find() + Get.find(), + Get.find(), )); } } \ No newline at end of file diff --git a/lib/features/manage_account/presentation/profiles/identities/identity_interactors_bindings.dart b/lib/features/manage_account/presentation/profiles/identities/identity_interactors_bindings.dart index 495d435b3..7e3addd7d 100644 --- a/lib/features/manage_account/presentation/profiles/identities/identity_interactors_bindings.dart +++ b/lib/features/manage_account/presentation/profiles/identities/identity_interactors_bindings.dart @@ -1,6 +1,11 @@ import 'package:core/presentation/utils/html_transformer/html_transform.dart'; import 'package:get/get.dart'; import 'package:tmail_ui_user/features/base/interactors_bindings.dart'; +import 'package:tmail_ui_user/features/identity_creator/data/datasource/identity_creator_data_source.dart'; +import 'package:tmail_ui_user/features/identity_creator/data/datasource_impl/local_identity_creator_data_source_impl.dart'; +import 'package:tmail_ui_user/features/identity_creator/data/repository/identity_creator_repository_impl.dart'; +import 'package:tmail_ui_user/features/identity_creator/domain/repository/identity_creator_repository.dart'; +import 'package:tmail_ui_user/features/identity_creator/domain/usecase/save_identity_cache_on_web_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/data/datasource/identity_data_source.dart'; import 'package:tmail_ui_user/features/manage_account/data/datasource_impl/identity_data_source_impl.dart'; import 'package:tmail_ui_user/features/manage_account/data/network/identity_api.dart'; @@ -14,6 +19,7 @@ import 'package:tmail_ui_user/features/manage_account/domain/usecases/edit_ident import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_identities_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/transform_html_signature_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/utils/identity_utils.dart'; +import 'package:tmail_ui_user/main/exceptions/cache_exception_thrower.dart'; import 'package:tmail_ui_user/main/exceptions/remote_exception_thrower.dart'; class IdentityInteractorsBindings extends InteractorsBindings { @@ -27,6 +33,7 @@ class IdentityInteractorsBindings extends InteractorsBindings { @override void bindingsDataSource() { Get.lazyPut(() => Get.find()); + Get.lazyPut(() => Get.find()); } void _bindingsUtils() { @@ -39,6 +46,9 @@ class IdentityInteractorsBindings extends InteractorsBindings { Get.find(), Get.find(), Get.find())); + Get.lazyPut(() => LocalIdentityCreatorDataSourceImpl( + Get.find() + )); } @override @@ -56,15 +66,20 @@ class IdentityInteractorsBindings extends InteractorsBindings { Get.find(), Get.find())); Get.lazyPut(() => TransformHtmlSignatureInteractor(Get.find())); + Get.lazyPut(() => SaveIdentityCacheOnWebInteractor(Get.find())); } @override void bindingsRepository() { Get.lazyPut(() => Get.find()); + Get.lazyPut(() => Get.find()); } @override void bindingsRepositoryImpl() { Get.lazyPut(() => IdentityRepositoryImpl(Get.find())); + Get.lazyPut(() => IdentityCreatorRepositoryImpl( + Get.find() + )); } } \ No newline at end of file diff --git a/lib/main/bindings/network/binding_tag.dart b/lib/main/bindings/network/binding_tag.dart index 27123bd42..6e7ea8f9b 100644 --- a/lib/main/bindings/network/binding_tag.dart +++ b/lib/main/bindings/network/binding_tag.dart @@ -2,4 +2,5 @@ class BindingTag { static const isolateTag = 'isolateTag'; static const cleanUpPublicAssetsInteractorBindingsTag = 'CleanUpPublicAssetsInteractorBindingsTag'; static const publicAssetBindingsTag = 'PublicAssetBindingsTag'; + static const restoreIdentityCacheInteractorBindingsTag = 'RestoreIdentityCacheInteractorBindingsTag'; } \ No newline at end of file diff --git a/model/lib/extensions/identity_request_dto_extension.dart b/model/lib/extensions/identity_request_dto_extension.dart new file mode 100644 index 000000000..6c1060b57 --- /dev/null +++ b/model/lib/extensions/identity_request_dto_extension.dart @@ -0,0 +1,14 @@ +import 'package:jmap_dart_client/jmap/identities/identity.dart'; +import 'package:model/identity/identity_request_dto.dart'; + +extension IdentityRequestDtoExtension on IdentityRequestDto { + Identity toIdentityWithId(IdentityId identityId) => Identity( + id: identityId, + name: name, + replyTo: replyTo, + bcc: bcc, + textSignature: textSignature, + htmlSignature: htmlSignature, + sortOrder: sortOrder, + ); +} \ No newline at end of file diff --git a/test/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller_test.dart b/test/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller_test.dart index 15e641a1e..15c0a092f 100644 --- a/test/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller_test.dart +++ b/test/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller_test.dart @@ -30,6 +30,7 @@ import 'package:tmail_ui_user/features/email/domain/usecases/restore_deleted_mes import 'package:tmail_ui_user/features/email/domain/usecases/unsubscribe_email_interactor.dart'; import 'package:tmail_ui_user/features/home/domain/usecases/get_session_interactor.dart'; import 'package:tmail_ui_user/features/home/domain/usecases/store_session_interactor.dart'; +import 'package:tmail_ui_user/features/identity_creator/domain/usecase/get_identity_cache_on_web_interactor.dart'; import 'package:tmail_ui_user/features/login/data/network/interceptors/authorization_interceptors.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/delete_authority_oidc_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/delete_credential_interactor.dart'; @@ -171,6 +172,7 @@ const fallbackGenerators = { MockSpec(), MockSpec(), MockSpec(), + MockSpec(), ]) void main() { // mock mailbox dashboard controller direct dependencies @@ -179,6 +181,7 @@ void main() { MockDeleteEmailPermanentlyInteractor(); final markAsMailboxReadInteractor = MockMarkAsMailboxReadInteractor(); final getEmailCacheOnWebInteractor = MockGetComposerCacheOnWebInteractor(); + final getIdentityCacheOnWebInteractor = MockGetIdentityCacheOnWebInteractor(); final markAsEmailReadInteractor = MockMarkAsEmailReadInteractor(); final markAsStarEmailInteractor = MockMarkAsStarEmailInteractor(); final markAsMultipleEmailReadInteractor = @@ -317,6 +320,7 @@ void main() { deleteEmailPermanentlyInteractor, markAsMailboxReadInteractor, getEmailCacheOnWebInteractor, + getIdentityCacheOnWebInteractor, markAsEmailReadInteractor, markAsStarEmailInteractor, markAsMultipleEmailReadInteractor, diff --git a/test/features/mailbox_dashboard/presentation/view/mailbox_dashboard_view_widget_test.dart b/test/features/mailbox_dashboard/presentation/view/mailbox_dashboard_view_widget_test.dart index a3af7b8ef..a7bb6ea4e 100644 --- a/test/features/mailbox_dashboard/presentation/view/mailbox_dashboard_view_widget_test.dart +++ b/test/features/mailbox_dashboard/presentation/view/mailbox_dashboard_view_widget_test.dart @@ -28,6 +28,7 @@ import 'package:tmail_ui_user/features/email/domain/usecases/restore_deleted_mes import 'package:tmail_ui_user/features/email/domain/usecases/unsubscribe_email_interactor.dart'; import 'package:tmail_ui_user/features/home/domain/usecases/get_session_interactor.dart'; import 'package:tmail_ui_user/features/home/domain/usecases/store_session_interactor.dart'; +import 'package:tmail_ui_user/features/identity_creator/domain/usecase/get_identity_cache_on_web_interactor.dart'; import 'package:tmail_ui_user/features/login/data/network/interceptors/authorization_interceptors.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/delete_authority_oidc_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/delete_credential_interactor.dart'; @@ -170,13 +171,15 @@ const fallbackGenerators = { MockSpec(), MockSpec(), MockSpec(), - MockSpec() + MockSpec(), + MockSpec(), ]) void main() { final moveToMailboxInteractor = MockMoveToMailboxInteractor(); final deleteEmailPermanentlyInteractor = MockDeleteEmailPermanentlyInteractor(); final markAsMailboxReadInteractor = MockMarkAsMailboxReadInteractor(); final getEmailCacheOnWebInteractor = MockGetComposerCacheOnWebInteractor(); + final getIdentityCacheOnWebInteractor = MockGetIdentityCacheOnWebInteractor(); final markAsEmailReadInteractor = MockMarkAsEmailReadInteractor(); final markAsStarEmailInteractor = MockMarkAsStarEmailInteractor(); final markAsMultipleEmailReadInteractor = MockMarkAsMultipleEmailReadInteractor(); @@ -316,6 +319,7 @@ void main() { deleteEmailPermanentlyInteractor, markAsMailboxReadInteractor, getEmailCacheOnWebInteractor, + getIdentityCacheOnWebInteractor, markAsEmailReadInteractor, markAsStarEmailInteractor, markAsMultipleEmailReadInteractor, diff --git a/test/features/manage_account/presentation/profiles/identities/identities_controller_test.dart b/test/features/manage_account/presentation/profiles/identities/identities_controller_test.dart index f1b70a239..d185c0c3e 100644 --- a/test/features/manage_account/presentation/profiles/identities/identities_controller_test.dart +++ b/test/features/manage_account/presentation/profiles/identities/identities_controller_test.dart @@ -11,10 +11,15 @@ import 'package:jmap_dart_client/jmap/core/id.dart'; import 'package:jmap_dart_client/jmap/identities/identity.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; +import 'package:tmail_ui_user/features/base/before_reconnect_manager.dart'; import 'package:tmail_ui_user/features/caching/caching_manager.dart'; +import 'package:tmail_ui_user/features/home/domain/usecases/get_session_interactor.dart'; +import 'package:tmail_ui_user/features/identity_creator/domain/usecase/save_identity_cache_on_web_interactor.dart'; import 'package:tmail_ui_user/features/login/data/network/interceptors/authorization_interceptors.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/delete_authority_oidc_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/delete_credential_interactor.dart'; +import 'package:tmail_ui_user/features/login/domain/usecases/get_authenticated_account_interactor.dart'; +import 'package:tmail_ui_user/features/login/domain/usecases/update_account_cache_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/data/local/language_cache_manager.dart'; import 'package:tmail_ui_user/features/manage_account/domain/state/get_all_identities_state.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/create_new_default_identity_interactor.dart'; @@ -61,6 +66,11 @@ const fallbackGenerators = { MockSpec(), MockSpec(), + // Reloadable controller mockspecs + MockSpec(), + MockSpec(), + MockSpec(), + // Identities controller mockspecs MockSpec(), MockSpec(), @@ -70,6 +80,8 @@ const fallbackGenerators = { MockSpec(), MockSpec(), MockSpec(fallbackGenerators: fallbackGenerators), + MockSpec(), + MockSpec(), ]) void main() { TestWidgetsFlutterBinding.ensureInitialized(); @@ -98,6 +110,8 @@ void main() { late MockApplicationManager mockApplicationManager; late MockToastManager mockToastManager; + late MockSaveIdentityCacheOnWebInteractor mockSaveIdentityCacheOnWebInteractor; + setUpAll(() { //mock base controller mockCachingManager = MockCachingManager(); @@ -114,6 +128,8 @@ void main() { mockApplicationManager = MockApplicationManager(); mockToastManager = MockToastManager(); + mockSaveIdentityCacheOnWebInteractor = MockSaveIdentityCacheOnWebInteractor(); + Get.put(mockCachingManager); Get.put(mockLanguageCacheManager); Get.put(mockAuthorizationInterceptors); @@ -132,6 +148,11 @@ void main() { Get.put(mockApplicationManager); Get.put(mockToastManager); + // mock reloadable controller + Get.put(MockGetSessionInteractor()); + Get.put(MockGetAuthenticatedAccountInteractor()); + Get.put(MockUpdateAccountCacheInteractor()); + // mock identities controller mockGetAllIdentitiesInteractor = MockGetAllIdentitiesInteractor(); mockDeleteIdentityInteractor = MockDeleteIdentityInteractor(); @@ -142,6 +163,7 @@ void main() { mockTransformHtmlSignatureInteractor = MockTransformHtmlSignatureInteractor(); mockManageAccountDashBoardController = MockManageAccountDashBoardController(); + Get.put(MockBeforeReconnectManager()); Get.put(mockManageAccountDashBoardController); @@ -154,7 +176,8 @@ void main() { mockEditIdentityInteractor, mockCreateNewDefaultIdentityInteractor, mockEditDefaultIdentityInteractor, - mockTransformHtmlSignatureInteractor); + mockTransformHtmlSignatureInteractor, + mockSaveIdentityCacheOnWebInteractor); }); group('identities controller test:', () {