TF-123 Implement refresh all mailbox

This commit is contained in:
dab246
2021-09-28 15:51:10 +07:00
committed by Dat H. Pham
parent 8f5171d581
commit eb29f514dc
52 changed files with 745 additions and 368 deletions
@@ -1,19 +1,16 @@
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';
import 'package:tmail_ui_user/features/mailbox/data/model/mailbox_response.dart';
abstract class MailboxDataSource {
Future<GetMailboxResponse?> getAllMailbox(AccountId accountId, {Properties? properties});
Future<MailboxResponse> getAllMailbox(AccountId accountId, {Properties? properties});
Future<List<Mailbox>> getAllMailboxCache();
Future<MailboxChangeResponse> getChanges(AccountId accountId, State sinceState);
Future<MailboxCacheResponse> getAllMailboxCache();
Future<MailboxChangeResponse> combineMailboxCache(MailboxChangeResponse mailboxChangeResponse, List<Mailbox> mailboxList);
Future<void> asyncUpdateCache(MailboxChangeResponse mailboxChangeResponse);
Future<void> update({List<Mailbox>? updated, List<Mailbox>? created, List<MailboxId>? destroyed});
}
@@ -0,0 +1,9 @@
import 'package:jmap_dart_client/jmap/core/state.dart';
import 'package:tmail_ui_user/features/mailbox/data/model/state_cache.dart';
import 'package:tmail_ui_user/features/mailbox/data/model/state_type.dart';
abstract class StateDataSource {
Future<State?> getState(StateType stateType);
Future<void> saveState(StateCache stateCache);
}
@@ -1,12 +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_cache_manager.dart';
import 'package:tmail_ui_user/features/mailbox/data/local/mailbox_cache_manager.dart';
import 'package:tmail_ui_user/features/mailbox/data/model/mailbox_response.dart';
class MailboxCacheDataSourceImpl extends MailboxDataSource {
@@ -15,37 +14,28 @@ class MailboxCacheDataSourceImpl extends MailboxDataSource {
MailboxCacheDataSourceImpl(this._mailboxCacheManager);
@override
Future<GetMailboxResponse?> getAllMailbox(AccountId accountId, {Properties? properties}) {
Future<MailboxResponse> getAllMailbox(AccountId accountId, {Properties? properties}) {
throw UnimplementedError();
}
@override
Future<MailboxCacheResponse> getAllMailboxCache() {
return Future.sync(() async {
return await _mailboxCacheManager.getAllMailbox();
}).catchError((error) {
throw error;
});
}
@override
Future<void> asyncUpdateCache(MailboxChangeResponse mailboxChangeResponse) {
return Future.sync(() async {
return await _mailboxCacheManager.asyncUpdateCache(mailboxChangeResponse);
}).catchError((error) {
throw error;
});
}
@override
Future<MailboxChangeResponse> getChanges(AccountId accountId, State sinceState) {
throw UnimplementedError();
}
@override
Future<MailboxChangeResponse> combineMailboxCache(MailboxChangeResponse mailboxChangeResponse, List<Mailbox> mailboxList) {
Future<void> update({List<Mailbox>? updated, List<Mailbox>? created, List<MailboxId>? destroyed}) {
return Future.sync(() async {
return await _mailboxCacheManager.combineMailboxCache(mailboxChangeResponse, mailboxList);
return await _mailboxCacheManager.update(updated: updated, created: created, destroyed: destroyed);
}).catchError((error) {
throw error;
});
}
@override
Future<List<Mailbox>> getAllMailboxCache() {
return Future.sync(() async {
return await _mailboxCacheManager.getAllMailbox();
}).catchError((error) {
throw error;
});
@@ -1,21 +1,20 @@
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/model/mailbox_response.dart';
import 'package:tmail_ui_user/features/mailbox/data/network/mailbox_api.dart';
class MailboxDataSourceImpl extends MailboxDataSource {
final MailboxAPI mailboxAPI;
MailboxDataSourceImpl(this.mailboxAPI,);
MailboxDataSourceImpl(this.mailboxAPI);
@override
Future<GetMailboxResponse?> getAllMailbox(AccountId accountId, {Properties? properties}) {
Future<MailboxResponse> getAllMailbox(AccountId accountId, {Properties? properties}) {
return Future.sync(() async {
return await mailboxAPI.getAllMailbox(accountId, properties: properties);
}).catchError((error) {
@@ -23,16 +22,6 @@ class MailboxDataSourceImpl extends MailboxDataSource {
});
}
@override
Future<MailboxCacheResponse> getAllMailboxCache() {
throw UnimplementedError();
}
@override
Future<void> asyncUpdateCache(MailboxChangeResponse mailboxChangeResponse) {
throw UnimplementedError();
}
@override
Future<MailboxChangeResponse> getChanges(AccountId accountId, State sinceState) {
return Future.sync(() async {
@@ -43,7 +32,12 @@ class MailboxDataSourceImpl extends MailboxDataSource {
}
@override
Future<MailboxChangeResponse> combineMailboxCache(MailboxChangeResponse mailboxChangeResponse, List<Mailbox> mailboxList) {
Future<void> update({List<Mailbox>? updated, List<Mailbox>? created, List<MailboxId>? destroyed}) {
throw UnimplementedError();
}
@override
Future<List<Mailbox>> getAllMailboxCache() {
throw UnimplementedError();
}
}
@@ -0,0 +1,38 @@
import 'package:jmap_dart_client/jmap/core/state.dart';
import 'package:tmail_ui_user/features/caching/state_cache_client.dart';
import 'package:tmail_ui_user/features/mailbox/data/datasource/state_datasource.dart';
import 'package:tmail_ui_user/features/mailbox/data/model/state_cache.dart';
import 'package:tmail_ui_user/features/mailbox/data/model/state_type.dart';
import 'package:tmail_ui_user/features/mailbox/data/extensions/state_cache_extension.dart';
class StateDataSourceImpl extends StateDataSource {
final StateCacheClient _stateCacheClient;
StateDataSourceImpl(this._stateCacheClient);
@override
Future<State?> getState(StateType stateType) {
return Future.sync(() async {
final stateCache = await _stateCacheClient.getItem(stateType.value);
return stateCache != null ? stateCache.toState() : null;
}).catchError((error) {
throw error;
});
}
@override
Future<void> saveState(StateCache stateCache) {
return Future.sync(() async {
final stateCacheExist = await _stateCacheClient.isExistTable();
if (stateCacheExist) {
return await _stateCacheClient.updateItem(stateCache.type.value, stateCache);
} else {
return await _stateCacheClient.insertItem(stateCache.type.value, stateCache);
}
}).catchError((error) {
throw error;
});
}
}
@@ -0,0 +1,11 @@
import 'package:tmail_ui_user/features/mailbox/data/model/mailbox_cache.dart';
extension ListMailboxCacheExtension on List<MailboxCache> {
Map<String, MailboxCache> toMap() {
return Map<String, MailboxCache>.fromIterable(
this,
key: (mailboxCache) => mailboxCache.id,
value: (mailboxCache) => mailboxCache);
}
}
@@ -0,0 +1,24 @@
import 'package:jmap_dart_client/jmap/core/id.dart';
import 'package:jmap_dart_client/jmap/core/unsigned_int.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
import 'package:tmail_ui_user/features/mailbox/data/model/mailbox_cache.dart';
import 'package:tmail_ui_user/features/mailbox/data/extensions/mailbox_rights_cache_extension.dart';
extension MailboxCacheExtension on MailboxCache {
Mailbox toMailbox() {
return Mailbox(
MailboxId(Id(id)),
name != null ? MailboxName(name!) : null,
parentId != null ? MailboxId(Id(parentId!)) : null,
role != null ? Role(role!) : null,
sortOrder != null ? SortOrder(sortValue: sortOrder!) : null,
totalEmails != null ? TotalEmails(UnsignedInt(totalEmails!)) : null,
unreadEmails != null ? UnreadEmails(UnsignedInt(unreadEmails!)) : null,
totalThreads != null ? TotalThreads(UnsignedInt(totalThreads!)) : null,
unreadThreads != null ? UnreadThreads(UnsignedInt(unreadThreads!)) : null,
myRights != null ? myRights!.toMailboxRights() : null,
isSubscribed != null ? IsSubscribed(isSubscribed!) : null
);
}
}
@@ -0,0 +1,18 @@
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
import 'package:tmail_ui_user/features/mailbox/data/model/mailbox_change_response.dart';
extension MailboxChangeResponseExtension on MailboxChangeResponse {
MailboxChangeResponse updatedMailboxChangeResponse(List<Mailbox>? updatedMailbox) {
return MailboxChangeResponse(
updated: updatedMailbox,
created: created,
destroyed: destroyed,
newStateMailbox: newStateMailbox,
newStateChanges: newStateChanges,
hasMoreChanges: hasMoreChanges,
updatedProperties: updatedProperties
);
}
}
@@ -0,0 +1,22 @@
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
import 'package:tmail_ui_user/features/mailbox/data/model/mailbox_cache.dart';
import 'package:tmail_ui_user/features/mailbox/data/extensions/mailbox_rights_extension.dart';
extension MailboxExtension on Mailbox {
MailboxCache toMailboxCache() {
return MailboxCache(
id.id.value,
name: name?.name,
parentId: parentId?.id.value,
role: role?.value,
sortOrder: sortOrder?.value.value.round(),
totalEmails: totalEmails?.value.value.round(),
unreadEmails: unreadEmails?.value.value.round(),
totalThreads: totalThreads?.value.value.round(),
unreadThreads: unreadThreads?.value.value.round(),
myRights: myRights != null ? myRights!.toMailboxRightsCache() : null,
isSubscribed: isSubscribed?.value
);
}
}
@@ -0,0 +1,18 @@
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox_rights.dart';
import 'package:tmail_ui_user/features/mailbox/data/model/mailbox_rights_cache.dart';
extension MailboxRightsCacheExtension on MailboxRightsCache {
MailboxRights toMailboxRights() {
return MailboxRights(
mayReadItems,
mayAddItems,
mayRemoveItems,
maySetSeen,
maySetKeywords,
mayCreateChild,
mayRename,
mayDelete,
maySubmit
);
}
}
@@ -0,0 +1,18 @@
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox_rights.dart';
import 'package:tmail_ui_user/features/mailbox/data/model/mailbox_rights_cache.dart';
extension MailboxRightsExtension on MailboxRights {
MailboxRightsCache toMailboxRightsCache() {
return MailboxRightsCache(
mayReadItems,
mayAddItems,
mayRemoveItems,
maySetSeen,
maySetKeywords,
mayCreateChild,
mayRename,
mayDelete,
maySubmit
);
}
}
@@ -0,0 +1,9 @@
import 'package:jmap_dart_client/jmap/core/state.dart';
import 'package:tmail_ui_user/features/mailbox/data/model/state_cache.dart';
extension StateCacheExtension on StateCache {
State toState() {
return State(state);
}
}
@@ -0,0 +1,10 @@
import 'package:jmap_dart_client/jmap/core/state.dart';
import 'package:tmail_ui_user/features/mailbox/data/model/state_cache.dart';
import 'package:tmail_ui_user/features/mailbox/data/model/state_type.dart';
extension StateExtension on State {
StateCache toStateCache(StateType stateType) {
return StateCache(stateType, value);
}
}
@@ -0,0 +1,41 @@
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
import 'package:tmail_ui_user/features/caching/mailbox_cache_client.dart';
import 'package:tmail_ui_user/features/mailbox/data/extensions/list_mailbox_cache_extension.dart';
import 'package:tmail_ui_user/features/mailbox/data/extensions/mailbox_cache_extension.dart';
import 'package:tmail_ui_user/features/mailbox/data/extensions/mailbox_extension.dart';
import 'package:tmail_ui_user/features/mailbox/data/model/mailbox_cache.dart';
class MailboxCacheManager {
final MailboxCacheClient _mailboxCacheClient;
MailboxCacheManager(this._mailboxCacheClient);
Future<List<Mailbox>> getAllMailbox() async {
final mailboxCacheList = await _mailboxCacheClient.getAll();
final mailboxList = mailboxCacheList.map((mailboxCache) => mailboxCache.toMailbox()).toList();
return mailboxList;
}
Future<void> update({List<Mailbox>? updated, List<Mailbox>? created, List<MailboxId>? destroyed}) async {
final mailboxCacheExist = await _mailboxCacheClient.isExistTable();
if (mailboxCacheExist) {
final updatedCacheMailboxes = updated
?.map((mailbox) => mailbox.toMailboxCache()).toList() ?? <MailboxCache>[];
final createdCacheMailboxes = created
?.map((mailbox) => mailbox.toMailboxCache()).toList() ?? <MailboxCache>[];
final destroyedCacheMailboxes = destroyed
?.map((mailboxId) => mailboxId.id.value).toList() ?? <String>[];
await Future.wait([
_mailboxCacheClient.updateMultipleItem(updatedCacheMailboxes.toMap()),
_mailboxCacheClient.insertMultipleItem(createdCacheMailboxes.toMap()),
_mailboxCacheClient.deleteMultipleItem(destroyedCacheMailboxes)
]);
} else {
final createdCacheMailboxes = created
?.map((mailbox) => mailbox.toMailboxCache()).toList() ?? <MailboxCache>[];
await _mailboxCacheClient.insertMultipleItem(createdCacheMailboxes.toMap());
}
}
}
@@ -0,0 +1,74 @@
import 'package:equatable/equatable.dart';
import 'package:hive/hive.dart';
import 'package:tmail_ui_user/features/mailbox/data/model/mailbox_rights_cache.dart';
import 'package:tmail_ui_user/features/mailbox/data/utils/caching_constants.dart';
part 'mailbox_cache.g.dart';
@HiveType(typeId: CachingConstants.MAILBOX_CACHE_IDENTIFY)
class MailboxCache extends HiveObject with EquatableMixin {
@HiveField(0)
final String id;
@HiveField(1)
final String? name;
@HiveField(2)
final String? parentId;
@HiveField(3)
final String? role;
@HiveField(4)
final int? sortOrder;
@HiveField(5)
final int? totalEmails;
@HiveField(6)
final int? unreadEmails;
@HiveField(7)
final int? totalThreads;
@HiveField(8)
final int? unreadThreads;
@HiveField(9)
final MailboxRightsCache? myRights;
@HiveField(10)
final bool? isSubscribed;
@HiveField(11)
final DateTime? lastOpened;
MailboxCache(
this.id,
{
this.name,
this.parentId,
this.role,
this.sortOrder,
this.totalEmails,
this.unreadEmails,
this.totalThreads,
this.unreadThreads,
this.myRights,
this.isSubscribed,
this.lastOpened
}
);
@override
List<Object?> get props => [
id,
name,
parentId,
role,
unreadEmails,
lastOpened,
];
}
@@ -0,0 +1,74 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'mailbox_cache.dart';
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class MailboxCacheAdapter extends TypeAdapter<MailboxCache> {
@override
final int typeId = 1;
@override
MailboxCache read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return MailboxCache(
fields[0] as String,
name: fields[1] as String?,
parentId: fields[2] as String?,
role: fields[3] as String?,
sortOrder: fields[4] as int?,
totalEmails: fields[5] as int?,
unreadEmails: fields[6] as int?,
totalThreads: fields[7] as int?,
unreadThreads: fields[8] as int?,
myRights: fields[9] as MailboxRightsCache?,
isSubscribed: fields[10] as bool?,
lastOpened: fields[11] as DateTime?,
);
}
@override
void write(BinaryWriter writer, MailboxCache obj) {
writer
..writeByte(12)
..writeByte(0)
..write(obj.id)
..writeByte(1)
..write(obj.name)
..writeByte(2)
..write(obj.parentId)
..writeByte(3)
..write(obj.role)
..writeByte(4)
..write(obj.sortOrder)
..writeByte(5)
..write(obj.totalEmails)
..writeByte(6)
..write(obj.unreadEmails)
..writeByte(7)
..write(obj.totalThreads)
..writeByte(8)
..write(obj.unreadThreads)
..writeByte(9)
..write(obj.myRights)
..writeByte(10)
..write(obj.isSubscribed)
..writeByte(11)
..write(obj.lastOpened);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is MailboxCacheAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}
@@ -1,16 +0,0 @@
import 'package:equatable/equatable.dart';
import 'package:model/model.dart';
class MailboxCacheResponse with EquatableMixin {
final List<MailboxCache>? mailboxCaches;
final StateDao? oldState;
MailboxCacheResponse({
this.mailboxCaches,
this.oldState
});
@override
List<Object?> get props => [mailboxCaches, oldState];
}
@@ -8,17 +8,29 @@ class MailboxChangeResponse with EquatableMixin {
final List<Mailbox>? updated;
final List<Mailbox>? created;
final List<MailboxId>? destroyed;
final State? newState;
final State? newStateMailbox;
final State? newStateChanges;
final bool hasMoreChanges;
final Properties? updatedProperties;
MailboxChangeResponse({
this.updated,
this.created,
this.destroyed,
this.newState,
this.newStateMailbox,
this.newStateChanges,
this.hasMoreChanges = false,
this.updatedProperties,
});
@override
List<Object?> get props => [updated, created, destroyed, newState, updatedProperties];
List<Object?> get props => [
updated,
created,
destroyed,
newStateMailbox,
newStateChanges,
hasMoreChanges,
updatedProperties
];
}
@@ -0,0 +1,23 @@
import 'package:equatable/equatable.dart';
import 'package:jmap_dart_client/jmap/core/state.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
class MailboxResponse with EquatableMixin {
final List<Mailbox>? mailboxes;
final State? state;
MailboxResponse({
this.mailboxes,
this.state
});
bool hasData() {
return mailboxes != null
&& mailboxes!.isNotEmpty
&& state != null;
}
@override
List<Object?> get props => [mailboxes, state];
}
@@ -0,0 +1,62 @@
import 'package:equatable/equatable.dart';
import 'package:hive/hive.dart';
import 'package:tmail_ui_user/features/mailbox/data/utils/caching_constants.dart';
part 'mailbox_rights_cache.g.dart';
@HiveType(typeId: CachingConstants.MAILBOX_RIGHTS_CACHE_IDENTIFY)
class MailboxRightsCache extends HiveObject with EquatableMixin {
@HiveField(0)
final bool mayReadItems;
@HiveField(1)
final bool mayAddItems;
@HiveField(2)
final bool mayRemoveItems;
@HiveField(3)
final bool maySetSeen;
@HiveField(4)
final bool maySetKeywords;
@HiveField(5)
final bool mayCreateChild;
@HiveField(6)
final bool mayRename;
@HiveField(7)
final bool mayDelete;
@HiveField(8)
final bool maySubmit;
MailboxRightsCache(
this.mayReadItems,
this.mayAddItems,
this.mayRemoveItems,
this.maySetSeen,
this.maySetKeywords,
this.mayCreateChild,
this.mayRename,
this.mayDelete,
this.maySubmit
);
@override
List<Object?> get props => [
mayReadItems,
mayAddItems,
mayRemoveItems,
maySetSeen,
maySetKeywords,
mayCreateChild,
mayRename,
mayDelete,
maySubmit
];
}
@@ -0,0 +1,65 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'mailbox_rights_cache.dart';
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class MailboxRightsCacheAdapter extends TypeAdapter<MailboxRightsCache> {
@override
final int typeId = 2;
@override
MailboxRightsCache read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return MailboxRightsCache(
fields[0] as bool,
fields[1] as bool,
fields[2] as bool,
fields[3] as bool,
fields[4] as bool,
fields[5] as bool,
fields[6] as bool,
fields[7] as bool,
fields[8] as bool,
);
}
@override
void write(BinaryWriter writer, MailboxRightsCache obj) {
writer
..writeByte(9)
..writeByte(0)
..write(obj.mayReadItems)
..writeByte(1)
..write(obj.mayAddItems)
..writeByte(2)
..write(obj.mayRemoveItems)
..writeByte(3)
..write(obj.maySetSeen)
..writeByte(4)
..write(obj.maySetKeywords)
..writeByte(5)
..write(obj.mayCreateChild)
..writeByte(6)
..write(obj.mayRename)
..writeByte(7)
..write(obj.mayDelete)
..writeByte(8)
..write(obj.maySubmit);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is MailboxRightsCacheAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}
@@ -0,0 +1,22 @@
import 'package:equatable/equatable.dart';
import 'package:hive/hive.dart';
import 'package:tmail_ui_user/features/mailbox/data/model/state_type.dart';
import 'package:tmail_ui_user/features/mailbox/data/utils/caching_constants.dart';
part 'state_cache.g.dart';
@HiveType(typeId: CachingConstants.STATE_CACHE_IDENTIFY)
class StateCache extends HiveObject with EquatableMixin {
@HiveField(0)
final StateType type;
@HiveField(1)
final String state;
StateCache(this.type, this.state);
@override
List<Object?> get props => [type, state];
}
@@ -0,0 +1,44 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'state_cache.dart';
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class StateCacheAdapter extends TypeAdapter<StateCache> {
@override
final int typeId = 3;
@override
StateCache read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return StateCache(
fields[0] as StateType,
fields[1] as String,
);
}
@override
void write(BinaryWriter writer, StateCache obj) {
writer
..writeByte(2)
..writeByte(0)
..write(obj.type)
..writeByte(1)
..write(obj.state);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is StateCacheAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}
@@ -0,0 +1,21 @@
import 'package:hive/hive.dart';
import 'package:tmail_ui_user/features/mailbox/data/utils/caching_constants.dart';
part 'state_type.g.dart';
@HiveType(typeId: CachingConstants.STATE_TYPE_IDENTIFY)
enum StateType {
@HiveField(0)
mailbox
}
extension StateTypeExtension on StateType {
String get value {
switch(this) {
case StateType.mailbox:
return 'mailbox';
}
}
}
@@ -0,0 +1,41 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'state_type.dart';
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class StateTypeAdapter extends TypeAdapter<StateType> {
@override
final int typeId = 4;
@override
StateType read(BinaryReader reader) {
switch (reader.readByte()) {
case 0:
return StateType.mailbox;
default:
return StateType.mailbox;
}
}
@override
void write(BinaryWriter writer, StateType obj) {
switch (obj) {
case StateType.mailbox:
writer.writeByte(0);
break;
}
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is StateTypeAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}
@@ -13,6 +13,7 @@ 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';
import 'package:tmail_ui_user/features/mailbox/data/model/mailbox_response.dart';
class MailboxAPI {
@@ -20,7 +21,7 @@ class MailboxAPI {
MailboxAPI(this.httpClient);
Future<GetMailboxResponse?> getAllMailbox(AccountId accountId, {Properties? properties}) async {
Future<MailboxResponse> getAllMailbox(AccountId accountId, {Properties? properties}) async {
final processingInvocation = ProcessingInvocation();
final jmapRequestBuilder = JmapRequestBuilder(httpClient, processingInvocation);
@@ -38,7 +39,7 @@ class MailboxAPI {
queryInvocation.methodCallId,
GetMailboxResponse.deserialize);
return resultCreated;
return MailboxResponse(mailboxes: resultCreated?.list, state: resultCreated?.state);
}
Future<MailboxChangeResponse> getChanges(AccountId accountId, State sinceState) async {
@@ -85,19 +86,13 @@ class MailboxAPI {
final listMailboxIdDestroyed = resultChanges?.destroyed.map((id) => MailboxId(id)).toList() ?? <MailboxId>[];
final updatedProperties = resultChanges?.updatedProperties;
final listMailboxUpdated = resultUpdated?.list ?? <Mailbox>[];
final listMailboxCreated = resultCreated?.list ?? <Mailbox>[];
final newState = resultChanges?.newState;
return MailboxChangeResponse(
updated: listMailboxUpdated,
created: listMailboxCreated,
updated: resultUpdated?.list,
created: resultCreated?.list,
destroyed: listMailboxIdDestroyed,
newState: newState,
updatedProperties: updatedProperties);
newStateMailbox: resultUpdated?.state,
newStateChanges: resultChanges?.newState,
hasMoreChanges: resultChanges?.hasMoreChanges ?? false,
updatedProperties: resultChanges?.updatedProperties);
}
}
@@ -1,105 +0,0 @@
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<MailboxCacheResponse> 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<MailboxChangeResponse> combineMailboxCache(MailboxChangeResponse mailboxChangeResponse, List<Mailbox> 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<Mailbox> mailboxList) {
try {
return mailboxList.firstWhere((element) => element.id == mailboxId);
} catch (e) {
return null;
}
}
Future<void> 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() ?? <MailboxCache>[];
final createdCacheMailboxes = mailboxChangeResponse.created
?.map((mailbox) => mailbox.toMailboxCache())
.toList() ?? <MailboxCache>[];
final destroyedCacheMailboxes = mailboxChangeResponse.destroyed
?.map((mailboxId) => mailboxId.id.value)
.toList() ?? <String>[];
await Future.wait([
_mailboxCacheClient.updateMultipleItem(updatedCacheMailboxes.toMap()),
_mailboxCacheClient.insertMultipleItem(createdCacheMailboxes.toMap()),
_mailboxCacheClient.deleteMultipleItem(destroyedCacheMailboxes)
]);
} else {
final createdCacheMailboxes = mailboxChangeResponse.created
?.map((mailbox) => mailbox.toMailboxCache())
.toList() ?? <MailboxCache>[];
await _mailboxCacheClient.insertMultipleItem(createdCacheMailboxes.toMap());
}
}
}
@@ -1,65 +1,137 @@
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:model/model.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/data/datasource/state_datasource.dart';
import 'package:tmail_ui_user/features/mailbox/data/extensions/state_extension.dart';
import 'package:tmail_ui_user/features/mailbox/data/model/mailbox_response.dart';
import 'package:tmail_ui_user/features/mailbox/data/model/state_type.dart';
import 'package:tmail_ui_user/features/mailbox/domain/repository/mailbox_repository.dart';
class MailboxRepositoryImpl extends MailboxRepository {
final Map<DataSourceType, MailboxDataSource> mapDataSource;
final StateDataSource stateDataSource;
MailboxRepositoryImpl(this.mapDataSource);
MailboxRepositoryImpl(this.mapDataSource, this.stateDataSource);
@override
Stream<List<Mailbox>> getAllMailbox(AccountId accountId, {Properties? properties}) async* {
final mailboxCacheResponse = await mapDataSource[DataSourceType.local]!.getAllMailboxCache();
Stream<MailboxResponse> getAllMailbox(AccountId accountId, {Properties? properties}) async* {
final localMailboxResponse = await Future.wait([
mapDataSource[DataSourceType.local]!.getAllMailboxCache(),
stateDataSource.getState(StateType.mailbox)
]).then((List response) {
return MailboxResponse(mailboxes: response.first, state: response.last);
});
final mailboxList = mailboxCacheResponse.mailboxCaches != null
? mailboxCacheResponse.mailboxCaches!.toMailboxList()
: <Mailbox>[];
final oldState = mailboxCacheResponse.oldState?.state;
yield localMailboxResponse;
yield mailboxList;
if (localMailboxResponse.hasData()) {
bool hasMoreChanges = true;
State? sinceState = localMailboxResponse.state!;
if (mailboxList.isNotEmpty && oldState != null) {
final changesResponse = await mapDataSource[DataSourceType.network]!.getChanges(accountId, State(oldState));
while(hasMoreChanges && sinceState != null) {
final changesResponse = await mapDataSource[DataSourceType.network]!.getChanges(accountId, sinceState);
final newChangesResponse = await mapDataSource[DataSourceType.local]!.combineMailboxCache(changesResponse, mailboxList);
hasMoreChanges = changesResponse.hasMoreChanges;
sinceState = changesResponse.newStateChanges;
await mapDataSource[DataSourceType.local]!.asyncUpdateCache(newChangesResponse);
final newMailboxUpdated = await _combineMailboxCache(
mailboxUpdated: changesResponse.updated,
updatedProperties: changesResponse.updatedProperties,
mailboxCacheList: localMailboxResponse.mailboxes!);
await Future.wait([
mapDataSource[DataSourceType.local]!.update(
updated: newMailboxUpdated,
created: changesResponse.created,
destroyed: changesResponse.destroyed),
if (changesResponse.newStateMailbox != null)
stateDataSource.saveState(changesResponse.newStateMailbox!.toStateCache(StateType.mailbox)),
]);
}
} else {
final getMailboxResponse = await mapDataSource[DataSourceType.network]!.getAllMailbox(accountId);
final mailboxResponse = await mapDataSource[DataSourceType.network]!.getAllMailbox(accountId);
await mapDataSource[DataSourceType.local]!.asyncUpdateCache(MailboxChangeResponse(
created: getMailboxResponse?.list,
newState: getMailboxResponse?.state));
await Future.wait([
mapDataSource[DataSourceType.local]!.update(created: mailboxResponse.mailboxes),
if (mailboxResponse.state != null)
stateDataSource.saveState(mailboxResponse.state!.toStateCache(StateType.mailbox)),
]);
}
final newMailboxCacheResponse = await mapDataSource[DataSourceType.local]!.getAllMailboxCache();
final newMailboxResponse = await Future.wait([
mapDataSource[DataSourceType.local]!.getAllMailboxCache(),
stateDataSource.getState(StateType.mailbox)
]).then((List response) {
return MailboxResponse(mailboxes: response.first, state: response.last);
});
final newMailboxList = newMailboxCacheResponse.mailboxCaches != null
? newMailboxCacheResponse.mailboxCaches!.toMailboxList()
: <Mailbox>[];
yield newMailboxResponse;
}
yield newMailboxList;
Future<List<Mailbox>?> _combineMailboxCache({
List<Mailbox>? mailboxUpdated,
Properties? updatedProperties,
List<Mailbox>? mailboxCacheList
}) async {
if (mailboxUpdated != null && mailboxUpdated.isNotEmpty) {
final newMailboxUpdated = mailboxUpdated.map((mailboxUpdated) {
if (updatedProperties == null) {
return mailboxUpdated;
} else {
final mailboxOld = mailboxCacheList?.findMailbox(mailboxUpdated.id);
if (mailboxOld != null) {
return mailboxOld.combineMailbox(mailboxUpdated, updatedProperties);
} else {
return mailboxUpdated;
}
}
}).toList();
return newMailboxUpdated;
}
return mailboxUpdated;
}
@override
Stream<List<Mailbox>> refresh(AccountId accountId, State currentState) async* {
final changesResponse = await mapDataSource[DataSourceType.network]!.getChanges(accountId, currentState);
Stream<MailboxResponse> refresh(AccountId accountId, State currentState) async* {
final localMailboxList = await mapDataSource[DataSourceType.local]!.getAllMailboxCache();
await mapDataSource[DataSourceType.local]!.asyncUpdateCache(changesResponse);
bool hasMoreChanges = true;
State? sinceState = currentState;
final newMailboxCacheResponse = await mapDataSource[DataSourceType.local]!.getAllMailboxCache();
while(hasMoreChanges && sinceState != null) {
final changesResponse = await mapDataSource[DataSourceType.network]!.getChanges(accountId, sinceState);
final newMailboxList = newMailboxCacheResponse.mailboxCaches != null
? newMailboxCacheResponse.mailboxCaches!.toMailboxList()
: <Mailbox>[];
hasMoreChanges = changesResponse.hasMoreChanges;
sinceState = changesResponse.newStateChanges;
yield newMailboxList;
final newMailboxUpdated = await _combineMailboxCache(
mailboxUpdated: changesResponse.updated,
updatedProperties: changesResponse.updatedProperties,
mailboxCacheList: localMailboxList);
await Future.wait([
mapDataSource[DataSourceType.local]!.update(
updated: newMailboxUpdated,
created: changesResponse.created,
destroyed: changesResponse.destroyed),
if (changesResponse.newStateMailbox != null)
stateDataSource.saveState(changesResponse.newStateMailbox!.toStateCache(StateType.mailbox)),
]);
}
final newMailboxResponse = await Future.wait([
mapDataSource[DataSourceType.local]!.getAllMailboxCache(),
stateDataSource.getState(StateType.mailbox)
]).then((List response) {
return MailboxResponse(mailboxes: response.first, state: response.last);
});
yield newMailboxResponse;
}
}
@@ -0,0 +1,7 @@
class CachingConstants {
static const int MAILBOX_CACHE_IDENTIFY = 1;
static const int MAILBOX_RIGHTS_CACHE_IDENTIFY = 2;
static const int STATE_CACHE_IDENTIFY = 3;
static const int STATE_TYPE_IDENTIFY = 4;
}