TF-3480 Handle reopen multiple composer when reload page or session expired

Signed-off-by: dab246 <tdvu@linagora.com>
This commit is contained in:
dab246
2025-02-28 01:29:38 +07:00
committed by Dat H. Pham
parent 810d86b100
commit cfc72a6f4f
24 changed files with 318 additions and 101 deletions
@@ -10,11 +10,20 @@ abstract class SessionStorageComposerDatasource {
required ComposerCache composerCache,
});
Future<ComposerCache> getComposerCacheOnWeb(
Future<List<ComposerCache>> getComposerCacheOnWeb(
AccountId accountId,
UserName userName);
Future<void> removeComposerCacheOnWeb();
Future<void> removeAllComposerCacheOnWeb(
AccountId accountId,
UserName userName,
);
Future<void> removeComposerCacheByIdOnWeb(
AccountId accountId,
UserName userName,
String composerId,
);
Future<String> restoreEmailInlineImages(
String htmlContent,
@@ -1,11 +1,11 @@
import 'dart:convert';
import 'package:collection/collection.dart';
import 'package:core/domain/exceptions/web_session_exception.dart';
import 'package:core/presentation/utils/html_transformer/html_transform.dart';
import 'package:core/presentation/utils/html_transformer/transform_configuration.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/user_name.dart';
import 'package:model/model.dart';
import 'package:model/email/email_action_type.dart';
import 'package:model/extensions/account_id_extensions.dart';
import 'package:tmail_ui_user/features/caching/utils/cache_utils.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource/session_storage_composer_datasource.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/data/model/composer_cache.dart';
@@ -20,7 +20,7 @@ class SessionStorageComposerDatasourceImpl
final ExceptionThrower _exceptionThrower;
@override
Future<ComposerCache> getComposerCacheOnWeb(
Future<List<ComposerCache>> getComposerCacheOnWeb(
AccountId accountId,
UserName userName
) async {
@@ -30,24 +30,20 @@ class SessionStorageComposerDatasourceImpl
accountId.asString,
userName.value).toString();
final result = html.window.sessionStorage.entries.firstWhereOrNull(
(entry) => entry.key == keyWithIdentity);
if (result != null) {
return ComposerCache.fromJson(jsonDecode(result.value));
final listEntries = html.window.sessionStorage.entries.where(
(entry) => entry.key.startsWith(keyWithIdentity),
);
if (listEntries.isNotEmpty) {
return listEntries
.map((entry) => ComposerCache.fromJson(jsonDecode(entry.value)))
.toList();
} else {
throw NotFoundInWebSessionException();
}
}).catchError(_exceptionThrower.throwException);
}
@override
Future<void> removeComposerCacheOnWeb() async {
return Future.sync(() {
html.window.sessionStorage.removeWhere(
(key, value) => key.startsWith(EmailActionType.reopenComposerBrowser.name));
}).catchError(_exceptionThrower.throwException);
}
@override
Future<void> saveComposerCacheOnWeb({
required AccountId accountId,
@@ -58,7 +54,9 @@ class SessionStorageComposerDatasourceImpl
final composerCacheKey = TupleKey(
EmailActionType.reopenComposerBrowser.name,
accountId.asString,
userName.value).toString();
userName.value,
composerCache.composerId,
).toString();
Map<String, String> entries = {
composerCacheKey: jsonEncode(composerCache.toJson())
};
@@ -78,4 +76,31 @@ class SessionStorageComposerDatasourceImpl
mapCidImageDownloadUrl: mapUrlDownloadCID);
}).catchError(_exceptionThrower.throwException);
}
@override
Future<void> removeAllComposerCacheOnWeb(AccountId accountId, UserName userName) {
return Future.sync(() {
final keyWithIdentity = TupleKey(
EmailActionType.reopenComposerBrowser.name,
accountId.asString,
userName.value,
).toString();
html.window.sessionStorage.removeWhere((key, value) => key.startsWith(keyWithIdentity));
}).catchError(_exceptionThrower.throwException);
}
@override
Future<void> removeComposerCacheByIdOnWeb(AccountId accountId, UserName userName, String composerId) {
return Future.sync(() {
final keyWithIdentity = TupleKey(
EmailActionType.reopenComposerBrowser.name,
accountId.asString,
userName.value,
composerId,
).toString();
html.window.sessionStorage.removeWhere((key, value) => key == keyWithIdentity);
}).catchError(_exceptionThrower.throwException);
}
}
@@ -12,12 +12,16 @@ class ComposerCache with EquatableMixin {
final bool? hasRequestReadReceipt;
final bool? isMarkAsImportant;
final ScreenDisplayMode displayMode;
final int? composerIndex;
final String? composerId;
ComposerCache({
this.email,
this.hasRequestReadReceipt,
this.isMarkAsImportant,
this.displayMode = ScreenDisplayMode.normal
this.composerIndex,
this.composerId,
});
factory ComposerCache.fromJson(Map<String, dynamic> json) => _$ComposerCacheFromJson(json);
@@ -30,5 +34,7 @@ class ComposerCache with EquatableMixin {
hasRequestReadReceipt,
isMarkAsImportant,
displayMode
composerIndex,
composerId,
];
}
@@ -12,7 +12,7 @@ class ComposerCacheRepositoryImpl extends ComposerCacheRepository {
ComposerCacheRepositoryImpl(this.composerCacheDataSource);
@override
Future<ComposerCache> getComposerCacheOnWeb(
Future<List<ComposerCache>> getComposerCacheOnWeb(
AccountId accountId,
UserName userName
) {
@@ -20,8 +20,13 @@ class ComposerCacheRepositoryImpl extends ComposerCacheRepository {
}
@override
Future<void> removeComposerCacheOnWeb() {
return composerCacheDataSource.removeComposerCacheOnWeb();
Future<void> removeAllComposerCacheOnWeb(AccountId accountId, UserName userName) {
return composerCacheDataSource.removeAllComposerCacheOnWeb(accountId, userName);
}
@override
Future<void> removeComposerCacheByIdOnWeb(AccountId accountId, UserName userName, String composerId) {
return composerCacheDataSource.removeComposerCacheByIdOnWeb(accountId, userName, composerId);
}
@override
@@ -10,11 +10,20 @@ abstract class ComposerCacheRepository {
required ComposerCache composerCache,
});
Future<ComposerCache> getComposerCacheOnWeb(
Future<List<ComposerCache>> getComposerCacheOnWeb(
AccountId accountId,
UserName userName);
Future<void> removeComposerCacheOnWeb();
Future<void> removeAllComposerCacheOnWeb(
AccountId accountId,
UserName userName,
);
Future<void> removeComposerCacheByIdOnWeb(
AccountId accountId,
UserName userName,
String composerId,
);
Future<String> restoreEmailInlineImages(
String htmlContent,
@@ -4,12 +4,12 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/data/model/composer_cac
class GetComposerCacheSuccess extends UIState {
final ComposerCache composerCache;
final List<ComposerCache> listComposerCache;
GetComposerCacheSuccess(this.composerCache);
GetComposerCacheSuccess(this.listComposerCache);
@override
List<Object> get props => [composerCache];
List<Object> get props => [listComposerCache];
}
class GetComposerCacheFailure extends FeatureFailure {
@@ -13,8 +13,11 @@ class GetComposerCacheOnWebInteractor {
Stream<Either<Failure, Success>> execute(AccountId accountId, UserName userName) async* {
try {
final data = await composerCacheRepository.getComposerCacheOnWeb(accountId, userName);
yield Right(GetComposerCacheSuccess(data));
final listComposerCache = await composerCacheRepository.getComposerCacheOnWeb(
accountId,
userName,
);
yield Right(GetComposerCacheSuccess(listComposerCache));
} catch (exception) {
yield Left(GetComposerCacheFailure(exception));
}
@@ -0,0 +1,22 @@
import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.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/mailbox_dashboard/domain/repository/composer_cache_repository.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/remove_composer_cache_state.dart';
class RemoveAllComposerCacheOnWebInteractor {
final ComposerCacheRepository composerCacheRepository;
RemoveAllComposerCacheOnWebInteractor(this.composerCacheRepository);
Future<Either<Failure, Success>> execute(AccountId accountId, UserName userName) async {
try {
composerCacheRepository.removeAllComposerCacheOnWeb(accountId, userName);
return Right(RemoveComposerCacheSuccess());
} catch (exception) {
return Left(RemoveComposerCacheFailure(exception));
}
}
}
@@ -0,0 +1,30 @@
import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.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/mailbox_dashboard/domain/repository/composer_cache_repository.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/remove_composer_cache_state.dart';
class RemoveComposerCacheByIdOnWebInteractor {
final ComposerCacheRepository composerCacheRepository;
RemoveComposerCacheByIdOnWebInteractor(this.composerCacheRepository);
Future<Either<Failure, Success>> execute(
AccountId accountId,
UserName userName,
String composerId,
) async {
try {
composerCacheRepository.removeComposerCacheByIdOnWeb(
accountId,
userName,
composerId,
);
return Right(RemoveComposerCacheSuccess());
} catch (exception) {
return Left(RemoveComposerCacheFailure(exception));
}
}
}
@@ -1,19 +0,0 @@
import 'package:core/core.dart';
import 'package:dartz/dartz.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/composer_cache_repository.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/remove_composer_cache_state.dart';
class RemoveComposerCacheOnWebInteractor {
final ComposerCacheRepository composerCacheRepository;
RemoveComposerCacheOnWebInteractor(this.composerCacheRepository);
Future<Either<Failure, Success>> execute() async {
try {
composerCacheRepository.removeComposerCacheOnWeb();
return Right(RemoveComposerCacheSuccess());
} catch (exception) {
return Left(RemoveComposerCacheFailure(exception));
}
}
}
@@ -81,7 +81,8 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/get_com
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/get_spam_mailbox_cached_interactor.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/get_spam_report_state_interactor.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/quick_search_email_interactor.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/remove_composer_cache_on_web_interactor.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/remove_all_composer_cache_on_web_interactor.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/remove_composer_cache_by_id_on_web_interactor.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/remove_email_drafts_interactor.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/save_recent_search_interactor.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/store_last_time_dismissed_spam_reported_interactor.dart';
@@ -194,7 +195,8 @@ class MailboxDashBoardBindings extends BaseBindings {
Get.find<UnsubscribeEmailInteractor>(),
Get.find<RestoredDeletedMessageInteractor>(),
Get.find<GetRestoredDeletedMessageInterator>(),
Get.find<RemoveComposerCacheOnWebInteractor>(),
Get.find<RemoveAllComposerCacheOnWebInteractor>(),
Get.find<RemoveComposerCacheByIdOnWebInteractor>(),
Get.find<GetAllIdentitiesInteractor>(),
));
Get.put(AdvancedFilterController());
@@ -308,7 +310,8 @@ class MailboxDashBoardBindings extends BaseBindings {
Get.lazyPut(() => QuickSearchEmailInteractor(Get.find<ThreadRepository>()));
Get.lazyPut(() => MarkAsMailboxReadInteractor(Get.find<MailboxRepository>()));
Get.lazyPut(() => GetComposerCacheOnWebInteractor(Get.find<ComposerCacheRepository>()));
Get.lazyPut(() => RemoveComposerCacheOnWebInteractor(Get.find<ComposerCacheRepository>()));
Get.lazyPut(() => RemoveComposerCacheByIdOnWebInteractor(Get.find<ComposerCacheRepository>()));
Get.lazyPut(() => RemoveAllComposerCacheOnWebInteractor(Get.find<ComposerCacheRepository>()));
Get.lazyPut(() => MarkAsEmailReadInteractor(Get.find<EmailRepository>()));
Get.lazyPut(() => MarkAsStarEmailInteractor(Get.find<EmailRepository>()));
Get.lazyPut(() => MarkAsMultipleEmailReadInteractor(
@@ -92,7 +92,8 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/domain/model/spam_repor
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/get_composer_cache_state.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/remove_email_drafts_state.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/get_composer_cache_on_web_interactor.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/remove_composer_cache_on_web_interactor.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/remove_all_composer_cache_on_web_interactor.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/remove_composer_cache_by_id_on_web_interactor.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/remove_email_drafts_interactor.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/dashboard_action.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/app_grid_dashboard_controller.dart';
@@ -102,6 +103,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/delete_emails_in_mailbox_extension.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_preferences_setting_extension.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/open_and_close_composer_extension.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/reopen_composer_cache_extension.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/set_error_extension.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/update_current_emails_flags_extension.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mixin/user_setting_popup_menu_mixin.dart';
@@ -213,7 +215,8 @@ class MailboxDashBoardController extends ReloadableController
final UnsubscribeEmailInteractor _unsubscribeEmailInteractor;
final RestoredDeletedMessageInteractor _restoreDeletedMessageInteractor;
final GetRestoredDeletedMessageInterator _getRestoredDeletedMessageInteractor;
final RemoveComposerCacheOnWebInteractor _removeComposerCacheOnWebInteractor;
final RemoveComposerCacheByIdOnWebInteractor _removeComposerCacheByIdOnWebInteractor;
final RemoveAllComposerCacheOnWebInteractor _removeAllComposerCacheOnWebInteractor;
final GetAllIdentitiesInteractor _getAllIdentitiesInteractor;
GetAllVacationInteractor? _getAllVacationInteractor;
@@ -304,7 +307,8 @@ class MailboxDashBoardController extends ReloadableController
this._unsubscribeEmailInteractor,
this._restoreDeletedMessageInteractor,
this._getRestoredDeletedMessageInteractor,
this._removeComposerCacheOnWebInteractor,
this._removeAllComposerCacheOnWebInteractor,
this._removeComposerCacheByIdOnWebInteractor,
this._getAllIdentitiesInteractor,
);
@@ -433,8 +437,7 @@ class MailboxDashBoardController extends ReloadableController
} else if (success is GetAllIdentitiesSuccess) {
_handleGetAllIdentitiesSuccess(success);
} else if (success is GetComposerCacheSuccess) {
removeComposerCacheOnWeb();
openComposer(ComposerArguments.fromSessionStorageBrowser(success.composerCache));
handleGetComposerCacheSuccess(success);
} else if (success is GetIdentityCacheOnWebSuccess) {
goToSettings();
} else if (success is MarkAsStarEmailSuccess) {
@@ -3058,8 +3061,23 @@ class MailboxDashBoardController extends ReloadableController
isRecoveringDeletedMessage.value = true;
}
Future<void> _removeComposerCacheOnWeb() async {
await _removeComposerCacheOnWebInteractor.execute();
Future<void> removeComposerCacheByIdOnWeb(String composerId) async {
if (accountId.value == null || sessionCurrent == null) return;
await _removeComposerCacheByIdOnWebInteractor.execute(
accountId.value!,
sessionCurrent!.username,
composerId,
);
}
Future<void> removeAllComposerCacheOnWeb() async {
if (accountId.value == null || sessionCurrent == null) return;
await _removeAllComposerCacheOnWebInteractor.execute(
accountId.value!,
sessionCurrent!.username,
);
}
bool validateSendingEmailFailedWhenNetworkIsLostOnMobile(dynamic failure) {
@@ -30,6 +30,20 @@ extension OpenAndCloseComposerExtension on MailboxDashBoardController {
}
}
void openListComposer(List<ComposerArguments> listArguments) {
final newListArguments = listArguments
.map((argument) => argument.withIdentity(
identities: List.from(listIdentities),
selectedIdentityId: argument.selectedIdentityId,
))
.toList();
composerManager.addListComposer(newListArguments);
if (!twakeAppManager.hasComposer) {
twakeAppManager.setHasComposer(true);
}
}
void _openComposerOnWeb(ComposerArguments composerArguments) {
composerManager.addComposer(composerArguments);
if (!twakeAppManager.hasComposer) {
@@ -99,7 +113,9 @@ extension OpenAndCloseComposerExtension on MailboxDashBoardController {
_handleResultAfterCloseComposer(result);
await removeComposerCacheOnWeb();
if (composerId != null) {
await removeComposerCacheByIdOnWeb(composerId);
}
}
void _handleResultAfterCloseComposer(dynamic result) {
@@ -0,0 +1,21 @@
import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/get_composer_cache_state.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/open_and_close_composer_extension.dart';
extension ReopenComposerCacheExtension on MailboxDashBoardController {
void handleGetComposerCacheSuccess(GetComposerCacheSuccess success) {
removeAllComposerCacheOnWeb();
final listComposerCacheSortByIndex = success.listComposerCache
..sort((a, b) => (a.composerIndex ?? 0).compareTo(b.composerIndex ?? 0));
final listArguments = listComposerCacheSortByIndex
.map((composerCache) => ComposerArguments.fromSessionStorageBrowser(composerCache))
.toList();
openListComposer(listArguments);
}
}