From e4107c73637c02cc6f24a8267609256bc44f0ba6 Mon Sep 17 00:00:00 2001 From: dab246 Date: Wed, 25 Mar 2026 15:43:33 +0700 Subject: [PATCH] TF-4385 Fix spam banner show it once per day --- .../presentation/mailbox_controller.dart | 2 +- .../datasource/spam_report_datasource.dart | 2 +- .../hive_spam_report_datasource_impl.dart | 2 +- .../local_spam_report_datasource_impl.dart | 6 ++-- .../spam_report_repository_impl.dart | 4 +-- .../exceptions/spam_report_exception.dart | 13 +++++-- .../repository/spam_report_repository.dart | 2 +- .../state/get_spam_mailbox_cached_state.dart | 2 -- .../get_spam_mailbox_cached_interactor.dart | 34 +++++++++++++------ .../mailbox_dashboard_controller.dart | 18 ---------- .../controller/spam_report_controller.dart | 13 ++++++- 11 files changed, 54 insertions(+), 44 deletions(-) diff --git a/lib/features/mailbox/presentation/mailbox_controller.dart b/lib/features/mailbox/presentation/mailbox_controller.dart index f6a834455..d9e936af4 100644 --- a/lib/features/mailbox/presentation/mailbox_controller.dart +++ b/lib/features/mailbox/presentation/mailbox_controller.dart @@ -284,7 +284,7 @@ class MailboxController extends BaseMailboxController mailboxDashBoardController.updateRefreshAllMailboxState(Right(RefreshAllMailboxSuccess())); _handleCreateDefaultFolderIfMissing(mailboxDashBoardController.mapDefaultMailboxIdByRole); _handleDataFromNavigationRouter(); - mailboxDashBoardController.getSpamReportBanner(); + mailboxDashBoardController.refreshSpamReportBanner(); if (PlatformInfo.isIOS) { _updateMailboxIdsBlockNotificationToKeychain(success.mailboxList); } diff --git a/lib/features/mailbox_dashboard/data/datasource/spam_report_datasource.dart b/lib/features/mailbox_dashboard/data/datasource/spam_report_datasource.dart index bb443a74a..210564ac6 100644 --- a/lib/features/mailbox_dashboard/data/datasource/spam_report_datasource.dart +++ b/lib/features/mailbox_dashboard/data/datasource/spam_report_datasource.dart @@ -10,7 +10,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/domain/model/unread_spa abstract class SpamReportDataSource { Future storeLastTimeDismissedSpamReported(DateTime lastTimeDismissedSpamReported); - Future getLastTimeDismissedSpamReported(); + Future getLastTimeDismissedSpamReportedMilliseconds(); Future deleteLastTimeDismissedSpamReported(); diff --git a/lib/features/mailbox_dashboard/data/datasource_impl/hive_spam_report_datasource_impl.dart b/lib/features/mailbox_dashboard/data/datasource_impl/hive_spam_report_datasource_impl.dart index 1a074d6f3..502d822b8 100644 --- a/lib/features/mailbox_dashboard/data/datasource_impl/hive_spam_report_datasource_impl.dart +++ b/lib/features/mailbox_dashboard/data/datasource_impl/hive_spam_report_datasource_impl.dart @@ -28,7 +28,7 @@ class HiveSpamReportDataSourceImpl extends SpamReportDataSource { } @override - Future getLastTimeDismissedSpamReported() { + Future getLastTimeDismissedSpamReportedMilliseconds() { throw UnimplementedError(); } diff --git a/lib/features/mailbox_dashboard/data/datasource_impl/local_spam_report_datasource_impl.dart b/lib/features/mailbox_dashboard/data/datasource_impl/local_spam_report_datasource_impl.dart index d10c76951..c522c7e7d 100644 --- a/lib/features/mailbox_dashboard/data/datasource_impl/local_spam_report_datasource_impl.dart +++ b/lib/features/mailbox_dashboard/data/datasource_impl/local_spam_report_datasource_impl.dart @@ -21,12 +21,10 @@ class LocalSpamReportDataSourceImpl extends SpamReportDataSource { ); @override - Future getLastTimeDismissedSpamReported() async { + Future getLastTimeDismissedSpamReportedMilliseconds() async { return Future.sync(() async { final spamReportConfig = await _preferencesSettingManager.getSpamReportConfig(); - return DateTime.fromMillisecondsSinceEpoch( - spamReportConfig.lastTimeDismissedMilliseconds, - ); + return spamReportConfig.lastTimeDismissedMilliseconds; }).catchError(_exceptionThrower.throwException); } diff --git a/lib/features/mailbox_dashboard/data/repository/spam_report_repository_impl.dart b/lib/features/mailbox_dashboard/data/repository/spam_report_repository_impl.dart index aad9696f0..1eb6be839 100644 --- a/lib/features/mailbox_dashboard/data/repository/spam_report_repository_impl.dart +++ b/lib/features/mailbox_dashboard/data/repository/spam_report_repository_impl.dart @@ -12,8 +12,8 @@ class SpamReportRepositoryImpl extends SpamReportRepository { SpamReportRepositoryImpl(this.mapDataSource); @override - Future getLastTimeDismissedSpamReported() async { - return await mapDataSource[DataSourceType.local]!.getLastTimeDismissedSpamReported(); + Future getLastTimeDismissedSpamReportedMilliseconds() async { + return await mapDataSource[DataSourceType.local]!.getLastTimeDismissedSpamReportedMilliseconds(); } @override diff --git a/lib/features/mailbox_dashboard/domain/exceptions/spam_report_exception.dart b/lib/features/mailbox_dashboard/domain/exceptions/spam_report_exception.dart index a361d9bc4..e33458352 100644 --- a/lib/features/mailbox_dashboard/domain/exceptions/spam_report_exception.dart +++ b/lib/features/mailbox_dashboard/domain/exceptions/spam_report_exception.dart @@ -1,10 +1,10 @@ import 'package:core/domain/exceptions/app_base_exception.dart'; -class NotFoundLastTimeDismissedSpamReportException extends AppBaseException { - NotFoundLastTimeDismissedSpamReportException([super.message]); +class SpamDismissCooldownActiveException extends AppBaseException { + SpamDismissCooldownActiveException([super.message]); @override - String get exceptionName => 'NotFoundLastTimeDismissedSpamReportException'; + String get exceptionName => 'SpamDismissCooldownActiveException'; } class NotFoundSpamMailboxCachedException extends AppBaseException { @@ -20,3 +20,10 @@ class NotFoundSpamMailboxException extends AppBaseException { @override String get exceptionName => 'NotFoundSpamMailboxException'; } + +class NoUnreadSpamEmailsException extends AppBaseException { + NoUnreadSpamEmailsException([super.message]); + + @override + String get exceptionName => 'NoUnreadSpamEmailsException'; +} diff --git a/lib/features/mailbox_dashboard/domain/repository/spam_report_repository.dart b/lib/features/mailbox_dashboard/domain/repository/spam_report_repository.dart index 39e37628c..61064156a 100644 --- a/lib/features/mailbox_dashboard/domain/repository/spam_report_repository.dart +++ b/lib/features/mailbox_dashboard/domain/repository/spam_report_repository.dart @@ -6,7 +6,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/domain/model/spam_repor abstract class SpamReportRepository { Future storeLastTimeDismissedSpamReported(DateTime lastTimeDismissedSpamReported); - Future getLastTimeDismissedSpamReported(); + Future getLastTimeDismissedSpamReportedMilliseconds(); Future deleteLastTimeDismissedSpamReported(); diff --git a/lib/features/mailbox_dashboard/domain/state/get_spam_mailbox_cached_state.dart b/lib/features/mailbox_dashboard/domain/state/get_spam_mailbox_cached_state.dart index f256506a1..99dd8c318 100644 --- a/lib/features/mailbox_dashboard/domain/state/get_spam_mailbox_cached_state.dart +++ b/lib/features/mailbox_dashboard/domain/state/get_spam_mailbox_cached_state.dart @@ -18,5 +18,3 @@ class GetSpamMailboxCachedFailure extends FeatureFailure { GetSpamMailboxCachedFailure(exception) : super(exception: exception); } - -class InvalidSpamReportCondition extends FeatureFailure {} \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/domain/usecases/get_spam_mailbox_cached_interactor.dart b/lib/features/mailbox_dashboard/domain/usecases/get_spam_mailbox_cached_interactor.dart index 89fb99857..a673d2e7c 100644 --- a/lib/features/mailbox_dashboard/domain/usecases/get_spam_mailbox_cached_interactor.dart +++ b/lib/features/mailbox_dashboard/domain/usecases/get_spam_mailbox_cached_interactor.dart @@ -1,15 +1,15 @@ import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; -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:tmail_ui_user/features/mailbox_dashboard/domain/exceptions/spam_report_exception.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/spam_report_repository.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/get_spam_mailbox_cached_state.dart'; class GetSpamMailboxCachedInteractor { - static const int spamReportBannerDisplayIntervalInHour = 12; + static const int spamReportBannerDisplayIntervalInHours = 24; final SpamReportRepository _spamReportRepository; @@ -19,15 +19,19 @@ class GetSpamMailboxCachedInteractor { try { yield Right(GetSpamMailboxCachedLoading()); if (await _validateIntervalToShowBanner()) { - final spamMailbox = await _spamReportRepository.getSpamMailboxCached(accountId, userName); + final spamMailbox = await _spamReportRepository.getSpamMailboxCached(accountId, userName); final countUnreadSpamMailbox = spamMailbox.unreadEmails?.value.value.toInt() ?? 0; if (countUnreadSpamMailbox > 0) { yield Right(GetSpamMailboxCachedSuccess(spamMailbox)); } else { - yield Left(InvalidSpamReportCondition()); + yield Left( + GetSpamMailboxCachedFailure(NoUnreadSpamEmailsException()), + ); } } else { - yield Left(InvalidSpamReportCondition()); + yield Left( + GetSpamMailboxCachedFailure(SpamDismissCooldownActiveException()), + ); } } catch (e) { yield Left(GetSpamMailboxCachedFailure(e)); @@ -35,9 +39,19 @@ class GetSpamMailboxCachedInteractor { } Future _validateIntervalToShowBanner() async { - final lastTimeDismissedSpamReported = await _spamReportRepository.getLastTimeDismissedSpamReported(); - final currentTime = DateTime.now().difference(lastTimeDismissedSpamReported); - log('GetSpamMailboxCachedInteractor::_compareSpamReportTime:lastTimeDismissedSpamReported: $lastTimeDismissedSpamReported | currentTime: $currentTime'); - return currentTime.inHours > spamReportBannerDisplayIntervalInHour; + final lastTimeDismissedMs = await _spamReportRepository + .getLastTimeDismissedSpamReportedMilliseconds(); + + if (lastTimeDismissedMs <= 0) { + return true; + } + + final lastTime = DateTime.fromMillisecondsSinceEpoch(lastTimeDismissedMs); + final now = DateTime.now(); + final elapsed = now.difference(lastTime); + final isIntervalElapsed = + elapsed.inHours > spamReportBannerDisplayIntervalInHours; + + return isIntervalElapsed; } -} \ No newline at end of file +} diff --git a/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart b/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart index dfafb9d84..c544b6918 100644 --- a/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart @@ -2534,24 +2534,6 @@ class MailboxDashBoardController extends ReloadableController bool get enableSpamReport => spamReportController.enableSpamReport; - void getSpamReportBanner() { - if (enableSpamReport) { - final spamId = spamMailboxId; - if (spamId == null) { - spamReportController.setSpamPresentationMailbox(null); - return; - } - - final spamMailbox = mapMailboxById[spamId]; - final unreadEmails = spamMailbox?.unreadEmails?.value.value ?? 0; - if (unreadEmails > 0) { - spamReportController.setSpamPresentationMailbox(spamMailbox); - } else { - spamReportController.setSpamPresentationMailbox(null); - } - } - } - void refreshSpamReportBanner() { if (enableSpamReport && sessionCurrent != null && accountId.value != null) { spamReportController.getSpamMailboxCached(accountId.value!, sessionCurrent!.username); diff --git a/lib/features/mailbox_dashboard/presentation/controller/spam_report_controller.dart b/lib/features/mailbox_dashboard/presentation/controller/spam_report_controller.dart index de635206d..cb2705d4a 100644 --- a/lib/features/mailbox_dashboard/presentation/controller/spam_report_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/controller/spam_report_controller.dart @@ -9,6 +9,7 @@ import 'package:model/extensions/presentation_mailbox_extension.dart'; import 'package:model/mailbox/presentation_mailbox.dart'; import 'package:tmail_ui_user/features/base/base_controller.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/extensions/presentation_mailbox_extension.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/exceptions/spam_report_exception.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/model/spam_report_state.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/get_spam_mailbox_cached_state.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/get_spam_report_state.dart'; @@ -75,7 +76,7 @@ class SpamReportController extends BaseController { @override void handleFailureViewState(Failure failure) { if (failure is GetSpamMailboxCachedFailure) { - presentationSpamMailbox.value = null; + _validateSpamMailboxChanged(failure); } else if (failure is GetSpamReportStateFailure) { _spamReportLoaderStatus = LoaderStatus.completed; } else { @@ -147,6 +148,16 @@ class SpamReportController extends BaseController { presentationSpamMailbox.value = spamMailbox; } + void _validateSpamMailboxChanged(GetSpamMailboxCachedFailure failure) { + if (failure.exception is NoUnreadSpamEmailsException) { + final currentSpamMailbox = presentationSpamMailbox.value; + if (currentSpamMailbox != null && currentSpamMailbox.countUnreadEmails > 0) { + _storeLastTimeDismissedSpamReportedAction(); + } + } + setSpamPresentationMailbox(null); + } + @override void onClose() { _appLifecycleListener?.dispose();