TF-3278 Handle open tmail app deep link but it was installed but not signed in

This commit is contained in:
dab246
2024-11-18 16:39:12 +07:00
committed by Dat H. Pham
parent bebefb4463
commit cdfd2b7ee7
9 changed files with 209 additions and 3 deletions
+11
View File
@@ -90,6 +90,17 @@
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="*/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="twakemail.mobile"
android:host="openApp" />
</intent-filter>
</activity>
<activity
@@ -29,9 +29,7 @@ abstract class ReloadableController extends BaseController {
@override
void handleFailureViewState(Failure failure) {
if (failure is GetCredentialFailure ||
failure is GetStoredTokenOidcFailure ||
failure is GetAuthenticatedAccountFailure) {
if (isNotSignedIn(failure)) {
logError('$runtimeType::handleFailureViewState():Failure = $failure');
goToLogin();
} else if (failure is GetSessionFailure) {
@@ -154,4 +152,10 @@ abstract class ReloadableController extends BaseController {
baseUrl: baseUrl
));
}
bool isNotSignedIn(Failure failure) {
return failure is GetCredentialFailure ||
failure is GetStoredTokenOidcFailure ||
failure is GetAuthenticatedAccountFailure;
}
}
@@ -1,7 +1,12 @@
import 'dart:async';
import 'package:core/presentation/state/failure.dart';
import 'package:core/utils/app_logger.dart';
import 'package:core/utils/platform_info.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_downloader/flutter_downloader.dart';
import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:model/oidc/oidc_configuration.dart';
import 'package:tmail_ui_user/features/base/reloadable/reloadable_controller.dart';
import 'package:tmail_ui_user/features/caching/config/hive_cache_config.dart';
import 'package:tmail_ui_user/features/cleanup/domain/model/cleanup_rule.dart';
@@ -13,9 +18,13 @@ import 'package:tmail_ui_user/features/cleanup/domain/usecases/cleanup_email_cac
import 'package:tmail_ui_user/features/cleanup/domain/usecases/cleanup_recent_login_url_cache_interactor.dart';
import 'package:tmail_ui_user/features/cleanup/domain/usecases/cleanup_recent_login_username_interactor.dart';
import 'package:tmail_ui_user/features/cleanup/domain/usecases/cleanup_recent_search_cache_interactor.dart';
import 'package:tmail_ui_user/features/login/data/network/config/oidc_constant.dart';
import 'package:tmail_ui_user/main/deep_links/deep_link_data.dart';
import 'package:tmail_ui_user/main/routes/app_routes.dart';
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
import 'package:tmail_ui_user/main/routes/route_utils.dart';
import 'package:tmail_ui_user/main/utils/app_config.dart';
import 'package:tmail_ui_user/main/deep_links/deep_links_manager.dart';
import 'package:tmail_ui_user/main/utils/email_receive_manager.dart';
import 'package:tmail_ui_user/main/utils/ios_notification_manager.dart';
@@ -27,6 +36,7 @@ class HomeController extends ReloadableController {
final CleanupRecentLoginUsernameCacheInteractor _cleanupRecentLoginUsernameCacheInteractor;
IOSNotificationManager? _iosNotificationManager;
DeepLinksManager? _deepLinksManager;
HomeController(
this._cleanupEmailCacheInteractor,
@@ -41,6 +51,7 @@ class HomeController extends ReloadableController {
if (PlatformInfo.isMobile) {
_initFlutterDownloader();
_registerReceivingFileSharing();
_registerDeepLinks();
}
if (PlatformInfo.isIOS) {
_registerNotificationClickOnIOS();
@@ -88,4 +99,50 @@ class HomeController extends ReloadableController {
_iosNotificationManager = getBinding<IOSNotificationManager>();
_iosNotificationManager?.listenClickNotification();
}
void _registerDeepLinks() {
_deepLinksManager = getBinding<DeepLinksManager>();
}
Future<void> _handleDeepLinks() async {
final deepLinkData = await _deepLinksManager?.getDeepLinkData();
log('HomeController::_handleDeepLinks:DeepLinkData = $deepLinkData');
if (deepLinkData == null) {
goToLogin();
return;
}
if (deepLinkData.path == AppConfig.openAppHostDeepLink) {
_handleOpenApp(deepLinkData);
return;
}
goToLogin();
}
void _handleOpenApp(DeepLinkData deepLinkData) {
if (deepLinkData.isValidToken()) {
setDataToInterceptors(
baseUrl: AppConfig.saasJmapServerUrl,
tokenOIDC: deepLinkData.getTokenOIDC(),
oidcConfiguration: OIDCConfiguration(
authority: AppConfig.saasRegistrationUrl,
clientId: OIDCConstant.clientId,
scopes: AppConfig.oidcScopes,
)
);
getSessionAction();
} else {
goToLogin();
}
}
@override
void handleFailureViewState(Failure failure) {
if (PlatformInfo.isMobile && isNotSignedIn(failure)) {
_handleDeepLinks();
} else {
super.handleFailureViewState(failure);
}
}
}
@@ -18,6 +18,7 @@ import 'package:tmail_ui_user/features/caching/utils/local_storage_manager.dart'
import 'package:tmail_ui_user/features/caching/utils/session_storage_manager.dart';
import 'package:tmail_ui_user/features/sending_queue/presentation/utils/sending_queue_isolate_manager.dart';
import 'package:tmail_ui_user/main/utils/app_config.dart';
import 'package:tmail_ui_user/main/deep_links/deep_links_manager.dart';
import 'package:tmail_ui_user/main/utils/email_receive_manager.dart';
import 'package:tmail_ui_user/main/utils/ios_notification_manager.dart';
import 'package:tmail_ui_user/main/utils/toast_manager.dart';
@@ -75,6 +76,9 @@ class CoreBindings extends Bindings {
if (PlatformInfo.isIOS) {
Get.put(IOSNotificationManager());
}
if (PlatformInfo.isMobile) {
Get.put(DeepLinksManager());
}
Get.put(PreviewEmlFileUtils());
}
+45
View File
@@ -0,0 +1,45 @@
import 'package:equatable/equatable.dart';
import 'package:model/model.dart';
class DeepLinkData with EquatableMixin {
final String path;
final String? accessToken;
final String? refreshToken;
final String? idToken;
final int? expiresIn;
final String? username;
DeepLinkData({
required this.path,
this.accessToken,
this.refreshToken,
this.idToken,
this.expiresIn,
this.username,
});
bool isValidToken() => accessToken?.isNotEmpty == true && username?.isNotEmpty == true;
TokenOIDC getTokenOIDC() {
final expiredTime = expiresIn == null
? null
: DateTime.now().add(Duration(seconds: expiresIn!));
return TokenOIDC(
accessToken!,
TokenId(idToken ?? ''),
refreshToken ?? '',
expiredTime: expiredTime,
);
}
@override
List<Object?> get props => [
path,
accessToken,
refreshToken,
idToken,
expiresIn,
username,
];
}
@@ -0,0 +1,42 @@
import 'dart:async';
import 'package:app_links/app_links.dart';
import 'package:core/utils/app_logger.dart';
import 'package:tmail_ui_user/features/login/data/network/config/oidc_constant.dart';
import 'package:tmail_ui_user/main/deep_links/deep_link_data.dart';
class DeepLinksManager {
Future<DeepLinkData?> getDeepLinkData() async {
final uriLink = await AppLinks().getInitialLink();
if (uriLink == null) return null;
final deepLinkData = parseDeepLink(uriLink.toString());
return deepLinkData;
}
DeepLinkData? parseDeepLink(String url) {
try {
final uri = Uri.parse(url.replaceFirst(OIDCConstant.twakeWorkplaceUrlScheme, 'https'));
final accessToken = uri.queryParameters['access_token'] ?? '';
final refreshToken = uri.queryParameters['refresh_token'] ?? '';
final idToken = uri.queryParameters['id_token'] ?? '';
final expiresInStr = uri.queryParameters['expires_in'] ?? '';
final username = uri.queryParameters['username'] ?? '';
final expiresIn = int.tryParse(expiresInStr);
return DeepLinkData(
path: uri.path,
accessToken: accessToken,
refreshToken: refreshToken,
idToken: idToken,
expiresIn: expiresIn,
username: username,
);
} catch (e) {
logError('DeepLinksManager::parseDeepLink:Exception = $e');
return null;
}
}
}
+1
View File
@@ -19,6 +19,7 @@ class AppConfig {
static const String linagoraPrivacyUrl = 'https://www.linagora.com/en/legal/privacy';
static const String saasRegistrationUrl = 'https://sign-up.twake.app';
static const String saasJmapServerUrl = 'https://jmap.twake.app';
static const String openAppHostDeepLink = 'openApp';
static String get baseUrl => dotenv.get('SERVER_URL', fallback: '');
static String get domainRedirectUrl => dotenv.get('DOMAIN_REDIRECT_URL', fallback: '');
+40
View File
@@ -25,6 +25,38 @@ packages:
url: "https://pub.dev"
source: hosted
version: "5.13.0"
app_links:
dependency: "direct main"
description:
name: app_links
sha256: ad1a6d598e7e39b46a34f746f9a8b011ee147e4c275d407fa457e7a62f84dd99
url: "https://pub.dev"
source: hosted
version: "6.3.2"
app_links_linux:
dependency: transitive
description:
name: app_links_linux
sha256: f5f7173a78609f3dfd4c2ff2c95bd559ab43c80a87dc6a095921d96c05688c81
url: "https://pub.dev"
source: hosted
version: "1.0.3"
app_links_platform_interface:
dependency: transitive
description:
name: app_links_platform_interface
sha256: "05f5379577c513b534a29ddea68176a4d4802c46180ee8e2e966257158772a3f"
url: "https://pub.dev"
source: hosted
version: "2.0.2"
app_links_web:
dependency: transitive
description:
name: app_links_web
sha256: af060ed76183f9e2b87510a9480e56a5352b6c249778d07bd2c95fc35632a555
url: "https://pub.dev"
source: hosted
version: "1.0.4"
app_settings:
dependency: "direct main"
description:
@@ -1158,6 +1190,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.3.1"
gtk:
dependency: transitive
description:
name: gtk
sha256: e8ce9ca4b1df106e4d72dad201d345ea1a036cc12c360f1a7d5a758f78ffa42c
url: "https://pub.dev"
source: hosted
version: "2.1.0"
hive:
dependency: "direct main"
description:
+2
View File
@@ -251,6 +251,8 @@ dependencies:
flutter_web_auth_2: 3.1.1
app_links: 6.3.2
duration: 4.0.3
dev_dependencies: