TF-1599 Handle remove notification when email mask as read or delete permanent

(cherry picked from commit 1abcc61474728cadca024d21eba43aaf4f09a078)
This commit is contained in:
dab246
2023-04-07 16:02:54 +07:00
committed by Dat Vu
parent fbaea1a951
commit 9dac861baa
10 changed files with 159 additions and 24 deletions
+1 -1
View File
@@ -242,7 +242,7 @@ abstract class BaseController extends GetxController
await AppUtils.loadFcmConfigFileToEnv(currentMapEnvData: mapEnvData);
FcmConfiguration.initialize();
FcmInteractorBindings().dependencies();
FcmMessageController.instance.initializeFromAccountId(accountId);
FcmMessageController.instance.initializeFromAccountId(accountId, session);
if (!BuildUtils.isWeb) {
LocalNotificationManager.instance.setUp();
}
@@ -5,6 +5,9 @@ import 'package:fcm/model/type_name.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/properties/properties.dart';
import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:jmap_dart_client/jmap/mail/email/keyword_identifier.dart';
import 'package:model/extensions/list_email_extension.dart';
import 'package:model/extensions/mailbox_extension.dart';
import 'package:model/mailbox/presentation_mailbox.dart';
import 'package:tmail_ui_user/features/mailbox/data/datasource/mailbox_datasource.dart';
@@ -133,4 +136,51 @@ class FCMRepositoryImpl extends FCMRepository {
return mailboxesNotPutNotifications;
}
}
@override
Future<List<EmailId>> getEmailChangesToRemoveNotification(
Session session,
AccountId accountId,
jmap.State currentState,
{
Properties? propertiesCreated,
Properties? propertiesUpdated
}
) async {
EmailChangeResponse? emailChangeResponse;
bool hasMoreChanges = true;
jmap.State? sinceState = currentState;
while (hasMoreChanges && sinceState != null) {
final changesResponse = await _threadDataSource.getChanges(
session,
accountId,
sinceState,
propertiesCreated: propertiesCreated,
propertiesUpdated: propertiesUpdated
);
hasMoreChanges = changesResponse.hasMoreChanges;
sinceState = changesResponse.newStateChanges;
if (emailChangeResponse != null) {
emailChangeResponse.union(changesResponse);
} else {
emailChangeResponse = changesResponse;
}
}
if (emailChangeResponse != null) {
final listEmailIdMarkAsRead = emailChangeResponse.updated
?.where((email) => email.keywords?.containsKey(KeyWordIdentifier.emailSeen) == true)
.toList()
.listEmailIds ?? [];
final listEmailIdDestroyed = emailChangeResponse.destroyed ?? [];
log('FCMRepositoryImpl::getEmailChangesToRemoveNotification():listEmailIdMarkAsRead: $listEmailIdMarkAsRead | listEmailIdDestroyed: $listEmailIdDestroyed');
final allListEmailIdsNeedRemove = listEmailIdMarkAsRead + listEmailIdDestroyed;
return allListEmailIdsNeedRemove;
} else {
return [];
}
}
}
@@ -16,9 +16,8 @@ class GetEmailChangesToPushNotificationSuccess extends UIState {
}
class GetEmailChangesToPushNotificationFailure extends FeatureFailure {
final dynamic exception;
GetEmailChangesToPushNotificationFailure(this.exception);
GetEmailChangesToPushNotificationFailure(exception) : super(exception: exception);
@override
List<Object> get props => [exception];
@@ -7,7 +7,7 @@ import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:jmap_dart_client/jmap/core/state.dart' as jmap;
import 'package:model/extensions/email_extension.dart';
import 'package:tmail_ui_user/features/push_notification/domain/repository/fcm_repository.dart';
import 'package:tmail_ui_user/features/push_notification/domain/state/get_email_changes_state.dart';
import 'package:tmail_ui_user/features/push_notification/domain/state/get_email_changes_to_push_notification_state.dart';
class GetEmailChangesToPushNotificationInteractor {
final FCMRepository _fcmRepository;
@@ -8,15 +8,17 @@ import 'package:jmap_dart_client/jmap/core/state.dart' as jmap;
class SynchronizeEmailOnForegroundAction extends FcmStateChangeAction {
final AccountId accountId;
final Session? session;
SynchronizeEmailOnForegroundAction(
TypeName typeName,
jmap.State newState,
this.accountId
this.accountId,
this.session
) : super(typeName, newState);
@override
List<Object?> get props => [typeName, newState, accountId];
List<Object?> get props => [typeName, newState, accountId, session];
}
class PushNotificationAction extends FcmStateChangeAction {
@@ -38,15 +40,17 @@ class PushNotificationAction extends FcmStateChangeAction {
class StoreEmailStateToRefreshAction extends FcmStateChangeAction {
final AccountId accountId;
final Session? session;
StoreEmailStateToRefreshAction(
TypeName typeName,
jmap.State newState,
this.accountId
this.accountId,
this.session
) : super(typeName, newState);
@override
List<Object?> get props => [typeName, newState, accountId];
List<Object?> get props => [typeName, newState, accountId, session];
}
class SynchronizeMailboxOnForegroundAction extends FcmStateChangeAction {
@@ -1,9 +1,21 @@
import 'package:core/data/model/source_type/data_source_type.dart';
import 'package:core/data/network/dio_client.dart';
import 'package:get/get.dart';
import 'package:tmail_ui_user/features/base/interactors_bindings.dart';
import 'package:tmail_ui_user/features/caching/state_cache_client.dart';
import 'package:tmail_ui_user/features/email/data/datasource/email_datasource.dart';
import 'package:tmail_ui_user/features/email/data/datasource/html_datasource.dart';
import 'package:tmail_ui_user/features/email/data/datasource_impl/email_datasource_impl.dart';
import 'package:tmail_ui_user/features/email/data/datasource_impl/html_datasource_impl.dart';
import 'package:tmail_ui_user/features/email/data/local/html_analyzer.dart';
import 'package:tmail_ui_user/features/email/data/network/email_api.dart';
import 'package:tmail_ui_user/features/email/data/repository/email_repository_impl.dart';
import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart';
import 'package:tmail_ui_user/features/mailbox/data/datasource/mailbox_datasource.dart';
import 'package:tmail_ui_user/features/mailbox/data/datasource/state_datasource.dart';
import 'package:tmail_ui_user/features/mailbox/data/datasource_impl/mailbox_cache_datasource_impl.dart';
import 'package:tmail_ui_user/features/mailbox/data/datasource_impl/mailbox_datasource_impl.dart';
import 'package:tmail_ui_user/features/mailbox/data/datasource_impl/state_datasource_impl.dart';
import 'package:tmail_ui_user/features/mailbox/data/local/mailbox_cache_manager.dart';
import 'package:tmail_ui_user/features/mailbox/data/network/mailbox_api.dart';
import 'package:tmail_ui_user/features/mailbox/data/network/mailbox_isolate_worker.dart';
@@ -17,6 +29,7 @@ import 'package:tmail_ui_user/features/push_notification/domain/repository/fcm_r
import 'package:tmail_ui_user/features/push_notification/domain/usecases/delete_email_state_to_refresh_interactor.dart';
import 'package:tmail_ui_user/features/push_notification/domain/usecases/destroy_subscription_interactor.dart';
import 'package:tmail_ui_user/features/push_notification/domain/usecases/get_email_changes_to_push_notification_interactor.dart';
import 'package:tmail_ui_user/features/push_notification/domain/usecases/get_email_changes_to_remove_notification_interactor.dart';
import 'package:tmail_ui_user/features/push_notification/domain/usecases/get_email_state_to_refresh_interactor.dart';
import 'package:tmail_ui_user/features/push_notification/domain/usecases/get_fcm_subscription_local_interactor.dart';
import 'package:tmail_ui_user/features/push_notification/domain/usecases/get_firebase_subscription_interactor.dart';
@@ -42,6 +55,9 @@ class FcmInteractorBindings extends InteractorsBindings {
Get.lazyPut<FCMDatasource>(() => Get.find<FcmDatasourceImpl>());
Get.lazyPut<ThreadDataSource>(() => Get.find<ThreadDataSourceImpl>());
Get.lazyPut<MailboxDataSource>(() => Get.find<MailboxDataSourceImpl>());
Get.lazyPut<EmailDataSource>(() => Get.find<EmailDataSourceImpl>());
Get.lazyPut<HtmlDataSource>(() => Get.find<HtmlDataSourceImpl>());
Get.lazyPut<StateDataSource>(() => Get.find<StateDataSourceImpl>());
}
@override
@@ -66,6 +82,16 @@ class FcmInteractorBindings extends InteractorsBindings {
Get.lazyPut(() => MailboxCacheDataSourceImpl(
Get.find<MailboxCacheManager>(),
Get.find<CacheExceptionThrower>()));
Get.lazyPut(() => EmailDataSourceImpl(
Get.find<EmailAPI>(),
Get.find<RemoteExceptionThrower>()));
Get.lazyPut(() => HtmlDataSourceImpl(
Get.find<HtmlAnalyzer>(),
Get.find<DioClient>(),
Get.find<RemoteExceptionThrower>()));
Get.lazyPut(() => StateDataSourceImpl(
Get.find<StateCacheClient>(),
Get.find<CacheExceptionThrower>()));
}
@override
@@ -73,6 +99,9 @@ class FcmInteractorBindings extends InteractorsBindings {
Get.lazyPut(() => GetStoredEmailDeliveryStateInteractor(Get.find<FCMRepositoryImpl>()));
Get.lazyPut(() => StoreEmailDeliveryStateInteractor(Get.find<FCMRepositoryImpl>()));
Get.lazyPut(() => GetEmailChangesToPushNotificationInteractor(Get.find<FCMRepositoryImpl>()));
Get.lazyPut(() => GetEmailChangesToRemoveNotificationInteractor(
Get.find<FCMRepositoryImpl>(),
Get.find<EmailRepository>()));
Get.lazyPut(() => StoreEmailStateToRefreshInteractor(Get.find<FCMRepositoryImpl>()));
Get.lazyPut(() => GetEmailStateToRefreshInteractor(Get.find<FCMRepositoryImpl>()));
Get.lazyPut(() => DeleteEmailStateToRefreshInteractor(Get.find<FCMRepositoryImpl>()));
@@ -89,6 +118,7 @@ class FcmInteractorBindings extends InteractorsBindings {
@override
void bindingsRepository() {
Get.lazyPut<FCMRepository>(() => Get.find<FCMRepositoryImpl>());
Get.lazyPut<EmailRepository>(() => Get.find<EmailRepositoryImpl>());
}
@override
@@ -104,5 +134,9 @@ class FcmInteractorBindings extends InteractorsBindings {
DataSourceType.network: Get.find<MailboxDataSource>(),
},
));
Get.lazyPut(() => EmailRepositoryImpl(
Get.find<EmailDataSource>(),
Get.find<HtmlDataSource>(),
Get.find<StateDataSource>()));
}
}
@@ -57,8 +57,9 @@ class FcmMessageController extends FcmBaseController {
static FcmMessageController get instance => _instance;
void initializeFromAccountId(AccountId accountId) {
void initializeFromAccountId(AccountId accountId, Session session) {
_currentAccountId = accountId;
_currentSession = session;
FcmTokenController.instance.initialize();
}
@@ -98,7 +99,7 @@ class FcmMessageController extends FcmBaseController {
if (_currentAccountId != null) {
final stateChange = _convertRemoteMessageToStateChange(newRemoteMessage);
final mapTypeState = stateChange.getMapTypeState(_currentAccountId!);
_mappingTypeStateToAction(mapTypeState, _currentAccountId!);
_mappingTypeStateToAction(mapTypeState, _currentAccountId!, session: _currentSession);
}
}
@@ -161,9 +162,9 @@ class FcmMessageController extends FcmBaseController {
final newState = jmap.State(mapTypeState[typeName.value]);
if (typeName == TypeName.emailType) {
if (isForeground) {
return SynchronizeEmailOnForegroundAction(typeName, newState, accountId);
return SynchronizeEmailOnForegroundAction(typeName, newState, accountId, session);
} else {
return StoreEmailStateToRefreshAction(typeName, newState, accountId);
return StoreEmailStateToRefreshAction(typeName, newState, accountId, session);
}
} else if (typeName == TypeName.emailDelivery) {
if (!isForeground) {
@@ -6,8 +6,11 @@ import 'package:core/presentation/state/success.dart';
import 'package:core/utils/app_logger.dart';
import 'package:core/utils/build_utils.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/properties/properties.dart';
import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:jmap_dart_client/jmap/core/state.dart' as jmap;
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:model/email/email_property.dart';
import 'package:model/email/presentation_email.dart';
import 'package:model/extensions/list_presentation_email_extension.dart';
import 'package:model/extensions/list_presentation_mailbox_extension.dart';
@@ -18,10 +21,12 @@ import 'package:tmail_ui_user/features/email/domain/usecases/get_stored_email_st
import 'package:tmail_ui_user/features/email/presentation/action/email_ui_action.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart';
import 'package:tmail_ui_user/features/push_notification/domain/exceptions/fcm_exception.dart';
import 'package:tmail_ui_user/features/push_notification/domain/state/get_email_changes_state.dart';
import 'package:tmail_ui_user/features/push_notification/domain/state/get_email_changes_to_push_notification_state.dart';
import 'package:tmail_ui_user/features/push_notification/domain/state/get_email_changes_to_remove_notification_state.dart';
import 'package:tmail_ui_user/features/push_notification/domain/state/get_mailboxes_not_put_notifications_state.dart';
import 'package:tmail_ui_user/features/push_notification/domain/state/get_stored_email_delivery_state.dart';
import 'package:tmail_ui_user/features/push_notification/domain/usecases/get_email_changes_to_push_notification_interactor.dart';
import 'package:tmail_ui_user/features/push_notification/domain/usecases/get_email_changes_to_remove_notification_interactor.dart';
import 'package:tmail_ui_user/features/push_notification/domain/usecases/get_mailboxes_not_put_notifications_interactor.dart';
import 'package:tmail_ui_user/features/push_notification/domain/usecases/get_stored_email_delivery_state_interactor.dart';
import 'package:tmail_ui_user/features/push_notification/domain/usecases/store_email_delivery_state_interactor.dart';
@@ -44,8 +49,9 @@ class EmailChangeListener extends ChangeListener {
GetStoredEmailStateInteractor? _getStoredEmailStateInteractor;
StoreEmailStateToRefreshInteractor? _storeEmailStateToRefreshInteractor;
GetMailboxesNotPutNotificationsInteractor? _getMailboxesNotPutNotificationsInteractor;
GetEmailChangesToRemoveNotificationInteractor? _getEmailChangesToRemoveNotificationInteractor;
jmap.State? _newState;
jmap.State? _newStateEmailDelivery;
AccountId? _accountId;
Session? _session;
List<PresentationEmail> _emailsAvailablePushNotification = [];
@@ -59,6 +65,7 @@ class EmailChangeListener extends ChangeListener {
_getEmailChangesToPushNotificationInteractor = getBinding<GetEmailChangesToPushNotificationInteractor>();
_storeEmailStateToRefreshInteractor = getBinding<StoreEmailStateToRefreshInteractor>();
_getMailboxesNotPutNotificationsInteractor = getBinding<GetMailboxesNotPutNotificationsInteractor>();
_getEmailChangesToRemoveNotificationInteractor = getBinding<GetEmailChangesToRemoveNotificationInteractor>();
} catch (e) {
logError('EmailChangeListener::_internal(): IS NOT REGISTERED: ${e.toString()}');
}
@@ -73,10 +80,16 @@ class EmailChangeListener extends ChangeListener {
log('EmailChangeListener::dispatchActions():actions: $actions');
for (var action in actions) {
if (action is SynchronizeEmailOnForegroundAction) {
if (FcmUtils.instance.isMobileAndroid) {
_handleRemoveNotificationWhenEmailMarkAsRead(action.newState, action.accountId, action.session);
}
_synchronizeEmailOnForegroundAction(action.newState);
} else if (action is PushNotificationAction) {
_pushNotificationAction(action.newState, action.accountId, action.session);
} else if (action is StoreEmailStateToRefreshAction) {
if (FcmUtils.instance.isMobileAndroid) {
_handleRemoveNotificationWhenEmailMarkAsRead(action.newState, action.accountId, action.session);
}
_handleStoreEmailStateToRefreshAction(action.newState);
}
}
@@ -90,27 +103,25 @@ class EmailChangeListener extends ChangeListener {
}
void _pushNotificationAction(jmap.State newState, AccountId accountId, Session? session) {
_newState = newState;
_newStateEmailDelivery = newState;
_accountId = accountId;
_session = session;
log('EmailChangeListener::_pushNotificationAction():newState: $newState');
if (BuildUtils.isWeb) {
_storeEmailDeliveryStateAction(_newState!);
_storeEmailDeliveryStateAction(_newStateEmailDelivery!);
} else {
if (Platform.isAndroid) {
_getStoredEmailDeliveryState();
} else if (Platform.isIOS) {
_storeEmailDeliveryStateAction(_newState!);
_showLocalNotificationForIOS(_newState!, _accountId!);
_storeEmailDeliveryStateAction(_newStateEmailDelivery!);
_showLocalNotificationForIOS(_newStateEmailDelivery!, _accountId!);
} else {
logError('EmailChangeListener::_pushNotificationAction(): NOT SUPPORTED PLATFORM');
}
}
}
void _getStoredEmailDeliveryState() {
if (_getStoredEmailDeliveryStateInteractor != null) {
consumeState(_getStoredEmailDeliveryStateInteractor!.execute());
@@ -186,14 +197,14 @@ class EmailChangeListener extends ChangeListener {
void handleSuccessViewState(Success success) {
log('EmailChangeListener::_handleSuccessViewState(): $success');
if (success is GetStoredEmailDeliveryStateSuccess) {
if (_newState != success.state) {
if (_newStateEmailDelivery != success.state) {
_getEmailChangesAction(success.state);
}
} else if (success is GetStoredEmailStateSuccess) {
_getEmailChangesAction(success.state);
} else if (success is GetEmailChangesToPushNotificationSuccess) {
if (_newState != null) {
_storeEmailDeliveryStateAction(_newState!);
if (_newStateEmailDelivery != null) {
_storeEmailDeliveryStateAction(_newStateEmailDelivery!);
if (FcmUtils.instance.isMobileAndroid) {
_handleListEmailToPushNotification(success.emailList);
@@ -203,6 +214,8 @@ class EmailChangeListener extends ChangeListener {
final listEmails = _emailsAvailablePushNotification.toEmailsAvailablePushNotification(
mailboxIdsNotPutNotifications: success.mailboxes.mailboxIds);
_handleLocalPushNotification(listEmails);
} else if (success is GetEmailChangesToRemoveNotificationSuccess) {
_handleRemoveLocalNotification(success.emailIds);
}
}
@@ -239,4 +252,22 @@ class EmailChangeListener extends ChangeListener {
logError('EmailChangeListener::_handleStoreEmailStateToRefreshAction():_storeEmailStateToRefreshInteractor is null');
}
}
void _handleRemoveNotificationWhenEmailMarkAsRead(jmap.State newState, AccountId accountId, Session? session) {
if (_getEmailChangesToRemoveNotificationInteractor != null && session != null) {
consumeState(_getEmailChangesToRemoveNotificationInteractor!.execute(
session,
accountId,
newState,
propertiesCreated: Properties({EmailProperty.id, EmailProperty.keywords}),
propertiesUpdated: Properties({EmailProperty.keywords}),
));
}
}
void _handleRemoveLocalNotification(List<EmailId> emailIds) async {
log('EmailChangeListener::_handleRemoveLocalNotification():emailIds: $emailIds');
await Future.wait(emailIds.map((emailId) => LocalNotificationManager.instance.removeNotification(emailId.id.value)));
LocalNotificationManager.instance.removeGroupPushNotification();
}
}
@@ -9,6 +9,7 @@ class LocalNotificationConfig {
static const _channelDescription = 'Team Mail notifications';
static const notificationTitle = 'Team Mail';
static const notificationMessage = 'You have new messages';
static const int groupNotificationId = 1995;
static const iosInitializationSettings = DarwinInitializationSettings();
@@ -151,6 +151,10 @@ class LocalNotificationManager {
);
}
Future<void> removeNotification(String id) {
return _localNotificationsPlugin.cancel(id.hashCode);
}
void groupPushNotification() async {
if (Platform.isIOS) {
return;
@@ -170,7 +174,7 @@ class LocalNotificationManager {
);
await _localNotificationsPlugin.show(
1995,
LocalNotificationConfig.groupNotificationId,
null,
null,
LocalNotificationConfig.instance.generateNotificationDetails(
@@ -181,6 +185,17 @@ class LocalNotificationManager {
}
}
void removeGroupPushNotification() async {
final activeNotifications = await _localNotificationsPlugin
.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>()
?.getActiveNotifications();
log('LocalNotificationManager::removeGroupPushNotification():activeNotifications: ${activeNotifications?.length}');
if (activeNotifications == null || activeNotifications.length <= 1) {
log('LocalNotificationManager::groupPushNotification():canceled');
await _localNotificationsPlugin.cancel(LocalNotificationConfig.groupNotificationId);
}
}
Future<void> recreateStreamController() {
if (localNotificationsController.isClosed) {
localNotificationsController = StreamController<NotificationResponse>.broadcast();