TF-2871 Handle click notification to open detailed email on iOS

This commit is contained in:
dab246
2024-07-11 09:46:23 +07:00
committed by Dat H. Pham
parent 9ffd20f1a7
commit 501d4d38d9
32 changed files with 416 additions and 354 deletions
@@ -0,0 +1,79 @@
import 'dart:async';
import 'package:core/utils/app_logger.dart';
import 'package:flutter/services.dart';
import 'package:jmap_dart_client/jmap/core/id.dart';
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:model/model.dart';
import 'package:rxdart/rxdart.dart';
class IOSNotificationManager {
static const _notificationInteractionChannel = MethodChannel('notification_interaction_channel');
static const String CURRENT_EMAIL_ID_IN_NOTIFICATION_CLICK_WHEN_APP_FOREGROUND_OR_BACKGROUND = 'current_email_id_in_notification_click_when_app_foreground_or_background';
static const String CURRENT_EMAIL_ID_IN_NOTIFICATION_CLICK_WHEN_APP_TERMINATED = 'current_email_id_in_notification_click_when_app_terminated';
BehaviorSubject<EmailId?> _pendingCurrentEmailIdInNotification = BehaviorSubject.seeded(null);
BehaviorSubject<EmailId?> get pendingCurrentEmailIdInNotification => _pendingCurrentEmailIdInNotification;
StreamSubscription<EmailId?>? _getCurrentEmailIdStreamSubscription;
void listenClickNotification() {
try {
_notificationInteractionChannel.setMethodCallHandler((methodCall) async {
log('IOSNotificationManager::listenClickNotification: $methodCall');
if (methodCall.method == CURRENT_EMAIL_ID_IN_NOTIFICATION_CLICK_WHEN_APP_FOREGROUND_OR_BACKGROUND
&& methodCall.arguments != null) {
final emailId = EmailId(Id(methodCall.arguments));
setPendingCurrentEmailId(emailId);
}
});
_getCurrentEmailIdStreamSubscription = Stream.fromFuture(_getCurrentEmailIdInNotificationClick()).listen((emailId) {
log('IOSNotificationManager::listenClickNotification:_getCurrentEmailIdInNotificationClick:EmailId = ${emailId?.asString}');
if (emailId != null) {
setPendingCurrentEmailId(emailId);
}
});
} catch (e) {
logError('IOSNotificationManager::listenClickNotification:Exception = $e');
}
}
Future<EmailId?> _getCurrentEmailIdInNotificationClick() async {
try {
log('IOSNotificationManager::_getCurrentEmailIdInNotificationClick: START');
final emailId = await _notificationInteractionChannel.invokeMethod<String?>(CURRENT_EMAIL_ID_IN_NOTIFICATION_CLICK_WHEN_APP_TERMINATED);
log('IOSNotificationManager::_getCurrentEmailIdInNotificationClick: END');
if (emailId?.isNotEmpty == true) {
return EmailId(Id(emailId!));
} else {
return null;
}
} catch (e) {
logError('IOSNotificationManager::getCurrentEmailIdInNotificationClick:Exception = $e');
return null;
}
}
void setPendingCurrentEmailId(EmailId emailId) async {
clearPendingCurrentEmailId();
_pendingCurrentEmailIdInNotification.add(emailId);
}
void clearPendingCurrentEmailId() {
if(_pendingCurrentEmailIdInNotification.isClosed) {
_pendingCurrentEmailIdInNotification = BehaviorSubject.seeded(null);
} else {
_pendingCurrentEmailIdInNotification.add(null);
}
}
void dispose() {
_pendingCurrentEmailIdInNotification.close();
_getCurrentEmailIdStreamSubscription?.cancel();
_getCurrentEmailIdStreamSubscription = null;
}
}
+19 -22
View File
@@ -2,9 +2,9 @@
import 'dart:convert';
import 'package:core/utils/app_logger.dart';
import 'package:dartz/dartz.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/user_name.dart';
import 'package:model/account/authentication_type.dart';
import 'package:model/account/personal_account.dart';
import 'package:model/oidc/token_oidc.dart';
import 'package:tmail_ui_user/features/login/data/local/authentication_info_cache_manager.dart';
@@ -50,20 +50,22 @@ class IOSSharingManager {
}
}
Future saveKeyChainSharingSession(PersonalAccount personalAccount) async {
Future<void> saveKeyChainSharingSession(PersonalAccount personalAccount) async {
log('IOSSharingManager::saveKeyChainSharingSession: START');
try {
if (!_validateToSaveKeychain(personalAccount)) {
logError('IOSSharingManager::saveKeyChainSharingSession: account is null');
logError('IOSSharingManager::saveKeyChainSharingSession: AccountId | Username | ApiUrl is NULL');
return Future.value(null);
}
Tuple2<TokenOIDC?, String?> authenticationInfo = await Future.wait(
[
_getTokenOidc(tokeHashId: personalAccount.id),
_getCredentialAuthentication()
],
eagerError: true
).then((listValue) => Tuple2(listValue[0] as TokenOIDC?, listValue[1] as String?));
TokenOIDC? tokenOIDC;
String? credentialInfo;
if (personalAccount.authenticationType == AuthenticationType.oidc) {
tokenOIDC = await _getTokenOidc(tokeHashId: personalAccount.id);
} else {
credentialInfo = await _getCredentialAuthentication();
}
final emailDeliveryState = await _getEmailDeliveryState(
accountId: personalAccount.accountId!,
@@ -84,13 +86,15 @@ class IOSSharingManager {
apiUrl: personalAccount.apiUrl!,
emailState: emailState,
emailDeliveryState: emailDeliveryState,
tokenOIDC: authenticationInfo.value1,
basicAuth: authenticationInfo.value2,
tokenOIDC: tokenOIDC,
basicAuth: credentialInfo,
tokenEndpoint: tokenRecords?.tokenEndpoint,
oidcScopes: tokenRecords?.scopes,
);
log('IOSSharingManager::_saveKeyChainSharingSession: $keychainSharingSession');
await _keychainSharingManager.save(keychainSharingSession);
log('IOSSharingManager::_saveKeyChainSharingSession: COMPLETED');
} catch (e) {
logError('IOSSharingManager::_saveKeyChainSharingSession: Exception: $e');
}
@@ -111,9 +115,7 @@ class IOSSharingManager {
Future<TokenOIDC?> _getTokenOidc({required String tokeHashId}) async {
try {
final tokenOidc = await _tokenOidcCacheManager.getTokenOidc(tokeHashId);
log('IOSSharingManager::_getTokenOidc:tokenOidc: $tokenOidc');
return tokenOidc;
return await _tokenOidcCacheManager.getTokenOidc(tokeHashId);
} catch (e) {
logError('IOSSharingManager::_getTokenOidc:Exception: $e');
return null;
@@ -123,7 +125,6 @@ class IOSSharingManager {
Future<String?> _getCredentialAuthentication() async {
try {
final credentialInfo = await _authenticationInfoCacheManager.getAuthenticationInfoStored();
log('IOSSharingManager::_getCredentialAuthentication:credentialInfo: $credentialInfo');
return base64Encode(utf8.encode('${credentialInfo.username}:${credentialInfo.password}'));
} catch (e) {
logError('IOSSharingManager::_getCredentialAuthentication:Exception: $e');
@@ -136,9 +137,7 @@ class IOSSharingManager {
required UserName userName
}) async {
try {
final emailDeliveryState = await getEmailDeliveryStateFromKeychain(accountId);
log('IOSSharingManager::_getEmailState:emailDeliveryState: $emailDeliveryState');
return emailDeliveryState;
return await getEmailDeliveryStateFromKeychain(accountId);
} catch (e) {
logError('IOSSharingManager::_getEmailDeliveryState:Exception: $e');
return null;
@@ -167,7 +166,6 @@ class IOSSharingManager {
try {
final oidcConfig = await _oidcConfigurationCacheManager.getOidcConfiguration();
final oidcDiscoveryResponse = await _oidcHttpClient.discoverOIDC(oidcConfig);
log('IOSSharingManager::_getTokenEndpointAndScopes:oidcDiscoveryResponse = $oidcDiscoveryResponse | oidcConfig = $oidcConfig');
return (
tokenEndpoint: oidcDiscoveryResponse.tokenEndpoint,
scopes: oidcConfig.scopes
@@ -180,7 +178,6 @@ class IOSSharingManager {
Future updateEmailStateInKeyChain(AccountId accountId, String newEmailState) async {
final keychainSharingStored = await getKeychainSharingSession(accountId);
log('IOSSharingManager::updateEmailStateInKeyChain:keychainSharingStored: $keychainSharingStored | newEmailState: $newEmailState');
if (keychainSharingStored == null) {
return;
}