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
+4
View File
@@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M24 12C24 18.6274 18.6274 24 12 24C5.37258 24 0 18.6274 0 12C0 5.37258 5.37258 0 12 0C18.6274 0 24 5.37258 24 12Z" fill="#FF3B30"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.3 6.40002C11.17 6.40009 11.0427 6.43634 10.9321 6.50471C10.8216 6.57308 10.7323 6.67087 10.6742 6.78712L10.1674 7.80002H7.80001C7.61435 7.80002 7.43631 7.87377 7.30503 8.00505C7.17376 8.13633 7.10001 8.31437 7.10001 8.50002C7.10001 8.68568 7.17376 8.86372 7.30503 8.995C7.43631 9.12627 7.61435 9.20002 7.80001 9.20002V16.2C7.80001 16.5713 7.94751 16.9274 8.21006 17.19C8.47261 17.4525 8.8287 17.6 9.20001 17.6H14.8C15.1713 17.6 15.5274 17.4525 15.79 17.19C16.0525 16.9274 16.2 16.5713 16.2 16.2V9.20002C16.3857 9.20002 16.5637 9.12627 16.695 8.995C16.8263 8.86372 16.9 8.68568 16.9 8.50002C16.9 8.31437 16.8263 8.13633 16.695 8.00505C16.5637 7.87377 16.3857 7.80002 16.2 7.80002H13.8326L13.3258 6.78712C13.2677 6.67087 13.1784 6.57308 13.0679 6.50471C12.9573 6.43634 12.83 6.40009 12.7 6.40002H11.3ZM9.90001 10.6C9.90001 10.4144 9.97376 10.2363 10.105 10.105C10.2363 9.97377 10.4144 9.90002 10.6 9.90002C10.7857 9.90002 10.9637 9.97377 11.095 10.105C11.2263 10.2363 11.3 10.4144 11.3 10.6V14.8C11.3 14.9857 11.2263 15.1637 11.095 15.295C10.9637 15.4263 10.7857 15.5 10.6 15.5C10.4144 15.5 10.2363 15.4263 10.105 15.295C9.97376 15.1637 9.90001 14.9857 9.90001 14.8V10.6ZM13.4 9.90002C13.2144 9.90002 13.0363 9.97377 12.905 10.105C12.7738 10.2363 12.7 10.4144 12.7 10.6V14.8C12.7 14.9857 12.7738 15.1637 12.905 15.295C13.0363 15.4263 13.2144 15.5 13.4 15.5C13.5857 15.5 13.7637 15.4263 13.895 15.295C14.0263 15.1637 14.1 14.9857 14.1 14.8V10.6C14.1 10.4144 14.0263 10.2363 13.895 10.105C13.7637 9.97377 13.5857 9.90002 13.4 9.90002Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

+4
View File
@@ -0,0 +1,4 @@
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="64" height="64" rx="16" fill="#FFE9DB"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M30.2857 19C29.9674 19.0002 29.6555 19.0876 29.3848 19.2524C29.1141 19.4172 28.8954 19.6529 28.7531 19.9332L27.512 22.375H21.7143C21.2596 22.375 20.8236 22.5528 20.5021 22.8693C20.1806 23.1857 20 23.6149 20 24.0625C20 24.5101 20.1806 24.9393 20.5021 25.2557C20.8236 25.5722 21.2596 25.75 21.7143 25.75V42.625C21.7143 43.5201 22.0755 44.3785 22.7185 45.0115C23.3615 45.6444 24.2335 46 25.1429 46H38.8571C39.7665 46 40.6385 45.6444 41.2815 45.0115C41.9245 44.3785 42.2857 43.5201 42.2857 42.625V25.75C42.7404 25.75 43.1764 25.5722 43.4979 25.2557C43.8194 24.9393 44 24.5101 44 24.0625C44 23.6149 43.8194 23.1857 43.4979 22.8693C43.1764 22.5528 42.7404 22.375 42.2857 22.375H36.488L35.2469 19.9332C35.1046 19.6529 34.8859 19.4172 34.6152 19.2524C34.3445 19.0876 34.0326 19.0002 33.7143 19H30.2857ZM26.8571 29.125C26.8571 28.6774 27.0378 28.2482 27.3592 27.9318C27.6807 27.6153 28.1168 27.4375 28.5714 27.4375C29.0261 27.4375 29.4621 27.6153 29.7836 27.9318C30.1051 28.2482 30.2857 28.6774 30.2857 29.125V39.25C30.2857 39.6976 30.1051 40.1268 29.7836 40.4432C29.4621 40.7597 29.0261 40.9375 28.5714 40.9375C28.1168 40.9375 27.6807 40.7597 27.3592 40.4432C27.0378 40.1268 26.8571 39.6976 26.8571 39.25V29.125ZM35.4286 27.4375C34.9739 27.4375 34.5379 27.6153 34.2164 27.9318C33.8949 28.2482 33.7143 28.6774 33.7143 29.125V39.25C33.7143 39.6976 33.8949 40.1268 34.2164 40.4432C34.5379 40.7597 34.9739 40.9375 35.4286 40.9375C35.8832 40.9375 36.3193 40.7597 36.6408 40.4432C36.9622 40.1268 37.1429 39.6976 37.1429 39.25V29.125C37.1429 28.6774 36.9622 28.2482 36.6408 27.9318C36.3193 27.6153 35.8832 27.4375 35.4286 27.4375Z" fill="#E64646"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

