diff --git a/lib/features/email/domain/state/labels/add_list_label_to_list_email_state.dart b/lib/features/email/domain/state/labels/add_list_label_to_list_email_state.dart new file mode 100644 index 000000000..f3127b31b --- /dev/null +++ b/lib/features/email/domain/state/labels/add_list_label_to_list_email_state.dart @@ -0,0 +1,45 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email.dart'; +import 'package:jmap_dart_client/jmap/mail/email/keyword_identifier.dart'; + +class AddingListLabelsToListEmails extends LoadingState {} + +class AddListLabelsToListEmailsSuccess extends UIState { + final List emailIds; + final List labelKeywords; + final List labelDisplays; + + AddListLabelsToListEmailsSuccess( + this.emailIds, + this.labelKeywords, + this.labelDisplays, + ); + + @override + List get props => [emailIds, labelKeywords, labelDisplays]; +} + +class AddListLabelsToListEmailsHasSomeFailure + extends AddListLabelsToListEmailsSuccess { + AddListLabelsToListEmailsHasSomeFailure( + super.emailIds, + super.labelKeywords, + super.labelDisplays, + ); + + @override + List get props => [...super.props, 'hasSomeFailure']; +} + +class AddListLabelsToListEmailsFailure extends FeatureFailure { + final List labelDisplays; + + AddListLabelsToListEmailsFailure({ + dynamic exception, + required this.labelDisplays, + }) : super(exception: exception); + + @override + List get props => [...super.props, labelDisplays]; +} diff --git a/lib/features/email/domain/usecases/labels/add_list_label_to_list_emails_interactor.dart b/lib/features/email/domain/usecases/labels/add_list_label_to_list_emails_interactor.dart new file mode 100644 index 000000000..c70218a5e --- /dev/null +++ b/lib/features/email/domain/usecases/labels/add_list_label_to_list_emails_interactor.dart @@ -0,0 +1,67 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:dartz/dartz.dart'; +import 'package:jmap_dart_client/jmap/account_id.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:tmail_ui_user/features/composer/domain/exceptions/set_method_exception.dart'; +import 'package:tmail_ui_user/features/email/domain/exceptions/email_exceptions.dart'; +import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart'; +import 'package:tmail_ui_user/features/email/domain/state/labels/add_list_label_to_list_email_state.dart'; + +class AddListLabelToListEmailsInteractor { + final EmailRepository _emailRepository; + + AddListLabelToListEmailsInteractor(this._emailRepository); + + Stream> execute( + Session session, + AccountId accountId, + List emailIds, + List labelKeywords, + List labelDisplays, + ) async* { + try { + if (emailIds.isEmpty) { + yield Left(AddListLabelsToListEmailsFailure( + exception: EmailIdsSuccessIsEmptyException(), + labelDisplays: labelDisplays, + )); + return; + } + yield Right(AddingListLabelsToListEmails()); + final result = await _emailRepository.addListLabelToListEmail( + session, + accountId, + emailIds, + labelKeywords, + ); + if (emailIds.length == result.emailIdsSuccess.length) { + yield Right(AddListLabelsToListEmailsSuccess( + result.emailIdsSuccess, + labelKeywords, + labelDisplays, + )); + } else if (result.emailIdsSuccess.isEmpty) { + yield Left(AddListLabelsToListEmailsFailure( + exception: result.mapErrors.isNotEmpty + ? SetMethodException(result.mapErrors) + : EmailIdsSuccessIsEmptyException(), + labelDisplays: labelDisplays, + )); + } else { + yield Right(AddListLabelsToListEmailsHasSomeFailure( + result.emailIdsSuccess, + labelKeywords, + labelDisplays, + )); + } + } catch (e) { + yield Left(AddListLabelsToListEmailsFailure( + exception: e, + labelDisplays: labelDisplays, + )); + } + } +} diff --git a/lib/features/thread/presentation/extensions/email_selection_action_type_extension.dart b/lib/features/thread/presentation/extensions/email_selection_action_type_extension.dart index 222f8f785..8b7f09f4b 100644 --- a/lib/features/thread/presentation/extensions/email_selection_action_type_extension.dart +++ b/lib/features/thread/presentation/extensions/email_selection_action_type_extension.dart @@ -27,6 +27,8 @@ extension EmailSelectionActionTypeExtension on EmailSelectionActionType { case EmailSelectionActionType.selectAll: case EmailSelectionActionType.moreAction: return null; + case EmailSelectionActionType.labelAs: + return EmailActionType.labelAs; } } } diff --git a/lib/features/thread/presentation/model/email_selection_action_type.dart b/lib/features/thread/presentation/model/email_selection_action_type.dart index 3fe79014e..dd8505018 100644 --- a/lib/features/thread/presentation/model/email_selection_action_type.dart +++ b/lib/features/thread/presentation/model/email_selection_action_type.dart @@ -10,6 +10,7 @@ enum EmailSelectionActionType { markAsStarred, unMarkAsStarred, moveToFolder, + labelAs, moveToTrash, markAsSpam, markAsNotSpam, @@ -43,6 +44,8 @@ enum EmailSelectionActionType { return appLocalizations.delete_permanently; case EmailSelectionActionType.moreAction: return appLocalizations.more; + case EmailSelectionActionType.labelAs: + return appLocalizations.labelAs; } } @@ -71,6 +74,8 @@ enum EmailSelectionActionType { return imagePaths.icMailboxArchived; case EmailSelectionActionType.moreAction: return imagePaths.icMoreVertical; + case EmailSelectionActionType.labelAs: + return imagePaths.icTag; } } diff --git a/lib/l10n/intl_messages.arb b/lib/l10n/intl_messages.arb index 35c6aef7c..81179a713 100644 --- a/lib/l10n/intl_messages.arb +++ b/lib/l10n/intl_messages.arb @@ -5483,5 +5483,35 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "chooseLabel": "Choose label", + "@chooseLabel": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "selectLabel": "Select label", + "@selectLabel": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "addListLabelToListEmailSuccessfullyMessage": "Labels have been successfully added to these emails.", + "@addListLabelToListEmailSuccessfullyMessage": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "addListLabelToListEmailHasSomeFailureMessage": "Labels have been successfully added to some emails.", + "@addListLabelToListEmailHasSomeFailureMessage": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "addListLabelToListEmailFailureMessage": "Adding labels to these emails failed", + "@addListLabelToListEmailFailureMessage": { + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/lib/main/localizations/app_localizations.dart b/lib/main/localizations/app_localizations.dart index 68a2d30d3..76a3a3fb5 100644 --- a/lib/main/localizations/app_localizations.dart +++ b/lib/main/localizations/app_localizations.dart @@ -5804,4 +5804,39 @@ class AppLocalizations { name: 'allEmailTrashAndSpam', ); } + + String get chooseLabel { + return Intl.message( + 'Choose label', + name: 'chooseLabel', + ); + } + + String get selectLabel { + return Intl.message( + 'Select label', + name: 'selectLabel', + ); + } + + String get addListLabelToListEmailSuccessfullyMessage { + return Intl.message( + 'Labels have been successfully added to these emails.', + name: 'addListLabelToListEmailSuccessfullyMessage', + ); + } + + String get addListLabelToListEmailHasSomeFailureMessage { + return Intl.message( + 'Labels have been successfully added to some emails.', + name: 'addListLabelToListEmailHasSomeFailureMessage', + ); + } + + String get addListLabelToListEmailFailureMessage { + return Intl.message( + 'Adding labels to these emails failed', + name: 'addListLabelToListEmailFailureMessage', + ); + } } diff --git a/lib/main/utils/toast_manager.dart b/lib/main/utils/toast_manager.dart index 51c78779f..330e3058e 100644 --- a/lib/main/utils/toast_manager.dart +++ b/lib/main/utils/toast_manager.dart @@ -26,6 +26,7 @@ import 'package:tmail_ui_user/features/email/domain/model/move_action.dart'; import 'package:tmail_ui_user/features/email/domain/state/add_a_label_to_a_thread_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/add_a_label_to_an_email_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/calendar_event_reply_state.dart'; +import 'package:tmail_ui_user/features/email/domain/state/labels/add_list_label_to_list_email_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/labels/remove_a_label_from_a_thread_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/mark_as_email_star_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/remove_a_label_from_an_email_state.dart'; @@ -229,6 +230,9 @@ class ToastManager { ); } else if (failure is DeleteALabelFailure) { message = message ?? appLocalizations.deleteALabelFailure; + } else if (failure is AddListLabelsToListEmailsFailure) { + message = + message ?? appLocalizations.addListLabelToListEmailFailureMessage; } log('ToastManager::showMessageFailure: Message: $message'); if (message?.trim().isNotEmpty == true) { @@ -320,6 +324,10 @@ class ToastManager { message = appLocalizations.deleteLabelSuccessfullyMessage( success.deletedLabel.safeDisplayName, ); + } else if (success is AddListLabelsToListEmailsHasSomeFailure) { + message = appLocalizations.addListLabelToListEmailHasSomeFailureMessage; + } else if (success is AddListLabelsToListEmailsSuccess) { + message = appLocalizations.addListLabelToListEmailSuccessfullyMessage; } log('ToastManager::showMessageSuccess: Message: $message'); if (message?.trim().isNotEmpty == true) { diff --git a/model/lib/extensions/list_email_id_extension.dart b/model/lib/extensions/list_email_id_extension.dart index 962049188..87953e3cc 100644 --- a/model/lib/extensions/list_email_id_extension.dart +++ b/model/lib/extensions/list_email_id_extension.dart @@ -62,4 +62,17 @@ extension ListEmailIdExtension on List { emailId.id: labelKeyword.generateLabelActionPath(remove: remove) }; } + + Map generateMapUpdateObjectListLabel( + List labelKeywords, { + bool remove = false, + }) { + return { + for (var emailId in this) + emailId.id: PatchObject({ + for (var key in labelKeywords) + key.generatePath(): remove ? null : true, + }), + }; + } } \ No newline at end of file