TF-3515 Add save email as template feature
This commit is contained in:
@@ -49,6 +49,7 @@ class ComposerRepositoryImpl extends ComposerRepository {
|
|||||||
{
|
{
|
||||||
bool withIdentityHeader = false,
|
bool withIdentityHeader = false,
|
||||||
bool isDraft = false,
|
bool isDraft = false,
|
||||||
|
bool isTemplate = false,
|
||||||
}
|
}
|
||||||
) async {
|
) async {
|
||||||
String emailContent = createEmailRequest.emailContent;
|
String emailContent = createEmailRequest.emailContent;
|
||||||
@@ -75,6 +76,7 @@ class ComposerRepositoryImpl extends ComposerRepository {
|
|||||||
partId: emailBodyPartId,
|
partId: emailBodyPartId,
|
||||||
withIdentityHeader: withIdentityHeader,
|
withIdentityHeader: withIdentityHeader,
|
||||||
isDraft: isDraft,
|
isDraft: isDraft,
|
||||||
|
isTemplate: isTemplate,
|
||||||
);
|
);
|
||||||
|
|
||||||
return emailObject;
|
return emailObject;
|
||||||
|
|||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/core/error/error_type.dart';
|
||||||
|
import 'package:tmail_ui_user/features/composer/domain/exceptions/set_method_exception.dart';
|
||||||
|
|
||||||
|
extension SetMethodExceptionDescriptionExtension on SetMethodException {
|
||||||
|
String? getDescriptionFromErrorType(ErrorType errorType) {
|
||||||
|
return mapErrors.values
|
||||||
|
.firstWhereOrNull((setError) => setError.type == errorType)
|
||||||
|
?.description;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ abstract class ComposerRepository {
|
|||||||
{
|
{
|
||||||
bool withIdentityHeader = false,
|
bool withIdentityHeader = false,
|
||||||
bool isDraft = false,
|
bool isDraft = false,
|
||||||
|
bool isTemplate = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
Future<UploadAttachment> uploadAttachment(FileInfo fileInfo, Uri uploadUri, {CancelToken? cancelToken});
|
Future<UploadAttachment> uploadAttachment(FileInfo fileInfo, Uri uploadUri, {CancelToken? cancelToken});
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ class SaveComposerCacheOnWebInteractor {
|
|||||||
draftHash: createEmailRequest.savedDraftHash,
|
draftHash: createEmailRequest.savedDraftHash,
|
||||||
actionType: createEmailRequest.savedActionType,
|
actionType: createEmailRequest.savedActionType,
|
||||||
draftEmailId: createEmailRequest.draftsEmailId,
|
draftEmailId: createEmailRequest.draftsEmailId,
|
||||||
|
templateEmailId: createEmailRequest.templateEmailId,
|
||||||
));
|
));
|
||||||
return Right(SaveComposerCacheSuccess());
|
return Right(SaveComposerCacheSuccess());
|
||||||
} catch (exception) {
|
} catch (exception) {
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import 'package:tmail_ui_user/features/email/data/repository/email_repository_im
|
|||||||
import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart';
|
import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart';
|
||||||
import 'package:tmail_ui_user/features/email/domain/usecases/get_email_content_interactor.dart';
|
import 'package:tmail_ui_user/features/email/domain/usecases/get_email_content_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/email/domain/usecases/print_email_interactor.dart';
|
import 'package:tmail_ui_user/features/email/domain/usecases/print_email_interactor.dart';
|
||||||
|
import 'package:tmail_ui_user/features/email/domain/usecases/save_template_email_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/email/domain/usecases/transform_html_email_content_interactor.dart';
|
import 'package:tmail_ui_user/features/email/domain/usecases/transform_html_email_content_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart';
|
import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox/data/datasource/mailbox_datasource.dart';
|
import 'package:tmail_ui_user/features/mailbox/data/datasource/mailbox_datasource.dart';
|
||||||
@@ -303,6 +304,11 @@ class ComposerBindings extends BaseBindings {
|
|||||||
|
|
||||||
IdentityInteractorsBindings(composerId: composerId).dependencies();
|
IdentityInteractorsBindings(composerId: composerId).dependencies();
|
||||||
PreferencesInteractorsBindings(composerId: composerId).dependencies();
|
PreferencesInteractorsBindings(composerId: composerId).dependencies();
|
||||||
|
|
||||||
|
Get.lazyPut(() => SaveTemplateEmailInteractor(
|
||||||
|
Get.find<ComposerRepository>(tag: composerId),
|
||||||
|
Get.find<EmailRepository>(tag: composerId),
|
||||||
|
), tag: composerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -331,6 +337,7 @@ class ComposerBindings extends BaseBindings {
|
|||||||
Get.find<CreateNewAndSaveEmailToDraftsInteractor>(tag: composerId),
|
Get.find<CreateNewAndSaveEmailToDraftsInteractor>(tag: composerId),
|
||||||
Get.find<PrintEmailInteractor>(tag: composerId),
|
Get.find<PrintEmailInteractor>(tag: composerId),
|
||||||
Get.find<ComposerRepository>(tag: composerId),
|
Get.find<ComposerRepository>(tag: composerId),
|
||||||
|
Get.find<SaveTemplateEmailInteractor>(tag: composerId),
|
||||||
composerId: composerId,
|
composerId: composerId,
|
||||||
composerArgs: composerArguments,
|
composerArgs: composerArguments,
|
||||||
), tag: composerId);
|
), tag: composerId);
|
||||||
|
|||||||
@@ -16,10 +16,12 @@ import 'package:flutter/services.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:html_editor_enhanced/html_editor.dart';
|
import 'package:html_editor_enhanced/html_editor.dart';
|
||||||
import 'package:jmap_dart_client/jmap/account_id.dart';
|
import 'package:jmap_dart_client/jmap/account_id.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/core/error/method/error_method_response.dart';
|
||||||
import 'package:jmap_dart_client/jmap/core/session/session.dart';
|
import 'package:jmap_dart_client/jmap/core/session/session.dart';
|
||||||
import 'package:jmap_dart_client/jmap/identities/identity.dart';
|
import 'package:jmap_dart_client/jmap/identities/identity.dart';
|
||||||
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
||||||
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
|
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
|
||||||
import 'package:model/model.dart';
|
import 'package:model/model.dart';
|
||||||
import 'package:permission_handler/permission_handler.dart';
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
import 'package:pointer_interceptor/pointer_interceptor.dart';
|
import 'package:pointer_interceptor/pointer_interceptor.dart';
|
||||||
@@ -32,6 +34,8 @@ import 'package:tmail_ui_user/features/base/mixin/auto_complete_result_mixin.dar
|
|||||||
import 'package:tmail_ui_user/features/base/state/base_ui_state.dart';
|
import 'package:tmail_ui_user/features/base/state/base_ui_state.dart';
|
||||||
import 'package:tmail_ui_user/features/base/state/button_state.dart';
|
import 'package:tmail_ui_user/features/base/state/button_state.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/domain/exceptions/compose_email_exception.dart';
|
import 'package:tmail_ui_user/features/composer/domain/exceptions/compose_email_exception.dart';
|
||||||
|
import 'package:tmail_ui_user/features/composer/domain/exceptions/set_method_exception.dart';
|
||||||
|
import 'package:tmail_ui_user/features/composer/domain/extensions/set_method_exception_description_extension.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/domain/model/contact_suggestion_source.dart';
|
import 'package:tmail_ui_user/features/composer/domain/model/contact_suggestion_source.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/domain/repository/composer_repository.dart';
|
import 'package:tmail_ui_user/features/composer/domain/repository/composer_repository.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/domain/state/download_image_as_base64_state.dart';
|
import 'package:tmail_ui_user/features/composer/domain/state/download_image_as_base64_state.dart';
|
||||||
@@ -64,27 +68,33 @@ import 'package:tmail_ui_user/features/composer/presentation/extensions/setup_em
|
|||||||
import 'package:tmail_ui_user/features/composer/presentation/extensions/setup_email_recipients_extension.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/extensions/setup_email_recipients_extension.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/extensions/setup_email_request_read_receipt_flag_extension.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/extensions/setup_email_request_read_receipt_flag_extension.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/extensions/setup_email_subject_extension.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/extensions/setup_email_subject_extension.dart';
|
||||||
|
import 'package:tmail_ui_user/features/composer/presentation/extensions/setup_email_template_id_extension.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/extensions/setup_list_identities_extension.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/extensions/setup_list_identities_extension.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/extensions/setup_selected_identity_extension.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/extensions/setup_selected_identity_extension.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/extensions/update_screen_display_mode_extension.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/extensions/update_screen_display_mode_extension.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/mixin/drag_drog_file_mixin.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/mixin/drag_drog_file_mixin.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/model/create_email_request.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/model/create_email_request.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/model/saved_email_draft.dart';
|
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/model/inline_image.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/model/inline_image.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/model/prefix_recipient_state.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/model/prefix_recipient_state.dart';
|
||||||
|
import 'package:tmail_ui_user/features/composer/presentation/model/saved_composing_email.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/model/screen_display_mode.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/model/screen_display_mode.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/styles/composer_style.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/styles/composer_style.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/view/editor_view_mixin.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/view/editor_view_mixin.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/widgets/mobile/from_composer_bottom_sheet_builder.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/widgets/mobile/from_composer_bottom_sheet_builder.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/widgets/saving_message_dialog_view.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/widgets/saving_message_dialog_view.dart';
|
||||||
|
import 'package:tmail_ui_user/features/composer/presentation/widgets/saving_template_dialog_view.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/widgets/sending_message_dialog_view.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/widgets/sending_message_dialog_view.dart';
|
||||||
import 'package:tmail_ui_user/features/email/domain/state/get_email_content_state.dart';
|
import 'package:tmail_ui_user/features/email/domain/state/get_email_content_state.dart';
|
||||||
|
import 'package:tmail_ui_user/features/email/domain/state/save_template_email_state.dart';
|
||||||
|
import 'package:tmail_ui_user/features/email/domain/state/update_template_email_state.dart';
|
||||||
import 'package:tmail_ui_user/features/email/domain/usecases/get_email_content_interactor.dart';
|
import 'package:tmail_ui_user/features/email/domain/usecases/get_email_content_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/email/domain/usecases/print_email_interactor.dart';
|
import 'package:tmail_ui_user/features/email/domain/usecases/print_email_interactor.dart';
|
||||||
|
import 'package:tmail_ui_user/features/email/domain/usecases/save_template_email_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/email/domain/usecases/transform_html_email_content_interactor.dart';
|
import 'package:tmail_ui_user/features/email/domain/usecases/transform_html_email_content_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/email/presentation/extensions/presentation_email_extension.dart';
|
import 'package:tmail_ui_user/features/email/presentation/extensions/presentation_email_extension.dart';
|
||||||
import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart';
|
import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart';
|
||||||
import 'package:tmail_ui_user/features/email/presentation/utils/email_utils.dart';
|
import 'package:tmail_ui_user/features/email/presentation/utils/email_utils.dart';
|
||||||
|
import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/remove_composer_cache_by_id_on_web_interactor.dart';
|
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/remove_composer_cache_by_id_on_web_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart';
|
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/open_and_close_composer_extension.dart';
|
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/open_and_close_composer_extension.dart';
|
||||||
@@ -157,6 +167,7 @@ class ComposerController extends BaseController
|
|||||||
final ComposerRepository _composerRepository;
|
final ComposerRepository _composerRepository;
|
||||||
final String? composerId;
|
final String? composerId;
|
||||||
final ComposerArguments? composerArgs;
|
final ComposerArguments? composerArgs;
|
||||||
|
final SaveTemplateEmailInteractor _saveTemplateEmailInteractor;
|
||||||
|
|
||||||
GetAllAutoCompleteInteractor? _getAllAutoCompleteInteractor;
|
GetAllAutoCompleteInteractor? _getAllAutoCompleteInteractor;
|
||||||
GetAutoCompleteInteractor? _getAutoCompleteInteractor;
|
GetAutoCompleteInteractor? _getAutoCompleteInteractor;
|
||||||
@@ -226,6 +237,7 @@ class ComposerController extends BaseController
|
|||||||
EmailActionType? currentEmailActionType;
|
EmailActionType? currentEmailActionType;
|
||||||
EmailActionType? savedActionType;
|
EmailActionType? savedActionType;
|
||||||
int minInputLengthAutocomplete = AppConfig.defaultMinInputLengthAutocomplete;
|
int minInputLengthAutocomplete = AppConfig.defaultMinInputLengthAutocomplete;
|
||||||
|
EmailId? currentTemplateEmailId;
|
||||||
|
|
||||||
@visibleForTesting
|
@visibleForTesting
|
||||||
int? get savedEmailDraftHash => _savedEmailDraftHash;
|
int? get savedEmailDraftHash => _savedEmailDraftHash;
|
||||||
@@ -257,6 +269,7 @@ class ComposerController extends BaseController
|
|||||||
this._createNewAndSaveEmailToDraftsInteractor,
|
this._createNewAndSaveEmailToDraftsInteractor,
|
||||||
this.printEmailInteractor,
|
this.printEmailInteractor,
|
||||||
this._composerRepository,
|
this._composerRepository,
|
||||||
|
this._saveTemplateEmailInteractor,
|
||||||
{
|
{
|
||||||
this.composerId,
|
this.composerId,
|
||||||
this.composerArgs,
|
this.composerArgs,
|
||||||
@@ -528,6 +541,7 @@ class ComposerController extends BaseController
|
|||||||
savedDraftHash: arguments.savedDraftHash ?? _savedEmailDraftHash,
|
savedDraftHash: arguments.savedDraftHash ?? _savedEmailDraftHash,
|
||||||
savedActionType: savedActionType ?? currentEmailActionType,
|
savedActionType: savedActionType ?? currentEmailActionType,
|
||||||
savedEmailDraftId: emailIdEditing,
|
savedEmailDraftId: emailIdEditing,
|
||||||
|
templateEmailId: currentTemplateEmailId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -627,6 +641,7 @@ class ComposerController extends BaseController
|
|||||||
setupEmailAttachments(arguments);
|
setupEmailAttachments(arguments);
|
||||||
setupEmailOtherComponents(arguments);
|
setupEmailOtherComponents(arguments);
|
||||||
setupEmailRequestReadReceiptFlag(arguments);
|
setupEmailRequestReadReceiptFlag(arguments);
|
||||||
|
setupEmailTemplateId(arguments);
|
||||||
|
|
||||||
await setupListIdentities(arguments);
|
await setupListIdentities(arguments);
|
||||||
await setupEmailContent(arguments);
|
await setupEmailContent(arguments);
|
||||||
@@ -1130,12 +1145,11 @@ class ComposerController extends BaseController
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> _validateEmailChange() async {
|
Future<bool> _validateEmailChange() async {
|
||||||
final newDraftHash = await _hashDraftEmail();
|
final newDraftHash = await _hashComposingEmail();
|
||||||
log('ComposerController::_validateEmailChange:newDraftHash = $newDraftHash | _savedEmailDraftHash = $_savedEmailDraftHash');
|
|
||||||
return _savedEmailDraftHash != newDraftHash;
|
return _savedEmailDraftHash != newDraftHash;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<int> _hashDraftEmail() async {
|
Future<int> _hashComposingEmail() async {
|
||||||
String emailContent = await getContentInEditor();
|
String emailContent = await getContentInEditor();
|
||||||
|
|
||||||
emailContent = await _composerRepository.removeCollapsedExpandedSignatureEffect(
|
emailContent = await _composerRepository.removeCollapsedExpandedSignatureEffect(
|
||||||
@@ -1149,7 +1163,7 @@ class ComposerController extends BaseController
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final savedEmailDraft = SavedEmailDraft(
|
final savedEmailDraft = SavedComposingEmail(
|
||||||
subject: subjectEmail.value ?? '',
|
subject: subjectEmail.value ?? '',
|
||||||
content: emailContent,
|
content: emailContent,
|
||||||
toRecipients: listToEmailAddress.toSet(),
|
toRecipients: listToEmailAddress.toSet(),
|
||||||
@@ -1168,11 +1182,11 @@ class ComposerController extends BaseController
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _updateSavedEmailDraftHash() async {
|
Future<void> _updateSavedEmailDraftHash() async {
|
||||||
_savedEmailDraftHash = await _hashDraftEmail();
|
_savedEmailDraftHash = await _hashComposingEmail();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> initEmailDraftHash() async {
|
Future<void> initEmailDraftHash() async {
|
||||||
final currentDraftHash = await _hashDraftEmail();
|
final currentDraftHash = await _hashComposingEmail();
|
||||||
|
|
||||||
final oldSavedDraftHash = composerArguments.value?.savedDraftHash;
|
final oldSavedDraftHash = composerArguments.value?.savedDraftHash;
|
||||||
|
|
||||||
@@ -1264,6 +1278,76 @@ class ComposerController extends BaseController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> handleClickSaveAsTemplateButton(BuildContext context) async {
|
||||||
|
if (composerArguments.value == null ||
|
||||||
|
mailboxDashBoardController.sessionCurrent == null ||
|
||||||
|
mailboxDashBoardController.accountId.value == null
|
||||||
|
) {
|
||||||
|
log('ComposerController::handleClickSaveAsTemplateButton: SESSION or ACCOUNT_ID or ARGUMENTS is NULL');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MailboxId? templateMailboxId = mailboxDashBoardController
|
||||||
|
.getMailboxIdByRole(PresentationMailbox.roleTemplates);
|
||||||
|
templateMailboxId ??= mailboxDashBoardController.mapMailboxById
|
||||||
|
.where((_, mailbox) => mailbox.name?.name.toLowerCase() ==
|
||||||
|
PresentationMailbox.roleTemplates.value.toLowerCase())
|
||||||
|
.keys
|
||||||
|
.firstOrNull;
|
||||||
|
|
||||||
|
final emailContent = await getContentInEditor();
|
||||||
|
final cancelToken = CancelToken();
|
||||||
|
final resultState = await _showSavingMessageToTemplateDialog(
|
||||||
|
emailContent: emailContent,
|
||||||
|
templateMailboxId: templateMailboxId,
|
||||||
|
templateEmailId: currentTemplateEmailId,
|
||||||
|
createNewMailboxRequest: templateMailboxId != null
|
||||||
|
? null
|
||||||
|
: CreateNewMailboxRequest(
|
||||||
|
MailboxName(PresentationMailbox.roleTemplates.value.toUpperCase()),
|
||||||
|
),
|
||||||
|
cancelToken: cancelToken,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (resultState is SaveTemplateEmailSuccess && context.mounted == true) {
|
||||||
|
currentTemplateEmailId = resultState.emailId;
|
||||||
|
appToast.showToastSuccessMessage(
|
||||||
|
context,
|
||||||
|
AppLocalizations.of(context).saveMessageToTemplateSuccess,
|
||||||
|
);
|
||||||
|
} else if (resultState is UpdateTemplateEmailSuccess && context.mounted == true) {
|
||||||
|
currentTemplateEmailId = resultState.emailId;
|
||||||
|
appToast.showToastSuccessMessage(
|
||||||
|
context,
|
||||||
|
AppLocalizations.of(context).updateMessageToTemplateSuccess,
|
||||||
|
);
|
||||||
|
} else if (resultState is SaveTemplateEmailFailure ||
|
||||||
|
resultState is UpdateTemplateEmailFailure ||
|
||||||
|
resultState is GenerateEmailFailure
|
||||||
|
) {
|
||||||
|
if (resultState.exception is BadCredentialsException) {
|
||||||
|
handleBadCredentialsException();
|
||||||
|
} else if (context.mounted) {
|
||||||
|
String message = '';
|
||||||
|
final exception = resultState.exception;
|
||||||
|
if (exception is SetMethodException) {
|
||||||
|
final exceptionDescription = exception.getDescriptionFromErrorType(
|
||||||
|
ErrorMethodResponse.invalidArguments,
|
||||||
|
);
|
||||||
|
message = exceptionDescription != null
|
||||||
|
? AppLocalizations.of(context).invalidArguments(exceptionDescription)
|
||||||
|
: AppLocalizations.of(context).saveMessageToTemplateFailed;
|
||||||
|
} else if (cancelToken.isCancelled) {
|
||||||
|
message = AppLocalizations.of(context).saveMessageToTemplateCancelled;
|
||||||
|
} else {
|
||||||
|
message = AppLocalizations.of(context).saveMessageToTemplateFailed;
|
||||||
|
}
|
||||||
|
|
||||||
|
appToast.showToastErrorMessage(context, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void clearFocus(BuildContext context) {
|
void clearFocus(BuildContext context) {
|
||||||
log('ComposerController::clearFocus:');
|
log('ComposerController::clearFocus:');
|
||||||
if (PlatformInfo.isMobile) {
|
if (PlatformInfo.isMobile) {
|
||||||
@@ -2032,6 +2116,56 @@ class ComposerController extends BaseController
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<dynamic> _showSavingMessageToTemplateDialog({
|
||||||
|
required String emailContent,
|
||||||
|
required MailboxId? templateMailboxId,
|
||||||
|
required EmailId? templateEmailId,
|
||||||
|
required CreateNewMailboxRequest? createNewMailboxRequest,
|
||||||
|
CancelToken? cancelToken,
|
||||||
|
}) {
|
||||||
|
final childWidget = PointerInterceptor(
|
||||||
|
child: SavingTemplateDialogView(
|
||||||
|
createEmailRequest: CreateEmailRequest(
|
||||||
|
session: mailboxDashBoardController.sessionCurrent!,
|
||||||
|
accountId: mailboxDashBoardController.accountId.value!,
|
||||||
|
emailActionType: composerArguments.value!.emailActionType,
|
||||||
|
subject: subjectEmail.value ?? '',
|
||||||
|
emailContent: emailContent,
|
||||||
|
fromSender: composerArguments.value!.presentationEmail?.from ?? {},
|
||||||
|
toRecipients: listToEmailAddress.toSet(),
|
||||||
|
ccRecipients: listCcEmailAddress.toSet(),
|
||||||
|
bccRecipients: listBccEmailAddress.toSet(),
|
||||||
|
replyToRecipients: listReplyToEmailAddress.toSet(),
|
||||||
|
hasRequestReadReceipt: hasRequestReadReceipt.value,
|
||||||
|
isMarkAsImportant: isMarkAsImportant.value,
|
||||||
|
identity: identitySelected.value,
|
||||||
|
attachments: uploadController.attachmentsUploaded,
|
||||||
|
inlineAttachments: uploadController.mapInlineAttachments,
|
||||||
|
sentMailboxId: getSentMailboxIdForComposer(),
|
||||||
|
templateMailboxId: templateMailboxId,
|
||||||
|
templateEmailId: templateEmailId,
|
||||||
|
answerForwardEmailId: composerArguments.value!.presentationEmail?.id,
|
||||||
|
unsubscribeEmailId: composerArguments.value!.previousEmailId,
|
||||||
|
messageId: composerArguments.value!.messageId,
|
||||||
|
references: composerArguments.value!.references,
|
||||||
|
emailSendingQueue: composerArguments.value!.sendingEmail,
|
||||||
|
displayMode: screenDisplayMode.value
|
||||||
|
),
|
||||||
|
saveTemplateEmailInteractor: _saveTemplateEmailInteractor,
|
||||||
|
createNewMailboxRequest: createNewMailboxRequest,
|
||||||
|
onCancel: (cancelToken) => cancelToken?.cancel(),
|
||||||
|
cancelToken: cancelToken,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return Get.dialog(
|
||||||
|
PlatformInfo.isMobile
|
||||||
|
? PopScope(canPop: false, child: childWidget)
|
||||||
|
: childWidget,
|
||||||
|
barrierDismissible: false,
|
||||||
|
barrierColor: AppColor.colorDefaultCupertinoActionSheet,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void _handleCancelSavingMessageToDrafts({CancelToken? cancelToken}) {
|
void _handleCancelSavingMessageToDrafts({CancelToken? cancelToken}) {
|
||||||
cancelToken?.cancel([SavingEmailToDraftsCanceledException()]);
|
cancelToken?.cancel([SavingEmailToDraftsCanceledException()]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -563,6 +563,20 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
PopupMenuItem(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
child: PopupItemWidget(
|
||||||
|
iconAction: controller.imagePaths.icSaveToDraft,
|
||||||
|
nameAction: AppLocalizations.of(context).saveAsTemplate,
|
||||||
|
colorIcon: ComposerStyle.popupItemIconColor,
|
||||||
|
styleName: ComposerStyle.popupItemTextStyle,
|
||||||
|
padding: ComposerStyle.popupItemPadding,
|
||||||
|
onCallbackAction: () {
|
||||||
|
popBack();
|
||||||
|
controller.handleClickSaveAsTemplateButton(context);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
),
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
child: PopupItemWidget(
|
child: PopupItemWidget(
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
toggleRequestReadReceiptAction: () => controller.toggleRequestReadReceipt(context),
|
toggleRequestReadReceiptAction: () => controller.toggleRequestReadReceipt(context),
|
||||||
toggleMarkAsImportantAction: () => controller.toggleMarkAsImportant(context),
|
toggleMarkAsImportantAction: () => controller.toggleMarkAsImportant(context),
|
||||||
saveToDraftsAction: () => controller.handleClickSaveAsDraftsButton(context),
|
saveToDraftsAction: () => controller.handleClickSaveAsDraftsButton(context),
|
||||||
|
saveToTemplateAction: () => controller.handleClickSaveAsTemplateButton(context),
|
||||||
deleteComposerAction: () => controller.handleClickDeleteComposer(context),
|
deleteComposerAction: () => controller.handleClickDeleteComposer(context),
|
||||||
)),
|
)),
|
||||||
ConstrainedBox(
|
ConstrainedBox(
|
||||||
@@ -576,6 +577,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
printDraftAction: () => controller.printDraft(context),
|
printDraftAction: () => controller.printDraft(context),
|
||||||
toggleRequestReadReceiptAction: () => controller.toggleRequestReadReceipt(context),
|
toggleRequestReadReceiptAction: () => controller.toggleRequestReadReceipt(context),
|
||||||
toggleMarkAsImportantAction: () => controller.toggleMarkAsImportant(context),
|
toggleMarkAsImportantAction: () => controller.toggleMarkAsImportant(context),
|
||||||
|
saveAsTemplateAction: () => controller.handleClickSaveAsTemplateButton(context),
|
||||||
)),
|
)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -885,6 +887,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
printDraftAction: () => controller.printDraft(context),
|
printDraftAction: () => controller.printDraft(context),
|
||||||
toggleRequestReadReceiptAction: () => controller.toggleRequestReadReceipt(context),
|
toggleRequestReadReceiptAction: () => controller.toggleRequestReadReceipt(context),
|
||||||
toggleMarkAsImportantAction: () => controller.toggleMarkAsImportant(context),
|
toggleMarkAsImportantAction: () => controller.toggleMarkAsImportant(context),
|
||||||
|
saveAsTemplateAction: () => controller.handleClickSaveAsTemplateButton(context),
|
||||||
)),
|
)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -39,12 +39,12 @@ extension CreateEmailRequestExtension on CreateEmailRequest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<EmailAddress>? createReplyToRecipients({bool isDraft = false}) {
|
Set<EmailAddress>? createReplyToRecipients({bool isNotReplyTo = false}) {
|
||||||
if (replyToRecipients?.isNotEmpty == true) {
|
if (replyToRecipients?.isNotEmpty == true) {
|
||||||
return replyToRecipients;
|
return replyToRecipients;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isDraft) return null;
|
if (isNotReplyTo) return null;
|
||||||
|
|
||||||
return identity?.replyTo?.isNotEmpty == true
|
return identity?.replyTo?.isNotEmpty == true
|
||||||
? identity!.replyTo!
|
? identity!.replyTo!
|
||||||
@@ -60,18 +60,27 @@ extension CreateEmailRequestExtension on CreateEmailRequest {
|
|||||||
KeyWordIdentifier.emailDraft: true,
|
KeyWordIdentifier.emailDraft: true,
|
||||||
KeyWordIdentifier.emailSeen: true,
|
KeyWordIdentifier.emailSeen: true,
|
||||||
};
|
};
|
||||||
|
} else if (templateMailboxId != null) {
|
||||||
|
return {
|
||||||
|
KeyWordIdentifier.emailSeen: true,
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<MailboxId, bool>? createMailboxIds() {
|
Map<MailboxId, bool>? createMailboxIds() {
|
||||||
if (draftsMailboxId != null || outboxMailboxId != null) {
|
if (draftsMailboxId != null
|
||||||
|
|| outboxMailboxId != null
|
||||||
|
|| templateMailboxId != null
|
||||||
|
) {
|
||||||
return {
|
return {
|
||||||
if (draftsMailboxId != null)
|
if (draftsMailboxId != null)
|
||||||
draftsMailboxId!: true,
|
draftsMailboxId!: true,
|
||||||
if (outboxMailboxId != null)
|
if (outboxMailboxId != null)
|
||||||
outboxMailboxId!: true,
|
outboxMailboxId!: true,
|
||||||
|
if (templateMailboxId != null)
|
||||||
|
templateMailboxId!: true,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
@@ -115,6 +124,7 @@ extension CreateEmailRequestExtension on CreateEmailRequest {
|
|||||||
required PartId partId,
|
required PartId partId,
|
||||||
bool withIdentityHeader = false,
|
bool withIdentityHeader = false,
|
||||||
bool isDraft = false,
|
bool isDraft = false,
|
||||||
|
bool isTemplate = false,
|
||||||
}) {
|
}) {
|
||||||
return Email(
|
return Email(
|
||||||
mailboxIds: createMailboxIds(),
|
mailboxIds: createMailboxIds(),
|
||||||
@@ -122,7 +132,7 @@ extension CreateEmailRequestExtension on CreateEmailRequest {
|
|||||||
to: toRecipients,
|
to: toRecipients,
|
||||||
cc: ccRecipients,
|
cc: ccRecipients,
|
||||||
bcc: bccRecipients,
|
bcc: bccRecipients,
|
||||||
replyTo: createReplyToRecipients(isDraft: isDraft),
|
replyTo: createReplyToRecipients(isNotReplyTo: isDraft || isTemplate),
|
||||||
inReplyTo: createInReplyTo(),
|
inReplyTo: createInReplyTo(),
|
||||||
references: createReferences(),
|
references: createReferences(),
|
||||||
keywords: createKeywords(),
|
keywords: createKeywords(),
|
||||||
|
|||||||
@@ -65,8 +65,9 @@ extension SetupEmailContentExtension on ComposerController {
|
|||||||
inlineImages: uiState.inlineImages,
|
inlineImages: uiState.inlineImages,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (currentEmailActionType == EmailActionType.editDraft) {
|
if (currentEmailActionType == EmailActionType.editDraft ||
|
||||||
setupEmailRequestReadReceiptFlagForEditDraft(
|
currentEmailActionType == EmailActionType.editAsNewEmail) {
|
||||||
|
setupEmailRequestReadReceiptFlag(
|
||||||
uiState.emailCurrent!.hasRequestReadReceipt,
|
uiState.emailCurrent!.hasRequestReadReceipt,
|
||||||
);
|
);
|
||||||
setupSelectedIdentityForEditDraft(
|
setupSelectedIdentityForEditDraft(
|
||||||
@@ -81,8 +82,9 @@ extension SetupEmailContentExtension on ComposerController {
|
|||||||
inlineImages: uiState.inlineImages,
|
inlineImages: uiState.inlineImages,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (currentEmailActionType == EmailActionType.editDraft) {
|
if (currentEmailActionType == EmailActionType.editDraft ||
|
||||||
setupEmailRequestReadReceiptFlagForEditDraft(
|
currentEmailActionType == EmailActionType.editAsNewEmail) {
|
||||||
|
setupEmailRequestReadReceiptFlag(
|
||||||
uiState.emailCurrent.hasRequestReadReceipt,
|
uiState.emailCurrent.hasRequestReadReceipt,
|
||||||
);
|
);
|
||||||
setupSelectedIdentityForEditDraft(
|
setupSelectedIdentityForEditDraft(
|
||||||
|
|||||||
+2
-1
@@ -10,7 +10,8 @@ extension SetupEmailRequestReadReceiptFlagExtension on ComposerController {
|
|||||||
void setupEmailRequestReadReceiptFlag(ComposerArguments arguments) {
|
void setupEmailRequestReadReceiptFlag(ComposerArguments arguments) {
|
||||||
if (currentEmailActionType == EmailActionType.reopenComposerBrowser) {
|
if (currentEmailActionType == EmailActionType.reopenComposerBrowser) {
|
||||||
hasRequestReadReceipt.value = arguments.hasRequestReadReceipt ?? false;
|
hasRequestReadReceipt.value = arguments.hasRequestReadReceipt ?? false;
|
||||||
} else if (currentEmailActionType != EmailActionType.editDraft) {
|
} else if (currentEmailActionType != EmailActionType.editDraft &&
|
||||||
|
currentEmailActionType != EmailActionType.editAsNewEmail) {
|
||||||
getServerSetting();
|
getServerSetting();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@ import 'package:tmail_ui_user/features/composer/presentation/composer_controller
|
|||||||
|
|
||||||
extension SetupEmailRequestReadReceiptFlagExtension on ComposerController {
|
extension SetupEmailRequestReadReceiptFlagExtension on ComposerController {
|
||||||
|
|
||||||
void setupEmailRequestReadReceiptFlagForEditDraft(bool isRequestReadReceipt) {
|
void setupEmailRequestReadReceiptFlag(bool isRequestReadReceipt) {
|
||||||
hasRequestReadReceipt.value = isRequestReadReceipt;
|
hasRequestReadReceipt.value = isRequestReadReceipt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import 'package:model/email/email_action_type.dart';
|
||||||
|
import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart';
|
||||||
|
import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart';
|
||||||
|
|
||||||
|
extension SetupEmailTemplateIdExtension on ComposerController {
|
||||||
|
void setupEmailTemplateId(ComposerArguments arguments) {
|
||||||
|
if (currentEmailActionType == EmailActionType.editAsNewEmail ||
|
||||||
|
currentEmailActionType == EmailActionType.reopenComposerBrowser
|
||||||
|
) {
|
||||||
|
currentTemplateEmailId = arguments.savedEmailTemplateId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,7 +31,9 @@ class CreateEmailRequest with EquatableMixin {
|
|||||||
final MailboxId? outboxMailboxId;
|
final MailboxId? outboxMailboxId;
|
||||||
final MailboxId? sentMailboxId;
|
final MailboxId? sentMailboxId;
|
||||||
final MailboxId? draftsMailboxId;
|
final MailboxId? draftsMailboxId;
|
||||||
|
final MailboxId? templateMailboxId;
|
||||||
final EmailId? draftsEmailId;
|
final EmailId? draftsEmailId;
|
||||||
|
final EmailId? templateEmailId;
|
||||||
final EmailId? answerForwardEmailId;
|
final EmailId? answerForwardEmailId;
|
||||||
final EmailId? unsubscribeEmailId;
|
final EmailId? unsubscribeEmailId;
|
||||||
final MessageIdsHeaderValue? messageId;
|
final MessageIdsHeaderValue? messageId;
|
||||||
@@ -64,7 +66,9 @@ class CreateEmailRequest with EquatableMixin {
|
|||||||
this.outboxMailboxId,
|
this.outboxMailboxId,
|
||||||
this.sentMailboxId,
|
this.sentMailboxId,
|
||||||
this.draftsMailboxId,
|
this.draftsMailboxId,
|
||||||
|
this.templateMailboxId,
|
||||||
this.draftsEmailId,
|
this.draftsEmailId,
|
||||||
|
this.templateEmailId,
|
||||||
this.answerForwardEmailId,
|
this.answerForwardEmailId,
|
||||||
this.unsubscribeEmailId,
|
this.unsubscribeEmailId,
|
||||||
this.messageId,
|
this.messageId,
|
||||||
@@ -99,7 +103,9 @@ class CreateEmailRequest with EquatableMixin {
|
|||||||
outboxMailboxId,
|
outboxMailboxId,
|
||||||
sentMailboxId,
|
sentMailboxId,
|
||||||
draftsMailboxId,
|
draftsMailboxId,
|
||||||
|
templateMailboxId,
|
||||||
draftsEmailId,
|
draftsEmailId,
|
||||||
|
templateEmailId,
|
||||||
answerForwardEmailId,
|
answerForwardEmailId,
|
||||||
unsubscribeEmailId,
|
unsubscribeEmailId,
|
||||||
references,
|
references,
|
||||||
|
|||||||
+19
-5
@@ -6,10 +6,10 @@ import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
|
|||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
import 'package:model/email/attachment.dart';
|
import 'package:model/email/attachment.dart';
|
||||||
|
|
||||||
part 'saved_email_draft.g.dart';
|
part 'saved_composing_email.g.dart';
|
||||||
|
|
||||||
@JsonSerializable(includeIfNull: false, explicitToJson: true)
|
@JsonSerializable(includeIfNull: false, explicitToJson: true)
|
||||||
class SavedEmailDraft with EquatableMixin {
|
class SavedComposingEmail with EquatableMixin {
|
||||||
final String content;
|
final String content;
|
||||||
final String subject;
|
final String subject;
|
||||||
final Set<EmailAddress> toRecipients;
|
final Set<EmailAddress> toRecipients;
|
||||||
@@ -21,7 +21,7 @@ class SavedEmailDraft with EquatableMixin {
|
|||||||
final bool hasReadReceipt;
|
final bool hasReadReceipt;
|
||||||
final bool isMarkAsImportant;
|
final bool isMarkAsImportant;
|
||||||
|
|
||||||
SavedEmailDraft({
|
SavedComposingEmail({
|
||||||
required this.content,
|
required this.content,
|
||||||
required this.subject,
|
required this.subject,
|
||||||
required this.toRecipients,
|
required this.toRecipients,
|
||||||
@@ -34,12 +34,26 @@ class SavedEmailDraft with EquatableMixin {
|
|||||||
this.isMarkAsImportant = false,
|
this.isMarkAsImportant = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory SavedEmailDraft.fromJson(Map<String, dynamic> json) => _$SavedEmailDraftFromJson(json);
|
factory SavedComposingEmail.fromJson(Map<String, dynamic> json) => _$SavedComposingEmailFromJson(json);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => _$SavedEmailDraftToJson(this);
|
Map<String, dynamic> toJson() => _$SavedComposingEmailToJson(this);
|
||||||
|
|
||||||
String asString() => jsonEncode(toJson());
|
String asString() => jsonEncode(toJson());
|
||||||
|
|
||||||
|
factory SavedComposingEmail.empty() {
|
||||||
|
return SavedComposingEmail(
|
||||||
|
subject: '',
|
||||||
|
content: '',
|
||||||
|
toRecipients: {},
|
||||||
|
ccRecipients: {},
|
||||||
|
bccRecipients: {},
|
||||||
|
replyToRecipients: {},
|
||||||
|
attachments: [],
|
||||||
|
identity: null,
|
||||||
|
hasReadReceipt: false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [
|
List<Object?> get props => [
|
||||||
content,
|
content,
|
||||||
@@ -0,0 +1,230 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:core/presentation/extensions/capitalize_extension.dart';
|
||||||
|
import 'package:core/presentation/extensions/color_extension.dart';
|
||||||
|
import 'package:core/presentation/state/failure.dart';
|
||||||
|
import 'package:core/presentation/state/success.dart';
|
||||||
|
import 'package:core/presentation/views/button/tmail_button_widget.dart';
|
||||||
|
import 'package:core/utils/app_logger.dart';
|
||||||
|
import 'package:dartz/dartz.dart' hide State;
|
||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:tmail_ui_user/features/composer/domain/state/generate_email_state.dart';
|
||||||
|
import 'package:tmail_ui_user/features/composer/presentation/model/create_email_request.dart';
|
||||||
|
import 'package:tmail_ui_user/features/email/domain/state/save_template_email_state.dart';
|
||||||
|
import 'package:tmail_ui_user/features/email/domain/state/update_template_email_state.dart';
|
||||||
|
import 'package:tmail_ui_user/features/email/domain/usecases/save_template_email_interactor.dart';
|
||||||
|
import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart';
|
||||||
|
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||||
|
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
||||||
|
|
||||||
|
class SavingTemplateDialogView extends StatefulWidget {
|
||||||
|
|
||||||
|
final CreateEmailRequest createEmailRequest;
|
||||||
|
final SaveTemplateEmailInteractor saveTemplateEmailInteractor;
|
||||||
|
final CreateNewMailboxRequest? createNewMailboxRequest;
|
||||||
|
final CancelToken? cancelToken;
|
||||||
|
final void Function(CancelToken? cancelToken)? onCancel;
|
||||||
|
|
||||||
|
const SavingTemplateDialogView({
|
||||||
|
super.key,
|
||||||
|
required this.createEmailRequest,
|
||||||
|
required this.saveTemplateEmailInteractor,
|
||||||
|
required this.createNewMailboxRequest,
|
||||||
|
this.cancelToken,
|
||||||
|
this.onCancel,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SavingTemplateDialogView> createState() => _SavingTemplateDialogViewState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SavingTemplateDialogViewState extends State<SavingTemplateDialogView> {
|
||||||
|
StreamSubscription? _streamSubscription;
|
||||||
|
final ValueNotifier<Either<Failure, Success>?> _viewStateNotifier = ValueNotifier(null);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_streamSubscription = widget.saveTemplateEmailInteractor
|
||||||
|
.execute(
|
||||||
|
createEmailRequest: widget.createEmailRequest,
|
||||||
|
createNewMailboxRequest: widget.createNewMailboxRequest,
|
||||||
|
cancelToken: widget.cancelToken
|
||||||
|
)
|
||||||
|
.listen(_handleDataStream, onError: _handleErrorStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleDataStream(Either<Failure, Success> newState) {
|
||||||
|
_viewStateNotifier.value = newState;
|
||||||
|
|
||||||
|
newState.fold(
|
||||||
|
(failure) {
|
||||||
|
if (failure is SaveTemplateEmailFailure ||
|
||||||
|
failure is UpdateTemplateEmailFailure ||
|
||||||
|
failure is GenerateEmailFailure) {
|
||||||
|
popBack(result: failure);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(success) {
|
||||||
|
if (success is SaveTemplateEmailSuccess || success is UpdateTemplateEmailSuccess) {
|
||||||
|
popBack(result: success);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleErrorStream(Object error, StackTrace stackTrace) {
|
||||||
|
logError('SavingTemplateDialogView::_handleErrorStream: Exception = $error');
|
||||||
|
popBack(result: SaveTemplateEmailFailure(exception: error));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Dialog(
|
||||||
|
shape: const RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(12)),
|
||||||
|
),
|
||||||
|
insetPadding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 16.0),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: Container(
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(12)),
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
width: min(context.width, 400),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
clipBehavior: Clip.antiAlias,
|
||||||
|
padding: const EdgeInsetsDirectional.symmetric(vertical: 8, horizontal: 12),
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(12)),
|
||||||
|
color: AppColor.colorItemSelected,
|
||||||
|
),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: Text(
|
||||||
|
AppLocalizations.of(context).savingTemplate.capitalizeFirstEach,
|
||||||
|
style: Theme.of(context).textTheme.labelLarge?.copyWith(
|
||||||
|
color: Colors.black,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 17
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsetsDirectional.only(start: 16, end: 16, top: 12, bottom: 4),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'${AppLocalizations.of(context).status}:',
|
||||||
|
style: Theme.of(context).textTheme.labelMedium?.copyWith(
|
||||||
|
color: Colors.black,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
fontSize: 14
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Expanded(
|
||||||
|
child: ValueListenableBuilder(
|
||||||
|
valueListenable: _viewStateNotifier,
|
||||||
|
builder: (context, value, child) {
|
||||||
|
if (value == null) {
|
||||||
|
return child!;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value.fold(
|
||||||
|
(failure) => child!,
|
||||||
|
(success) {
|
||||||
|
return Text(
|
||||||
|
'${_getStatusMessage(success)}...',
|
||||||
|
style: Theme.of(context).textTheme.labelSmall?.copyWith(
|
||||||
|
color: AppColor.labelColor,
|
||||||
|
fontSize: 14
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
'...',
|
||||||
|
style: Theme.of(context).textTheme.labelSmall?.copyWith(
|
||||||
|
color: AppColor.labelColor,
|
||||||
|
fontSize: 14
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsetsDirectional.only(start: 16, end: 16, top: 4, bottom: 16),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'${AppLocalizations.of(context).progress}:',
|
||||||
|
style: Theme.of(context).textTheme.labelMedium?.copyWith(
|
||||||
|
color: Colors.black,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
fontSize: 14
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Expanded(
|
||||||
|
child: LinearProgressIndicator(
|
||||||
|
color: Colors.white.withOpacity(0.6),
|
||||||
|
backgroundColor: AppColor.primaryColor,
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (widget.onCancel != null)
|
||||||
|
Align(
|
||||||
|
alignment: AlignmentDirectional.centerEnd,
|
||||||
|
child: TMailButtonWidget.fromText(
|
||||||
|
text: AppLocalizations.of(context).cancel,
|
||||||
|
textStyle: Theme.of(context).textTheme.labelSmall?.copyWith(
|
||||||
|
color: Colors.black87,
|
||||||
|
fontSize: 15
|
||||||
|
),
|
||||||
|
padding: const EdgeInsetsDirectional.symmetric(horizontal: 20, vertical: 8),
|
||||||
|
margin: const EdgeInsetsDirectional.only(start: 12, end: 12, bottom: 16),
|
||||||
|
onTapActionCallback: () {
|
||||||
|
_viewStateNotifier.value = Left(SaveTemplateEmailFailure());
|
||||||
|
widget.onCancel!(widget.cancelToken);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String _getStatusMessage(Success success) {
|
||||||
|
if (success is GenerateEmailLoading) {
|
||||||
|
return AppLocalizations.of(context).creatingMessage;
|
||||||
|
} else {
|
||||||
|
return AppLocalizations.of(context).savingMessageToTemplateFolder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_streamSubscription?.cancel();
|
||||||
|
_viewStateNotifier.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,6 +28,7 @@ class BottomBarComposerWidget extends StatelessWidget {
|
|||||||
final VoidCallback toggleRequestReadReceiptAction;
|
final VoidCallback toggleRequestReadReceiptAction;
|
||||||
final VoidCallback printDraftAction;
|
final VoidCallback printDraftAction;
|
||||||
final VoidCallback toggleMarkAsImportantAction;
|
final VoidCallback toggleMarkAsImportantAction;
|
||||||
|
final VoidCallback saveAsTemplateAction;
|
||||||
|
|
||||||
const BottomBarComposerWidget({
|
const BottomBarComposerWidget({
|
||||||
super.key,
|
super.key,
|
||||||
@@ -48,6 +49,7 @@ class BottomBarComposerWidget extends StatelessWidget {
|
|||||||
required this.toggleRequestReadReceiptAction,
|
required this.toggleRequestReadReceiptAction,
|
||||||
required this.printDraftAction,
|
required this.printDraftAction,
|
||||||
required this.toggleMarkAsImportantAction,
|
required this.toggleMarkAsImportantAction,
|
||||||
|
required this.saveAsTemplateAction,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -158,6 +160,17 @@ class BottomBarComposerWidget extends StatelessWidget {
|
|||||||
printDraftAction();
|
printDraftAction();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
PopupItemWidget(
|
||||||
|
iconAction: imagePaths.icSaveToDraft,
|
||||||
|
nameAction: AppLocalizations.of(context).saveAsTemplate,
|
||||||
|
colorIcon: BottomBarComposerWidgetStyle.iconColor,
|
||||||
|
styleName: BottomBarComposerWidgetStyle.popupItemTextStyle,
|
||||||
|
padding: BottomBarComposerWidgetStyle.popupItemPadding,
|
||||||
|
onCallbackAction: () {
|
||||||
|
menuMoreOptionController.hideMenu();
|
||||||
|
saveAsTemplateAction();
|
||||||
|
},
|
||||||
|
),
|
||||||
],
|
],
|
||||||
arrangeAsList: true,
|
arrangeAsList: true,
|
||||||
position: null,
|
position: null,
|
||||||
|
|||||||
+13
@@ -28,6 +28,7 @@ class MobileResponsiveAppBarComposerWidget extends StatelessWidget {
|
|||||||
final VoidCallback toggleRequestReadReceiptAction;
|
final VoidCallback toggleRequestReadReceiptAction;
|
||||||
final VoidCallback printDraftAction;
|
final VoidCallback printDraftAction;
|
||||||
final VoidCallback saveToDraftsAction;
|
final VoidCallback saveToDraftsAction;
|
||||||
|
final VoidCallback saveToTemplateAction;
|
||||||
final VoidCallback deleteComposerAction;
|
final VoidCallback deleteComposerAction;
|
||||||
final VoidCallback toggleMarkAsImportantAction;
|
final VoidCallback toggleMarkAsImportantAction;
|
||||||
|
|
||||||
@@ -50,6 +51,7 @@ class MobileResponsiveAppBarComposerWidget extends StatelessWidget {
|
|||||||
required this.toggleRequestReadReceiptAction,
|
required this.toggleRequestReadReceiptAction,
|
||||||
required this.printDraftAction,
|
required this.printDraftAction,
|
||||||
required this.saveToDraftsAction,
|
required this.saveToDraftsAction,
|
||||||
|
required this.saveToTemplateAction,
|
||||||
required this.deleteComposerAction,
|
required this.deleteComposerAction,
|
||||||
required this.toggleMarkAsImportantAction,
|
required this.toggleMarkAsImportantAction,
|
||||||
});
|
});
|
||||||
@@ -186,6 +188,17 @@ class MobileResponsiveAppBarComposerWidget extends StatelessWidget {
|
|||||||
saveToDraftsAction();
|
saveToDraftsAction();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
PopupItemWidget(
|
||||||
|
iconAction: imagePaths.icSaveToDraft,
|
||||||
|
nameAction: AppLocalizations.of(context).saveAsTemplate,
|
||||||
|
colorIcon: MobileAppBarComposerWidgetStyle.popupItemIconColor,
|
||||||
|
styleName: MobileAppBarComposerWidgetStyle.popupItemTextStyle,
|
||||||
|
padding: MobileAppBarComposerWidgetStyle.popupItemPadding,
|
||||||
|
onCallbackAction: () {
|
||||||
|
menuMoreOptionController.hideMenu();
|
||||||
|
saveToTemplateAction();
|
||||||
|
},
|
||||||
|
),
|
||||||
PopupItemWidget(
|
PopupItemWidget(
|
||||||
iconAction: imagePaths.icDeleteMailbox,
|
iconAction: imagePaths.icDeleteMailbox,
|
||||||
nameAction: AppLocalizations.of(context).delete,
|
nameAction: AppLocalizations.of(context).delete,
|
||||||
|
|||||||
@@ -123,6 +123,24 @@ abstract class EmailDataSource {
|
|||||||
{CancelToken? cancelToken}
|
{CancelToken? cancelToken}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Future<Email> saveEmailAsTemplate(
|
||||||
|
Session session,
|
||||||
|
AccountId accountId,
|
||||||
|
Email email,
|
||||||
|
{
|
||||||
|
CreateNewMailboxRequest? createNewMailboxRequest,
|
||||||
|
CancelToken? cancelToken
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Future<Email> updateEmailTemplate(
|
||||||
|
Session session,
|
||||||
|
AccountId accountId,
|
||||||
|
Email newEmail,
|
||||||
|
EmailId oldEmailId,
|
||||||
|
{CancelToken? cancelToken}
|
||||||
|
);
|
||||||
|
|
||||||
Future<({
|
Future<({
|
||||||
List<EmailId> emailIdsSuccess,
|
List<EmailId> emailIdsSuccess,
|
||||||
Map<Id, SetError> mapErrors,
|
Map<Id, SetError> mapErrors,
|
||||||
|
|||||||
@@ -221,6 +221,46 @@ class EmailDataSourceImpl extends EmailDataSource {
|
|||||||
}).catchError(_exceptionThrower.throwException);
|
}).catchError(_exceptionThrower.throwException);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Email> saveEmailAsTemplate(
|
||||||
|
Session session,
|
||||||
|
AccountId accountId,
|
||||||
|
Email email,
|
||||||
|
{
|
||||||
|
CreateNewMailboxRequest? createNewMailboxRequest,
|
||||||
|
CancelToken? cancelToken
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
return Future.sync(() async {
|
||||||
|
return await emailAPI.saveEmailAsTemplate(
|
||||||
|
session,
|
||||||
|
accountId,
|
||||||
|
email,
|
||||||
|
createNewMailboxRequest: createNewMailboxRequest,
|
||||||
|
cancelToken: cancelToken
|
||||||
|
);
|
||||||
|
}).catchError(_exceptionThrower.throwException);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Email> updateEmailTemplate(
|
||||||
|
Session session,
|
||||||
|
AccountId accountId,
|
||||||
|
Email newEmail,
|
||||||
|
EmailId oldEmailId,
|
||||||
|
{CancelToken? cancelToken}
|
||||||
|
) {
|
||||||
|
return Future.sync(() async {
|
||||||
|
return await emailAPI.updateEmailTemplate(
|
||||||
|
session,
|
||||||
|
accountId,
|
||||||
|
newEmail,
|
||||||
|
oldEmailId,
|
||||||
|
cancelToken: cancelToken
|
||||||
|
);
|
||||||
|
}).catchError(_exceptionThrower.throwException);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Uint8List> downloadAttachmentForWeb(
|
Future<Uint8List> downloadAttachmentForWeb(
|
||||||
DownloadTaskId taskId,
|
DownloadTaskId taskId,
|
||||||
|
|||||||
@@ -275,6 +275,36 @@ class EmailHiveCacheDataSourceImpl extends EmailDataSource {
|
|||||||
return email;
|
return email;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Email> saveEmailAsTemplate(
|
||||||
|
Session session,
|
||||||
|
AccountId accountId,
|
||||||
|
Email email,
|
||||||
|
{
|
||||||
|
CreateNewMailboxRequest? createNewMailboxRequest,
|
||||||
|
CancelToken? cancelToken
|
||||||
|
}
|
||||||
|
) async {
|
||||||
|
await _emailCacheManager.update(accountId, session.username, created: [email]);
|
||||||
|
return email;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Email> updateEmailTemplate(
|
||||||
|
Session session,
|
||||||
|
AccountId accountId,
|
||||||
|
Email newEmail,
|
||||||
|
EmailId oldEmailId,
|
||||||
|
{CancelToken? cancelToken}
|
||||||
|
) async {
|
||||||
|
await _emailCacheManager.update(
|
||||||
|
accountId,
|
||||||
|
session.username,
|
||||||
|
updated: [newEmail],
|
||||||
|
);
|
||||||
|
return newEmail;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<bool> sendEmail(
|
Future<bool> sendEmail(
|
||||||
Session session,
|
Session session,
|
||||||
|
|||||||
@@ -355,4 +355,26 @@ class EmailLocalStorageDataSourceImpl extends EmailDataSource {
|
|||||||
return htmlDocument;
|
return htmlDocument;
|
||||||
}).catchError(_exceptionThrower.throwException);
|
}).catchError(_exceptionThrower.throwException);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Email> saveEmailAsTemplate(
|
||||||
|
Session session,
|
||||||
|
AccountId accountId,
|
||||||
|
Email email, {
|
||||||
|
CreateNewMailboxRequest? createNewMailboxRequest,
|
||||||
|
CancelToken? cancelToken,
|
||||||
|
}) {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Email> updateEmailTemplate(
|
||||||
|
Session session,
|
||||||
|
AccountId accountId,
|
||||||
|
Email newEmail,
|
||||||
|
EmailId oldEmailId, {
|
||||||
|
CancelToken? cancelToken,
|
||||||
|
}) {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -271,4 +271,26 @@ class EmailSessionStorageDatasourceImpl extends EmailDataSource {
|
|||||||
Future<String> generateEntireMessageAsDocument(ViewEntireMessageRequest entireMessageRequest) {
|
Future<String> generateEntireMessageAsDocument(ViewEntireMessageRequest entireMessageRequest) {
|
||||||
throw UnimplementedError();
|
throw UnimplementedError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Email> saveEmailAsTemplate(
|
||||||
|
Session session,
|
||||||
|
AccountId accountId,
|
||||||
|
Email email, {
|
||||||
|
CreateNewMailboxRequest? createNewMailboxRequest,
|
||||||
|
CancelToken? cancelToken,
|
||||||
|
}) {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Email> updateEmailTemplate(
|
||||||
|
Session session,
|
||||||
|
AccountId accountId,
|
||||||
|
Email newEmail,
|
||||||
|
EmailId oldEmailId, {
|
||||||
|
CancelToken? cancelToken,
|
||||||
|
}) {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -656,18 +656,45 @@ class EmailAPI with HandleSetErrorMixin {
|
|||||||
return (emailIdsSuccess: updatedEmailIds, mapErrors: mapErrors);
|
return (emailIdsSuccess: updatedEmailIds, mapErrors: mapErrors);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Email> saveEmailAsDrafts(
|
Future<Email> _emailSetCreateMethod(
|
||||||
Session session,
|
Session session,
|
||||||
AccountId accountId,
|
AccountId accountId,
|
||||||
Email email,
|
Email email,
|
||||||
{CancelToken? cancelToken}
|
{
|
||||||
|
CreateNewMailboxRequest? createNewMailboxRequest,
|
||||||
|
CancelToken? cancelToken
|
||||||
|
}
|
||||||
) async {
|
) async {
|
||||||
|
final requestBuilder = JmapRequestBuilder(_httpClient, ProcessingInvocation());
|
||||||
|
|
||||||
|
MailboxId? mailboxId;
|
||||||
|
if (createNewMailboxRequest != null) {
|
||||||
|
final generateCreateId = Id(_uuid.v1());
|
||||||
|
final setMailboxMethod = SetMailboxMethod(accountId)
|
||||||
|
..addCreate(
|
||||||
|
generateCreateId,
|
||||||
|
Mailbox(
|
||||||
|
name: createNewMailboxRequest.newName,
|
||||||
|
parentId: createNewMailboxRequest.parentId,
|
||||||
|
isSubscribed: IsSubscribed(createNewMailboxRequest.isSubscribed)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
requestBuilder.invocation(setMailboxMethod);
|
||||||
|
|
||||||
|
mailboxId = MailboxId(ReferenceId(
|
||||||
|
ReferencePrefix.defaultPrefix,
|
||||||
|
generateCreateId));
|
||||||
|
} else {
|
||||||
|
mailboxId = email.mailboxIds?.keys.first;
|
||||||
|
}
|
||||||
|
if (mailboxId != null) {
|
||||||
|
email.mailboxIds?.addAll({mailboxId: true});
|
||||||
|
}
|
||||||
final idCreateMethod = Id(_uuid.v1());
|
final idCreateMethod = Id(_uuid.v1());
|
||||||
final setEmailMethod = SetEmailMethod(accountId)
|
final setEmailMethod = SetEmailMethod(accountId)
|
||||||
..addCreate(idCreateMethod, email);
|
..addCreate(idCreateMethod, email);
|
||||||
|
|
||||||
final requestBuilder = JmapRequestBuilder(_httpClient, ProcessingInvocation());
|
|
||||||
|
|
||||||
final setEmailInvocation = requestBuilder.invocation(setEmailMethod);
|
final setEmailInvocation = requestBuilder.invocation(setEmailMethod);
|
||||||
|
|
||||||
final capabilities = setEmailMethod.requiredCapabilities
|
final capabilities = setEmailMethod.requiredCapabilities
|
||||||
@@ -693,7 +720,7 @@ class EmailAPI with HandleSetErrorMixin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> removeEmailDrafts(
|
Future<bool> _emailSetDestroyMethod(
|
||||||
Session session,
|
Session session,
|
||||||
AccountId accountId,
|
AccountId accountId,
|
||||||
EmailId emailId,
|
EmailId emailId,
|
||||||
@@ -728,6 +755,20 @@ class EmailAPI with HandleSetErrorMixin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<Email> saveEmailAsDrafts(
|
||||||
|
Session session,
|
||||||
|
AccountId accountId,
|
||||||
|
Email email,
|
||||||
|
{CancelToken? cancelToken}
|
||||||
|
) => _emailSetCreateMethod(session, accountId, email, cancelToken: cancelToken);
|
||||||
|
|
||||||
|
Future<bool> removeEmailDrafts(
|
||||||
|
Session session,
|
||||||
|
AccountId accountId,
|
||||||
|
EmailId emailId,
|
||||||
|
{CancelToken? cancelToken}
|
||||||
|
) => _emailSetDestroyMethod(session, accountId, emailId, cancelToken: cancelToken);
|
||||||
|
|
||||||
Future<Email> updateEmailDrafts(
|
Future<Email> updateEmailDrafts(
|
||||||
Session session,
|
Session session,
|
||||||
AccountId accountId,
|
AccountId accountId,
|
||||||
@@ -756,6 +797,51 @@ class EmailAPI with HandleSetErrorMixin {
|
|||||||
return emailCreated;
|
return emailCreated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<Email> saveEmailAsTemplate(
|
||||||
|
Session session,
|
||||||
|
AccountId accountId,
|
||||||
|
Email email,
|
||||||
|
{
|
||||||
|
CreateNewMailboxRequest? createNewMailboxRequest,
|
||||||
|
CancelToken? cancelToken
|
||||||
|
}
|
||||||
|
) => _emailSetCreateMethod(session, accountId, email, createNewMailboxRequest: createNewMailboxRequest, cancelToken: cancelToken);
|
||||||
|
|
||||||
|
Future<bool> removeEmailTemplate(
|
||||||
|
Session session,
|
||||||
|
AccountId accountId,
|
||||||
|
EmailId emailId,
|
||||||
|
{CancelToken? cancelToken}
|
||||||
|
) => _emailSetDestroyMethod(session, accountId, emailId, cancelToken: cancelToken);
|
||||||
|
|
||||||
|
Future<Email> updateEmailTemplate(
|
||||||
|
Session session,
|
||||||
|
AccountId accountId,
|
||||||
|
Email newEmail,
|
||||||
|
EmailId oldEmailId,
|
||||||
|
{CancelToken? cancelToken}
|
||||||
|
) async {
|
||||||
|
final emailCreated = await saveEmailAsTemplate(
|
||||||
|
session,
|
||||||
|
accountId,
|
||||||
|
newEmail,
|
||||||
|
cancelToken: cancelToken
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await removeEmailTemplate(
|
||||||
|
session,
|
||||||
|
accountId,
|
||||||
|
oldEmailId,
|
||||||
|
cancelToken: cancelToken
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
logError('EmailAPI::updateEmailTemplate: Exception = $e');
|
||||||
|
}
|
||||||
|
|
||||||
|
return emailCreated;
|
||||||
|
}
|
||||||
|
|
||||||
Future<({
|
Future<({
|
||||||
List<EmailId> emailIdsSuccess,
|
List<EmailId> emailIdsSuccess,
|
||||||
Map<Id, SetError> mapErrors,
|
Map<Id, SetError> mapErrors,
|
||||||
|
|||||||
@@ -336,6 +336,65 @@ class EmailRepositoryImpl extends EmailRepository {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Email> saveEmailAsTemplate(
|
||||||
|
Session session,
|
||||||
|
AccountId accountId,
|
||||||
|
Email email,
|
||||||
|
{
|
||||||
|
CreateNewMailboxRequest? createNewMailboxRequest,
|
||||||
|
CancelToken? cancelToken,
|
||||||
|
}
|
||||||
|
) async {
|
||||||
|
final result = await emailDataSource[DataSourceType.network]!.saveEmailAsTemplate(
|
||||||
|
session,
|
||||||
|
accountId,
|
||||||
|
email,
|
||||||
|
createNewMailboxRequest: createNewMailboxRequest,
|
||||||
|
cancelToken: cancelToken
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
await emailDataSource[DataSourceType.hiveCache]!.saveEmailAsTemplate(
|
||||||
|
session,
|
||||||
|
accountId,
|
||||||
|
result,
|
||||||
|
createNewMailboxRequest: createNewMailboxRequest,
|
||||||
|
cancelToken: cancelToken
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
logError('EmailRepositoryImpl::saveEmailAsTemplate:exception $e');
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Email> updateEmailTemplate(
|
||||||
|
Session session,
|
||||||
|
AccountId accountId,
|
||||||
|
Email newEmail,
|
||||||
|
EmailId oldEmailId,
|
||||||
|
{CancelToken? cancelToken}
|
||||||
|
) async {
|
||||||
|
final result = await emailDataSource[DataSourceType.network]!.updateEmailTemplate(
|
||||||
|
session,
|
||||||
|
accountId,
|
||||||
|
newEmail,
|
||||||
|
oldEmailId,
|
||||||
|
cancelToken: cancelToken
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
await emailDataSource[DataSourceType.hiveCache]!.updateEmailTemplate(
|
||||||
|
session,
|
||||||
|
accountId,
|
||||||
|
result,
|
||||||
|
oldEmailId,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
logError('EmailRepositoryImpl::updateEmailTemplate:exception $e');
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Uint8List> downloadAttachmentForWeb(
|
Future<Uint8List> downloadAttachmentForWeb(
|
||||||
DownloadTaskId taskId,
|
DownloadTaskId taskId,
|
||||||
|
|||||||
@@ -131,6 +131,24 @@ abstract class EmailRepository {
|
|||||||
{CancelToken? cancelToken}
|
{CancelToken? cancelToken}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Future<Email> saveEmailAsTemplate(
|
||||||
|
Session session,
|
||||||
|
AccountId accountId,
|
||||||
|
Email email,
|
||||||
|
{
|
||||||
|
CreateNewMailboxRequest? createNewMailboxRequest,
|
||||||
|
CancelToken? cancelToken
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Future<Email> updateEmailTemplate(
|
||||||
|
Session session,
|
||||||
|
AccountId accountId,
|
||||||
|
Email newEmail,
|
||||||
|
EmailId oldEmailId,
|
||||||
|
{CancelToken? cancelToken}
|
||||||
|
);
|
||||||
|
|
||||||
Future<({
|
Future<({
|
||||||
List<EmailId> emailIdsSuccess,
|
List<EmailId> emailIdsSuccess,
|
||||||
Map<Id, SetError> mapErrors,
|
Map<Id, SetError> mapErrors,
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import 'package:core/presentation/state/failure.dart';
|
||||||
|
import 'package:core/presentation/state/success.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
||||||
|
|
||||||
|
class SavingTemplateEmail extends LoadingState {}
|
||||||
|
|
||||||
|
class SaveTemplateEmailSuccess extends UIState {
|
||||||
|
SaveTemplateEmailSuccess(this.emailId);
|
||||||
|
|
||||||
|
final EmailId emailId;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [emailId];
|
||||||
|
}
|
||||||
|
|
||||||
|
class SaveTemplateEmailFailure extends FeatureFailure {
|
||||||
|
SaveTemplateEmailFailure({super.exception});
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import 'package:core/presentation/state/failure.dart';
|
||||||
|
import 'package:core/presentation/state/success.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
||||||
|
|
||||||
|
class UpdatingTemplateEmail extends LoadingState {}
|
||||||
|
|
||||||
|
class UpdateTemplateEmailSuccess extends UIState {
|
||||||
|
UpdateTemplateEmailSuccess(this.emailId);
|
||||||
|
|
||||||
|
final EmailId emailId;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [emailId];
|
||||||
|
}
|
||||||
|
|
||||||
|
class UpdateTemplateEmailFailure extends FeatureFailure {
|
||||||
|
UpdateTemplateEmailFailure({super.exception});
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
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:dio/dio.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
||||||
|
import 'package:tmail_ui_user/features/composer/domain/repository/composer_repository.dart';
|
||||||
|
import 'package:tmail_ui_user/features/composer/domain/state/generate_email_state.dart';
|
||||||
|
import 'package:tmail_ui_user/features/composer/presentation/model/create_email_request.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/save_template_email_state.dart';
|
||||||
|
import 'package:tmail_ui_user/features/email/domain/state/update_template_email_state.dart';
|
||||||
|
import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart';
|
||||||
|
|
||||||
|
class SaveTemplateEmailInteractor {
|
||||||
|
const SaveTemplateEmailInteractor(
|
||||||
|
this._composerRepository,
|
||||||
|
this._emailRepository,
|
||||||
|
);
|
||||||
|
|
||||||
|
final ComposerRepository _composerRepository;
|
||||||
|
final EmailRepository _emailRepository;
|
||||||
|
|
||||||
|
Stream<Either<Failure, Success>> execute({
|
||||||
|
required CreateEmailRequest createEmailRequest,
|
||||||
|
required CreateNewMailboxRequest? createNewMailboxRequest,
|
||||||
|
CancelToken? cancelToken,
|
||||||
|
}) async* {
|
||||||
|
yield Right(GenerateEmailLoading());
|
||||||
|
|
||||||
|
try {
|
||||||
|
final emailCreated = await _createEmailObject(createEmailRequest);
|
||||||
|
|
||||||
|
if (emailCreated == null) {
|
||||||
|
yield Left(GenerateEmailFailure(CannotCreateEmailObjectException()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (createEmailRequest.templateEmailId != null) {
|
||||||
|
yield Right(UpdatingTemplateEmail());
|
||||||
|
final emailTemplateSaved = await _emailRepository.updateEmailTemplate(
|
||||||
|
createEmailRequest.session,
|
||||||
|
createEmailRequest.accountId,
|
||||||
|
emailCreated,
|
||||||
|
createEmailRequest.templateEmailId!,
|
||||||
|
cancelToken: cancelToken,
|
||||||
|
);
|
||||||
|
yield Right(UpdateTemplateEmailSuccess(emailTemplateSaved.id!));
|
||||||
|
} else {
|
||||||
|
yield Right(SavingTemplateEmail());
|
||||||
|
final emailTemplateSaved = await _emailRepository.saveEmailAsTemplate(
|
||||||
|
createEmailRequest.session,
|
||||||
|
createEmailRequest.accountId,
|
||||||
|
emailCreated,
|
||||||
|
createNewMailboxRequest: createNewMailboxRequest,
|
||||||
|
cancelToken: cancelToken,
|
||||||
|
);
|
||||||
|
yield Right(SaveTemplateEmailSuccess(emailTemplateSaved.id!));
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
logError('SaveTemplateEmailInteractor::execute(): $e');
|
||||||
|
if (createEmailRequest.templateEmailId != null) {
|
||||||
|
yield Left(UpdateTemplateEmailFailure(exception: e));
|
||||||
|
} else {
|
||||||
|
yield Left(SaveTemplateEmailFailure(exception: e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Email?> _createEmailObject(CreateEmailRequest createEmailRequest) async {
|
||||||
|
try {
|
||||||
|
final emailCreated = await _composerRepository.generateEmail(
|
||||||
|
createEmailRequest,
|
||||||
|
withIdentityHeader: true,
|
||||||
|
isTemplate: true);
|
||||||
|
return emailCreated;
|
||||||
|
} catch (e) {
|
||||||
|
logError('CreateNewAndSaveEmailToDraftsInteractor::_createEmailObject: Exception: $e');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -548,7 +548,8 @@ class EmailView extends GetWidget<SingleEmailController> {
|
|||||||
EmailActionType.archiveMessage,
|
EmailActionType.archiveMessage,
|
||||||
if (PlatformInfo.isWeb && PlatformInfo.isCanvasKit)
|
if (PlatformInfo.isWeb && PlatformInfo.isCanvasKit)
|
||||||
EmailActionType.downloadMessageAsEML,
|
EmailActionType.downloadMessageAsEML,
|
||||||
EmailActionType.editAsNewEmail,
|
if (mailboxContain?.isTemplates == false)
|
||||||
|
EmailActionType.editAsNewEmail,
|
||||||
];
|
];
|
||||||
|
|
||||||
if (position == null) {
|
if (position == null) {
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ class ComposerArguments extends RouterArguments {
|
|||||||
final int? savedDraftHash;
|
final int? savedDraftHash;
|
||||||
final EmailActionType? savedActionType;
|
final EmailActionType? savedActionType;
|
||||||
final EmailId? savedEmailDraftId;
|
final EmailId? savedEmailDraftId;
|
||||||
|
final EmailId? savedEmailTemplateId;
|
||||||
|
|
||||||
ComposerArguments({
|
ComposerArguments({
|
||||||
this.emailActionType = EmailActionType.compose,
|
this.emailActionType = EmailActionType.compose,
|
||||||
@@ -66,6 +67,7 @@ class ComposerArguments extends RouterArguments {
|
|||||||
this.savedDraftHash,
|
this.savedDraftHash,
|
||||||
this.savedActionType,
|
this.savedActionType,
|
||||||
this.savedEmailDraftId,
|
this.savedEmailDraftId,
|
||||||
|
this.savedEmailTemplateId,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory ComposerArguments.fromSendingEmail(SendingEmail sendingEmail) =>
|
factory ComposerArguments.fromSendingEmail(SendingEmail sendingEmail) =>
|
||||||
@@ -113,11 +115,14 @@ class ComposerArguments extends RouterArguments {
|
|||||||
presentationEmail: presentationEmail,
|
presentationEmail: presentationEmail,
|
||||||
);
|
);
|
||||||
|
|
||||||
factory ComposerArguments.editAsNewEmail(PresentationEmail presentationEmail) =>
|
factory ComposerArguments.editAsNewEmail(
|
||||||
ComposerArguments(
|
PresentationEmail presentationEmail, {
|
||||||
emailActionType: EmailActionType.editAsNewEmail,
|
EmailId? savedEmailTemplateId
|
||||||
presentationEmail: presentationEmail,
|
}) => ComposerArguments(
|
||||||
);
|
emailActionType: EmailActionType.editAsNewEmail,
|
||||||
|
presentationEmail: presentationEmail,
|
||||||
|
savedEmailTemplateId: savedEmailTemplateId,
|
||||||
|
);
|
||||||
|
|
||||||
factory ComposerArguments.fromSessionStorageBrowser(ComposerCache composerCache) =>
|
factory ComposerArguments.fromSessionStorageBrowser(ComposerCache composerCache) =>
|
||||||
ComposerArguments(
|
ComposerArguments(
|
||||||
@@ -134,6 +139,7 @@ class ComposerArguments extends RouterArguments {
|
|||||||
savedDraftHash: composerCache.draftHash,
|
savedDraftHash: composerCache.draftHash,
|
||||||
savedActionType: composerCache.actionType,
|
savedActionType: composerCache.actionType,
|
||||||
savedEmailDraftId: composerCache.draftEmailId,
|
savedEmailDraftId: composerCache.draftEmailId,
|
||||||
|
savedEmailTemplateId: composerCache.templateEmailId,
|
||||||
);
|
);
|
||||||
|
|
||||||
factory ComposerArguments.replyEmail({
|
factory ComposerArguments.replyEmail({
|
||||||
@@ -258,6 +264,7 @@ class ComposerArguments extends RouterArguments {
|
|||||||
savedDraftHash,
|
savedDraftHash,
|
||||||
savedActionType,
|
savedActionType,
|
||||||
savedEmailDraftId,
|
savedEmailDraftId,
|
||||||
|
sendingEmailActionType,
|
||||||
];
|
];
|
||||||
|
|
||||||
ComposerArguments copyWith({
|
ComposerArguments copyWith({
|
||||||
@@ -287,6 +294,7 @@ class ComposerArguments extends RouterArguments {
|
|||||||
int? savedDraftHash,
|
int? savedDraftHash,
|
||||||
EmailActionType? savedActionType,
|
EmailActionType? savedActionType,
|
||||||
EmailId? savedEmailDraftId,
|
EmailId? savedEmailDraftId,
|
||||||
|
EmailId? savedEmailTemplateId,
|
||||||
}) {
|
}) {
|
||||||
return ComposerArguments(
|
return ComposerArguments(
|
||||||
emailActionType: emailActionType ?? this.emailActionType,
|
emailActionType: emailActionType ?? this.emailActionType,
|
||||||
@@ -315,6 +323,7 @@ class ComposerArguments extends RouterArguments {
|
|||||||
savedDraftHash: savedDraftHash ?? this.savedDraftHash,
|
savedDraftHash: savedDraftHash ?? this.savedDraftHash,
|
||||||
savedActionType: savedActionType ?? this.savedActionType,
|
savedActionType: savedActionType ?? this.savedActionType,
|
||||||
savedEmailDraftId: savedEmailDraftId ?? this.savedEmailDraftId,
|
savedEmailDraftId: savedEmailDraftId ?? this.savedEmailDraftId,
|
||||||
|
savedEmailTemplateId: savedEmailTemplateId ?? this.savedEmailTemplateId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ class ComposerCache with EquatableMixin {
|
|||||||
final int? draftHash;
|
final int? draftHash;
|
||||||
final EmailActionType? actionType;
|
final EmailActionType? actionType;
|
||||||
final EmailId? draftEmailId;
|
final EmailId? draftEmailId;
|
||||||
|
final EmailId? templateEmailId;
|
||||||
|
|
||||||
ComposerCache({
|
ComposerCache({
|
||||||
this.email,
|
this.email,
|
||||||
@@ -36,6 +37,7 @@ class ComposerCache with EquatableMixin {
|
|||||||
this.draftHash,
|
this.draftHash,
|
||||||
this.actionType,
|
this.actionType,
|
||||||
this.draftEmailId,
|
this.draftEmailId,
|
||||||
|
this.templateEmailId,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory ComposerCache.fromJson(Map<String, dynamic> json) => _$ComposerCacheFromJson(json);
|
factory ComposerCache.fromJson(Map<String, dynamic> json) => _$ComposerCacheFromJson(json);
|
||||||
@@ -53,5 +55,6 @@ class ComposerCache with EquatableMixin {
|
|||||||
draftHash,
|
draftHash,
|
||||||
actionType,
|
actionType,
|
||||||
draftEmailId,
|
draftEmailId,
|
||||||
|
templateEmailId,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,9 +43,15 @@ mixin EmailActionController {
|
|||||||
mailboxDashBoardController.openComposer(ComposerArguments.editDraftEmail(presentationEmail));
|
mailboxDashBoardController.openComposer(ComposerArguments.editDraftEmail(presentationEmail));
|
||||||
}
|
}
|
||||||
|
|
||||||
void editAsNewEmail(PresentationEmail presentationEmail) {
|
void editAsNewEmail(
|
||||||
|
PresentationEmail presentationEmail, {
|
||||||
|
EmailId? savedEmailTemplateId,
|
||||||
|
}) {
|
||||||
mailboxDashBoardController.openComposer(
|
mailboxDashBoardController.openComposer(
|
||||||
ComposerArguments.editAsNewEmail(presentationEmail),
|
ComposerArguments.editAsNewEmail(
|
||||||
|
presentationEmail,
|
||||||
|
savedEmailTemplateId: savedEmailTemplateId,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1176,7 +1176,7 @@ class ThreadController extends BaseController with EmailActionController {
|
|||||||
if (mailboxContain?.isDrafts == true) {
|
if (mailboxContain?.isDrafts == true) {
|
||||||
editDraftEmail(selectedEmail);
|
editDraftEmail(selectedEmail);
|
||||||
} else if (mailboxContain?.isTemplates == true) {
|
} else if (mailboxContain?.isTemplates == true) {
|
||||||
editAsNewEmail(selectedEmail);
|
editAsNewEmail(selectedEmail, savedEmailTemplateId: selectedEmail.id);
|
||||||
} else {
|
} else {
|
||||||
previewEmail(selectedEmail);
|
previewEmail(selectedEmail);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -724,7 +724,7 @@ class ThreadView extends GetWidget<ThreadController>
|
|||||||
_markAsEmailSpamOrUnSpamContextMenuItemAction(context, email, mailboxContain),
|
_markAsEmailSpamOrUnSpamContextMenuItemAction(context, email, mailboxContain),
|
||||||
if (mailboxContain?.isArchive == false)
|
if (mailboxContain?.isArchive == false)
|
||||||
_archiveMessageContextMenuItemAction(context, email),
|
_archiveMessageContextMenuItemAction(context, email),
|
||||||
if (mailboxContain?.isDrafts == false)
|
if (mailboxContain?.isDrafts == false && mailboxContain?.isTemplates == false)
|
||||||
_editAsNewEmailContextMenuItemAction(context, email),
|
_editAsNewEmailContextMenuItemAction(context, email),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -846,7 +846,7 @@ class ThreadView extends GetWidget<ThreadController>
|
|||||||
_buildMarkAsSpamPopupMenuItem(context, email, mailboxContain),
|
_buildMarkAsSpamPopupMenuItem(context, email, mailboxContain),
|
||||||
if (mailboxContain?.isArchive == false)
|
if (mailboxContain?.isArchive == false)
|
||||||
_buildArchiveMessagePopupMenuItem(context, email),
|
_buildArchiveMessagePopupMenuItem(context, email),
|
||||||
if (mailboxContain?.isDrafts == false)
|
if (mailboxContain?.isDrafts == false && mailboxContain?.isTemplates == false)
|
||||||
_buildEditAsNewEmailPopupMenuItem(AppLocalizations.of(context), email),
|
_buildEditAsNewEmailPopupMenuItem(AppLocalizations.of(context), email),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4417,5 +4417,57 @@
|
|||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders_order": [],
|
"placeholders_order": [],
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"saveAsTemplate": "Save as template",
|
||||||
|
"@saveAsTemplate": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders_order": [],
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"savingTemplate": "Saving template",
|
||||||
|
"@savingTemplate": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders_order": [],
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"savingMessageToTemplateFolder": "Saving message to template folder",
|
||||||
|
"@savingMessageToTemplateFolder": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders_order": [],
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"saveMessageToTemplateSuccess": "Save message to template folder successfully",
|
||||||
|
"@saveMessageToTemplateSuccess": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders_order": [],
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"updateMessageToTemplateSuccess": "Update message to template folder successfully",
|
||||||
|
"@updateMessageToTemplateSuccess": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders_order": [],
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"saveMessageToTemplateFailed": "Save message to template folder failed",
|
||||||
|
"@saveMessageToTemplateFailed": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders_order": [],
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"saveMessageToTemplateCancelled": "Save message to template folder has been cancelled",
|
||||||
|
"@saveMessageToTemplateCancelled": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders_order": [],
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"invalidArguments": "Invalid arguments: \"{description}\"",
|
||||||
|
"@invalidArguments": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders_order": [
|
||||||
|
"description"
|
||||||
|
],
|
||||||
|
"placeholders": {
|
||||||
|
"description": {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4645,4 +4645,61 @@ class AppLocalizations {
|
|||||||
name: 'pullHarderFor',
|
name: 'pullHarderFor',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String get saveAsTemplate {
|
||||||
|
return Intl.message(
|
||||||
|
'Save as template',
|
||||||
|
name: 'saveAsTemplate',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String get savingTemplate {
|
||||||
|
return Intl.message(
|
||||||
|
'Saving template',
|
||||||
|
name: 'savingTemplate',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String get savingMessageToTemplateFolder {
|
||||||
|
return Intl.message(
|
||||||
|
'Saving message to template folder',
|
||||||
|
name: 'savingMessageToTemplateFolder',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String get saveMessageToTemplateSuccess {
|
||||||
|
return Intl.message(
|
||||||
|
'Save message to template folder successfully',
|
||||||
|
name: 'saveMessageToTemplateSuccess',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String get updateMessageToTemplateSuccess {
|
||||||
|
return Intl.message(
|
||||||
|
'Update message to template folder successfully',
|
||||||
|
name: 'updateMessageToTemplateSuccess',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String get saveMessageToTemplateFailed {
|
||||||
|
return Intl.message(
|
||||||
|
'Save message to template folder failed',
|
||||||
|
name: 'saveMessageToTemplateFailed',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String get saveMessageToTemplateCancelled {
|
||||||
|
return Intl.message(
|
||||||
|
'Save message to template folder has been cancelled',
|
||||||
|
name: 'saveMessageToTemplateCancelled',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String invalidArguments(String description) {
|
||||||
|
return Intl.message(
|
||||||
|
'Invalid arguments: "$description"',
|
||||||
|
name: 'invalidArguments',
|
||||||
|
args: [description],
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ void main() {
|
|||||||
when(mockCreateEmailRequest.emailActionType).thenReturn(EmailActionType.compose);
|
when(mockCreateEmailRequest.emailActionType).thenReturn(EmailActionType.compose);
|
||||||
when(mockCreateEmailRequest.hasRequestReadReceipt).thenReturn(false);
|
when(mockCreateEmailRequest.hasRequestReadReceipt).thenReturn(false);
|
||||||
when(mockCreateEmailRequest.isMarkAsImportant).thenReturn(false);
|
when(mockCreateEmailRequest.isMarkAsImportant).thenReturn(false);
|
||||||
|
when(mockCreateEmailRequest.templateMailboxId).thenReturn(null);
|
||||||
|
|
||||||
when(mockHtmlDataSource.replaceImageBase64ToImageCID(
|
when(mockHtmlDataSource.replaceImageBase64ToImageCID(
|
||||||
emailContent: emailContentWithBase64,
|
emailContent: emailContentWithBase64,
|
||||||
@@ -160,6 +161,7 @@ void main() {
|
|||||||
when(mockCreateEmailRequest.emailActionType).thenReturn(EmailActionType.compose);
|
when(mockCreateEmailRequest.emailActionType).thenReturn(EmailActionType.compose);
|
||||||
when(mockCreateEmailRequest.hasRequestReadReceipt).thenReturn(false);
|
when(mockCreateEmailRequest.hasRequestReadReceipt).thenReturn(false);
|
||||||
when(mockCreateEmailRequest.isMarkAsImportant).thenReturn(false);
|
when(mockCreateEmailRequest.isMarkAsImportant).thenReturn(false);
|
||||||
|
when(mockCreateEmailRequest.templateMailboxId).thenReturn(null);
|
||||||
|
|
||||||
when(mockHtmlDataSource.replaceImageBase64ToImageCID(
|
when(mockHtmlDataSource.replaceImageBase64ToImageCID(
|
||||||
emailContent: emailContentWithBase64,
|
emailContent: emailContentWithBase64,
|
||||||
@@ -214,6 +216,7 @@ void main() {
|
|||||||
when(mockCreateEmailRequest.emailActionType).thenReturn(EmailActionType.compose);
|
when(mockCreateEmailRequest.emailActionType).thenReturn(EmailActionType.compose);
|
||||||
when(mockCreateEmailRequest.hasRequestReadReceipt).thenReturn(false);
|
when(mockCreateEmailRequest.hasRequestReadReceipt).thenReturn(false);
|
||||||
when(mockCreateEmailRequest.isMarkAsImportant).thenReturn(false);
|
when(mockCreateEmailRequest.isMarkAsImportant).thenReturn(false);
|
||||||
|
when(mockCreateEmailRequest.templateMailboxId).thenReturn(null);
|
||||||
|
|
||||||
when(mockHtmlDataSource.replaceImageBase64ToImageCID(
|
when(mockHtmlDataSource.replaceImageBase64ToImageCID(
|
||||||
emailContent: plainEmailContent,
|
emailContent: plainEmailContent,
|
||||||
|
|||||||
@@ -39,10 +39,11 @@ import 'package:tmail_ui_user/features/composer/presentation/controller/rich_tex
|
|||||||
import 'package:tmail_ui_user/features/composer/presentation/controller/rich_text_web_controller.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/controller/rich_text_web_controller.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/extensions/setup_selected_identity_extension.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/extensions/setup_selected_identity_extension.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/model/formatting_options_state.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/model/formatting_options_state.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/model/saved_email_draft.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/model/saved_composing_email.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/model/screen_display_mode.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/model/screen_display_mode.dart';
|
||||||
import 'package:tmail_ui_user/features/email/domain/usecases/get_email_content_interactor.dart';
|
import 'package:tmail_ui_user/features/email/domain/usecases/get_email_content_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/email/domain/usecases/print_email_interactor.dart';
|
import 'package:tmail_ui_user/features/email/domain/usecases/print_email_interactor.dart';
|
||||||
|
import 'package:tmail_ui_user/features/email/domain/usecases/save_template_email_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/email/domain/usecases/transform_html_email_content_interactor.dart';
|
import 'package:tmail_ui_user/features/email/domain/usecases/transform_html_email_content_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart';
|
import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart';
|
||||||
import 'package:tmail_ui_user/features/login/data/network/interceptors/authorization_interceptors.dart';
|
import 'package:tmail_ui_user/features/login/data/network/interceptors/authorization_interceptors.dart';
|
||||||
@@ -160,6 +161,7 @@ class MockMailboxDashBoardController extends Mock implements MailboxDashBoardCon
|
|||||||
MockSpec<CreateNewAndSaveEmailToDraftsInteractor>(),
|
MockSpec<CreateNewAndSaveEmailToDraftsInteractor>(),
|
||||||
MockSpec<PrintEmailInteractor>(),
|
MockSpec<PrintEmailInteractor>(),
|
||||||
MockSpec<ComposerRepository>(),
|
MockSpec<ComposerRepository>(),
|
||||||
|
MockSpec<SaveTemplateEmailInteractor>(),
|
||||||
|
|
||||||
// Additional Getx dependencies mock specs
|
// Additional Getx dependencies mock specs
|
||||||
MockSpec<NetworkConnectionController>(fallbackGenerators: fallbackGenerators),
|
MockSpec<NetworkConnectionController>(fallbackGenerators: fallbackGenerators),
|
||||||
@@ -204,6 +206,7 @@ void main() {
|
|||||||
late MockCreateNewAndSaveEmailToDraftsInteractor mockCreateNewAndSaveEmailToDraftsInteractor;
|
late MockCreateNewAndSaveEmailToDraftsInteractor mockCreateNewAndSaveEmailToDraftsInteractor;
|
||||||
late MockPrintEmailInteractor mockPrintEmailInteractor;
|
late MockPrintEmailInteractor mockPrintEmailInteractor;
|
||||||
late MockComposerRepository mockComposerRepository;
|
late MockComposerRepository mockComposerRepository;
|
||||||
|
late MockSaveTemplateEmailInteractor mockSaveTemplateEmailInteractor;
|
||||||
|
|
||||||
// Declaration Getx dependencies
|
// Declaration Getx dependencies
|
||||||
final mockMailboxDashBoardController = MockMailboxDashBoardController();
|
final mockMailboxDashBoardController = MockMailboxDashBoardController();
|
||||||
@@ -273,6 +276,7 @@ void main() {
|
|||||||
mockCreateNewAndSaveEmailToDraftsInteractor = MockCreateNewAndSaveEmailToDraftsInteractor();
|
mockCreateNewAndSaveEmailToDraftsInteractor = MockCreateNewAndSaveEmailToDraftsInteractor();
|
||||||
mockPrintEmailInteractor = MockPrintEmailInteractor();
|
mockPrintEmailInteractor = MockPrintEmailInteractor();
|
||||||
mockComposerRepository = MockComposerRepository();
|
mockComposerRepository = MockComposerRepository();
|
||||||
|
mockSaveTemplateEmailInteractor = MockSaveTemplateEmailInteractor();
|
||||||
|
|
||||||
composerController = ComposerController(
|
composerController = ComposerController(
|
||||||
mockLocalFilePickerInteractor,
|
mockLocalFilePickerInteractor,
|
||||||
@@ -289,6 +293,7 @@ void main() {
|
|||||||
mockCreateNewAndSaveEmailToDraftsInteractor,
|
mockCreateNewAndSaveEmailToDraftsInteractor,
|
||||||
mockPrintEmailInteractor,
|
mockPrintEmailInteractor,
|
||||||
mockComposerRepository,
|
mockComposerRepository,
|
||||||
|
mockSaveTemplateEmailInteractor,
|
||||||
);
|
);
|
||||||
|
|
||||||
mockHtmlEditorApi = MockHtmlEditorApi();
|
mockHtmlEditorApi = MockHtmlEditorApi();
|
||||||
@@ -344,7 +349,7 @@ void main() {
|
|||||||
emailContent: anyNamed('emailContent'),
|
emailContent: anyNamed('emailContent'),
|
||||||
)).thenAnswer((_) async => emailContent);
|
)).thenAnswer((_) async => emailContent);
|
||||||
|
|
||||||
final savedEmailDraft = SavedEmailDraft(
|
final savedEmailDraft = SavedComposingEmail(
|
||||||
content: emailContent,
|
content: emailContent,
|
||||||
subject: emailSubject,
|
subject: emailSubject,
|
||||||
toRecipients: {toRecipient},
|
toRecipients: {toRecipient},
|
||||||
@@ -404,7 +409,7 @@ void main() {
|
|||||||
emailContent: anyNamed('emailContent'),
|
emailContent: anyNamed('emailContent'),
|
||||||
)).thenAnswer((_) async => emailContent);
|
)).thenAnswer((_) async => emailContent);
|
||||||
|
|
||||||
final savedEmailDraft = SavedEmailDraft(
|
final savedEmailDraft = SavedComposingEmail(
|
||||||
content: emailContent,
|
content: emailContent,
|
||||||
subject: emailSubject,
|
subject: emailSubject,
|
||||||
toRecipients: {toRecipient},
|
toRecipients: {toRecipient},
|
||||||
@@ -471,7 +476,7 @@ void main() {
|
|||||||
.thenAnswer((_) => Stream.value(
|
.thenAnswer((_) => Stream.value(
|
||||||
Right(SaveEmailAsDraftsSuccess(EmailId(Id('123')), null))));
|
Right(SaveEmailAsDraftsSuccess(EmailId(Id('123')), null))));
|
||||||
|
|
||||||
final savedEmailDraft = SavedEmailDraft(
|
final savedEmailDraft = SavedComposingEmail(
|
||||||
content: emailContent,
|
content: emailContent,
|
||||||
subject: emailSubject,
|
subject: emailSubject,
|
||||||
toRecipients: {toRecipient},
|
toRecipients: {toRecipient},
|
||||||
@@ -551,7 +556,7 @@ void main() {
|
|||||||
.thenAnswer((_) => Stream.value(
|
.thenAnswer((_) => Stream.value(
|
||||||
Right(UpdateEmailDraftsSuccess(EmailId(Id('123'))))));
|
Right(UpdateEmailDraftsSuccess(EmailId(Id('123'))))));
|
||||||
|
|
||||||
final savedEmailDraft = SavedEmailDraft(
|
final savedEmailDraft = SavedComposingEmail(
|
||||||
content: emailContent,
|
content: emailContent,
|
||||||
subject: emailSubject,
|
subject: emailSubject,
|
||||||
toRecipients: {toRecipient},
|
toRecipients: {toRecipient},
|
||||||
@@ -626,7 +631,7 @@ void main() {
|
|||||||
emailContent: anyNamed('emailContent'),
|
emailContent: anyNamed('emailContent'),
|
||||||
)).thenAnswer((_) async => emailContent);
|
)).thenAnswer((_) async => emailContent);
|
||||||
|
|
||||||
final savedEmailDraft = SavedEmailDraft(
|
final savedEmailDraft = SavedComposingEmail(
|
||||||
content: emailContent,
|
content: emailContent,
|
||||||
subject: emailSubject,
|
subject: emailSubject,
|
||||||
toRecipients: {toRecipient},
|
toRecipients: {toRecipient},
|
||||||
@@ -686,7 +691,7 @@ void main() {
|
|||||||
emailContent: anyNamed('emailContent'),
|
emailContent: anyNamed('emailContent'),
|
||||||
)).thenAnswer((_) async => emailContent);
|
)).thenAnswer((_) async => emailContent);
|
||||||
|
|
||||||
final savedEmailDraft = SavedEmailDraft(
|
final savedEmailDraft = SavedComposingEmail(
|
||||||
content: emailContent,
|
content: emailContent,
|
||||||
subject: emailSubject,
|
subject: emailSubject,
|
||||||
toRecipients: {toRecipient},
|
toRecipients: {toRecipient},
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import 'package:flutter_test/flutter_test.dart';
|
|||||||
import 'package:jmap_dart_client/jmap/identities/identity.dart';
|
import 'package:jmap_dart_client/jmap/identities/identity.dart';
|
||||||
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
|
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
|
||||||
import 'package:model/email/attachment.dart';
|
import 'package:model/email/attachment.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/model/saved_email_draft.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/model/saved_composing_email.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
group('saved email draft test', () {
|
group('saved email draft test', () {
|
||||||
@@ -10,7 +10,7 @@ void main() {
|
|||||||
'should tag toRecipients, ccRecipients and bccRecipients in props',
|
'should tag toRecipients, ccRecipients and bccRecipients in props',
|
||||||
() {
|
() {
|
||||||
// arrange
|
// arrange
|
||||||
final savedEmailDraft = SavedEmailDraft(
|
final savedEmailDraft = SavedComposingEmail(
|
||||||
subject: 'subject',
|
subject: 'subject',
|
||||||
content: 'content',
|
content: 'content',
|
||||||
toRecipients: {EmailAddress('to name', 'to email')},
|
toRecipients: {EmailAddress('to name', 'to email')},
|
||||||
@@ -43,7 +43,7 @@ void main() {
|
|||||||
final attachments = <Attachment>[];
|
final attachments = <Attachment>[];
|
||||||
const hasReadReceipt = false;
|
const hasReadReceipt = false;
|
||||||
|
|
||||||
final toSavedEmailDraft = SavedEmailDraft(
|
final toSavedEmailDraft = SavedComposingEmail(
|
||||||
subject: subject,
|
subject: subject,
|
||||||
content: content,
|
content: content,
|
||||||
toRecipients: {recipient},
|
toRecipients: {recipient},
|
||||||
@@ -55,7 +55,7 @@ void main() {
|
|||||||
hasReadReceipt: hasReadReceipt
|
hasReadReceipt: hasReadReceipt
|
||||||
);
|
);
|
||||||
|
|
||||||
final ccSavedEmailDraft = SavedEmailDraft(
|
final ccSavedEmailDraft = SavedComposingEmail(
|
||||||
subject: subject,
|
subject: subject,
|
||||||
content: content,
|
content: content,
|
||||||
toRecipients: {},
|
toRecipients: {},
|
||||||
@@ -67,7 +67,7 @@ void main() {
|
|||||||
hasReadReceipt: hasReadReceipt
|
hasReadReceipt: hasReadReceipt
|
||||||
);
|
);
|
||||||
|
|
||||||
final bccSavedEmailDraft = SavedEmailDraft(
|
final bccSavedEmailDraft = SavedComposingEmail(
|
||||||
subject: subject,
|
subject: subject,
|
||||||
content: content,
|
content: content,
|
||||||
toRecipients: {},
|
toRecipients: {},
|
||||||
@@ -79,7 +79,7 @@ void main() {
|
|||||||
hasReadReceipt: hasReadReceipt
|
hasReadReceipt: hasReadReceipt
|
||||||
);
|
);
|
||||||
|
|
||||||
final replyToSavedEmailDraft = SavedEmailDraft(
|
final replyToSavedEmailDraft = SavedComposingEmail(
|
||||||
subject: subject,
|
subject: subject,
|
||||||
content: content,
|
content: content,
|
||||||
toRecipients: {},
|
toRecipients: {},
|
||||||
@@ -112,7 +112,7 @@ void main() {
|
|||||||
final listToRecipients = {
|
final listToRecipients = {
|
||||||
EmailAddress('to name', 'to email')
|
EmailAddress('to name', 'to email')
|
||||||
};
|
};
|
||||||
final savedEmailDraft = SavedEmailDraft(
|
final savedEmailDraft = SavedComposingEmail(
|
||||||
subject: 'subject',
|
subject: 'subject',
|
||||||
content: 'content',
|
content: 'content',
|
||||||
toRecipients: listToRecipients,
|
toRecipients: listToRecipients,
|
||||||
@@ -138,7 +138,7 @@ void main() {
|
|||||||
'when all properties are the same',
|
'when all properties are the same',
|
||||||
() {
|
() {
|
||||||
// arrange
|
// arrange
|
||||||
final savedEmailDraft = SavedEmailDraft(
|
final savedEmailDraft = SavedComposingEmail(
|
||||||
subject: 'subject',
|
subject: 'subject',
|
||||||
content: 'content',
|
content: 'content',
|
||||||
toRecipients: {EmailAddress('to name', 'to email')},
|
toRecipients: {EmailAddress('to name', 'to email')},
|
||||||
@@ -150,7 +150,7 @@ void main() {
|
|||||||
hasReadReceipt: false
|
hasReadReceipt: false
|
||||||
);
|
);
|
||||||
|
|
||||||
final savedEmailDraft2 = SavedEmailDraft(
|
final savedEmailDraft2 = SavedComposingEmail(
|
||||||
subject: 'subject',
|
subject: 'subject',
|
||||||
content: 'content',
|
content: 'content',
|
||||||
toRecipients: {EmailAddress('to name', 'to email')},
|
toRecipients: {EmailAddress('to name', 'to email')},
|
||||||
|
|||||||
Reference in New Issue
Block a user