+2
View File
@@ -41,11 +41,13 @@ export 'presentation/views/context_menu/context_menu_header_builder.dart';
export 'presentation/views/context_menu/simple_context_menu_action_builder.dart'; export 'presentation/views/context_menu/simple_context_menu_action_builder.dart';
export 'presentation/views/dialog/loading_dialog_builder.dart'; export 'presentation/views/dialog/loading_dialog_builder.dart';
export 'presentation/views/dialog/downloading_file_dialog_builder.dart'; export 'presentation/views/dialog/downloading_file_dialog_builder.dart';
export 'presentation/views/dialog/confirmation_dialog_builder.dart';
export 'presentation/views/background/background_widget_builder.dart'; export 'presentation/views/background/background_widget_builder.dart';
export 'presentation/views/html_viewer/html_content_viewer_widget.dart'; export 'presentation/views/html_viewer/html_content_viewer_widget.dart';
export 'presentation/views/floating_button/scrolling_floating_button_animated.dart'; export 'presentation/views/floating_button/scrolling_floating_button_animated.dart';
export 'presentation/views/bottom_popup/cupertino_action_sheet_action_builder.dart'; export 'presentation/views/bottom_popup/cupertino_action_sheet_action_builder.dart';
export 'presentation/views/bottom_popup/cupertino_action_sheet_builder.dart'; export 'presentation/views/bottom_popup/cupertino_action_sheet_builder.dart';
export 'presentation/views/bottom_popup/confirmation_dialog_action_sheet_builder.dart';
// Resources // Resources
export 'presentation/resources/assets_paths.dart'; export 'presentation/resources/assets_paths.dart';
@@ -80,6 +80,11 @@ extension AppColor on Color {
static const colorInputBackgroundErrorVerifyName = Color(0xFFFAEBEB); static const colorInputBackgroundErrorVerifyName = Color(0xFFFAEBEB);
static const colorInputBackgroundCreateMailbox = Color(0xFFF2F3F5); static const colorInputBackgroundCreateMailbox = Color(0xFFF2F3F5);
static const colorHintInputCreateMailbox= Color(0xFFA9B4C2); static const colorHintInputCreateMailbox= Color(0xFFA9B4C2);
static const colorMessageConfirmDialog= Color(0xFF6D7885);
static const colorActionDeleteConfirmDialog= Color(0xFFE64646);
static const colorActionCancelDialog= Color(0xFF007AFF);
static const colorMessageDialog= Color(0xFF222222);
static const colorConfirmActionDialog= Color(0xFFF2F2F2);
static const mapGradientColor = [ static const mapGradientColor = [
[Color(0xFF21D4FD), Color(0xFFB721FF)], [Color(0xFF21D4FD), Color(0xFFB721FF)],
@@ -82,6 +82,8 @@ class ImagePaths {
String get icSendToast => _getImagePath('ic_send_toast.svg'); String get icSendToast => _getImagePath('ic_send_toast.svg');
String get icClearTextSearch => _getImagePath('ic_clear_text_search.svg'); String get icClearTextSearch => _getImagePath('ic_clear_text_search.svg');
String get icRenameMailbox => _getImagePath('ic_rename_mailbox.svg'); String get icRenameMailbox => _getImagePath('ic_rename_mailbox.svg');
String get icDeleteToast => _getImagePath('ic_delete_toast.svg');
String get icRemoveDialog => _getImagePath('ic_remove_dialog.svg');
String _getImagePath(String imageName) { String _getImagePath(String imageName) {
return AssetsPaths.images + imageName; return AssetsPaths.images + imageName;
@@ -0,0 +1,71 @@
import 'package:core/core.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
typedef OnConfirmActionClick = void Function();
typedef OnCancelActionClick = void Function();
class ConfirmationDialogActionSheetBuilder {
final BuildContext _context;
String? _messageText;
String? _confirmText;
String? _cancelText;
OnConfirmActionClick?_onConfirmActionClick;
OnCancelActionClick? _onCancelActionClick;
ConfirmationDialogActionSheetBuilder(this._context);
void onConfirmAction(String confirmText, OnConfirmActionClick onConfirmActionClick) {
_onConfirmActionClick = onConfirmActionClick;
_confirmText = confirmText;
}
void onCancelAction(String cancelText, OnCancelActionClick onCancelActionClick) {
_onCancelActionClick = onCancelActionClick;
_cancelText = cancelText;
}
void messageText(String message) {
_messageText = message;
}
void show() {
showCupertinoModalPopup(
context: _context,
barrierColor: AppColor.colorDefaultCupertinoActionSheet,
builder: (context) => CupertinoActionSheet(
actions: [
Container(
padding: EdgeInsets.symmetric(vertical: 8, horizontal: 10),
color: Colors.white,
child: CupertinoActionSheetAction(
child: Text(
_messageText ?? '',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 14, color: AppColor.colorMessageConfirmDialog)),
onPressed: () => {},
)
),
Container(
color: Colors.white,
child: CupertinoActionSheetAction(
child: Text(
_confirmText ?? '',
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 20, color: AppColor.colorActionDeleteConfirmDialog)),
onPressed: () => _onConfirmActionClick?.call(),
)
),
],
cancelButton: CupertinoActionSheetAction(
child: Text(
_cancelText ?? '',
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 20, color: AppColor.colorActionCancelDialog)),
onPressed: () => _onCancelActionClick?.call(),
),
)
);
}
}
@@ -29,7 +29,7 @@ class CupertinoActionSheetBuilder {
_actionTiles.addAll(tiles); _actionTiles.addAll(tiles);
} }
void build() { void show() {
showCupertinoModalPopup( showCupertinoModalPopup(
context: _context, context: _context,
barrierColor: AppColor.colorDefaultCupertinoActionSheet, barrierColor: AppColor.colorDefaultCupertinoActionSheet,
@@ -0,0 +1,148 @@
import 'package:core/core.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
typedef OnConfirmButtonAction = void Function();
typedef OnCancelButtonAction = void Function();
typedef OnCloseButtonAction = void Function();
class ConfirmDialogBuilder {
final ImagePaths _imagePath;
Key? _key;
String _title = '';
String _content = '';
String _confirmText = '';
String _cancelText = '';
Widget? _iconWidget;
OnConfirmButtonAction? _onConfirmButtonAction;
OnCancelButtonAction? _onCancelButtonAction;
OnCloseButtonAction? _onCloseButtonAction;
ConfirmDialogBuilder(this._imagePath);
void key(Key key) {
_key = key;
}
void title(String title) {
_title = title;
}
void content(String content) {
_content = content;
}
void addIcon(Widget? icon) {
_iconWidget = icon;
}
void onConfirmButtonAction(String confirmText, OnConfirmButtonAction? onConfirmButtonAction) {
_confirmText = confirmText;
_onConfirmButtonAction = onConfirmButtonAction;
}
void onCancelButtonAction(String cancelText, OnCancelButtonAction? onCancelButtonAction) {
_cancelText = cancelText;
_onCancelButtonAction = onCancelButtonAction;
}
void onCloseButtonAction(OnCloseButtonAction? onCloseButtonAction) {
_onCloseButtonAction = onCloseButtonAction;
}
Widget build() {
return Dialog(
key: _key,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16))),
insetPadding: EdgeInsets.symmetric(horizontal: 24.0, vertical: 16.0),
child: Container(
margin: EdgeInsets.zero,
padding: EdgeInsets.zero,
width: 400,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(16))),
child: Wrap(children: [
Align(
alignment: Alignment.centerRight,
child: IconButton(
padding: EdgeInsets.only(top: 16, right: 16),
onPressed: () => _onCloseButtonAction?.call(),
icon: SvgPicture.asset(_imagePath.icCloseMailbox, width: 30, height: 30, fit: BoxFit.fill))),
if (_iconWidget != null)
Container(
margin: EdgeInsets.only(top: 24),
alignment: Alignment.center,
child: _iconWidget,
),
Padding(
padding: EdgeInsets.only(top: 12),
child: Center(
child: Text(
_title,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 20.0, color: AppColor.colorActionDeleteConfirmDialog, fontWeight: FontWeight.w500)
)
)
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 24),
child: Center(
child: Text(_content,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 17.0, color: AppColor.colorMessageDialog)
),
),
),
Padding(
padding: EdgeInsets.only(bottom: 16, left: 16, right: 16),
child: Row(
children: [
Expanded(
child: _buildButton(name: _cancelText, action: _onCancelButtonAction)
),
SizedBox(width: 16),
Expanded(
child: _buildButton(
name: _confirmText,
bgColor: AppColor.colorConfirmActionDialog,
nameColor: AppColor.colorActionDeleteConfirmDialog,
action: _onConfirmButtonAction)
)
]
))
])
),
);
}
Widget _buildButton({
String? name, Color? nameColor, Color? bgColor, Function? action
}) {
return SizedBox(
width: double.infinity,
height: 48,
child: ElevatedButton(
onPressed: () => action?.call(),
style: ButtonStyle(
foregroundColor: MaterialStateProperty.resolveWith<Color>(
(Set<MaterialState> states) => bgColor ?? AppColor.colorTextButton),
backgroundColor: MaterialStateProperty.resolveWith<Color>(
(Set<MaterialState> states) => bgColor ?? AppColor.colorTextButton),
shape: MaterialStateProperty.all(RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: BorderSide(width: 0, color: bgColor ?? AppColor.colorTextButton),
)),
padding: MaterialStateProperty.resolveWith<EdgeInsets>(
(Set<MaterialState> states) => EdgeInsets.symmetric(horizontal: 16)),
elevation: MaterialStateProperty.resolveWith<double>((Set<MaterialState> states) => 0)),
child: Text(name ?? '',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 17, fontWeight: FontWeight.w500, color: nameColor ?? Colors.white)),
)
);
}
}
@@ -276,7 +276,7 @@ class DestinationPickerView extends GetWidget<DestinationPickerController> {
_imagePaths, _imagePaths,
mailboxNode, mailboxNode,
mailboxDisplayed: MailboxDisplayed.destinationPicker) mailboxDisplayed: MailboxDisplayed.destinationPicker)
..addOnSelectMailboxFolderClick((mailboxNode) => controller.selectMailboxAction(mailboxNode.item.toPresentationMailboxWithMailboxPath( ..addOnOpenMailboxFolderClick((mailboxNode) => controller.selectMailboxAction(mailboxNode.item.toPresentationMailboxWithMailboxPath(
mailboxNode.getPathMailboxNode( mailboxNode.getPathMailboxNode(
controller.folderMailboxTree, controller.folderMailboxTree,
controller.defaultMailboxList, controller.defaultMailboxList,
@@ -292,7 +292,7 @@ class DestinationPickerView extends GetWidget<DestinationPickerController> {
_imagePaths, _imagePaths,
mailboxNode, mailboxNode,
mailboxDisplayed: MailboxDisplayed.destinationPicker) mailboxDisplayed: MailboxDisplayed.destinationPicker)
..addOnSelectMailboxFolderClick((mailboxNode) => controller.selectMailboxAction(mailboxNode.item.toPresentationMailboxWithMailboxPath( ..addOnOpenMailboxFolderClick((mailboxNode) => controller.selectMailboxAction(mailboxNode.item.toPresentationMailboxWithMailboxPath(
mailboxNode.getPathMailboxNode( mailboxNode.getPathMailboxNode(
controller.folderMailboxTree, controller.folderMailboxTree,
controller.defaultMailboxList, controller.defaultMailboxList,
@@ -16,4 +16,6 @@ abstract class MailboxDataSource {
Future<void> update({List<Mailbox>? updated, List<Mailbox>? created, List<MailboxId>? destroyed}); Future<void> update({List<Mailbox>? updated, List<Mailbox>? created, List<MailboxId>? destroyed});
Future<Mailbox?> createNewMailbox(AccountId accountId, CreateNewMailboxRequest newMailboxRequest); 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) { Future<Mailbox?> createNewMailbox(AccountId accountId, CreateNewMailboxRequest newMailboxRequest) {
throw UnimplementedError(); throw UnimplementedError();
} }
@override
Future<bool> deleteMultipleMailbox(AccountId accountId, List<MailboxId> mailboxIds) {
throw UnimplementedError();
}
} }
@@ -50,4 +50,13 @@ class MailboxDataSourceImpl extends MailboxDataSource {
throw error; 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; 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) { Future<Mailbox?> createNewMailbox(AccountId accountId, CreateNewMailboxRequest newMailboxRequest) {
return mapDataSource[DataSourceType.network]!.createNewMailbox(accountId, 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); Stream<MailboxResponse> refresh(AccountId accountId, State currentState);
Future<Mailbox?> createNewMailbox(AccountId accountId, CreateNewMailboxRequest newMailboxRequest); 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/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/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/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/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/refresh_all_mailbox_interactor.dart';
import 'package:tmail_ui_user/features/mailbox/domain/usecases/search_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<RefreshAllMailboxInteractor>(),
Get.find<CreateNewMailboxInteractor>(), Get.find<CreateNewMailboxInteractor>(),
Get.find<SearchMailboxInteractor>(), Get.find<SearchMailboxInteractor>(),
Get.find<DeleteMultipleMailboxInteractor>(),
Get.find<TreeBuilder>(), Get.find<TreeBuilder>(),
Get.find<Uuid>(), Get.find<Uuid>(),
Get.find<AppToast>(), Get.find<AppToast>(),
@@ -58,7 +60,6 @@ class MailboxBindings extends BaseBindings {
void bindingsDataSource() { void bindingsDataSource() {
Get.lazyPut<MailboxDataSource>(() => Get.find<MailboxDataSourceImpl>()); Get.lazyPut<MailboxDataSource>(() => Get.find<MailboxDataSourceImpl>());
Get.lazyPut<StateDataSource>(() => Get.find<StateDataSourceImpl>()); Get.lazyPut<StateDataSource>(() => Get.find<StateDataSourceImpl>());
} }
@override @override
@@ -66,7 +67,6 @@ class MailboxBindings extends BaseBindings {
Get.lazyPut(() => MailboxDataSourceImpl(Get.find<MailboxAPI>())); Get.lazyPut(() => MailboxDataSourceImpl(Get.find<MailboxAPI>()));
Get.lazyPut(() => MailboxCacheDataSourceImpl(Get.find<MailboxCacheManager>())); Get.lazyPut(() => MailboxCacheDataSourceImpl(Get.find<MailboxCacheManager>()));
Get.lazyPut(() => StateDataSourceImpl(Get.find<StateCacheClient>())); Get.lazyPut(() => StateDataSourceImpl(Get.find<StateCacheClient>()));
} }
@override @override
@@ -76,13 +76,13 @@ class MailboxBindings extends BaseBindings {
Get.lazyPut(() => RefreshAllMailboxInteractor(Get.find<MailboxRepository>())); Get.lazyPut(() => RefreshAllMailboxInteractor(Get.find<MailboxRepository>()));
Get.lazyPut(() => CreateNewMailboxInteractor(Get.find<MailboxRepository>())); Get.lazyPut(() => CreateNewMailboxInteractor(Get.find<MailboxRepository>()));
Get.lazyPut(() => SearchMailboxInteractor()); Get.lazyPut(() => SearchMailboxInteractor());
Get.lazyPut(() => DeleteMultipleMailboxInteractor(Get.find<MailboxRepository>()));
} }
@override @override
void bindingsRepository() { void bindingsRepository() {
Get.lazyPut<CredentialRepository>(() => Get.find<CredentialRepositoryImpl>()); Get.lazyPut<CredentialRepository>(() => Get.find<CredentialRepositoryImpl>());
Get.lazyPut<MailboxRepository>(() => Get.find<MailboxRepositoryImpl>()); Get.lazyPut<MailboxRepository>(() => Get.find<MailboxRepositoryImpl>());
} }
@override @override
@@ -1,7 +1,7 @@
import 'package:core/core.dart'; import 'package:core/core.dart';
import 'package:dartz/dartz.dart'; import 'package:dartz/dartz.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart'; import 'package:get/get.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/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/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/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/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/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/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/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/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/refresh_all_mailbox_interactor.dart';
import 'package:tmail_ui_user/features/mailbox/domain/usecases/search_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 RefreshAllMailboxInteractor _refreshAllMailboxInteractor;
final CreateNewMailboxInteractor _createNewMailboxInteractor; final CreateNewMailboxInteractor _createNewMailboxInteractor;
final SearchMailboxInteractor _searchMailboxInteractor; final SearchMailboxInteractor _searchMailboxInteractor;
final DeleteMultipleMailboxInteractor _deleteMultipleMailboxInteractor;
final TreeBuilder _treeBuilder; final TreeBuilder _treeBuilder;
final Uuid _uuid; final Uuid _uuid;
final AppToast _appToast; final AppToast _appToast;
@@ -77,6 +80,7 @@ class MailboxController extends BaseController {
this._refreshAllMailboxInteractor, this._refreshAllMailboxInteractor,
this._createNewMailboxInteractor, this._createNewMailboxInteractor,
this._searchMailboxInteractor, this._searchMailboxInteractor,
this._deleteMultipleMailboxInteractor,
this._treeBuilder, this._treeBuilder,
this._uuid, this._uuid,
this._appToast, this._appToast,
@@ -144,6 +148,8 @@ class MailboxController extends BaseController {
_createNewMailboxFailure(failure); _createNewMailboxFailure(failure);
} else if (failure is SearchMailboxFailure) { } else if (failure is SearchMailboxFailure) {
_searchMailboxFailure(failure); _searchMailboxFailure(failure);
} else if (failure is DeleteMultipleMailboxFailure) {
_deleteMailboxFailure(failure);
} }
}, },
(success) { (success) {
@@ -151,6 +157,12 @@ class MailboxController extends BaseController {
_createNewMailboxSuccess(success); _createNewMailboxSuccess(success);
} else if (success is SearchMailboxSuccess) { } else if (success is SearchMailboxSuccess) {
_searchMailboxSuccess(success); _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; 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) { void disableSearch(BuildContext context) {
_cancelSelectMailbox();
listMailboxSearched.clear(); listMailboxSearched.clear();
searchState.value = searchState.value.disableSearchState(); searchState.value = searchState.value.disableSearchState();
searchQuery.value = SearchQuery.initial(); searchQuery.value = SearchQuery.initial();
@@ -446,13 +463,80 @@ class MailboxController extends BaseController {
List<PresentationMailbox> selectionMailbox) { List<PresentationMailbox> selectionMailbox) {
switch(actions) { switch(actions) {
case MailboxActions.delete: case MailboxActions.delete:
_openConfirmationDialogDeleteMailboxAction(context, selectionMailbox.first);
break; break;
default: default:
break; 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) { void closeMailboxScreen(BuildContext context) {
_cancelSelectMailbox();
mailboxDashBoardController.closeDrawer(); mailboxDashBoardController.closeDrawer();
} }
@@ -70,14 +70,28 @@ class MailboxView extends GetWidget<MailboxController> {
bottom: 10), bottom: 10),
child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [ child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [
!responsiveUtils.isDesktop(context) ? _buildCloseScreenButton(context) : SizedBox(width: 50), !responsiveUtils.isDesktop(context) ? _buildCloseScreenButton(context) : SizedBox(width: 50),
Obx(() => controller.currentSelectMode.value == SelectMode.INACTIVE Obx(() {
? SizedBox(width: 40) if (controller.isSearchActive()) {
: SizedBox(width: 49)), return controller.listMailboxSearched.isNotEmpty
? SizedBox(width: controller.isSelectionEnabled() ? 49 : 40)
: SizedBox.shrink();
} else {
return SizedBox(width: controller.isSelectionEnabled() ? 49 : 40);
}
}),
Expanded(child: Text( Expanded(child: Text(
AppLocalizations.of(context).folders, AppLocalizations.of(context).folders,
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle(fontSize: 20, color: AppColor.colorNameEmail, fontWeight: FontWeight.w700))), 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), _buildAddNewFolderButton(context),
])), ])),
Divider(color: AppColor.colorDividerMailbox, height: 0.5, thickness: 0.2), 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))); 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( return Material(
borderRadius: BorderRadius.circular(20), borderRadius: BorderRadius.circular(20),
color: Colors.transparent, color: Colors.transparent,
child: TextButton( child: TextButton(
child: Text( 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)), style: TextStyle(fontSize: 17, color: AppColor.colorTextButton)),
onPressed: () => selectMode == SelectMode.INACTIVE onPressed: () => !isSelectionEnabled
? controller.enableSelectionMailbox() ? controller.enableSelectionMailbox()
: controller.disableSelectionMailbox() : controller.disableSelectionMailbox()
) )
@@ -283,7 +297,6 @@ class MailboxView extends GetWidget<MailboxController> {
..addDecoration(BoxDecoration(borderRadius: BorderRadius.circular(12), color: AppColor.colorBgSearchBar)) ..addDecoration(BoxDecoration(borderRadius: BorderRadius.circular(12), color: AppColor.colorBgSearchBar))
..addIconClearText(SvgPicture.asset(imagePaths.icClearTextSearch, width: 20, height: 20, fit: BoxFit.fill)) ..addIconClearText(SvgPicture.asset(imagePaths.icClearTextSearch, width: 20, height: 20, fit: BoxFit.fill))
..setHintText(AppLocalizations.of(context).hint_search_mailboxes) ..setHintText(AppLocalizations.of(context).hint_search_mailboxes)
..addOnCancelSearchPressed(() => controller.disableSearch(context))
..addOnClearTextSearchAction(() => controller.clearSearchText()) ..addOnClearTextSearchAction(() => controller.clearSearchText())
..addOnTextChangeSearchAction((query) => controller.searchMailbox(query)) ..addOnTextChangeSearchAction((query) => controller.searchMailbox(query))
..addOnSearchTextAction((query) => controller.searchMailbox(query))) ..addOnSearchTextAction((query) => controller.searchMailbox(query)))
@@ -324,8 +337,10 @@ class MailboxView extends GetWidget<MailboxController> {
imagePaths, imagePaths,
listMailbox[index], listMailbox[index],
isSearchActive: controller.isSearchActive(), isSearchActive: controller.isSearchActive(),
allSelectMode: controller.currentSelectMode.value,
isLastElement: index == listMailbox.length - 1) isLastElement: index == listMailbox.length - 1)
..addOnOpenMailboxAction((mailbox) => controller.openMailbox(context, mailbox))) ..addOnOpenMailboxAction((mailbox) => controller.openMailbox(context, mailbox))
..addOnSelectMailboxActionClick((mailbox) => controller.selectMailbox(context, mailbox)))
.build())); .build()));
} }
@@ -135,12 +135,12 @@ extension MailboxNodeExtension on MailboxNode {
); );
} }
MailboxNode toSelectedMailboxNode({required SelectMode selectMode, ExpandMode? newExpandMode}) { MailboxNode toSelectedMailboxNode({required SelectMode selectMode, ExpandMode? newExpandMode}) {
return MailboxNode( return MailboxNode(
item, item,
childrenItems: childrenItems, childrenItems: childrenItems,
expandMode: newExpandMode ?? expandMode, expandMode: newExpandMode ?? expandMode,
selectMode: selectMode, selectMode: selectMode,
); );
} }
} }
@@ -27,6 +27,24 @@ class MailboxTree with EquatableMixin {
return result; 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 @override
List<Object?> get props => [root]; List<Object?> get props => [root];
} }
@@ -448,7 +448,7 @@ class ThreadController extends BaseController {
(CupertinoActionSheetBuilder(context) (CupertinoActionSheetBuilder(context)
..addTiles(actionTiles) ..addTiles(actionTiles)
..addCancelButton(cancelButton)) ..addCancelButton(cancelButton))
.build(); .show();
} }
void openFilterMessagesForTablet(BuildContext context, RelativeRect? position, List<PopupMenuEntry> popupMenuItems) async { void openFilterMessagesForTablet(BuildContext context, RelativeRect? position, List<PopupMenuEntry> popupMenuItems) async {
@@ -676,4 +676,30 @@ class AppLocalizations {
'Rename', 'Rename',
name: '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]
);
}
} }