diff --git a/lib/features/caching/adapter/uri_adapter.dart b/lib/features/caching/adapter/uri_adapter.dart new file mode 100644 index 000000000..dbf319518 --- /dev/null +++ b/lib/features/caching/adapter/uri_adapter.dart @@ -0,0 +1,18 @@ +import 'package:hive/hive.dart'; +import 'package:tmail_ui_user/features/caching/utils/caching_constants.dart'; + +class UriAdapter extends TypeAdapter { + @override + final int typeId = CachingConstants.URI_CACHE_IDENTITY; + + @override + Uri read(BinaryReader reader) { + return Uri.parse(reader.readString()); + } + + @override + void write(BinaryWriter writer, Uri obj) { + writer.writeString(obj.toString()); + } + +} \ No newline at end of file diff --git a/lib/features/caching/config/hive_cache_config.dart b/lib/features/caching/config/hive_cache_config.dart index 3d53fc59e..798b1b4c0 100644 --- a/lib/features/caching/config/hive_cache_config.dart +++ b/lib/features/caching/config/hive_cache_config.dart @@ -6,10 +6,12 @@ import 'package:core/utils/app_logger.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:path_provider/path_provider.dart' as path_provider; +import 'package:tmail_ui_user/features/caching/adapter/uri_adapter.dart'; import 'package:tmail_ui_user/features/login/data/local/encryption_key_cache_manager.dart'; 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/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'; @@ -74,6 +76,8 @@ class HiveCacheConfig { Hive.registerAdapter(AccountCacheAdapter()); Hive.registerAdapter(EncryptionKeyCacheAdapter()); Hive.registerAdapter(AuthenticationInfoCacheAdapter()); + Hive.registerAdapter(UriAdapter()); + Hive.registerAdapter(RecentLoginUrlCacheAdapter()); } Future closeHive() async { diff --git a/lib/features/caching/recent_login_url_cache_client.dart b/lib/features/caching/recent_login_url_cache_client.dart new file mode 100644 index 000000000..3366667de --- /dev/null +++ b/lib/features/caching/recent_login_url_cache_client.dart @@ -0,0 +1,126 @@ +import 'package:hive/hive.dart'; +import 'package:tmail_ui_user/features/caching/config/hive_cache_client.dart'; +import 'package:tmail_ui_user/features/login/data/model/recent_login_url_cache.dart'; + +class RecentLoginUrlCacheClient extends HiveCacheClient { + + @override + String get tableName => 'RecentLoginUrlCache'; + + @override + Future clearAllData() { + return Future.sync(() async { + final boxRecent = await openBox(); + return boxRecent.clear(); + }).catchError((error) { + throw error; + }); + } + + @override + Future deleteItem(String key) { + return Future.sync(() async { + final boxRecent = await openBox(); + return boxRecent.delete(key); + }).catchError((error) { + throw error; + }); + } + + @override + Future deleteMultipleItem(List listKey) { + return Future.sync(() async { + final boxRecent = await openBox(); + return boxRecent.deleteAll(listKey); + }).catchError((error) { + throw error; + }); + } + + @override + Future> getAll() { + return Future.sync(() async { + final boxRecent = await openBox(); + return boxRecent.values.toList(); + }).catchError((error) { + throw error; + }); + } + + @override + Future getItem(String key) { + return Future.sync(() async { + final boxRecent = await openBox(); + return boxRecent.get(key); + }).catchError((error) { + throw error; + }); + } + + @override + Future insertItem(String key, RecentLoginUrlCache newObject) { + return Future.sync(() async { + final boxRecent = await openBox(); + return boxRecent.put(key, newObject); + }).catchError((error) { + throw error; + }); + } + + @override + Future insertMultipleItem(Map mapObject) { + return Future.sync(() async { + final boxRecent = await openBox(); + return boxRecent.putAll(mapObject); + }).catchError((error) { + throw error; + }); + } + + @override + Future isExistItem(String key) { + return Future.sync(() async { + final boxRecent = await openBox(); + return boxRecent.containsKey(key); + }).catchError((error) { + throw error; + }); + } + + @override + Future isExistTable() { + return Future.sync(() async { + return Hive.boxExists(tableName); + }).catchError((error) { + throw error; + }); + } + + @override + Future> openBox() async { + if (Hive.isBoxOpen(tableName)) { + return Hive.box(tableName); + } + return Hive.openBox(tableName); + } + + @override + Future updateItem(String key, RecentLoginUrlCache newObject) { + return Future.sync(() async { + final boxRecent = await openBox(); + return boxRecent.put(key, newObject); + }).catchError((error) { + throw error; + }); + } + + @override + Future updateMultipleItem(Map mapObject) { + return Future.sync(() async { + final boxRecent = await openBox(); + return boxRecent.putAll(mapObject); + }).catchError((error) { + throw error; + }); + } +} \ 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 785b9766a..2691f6ce5 100644 --- a/lib/features/caching/utils/caching_constants.dart +++ b/lib/features/caching/utils/caching_constants.dart @@ -11,4 +11,6 @@ class CachingConstants { static const int ACCOUNT_HIVE_CACHE_IDENTIFY = 9; static const int ENCRYPTION_KEY_HIVE_CACHE_IDENTIFY = 10; static const int AUTHENTICATION_INFO_HIVE_CACHE_IDENTIFY = 11; + static const int URI_CACHE_IDENTITY = 12; + static const int LOGIN_URL_CACHE_IDENTITY = 13; } \ No newline at end of file diff --git a/lib/features/login/data/datasource/login_url_datasource.dart b/lib/features/login/data/datasource/login_url_datasource.dart new file mode 100644 index 000000000..e9d307aea --- /dev/null +++ b/lib/features/login/data/datasource/login_url_datasource.dart @@ -0,0 +1,5 @@ +import 'package:tmail_ui_user/features/login/domain/model/recent_login_url.dart'; + +abstract class LoginUrlDataSource { + Future saveLoginUrl(RecentLoginUrl baseUrl); +} \ No newline at end of file diff --git a/lib/features/login/data/datasource_impl/login_url_datasource_impl.dart b/lib/features/login/data/datasource_impl/login_url_datasource_impl.dart new file mode 100644 index 000000000..7fed19e29 --- /dev/null +++ b/lib/features/login/data/datasource_impl/login_url_datasource_impl.dart @@ -0,0 +1,26 @@ +import 'package:tmail_ui_user/features/caching/recent_login_url_cache_client.dart'; +import 'package:tmail_ui_user/features/login/data/datasource/login_url_datasource.dart'; +import 'package:tmail_ui_user/features/login/data/model/recent_login_url_cache.dart'; +import 'package:tmail_ui_user/features/login/domain/model/recent_login_url.dart'; + +class LoginUrlDataSourceImpl implements LoginUrlDataSource { + + final RecentLoginUrlCacheClient _recentLoginUrlCacheClient; + + LoginUrlDataSourceImpl(this._recentLoginUrlCacheClient); + + @override + Future saveLoginUrl(RecentLoginUrl recentLoginUrl) { + return Future.sync(() async { + final tmpRecentLoginUrl = recentLoginUrl.url.toString(); + if(await _recentLoginUrlCacheClient.isExistItem(tmpRecentLoginUrl)){ + await _recentLoginUrlCacheClient.updateItem(tmpRecentLoginUrl, recentLoginUrl.toRecentLoginUrlCache()); + } else { + await _recentLoginUrlCacheClient.insertItem(tmpRecentLoginUrl, recentLoginUrl.toRecentLoginUrlCache()); + } + }).catchError((error) { + throw error; + }); + } + +} \ No newline at end of file diff --git a/lib/features/login/data/model/recent_login_url_cache.dart b/lib/features/login/data/model/recent_login_url_cache.dart new file mode 100644 index 000000000..b81b98c13 --- /dev/null +++ b/lib/features/login/data/model/recent_login_url_cache.dart @@ -0,0 +1,38 @@ +import 'package:equatable/equatable.dart'; +import 'package:hive/hive.dart'; +import 'package:tmail_ui_user/features/caching/utils/caching_constants.dart'; +import 'package:tmail_ui_user/features/login/domain/model/recent_login_url.dart'; + +part 'recent_login_url_cache.g.dart'; + +@HiveType(typeId: CachingConstants.LOGIN_URL_CACHE_IDENTITY) +class RecentLoginUrlCache extends HiveObject with EquatableMixin { + + @HiveField(0) + final Uri url; + + @HiveField(1) + final DateTime creationDate; + + RecentLoginUrlCache(this.url, this.creationDate); + + @override + List get props => [url, creationDate]; +} + +extension RecentLoginUrlExtension on RecentLoginUrl { + RecentLoginUrlCache toRecentLoginUrlCache() { + return RecentLoginUrlCache(url, creationDate); + } +} + +extension RecentLoginUrlCacheExtension on RecentLoginUrlCache { + RecentLoginUrl toRecentLoginUrl() { + return RecentLoginUrl(url, creationDate); + } + + bool matchUrl(Uri pattern) { + return url.toString().toLowerCase().contains(pattern.toString().toLowerCase()); + } +} + diff --git a/lib/features/login/data/repository/login_url_repository_impl.dart b/lib/features/login/data/repository/login_url_repository_impl.dart new file mode 100644 index 000000000..3dfdf9543 --- /dev/null +++ b/lib/features/login/data/repository/login_url_repository_impl.dart @@ -0,0 +1,14 @@ +import 'package:tmail_ui_user/features/login/data/datasource/login_url_datasource.dart'; +import 'package:tmail_ui_user/features/login/domain/model/recent_login_url.dart'; +import 'package:tmail_ui_user/features/login/domain/repository/login_url_repository.dart'; + +class LoginUrlRepositoryImpl implements LoginUrlRepository { + final LoginUrlDataSource loginInfoDataSource; + + LoginUrlRepositoryImpl(this.loginInfoDataSource); + + @override + Future saveRecentLoginUrl(RecentLoginUrl recentLogin) { + return loginInfoDataSource.saveLoginUrl(recentLogin); + } +} \ No newline at end of file diff --git a/lib/features/login/domain/model/recent_login_url.dart b/lib/features/login/domain/model/recent_login_url.dart new file mode 100644 index 000000000..3822c91cd --- /dev/null +++ b/lib/features/login/domain/model/recent_login_url.dart @@ -0,0 +1,14 @@ +import 'package:equatable/equatable.dart'; + +class RecentLoginUrl with EquatableMixin { + final Uri url; + final DateTime creationDate; + + RecentLoginUrl(this.url, this.creationDate); + + factory RecentLoginUrl.now(Uri url) => + RecentLoginUrl(url, DateTime.now()); + + @override + List get props => [url, creationDate]; +} \ No newline at end of file diff --git a/lib/features/login/domain/repository/login_url_repository.dart b/lib/features/login/domain/repository/login_url_repository.dart new file mode 100644 index 000000000..276cb1efa --- /dev/null +++ b/lib/features/login/domain/repository/login_url_repository.dart @@ -0,0 +1,5 @@ +import 'package:tmail_ui_user/features/login/domain/model/recent_login_url.dart'; + +abstract class LoginUrlRepository { + Future saveRecentLoginUrl(RecentLoginUrl recentLoginUrl); +} \ No newline at end of file diff --git a/lib/features/login/domain/state/save_recent_login_url_state.dart b/lib/features/login/domain/state/save_recent_login_url_state.dart new file mode 100644 index 000000000..4932b9ac7 --- /dev/null +++ b/lib/features/login/domain/state/save_recent_login_url_state.dart @@ -0,0 +1,20 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; + +class SaveRecentLoginUrlSuccess extends UIState { + + SaveRecentLoginUrlSuccess(); + + @override + List get props => []; +} + +class SaveRecentLoginUrlFailed extends FeatureFailure { + final dynamic exception; + + SaveRecentLoginUrlFailed(this.exception); + + @override + List get props => [exception]; + +} \ No newline at end of file diff --git a/lib/features/login/domain/usecases/save_login_url_on_mobile_interactor.dart b/lib/features/login/domain/usecases/save_login_url_on_mobile_interactor.dart new file mode 100644 index 000000000..9655a1e1a --- /dev/null +++ b/lib/features/login/domain/usecases/save_login_url_on_mobile_interactor.dart @@ -0,0 +1,21 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:dartz/dartz.dart'; +import 'package:tmail_ui_user/features/login/domain/model/recent_login_url.dart'; +import 'package:tmail_ui_user/features/login/domain/repository/login_url_repository.dart'; +import 'package:tmail_ui_user/features/login/domain/state/save_recent_login_url_state.dart'; + +class SaveLoginUrlOnMobileInteractor { + final LoginUrlRepository loginUrlRepository; + + SaveLoginUrlOnMobileInteractor(this.loginUrlRepository); + + Stream> execute(RecentLoginUrl recentLoginUrl) async* { + try{ + await loginUrlRepository.saveRecentLoginUrl(recentLoginUrl); + yield Right(SaveRecentLoginUrlSuccess()); + } catch(e) { + yield Left(SaveRecentLoginUrlFailed(e)); + } + } +} \ No newline at end of file diff --git a/lib/features/login/presentation/login_bindings.dart b/lib/features/login/presentation/login_bindings.dart index c2a4461d6..2fe3c2710 100644 --- a/lib/features/login/presentation/login_bindings.dart +++ b/lib/features/login/presentation/login_bindings.dart @@ -1,12 +1,15 @@ import 'package:core/core.dart'; import 'package:get/get.dart'; import 'package:tmail_ui_user/features/base/base_bindings.dart'; +import 'package:tmail_ui_user/features/caching/recent_login_url_cache_client.dart'; import 'package:tmail_ui_user/features/login/data/datasource/account_datasource.dart'; import 'package:tmail_ui_user/features/login/data/datasource/authentication_datasource.dart'; import 'package:tmail_ui_user/features/login/data/datasource/authentication_oidc_datasource.dart'; +import 'package:tmail_ui_user/features/login/data/datasource/login_url_datasource.dart'; import 'package:tmail_ui_user/features/login/data/datasource_impl/authentication_datasource_impl.dart'; import 'package:tmail_ui_user/features/login/data/datasource_impl/authentication_oidc_datasource_impl.dart'; import 'package:tmail_ui_user/features/login/data/datasource_impl/hive_account_datasource_impl.dart'; +import 'package:tmail_ui_user/features/login/data/datasource_impl/login_url_datasource_impl.dart'; import 'package:tmail_ui_user/features/login/data/local/account_cache_manager.dart'; import 'package:tmail_ui_user/features/login/data/local/oidc_configuration_cache_manager.dart'; import 'package:tmail_ui_user/features/login/data/local/token_oidc_cache_manager.dart'; @@ -16,10 +19,12 @@ import 'package:tmail_ui_user/features/login/data/network/oidc_http_client.dart' import 'package:tmail_ui_user/features/login/data/repository/account_repository_impl.dart'; import 'package:tmail_ui_user/features/login/data/repository/authentication_oidc_repository_impl.dart'; import 'package:tmail_ui_user/features/login/data/repository/authentication_repository_impl.dart'; +import 'package:tmail_ui_user/features/login/data/repository/login_url_repository_impl.dart'; import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart'; import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart'; import 'package:tmail_ui_user/features/login/domain/repository/authentication_repository.dart'; import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart'; +import 'package:tmail_ui_user/features/login/domain/repository/login_url_repository.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/authenticate_oidc_on_browser_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/authentication_user_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/check_oidc_is_available_interactor.dart'; @@ -27,6 +32,7 @@ import 'package:tmail_ui_user/features/login/domain/usecases/get_authentication_ import 'package:tmail_ui_user/features/login/domain/usecases/get_oidc_configuration_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/get_stored_oidc_configuration_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/get_token_oidc_interactor.dart'; +import 'package:tmail_ui_user/features/login/domain/usecases/save_login_url_on_mobile_interactor.dart'; import 'package:tmail_ui_user/features/login/presentation/login_controller.dart'; import 'package:tmail_ui_user/main/bindings/network/binding_tag.dart'; @@ -45,6 +51,7 @@ class LoginBindings extends BaseBindings { Get.find(), Get.find(), Get.find(), + Get.find(), )); } @@ -53,6 +60,7 @@ class LoginBindings extends BaseBindings { Get.lazyPut(() => Get.find()); Get.lazyPut(() => Get.find()); Get.lazyPut(() => Get.find()); + Get.lazyPut(() => Get.find()); } @override @@ -67,6 +75,9 @@ class LoginBindings extends BaseBindings { Get.lazyPut(() => HiveAccountDatasourceImpl( Get.find() )); + Get.lazyPut(() => LoginUrlDataSourceImpl( + Get.find() + )); } @override @@ -96,6 +107,9 @@ class LoginBindings extends BaseBindings { Get.lazyPut(() => GetStoredOidcConfigurationInteractor( Get.find(), )); + Get.lazyPut(() => SaveLoginUrlOnMobileInteractor( + Get.find(), + )); } @override @@ -103,6 +117,7 @@ class LoginBindings extends BaseBindings { Get.lazyPut(() => Get.find()); Get.lazyPut(() => Get.find()); Get.lazyPut(() => Get.find()); + Get.lazyPut(() => Get.find()); } @override @@ -110,5 +125,6 @@ class LoginBindings extends BaseBindings { Get.lazyPut(() => AuthenticationRepositoryImpl(Get.find())); Get.lazyPut(() => AuthenticationOIDCRepositoryImpl(Get.find())); Get.lazyPut(() => AccountRepositoryImpl(Get.find())); + Get.lazyPut(() => LoginUrlRepositoryImpl(Get.find())); } } \ No newline at end of file diff --git a/lib/features/login/presentation/login_controller.dart b/lib/features/login/presentation/login_controller.dart index 36c54f926..ab1a8ff48 100644 --- a/lib/features/login/presentation/login_controller.dart +++ b/lib/features/login/presentation/login_controller.dart @@ -20,6 +20,7 @@ import 'package:tmail_ui_user/features/login/domain/usecases/get_authentication_ import 'package:tmail_ui_user/features/login/domain/usecases/get_oidc_configuration_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/get_stored_oidc_configuration_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/get_token_oidc_interactor.dart'; +import 'package:tmail_ui_user/features/login/domain/usecases/save_login_url_on_mobile_interactor.dart'; import 'package:tmail_ui_user/features/login/presentation/login_form_type.dart'; import 'package:tmail_ui_user/features/login/presentation/model/login_arguments.dart'; import 'package:tmail_ui_user/features/login/presentation/state/login_state.dart'; @@ -40,6 +41,7 @@ class LoginController extends BaseController { final AuthenticateOidcOnBrowserInteractor _authenticateOidcOnBrowserInteractor; final GetAuthenticationInfoInteractor _getAuthenticationInfoInteractor; final GetStoredOidcConfigurationInteractor _getStoredOidcConfigurationInteractor; + final SaveLoginUrlOnMobileInteractor _saveLoginUrlOnMobileInteractor; final TextEditingController urlInputController = TextEditingController(); @@ -56,6 +58,7 @@ class LoginController extends BaseController { this._authenticateOidcOnBrowserInteractor, this._getAuthenticationInfoInteractor, this._getStoredOidcConfigurationInteractor, + this._saveLoginUrlOnMobileInteractor, ); var loginState = LoginState(Right(LoginInitAction())).obs;