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" />
|
||||
<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());
|
||||
}
|
||||
|
||||
|
||||
@@ -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 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: '');
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -251,6 +251,8 @@ dependencies:
|
||||
|
||||
flutter_web_auth_2: 3.1.1
|
||||
|
||||
app_links: 6.3.2
|
||||
|
||||
duration: 4.0.3
|
||||
|
||||
dev_dependencies:
|
||||
|
||||
Reference in New Issue
Block a user