TF-234 Implement delete a mailbox

This commit is contained in:
dab246
2022-02-24 18:55:15 +07:00
committed by Dat H. Pham
parent a19f3841d3
commit 79a7b45142
24 changed files with 495 additions and 25 deletions
@@ -276,7 +276,7 @@ class DestinationPickerView extends GetWidget<DestinationPickerController> {
_imagePaths,
mailboxNode,
mailboxDisplayed: MailboxDisplayed.destinationPicker)
..addOnSelectMailboxFolderClick((mailboxNode) => controller.selectMailboxAction(mailboxNode.item.toPresentationMailboxWithMailboxPath(
..addOnOpenMailboxFolderClick((mailboxNode) => controller.selectMailboxAction(mailboxNode.item.toPresentationMailboxWithMailboxPath(
mailboxNode.getPathMailboxNode(
controller.folderMailboxTree,
controller.defaultMailboxList,
@@ -292,7 +292,7 @@ class DestinationPickerView extends GetWidget<DestinationPickerController> {
_imagePaths,
mailboxNode,
mailboxDisplayed: MailboxDisplayed.destinationPicker)
..addOnSelectMailboxFolderClick((mailboxNode) => controller.selectMailboxAction(mailboxNode.item.toPresentationMailboxWithMailboxPath(
..addOnOpenMailboxFolderClick((mailboxNode) => controller.selectMailboxAction(mailboxNode.item.toPresentationMailboxWithMailboxPath(
mailboxNode.getPathMailboxNode(
controller.folderMailboxTree,
controller.defaultMailboxList,
@@ -16,4 +16,6 @@ abstract class MailboxDataSource {
Future<void> update({List<Mailbox>? updated, List<Mailbox>? created, List<MailboxId>? destroyed});
Future<Mailbox?> createNewMailbox(AccountId accountId, CreateNewMailboxRequest newMailboxRequest);
Future<bool> deleteMultipleMailbox(AccountId accountId, List<MailboxId> mailboxIds);
}
@@ -49,4 +49,9 @@ class MailboxCacheDataSourceImpl extends MailboxDataSource {
Future<Mailbox?> createNewMailbox(AccountId accountId, CreateNewMailboxRequest newMailboxRequest) {
throw UnimplementedError();
}
@override
Future<bool> deleteMultipleMailbox(AccountId accountId, List<MailboxId> mailboxIds) {
throw UnimplementedError();
}
}
@@ -50,4 +50,13 @@ class MailboxDataSourceImpl extends MailboxDataSource {
throw error;
});
}
@override
Future<bool> deleteMultipleMailbox(AccountId accountId, List<MailboxId> mailboxIds) {
return Future.sync(() async {
return await mailboxAPI.deleteMultipleMailbox(accountId, mailboxIds);
}).catchError((error) {
throw error;
});
}
}
@@ -128,4 +128,29 @@ class MailboxAPI {
throw error;
});
}
Future<bool> deleteMultipleMailbox(AccountId accountId, List<MailboxId> mailboxIds) async {
final setMailboxMethod = SetMailboxMethod(accountId)
..addDestroy(mailboxIds.map((mailboxId) => mailboxId.id).toSet())
..addOnDestroyRemoveEmails(true);
final requestBuilder = JmapRequestBuilder(httpClient, ProcessingInvocation());
final setMailboxInvocation = requestBuilder.invocation(setMailboxMethod);
final response = await (requestBuilder
..usings(setMailboxMethod.requiredCapabilities))
.build()
.execute();
final setMailboxResponse = response.parse<SetMailboxResponse>(
setMailboxInvocation.methodCallId,
SetMailboxResponse.deserialize);
return Future.sync(() async {
return setMailboxResponse?.destroyed?.isNotEmpty == true;
}).catchError((error) {
throw error;
});
}
}
@@ -140,4 +140,9 @@ class MailboxRepositoryImpl extends MailboxRepository {
Future<Mailbox?> createNewMailbox(AccountId accountId, CreateNewMailboxRequest newMailboxRequest) {
return mapDataSource[DataSourceType.network]!.createNewMailbox(accountId, newMailboxRequest);
}
@override
Future<bool> deleteMultipleMailbox(AccountId accountId, List<MailboxId> mailboxIds) {
return mapDataSource[DataSourceType.network]!.deleteMultipleMailbox(accountId, mailboxIds);
}
}
@@ -11,4 +11,6 @@ abstract class MailboxRepository {
Stream<MailboxResponse> refresh(AccountId accountId, State currentState);
Future<Mailbox?> createNewMailbox(AccountId accountId, CreateNewMailboxRequest newMailboxRequest);
Future<bool> deleteMultipleMailbox(AccountId accountId, List<MailboxId> mailboxIds);
}
@@ -0,0 +1,18 @@
import 'package:core/core.dart';
class DeleteMultipleMailboxSuccess extends UIState {
DeleteMultipleMailboxSuccess();
@override
List<Object?> get props => [];
}
class DeleteMultipleMailboxFailure extends FeatureFailure {
final exception;
DeleteMultipleMailboxFailure(this.exception);
@override
List<Object> get props => [exception];
}
@@ -0,0 +1,25 @@
import 'package:core/core.dart';
import 'package:dartz/dartz.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
import 'package:tmail_ui_user/features/mailbox/domain/repository/mailbox_repository.dart';
import 'package:tmail_ui_user/features/mailbox/domain/state/delete_multiple_mailbox_state.dart';
class DeleteMultipleMailboxInteractor {
final MailboxRepository mailboxRepository;
DeleteMultipleMailboxInteractor(this.mailboxRepository);
Stream<Either<Failure, Success>> execute(AccountId accountId, List<MailboxId> mailboxIds) async* {
try {
final result = await mailboxRepository.deleteMultipleMailbox(accountId, mailboxIds);
if (result) {
yield Right<Failure, Success>(DeleteMultipleMailboxSuccess());
} else {
yield Left<Failure, Success>(DeleteMultipleMailboxFailure(null));
}
} catch (e) {
yield Left<Failure, Success>(DeleteMultipleMailboxFailure(e));
}
}
}
@@ -17,6 +17,7 @@ import 'package:tmail_ui_user/features/mailbox/data/network/mailbox_api.dart';
import 'package:tmail_ui_user/features/mailbox/data/repository/mailbox_repository_impl.dart';
import 'package:tmail_ui_user/features/mailbox/domain/repository/mailbox_repository.dart';
import 'package:tmail_ui_user/features/mailbox/domain/usecases/create_new_mailbox_interactor.dart';
import 'package:tmail_ui_user/features/mailbox/domain/usecases/delete_multiple_mailbox_interactor.dart';
import 'package:tmail_ui_user/features/mailbox/domain/usecases/get_all_mailbox_interactor.dart';
import 'package:tmail_ui_user/features/mailbox/domain/usecases/refresh_all_mailbox_interactor.dart';
import 'package:tmail_ui_user/features/mailbox/domain/usecases/search_mailbox_interactor.dart';
@@ -45,6 +46,7 @@ class MailboxBindings extends BaseBindings {
Get.find<RefreshAllMailboxInteractor>(),
Get.find<CreateNewMailboxInteractor>(),
Get.find<SearchMailboxInteractor>(),
Get.find<DeleteMultipleMailboxInteractor>(),
Get.find<TreeBuilder>(),
Get.find<Uuid>(),
Get.find<AppToast>(),
@@ -58,7 +60,6 @@ class MailboxBindings extends BaseBindings {
void bindingsDataSource() {
Get.lazyPut<MailboxDataSource>(() => Get.find<MailboxDataSourceImpl>());
Get.lazyPut<StateDataSource>(() => Get.find<StateDataSourceImpl>());
}
@override
@@ -66,7 +67,6 @@ class MailboxBindings extends BaseBindings {
Get.lazyPut(() => MailboxDataSourceImpl(Get.find<MailboxAPI>()));
Get.lazyPut(() => MailboxCacheDataSourceImpl(Get.find<MailboxCacheManager>()));
Get.lazyPut(() => StateDataSourceImpl(Get.find<StateCacheClient>()));
}
@override
@@ -76,13 +76,13 @@ class MailboxBindings extends BaseBindings {
Get.lazyPut(() => RefreshAllMailboxInteractor(Get.find<MailboxRepository>()));
Get.lazyPut(() => CreateNewMailboxInteractor(Get.find<MailboxRepository>()));
Get.lazyPut(() => SearchMailboxInteractor());
Get.lazyPut(() => DeleteMultipleMailboxInteractor(Get.find<MailboxRepository>()));
}
@override
void bindingsRepository() {
Get.lazyPut<CredentialRepository>(() => Get.find<CredentialRepositoryImpl>());
Get.lazyPut<MailboxRepository>(() => Get.find<MailboxRepositoryImpl>());
}
@override
@@ -1,7 +1,7 @@
import 'package:core/core.dart';
import 'package:dartz/dartz.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/id.dart';
@@ -16,9 +16,11 @@ import 'package:tmail_ui_user/features/email/domain/state/mark_as_email_read_sta
import 'package:tmail_ui_user/features/login/domain/usecases/delete_credential_interactor.dart';
import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart';
import 'package:tmail_ui_user/features/mailbox/domain/state/create_new_mailbox_state.dart';
import 'package:tmail_ui_user/features/mailbox/domain/state/delete_multiple_mailbox_state.dart';
import 'package:tmail_ui_user/features/mailbox/domain/state/get_all_mailboxes_state.dart';
import 'package:tmail_ui_user/features/mailbox/domain/state/search_mailbox_state.dart';
import 'package:tmail_ui_user/features/mailbox/domain/usecases/create_new_mailbox_interactor.dart';
import 'package:tmail_ui_user/features/mailbox/domain/usecases/delete_multiple_mailbox_interactor.dart';
import 'package:tmail_ui_user/features/mailbox/domain/usecases/get_all_mailbox_interactor.dart';
import 'package:tmail_ui_user/features/mailbox/domain/usecases/refresh_all_mailbox_interactor.dart';
import 'package:tmail_ui_user/features/mailbox/domain/usecases/search_mailbox_interactor.dart';
@@ -50,6 +52,7 @@ class MailboxController extends BaseController {
final RefreshAllMailboxInteractor _refreshAllMailboxInteractor;
final CreateNewMailboxInteractor _createNewMailboxInteractor;
final SearchMailboxInteractor _searchMailboxInteractor;
final DeleteMultipleMailboxInteractor _deleteMultipleMailboxInteractor;
final TreeBuilder _treeBuilder;
final Uuid _uuid;
final AppToast _appToast;
@@ -77,6 +80,7 @@ class MailboxController extends BaseController {
this._refreshAllMailboxInteractor,
this._createNewMailboxInteractor,
this._searchMailboxInteractor,
this._deleteMultipleMailboxInteractor,
this._treeBuilder,
this._uuid,
this._appToast,
@@ -144,6 +148,8 @@ class MailboxController extends BaseController {
_createNewMailboxFailure(failure);
} else if (failure is SearchMailboxFailure) {
_searchMailboxFailure(failure);
} else if (failure is DeleteMultipleMailboxFailure) {
_deleteMailboxFailure(failure);
}
},
(success) {
@@ -151,6 +157,12 @@ class MailboxController extends BaseController {
_createNewMailboxSuccess(success);
} else if (success is SearchMailboxSuccess) {
_searchMailboxSuccess(success);
} else if (success is DeleteMultipleMailboxSuccess) {
_deleteMailboxSuccess(success);
} else if (success is GetAllMailboxSuccess) {
if (isSearchActive()) {
_searchMailboxAction(allMailboxes, searchQuery.value);
}
}
}
);
@@ -310,9 +322,14 @@ class MailboxController extends BaseController {
bool isSearchActive() => searchState.value.searchStatus == SearchStatus.ACTIVE;
void enableSearch() => searchState.value = searchState.value.enableSearchState();
void enableSearch() {
_cancelSelectMailbox();
searchState.value = searchState.value.enableSearchState();
}
void disableSearch(BuildContext context) {
_cancelSelectMailbox();
listMailboxSearched.clear();
searchState.value = searchState.value.disableSearchState();
searchQuery.value = SearchQuery.initial();
@@ -446,13 +463,80 @@ class MailboxController extends BaseController {
List<PresentationMailbox> selectionMailbox) {
switch(actions) {
case MailboxActions.delete:
_openConfirmationDialogDeleteMailboxAction(context, selectionMailbox.first);
break;
default:
break;
}
}
void _openConfirmationDialogDeleteMailboxAction(BuildContext context, PresentationMailbox presentationMailbox) {
if (responsiveUtils.isMobile(context) || responsiveUtils.isMobileDevice(context)) {
(ConfirmationDialogActionSheetBuilder(context)
..messageText(AppLocalizations.of(context).message_confirmation_dialog_delete_mailbox(presentationMailbox.name?.name ?? ''))
..onCancelAction(AppLocalizations.of(context).cancel, () => popBack())
..onConfirmAction(AppLocalizations.of(context).delete, () => _deleteMailboxAction(presentationMailbox)))
.show();
} else {
showDialog(
context: context,
barrierColor: AppColor.colorDefaultCupertinoActionSheet,
builder: (BuildContext context) => (ConfirmDialogBuilder(_imagePaths)
..key(Key('confirm_dialog_delete_mailbox'))
..title(AppLocalizations.of(context).delete_mailboxes)
..content(AppLocalizations.of(context).message_confirmation_dialog_delete_mailbox(presentationMailbox.name?.name ?? ''))
..addIcon(SvgPicture.asset(_imagePaths.icRemoveDialog, fit: BoxFit.fill))
..onCloseButtonAction(() => popBack())
..onConfirmButtonAction(AppLocalizations.of(context).delete, () => _deleteMailboxAction(presentationMailbox))
..onCancelButtonAction(AppLocalizations.of(context).cancel, () => popBack()))
.build());
}
}
void _deleteMailboxAction(PresentationMailbox presentationMailbox) {
final newFolderMailboxTree = MailboxTree(MailboxNode(
MailboxNode.rootItem(),
childrenItems: folderMailboxNodeList));
final mailboxNode = newFolderMailboxTree.findNode(presentationMailbox.id);
final accountId = mailboxDashBoardController.accountId.value;
if (mailboxNode != null && accountId != null) {
final allMailboxId = newFolderMailboxTree
.getAllNodes(mailboxNode)
.map((node) => node.item.id)
.toList();
final allMailboxIdReversed = allMailboxId.reversed.toList();
consumeState(_deleteMultipleMailboxInteractor.execute(accountId, allMailboxIdReversed));
} else {
_deleteMailboxFailure(DeleteMultipleMailboxFailure(null));
}
_cancelSelectMailbox();
popBack();
}
void _deleteMailboxSuccess(DeleteMultipleMailboxSuccess success) {
if (Get.overlayContext != null && Get.context != null) {
_appToast.showToastWithIcon(
Get.overlayContext!,
message: AppLocalizations.of(Get.context!).delete_mailboxes_successfully,
icon: _imagePaths.icSelectedV2);
}
refreshMailboxChanges();
}
void _deleteMailboxFailure(DeleteMultipleMailboxFailure failure) {
if (Get.overlayContext != null && Get.context != null) {
_appToast.showToastWithIcon(
Get.overlayContext!,
message: AppLocalizations.of(Get.context!).delete_mailboxes_failure,
icon: _imagePaths.icDeleteToast);
}
}
void closeMailboxScreen(BuildContext context) {
_cancelSelectMailbox();
mailboxDashBoardController.closeDrawer();
}
@@ -70,14 +70,28 @@ class MailboxView extends GetWidget<MailboxController> {
bottom: 10),
child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [
!responsiveUtils.isDesktop(context) ? _buildCloseScreenButton(context) : SizedBox(width: 50),
Obx(() => controller.currentSelectMode.value == SelectMode.INACTIVE
? SizedBox(width: 40)
: SizedBox(width: 49)),
Obx(() {
if (controller.isSearchActive()) {
return controller.listMailboxSearched.isNotEmpty
? SizedBox(width: controller.isSelectionEnabled() ? 49 : 40)
: SizedBox.shrink();
} else {
return SizedBox(width: controller.isSelectionEnabled() ? 49 : 40);
}
}),
Expanded(child: Text(
AppLocalizations.of(context).folders,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 20, color: AppColor.colorNameEmail, fontWeight: FontWeight.w700))),
Obx(() => _buildEditMailboxButton(context, controller.currentSelectMode.value)),
Obx(() {
if (controller.isSearchActive()) {
return controller.listMailboxSearched.isNotEmpty
? _buildEditMailboxButton(context, controller.isSelectionEnabled())
: SizedBox(width: 25);
} else {
return _buildEditMailboxButton(context, controller.isSelectionEnabled());
}
}),
_buildAddNewFolderButton(context),
])),
Divider(color: AppColor.colorDividerMailbox, height: 0.5, thickness: 0.2),
@@ -103,15 +117,15 @@ class MailboxView extends GetWidget<MailboxController> {
icon: SvgPicture.asset(imagePaths.icAddNewFolder, width: 30, height: 30, fit: BoxFit.fill)));
}
Widget _buildEditMailboxButton(BuildContext context, SelectMode selectMode) {
Widget _buildEditMailboxButton(BuildContext context, bool isSelectionEnabled) {
return Material(
borderRadius: BorderRadius.circular(20),
color: Colors.transparent,
child: TextButton(
child: Text(
selectMode == SelectMode.INACTIVE ? AppLocalizations.of(context).edit : AppLocalizations.of(context).cancel,
!isSelectionEnabled ? AppLocalizations.of(context).edit : AppLocalizations.of(context).cancel,
style: TextStyle(fontSize: 17, color: AppColor.colorTextButton)),
onPressed: () => selectMode == SelectMode.INACTIVE
onPressed: () => !isSelectionEnabled
? controller.enableSelectionMailbox()
: controller.disableSelectionMailbox()
)
@@ -283,7 +297,6 @@ class MailboxView extends GetWidget<MailboxController> {
..addDecoration(BoxDecoration(borderRadius: BorderRadius.circular(12), color: AppColor.colorBgSearchBar))
..addIconClearText(SvgPicture.asset(imagePaths.icClearTextSearch, width: 20, height: 20, fit: BoxFit.fill))
..setHintText(AppLocalizations.of(context).hint_search_mailboxes)
..addOnCancelSearchPressed(() => controller.disableSearch(context))
..addOnClearTextSearchAction(() => controller.clearSearchText())
..addOnTextChangeSearchAction((query) => controller.searchMailbox(query))
..addOnSearchTextAction((query) => controller.searchMailbox(query)))
@@ -324,8 +337,10 @@ class MailboxView extends GetWidget<MailboxController> {
imagePaths,
listMailbox[index],
isSearchActive: controller.isSearchActive(),
allSelectMode: controller.currentSelectMode.value,
isLastElement: index == listMailbox.length - 1)
..addOnOpenMailboxAction((mailbox) => controller.openMailbox(context, mailbox)))
..addOnOpenMailboxAction((mailbox) => controller.openMailbox(context, mailbox))
..addOnSelectMailboxActionClick((mailbox) => controller.selectMailbox(context, mailbox)))
.build()));
}
@@ -135,12 +135,12 @@ extension MailboxNodeExtension on MailboxNode {
);
}
MailboxNode toSelectedMailboxNode({required SelectMode selectMode, ExpandMode? newExpandMode}) {
return MailboxNode(
item,
childrenItems: childrenItems,
expandMode: newExpandMode ?? expandMode,
selectMode: selectMode,
);
MailboxNode toSelectedMailboxNode({required SelectMode selectMode, ExpandMode? newExpandMode}) {
return MailboxNode(
item,
childrenItems: childrenItems,
expandMode: newExpandMode ?? expandMode,
selectMode: selectMode,
);
}
}
@@ -27,6 +27,24 @@ class MailboxTree with EquatableMixin {
return result;
}
List<MailboxNode> getAllNodes(MailboxNode node){
List<MailboxNode> listOfNodes = <MailboxNode>[];
_addAllNodes(node, listOfNodes);
return listOfNodes;
}
void _addAllNodes(MailboxNode? node, List<MailboxNode> listOfNodes) {
if (node != null) {
listOfNodes.add(node);
List<MailboxNode>? childrenItems = node.childrenItems;
if (childrenItems != null) {
childrenItems.forEach((child) {
_addAllNodes(child, listOfNodes);
});
}
}
}
@override
List<Object?> get props => [root];
}
@@ -448,7 +448,7 @@ class ThreadController extends BaseController {
(CupertinoActionSheetBuilder(context)
..addTiles(actionTiles)
..addCancelButton(cancelButton))
.build();
.show();
}
void openFilterMessagesForTablet(BuildContext context, RelativeRect? position, List<PopupMenuEntry> popupMenuItems) async {
@@ -676,4 +676,30 @@ class AppLocalizations {
'Rename',
name: 'rename');
}
String get delete_mailboxes_successfully {
return Intl.message(
'Delete mailboxes successfully',
name: 'delete_mailboxes_successfully');
}
String get delete_mailboxes_failure {
return Intl.message(
'Delete mailboxes failure',
name: 'delete_mailboxes_failure');
}
String get delete_mailboxes {
return Intl.message(
'Delete mailboxes',
name: 'delete_mailboxes');
}
String message_confirmation_dialog_delete_mailbox(String nameMailbox) {
return Intl.message(
'"$nameMailbox" mailbox and all of the sub-folders and messages it contains will be deleted and won\'t be able to recover. Do you want to continue to delete?',
name: 'message_confirmation_dialog_delete_mailbox',
args: [nameMailbox]
);
}
}