TF-3515 Add save email as template feature
This commit is contained in:
@@ -123,6 +123,24 @@ abstract class EmailDataSource {
|
||||
{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<({
|
||||
List<EmailId> emailIdsSuccess,
|
||||
Map<Id, SetError> mapErrors,
|
||||
|
||||
@@ -221,6 +221,46 @@ class EmailDataSourceImpl extends EmailDataSource {
|
||||
}).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
|
||||
Future<Uint8List> downloadAttachmentForWeb(
|
||||
DownloadTaskId taskId,
|
||||
|
||||
@@ -275,6 +275,36 @@ class EmailHiveCacheDataSourceImpl extends EmailDataSource {
|
||||
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
|
||||
Future<bool> sendEmail(
|
||||
Session session,
|
||||
|
||||
@@ -355,4 +355,26 @@ class EmailLocalStorageDataSourceImpl extends EmailDataSource {
|
||||
return htmlDocument;
|
||||
}).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) {
|
||||
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);
|
||||
}
|
||||
|
||||
Future<Email> saveEmailAsDrafts(
|
||||
Future<Email> _emailSetCreateMethod(
|
||||
Session session,
|
||||
AccountId accountId,
|
||||
Email email,
|
||||
{CancelToken? cancelToken}
|
||||
{
|
||||
CreateNewMailboxRequest? createNewMailboxRequest,
|
||||
CancelToken? cancelToken
|
||||
}
|
||||
) 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 setEmailMethod = SetEmailMethod(accountId)
|
||||
..addCreate(idCreateMethod, email);
|
||||
|
||||
final requestBuilder = JmapRequestBuilder(_httpClient, ProcessingInvocation());
|
||||
|
||||
final setEmailInvocation = requestBuilder.invocation(setEmailMethod);
|
||||
|
||||
final capabilities = setEmailMethod.requiredCapabilities
|
||||
@@ -693,7 +720,7 @@ class EmailAPI with HandleSetErrorMixin {
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> removeEmailDrafts(
|
||||
Future<bool> _emailSetDestroyMethod(
|
||||
Session session,
|
||||
AccountId accountId,
|
||||
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(
|
||||
Session session,
|
||||
AccountId accountId,
|
||||
@@ -756,6 +797,51 @@ class EmailAPI with HandleSetErrorMixin {
|
||||
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<({
|
||||
List<EmailId> emailIdsSuccess,
|
||||
Map<Id, SetError> mapErrors,
|
||||
|
||||
@@ -336,6 +336,65 @@ class EmailRepositoryImpl extends EmailRepository {
|
||||
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
|
||||
Future<Uint8List> downloadAttachmentForWeb(
|
||||
DownloadTaskId taskId,
|
||||
|
||||
@@ -131,6 +131,24 @@ abstract class EmailRepository {
|
||||
{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<({
|
||||
List<EmailId> emailIdsSuccess,
|
||||
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,
|
||||
if (PlatformInfo.isWeb && PlatformInfo.isCanvasKit)
|
||||
EmailActionType.downloadMessageAsEML,
|
||||
EmailActionType.editAsNewEmail,
|
||||
if (mailboxContain?.isTemplates == false)
|
||||
EmailActionType.editAsNewEmail,
|
||||
];
|
||||
|
||||
if (position == null) {
|
||||
|
||||
@@ -38,6 +38,7 @@ class ComposerArguments extends RouterArguments {
|
||||
final int? savedDraftHash;
|
||||
final EmailActionType? savedActionType;
|
||||
final EmailId? savedEmailDraftId;
|
||||
final EmailId? savedEmailTemplateId;
|
||||
|
||||
ComposerArguments({
|
||||
this.emailActionType = EmailActionType.compose,
|
||||
@@ -66,6 +67,7 @@ class ComposerArguments extends RouterArguments {
|
||||
this.savedDraftHash,
|
||||
this.savedActionType,
|
||||
this.savedEmailDraftId,
|
||||
this.savedEmailTemplateId,
|
||||
});
|
||||
|
||||
factory ComposerArguments.fromSendingEmail(SendingEmail sendingEmail) =>
|
||||
@@ -113,11 +115,14 @@ class ComposerArguments extends RouterArguments {
|
||||
presentationEmail: presentationEmail,
|
||||
);
|
||||
|
||||
factory ComposerArguments.editAsNewEmail(PresentationEmail presentationEmail) =>
|
||||
ComposerArguments(
|
||||
emailActionType: EmailActionType.editAsNewEmail,
|
||||
presentationEmail: presentationEmail,
|
||||
);
|
||||
factory ComposerArguments.editAsNewEmail(
|
||||
PresentationEmail presentationEmail, {
|
||||
EmailId? savedEmailTemplateId
|
||||
}) => ComposerArguments(
|
||||
emailActionType: EmailActionType.editAsNewEmail,
|
||||
presentationEmail: presentationEmail,
|
||||
savedEmailTemplateId: savedEmailTemplateId,
|
||||
);
|
||||
|
||||
factory ComposerArguments.fromSessionStorageBrowser(ComposerCache composerCache) =>
|
||||
ComposerArguments(
|
||||
@@ -134,6 +139,7 @@ class ComposerArguments extends RouterArguments {
|
||||
savedDraftHash: composerCache.draftHash,
|
||||
savedActionType: composerCache.actionType,
|
||||
savedEmailDraftId: composerCache.draftEmailId,
|
||||
savedEmailTemplateId: composerCache.templateEmailId,
|
||||
);
|
||||
|
||||
factory ComposerArguments.replyEmail({
|
||||
@@ -258,6 +264,7 @@ class ComposerArguments extends RouterArguments {
|
||||
savedDraftHash,
|
||||
savedActionType,
|
||||
savedEmailDraftId,
|
||||
sendingEmailActionType,
|
||||
];
|
||||
|
||||
ComposerArguments copyWith({
|
||||
@@ -287,6 +294,7 @@ class ComposerArguments extends RouterArguments {
|
||||
int? savedDraftHash,
|
||||
EmailActionType? savedActionType,
|
||||
EmailId? savedEmailDraftId,
|
||||
EmailId? savedEmailTemplateId,
|
||||
}) {
|
||||
return ComposerArguments(
|
||||
emailActionType: emailActionType ?? this.emailActionType,
|
||||
@@ -315,6 +323,7 @@ class ComposerArguments extends RouterArguments {
|
||||
savedDraftHash: savedDraftHash ?? this.savedDraftHash,
|
||||
savedActionType: savedActionType ?? this.savedActionType,
|
||||
savedEmailDraftId: savedEmailDraftId ?? this.savedEmailDraftId,
|
||||
savedEmailTemplateId: savedEmailTemplateId ?? this.savedEmailTemplateId,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user