TF-3278 Handle open tmail app deep link but it was installed but not signed in
This commit is contained in:
@@ -90,6 +90,17 @@
|
|||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
<data android:mimeType="*/*" />
|
<data android:mimeType="*/*" />
|
||||||
</intent-filter>
|
</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>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
|
|||||||
@@ -29,9 +29,7 @@ abstract class ReloadableController extends BaseController {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void handleFailureViewState(Failure failure) {
|
void handleFailureViewState(Failure failure) {
|
||||||
if (failure is GetCredentialFailure ||
|
if (isNotSignedIn(failure)) {
|
||||||
failure is GetStoredTokenOidcFailure ||
|
|
||||||
failure is GetAuthenticatedAccountFailure) {
|
|
||||||
logError('$runtimeType::handleFailureViewState():Failure = $failure');
|
logError('$runtimeType::handleFailureViewState():Failure = $failure');
|
||||||
goToLogin();
|
goToLogin();
|
||||||
} else if (failure is GetSessionFailure) {
|
} else if (failure is GetSessionFailure) {
|
||||||
@@ -154,4 +152,10 @@ abstract class ReloadableController extends BaseController {
|
|||||||
baseUrl: baseUrl
|
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:core/utils/platform_info.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter_downloader/flutter_downloader.dart';
|
import 'package:flutter_downloader/flutter_downloader.dart';
|
||||||
import 'package:jmap_dart_client/jmap/core/session/session.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/base/reloadable/reloadable_controller.dart';
|
||||||
import 'package:tmail_ui_user/features/caching/config/hive_cache_config.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';
|
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_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_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/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/app_routes.dart';
|
||||||
import 'package:tmail_ui_user/main/routes/route_navigation.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/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/email_receive_manager.dart';
|
||||||
import 'package:tmail_ui_user/main/utils/ios_notification_manager.dart';
|
import 'package:tmail_ui_user/main/utils/ios_notification_manager.dart';
|
||||||
|
|
||||||
@@ -27,6 +36,7 @@ class HomeController extends ReloadableController {
|
|||||||
final CleanupRecentLoginUsernameCacheInteractor _cleanupRecentLoginUsernameCacheInteractor;
|
final CleanupRecentLoginUsernameCacheInteractor _cleanupRecentLoginUsernameCacheInteractor;
|
||||||
|
|
||||||
IOSNotificationManager? _iosNotificationManager;
|
IOSNotificationManager? _iosNotificationManager;
|
||||||
|
DeepLinksManager? _deepLinksManager;
|
||||||
|
|
||||||
HomeController(
|
HomeController(
|
||||||
this._cleanupEmailCacheInteractor,
|
this._cleanupEmailCacheInteractor,
|
||||||
@@ -41,6 +51,7 @@ class HomeController extends ReloadableController {
|
|||||||
if (PlatformInfo.isMobile) {
|
if (PlatformInfo.isMobile) {
|
||||||
_initFlutterDownloader();
|
_initFlutterDownloader();
|
||||||
_registerReceivingFileSharing();
|
_registerReceivingFileSharing();
|
||||||
|
_registerDeepLinks();
|
||||||
}
|
}
|
||||||
if (PlatformInfo.isIOS) {
|
if (PlatformInfo.isIOS) {
|
||||||
_registerNotificationClickOnIOS();
|
_registerNotificationClickOnIOS();
|
||||||
@@ -88,4 +99,50 @@ class HomeController extends ReloadableController {
|
|||||||
_iosNotificationManager = getBinding<IOSNotificationManager>();
|
_iosNotificationManager = getBinding<IOSNotificationManager>();
|
||||||
_iosNotificationManager?.listenClickNotification();
|
_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/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/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/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/email_receive_manager.dart';
|
||||||
import 'package:tmail_ui_user/main/utils/ios_notification_manager.dart';
|
import 'package:tmail_ui_user/main/utils/ios_notification_manager.dart';
|
||||||
import 'package:tmail_ui_user/main/utils/toast_manager.dart';
|
import 'package:tmail_ui_user/main/utils/toast_manager.dart';
|
||||||
@@ -75,6 +76,9 @@ class CoreBindings extends Bindings {
|
|||||||
if (PlatformInfo.isIOS) {
|
if (PlatformInfo.isIOS) {
|
||||||
Get.put(IOSNotificationManager());
|
Get.put(IOSNotificationManager());
|
||||||
}
|
}
|
||||||
|
if (PlatformInfo.isMobile) {
|
||||||
|
Get.put(DeepLinksManager());
|
||||||
|
}
|
||||||
Get.put(PreviewEmlFileUtils());
|
Get.put(PreviewEmlFileUtils());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@ class AppConfig {
|
|||||||
static const String linagoraPrivacyUrl = 'https://www.linagora.com/en/legal/privacy';
|
static const String linagoraPrivacyUrl = 'https://www.linagora.com/en/legal/privacy';
|
||||||
static const String saasRegistrationUrl = 'https://sign-up.twake.app';
|
static const String saasRegistrationUrl = 'https://sign-up.twake.app';
|
||||||
static const String saasJmapServerUrl = 'https://jmap.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 baseUrl => dotenv.get('SERVER_URL', fallback: '');
|
||||||
static String get domainRedirectUrl => dotenv.get('DOMAIN_REDIRECT_URL', fallback: '');
|
static String get domainRedirectUrl => dotenv.get('DOMAIN_REDIRECT_URL', fallback: '');
|
||||||
|
|||||||
@@ -25,6 +25,38 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.13.0"
|
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:
|
app_settings:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -1158,6 +1190,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.1"
|
version: "2.3.1"
|
||||||
|
gtk:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: gtk
|
||||||
|
sha256: e8ce9ca4b1df106e4d72dad201d345ea1a036cc12c360f1a7d5a758f78ffa42c
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.0"
|
||||||
hive:
|
hive:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -251,6 +251,8 @@ dependencies:
|
|||||||
|
|
||||||
flutter_web_auth_2: 3.1.1
|
flutter_web_auth_2: 3.1.1
|
||||||
|
|
||||||
|
app_links: 6.3.2
|
||||||
|
|
||||||
duration: 4.0.3
|
duration: 4.0.3
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
|
|||||||
Reference in New Issue
Block a user