diff --git a/lib/features/caching/config/hive_cache_config.dart b/lib/features/caching/config/hive_cache_config.dart index 92391291b..ee2b6fe04 100644 --- a/lib/features/caching/config/hive_cache_config.dart +++ b/lib/features/caching/config/hive_cache_config.dart @@ -11,6 +11,7 @@ import 'package:tmail_ui_user/features/login/data/model/account_cache.dart'; import 'package:tmail_ui_user/features/login/data/model/authentication_info_cache.dart'; import 'package:tmail_ui_user/features/login/data/model/encryption_key_cache.dart'; import 'package:tmail_ui_user/features/login/data/model/recent_login_url_cache.dart'; +import 'package:tmail_ui_user/features/login/data/model/recent_login_username_cache.dart'; import 'package:tmail_ui_user/features/login/data/model/token_oidc_cache.dart'; import 'package:tmail_ui_user/features/mailbox/data/model/mailbox_cache.dart'; import 'package:tmail_ui_user/features/mailbox/data/model/mailbox_rights_cache.dart'; @@ -76,6 +77,7 @@ class HiveCacheConfig { Hive.registerAdapter(EncryptionKeyCacheAdapter()); Hive.registerAdapter(AuthenticationInfoCacheAdapter()); Hive.registerAdapter(RecentLoginUrlCacheAdapter()); + Hive.registerAdapter(RecentLoginUsernameCacheAdapter()); } Future closeHive() async { diff --git a/lib/features/caching/recent_login_username_cache_client.dart b/lib/features/caching/recent_login_username_cache_client.dart new file mode 100644 index 000000000..e0b849440 --- /dev/null +++ b/lib/features/caching/recent_login_username_cache_client.dart @@ -0,0 +1,8 @@ +import 'package:tmail_ui_user/features/caching/config/hive_cache_client.dart'; +import 'package:tmail_ui_user/features/login/data/model/recent_login_username_cache.dart'; + +class RecentLoginUsernameCacheClient extends HiveCacheClient { + + @override + String get tableName => 'RecentLoginUsernameCache'; +} \ No newline at end of file diff --git a/lib/features/caching/utils/caching_constants.dart b/lib/features/caching/utils/caching_constants.dart index e94c9136c..ed2f0008e 100644 --- a/lib/features/caching/utils/caching_constants.dart +++ b/lib/features/caching/utils/caching_constants.dart @@ -12,4 +12,5 @@ class CachingConstants { static const int ENCRYPTION_KEY_HIVE_CACHE_IDENTIFY = 10; static const int AUTHENTICATION_INFO_HIVE_CACHE_IDENTIFY = 11; static const int RECENT_LOGIN_URL_HIVE_CACHE_IDENTITY = 12; + static const int RECENT_LOGIN_USERNAME_HIVE_CACHE_IDENTITY = 13; } \ No newline at end of file diff --git a/lib/features/login/data/datasource_impl/login_username_datasource_impl.dart b/lib/features/login/data/datasource_impl/login_username_datasource_impl.dart new file mode 100644 index 000000000..3afcbb348 --- /dev/null +++ b/lib/features/login/data/datasource_impl/login_username_datasource_impl.dart @@ -0,0 +1,59 @@ +import 'package:tmail_ui_user/features/caching/recent_login_username_cache_client.dart'; +import 'package:tmail_ui_user/features/login/data/datasource/login_username_datasource.dart'; +import 'package:tmail_ui_user/features/login/data/model/recent_login_username_cache.dart'; +import 'package:tmail_ui_user/features/login/domain/extensions/list_recent_login_username_extension.dart'; +import 'package:tmail_ui_user/features/login/domain/model/recent_login_username.dart'; + +class LoginUsernameDataSourceImpl implements LoginUsernameDataSource { + final RecentLoginUsernameCacheClient _recentLoginUsernameCacheClient; + + LoginUsernameDataSourceImpl(this._recentLoginUsernameCacheClient); + + @override + Future> getAllRecentLoginUsernamesLatest({int? limit, String? pattern}) { + return Future.sync(() async { + final listRecentUsernameCache = await _recentLoginUsernameCacheClient.getAll(); + final listValidRecentUsername = listRecentUsernameCache + .where((recentCache) => _filterRecentLoginUsernameCache(recentCache, pattern)) + .map((recentCache) => recentCache.toRecentLoginUsername()) + .toList(); + + listValidRecentUsername.sortByCreationDate(); + + final newLimit = limit ?? 5; + + return listValidRecentUsername.length > newLimit + ? listValidRecentUsername.sublist(0, newLimit) + : listValidRecentUsername; + }).catchError((error) { + throw error; + }); + } + + @override + Future saveLoginUsername(RecentLoginUsername recentLoginUsername) { + return Future.sync(() async { + if (await _recentLoginUsernameCacheClient + .isExistItem(recentLoginUsername.username)) { + await _recentLoginUsernameCacheClient.updateItem(recentLoginUsername.username, + recentLoginUsername.toRecentLoginUsernameCache()); + } else { + await _recentLoginUsernameCacheClient.insertItem(recentLoginUsername.username, + recentLoginUsername.toRecentLoginUsernameCache()); + } + }).catchError((error) { + throw error; + }); + } + + bool _filterRecentLoginUsernameCache( + RecentLoginUsernameCache recentLoginUsernameCache, + String? pattern + ) { + if (pattern == null || pattern.trim().isEmpty) { + return true; + } else { + return recentLoginUsernameCache.matchUsername(pattern); + } + } +} diff --git a/lib/features/login/data/model/recent_login_username_cache.dart b/lib/features/login/data/model/recent_login_username_cache.dart new file mode 100644 index 000000000..d6a74b72a --- /dev/null +++ b/lib/features/login/data/model/recent_login_username_cache.dart @@ -0,0 +1,46 @@ +import 'package:equatable/equatable.dart'; +import 'package:hive/hive.dart'; +import 'package:jmap_dart_client/jmap/core/extensions/date_time_extension.dart'; +import 'package:tmail_ui_user/features/caching/utils/caching_constants.dart'; +import 'package:tmail_ui_user/features/login/domain/model/recent_login_username.dart'; + +part 'recent_login_username_cache.g.dart'; + +@HiveType(typeId: CachingConstants.RECENT_LOGIN_USERNAME_HIVE_CACHE_IDENTITY) +class RecentLoginUsernameCache extends HiveObject with EquatableMixin { + + @HiveField(0) + final String username; + + @HiveField(1) + final DateTime creationDate; + + RecentLoginUsernameCache(this.username, this.creationDate); + + @override + List get props => [username, creationDate]; +} + +extension RecentLoginUsernameExtension on RecentLoginUsername { + RecentLoginUsernameCache toRecentLoginUsernameCache() { + return RecentLoginUsernameCache(username, creationDate); + } +} + +extension RecentLoginUsernameCacheExtension on RecentLoginUsernameCache { + RecentLoginUsername toRecentLoginUsername() { + return RecentLoginUsername(username, creationDate); + } + + bool matchUsername(String pattern) { + return username.toLowerCase().contains(pattern); + } +} + +extension ListRecentLoginUsernameCacheExtension on List { + void sortByCreationDate() { + sort((recentUsername1, recentUsername2) { + return recentUsername1.creationDate.compareToSort(recentUsername2.creationDate, false); + }); + } +} \ No newline at end of file diff --git a/lib/features/login/data/repository/login_username_repository_impl.dart b/lib/features/login/data/repository/login_username_repository_impl.dart new file mode 100644 index 000000000..e2a94df2a --- /dev/null +++ b/lib/features/login/data/repository/login_username_repository_impl.dart @@ -0,0 +1,19 @@ +import 'package:tmail_ui_user/features/login/data/datasource/login_username_datasource.dart'; +import 'package:tmail_ui_user/features/login/domain/model/recent_login_username.dart'; +import 'package:tmail_ui_user/features/login/domain/repository/login_username_repository.dart'; + +class LoginUsernameRepositoryImpl implements LoginUsernameRepository { + final LoginUsernameDataSource loginUsernameDatasource; + + LoginUsernameRepositoryImpl(this.loginUsernameDatasource); + + @override + Future> getAllRecentLoginUsernameLatest({int? limit, String? pattern}) { + return loginUsernameDatasource.getAllRecentLoginUsernamesLatest(limit: limit, pattern: pattern); + } + + @override + Future saveLoginUsername(RecentLoginUsername recentLoginUsername) { + return loginUsernameDatasource.saveLoginUsername(recentLoginUsername); + } +} \ No newline at end of file diff --git a/lib/features/login/domain/model/recent_login_username.dart b/lib/features/login/domain/model/recent_login_username.dart new file mode 100644 index 000000000..7130aa184 --- /dev/null +++ b/lib/features/login/domain/model/recent_login_username.dart @@ -0,0 +1,13 @@ +import 'package:equatable/equatable.dart'; + +class RecentLoginUsername with EquatableMixin { + final String username; + final DateTime creationDate; + + RecentLoginUsername(this.username, this.creationDate); + + factory RecentLoginUsername.now(String username) => RecentLoginUsername(username, DateTime.now()); + + @override + List get props => [ username, creationDate ]; +} \ No newline at end of file diff --git a/lib/features/login/domain/state/get_all_recent_login_username_state.dart b/lib/features/login/domain/state/get_all_recent_login_username_state.dart new file mode 100644 index 000000000..f8d305e97 --- /dev/null +++ b/lib/features/login/domain/state/get_all_recent_login_username_state.dart @@ -0,0 +1,22 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:tmail_ui_user/features/login/domain/model/recent_login_username.dart'; + +class GetAllRecentLoginUsernameLatestSuccess extends UIState { + + final List listRecentLoginUsername; + + GetAllRecentLoginUsernameLatestSuccess(this.listRecentLoginUsername); + + @override + List get props => [listRecentLoginUsername]; +} + +class GetAllRecentLoginUsernameLatestFailure extends FeatureFailure { + final dynamic exception; + + GetAllRecentLoginUsernameLatestFailure(this.exception); + + @override + List get props => [exception]; +} \ No newline at end of file diff --git a/lib/features/login/domain/state/save_recent_login_username_state.dart b/lib/features/login/domain/state/save_recent_login_username_state.dart new file mode 100644 index 000000000..5ae227914 --- /dev/null +++ b/lib/features/login/domain/state/save_recent_login_username_state.dart @@ -0,0 +1,20 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; + +class SaveRecentLoginUsernameSuccess extends UIState { + + SaveRecentLoginUsernameSuccess(); + + @override + List get props => []; +} + +class SaveRecentLoginUsernameFailed extends FeatureFailure { + final dynamic exception; + + SaveRecentLoginUsernameFailed(this.exception); + + @override + List get props => [exception]; + +} \ No newline at end of file diff --git a/lib/features/login/domain/usecases/get_all_recent_login_username_on_mobile_interactor.dart b/lib/features/login/domain/usecases/get_all_recent_login_username_on_mobile_interactor.dart new file mode 100644 index 000000000..aee585f0f --- /dev/null +++ b/lib/features/login/domain/usecases/get_all_recent_login_username_on_mobile_interactor.dart @@ -0,0 +1,20 @@ +import 'package:core/core.dart'; +import 'package:dartz/dartz.dart'; +import 'package:tmail_ui_user/features/login/domain/repository/login_username_repository.dart'; +import 'package:tmail_ui_user/features/login/domain/state/get_all_recent_login_username_state.dart'; + +class GetAllRecentLoginUsernameOnMobileInteractor { + final LoginUsernameRepository loginUsernameRepository; + + GetAllRecentLoginUsernameOnMobileInteractor(this.loginUsernameRepository); + + Future> execute({int? limit, String? pattern}) async { + try { + final listRecent = await loginUsernameRepository.getAllRecentLoginUsernameLatest( + limit: limit, pattern: pattern); + return Right(GetAllRecentLoginUsernameLatestSuccess(listRecent)); + } catch (exception) { + return Left(GetAllRecentLoginUsernameLatestFailure(exception)); + } + } +} diff --git a/lib/features/login/domain/usecases/save_login_username_on_mobile_interactor.dart b/lib/features/login/domain/usecases/save_login_username_on_mobile_interactor.dart new file mode 100644 index 000000000..dc29054f0 --- /dev/null +++ b/lib/features/login/domain/usecases/save_login_username_on_mobile_interactor.dart @@ -0,0 +1,20 @@ +import 'package:core/core.dart'; +import 'package:dartz/dartz.dart'; +import 'package:tmail_ui_user/features/login/domain/model/recent_login_username.dart'; +import 'package:tmail_ui_user/features/login/domain/repository/login_username_repository.dart'; +import 'package:tmail_ui_user/features/login/domain/state/save_recent_login_username_state.dart'; + +class SaveLoginUsernameOnMobileInteractor { + final LoginUsernameRepository loginUsernameRepository; + + SaveLoginUsernameOnMobileInteractor(this.loginUsernameRepository); + + Stream> execute(RecentLoginUsername recentLoginUsername) async* { + try { + await loginUsernameRepository.saveLoginUsername(recentLoginUsername); + yield Right(SaveRecentLoginUsernameSuccess()); + } catch(exception) { + yield Left(SaveRecentLoginUsernameFailed(exception)); + } + } +} \ No newline at end of file