From 8f5171d5810856786161fa53140b076cba0b0f62 Mon Sep 17 00:00:00 2001 From: dab246 Date: Tue, 28 Sep 2021 15:05:24 +0700 Subject: [PATCH] TF-123 Implement caching mailbox when get all mailbox --- .../destination_picker_bindings.dart | 9 +- .../data/datasource/mailbox_datasource.dart | 14 ++- .../mailbox_cache_datasource_impl.dart | 53 +++++++++ .../mailbox_datasource_impl.dart | 30 ++++- .../data/model/mailbox_cache_response.dart | 16 +++ .../data/model/mailbox_change_response.dart | 24 ++++ .../mailbox/data/network/mailbox_api.dart | 70 +++++++++++- .../data/network/mailbox_cache_manager.dart | 105 ++++++++++++++++++ .../repository/mailbox_repository_impl.dart | 56 +++++++++- .../extensions/list_mailbox_extension.dart | 1 - .../domain/extensions/mailbox_extension.dart | 24 ---- .../domain/repository/mailbox_repository.dart | 5 +- .../usecases/get_all_mailbox_interactor.dart | 22 ++-- .../presentation/mailbox_bindings.dart | 8 +- .../presentation/mailbox_controller.dart | 39 +++++-- .../model/mailbox_tree_builder.dart | 3 +- .../widgets/mailbox_folder_tile_builder.dart | 2 +- .../widgets/mailbox_tile_builder.dart | 2 +- .../mailbox_dashboard_controller.dart | 4 +- .../presentation/thread_controller.dart | 2 + lib/main/bindings/local/local_bindings.dart | 10 ++ 21 files changed, 438 insertions(+), 61 deletions(-) create mode 100644 lib/features/mailbox/data/datasource_impl/mailbox_cache_datasource_impl.dart create mode 100644 lib/features/mailbox/data/model/mailbox_cache_response.dart create mode 100644 lib/features/mailbox/data/model/mailbox_change_response.dart create mode 100644 lib/features/mailbox/data/network/mailbox_cache_manager.dart delete mode 100644 lib/features/mailbox/domain/extensions/mailbox_extension.dart diff --git a/lib/features/destination_picker/presentation/destination_picker_bindings.dart b/lib/features/destination_picker/presentation/destination_picker_bindings.dart index 6a1beb130..0ce0b00c3 100644 --- a/lib/features/destination_picker/presentation/destination_picker_bindings.dart +++ b/lib/features/destination_picker/presentation/destination_picker_bindings.dart @@ -1,8 +1,11 @@ +import 'package:core/core.dart'; import 'package:get/get.dart'; import 'package:tmail_ui_user/features/destination_picker/presentation/destination_picker_controller.dart'; import 'package:tmail_ui_user/features/mailbox/data/datasource/mailbox_datasource.dart'; +import 'package:tmail_ui_user/features/mailbox/data/datasource_impl/mailbox_cache_datasource_impl.dart'; import 'package:tmail_ui_user/features/mailbox/data/datasource_impl/mailbox_datasource_impl.dart'; import 'package:tmail_ui_user/features/mailbox/data/network/mailbox_api.dart'; +import 'package:tmail_ui_user/features/mailbox/data/network/mailbox_cache_manager.dart'; import 'package:tmail_ui_user/features/mailbox/data/repository/mailbox_repository_impl.dart'; import 'package:tmail_ui_user/features/mailbox/domain/repository/mailbox_repository.dart'; import 'package:tmail_ui_user/features/mailbox/domain/usecases/get_all_mailbox_interactor.dart'; @@ -13,7 +16,11 @@ class DestinationPickerBindings extends Bindings { void dependencies() { Get.lazyPut(() => MailboxDataSourceImpl(Get.find())); Get.lazyPut(() => Get.find()); - Get.lazyPut(() => MailboxRepositoryImpl(Get.find())); + Get.lazyPut(() => MailboxCacheDataSourceImpl(Get.find())); + Get.lazyPut(() => MailboxRepositoryImpl({ + DataSourceType.network: Get.find(), + DataSourceType.local: Get.find(), + })); Get.lazyPut(() => Get.find()); Get.lazyPut(() => GetAllMailboxInteractor(Get.find())); Get.lazyPut(() => TreeBuilder()); diff --git a/lib/features/mailbox/data/datasource/mailbox_datasource.dart b/lib/features/mailbox/data/datasource/mailbox_datasource.dart index f6ab5c7f3..e2983109c 100644 --- a/lib/features/mailbox/data/datasource/mailbox_datasource.dart +++ b/lib/features/mailbox/data/datasource/mailbox_datasource.dart @@ -1,7 +1,19 @@ import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/properties/properties.dart'; +import 'package:jmap_dart_client/jmap/core/state.dart'; +import 'package:jmap_dart_client/jmap/mail/mailbox/get/get_mailbox_response.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; +import 'package:tmail_ui_user/features/mailbox/data/model/mailbox_cache_response.dart'; +import 'package:tmail_ui_user/features/mailbox/data/model/mailbox_change_response.dart'; abstract class MailboxDataSource { - Future> getAllMailbox(AccountId accountId, {Properties? properties}); + Future getAllMailbox(AccountId accountId, {Properties? properties}); + + Future getChanges(AccountId accountId, State sinceState); + + Future getAllMailboxCache(); + + Future combineMailboxCache(MailboxChangeResponse mailboxChangeResponse, List mailboxList); + + Future asyncUpdateCache(MailboxChangeResponse mailboxChangeResponse); } \ No newline at end of file diff --git a/lib/features/mailbox/data/datasource_impl/mailbox_cache_datasource_impl.dart b/lib/features/mailbox/data/datasource_impl/mailbox_cache_datasource_impl.dart new file mode 100644 index 000000000..539e3e4da --- /dev/null +++ b/lib/features/mailbox/data/datasource_impl/mailbox_cache_datasource_impl.dart @@ -0,0 +1,53 @@ +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/core/properties/properties.dart'; +import 'package:jmap_dart_client/jmap/core/state.dart'; +import 'package:jmap_dart_client/jmap/mail/mailbox/get/get_mailbox_response.dart'; +import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; +import 'package:tmail_ui_user/features/mailbox/data/datasource/mailbox_datasource.dart'; +import 'package:tmail_ui_user/features/mailbox/data/model/mailbox_cache_response.dart'; +import 'package:tmail_ui_user/features/mailbox/data/model/mailbox_change_response.dart'; +import 'package:tmail_ui_user/features/mailbox/data/network/mailbox_cache_manager.dart'; + +class MailboxCacheDataSourceImpl extends MailboxDataSource { + + final MailboxCacheManager _mailboxCacheManager; + + MailboxCacheDataSourceImpl(this._mailboxCacheManager); + + @override + Future getAllMailbox(AccountId accountId, {Properties? properties}) { + throw UnimplementedError(); + } + + @override + Future getAllMailboxCache() { + return Future.sync(() async { + return await _mailboxCacheManager.getAllMailbox(); + }).catchError((error) { + throw error; + }); + } + + @override + Future asyncUpdateCache(MailboxChangeResponse mailboxChangeResponse) { + return Future.sync(() async { + return await _mailboxCacheManager.asyncUpdateCache(mailboxChangeResponse); + }).catchError((error) { + throw error; + }); + } + + @override + Future getChanges(AccountId accountId, State sinceState) { + throw UnimplementedError(); + } + + @override + Future combineMailboxCache(MailboxChangeResponse mailboxChangeResponse, List mailboxList) { + return Future.sync(() async { + return await _mailboxCacheManager.combineMailboxCache(mailboxChangeResponse, mailboxList); + }).catchError((error) { + throw error; + }); + } +} \ No newline at end of file diff --git a/lib/features/mailbox/data/datasource_impl/mailbox_datasource_impl.dart b/lib/features/mailbox/data/datasource_impl/mailbox_datasource_impl.dart index 8db4b779e..5ac4705b9 100644 --- a/lib/features/mailbox/data/datasource_impl/mailbox_datasource_impl.dart +++ b/lib/features/mailbox/data/datasource_impl/mailbox_datasource_impl.dart @@ -1,7 +1,11 @@ import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/properties/properties.dart'; +import 'package:jmap_dart_client/jmap/core/state.dart'; +import 'package:jmap_dart_client/jmap/mail/mailbox/get/get_mailbox_response.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:tmail_ui_user/features/mailbox/data/datasource/mailbox_datasource.dart'; +import 'package:tmail_ui_user/features/mailbox/data/model/mailbox_cache_response.dart'; +import 'package:tmail_ui_user/features/mailbox/data/model/mailbox_change_response.dart'; import 'package:tmail_ui_user/features/mailbox/data/network/mailbox_api.dart'; class MailboxDataSourceImpl extends MailboxDataSource { @@ -11,11 +15,35 @@ class MailboxDataSourceImpl extends MailboxDataSource { MailboxDataSourceImpl(this.mailboxAPI,); @override - Future> getAllMailbox(AccountId accountId, {Properties? properties}) { + Future getAllMailbox(AccountId accountId, {Properties? properties}) { return Future.sync(() async { return await mailboxAPI.getAllMailbox(accountId, properties: properties); }).catchError((error) { throw error; }); } + + @override + Future getAllMailboxCache() { + throw UnimplementedError(); + } + + @override + Future asyncUpdateCache(MailboxChangeResponse mailboxChangeResponse) { + throw UnimplementedError(); + } + + @override + Future getChanges(AccountId accountId, State sinceState) { + return Future.sync(() async { + return await mailboxAPI.getChanges(accountId, sinceState); + }).catchError((error) { + throw error; + }); + } + + @override + Future combineMailboxCache(MailboxChangeResponse mailboxChangeResponse, List mailboxList) { + throw UnimplementedError(); + } } \ No newline at end of file diff --git a/lib/features/mailbox/data/model/mailbox_cache_response.dart b/lib/features/mailbox/data/model/mailbox_cache_response.dart new file mode 100644 index 000000000..6866e1cd0 --- /dev/null +++ b/lib/features/mailbox/data/model/mailbox_cache_response.dart @@ -0,0 +1,16 @@ + +import 'package:equatable/equatable.dart'; +import 'package:model/model.dart'; + +class MailboxCacheResponse with EquatableMixin { + final List? mailboxCaches; + final StateDao? oldState; + + MailboxCacheResponse({ + this.mailboxCaches, + this.oldState + }); + + @override + List get props => [mailboxCaches, oldState]; +} \ No newline at end of file diff --git a/lib/features/mailbox/data/model/mailbox_change_response.dart b/lib/features/mailbox/data/model/mailbox_change_response.dart new file mode 100644 index 000000000..68a30f405 --- /dev/null +++ b/lib/features/mailbox/data/model/mailbox_change_response.dart @@ -0,0 +1,24 @@ + +import 'package:equatable/equatable.dart'; +import 'package:jmap_dart_client/jmap/core/properties/properties.dart'; +import 'package:jmap_dart_client/jmap/core/state.dart'; +import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; + +class MailboxChangeResponse with EquatableMixin { + final List? updated; + final List? created; + final List? destroyed; + final State? newState; + final Properties? updatedProperties; + + MailboxChangeResponse({ + this.updated, + this.created, + this.destroyed, + this.newState, + this.updatedProperties, + }); + + @override + List get props => [updated, created, destroyed, newState, updatedProperties]; +} \ No newline at end of file diff --git a/lib/features/mailbox/data/network/mailbox_api.dart b/lib/features/mailbox/data/network/mailbox_api.dart index d41d78282..9277af546 100644 --- a/lib/features/mailbox/data/network/mailbox_api.dart +++ b/lib/features/mailbox/data/network/mailbox_api.dart @@ -3,10 +3,16 @@ import 'dart:async'; import 'package:jmap_dart_client/http/http_client.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/properties/properties.dart'; +import 'package:jmap_dart_client/jmap/core/request/reference_path.dart'; +import 'package:jmap_dart_client/jmap/core/state.dart'; +import 'package:jmap_dart_client/jmap/core/unsigned_int.dart'; import 'package:jmap_dart_client/jmap/jmap_request.dart'; +import 'package:jmap_dart_client/jmap/mail/mailbox/changes/changes_mailbox_method.dart'; +import 'package:jmap_dart_client/jmap/mail/mailbox/changes/changes_mailbox_response.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/get/get_mailbox_method.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/get/get_mailbox_response.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; +import 'package:tmail_ui_user/features/mailbox/data/model/mailbox_change_response.dart'; class MailboxAPI { @@ -14,7 +20,7 @@ class MailboxAPI { MailboxAPI(this.httpClient); - Future> getAllMailbox(AccountId accountId, {Properties? properties}) async { + Future getAllMailbox(AccountId accountId, {Properties? properties}) async { final processingInvocation = ProcessingInvocation(); final jmapRequestBuilder = JmapRequestBuilder(httpClient, processingInvocation); @@ -32,6 +38,66 @@ class MailboxAPI { queryInvocation.methodCallId, GetMailboxResponse.deserialize); - return resultCreated == null ? [] : resultCreated.list; + return resultCreated; + } + + Future getChanges(AccountId accountId, State sinceState) async { + final processingInvocation = ProcessingInvocation(); + + final jmapRequestBuilder = JmapRequestBuilder(httpClient, processingInvocation); + + final changesMailboxMethod = ChangesMailboxMethod(accountId, sinceState, maxChanges: UnsignedInt(128)); + + final changesMailboxInvocation = jmapRequestBuilder.invocation(changesMailboxMethod); + + final getMailboxUpdated = GetMailboxMethod(accountId) + ..addReferenceIds(processingInvocation.createResultReference( + changesMailboxInvocation.methodCallId, + ReferencePath.updatedPath)) + ..addReferenceProperties(processingInvocation.createResultReference( + changesMailboxInvocation.methodCallId, + ReferencePath.updatedPropertiesPath)); + + final getMailboxCreated = GetMailboxMethod(accountId) + ..addReferenceIds(processingInvocation.createResultReference( + changesMailboxInvocation.methodCallId, + ReferencePath.createdPath)); + + final getMailboxUpdatedInvocation = jmapRequestBuilder.invocation(getMailboxUpdated); + final getMailboxCreatedInvocation = jmapRequestBuilder.invocation(getMailboxCreated); + + final result = await (jmapRequestBuilder + ..usings(getMailboxCreated.requiredCapabilities)) + .build() + .execute(); + + final resultChanges = result.parse( + changesMailboxInvocation.methodCallId, + ChangesMailboxResponse.deserialize); + + final resultUpdated = result.parse( + getMailboxUpdatedInvocation.methodCallId, + GetMailboxResponse.deserialize); + + final resultCreated = result.parse( + getMailboxCreatedInvocation.methodCallId, + GetMailboxResponse.deserialize); + + final listMailboxIdDestroyed = resultChanges?.destroyed.map((id) => MailboxId(id)).toList() ?? []; + + final updatedProperties = resultChanges?.updatedProperties; + + final listMailboxUpdated = resultUpdated?.list ?? []; + + final listMailboxCreated = resultCreated?.list ?? []; + + final newState = resultChanges?.newState; + + return MailboxChangeResponse( + updated: listMailboxUpdated, + created: listMailboxCreated, + destroyed: listMailboxIdDestroyed, + newState: newState, + updatedProperties: updatedProperties); } } \ No newline at end of file diff --git a/lib/features/mailbox/data/network/mailbox_cache_manager.dart b/lib/features/mailbox/data/network/mailbox_cache_manager.dart new file mode 100644 index 000000000..d8be739b5 --- /dev/null +++ b/lib/features/mailbox/data/network/mailbox_cache_manager.dart @@ -0,0 +1,105 @@ + +import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; +import 'package:model/model.dart'; +import 'package:tmail_ui_user/features/caching/mailbox_cache_client.dart'; +import 'package:tmail_ui_user/features/caching/state_cache_client.dart'; +import 'package:tmail_ui_user/features/mailbox/data/model/mailbox_cache_response.dart'; +import 'package:tmail_ui_user/features/mailbox/data/model/mailbox_change_response.dart'; + +class MailboxCacheManager { + + final MailboxCacheClient _mailboxCacheClient; + final StateCacheClient _stateCacheClient; + + MailboxCacheManager(this._mailboxCacheClient, this._stateCacheClient); + + Future getAllMailbox() async { + return await Future.wait( + [ + _stateCacheClient.getItem(StateType.mailbox.value), + _mailboxCacheClient.getListItem() + ], + eagerError: true + ).then((List responses) { + return MailboxCacheResponse(mailboxCaches: responses.last, oldState: responses.first); + }); + } + + Future combineMailboxCache(MailboxChangeResponse mailboxChangeResponse, List mailboxList) async { + + final mailboxUpdated = mailboxChangeResponse.updated; + final updatedProperties = mailboxChangeResponse.updatedProperties; + + if (mailboxUpdated != null && mailboxUpdated.isNotEmpty) { + final newListMailboxUpdated = mailboxUpdated.map((mailboxUpdated) { + if (updatedProperties == null) { + return mailboxUpdated; + } else { + final mailboxOld = findMailbox(mailboxUpdated.id, mailboxList); + if (mailboxOld != null) { + return mailboxOld.combineMailbox(mailboxUpdated, updatedProperties); + } else { + return mailboxUpdated; + } + } + }).toList(); + + final newMailboxChangeResponse = MailboxChangeResponse( + created: mailboxChangeResponse.created, + destroyed: mailboxChangeResponse.destroyed, + updated: newListMailboxUpdated, + newState: mailboxChangeResponse.newState, + updatedProperties: mailboxChangeResponse.updatedProperties + ); + + return newMailboxChangeResponse; + } + return mailboxChangeResponse; + } + + Mailbox? findMailbox(MailboxId mailboxId, List mailboxList) { + try { + return mailboxList.firstWhere((element) => element.id == mailboxId); + } catch (e) { + return null; + } + } + + Future asyncUpdateCache(MailboxChangeResponse mailboxChangeResponse) async { + if (mailboxChangeResponse.newState != null) { + final stateCacheExist = await _stateCacheClient.isExistTable(); + if (stateCacheExist) { + _stateCacheClient.updateItem( + StateType.mailbox.value, + mailboxChangeResponse.newState!.toStateDao(StateType.mailbox)); + } else { + _stateCacheClient.insertItem( + StateType.mailbox.value, + mailboxChangeResponse.newState!.toStateDao(StateType.mailbox)); + } + } + + final mailboxCacheExist = await _mailboxCacheClient.isExistTable(); + if (mailboxCacheExist) { + final updatedCacheMailboxes = mailboxChangeResponse.updated + ?.map((mailbox) => mailbox.toMailboxCache()) + .toList() ?? []; + final createdCacheMailboxes = mailboxChangeResponse.created + ?.map((mailbox) => mailbox.toMailboxCache()) + .toList() ?? []; + final destroyedCacheMailboxes = mailboxChangeResponse.destroyed + ?.map((mailboxId) => mailboxId.id.value) + .toList() ?? []; + await Future.wait([ + _mailboxCacheClient.updateMultipleItem(updatedCacheMailboxes.toMap()), + _mailboxCacheClient.insertMultipleItem(createdCacheMailboxes.toMap()), + _mailboxCacheClient.deleteMultipleItem(destroyedCacheMailboxes) + ]); + } else { + final createdCacheMailboxes = mailboxChangeResponse.created + ?.map((mailbox) => mailbox.toMailboxCache()) + .toList() ?? []; + await _mailboxCacheClient.insertMultipleItem(createdCacheMailboxes.toMap()); + } + } +} \ No newline at end of file diff --git a/lib/features/mailbox/data/repository/mailbox_repository_impl.dart b/lib/features/mailbox/data/repository/mailbox_repository_impl.dart index 5ccc85b91..f5d7b771d 100644 --- a/lib/features/mailbox/data/repository/mailbox_repository_impl.dart +++ b/lib/features/mailbox/data/repository/mailbox_repository_impl.dart @@ -1,17 +1,65 @@ +import 'package:core/core.dart'; +import 'package:model/model.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/properties/properties.dart'; +import 'package:jmap_dart_client/jmap/core/state.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:tmail_ui_user/features/mailbox/data/datasource/mailbox_datasource.dart'; +import 'package:tmail_ui_user/features/mailbox/data/model/mailbox_change_response.dart'; import 'package:tmail_ui_user/features/mailbox/domain/repository/mailbox_repository.dart'; class MailboxRepositoryImpl extends MailboxRepository { - final MailboxDataSource mailboxDataSource; + final Map mapDataSource; - MailboxRepositoryImpl(this.mailboxDataSource); + MailboxRepositoryImpl(this.mapDataSource); @override - Future> getAllMailbox(AccountId accountId, {Properties? properties}) { - return mailboxDataSource.getAllMailbox(accountId, properties: properties); + Stream> getAllMailbox(AccountId accountId, {Properties? properties}) async* { + final mailboxCacheResponse = await mapDataSource[DataSourceType.local]!.getAllMailboxCache(); + + final mailboxList = mailboxCacheResponse.mailboxCaches != null + ? mailboxCacheResponse.mailboxCaches!.toMailboxList() + : []; + final oldState = mailboxCacheResponse.oldState?.state; + + yield mailboxList; + + if (mailboxList.isNotEmpty && oldState != null) { + final changesResponse = await mapDataSource[DataSourceType.network]!.getChanges(accountId, State(oldState)); + + final newChangesResponse = await mapDataSource[DataSourceType.local]!.combineMailboxCache(changesResponse, mailboxList); + + await mapDataSource[DataSourceType.local]!.asyncUpdateCache(newChangesResponse); + } else { + final getMailboxResponse = await mapDataSource[DataSourceType.network]!.getAllMailbox(accountId); + + await mapDataSource[DataSourceType.local]!.asyncUpdateCache(MailboxChangeResponse( + created: getMailboxResponse?.list, + newState: getMailboxResponse?.state)); + } + + final newMailboxCacheResponse = await mapDataSource[DataSourceType.local]!.getAllMailboxCache(); + + final newMailboxList = newMailboxCacheResponse.mailboxCaches != null + ? newMailboxCacheResponse.mailboxCaches!.toMailboxList() + : []; + + yield newMailboxList; + } + + @override + Stream> refresh(AccountId accountId, State currentState) async* { + final changesResponse = await mapDataSource[DataSourceType.network]!.getChanges(accountId, currentState); + + await mapDataSource[DataSourceType.local]!.asyncUpdateCache(changesResponse); + + final newMailboxCacheResponse = await mapDataSource[DataSourceType.local]!.getAllMailboxCache(); + + final newMailboxList = newMailboxCacheResponse.mailboxCaches != null + ? newMailboxCacheResponse.mailboxCaches!.toMailboxList() + : []; + + yield newMailboxList; } } \ No newline at end of file diff --git a/lib/features/mailbox/domain/extensions/list_mailbox_extension.dart b/lib/features/mailbox/domain/extensions/list_mailbox_extension.dart index 8e96b32cf..17eff905d 100644 --- a/lib/features/mailbox/domain/extensions/list_mailbox_extension.dart +++ b/lib/features/mailbox/domain/extensions/list_mailbox_extension.dart @@ -2,7 +2,6 @@ import 'package:built_collection/built_collection.dart'; import 'package:dartz/dartz.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:model/model.dart'; -import 'package:tmail_ui_user/features/mailbox/domain/extensions/mailbox_extension.dart'; extension ListMailboxExtensions on List { Tuple2, List> splitMailboxList(bool test(Mailbox element)) { diff --git a/lib/features/mailbox/domain/extensions/mailbox_extension.dart b/lib/features/mailbox/domain/extensions/mailbox_extension.dart deleted file mode 100644 index e6bf5a4e2..000000000 --- a/lib/features/mailbox/domain/extensions/mailbox_extension.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; -import 'package:model/model.dart'; - -extension MailboxExtension on Mailbox { - - PresentationMailbox toPresentationMailbox({SelectMode selectMode = SelectMode.INACTIVE}) { - return PresentationMailbox( - id, - name: name, - parentId: parentId, - role: role, - sortOrder: sortOrder, - totalEmails: totalEmails, - unreadEmails: unreadEmails, - totalThreads: totalThreads, - unreadThreads: unreadThreads, - myRights: myRights, - isSubscribed: isSubscribed, - selectMode: selectMode - ); - } - - bool hasRole() => role != null && role!.value.isNotEmpty; -} \ No newline at end of file diff --git a/lib/features/mailbox/domain/repository/mailbox_repository.dart b/lib/features/mailbox/domain/repository/mailbox_repository.dart index b8286e0bb..e0953aea8 100644 --- a/lib/features/mailbox/domain/repository/mailbox_repository.dart +++ b/lib/features/mailbox/domain/repository/mailbox_repository.dart @@ -1,7 +1,10 @@ import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/properties/properties.dart'; +import 'package:jmap_dart_client/jmap/core/state.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; abstract class MailboxRepository { - Future> getAllMailbox(AccountId accountId, {Properties? properties}); + Stream> getAllMailbox(AccountId accountId, {Properties? properties}); + + Stream> refresh(AccountId accountId, State currentState); } \ No newline at end of file diff --git a/lib/features/mailbox/domain/usecases/get_all_mailbox_interactor.dart b/lib/features/mailbox/domain/usecases/get_all_mailbox_interactor.dart index 34c2118ac..0b55dbb1f 100644 --- a/lib/features/mailbox/domain/usecases/get_all_mailbox_interactor.dart +++ b/lib/features/mailbox/domain/usecases/get_all_mailbox_interactor.dart @@ -5,21 +5,29 @@ import 'package:jmap_dart_client/jmap/core/properties/properties.dart'; import 'package:tmail_ui_user/features/mailbox/domain/repository/mailbox_repository.dart'; import 'package:tmail_ui_user/features/mailbox/domain/state/get_all_mailboxes_state.dart'; import 'package:tmail_ui_user/features/mailbox/domain/extensions/list_mailbox_extension.dart'; -import 'package:tmail_ui_user/features/mailbox/domain/extensions/mailbox_extension.dart'; +import 'package:model/model.dart'; class GetAllMailboxInteractor { - final MailboxRepository mailboxRepository; + final MailboxRepository _mailboxRepository; - GetAllMailboxInteractor(this.mailboxRepository); + GetAllMailboxInteractor(this._mailboxRepository); Stream> execute(AccountId accountId, {Properties? properties}) async* { try { yield Right(LoadingState()); - final mailboxList = await mailboxRepository.getAllMailbox(accountId, properties: properties); - final tupleList = mailboxList.splitMailboxList((mailbox) => mailbox.hasRole()); - yield Right(GetAllMailboxSuccess(defaultMailboxList: tupleList.value1, folderMailboxList: tupleList.value2)); + + final streamListMailbox = _mailboxRepository.getAllMailbox(accountId, properties: properties); + + final streamResult = streamListMailbox.asyncExpand((mailboxList) async* { + final tupleList = mailboxList.splitMailboxList((mailbox) => mailbox.hasRole()); + yield Right(GetAllMailboxSuccess( + defaultMailboxList: tupleList.value1, + folderMailboxList: tupleList.value2)); + }); + + yield* streamResult; } catch (e) { - yield Left(GetAllMailboxFailure(e)); + yield Left(GetAllMailboxFailure(e)); } } } \ No newline at end of file diff --git a/lib/features/mailbox/presentation/mailbox_bindings.dart b/lib/features/mailbox/presentation/mailbox_bindings.dart index 261fb8dbf..7cd42c594 100644 --- a/lib/features/mailbox/presentation/mailbox_bindings.dart +++ b/lib/features/mailbox/presentation/mailbox_bindings.dart @@ -5,8 +5,10 @@ import 'package:tmail_ui_user/features/login/data/repository/credential_reposito import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/delete_credential_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/data/datasource/mailbox_datasource.dart'; +import 'package:tmail_ui_user/features/mailbox/data/datasource_impl/mailbox_cache_datasource_impl.dart'; import 'package:tmail_ui_user/features/mailbox/data/datasource_impl/mailbox_datasource_impl.dart'; import 'package:tmail_ui_user/features/mailbox/data/network/mailbox_api.dart'; +import 'package:tmail_ui_user/features/mailbox/data/network/mailbox_cache_manager.dart'; import 'package:tmail_ui_user/features/mailbox/data/repository/mailbox_repository_impl.dart'; import 'package:tmail_ui_user/features/mailbox/domain/repository/mailbox_repository.dart'; import 'package:tmail_ui_user/features/mailbox/domain/usecases/get_all_mailbox_interactor.dart'; @@ -21,7 +23,11 @@ class MailboxBindings extends Bindings { Get.lazyPut(() => DeleteCredentialInteractor(Get.find())); Get.lazyPut(() => MailboxDataSourceImpl(Get.find())); Get.lazyPut(() => Get.find()); - Get.lazyPut(() => MailboxRepositoryImpl(Get.find())); + Get.lazyPut(() => MailboxCacheDataSourceImpl(Get.find())); + Get.lazyPut(() => MailboxRepositoryImpl({ + DataSourceType.network: Get.find(), + DataSourceType.local: Get.find(), + })); Get.lazyPut(() => Get.find()); Get.lazyPut(() => GetAllMailboxInteractor(Get.find())); Get.lazyPut(() => TreeBuilder()); diff --git a/lib/features/mailbox/presentation/mailbox_controller.dart b/lib/features/mailbox/presentation/mailbox_controller.dart index b1ccf6147..81b4687e0 100644 --- a/lib/features/mailbox/presentation/mailbox_controller.dart +++ b/lib/features/mailbox/presentation/mailbox_controller.dart @@ -4,6 +4,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:model/model.dart'; import 'package:tmail_ui_user/features/base/base_controller.dart'; import 'package:tmail_ui_user/features/email/domain/state/mark_as_email_read_state.dart'; @@ -99,7 +100,9 @@ class MailboxController extends BaseController { } @override - void onError(error) {} + void onError(error) { + print('$error'); + } void _buildTree(List folderMailboxList) async { final _folderMailboxTree = await _treeBuilder.generateMailboxTree(folderMailboxList); @@ -119,20 +122,32 @@ class MailboxController extends BaseController { } void _setUpMapMailboxIdDefault(List defaultMailboxList) { + final mapMailboxId = Map.fromIterable( + defaultMailboxList, + key: (presentationMailbox) => presentationMailbox.role!, + value: (presentationMailbox) => presentationMailbox.id); + + final mapMailboxDefault = Map.fromIterable( + defaultMailboxList, + key: (presentationMailbox) => presentationMailbox.role!, + value: (presentationMailbox) => presentationMailbox); + + mailboxDashBoardController.setMapMailboxId(mapMailboxId); + var mailboxCurrent = mailboxDashBoardController.selectedMailbox.value; - defaultMailboxList.forEach((presentationMailbox) { - if (mailboxCurrent == null && presentationMailbox.role == PresentationMailbox.roleInbox) { - mailboxCurrent = presentationMailbox; - } else if (mailboxCurrent?.role == presentationMailbox.role) { - mailboxCurrent = presentationMailbox; - } - - mailboxDashBoardController.addMailboxIdToMap(presentationMailbox.role!, presentationMailbox.id); - }); - if (mailboxCurrent != null) { - mailboxDashBoardController.setNewFirstSelectedMailbox(mailboxCurrent); + if (mapMailboxDefault.containsKey(mailboxCurrent.role)) { + mailboxDashBoardController.setNewFirstSelectedMailbox(mapMailboxDefault[mailboxCurrent.role]); + } else { + mailboxDashBoardController.setNewFirstSelectedMailbox(mailboxCurrent); + } + } else { + if (mapMailboxDefault.containsKey(PresentationMailbox.roleInbox)) { + mailboxDashBoardController.setNewFirstSelectedMailbox(mapMailboxDefault[PresentationMailbox.roleInbox]); + } else { + mailboxDashBoardController.setNewFirstSelectedMailbox(mapMailboxDefault.values.first); + } } } diff --git a/lib/features/mailbox/presentation/model/mailbox_tree_builder.dart b/lib/features/mailbox/presentation/model/mailbox_tree_builder.dart index ea523a206..e483b0947 100644 --- a/lib/features/mailbox/presentation/model/mailbox_tree_builder.dart +++ b/lib/features/mailbox/presentation/model/mailbox_tree_builder.dart @@ -2,8 +2,7 @@ import 'dart:collection'; import 'package:collection/collection.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; -import 'package:model/mailbox/presentation_mailbox.dart'; -import 'package:tmail_ui_user/features/mailbox/domain/extensions/mailbox_name_extension.dart'; +import 'package:model/model.dart'; import 'mailbox_node.dart'; import 'mailbox_tree.dart'; diff --git a/lib/features/mailbox/presentation/widgets/mailbox_folder_tile_builder.dart b/lib/features/mailbox/presentation/widgets/mailbox_folder_tile_builder.dart index e702c5478..06640878b 100644 --- a/lib/features/mailbox/presentation/widgets/mailbox_folder_tile_builder.dart +++ b/lib/features/mailbox/presentation/widgets/mailbox_folder_tile_builder.dart @@ -77,7 +77,7 @@ class MailBoxFolderTileBuilder { title: Padding( padding: EdgeInsets.only(left: 8), child: Text( - _mailboxNode.item.name!.name, + '${_mailboxNode.item.name?.name}', maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle( diff --git a/lib/features/mailbox/presentation/widgets/mailbox_tile_builder.dart b/lib/features/mailbox/presentation/widgets/mailbox_tile_builder.dart index 25fb3593d..6cde001ed 100644 --- a/lib/features/mailbox/presentation/widgets/mailbox_tile_builder.dart +++ b/lib/features/mailbox/presentation/widgets/mailbox_tile_builder.dart @@ -68,7 +68,7 @@ class MailboxTileBuilder { title: Padding( padding: EdgeInsets.only(left: 8), child: Text( - _presentationMailbox.name!.name, + '${_presentationMailbox.name?.name}', maxLines: 1, overflow:TextOverflow.ellipsis, style: TextStyle( diff --git a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart index bbb358d31..2c7dbe871 100644 --- a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart @@ -91,8 +91,8 @@ class MailboxDashBoardController extends BaseController { }); } - void addMailboxIdToMap(Role role, MailboxId mailboxId) { - mapMailboxId[role] = mailboxId; + void setMapMailboxId(Map newMapMailboxId) { + mapMailboxId = newMapMailboxId; } void setSelectedMailbox(PresentationMailbox? newPresentationMailbox) { diff --git a/lib/features/thread/presentation/thread_controller.dart b/lib/features/thread/presentation/thread_controller.dart index e0155c56b..8f1427e9f 100644 --- a/lib/features/thread/presentation/thread_controller.dart +++ b/lib/features/thread/presentation/thread_controller.dart @@ -85,6 +85,7 @@ class ThreadController extends BaseController { mailboxDashBoardController.selectedMailbox.listen((selectedMailbox) { if (_currentMailboxId != selectedMailbox?.id) { _currentMailboxId = selectedMailbox?.id; + emailList.value = []; refreshGetAllEmailAction(); } }); @@ -440,6 +441,7 @@ class ThreadController extends BaseController { final mailboxCurrent = mailboxDashBoardController.selectedMailbox.value; if (accountId != null && mailboxCurrent != null) { final importantAction = presentationEmail.isFlaggedEmail() ? MarkStarAction.unMarkStar : MarkStarAction.markStar; + dispatchState(Right(LoadingState())); consumeState(_markAsStarEmailInteractor.execute(accountId, presentationEmail.toEmail(), importantAction)); } } diff --git a/lib/main/bindings/local/local_bindings.dart b/lib/main/bindings/local/local_bindings.dart index 4b3baed5d..93ac94ca4 100644 --- a/lib/main/bindings/local/local_bindings.dart +++ b/lib/main/bindings/local/local_bindings.dart @@ -1,15 +1,25 @@ import 'package:core/core.dart'; import 'package:get/get.dart'; +import 'package:tmail_ui_user/features/caching/mailbox_cache_client.dart'; +import 'package:tmail_ui_user/features/caching/state_cache_client.dart'; +import 'package:tmail_ui_user/features/mailbox/data/network/mailbox_cache_manager.dart'; class LocalBindings extends Bindings { @override void dependencies() { _bindingDatabase(); + _bindingCaching(); } void _bindingDatabase() { Get.put(DatabaseClient()); } + + void _bindingCaching() { + Get.put(MailboxCacheClient()); + Get.put(StateCacheClient()); + Get.put(MailboxCacheManager(Get.find(), Get.find())); + } } \ No newline at end of file