TF-80 Move email to Mailbox in Email Detailed view

This commit is contained in:
dab246
2021-09-21 01:26:50 +07:00
committed by Dat H. Pham
parent c227df8df2
commit afc8c0fe77
18 changed files with 222 additions and 16 deletions
@@ -63,7 +63,7 @@ class DestinationPickerController extends BaseController {
}
void moveEmailToMailboxAction(PresentationMailbox destinationMailbox) {
popBack();
popBack(result: destinationMailbox);
}
void closeDestinationPicker() {
@@ -2,15 +2,15 @@
import 'package:equatable/equatable.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
import 'package:model/mailbox/presentation_mailbox.dart';
class DestinationPickerArguments with EquatableMixin{
final AccountId accountId;
final List<EmailId> emailIds;
final MailboxId currentMailboxId;
final PresentationMailbox currentMailbox;
DestinationPickerArguments(this.accountId, this.emailIds, this.currentMailboxId);
DestinationPickerArguments(this.accountId, this.emailIds, this.currentMailbox);
@override
List<Object?> get props => [accountId, emailIds, currentMailboxId];
List<Object?> get props => [accountId, emailIds, currentMailbox];
}
@@ -4,6 +4,7 @@ import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:model/email/read_actions.dart';
import 'package:model/model.dart';
import 'package:tmail_ui_user/features/composer/domain/model/email_request.dart';
import 'package:tmail_ui_user/features/email/domain/model/move_request.dart';
abstract class EmailDataSource {
Future<Email> getEmailContent(AccountId accountId, EmailId emailId);
@@ -26,4 +27,6 @@ abstract class EmailDataSource {
AccountRequest accountRequest,
CancelToken cancelToken
);
Future<bool> moveToMailbox(AccountId accountId, MoveRequest moveRequest);
}
@@ -5,6 +5,7 @@ import 'package:model/model.dart';
import 'package:tmail_ui_user/features/composer/domain/model/email_request.dart';
import 'package:tmail_ui_user/features/email/data/datasource/email_datasource.dart';
import 'package:tmail_ui_user/features/email/data/network/email_api.dart';
import 'package:tmail_ui_user/features/email/domain/model/move_request.dart';
class EmailDataSourceImpl extends EmailDataSource {
@@ -67,4 +68,13 @@ class EmailDataSourceImpl extends EmailDataSource {
throw error;
});
}
@override
Future<bool> moveToMailbox(AccountId accountId, MoveRequest moveRequest) {
return Future.sync(() async {
return await emailAPI.moveToMailbox(accountId, moveRequest);
}).catchError((error) {
throw error;
});
}
}
@@ -26,6 +26,7 @@ import 'package:jmap_dart_client/jmap/mail/email/submission/set/set_email_submis
import 'package:path_provider/path_provider.dart';
import 'package:tmail_ui_user/features/composer/domain/model/email_request.dart';
import 'package:model/model.dart';
import 'package:tmail_ui_user/features/email/domain/model/move_request.dart';
class EmailAPI {
@@ -186,4 +187,32 @@ class EmailAPI {
accountRequest.basicAuth,
cancelToken: cancelToken);
}
Future<bool> moveToMailbox(AccountId accountId, MoveRequest moveRequest) async {
final setEmailMethod = SetEmailMethod(accountId)
..addUpdates({
moveRequest.emailId.id: moveRequest.currentMailboxId
.generateMoveToMailboxActionPath(moveRequest.destinationMailboxId)
});
final requestBuilder = JmapRequestBuilder(_httpClient, ProcessingInvocation());
final setEmailInvocation = requestBuilder.invocation(setEmailMethod);
final response = await (requestBuilder
..usings(setEmailMethod.requiredCapabilities))
.build()
.execute();
final setEmailResponse = response.parse<SetEmailResponse>(
setEmailInvocation.methodCallId,
SetEmailResponse.deserialize);
return Future.sync(() async {
final emailUpdated = setEmailResponse!.updated![moveRequest.emailId.id];
return emailUpdated == null;
}).catchError((error) {
throw error;
});
}
}
@@ -5,6 +5,7 @@ import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:model/model.dart';
import 'package:tmail_ui_user/features/composer/domain/model/email_request.dart';
import 'package:tmail_ui_user/features/email/data/datasource/email_datasource.dart';
import 'package:tmail_ui_user/features/email/domain/model/move_request.dart';
import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart';
class EmailRepositoryImpl extends EmailRepository {
@@ -53,4 +54,9 @@ class EmailRepositoryImpl extends EmailRepository {
accountRequest,
cancelToken);
}
@override
Future<bool> moveToMailbox(AccountId accountId, MoveRequest moveRequest) {
return emailDataSource.moveToMailbox(accountId, moveRequest);
}
}
@@ -0,0 +1,34 @@
import 'package:equatable/equatable.dart';
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
import 'package:model/model.dart';
class MoveRequest with EquatableMixin {
final EmailId emailId;
final MailboxId currentMailboxId;
final MailboxName currentMailboxName;
final MailboxId destinationMailboxId;
final MailboxName destinationMailboxName;
final MoveAction moveAction;
MoveRequest(
this.emailId,
this.currentMailboxId,
this.currentMailboxName,
this.destinationMailboxId,
this.destinationMailboxName,
this.moveAction,
);
@override
List<Object?> get props => [
emailId,
currentMailboxId,
currentMailboxName,
destinationMailboxId,
destinationMailboxName,
moveAction
];
}
@@ -3,6 +3,7 @@ import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:model/model.dart';
import 'package:tmail_ui_user/features/composer/domain/model/email_request.dart';
import 'package:tmail_ui_user/features/email/domain/model/move_request.dart';
abstract class EmailRepository {
Future<Email> getEmailContent(AccountId accountId, EmailId emailId);
@@ -25,4 +26,6 @@ abstract class EmailRepository {
AccountRequest accountRequest,
CancelToken cancelToken
);
Future<bool> moveToMailbox(AccountId accountId, MoveRequest moveRequest);
}
@@ -0,0 +1,20 @@
import 'package:core/core.dart';
import 'package:tmail_ui_user/features/email/domain/model/move_request.dart';
class MoveToMailboxSuccess extends UIState {
final MoveRequest moveRequest;
MoveToMailboxSuccess(this.moveRequest);
@override
List<Object?> get props => [moveRequest];
}
class MoveToMailboxFailure extends FeatureFailure {
final exception;
MoveToMailboxFailure(this.exception);
@override
List<Object> get props => [exception];
}
@@ -0,0 +1,23 @@
import 'package:core/core.dart';
import 'package:dartz/dartz.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:tmail_ui_user/features/email/domain/model/move_request.dart';
import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart';
import 'package:tmail_ui_user/features/email/domain/state/move_to_mailbox_state.dart';
class MoveToMailboxInteractor {
final EmailRepository emailRepository;
MoveToMailboxInteractor(this.emailRepository);
Stream<Either<Failure, Success>> execute(AccountId accountId, MoveRequest moveRequest) async* {
try {
final result = await emailRepository.moveToMailbox(accountId, moveRequest);
yield result
? Right(MoveToMailboxSuccess(moveRequest))
: Left(MoveToMailboxFailure(null));
} catch (e) {
yield Left(MoveToMailboxFailure(e));
}
}
}
@@ -11,6 +11,7 @@ import 'package:tmail_ui_user/features/email/domain/usecases/download_attachment
import 'package:tmail_ui_user/features/email/domain/usecases/export_attachment_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/mark_as_email_read_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/move_to_mailbox_interactor.dart';
import 'package:tmail_ui_user/features/email/presentation/email_controller.dart';
import 'package:tmail_ui_user/features/login/data/repository/credential_repository_impl.dart';
import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart';
@@ -33,12 +34,14 @@ class EmailBindings extends Bindings {
Get.lazyPut(() => ExportAttachmentInteractor(
Get.find<EmailRepository>(),
Get.find<CredentialRepository>()));
Get.lazyPut(() => MoveToMailboxInteractor(Get.find<EmailRepository>()));
Get.put(EmailController(
Get.find<GetEmailContentInteractor>(),
Get.find<MarkAsEmailReadInteractor>(),
Get.find<DownloadAttachmentsInteractor>(),
Get.find<DeviceManager>(),
Get.find<AppToast>(),
Get.find<ExportAttachmentInteractor>()));
Get.find<ExportAttachmentInteractor>(),
Get.find<MoveToMailboxInteractor>()));
}
}
@@ -8,18 +8,22 @@ import 'package:flutter/widgets.dart';
import 'package:get/get.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
import 'package:model/model.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:tmail_ui_user/features/base/base_controller.dart';
import 'package:tmail_ui_user/features/destination_picker/presentation/model/destination_picker_arguments.dart';
import 'package:tmail_ui_user/features/email/domain/model/move_request.dart';
import 'package:tmail_ui_user/features/email/domain/state/download_attachments_state.dart';
import 'package:tmail_ui_user/features/email/domain/state/export_attachment_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/move_to_mailbox_state.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/download_attachments_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/state/mark_as_email_read_state.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/export_attachment_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/mark_as_email_read_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/move_to_mailbox_interactor.dart';
import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
@@ -38,6 +42,7 @@ class EmailController extends BaseController {
final DeviceManager _deviceManager;
final AppToast _appToast;
final ExportAttachmentInteractor _exportAttachmentInteractor;
final MoveToMailboxInteractor _moveToMailboxInteractor;
final emailAddressExpandMode = ExpandMode.COLLAPSE.obs;
final attachmentsExpandMode = ExpandMode.COLLAPSE.obs;
@@ -50,6 +55,7 @@ class EmailController extends BaseController {
this._deviceManager,
this._appToast,
this._exportAttachmentInteractor,
this._moveToMailboxInteractor,
);
@override
@@ -101,6 +107,8 @@ class EmailController extends BaseController {
_markAsEmailReadSuccess(success);
} else if (success is ExportAttachmentSuccess) {
_exportAttachmentSuccessAction(success);
} else if (success is MoveToMailboxSuccess) {
_moveToMailboxSuccess(success);
}
});
}
@@ -231,18 +239,36 @@ class EmailController extends BaseController {
}
}
void openDestinationPickerView(PresentationEmail email) {
final currentMailboxId = mailboxDashBoardController.selectedMailbox.value?.id;
void openDestinationPickerView(PresentationEmail email) async {
final currentMailbox = mailboxDashBoardController.selectedMailbox.value;
final accountId = mailboxDashBoardController.accountId.value;
if (currentMailboxId != null && accountId != null) {
push(
if (currentMailbox != null && accountId != null) {
final mailboxDestination = await push(
AppRoutes.DESTINATION_PICKER,
arguments: DestinationPickerArguments(accountId, [email.id], currentMailboxId)
arguments: DestinationPickerArguments(accountId, [email.id], currentMailbox)
);
if (mailboxDestination != null && mailboxDestination is PresentationMailbox) {
_moveToMailbox(accountId, MoveRequest(
email.id,
currentMailbox.id,
currentMailbox.name ?? MailboxName(''),
mailboxDestination.id,
mailboxDestination.name ?? MailboxName(''),
MoveAction.moveTo));
}
}
}
void _moveToMailbox(AccountId accountId, MoveRequest moveRequest) {
consumeState(_moveToMailboxInteractor.execute(accountId, moveRequest));
}
void _moveToMailboxSuccess(Success success) {
mailboxDashBoardController.dispatchState(Right(success));
}
bool canComposeEmail() => mailboxDashBoardController.sessionCurrent != null
&& mailboxDashBoardController.userProfile.value != null
&& mailboxDashBoardController.mapMailboxId.containsKey(PresentationMailbox.roleOutbox)
@@ -17,7 +17,7 @@ import 'package:tmail_ui_user/features/email/presentation/widgets/sender_and_rec
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
import 'package:filesize/filesize.dart';
class EmailView extends GetWidget {
class EmailView extends GetView {
final emailController = Get.find<EmailController>();
final responsiveUtils = Get.find<ResponsiveUtils>();
@@ -17,6 +17,7 @@ import 'package:model/model.dart';
import 'package:tmail_ui_user/features/base/base_controller.dart';
import 'package:tmail_ui_user/features/destination_picker/presentation/model/destination_picker_arguments.dart';
import 'package:tmail_ui_user/features/email/domain/state/mark_as_email_read_state.dart';
import 'package:tmail_ui_user/features/email/domain/state/move_to_mailbox_state.dart';
import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart';
import 'package:tmail_ui_user/features/thread/domain/constants/thread_constants.dart';
@@ -77,16 +78,26 @@ class ThreadController extends BaseController {
if (success is MarkAsEmailReadSuccess ||
success is MarkAsMultipleEmailReadAllSuccess ||
success is MarkAsMultipleEmailReadHasSomeEmailFailure) {
_refreshListEmailMarkAsRead();
_refreshListEmail();
}
});
}
});
mailboxDashBoardController.viewState.listen((state) {
state.map((success) {
if (success is MoveToMailboxSuccess) {
_refreshListEmail();
mailboxDashBoardController.clearState();
}
});
});
}
@override
void onClose() {
mailboxDashBoardController.selectedMailbox.close();
mailboxDashBoardController.viewState.close();
listEmailController.dispose();
super.onClose();
}
@@ -233,7 +244,7 @@ class ThreadController extends BaseController {
currentSelectMode.value = SelectMode.INACTIVE;
}
void _refreshListEmailMarkAsRead() {
void _refreshListEmail() {
final newLimit = emailList.isNotEmpty ? UnsignedInt(emailList.length) : ThreadConstants.defaultLimit;
loadMoreState.value = LoadMoreState.IDLE;
emailList.clear();
@@ -320,14 +331,15 @@ class ThreadController extends BaseController {
}
void moveSelectedMultipleEmailToMailboxAction(List<PresentationEmail> listEmail) {
final currentMailbox = mailboxDashBoardController.selectedMailbox.value;
final accountId = mailboxDashBoardController.accountId.value;
if (_currentMailboxId != null && accountId != null) {
if (currentMailbox != null && accountId != null) {
popBack();
final listEmailIds = listEmail.map((email) => email.id).toList();
push(
AppRoutes.DESTINATION_PICKER,
arguments: DestinationPickerArguments(accountId, listEmailIds, _currentMailboxId!)
arguments: DestinationPickerArguments(accountId, listEmailIds, currentMailbox)
);
}
}
@@ -393,5 +393,20 @@ class AppLocalizations {
name: 'browse',
);
}
String moved_to_mailbox(String destinationMailboxPath) {
return Intl.message(
'Moved to $destinationMailboxPath',
name: 'moved_to_mailbox',
args: [destinationMailboxPath]
);
}
String get undo_action {
return Intl.message(
'UNDO',
name: 'undo_action'
);
}
}
+5
View File
@@ -0,0 +1,5 @@
enum MoveAction {
moveTo,
undo
}
@@ -0,0 +1,15 @@
import 'package:jmap_dart_client/jmap/core/patch_object.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
extension MailboxIdExtension on MailboxId {
String generatePath() {
return '${PatchObject.mailboxIdsProperty}/${id.value}';
}
PatchObject generateMoveToMailboxActionPath(MailboxId destinationMailboxId) {
return PatchObject({
generatePath(): null,
destinationMailboxId.generatePath(): true
});
}
}
+2
View File
@@ -24,6 +24,7 @@ export 'email/presentation_email_address.dart';
export 'email/email_address_cache.dart';
export 'email/read_actions.dart';
export 'email/attachment.dart';
export 'email/move_action.dart';
// Extensions
export 'extensions/email_address_extension.dart';
@@ -37,6 +38,7 @@ export 'extensions/keyword_identifier_extension.dart';
export 'extensions/presentation_mailbox_extension.dart';
export 'extensions/email_body_part_extension.dart';
export 'extensions/attachment_extension.dart';
export 'extensions/mailbox_id_extension.dart';
// Download
export 'download/download_task_id.dart';