TF-80 Move multiple email to mailbox from thread view
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
<svg width="122" height="100" viewBox="0 0 122 100" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M60.6033 99.4921C85.237 99.4921 105.207 95.7607 105.207 91.1579C105.207 86.555 85.237 82.8237 60.6033 82.8237C35.9696 82.8237 16 86.555 16 91.1579C16 95.7607 35.9696 99.4921 60.6033 99.4921Z" fill="#F5F5F7" fill-opacity="0.8"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M96.2855 66.6737L80.5454 47.3019C79.7902 46.3901 78.6862 45.8381 77.5237 45.8381H43.6816C42.5198 45.8381 41.4158 46.3901 40.6606 47.3019L24.9211 66.6737V76.7941H96.2862V66.6737H96.2855Z" fill="#AEB8C2"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M38.2566 20.8355H82.9493C83.6473 20.8355 84.3166 21.1128 84.8102 21.6063C85.3037 22.0998 85.5809 22.7692 85.5809 23.4671V84.8776C85.5809 85.5755 85.3037 86.2449 84.8102 86.7384C84.3166 87.2319 83.6473 87.5092 82.9493 87.5092H38.2566C37.5586 87.5092 36.8893 87.2319 36.3958 86.7384C35.9023 86.2449 35.625 85.5755 35.625 84.8776V23.4671C35.625 22.7692 35.9023 22.0998 36.3958 21.6063C36.8893 21.1128 37.5586 20.8355 38.2566 20.8355V20.8355Z" fill="#F5F5F7"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M44.0777 27.3835H77.1283C77.4773 27.3835 77.812 27.5222 78.0587 27.7689C78.3055 28.0157 78.4441 28.3504 78.4441 28.6993V45.1184C78.4441 45.4674 78.3055 45.8021 78.0587 46.0488C77.812 46.2956 77.4773 46.4342 77.1283 46.4342H44.0777C43.7287 46.4342 43.394 46.2956 43.1472 46.0488C42.9005 45.8021 42.7619 45.4674 42.7619 45.1184V28.6993C42.7619 28.3504 42.9005 28.0157 43.1472 27.7689C43.394 27.5222 43.7287 27.3835 44.0777 27.3835ZM44.25 53.577H76.9559C77.3506 53.577 77.7291 53.7338 78.0082 54.0128C78.2873 54.2919 78.4441 54.6704 78.4441 55.0651C78.4441 55.4598 78.2873 55.8383 78.0082 56.1174C77.7291 56.3965 77.3506 56.5533 76.9559 56.5533H44.25C43.8553 56.5533 43.4768 56.3965 43.1977 56.1174C42.9186 55.8383 42.7619 55.4598 42.7619 55.0651C42.7619 54.6704 42.9186 54.2919 43.1977 54.0128C43.4768 53.7338 43.8553 53.577 44.25 53.577ZM44.25 61.3158H76.9559C77.3507 61.3158 77.7293 61.4726 78.0085 61.7518C78.2876 62.0309 78.4444 62.4095 78.4444 62.8043C78.4444 63.199 78.2876 63.5776 78.0085 63.8568C77.7293 64.1359 77.3507 64.2928 76.9559 64.2928H44.25C43.8552 64.2928 43.4766 64.1359 43.1975 63.8568C42.9184 63.5776 42.7615 63.199 42.7615 62.8043C42.7615 62.4095 42.9184 62.0309 43.1975 61.7518C43.4766 61.4726 43.8552 61.3158 44.25 61.3158ZM96.1402 89.9355C95.6303 91.9559 93.8395 93.4618 91.7092 93.4618H29.4967C27.3665 93.4618 25.5757 91.9553 25.0665 89.9355C24.9693 89.5505 24.9203 89.1549 24.9204 88.7579V66.6743H42.2349C44.1474 66.6743 45.6888 68.2849 45.6888 70.2401V70.2664C45.6888 72.221 47.248 73.7993 49.1605 73.7993H72.0454C73.9579 73.7993 75.5171 72.2066 75.5171 70.2513V70.2434C75.5171 68.2881 77.0586 66.6737 78.9711 66.6737H96.2855V88.7585C96.2855 89.1645 96.2349 89.5585 96.1402 89.9355Z" fill="#DCE0E6"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M98.3165 21.9026L93.823 23.6461C93.7076 23.6909 93.5818 23.7023 93.4602 23.6789C93.3386 23.6555 93.2261 23.5982 93.1356 23.5137C93.045 23.4292 92.9802 23.3208 92.9486 23.2011C92.9169 23.0813 92.9197 22.9551 92.9566 22.8368L94.2309 18.7533C92.5276 16.8164 91.5276 14.4546 91.5276 11.9059C91.5276 5.33026 98.1842 0 106.396 0C114.606 0 121.263 5.33026 121.263 11.9059C121.263 18.4816 114.607 23.8118 106.395 23.8118C103.416 23.8118 100.643 23.1105 98.3165 21.9026Z" fill="#DCE0E6"/>
|
||||
<path d="M112.253 14.0559C113.288 14.0559 114.127 13.2268 114.127 12.204C114.127 11.1811 113.288 10.352 112.253 10.352C111.217 10.352 110.378 11.1811 110.378 12.204C110.378 13.2268 111.217 14.0559 112.253 14.0559Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M102.413 13.8243H98.6644L100.571 10.5836L102.413 13.8243ZM104.756 10.5836H108.035V13.8243H104.756V10.5836Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.7 KiB |
@@ -36,6 +36,8 @@ export 'presentation/views/context_menu/context_menu_builder.dart';
|
||||
export 'presentation/views/context_menu/context_menu_header_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/downloading_file_dialog_builder.dart';
|
||||
export 'presentation/views/background/background_widget_builder.dart';
|
||||
|
||||
// Resources
|
||||
export 'presentation/resources/assets_paths.dart';
|
||||
|
||||
@@ -41,6 +41,7 @@ class ImagePaths {
|
||||
String get icDownload => _getImagePath('ic_download.svg');
|
||||
String get icMore => _getImagePath('ic_more.svg');
|
||||
String get icPhotoLibrary => _getImagePath('ic_photo_library.svg');
|
||||
String get icEmptyImageDefault => _getImagePath('empty_image_default.svg');
|
||||
|
||||
String _getImagePath(String imageName) {
|
||||
return AssetsPaths.images + imageName;
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
|
||||
import 'package:core/core.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
|
||||
class BackgroundWidgetBuilder {
|
||||
Key? _key;
|
||||
SvgPicture? _image;
|
||||
String? _text;
|
||||
|
||||
final BuildContext _context;
|
||||
|
||||
BackgroundWidgetBuilder(this._context);
|
||||
|
||||
void key(Key key) {
|
||||
_key = key;
|
||||
}
|
||||
|
||||
void image(SvgPicture image) {
|
||||
_image = image;
|
||||
}
|
||||
|
||||
void text(String text) {
|
||||
_text = text;
|
||||
}
|
||||
|
||||
Widget build() {
|
||||
return Center(
|
||||
key: _key ?? Key('BackgroundWidgetBuilder'),
|
||||
child: CustomScrollView(
|
||||
slivers: [
|
||||
SliverFillRemaining(
|
||||
child: Container(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
_image ?? SizedBox.shrink(),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: _image != null ? 16 : 0),
|
||||
child: Text(
|
||||
_text ?? '',
|
||||
style: TextStyle(color: AppColor.baseTextColor, fontSize: 16),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
height: MediaQuery.of(_context).size.height,
|
||||
),
|
||||
)
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -50,84 +50,43 @@ class _TreeViewData extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class TreeViewChild extends StatefulWidget {
|
||||
final bool? startExpanded;
|
||||
class TreeViewChild {
|
||||
final BuildContext context;
|
||||
final bool? isExpanded;
|
||||
final Widget parent;
|
||||
final List<Widget> children;
|
||||
final VoidCallback? onTap;
|
||||
|
||||
TreeViewChild({
|
||||
required this.parent,
|
||||
required this.children,
|
||||
this.startExpanded,
|
||||
this.onTap,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
TreeViewChild(
|
||||
this.context,
|
||||
{
|
||||
required this.parent,
|
||||
required this.children,
|
||||
this.isExpanded,
|
||||
this.onTap,
|
||||
Key? key,
|
||||
}
|
||||
);
|
||||
|
||||
@override
|
||||
TreeViewChildState createState() => TreeViewChildState();
|
||||
|
||||
TreeViewChild copyWith(
|
||||
TreeViewChild source, {
|
||||
bool? startExpanded,
|
||||
Widget? parent,
|
||||
List<Widget>? children,
|
||||
VoidCallback? onTap,
|
||||
}) {
|
||||
return TreeViewChild(
|
||||
parent: parent ?? source.parent,
|
||||
children: children ?? source.children,
|
||||
startExpanded: startExpanded ?? source.startExpanded,
|
||||
onTap: onTap ?? source.onTap,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TreeViewChildState extends State<TreeViewChild> {
|
||||
bool? isExpanded;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
isExpanded = widget.startExpanded;
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
isExpanded = widget.startExpanded ?? TreeView.of(context).startExpanded;
|
||||
super.didChangeDependencies();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget build() {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
GestureDetector(
|
||||
child: widget.parent,
|
||||
child: parent,
|
||||
onTap: () {
|
||||
if (widget.onTap != null) {
|
||||
widget.onTap!();
|
||||
if (onTap != null) {
|
||||
onTap!();
|
||||
}
|
||||
toggleExpanded();
|
||||
},
|
||||
),
|
||||
AnimatedContainer(
|
||||
duration: Duration(milliseconds: 400),
|
||||
child: isExpanded!
|
||||
? Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: widget.children,
|
||||
)
|
||||
? Column(mainAxisSize: MainAxisSize.min, children: children)
|
||||
: Offstage(),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
void toggleExpanded() {
|
||||
setState(() {
|
||||
this.isExpanded = !this.isExpanded!;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import 'package:tmail_ui_user/features/mailbox/domain/usecases/get_all_mailbox_i
|
||||
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_node.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_tree.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_tree_builder.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox/presentation/extensions/list_mailbox_node_extension.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
||||
|
||||
@@ -17,7 +18,9 @@ class DestinationPickerController extends BaseController {
|
||||
final GetAllMailboxInteractor _getAllMailboxInteractor;
|
||||
final TreeBuilder _treeBuilder;
|
||||
|
||||
final folderMailboxTree = MailboxTree(MailboxNode.root()).obs;
|
||||
MailboxTree folderMailboxTree = MailboxTree(MailboxNode.root());
|
||||
final defaultMailboxList = <PresentationMailbox>[].obs;
|
||||
final folderMailboxNodeList = <MailboxNode>[].obs;
|
||||
|
||||
DestinationPickerArguments? destinationPickerArguments;
|
||||
|
||||
@@ -46,7 +49,13 @@ class DestinationPickerController extends BaseController {
|
||||
}
|
||||
|
||||
@override
|
||||
void onDone() {}
|
||||
void onDone() {
|
||||
viewState.value.map((success) {
|
||||
if (success is GetAllMailboxSuccess) {
|
||||
defaultMailboxList.value = success.defaultMailboxList;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void onError(error) {}
|
||||
@@ -59,7 +68,20 @@ class DestinationPickerController extends BaseController {
|
||||
}
|
||||
|
||||
void _buildTree(List<PresentationMailbox> folderMailboxList) async {
|
||||
folderMailboxTree.value = await _treeBuilder.generateMailboxTree(folderMailboxList);
|
||||
folderMailboxTree = await _treeBuilder.generateMailboxTree(folderMailboxList);
|
||||
folderMailboxNodeList.value = folderMailboxTree.root.childrenItems ?? [];
|
||||
}
|
||||
|
||||
void toggleMailboxFolder(MailboxNode selectedMailboxNode) {
|
||||
final newExpandMode = selectedMailboxNode.expandMode == ExpandMode.COLLAPSE
|
||||
? ExpandMode.EXPAND
|
||||
: ExpandMode.COLLAPSE;
|
||||
|
||||
final newMailboxNodeList = folderMailboxNodeList.updateNode(
|
||||
selectedMailboxNode.item.id,
|
||||
selectedMailboxNode.copyWith(newExpandMode: newExpandMode)) ?? [];
|
||||
|
||||
folderMailboxNodeList.value = newMailboxNodeList;
|
||||
}
|
||||
|
||||
void moveEmailToMailboxAction(PresentationMailbox destinationMailbox) {
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:core/presentation/resources/image_paths.dart';
|
||||
import 'package:core/presentation/utils/responsive_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:model/model.dart';
|
||||
import 'package:tmail_ui_user/features/destination_picker/presentation/destination_picker_controller.dart';
|
||||
import 'package:tmail_ui_user/features/destination_picker/presentation/widgets/app_bar_destination_picker_builder.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox/domain/state/get_all_mailboxes_state.dart';
|
||||
@@ -87,7 +88,7 @@ class DestinationPickerView extends GetWidget<DestinationPickerController> {
|
||||
Widget _buildLoadingView() {
|
||||
return Obx(() => controller.viewState.value.fold(
|
||||
(failure) => SizedBox.shrink(),
|
||||
(success) => success == UIState.loading
|
||||
(success) => success is LoadingState
|
||||
? Center(child: Padding(
|
||||
padding: EdgeInsets.only(top: 16),
|
||||
child: SizedBox(
|
||||
@@ -127,7 +128,8 @@ class DestinationPickerView extends GetWidget<DestinationPickerController> {
|
||||
_responsiveUtils,
|
||||
defaultMailboxList[index],
|
||||
mailboxDisplayed: MailboxDisplayed.destinationPicker)
|
||||
..onOpenMailboxAction((mailbox) => controller.moveEmailToMailboxAction(mailbox)))
|
||||
..onOpenMailboxAction((mailbox) =>
|
||||
controller.moveEmailToMailboxAction(mailbox.toPresentationMailboxWithMailboxPath(mailbox.name?.name ?? ''))))
|
||||
.build());
|
||||
} else {
|
||||
return SizedBox.shrink();
|
||||
@@ -137,7 +139,7 @@ class DestinationPickerView extends GetWidget<DestinationPickerController> {
|
||||
}
|
||||
|
||||
Widget _buildFolderMailbox(BuildContext context) {
|
||||
return Obx(() => controller.folderMailboxTree.value.root.hasChildren()
|
||||
return Obx(() => controller.folderMailboxNodeList.isNotEmpty
|
||||
? Transform(
|
||||
transform: Matrix4.translationValues(-12.0, 0.0, 0.0),
|
||||
child: Padding(
|
||||
@@ -145,7 +147,7 @@ class DestinationPickerView extends GetWidget<DestinationPickerController> {
|
||||
child: TreeView(
|
||||
startExpanded: false,
|
||||
key: Key('folder_mailbox_list'),
|
||||
children: _buildListChildTileWidget(context, controller.folderMailboxTree.value.root.childrenItems!))))
|
||||
children: _buildListChildTileWidget(context, controller.folderMailboxNodeList))))
|
||||
: SizedBox.shrink()
|
||||
);
|
||||
}
|
||||
@@ -155,28 +157,40 @@ class DestinationPickerView extends GetWidget<DestinationPickerController> {
|
||||
.map((mailboxNode) => mailboxNode.hasChildren()
|
||||
? Padding(
|
||||
padding: EdgeInsets.only(left: 20),
|
||||
child: TreeViewChild(
|
||||
key: Key('children_tree_mailbox_child'),
|
||||
parent: MailBoxFolderTileBuilder(
|
||||
context,
|
||||
_imagePaths,
|
||||
_responsiveUtils,
|
||||
mailboxNode,
|
||||
mailboxDisplayed: MailboxDisplayed.destinationPicker)
|
||||
.build(),
|
||||
children: _buildListChildTileWidget(context, mailboxNode.childrenItems!)))
|
||||
child: TreeViewChild(
|
||||
context,
|
||||
key: Key('children_tree_mailbox_child'),
|
||||
isExpanded: mailboxNode.expandMode == ExpandMode.EXPAND,
|
||||
parent: (MailBoxFolderTileBuilder(
|
||||
context,
|
||||
_imagePaths,
|
||||
_responsiveUtils,
|
||||
mailboxNode,
|
||||
mailboxDisplayed: MailboxDisplayed.destinationPicker)
|
||||
..addOnSelectMailboxFolderClick((mailboxNode) => controller.moveEmailToMailboxAction(mailboxNode.item.toPresentationMailboxWithMailboxPath(
|
||||
mailboxNode.getPathMailboxNode(
|
||||
controller.folderMailboxTree,
|
||||
controller.defaultMailboxList,
|
||||
))))
|
||||
..addOnExpandFolderActionClick((mailboxNode) => controller.toggleMailboxFolder(mailboxNode)))
|
||||
.build(),
|
||||
children: _buildListChildTileWidget(context, mailboxNode.childrenItems!)
|
||||
).build())
|
||||
: Padding(
|
||||
padding: EdgeInsets.only(left: 20),
|
||||
child: GestureDetector(
|
||||
onTap: () => controller.moveEmailToMailboxAction(mailboxNode.item),
|
||||
child: MailBoxFolderTileBuilder(
|
||||
child: (MailBoxFolderTileBuilder(
|
||||
context,
|
||||
_imagePaths,
|
||||
_responsiveUtils,
|
||||
mailboxNode,
|
||||
mailboxDisplayed: MailboxDisplayed.destinationPicker)
|
||||
.build(),
|
||||
)))
|
||||
..addOnSelectMailboxFolderClick((mailboxNode) => controller.moveEmailToMailboxAction(mailboxNode.item.toPresentationMailboxWithMailboxPath(
|
||||
mailboxNode.getPathMailboxNode(
|
||||
controller.folderMailboxTree,
|
||||
controller.defaultMailboxList,
|
||||
)))))
|
||||
.build(),
|
||||
))
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ abstract class EmailDataSource {
|
||||
|
||||
Future<bool> sendEmail(AccountId accountId, EmailRequest emailRequest);
|
||||
|
||||
Future<bool> markAsRead(AccountId accountId, EmailId emailId, ReadActions readActions);
|
||||
Future<List<Email>> markAsRead(AccountId accountId, List<Email> emails, ReadActions readActions);
|
||||
|
||||
Future<List<DownloadTaskId>> downloadAttachments(
|
||||
List<Attachment> attachments,
|
||||
@@ -28,5 +28,5 @@ abstract class EmailDataSource {
|
||||
CancelToken cancelToken
|
||||
);
|
||||
|
||||
Future<bool> moveToMailbox(AccountId accountId, MoveRequest moveRequest);
|
||||
Future<List<EmailId>> moveToMailbox(AccountId accountId, MoveRequest moveRequest);
|
||||
}
|
||||
@@ -32,9 +32,9 @@ class EmailDataSourceImpl extends EmailDataSource {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> markAsRead(AccountId accountId, EmailId emailId, ReadActions readActions) {
|
||||
Future<List<Email>> markAsRead(AccountId accountId, List<Email> emails, ReadActions readActions) {
|
||||
return Future.sync(() async {
|
||||
return await emailAPI.markAsRead(accountId, emailId, readActions);
|
||||
return await emailAPI.markAsRead(accountId, emails, readActions);
|
||||
}).catchError((error) {
|
||||
throw error;
|
||||
});
|
||||
@@ -70,7 +70,7 @@ class EmailDataSourceImpl extends EmailDataSource {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> moveToMailbox(AccountId accountId, MoveRequest moveRequest) {
|
||||
Future<List<EmailId>> moveToMailbox(AccountId accountId, MoveRequest moveRequest) {
|
||||
return Future.sync(() async {
|
||||
return await emailAPI.moveToMailbox(accountId, moveRequest);
|
||||
}).catchError((error) {
|
||||
|
||||
@@ -15,7 +15,6 @@ import 'package:jmap_dart_client/jmap/jmap_request.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/email/get/get_email_method.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/email/get/get_email_response.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/email/keyword_identifier.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/email/set/set_email_method.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/email/set/set_email_response.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/email/submission/address.dart';
|
||||
@@ -113,28 +112,33 @@ class EmailAPI {
|
||||
});
|
||||
}
|
||||
|
||||
Future<bool> markAsRead(AccountId accountId, EmailId emailId, ReadActions readAction) async {
|
||||
Future<List<Email>> markAsRead(AccountId accountId, List<Email> emails, ReadActions readActions) async {
|
||||
final emailIds = emails.map((email) => email.id).toList();
|
||||
|
||||
final setEmailMethod = SetEmailMethod(accountId)
|
||||
..addUpdates({
|
||||
emailId.id: KeyWordIdentifier.emailSeen.generateReadActionPath(readAction)
|
||||
});
|
||||
..addUpdates(emailIds.generateMapUpdateObjectMarkAsRead(readActions));
|
||||
|
||||
final getEmailMethod = GetEmailMethod(accountId)
|
||||
..addIds(emailIds.toIds().toSet())
|
||||
..addProperties(Properties({'keywords'}));
|
||||
|
||||
final requestBuilder = JmapRequestBuilder(_httpClient, ProcessingInvocation());
|
||||
|
||||
final setEmailInvocation = requestBuilder.invocation(setEmailMethod);
|
||||
requestBuilder.invocation(setEmailMethod);
|
||||
|
||||
final getEmailInvocation = requestBuilder.invocation(getEmailMethod);
|
||||
|
||||
final response = await (requestBuilder
|
||||
..usings(setEmailMethod.requiredCapabilities))
|
||||
.build()
|
||||
.execute();
|
||||
|
||||
final setEmailResponse = response.parse<SetEmailResponse>(
|
||||
setEmailInvocation.methodCallId,
|
||||
SetEmailResponse.deserialize);
|
||||
final getEmailResponse = response.parse<GetEmailResponse>(
|
||||
getEmailInvocation.methodCallId,
|
||||
GetEmailResponse.deserialize);
|
||||
|
||||
return Future.sync(() async {
|
||||
final emailUpdated = setEmailResponse!.updated![emailId.id];
|
||||
return emailUpdated == null;
|
||||
return getEmailResponse!.list;
|
||||
}).catchError((error) {
|
||||
throw error;
|
||||
});
|
||||
@@ -188,12 +192,10 @@ class EmailAPI {
|
||||
cancelToken: cancelToken);
|
||||
}
|
||||
|
||||
Future<bool> moveToMailbox(AccountId accountId, MoveRequest moveRequest) async {
|
||||
Future<List<EmailId>> moveToMailbox(AccountId accountId, MoveRequest moveRequest) async {
|
||||
final setEmailMethod = SetEmailMethod(accountId)
|
||||
..addUpdates({
|
||||
moveRequest.emailId.id: moveRequest.currentMailboxId
|
||||
.generateMoveToMailboxActionPath(moveRequest.destinationMailboxId)
|
||||
});
|
||||
..addUpdates(moveRequest.emailIds
|
||||
.generateMapUpdateObjectMoveToMailbox(moveRequest.currentMailboxId, moveRequest.destinationMailboxId));
|
||||
|
||||
final requestBuilder = JmapRequestBuilder(_httpClient, ProcessingInvocation());
|
||||
|
||||
@@ -209,8 +211,10 @@ class EmailAPI {
|
||||
SetEmailResponse.deserialize);
|
||||
|
||||
return Future.sync(() async {
|
||||
final emailUpdated = setEmailResponse!.updated![moveRequest.emailId.id];
|
||||
return emailUpdated == null;
|
||||
final mapUpdated = setEmailResponse!.updated!;
|
||||
return moveRequest.emailIds
|
||||
.where((emailId) => mapUpdated.containsKey(emailId.id))
|
||||
.toList();
|
||||
}).catchError((error) {
|
||||
throw error;
|
||||
});
|
||||
|
||||
@@ -25,8 +25,8 @@ class EmailRepositoryImpl extends EmailRepository {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> markAsRead(AccountId accountId, EmailId emailId, ReadActions readAction) {
|
||||
return emailDataSource.markAsRead(accountId, emailId, readAction);
|
||||
Future<List<Email>> markAsRead(AccountId accountId, List<Email> emails, ReadActions readActions) {
|
||||
return emailDataSource.markAsRead(accountId, emails, readActions);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -56,7 +56,7 @@ class EmailRepositoryImpl extends EmailRepository {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> moveToMailbox(AccountId accountId, MoveRequest moveRequest) {
|
||||
Future<List<EmailId>> moveToMailbox(AccountId accountId, MoveRequest moveRequest) {
|
||||
return emailDataSource.moveToMailbox(accountId, moveRequest);
|
||||
}
|
||||
}
|
||||
@@ -6,29 +6,28 @@ import 'package:model/model.dart';
|
||||
|
||||
class MoveRequest with EquatableMixin {
|
||||
|
||||
final EmailId emailId;
|
||||
final List<EmailId> emailIds;
|
||||
final MailboxId currentMailboxId;
|
||||
final MailboxName currentMailboxName;
|
||||
final MailboxId destinationMailboxId;
|
||||
final MailboxName destinationMailboxName;
|
||||
final MoveAction moveAction;
|
||||
final String? destinationPath;
|
||||
|
||||
MoveRequest(
|
||||
this.emailId,
|
||||
this.emailIds,
|
||||
this.currentMailboxId,
|
||||
this.currentMailboxName,
|
||||
this.destinationMailboxId,
|
||||
this.destinationMailboxName,
|
||||
this.moveAction,
|
||||
{
|
||||
this.destinationPath,
|
||||
}
|
||||
);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
emailId,
|
||||
emailIds,
|
||||
currentMailboxId,
|
||||
currentMailboxName,
|
||||
destinationMailboxId,
|
||||
destinationMailboxName,
|
||||
moveAction
|
||||
moveAction,
|
||||
destinationPath,
|
||||
];
|
||||
}
|
||||
@@ -10,7 +10,7 @@ abstract class EmailRepository {
|
||||
|
||||
Future<bool> sendEmail(AccountId accountId, EmailRequest emailRequest);
|
||||
|
||||
Future<bool> markAsRead(AccountId accountId, EmailId emailId, ReadActions readAction);
|
||||
Future<List<Email>> markAsRead(AccountId accountId, List<Email> emails, ReadActions readActions);
|
||||
|
||||
Future<List<DownloadTaskId>> downloadAttachments(
|
||||
List<Attachment> attachments,
|
||||
@@ -27,5 +27,5 @@ abstract class EmailRepository {
|
||||
CancelToken cancelToken
|
||||
);
|
||||
|
||||
Future<bool> moveToMailbox(AccountId accountId, MoveRequest moveRequest);
|
||||
Future<List<EmailId>> moveToMailbox(AccountId accountId, MoveRequest moveRequest);
|
||||
}
|
||||
@@ -1,15 +1,16 @@
|
||||
import 'package:core/core.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
||||
import 'package:model/email/read_actions.dart';
|
||||
import 'package:model/model.dart';
|
||||
|
||||
class MarkAsEmailReadSuccess extends UIState {
|
||||
final EmailId emailId;
|
||||
final Email updatedEmail;
|
||||
final ReadActions readActions;
|
||||
|
||||
MarkAsEmailReadSuccess(this.emailId, this.readActions);
|
||||
MarkAsEmailReadSuccess(this.updatedEmail, this.readActions);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [emailId, readActions];
|
||||
List<Object?> get props => [updatedEmail, readActions];
|
||||
}
|
||||
|
||||
class MarkAsEmailReadFailure extends FeatureFailure {
|
||||
|
||||
@@ -1,13 +1,29 @@
|
||||
import 'package:core/core.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/model/move_request.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 MoveToMailboxSuccess extends UIState {
|
||||
final MoveRequest moveRequest;
|
||||
final EmailId emailId;
|
||||
final MailboxId currentMailboxId;
|
||||
final MailboxId destinationMailboxId;
|
||||
final MoveAction moveAction;
|
||||
final String? destinationPath;
|
||||
|
||||
MoveToMailboxSuccess(this.moveRequest);
|
||||
MoveToMailboxSuccess(
|
||||
this.emailId,
|
||||
this.currentMailboxId,
|
||||
this.destinationMailboxId,
|
||||
this.moveAction,
|
||||
this.destinationPath);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [moveRequest];
|
||||
List<Object?> get props => [
|
||||
emailId,
|
||||
currentMailboxId,
|
||||
destinationMailboxId,
|
||||
moveAction,
|
||||
destinationPath];
|
||||
}
|
||||
|
||||
class MoveToMailboxFailure extends FeatureFailure {
|
||||
|
||||
@@ -2,19 +2,24 @@ 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/email/email.dart';
|
||||
import 'package:model/email/read_actions.dart';
|
||||
import 'package:model/model.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/state/mark_as_email_read_state.dart';
|
||||
|
||||
class MarkAsEmailReadInteractor {
|
||||
final EmailRepository emailRepository;
|
||||
final EmailRepository _emailRepository;
|
||||
|
||||
MarkAsEmailReadInteractor(this.emailRepository);
|
||||
MarkAsEmailReadInteractor(this._emailRepository);
|
||||
|
||||
Stream<Either<Failure, Success>> execute(AccountId accountId, EmailId emailId, ReadActions readAction) async* {
|
||||
Stream<Either<Failure, Success>> execute(AccountId accountId, Email email, ReadActions readAction) async* {
|
||||
try {
|
||||
final result = await emailRepository.markAsRead(accountId, emailId, readAction);
|
||||
yield result ? Right(MarkAsEmailReadSuccess(emailId, readAction)) : Left(MarkAsEmailReadFailure(null, readAction));
|
||||
final result = await _emailRepository.markAsRead(accountId, [email], readAction);
|
||||
if (result.isNotEmpty) {
|
||||
final updatedEmail = email.updatedEmail(newKeywords: result.first.keywords);
|
||||
yield Right(MarkAsEmailReadSuccess(updatedEmail, readAction));
|
||||
} else {
|
||||
yield Left(MarkAsEmailReadFailure(null, readAction));
|
||||
}
|
||||
} catch (e) {
|
||||
yield Left(MarkAsEmailReadFailure(e, readAction));
|
||||
}
|
||||
|
||||
@@ -13,9 +13,16 @@ class MoveToMailboxInteractor {
|
||||
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));
|
||||
if (result.isNotEmpty) {
|
||||
yield Right(MoveToMailboxSuccess(
|
||||
result.first,
|
||||
moveRequest.currentMailboxId,
|
||||
moveRequest.destinationMailboxId,
|
||||
moveRequest.moveAction,
|
||||
moveRequest.destinationPath));
|
||||
} else {
|
||||
yield Left(MoveToMailboxFailure(null));
|
||||
}
|
||||
} catch (e) {
|
||||
yield Left(MoveToMailboxFailure(e));
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ 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';
|
||||
@@ -47,6 +46,7 @@ class EmailController extends BaseController {
|
||||
final emailAddressExpandMode = ExpandMode.COLLAPSE.obs;
|
||||
final attachmentsExpandMode = ExpandMode.COLLAPSE.obs;
|
||||
final emailContent = Rxn<EmailContent>();
|
||||
EmailId? _currentEmailId;
|
||||
|
||||
EmailController(
|
||||
this._getEmailContentInteractor,
|
||||
@@ -62,12 +62,15 @@ class EmailController extends BaseController {
|
||||
void onReady() {
|
||||
super.onReady();
|
||||
mailboxDashBoardController.selectedEmail.listen((presentationEmail) {
|
||||
_clearEmailContent();
|
||||
final accountId = mailboxDashBoardController.accountId.value;
|
||||
if (accountId != null && presentationEmail != null) {
|
||||
_getEmailContentAction(accountId, presentationEmail.id);
|
||||
if (presentationEmail.isUnReadEmail()) {
|
||||
markAsEmailRead(presentationEmail, ReadActions.markAsRead);
|
||||
if (_currentEmailId != presentationEmail?.id) {
|
||||
_currentEmailId = presentationEmail?.id;
|
||||
_clearEmailContent();
|
||||
final accountId = mailboxDashBoardController.accountId.value;
|
||||
if (accountId != null && presentationEmail != null) {
|
||||
_getEmailContentAction(accountId, presentationEmail.id);
|
||||
if (presentationEmail.isUnReadEmail()) {
|
||||
markAsEmailRead(presentationEmail, ReadActions.markAsRead);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -131,11 +134,14 @@ class EmailController extends BaseController {
|
||||
final accountId = mailboxDashBoardController.accountId.value;
|
||||
final mailboxCurrent = mailboxDashBoardController.selectedMailbox.value;
|
||||
if (accountId != null && mailboxCurrent != null) {
|
||||
consumeState(_markAsEmailReadInteractor.execute(accountId, presentationEmail.id, readActions));
|
||||
consumeState(_markAsEmailReadInteractor.execute(accountId, presentationEmail.toEmail(), readActions));
|
||||
}
|
||||
}
|
||||
|
||||
void _markAsEmailReadSuccess(Success success) {
|
||||
if (success is MarkAsEmailReadSuccess) {
|
||||
mailboxDashBoardController.setSelectedEmail(success.updatedEmail.toPresentationEmail(selectMode: SelectMode.ACTIVE));
|
||||
}
|
||||
mailboxDashBoardController.dispatchState(Right(success));
|
||||
|
||||
if (success is MarkAsEmailReadSuccess && success.readActions == ReadActions.markAsUnread) {
|
||||
@@ -144,7 +150,9 @@ class EmailController extends BaseController {
|
||||
}
|
||||
|
||||
void _markAsEmailReadFailure(Failure failure) {
|
||||
backToThreadView();
|
||||
if (failure is MarkAsEmailReadFailure && failure.readActions == ReadActions.markAsUnread) {
|
||||
backToThreadView();
|
||||
}
|
||||
}
|
||||
|
||||
void toggleDisplayAttachmentsAction() {
|
||||
@@ -244,19 +252,18 @@ class EmailController extends BaseController {
|
||||
final accountId = mailboxDashBoardController.accountId.value;
|
||||
|
||||
if (currentMailbox != null && accountId != null) {
|
||||
final mailboxDestination = await push(
|
||||
final destinationMailbox = await push(
|
||||
AppRoutes.DESTINATION_PICKER,
|
||||
arguments: DestinationPickerArguments(accountId, [email.id], currentMailbox)
|
||||
);
|
||||
|
||||
if (mailboxDestination != null && mailboxDestination is PresentationMailbox) {
|
||||
if (destinationMailbox != null && destinationMailbox is PresentationMailbox) {
|
||||
_moveToMailbox(accountId, MoveRequest(
|
||||
email.id,
|
||||
[email.id],
|
||||
currentMailbox.id,
|
||||
currentMailbox.name ?? MailboxName(''),
|
||||
mailboxDestination.id,
|
||||
mailboxDestination.name ?? MailboxName(''),
|
||||
MoveAction.moveTo));
|
||||
destinationMailbox.id,
|
||||
MoveAction.moveTo,
|
||||
destinationPath: destinationMailbox.mailboxPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -269,19 +276,17 @@ class EmailController extends BaseController {
|
||||
mailboxDashBoardController.dispatchState(Right(success));
|
||||
|
||||
if (success is MoveToMailboxSuccess
|
||||
&& success.moveRequest.moveAction == MoveAction.moveTo
|
||||
&& success.moveAction == MoveAction.moveTo
|
||||
&& Get.context != null && Get.overlayContext != null) {
|
||||
_appToast.showToastWithAction(
|
||||
Get.overlayContext!,
|
||||
AppLocalizations.of(Get.context!).moved_to_mailbox(success.moveRequest.destinationMailboxName.name),
|
||||
AppLocalizations.of(Get.context!).moved_to_mailbox(success.destinationPath ?? ''),
|
||||
AppLocalizations.of(Get.context!).undo_action,
|
||||
() {
|
||||
final newMoveRequest = MoveRequest(
|
||||
success.moveRequest.emailId,
|
||||
success.moveRequest.destinationMailboxId,
|
||||
success.moveRequest.destinationMailboxName,
|
||||
success.moveRequest.currentMailboxId,
|
||||
success.moveRequest.currentMailboxName,
|
||||
[success.emailId],
|
||||
success.destinationMailboxId,
|
||||
success.currentMailboxId,
|
||||
MoveAction.undo);
|
||||
_undoMoveToMailbox(newMoveRequest);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
|
||||
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_node.dart';
|
||||
|
||||
extension ListMailboxNodeExtensions on List<MailboxNode> {
|
||||
List<MailboxNode>? updateNode(MailboxId currentMailboxId, MailboxNode newNode) {
|
||||
return map((MailboxNode child) {
|
||||
if (child.item.id == currentMailboxId) {
|
||||
return newNode;
|
||||
} else {
|
||||
if (child.hasChildren()) {
|
||||
return child.copyWith(mailboxNodes: child.updateNode(currentMailboxId, newNode));
|
||||
}
|
||||
return child;
|
||||
}
|
||||
}).toList();
|
||||
}
|
||||
}
|
||||
@@ -11,10 +11,11 @@ import 'package:tmail_ui_user/features/login/domain/usecases/delete_credential_i
|
||||
import 'package:tmail_ui_user/features/mailbox/domain/state/get_all_mailboxes_state.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox/domain/usecases/get_all_mailbox_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_node.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_tree.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_tree_builder.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox/presentation/extensions/list_mailbox_node_extension.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart';
|
||||
import 'package:tmail_ui_user/features/thread/domain/state/mark_as_multiple_email_read_state.dart';
|
||||
import 'package:tmail_ui_user/features/thread/domain/state/move_multiple_email_to_mailbox_state.dart';
|
||||
import 'package:tmail_ui_user/main/routes/app_routes.dart';
|
||||
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
||||
|
||||
@@ -26,8 +27,8 @@ class MailboxController extends BaseController {
|
||||
final TreeBuilder _treeBuilder;
|
||||
final ResponsiveUtils responsiveUtils;
|
||||
|
||||
final folderMailboxTree = MailboxTree(MailboxNode.root()).obs;
|
||||
final defaultMailboxList = <PresentationMailbox>[].obs;
|
||||
final folderMailboxNodeList = <MailboxNode>[].obs;
|
||||
|
||||
MailboxController(
|
||||
this._getAllMailboxInteractor,
|
||||
@@ -51,6 +52,10 @@ class MailboxController extends BaseController {
|
||||
success is MarkAsMultipleEmailReadAllSuccess ||
|
||||
success is MarkAsMultipleEmailReadHasSomeEmailFailure) {
|
||||
refreshGetAllMailboxAction();
|
||||
} else if (success is MoveMultipleEmailToMailboxAllSuccess
|
||||
|| success is MoveMultipleEmailToMailboxHasSomeEmailFailure) {
|
||||
mailboxDashBoardController.clearState();
|
||||
refreshGetAllMailboxAction();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -97,7 +102,20 @@ class MailboxController extends BaseController {
|
||||
void onError(error) {}
|
||||
|
||||
void _buildTree(List<PresentationMailbox> folderMailboxList) async {
|
||||
folderMailboxTree.value = await _treeBuilder.generateMailboxTree(folderMailboxList);
|
||||
final _folderMailboxTree = await _treeBuilder.generateMailboxTree(folderMailboxList);
|
||||
folderMailboxNodeList.value = _folderMailboxTree.root.childrenItems ?? [];
|
||||
}
|
||||
|
||||
void toggleMailboxFolder(MailboxNode mailboxNode) {
|
||||
final newExpandMode = mailboxNode.expandMode == ExpandMode.COLLAPSE
|
||||
? ExpandMode.EXPAND
|
||||
: ExpandMode.COLLAPSE;
|
||||
|
||||
final newMailboxNodeList = folderMailboxNodeList.updateNode(
|
||||
mailboxNode.item.id,
|
||||
mailboxNode.copyWith(newExpandMode: newExpandMode)) ?? [];
|
||||
|
||||
folderMailboxNodeList.value = newMailboxNodeList;
|
||||
}
|
||||
|
||||
void _setUpMapMailboxIdDefault(List<PresentationMailbox> defaultMailboxList) {
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
import 'package:core/core.dart';
|
||||
import 'package:core/presentation/extensions/color_extension.dart';
|
||||
import 'package:core/presentation/resources/image_paths.dart';
|
||||
import 'package:core/presentation/utils/responsive_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:get/get.dart';
|
||||
@@ -174,7 +171,7 @@ class MailboxView extends GetWidget<MailboxController> {
|
||||
}
|
||||
|
||||
Widget _buildFolderMailbox(BuildContext context) {
|
||||
return Obx(() => controller.folderMailboxTree.value.root.hasChildren()
|
||||
return Obx(() => controller.folderMailboxNodeList.isNotEmpty
|
||||
? Transform(
|
||||
transform: Matrix4.translationValues(responsiveUtils.isMobile(context) ? 0.0 : -15.0, 0.0, 0.0),
|
||||
child: Padding(
|
||||
@@ -182,7 +179,7 @@ class MailboxView extends GetWidget<MailboxController> {
|
||||
child: TreeView(
|
||||
startExpanded: false,
|
||||
key: Key('folder_mailbox_list'),
|
||||
children: _buildListChildTileWidget(context, controller.folderMailboxTree.value.root.childrenItems!)))
|
||||
children: _buildListChildTileWidget(context, controller.folderMailboxNodeList)))
|
||||
)
|
||||
: SizedBox.shrink()
|
||||
);
|
||||
@@ -192,24 +189,27 @@ class MailboxView extends GetWidget<MailboxController> {
|
||||
return listMailboxNode
|
||||
.map((mailboxNode) => mailboxNode.hasChildren()
|
||||
? Padding(
|
||||
padding: EdgeInsets.only(left: 16),
|
||||
padding: EdgeInsets.only(left: 20),
|
||||
child: TreeViewChild(
|
||||
key: Key('children_tree_mailbox_child'),
|
||||
parent: Obx(() => MailBoxFolderTileBuilder(
|
||||
context,
|
||||
imagePaths,
|
||||
responsiveUtils,
|
||||
mailboxNode,
|
||||
selectMode: controller.getSelectMode(
|
||||
mailboxNode.item,
|
||||
controller.mailboxDashBoardController.selectedMailbox.value))
|
||||
.build()),
|
||||
children: _buildListChildTileWidget(context, mailboxNode.childrenItems!)))
|
||||
context,
|
||||
key: Key('children_tree_mailbox_child'),
|
||||
isExpanded: mailboxNode.expandMode == ExpandMode.EXPAND,
|
||||
parent: Obx(() => (MailBoxFolderTileBuilder(
|
||||
context,
|
||||
imagePaths,
|
||||
responsiveUtils,
|
||||
mailboxNode,
|
||||
selectMode: controller.getSelectMode(
|
||||
mailboxNode.item,
|
||||
controller.mailboxDashBoardController.selectedMailbox.value))
|
||||
..addOnSelectMailboxFolderClick((mailboxNode) => controller.selectMailbox(context, mailboxNode.item))
|
||||
..addOnExpandFolderActionClick((mailboxNode) => controller.toggleMailboxFolder(mailboxNode)))
|
||||
.build()),
|
||||
children: _buildListChildTileWidget(context, mailboxNode.childrenItems!)
|
||||
).build())
|
||||
: Padding(
|
||||
padding: EdgeInsets.only(left: 16),
|
||||
child: Obx(() => GestureDetector(
|
||||
onTap: () => controller.selectMailbox(context, mailboxNode.item),
|
||||
child: MailBoxFolderTileBuilder(
|
||||
padding: EdgeInsets.only(left: 20),
|
||||
child: Obx(() => (MailBoxFolderTileBuilder(
|
||||
context,
|
||||
imagePaths,
|
||||
responsiveUtils,
|
||||
@@ -217,8 +217,9 @@ class MailboxView extends GetWidget<MailboxController> {
|
||||
selectMode: controller.getSelectMode(
|
||||
mailboxNode.item,
|
||||
controller.mailboxDashBoardController.selectedMailbox.value))
|
||||
.build(),
|
||||
))))
|
||||
..addOnSelectMailboxFolderClick((mailboxNode) => controller.selectMailbox(context, mailboxNode.item)))
|
||||
.build(),
|
||||
)))
|
||||
.toList();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,19 +1,24 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:jmap_dart_client/jmap/core/id.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
|
||||
import 'package:model/mailbox/expand_mode.dart';
|
||||
import 'package:model/mailbox/presentation_mailbox.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_tree.dart';
|
||||
|
||||
class MailboxNode with EquatableMixin{
|
||||
static PresentationMailbox _root = PresentationMailbox(MailboxId(Id('root')));
|
||||
|
||||
PresentationMailbox item;
|
||||
List<MailboxNode>? childrenItems;
|
||||
ExpandMode expandMode;
|
||||
|
||||
factory MailboxNode.root() => MailboxNode(_root);
|
||||
|
||||
static PresentationMailbox rootItem() => _root;
|
||||
|
||||
bool hasChildren() => childrenItems?.isNotEmpty ?? false;
|
||||
|
||||
MailboxNode(this.item, {this.childrenItems});
|
||||
MailboxNode(this.item, {this.childrenItems, this.expandMode = ExpandMode.COLLAPSE});
|
||||
|
||||
void addChildNode(MailboxNode node) {
|
||||
if (childrenItems == null) {
|
||||
@@ -22,6 +27,60 @@ class MailboxNode with EquatableMixin{
|
||||
childrenItems?.add(node);
|
||||
}
|
||||
|
||||
String getPathMailboxNode(MailboxTree mailboxTree, List<PresentationMailbox> defaultMailboxList) {
|
||||
String path = '${item.name?.name}';
|
||||
|
||||
var parentId = item.parentId;
|
||||
|
||||
while(parentId != null) {
|
||||
var parent = mailboxTree.findNode(parentId);
|
||||
if (parent == null) {
|
||||
try {
|
||||
final parentItem = defaultMailboxList.firstWhere((mailbox) => mailbox.id == parentId);
|
||||
parent = MailboxNode(parentItem);
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
if (parent != null) {
|
||||
path = '${parent.item.name?.name}/$path';
|
||||
parentId = parent.item.parentId;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
List<MailboxNode>? updateNode(MailboxId mailboxId, MailboxNode newNode, {MailboxNode? parent}) {
|
||||
List<MailboxNode>? _children = parent == null ? this.childrenItems : parent.childrenItems;
|
||||
return _children?.map((MailboxNode child) {
|
||||
if (child.item.id == mailboxId) {
|
||||
return newNode;
|
||||
} else {
|
||||
if (child.hasChildren()) {
|
||||
return child.copyWith(
|
||||
mailboxNodes: updateNode(
|
||||
mailboxId,
|
||||
newNode,
|
||||
parent: child,
|
||||
),
|
||||
);
|
||||
}
|
||||
return child;
|
||||
}
|
||||
}).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [item, childrenItems];
|
||||
}
|
||||
}
|
||||
|
||||
extension MailboxNodeExtension on MailboxNode {
|
||||
MailboxNode copyWith({ExpandMode? newExpandMode, List<MailboxNode>? mailboxNodes}) {
|
||||
return MailboxNode(
|
||||
item,
|
||||
childrenItems: mailboxNodes ?? childrenItems,
|
||||
expandMode: newExpandMode ?? expandMode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
|
||||
|
||||
import 'mailbox_node.dart';
|
||||
|
||||
@@ -7,6 +10,23 @@ class MailboxTree with EquatableMixin {
|
||||
MailboxNode root;
|
||||
MailboxTree(this.root);
|
||||
|
||||
MailboxNode? findNode(MailboxId? mailboxId) {
|
||||
var result;
|
||||
final queue = ListQueue<MailboxNode>();
|
||||
queue.addLast(root);
|
||||
while (queue.isNotEmpty && mailboxId != null) {
|
||||
final currentNode = queue.removeFirst();
|
||||
if (mailboxId == currentNode.item.id) {
|
||||
result = currentNode;
|
||||
break;
|
||||
}
|
||||
currentNode.childrenItems?.forEach((child) {
|
||||
queue.addLast(child);
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [root];
|
||||
}
|
||||
@@ -3,9 +3,13 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:model/mailbox/select_mode.dart';
|
||||
import 'package:model/model.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_displayed.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_node.dart';
|
||||
|
||||
typedef OnExpandFolderActionClick = void Function(MailboxNode mailboxNode);
|
||||
typedef OnSelectMailboxFolderClick = void Function(MailboxNode mailboxNode);
|
||||
|
||||
class MailBoxFolderTileBuilder {
|
||||
|
||||
final MailboxNode _mailboxNode;
|
||||
@@ -15,6 +19,9 @@ class MailBoxFolderTileBuilder {
|
||||
final SelectMode selectMode;
|
||||
final MailboxDisplayed mailboxDisplayed;
|
||||
|
||||
OnExpandFolderActionClick? _onExpandFolderActionClick;
|
||||
OnSelectMailboxFolderClick? _onSelectMailboxFolderClick;
|
||||
|
||||
MailBoxFolderTileBuilder(
|
||||
this._context,
|
||||
this._imagePaths,
|
||||
@@ -26,6 +33,14 @@ class MailBoxFolderTileBuilder {
|
||||
}
|
||||
);
|
||||
|
||||
void addOnExpandFolderActionClick(OnExpandFolderActionClick onExpandFolderActionClick) {
|
||||
this._onExpandFolderActionClick = onExpandFolderActionClick;
|
||||
}
|
||||
|
||||
void addOnSelectMailboxFolderClick(OnSelectMailboxFolderClick onSelectMailboxFolderClick) {
|
||||
this._onSelectMailboxFolderClick = onSelectMailboxFolderClick;
|
||||
}
|
||||
|
||||
Widget build() {
|
||||
return Container(
|
||||
key: Key('mailbox_folder_tile'),
|
||||
@@ -39,6 +54,11 @@ class MailBoxFolderTileBuilder {
|
||||
child: MediaQuery(
|
||||
data: MediaQueryData(padding: EdgeInsets.zero),
|
||||
child: ListTile(
|
||||
onTap: () {
|
||||
if (_onSelectMailboxFolderClick != null) {
|
||||
_onSelectMailboxFolderClick!(_mailboxNode);
|
||||
}
|
||||
},
|
||||
contentPadding: EdgeInsets.zero,
|
||||
leading: Padding(
|
||||
padding: EdgeInsets.only(left: mailboxDisplayed == MailboxDisplayed.mailbox
|
||||
@@ -72,14 +92,21 @@ class MailBoxFolderTileBuilder {
|
||||
: 36,
|
||||
left: mailboxDisplayed == MailboxDisplayed.mailbox ? 16 : 16),
|
||||
child: _mailboxNode.hasChildren()
|
||||
? SvgPicture.asset(
|
||||
_imagePaths.icFolderArrow,
|
||||
width: 12,
|
||||
height: 12,
|
||||
color: selectMode == SelectMode.ACTIVE
|
||||
? AppColor.mailboxSelectedIconColor
|
||||
: AppColor.mailboxIconColor,
|
||||
fit: BoxFit.fill)
|
||||
? GestureDetector(
|
||||
onTap: () {
|
||||
if (_onExpandFolderActionClick != null) {
|
||||
_onExpandFolderActionClick!(_mailboxNode);
|
||||
}
|
||||
},
|
||||
child: SvgPicture.asset(
|
||||
_mailboxNode.expandMode == ExpandMode.EXPAND ? _imagePaths.icExpandFolder : _imagePaths.icFolderArrow,
|
||||
width: _mailboxNode.expandMode == ExpandMode.EXPAND ? 8 : 12,
|
||||
height: _mailboxNode.expandMode == ExpandMode.EXPAND ? 8 : 12,
|
||||
color: selectMode == SelectMode.ACTIVE
|
||||
? AppColor.mailboxSelectedIconColor
|
||||
: AppColor.mailboxIconColor,
|
||||
fit: BoxFit.fill),
|
||||
)
|
||||
: SizedBox.shrink())
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1,35 +1,33 @@
|
||||
import 'package:core/core.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:model/email/read_actions.dart';
|
||||
|
||||
class MarkAsMultipleEmailReadAllSuccess extends UIState {
|
||||
final List<Either<Failure, Success>> resultList;
|
||||
final int countMarkAsReadSuccess;
|
||||
final ReadActions readActions;
|
||||
|
||||
MarkAsMultipleEmailReadAllSuccess(this.resultList, this.readActions);
|
||||
MarkAsMultipleEmailReadAllSuccess(this.countMarkAsReadSuccess, this.readActions);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [resultList, readActions];
|
||||
List<Object?> get props => [countMarkAsReadSuccess, readActions];
|
||||
}
|
||||
|
||||
class MarkAsMultipleEmailReadAllFailure extends FeatureFailure {
|
||||
final List<Either<Failure, Success>> resultList;
|
||||
final ReadActions readActions;
|
||||
|
||||
MarkAsMultipleEmailReadAllFailure(this.resultList, this.readActions);
|
||||
MarkAsMultipleEmailReadAllFailure(this.readActions);
|
||||
|
||||
@override
|
||||
List<Object> get props => [resultList, readActions];
|
||||
List<Object> get props => [readActions];
|
||||
}
|
||||
|
||||
class MarkAsMultipleEmailReadHasSomeEmailFailure extends UIState {
|
||||
final List<Either<Failure, Success>> resultList;
|
||||
final int countMarkAsReadSuccess;
|
||||
final ReadActions readActions;
|
||||
|
||||
MarkAsMultipleEmailReadHasSomeEmailFailure(this.resultList, this.readActions);
|
||||
MarkAsMultipleEmailReadHasSomeEmailFailure(this.countMarkAsReadSuccess, this.readActions);
|
||||
|
||||
@override
|
||||
List<Object> get props => [resultList, readActions];
|
||||
List<Object> get props => [countMarkAsReadSuccess, readActions];
|
||||
}
|
||||
|
||||
class MarkAsMultipleEmailReadFailure extends FeatureFailure {
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
import 'package:core/core.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 MoveMultipleEmailToMailboxAllSuccess extends UIState {
|
||||
final List<EmailId> movedListEmailId;
|
||||
final MailboxId currentMailboxId;
|
||||
final MailboxId destinationMailboxId;
|
||||
final MoveAction moveAction;
|
||||
final String? destinationPath;
|
||||
|
||||
MoveMultipleEmailToMailboxAllSuccess(
|
||||
this.movedListEmailId,
|
||||
this.currentMailboxId,
|
||||
this.destinationMailboxId,
|
||||
this.moveAction,
|
||||
this.destinationPath);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
movedListEmailId,
|
||||
currentMailboxId,
|
||||
destinationMailboxId,
|
||||
moveAction,
|
||||
destinationPath];
|
||||
}
|
||||
|
||||
class MoveMultipleEmailToMailboxAllFailure extends FeatureFailure {
|
||||
final MoveAction moveAction;
|
||||
|
||||
MoveMultipleEmailToMailboxAllFailure(this.moveAction);
|
||||
|
||||
@override
|
||||
List<Object> get props => [moveAction];
|
||||
}
|
||||
|
||||
class MoveMultipleEmailToMailboxHasSomeEmailFailure extends UIState {
|
||||
final List<EmailId> movedListEmailId;
|
||||
final MailboxId currentMailboxId;
|
||||
final MailboxId destinationMailboxId;
|
||||
final MoveAction moveAction;
|
||||
final String? destinationPath;
|
||||
|
||||
MoveMultipleEmailToMailboxHasSomeEmailFailure(
|
||||
this.movedListEmailId,
|
||||
this.currentMailboxId,
|
||||
this.destinationMailboxId,
|
||||
this.moveAction,
|
||||
this.destinationPath);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
movedListEmailId,
|
||||
currentMailboxId,
|
||||
destinationMailboxId,
|
||||
moveAction,
|
||||
destinationPath];
|
||||
}
|
||||
|
||||
class MoveMultipleEmailToMailboxFailure extends FeatureFailure {
|
||||
final exception;
|
||||
final MoveAction moveAction;
|
||||
|
||||
MoveMultipleEmailToMailboxFailure(this.exception, this.moveAction);
|
||||
|
||||
@override
|
||||
List<Object> get props => [exception, moveAction];
|
||||
}
|
||||
@@ -2,40 +2,37 @@ 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/email/email.dart';
|
||||
import 'package:model/email/read_actions.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_email_read_interactor.dart';
|
||||
import 'package:model/model.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart';
|
||||
import 'package:tmail_ui_user/features/thread/domain/state/mark_as_multiple_email_read_state.dart';
|
||||
|
||||
class MarkAsMultipleEmailReadInteractor {
|
||||
final MarkAsEmailReadInteractor markAsEmailReadInteractor;
|
||||
final EmailRepository _emailRepository;
|
||||
|
||||
MarkAsMultipleEmailReadInteractor(this.markAsEmailReadInteractor);
|
||||
MarkAsMultipleEmailReadInteractor(this._emailRepository);
|
||||
|
||||
Stream<Either<Failure, Success>> execute(
|
||||
AccountId accountId,
|
||||
List<EmailId> listEmailId,
|
||||
List<Email> emails,
|
||||
ReadActions readAction
|
||||
) async* {
|
||||
try {
|
||||
final listResult = await Future.wait(listEmailId.map((emailId) async {
|
||||
final result = await markAsEmailReadInteractor.execute(accountId, emailId, readAction).toList();
|
||||
return result.first;
|
||||
}));
|
||||
if (listResult.length == 1) {
|
||||
yield listResult.first;
|
||||
yield Right<Failure, Success>(LoadingState());
|
||||
|
||||
final listEmailNeedMarkAsRead = emails
|
||||
.where((email) => readAction == ReadActions.markAsUnread ? email.isReadEmail() : email.isUnReadEmail())
|
||||
.toList();
|
||||
|
||||
final result = await _emailRepository.markAsRead(accountId, listEmailNeedMarkAsRead, readAction);
|
||||
|
||||
if (listEmailNeedMarkAsRead.length == result.length) {
|
||||
final countMarkAsReadSuccess = emails.length;
|
||||
yield Right(MarkAsMultipleEmailReadAllSuccess(countMarkAsReadSuccess, readAction));
|
||||
} else if (result.isEmpty) {
|
||||
yield Left(MarkAsMultipleEmailReadAllFailure(readAction));
|
||||
} else {
|
||||
var failedFileCount = 0;
|
||||
listResult.forEach((element) {
|
||||
if (element is Left) {
|
||||
failedFileCount++;
|
||||
}
|
||||
});
|
||||
if (failedFileCount == 0) {
|
||||
yield Right(MarkAsMultipleEmailReadAllSuccess(listResult, readAction));
|
||||
} else if (failedFileCount == listResult.length) {
|
||||
yield Left(MarkAsMultipleEmailReadAllFailure(listResult, readAction));
|
||||
}
|
||||
yield Right(MarkAsMultipleEmailReadHasSomeEmailFailure(listResult, readAction));
|
||||
final countMarkAsReadSuccess = emails.length - (listEmailNeedMarkAsRead.length - result.length);
|
||||
yield Right(MarkAsMultipleEmailReadHasSomeEmailFailure(countMarkAsReadSuccess, readAction));
|
||||
}
|
||||
} catch (e) {
|
||||
yield Left(MarkAsMultipleEmailReadFailure(e, readAction));
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import 'dart:async';
|
||||
|
||||
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/thread/domain/state/move_multiple_email_to_mailbox_state.dart';
|
||||
|
||||
class MoveMultipleEmailToMailboxInteractor {
|
||||
final EmailRepository _emailRepository;
|
||||
|
||||
MoveMultipleEmailToMailboxInteractor(this._emailRepository);
|
||||
|
||||
Stream<Either<Failure, Success>> execute(AccountId accountId, MoveRequest moveRequest) async* {
|
||||
try {
|
||||
yield Right<Failure, Success>(LoadingState());
|
||||
|
||||
final result = await _emailRepository.moveToMailbox(accountId, moveRequest);
|
||||
|
||||
if (moveRequest.emailIds.length == result.length) {
|
||||
yield Right(MoveMultipleEmailToMailboxAllSuccess(
|
||||
result,
|
||||
moveRequest.currentMailboxId,
|
||||
moveRequest.destinationMailboxId,
|
||||
moveRequest.moveAction,
|
||||
moveRequest.destinationPath));
|
||||
} else if (result.isEmpty) {
|
||||
yield Left(MoveMultipleEmailToMailboxAllFailure(moveRequest.moveAction));
|
||||
} else {
|
||||
yield Right(MoveMultipleEmailToMailboxHasSomeEmailFailure(
|
||||
result,
|
||||
moveRequest.currentMailboxId,
|
||||
moveRequest.destinationMailboxId,
|
||||
moveRequest.moveAction,
|
||||
moveRequest.destinationPath));
|
||||
}
|
||||
} catch (e) {
|
||||
yield Left(MoveMultipleEmailToMailboxFailure(e, moveRequest.moveAction));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import 'package:tmail_ui_user/features/email/data/network/email_api.dart';
|
||||
import 'package:tmail_ui_user/features/email/data/repository/email_repository_impl.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/repository/email_repository.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/thread/data/datasource/thread_datasource.dart';
|
||||
import 'package:tmail_ui_user/features/thread/data/datasource_impl/thread_datasource_impl.dart';
|
||||
import 'package:tmail_ui_user/features/thread/data/network/thread_api.dart';
|
||||
@@ -15,6 +16,7 @@ import 'package:tmail_ui_user/features/thread/data/repository/thread_repository_
|
||||
import 'package:tmail_ui_user/features/thread/domain/repository/thread_repository.dart';
|
||||
import 'package:tmail_ui_user/features/thread/domain/usecases/get_emails_in_mailbox_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/thread/domain/usecases/mark_as_multiple_email_read_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/thread/domain/usecases/move_multiple_email_to_mailbox_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/thread/presentation/thread_controller.dart';
|
||||
|
||||
class ThreadBindings extends Bindings {
|
||||
@@ -31,13 +33,16 @@ class ThreadBindings extends Bindings {
|
||||
Get.lazyPut(() => EmailRepositoryImpl(Get.find<EmailDataSource>()));
|
||||
Get.lazyPut<EmailRepository>(() => Get.find<EmailRepositoryImpl>());
|
||||
Get.lazyPut(() => MarkAsEmailReadInteractor(Get.find<EmailRepository>()));
|
||||
Get.lazyPut(() => MarkAsMultipleEmailReadInteractor(Get.find<MarkAsEmailReadInteractor>()));
|
||||
Get.lazyPut(() => MarkAsMultipleEmailReadInteractor(Get.find<EmailRepository>()));
|
||||
Get.lazyPut(() => MoveToMailboxInteractor(Get.find<EmailRepository>()));
|
||||
Get.lazyPut(() => MoveMultipleEmailToMailboxInteractor(Get.find<EmailRepository>()));
|
||||
Get.put(ThreadController(
|
||||
Get.find<ResponsiveUtils>(),
|
||||
Get.find<GetEmailsInMailboxInteractor>(),
|
||||
Get.find<ScrollController>(),
|
||||
Get.find<MarkAsMultipleEmailReadInteractor>(),
|
||||
Get.find<AppToast>(),
|
||||
Get.find<MoveMultipleEmailToMailboxInteractor>(),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
|
||||
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/model/move_request.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';
|
||||
@@ -23,8 +24,10 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_da
|
||||
import 'package:tmail_ui_user/features/thread/domain/constants/thread_constants.dart';
|
||||
import 'package:tmail_ui_user/features/thread/domain/state/get_all_email_state.dart';
|
||||
import 'package:tmail_ui_user/features/thread/domain/state/mark_as_multiple_email_read_state.dart';
|
||||
import 'package:tmail_ui_user/features/thread/domain/state/move_multiple_email_to_mailbox_state.dart';
|
||||
import 'package:tmail_ui_user/features/thread/domain/usecases/get_emails_in_mailbox_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/thread/domain/usecases/mark_as_multiple_email_read_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/thread/domain/usecases/move_multiple_email_to_mailbox_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/thread/presentation/model/load_more_state.dart';
|
||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||
import 'package:tmail_ui_user/main/routes/app_routes.dart';
|
||||
@@ -38,6 +41,7 @@ class ThreadController extends BaseController {
|
||||
final AppToast _appToast;
|
||||
final ResponsiveUtils responsiveUtils;
|
||||
final ScrollController listEmailController;
|
||||
final MoveMultipleEmailToMailboxInteractor _moveMultipleEmailToMailboxInteractor;
|
||||
|
||||
final _properties = Properties({
|
||||
'id', 'subject', 'from', 'to', 'cc', 'bcc', 'keywords', 'receivedAt',
|
||||
@@ -58,6 +62,7 @@ class ThreadController extends BaseController {
|
||||
this.listEmailController,
|
||||
this._markAsMultipleEmailReadInteractor,
|
||||
this._appToast,
|
||||
this._moveMultipleEmailToMailboxInteractor,
|
||||
);
|
||||
|
||||
@override
|
||||
@@ -73,20 +78,17 @@ class ThreadController extends BaseController {
|
||||
if (_currentMailboxId != selectedMailbox?.id) {
|
||||
_currentMailboxId = selectedMailbox?.id;
|
||||
refreshGetAllEmailAction();
|
||||
} else {
|
||||
mailboxDashBoardController.viewState.value.map((success) {
|
||||
if (success is MarkAsEmailReadSuccess ||
|
||||
success is MarkAsMultipleEmailReadAllSuccess ||
|
||||
success is MarkAsMultipleEmailReadHasSomeEmailFailure) {
|
||||
_refreshListEmail();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
mailboxDashBoardController.viewState.listen((state) {
|
||||
state.map((success) {
|
||||
if (success is MoveToMailboxSuccess) {
|
||||
if (success is MarkAsEmailReadSuccess
|
||||
|| success is MarkAsMultipleEmailReadAllSuccess
|
||||
|| success is MarkAsMultipleEmailReadHasSomeEmailFailure) {
|
||||
_refreshListEmail();
|
||||
mailboxDashBoardController.clearState();
|
||||
} else if (success is MoveToMailboxSuccess) {
|
||||
_refreshListEmail();
|
||||
mailboxDashBoardController.clearState();
|
||||
}
|
||||
@@ -108,19 +110,20 @@ class ThreadController extends BaseController {
|
||||
(failure) {
|
||||
if (failure is GetAllEmailFailure) {
|
||||
_resetPositionCurrentAndLoadMoreState();
|
||||
} else if (failure is MarkAsEmailReadFailure ||
|
||||
failure is MarkAsMultipleEmailReadAllFailure ||
|
||||
failure is MarkAsMultipleEmailReadFailure) {
|
||||
} else if (failure is MarkAsMultipleEmailReadAllFailure
|
||||
|| failure is MarkAsMultipleEmailReadFailure) {
|
||||
_markAsSelectedEmailReadFailure(failure);
|
||||
}
|
||||
},
|
||||
(success) {
|
||||
if (success is GetAllEmailSuccess) {
|
||||
_getAllEmailSuccess(success);
|
||||
} else if (success is MarkAsEmailReadSuccess ||
|
||||
success is MarkAsMultipleEmailReadAllSuccess ||
|
||||
success is MarkAsMultipleEmailReadHasSomeEmailFailure) {
|
||||
} else if (success is MarkAsMultipleEmailReadAllSuccess
|
||||
|| success is MarkAsMultipleEmailReadHasSomeEmailFailure) {
|
||||
_markAsSelectedEmailReadSuccess(success);
|
||||
} else if (success is MoveMultipleEmailToMailboxAllSuccess
|
||||
|| success is MoveMultipleEmailToMailboxHasSomeEmailFailure) {
|
||||
_moveSelectedMultipleEmailToMailboxSuccess(success);
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -177,7 +180,8 @@ class ThreadController extends BaseController {
|
||||
void refreshGetAllEmailAction() {
|
||||
loadMoreState.value = LoadMoreState.IDLE;
|
||||
positionCurrent = 0;
|
||||
emailList.clear();
|
||||
dispatchState(Right(LoadingState()));
|
||||
emailList.value = <PresentationEmail>[];
|
||||
|
||||
final accountId = mailboxDashBoardController.accountId.value;
|
||||
|
||||
@@ -245,9 +249,11 @@ class ThreadController extends BaseController {
|
||||
}
|
||||
|
||||
void _refreshListEmail() {
|
||||
currentSelectMode.value = SelectMode.INACTIVE;
|
||||
final newLimit = emailList.isNotEmpty ? UnsignedInt(emailList.length) : ThreadConstants.defaultLimit;
|
||||
loadMoreState.value = LoadMoreState.IDLE;
|
||||
emailList.clear();
|
||||
dispatchState(Right(LoadingState()));
|
||||
emailList.value = <PresentationEmail>[];
|
||||
|
||||
final accountId = mailboxDashBoardController.accountId.value;
|
||||
|
||||
@@ -262,61 +268,42 @@ class ThreadController extends BaseController {
|
||||
}
|
||||
}
|
||||
|
||||
void markAsSelectedEmailRead(List<PresentationEmail> listEmail, {bool fromContextMenuAction = false}) {
|
||||
void markAsSelectedEmailRead(List<PresentationEmail> listPresentationEmail, {bool fromContextMenuAction = false}) {
|
||||
if (fromContextMenuAction) {
|
||||
popBack();
|
||||
}
|
||||
|
||||
final readAction = isEmailAllRead(listEmail) ? ReadActions.markAsUnread : ReadActions.markAsRead;
|
||||
|
||||
final listEmailId = listEmail
|
||||
.where((email) => readAction == ReadActions.markAsUnread ? email.isReadEmail() : email.isUnReadEmail())
|
||||
.map((email) => email.id)
|
||||
.toList();
|
||||
final readAction = isEmailAllRead(listPresentationEmail) ? ReadActions.markAsUnread : ReadActions.markAsRead;
|
||||
|
||||
final accountId = mailboxDashBoardController.accountId.value;
|
||||
final mailboxCurrent = mailboxDashBoardController.selectedMailbox.value;
|
||||
if (accountId != null && mailboxCurrent != null) {
|
||||
consumeState(_markAsMultipleEmailReadInteractor.execute(accountId, listEmailId, readAction));
|
||||
final listEmail = listPresentationEmail.map((presentationEmail) => presentationEmail.toEmail()).toList();
|
||||
consumeState(_markAsMultipleEmailReadInteractor.execute(accountId, listEmail, readAction));
|
||||
}
|
||||
}
|
||||
|
||||
void _markAsSelectedEmailReadSuccess(Success success) {
|
||||
cancelSelectEmail();
|
||||
|
||||
List<EmailId> listEmailId = <EmailId>[];
|
||||
ReadActions? readActions;
|
||||
mailboxDashBoardController.dispatchState(Right(success));
|
||||
|
||||
if (success is MarkAsEmailReadSuccess) {
|
||||
listEmailId.add(success.emailId);
|
||||
readActions = success.readActions;
|
||||
} else if (success is MarkAsMultipleEmailReadAllSuccess) {
|
||||
success.resultList.forEach((either) {
|
||||
either.map((success) {
|
||||
if (success is MarkAsEmailReadSuccess) {
|
||||
listEmailId.add(success.emailId);
|
||||
}
|
||||
});
|
||||
});
|
||||
ReadActions? readActions;
|
||||
int countMarkAsReadSuccess = 0;
|
||||
|
||||
if (success is MarkAsMultipleEmailReadAllSuccess) {
|
||||
readActions = success.readActions;
|
||||
countMarkAsReadSuccess = success.countMarkAsReadSuccess;
|
||||
} else if (success is MarkAsMultipleEmailReadHasSomeEmailFailure) {
|
||||
success.resultList.forEach((either) {
|
||||
either.map((success) {
|
||||
if (success is MarkAsEmailReadSuccess) {
|
||||
listEmailId.add(success.emailId);
|
||||
}
|
||||
});
|
||||
});
|
||||
readActions = success.readActions;
|
||||
countMarkAsReadSuccess = success.countMarkAsReadSuccess;
|
||||
}
|
||||
|
||||
if (Get.context != null && readActions != null) {
|
||||
_appToast.showSuccessToast(readActions == ReadActions.markAsUnread
|
||||
? AppLocalizations.of(Get.context!).marked_multiple_item_as_unread(listEmailId.length)
|
||||
: AppLocalizations.of(Get.context!).marked_multiple_item_as_read(listEmailId.length));
|
||||
? AppLocalizations.of(Get.context!).marked_multiple_item_as_unread(countMarkAsReadSuccess)
|
||||
: AppLocalizations.of(Get.context!).marked_multiple_item_as_read(countMarkAsReadSuccess));
|
||||
}
|
||||
|
||||
mailboxDashBoardController.dispatchState(Right(success));
|
||||
}
|
||||
|
||||
void _markAsSelectedEmailReadFailure(Failure failure) {
|
||||
@@ -330,17 +317,87 @@ class ThreadController extends BaseController {
|
||||
.build();
|
||||
}
|
||||
|
||||
void moveSelectedMultipleEmailToMailboxAction(List<PresentationEmail> listEmail) {
|
||||
void moveSelectedMultipleEmailToMailboxAction(List<PresentationEmail> listEmail) async {
|
||||
final currentMailbox = mailboxDashBoardController.selectedMailbox.value;
|
||||
final accountId = mailboxDashBoardController.accountId.value;
|
||||
if (currentMailbox != null && accountId != null) {
|
||||
popBack();
|
||||
|
||||
final listEmailIds = listEmail.map((email) => email.id).toList();
|
||||
push(
|
||||
final destinationMailbox = await push(
|
||||
AppRoutes.DESTINATION_PICKER,
|
||||
arguments: DestinationPickerArguments(accountId, listEmailIds, currentMailbox)
|
||||
);
|
||||
|
||||
if (destinationMailbox != null && destinationMailbox is PresentationMailbox) {
|
||||
_moveSelectedEmailMultipleToMailbox(
|
||||
accountId,
|
||||
MoveRequest(
|
||||
listEmailIds,
|
||||
currentMailbox.id,
|
||||
destinationMailbox.id,
|
||||
MoveAction.moveTo,
|
||||
destinationPath: destinationMailbox.mailboxPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _moveSelectedEmailMultipleToMailbox(AccountId accountId, MoveRequest moveRequest) {
|
||||
consumeState(_moveMultipleEmailToMailboxInteractor.execute(accountId, moveRequest));
|
||||
}
|
||||
|
||||
void _moveSelectedMultipleEmailToMailboxSuccess(Success success) {
|
||||
mailboxDashBoardController.dispatchState(Right(success));
|
||||
|
||||
String? destinationPath;
|
||||
List<EmailId> movedEmailIds = [];
|
||||
MailboxId? currentMailboxId;
|
||||
MailboxId? destinationMailboxId;
|
||||
MoveAction? moveAction;
|
||||
|
||||
if (success is MoveMultipleEmailToMailboxAllSuccess) {
|
||||
destinationPath = success.destinationPath;
|
||||
movedEmailIds = success.movedListEmailId;
|
||||
currentMailboxId = success.currentMailboxId;
|
||||
destinationMailboxId = success.destinationMailboxId;
|
||||
moveAction = success.moveAction;
|
||||
} else if (success is MoveMultipleEmailToMailboxHasSomeEmailFailure) {
|
||||
destinationPath = success.destinationPath;
|
||||
movedEmailIds = success.movedListEmailId;
|
||||
currentMailboxId = success.currentMailboxId;
|
||||
destinationMailboxId = success.destinationMailboxId;
|
||||
moveAction = success.moveAction;
|
||||
}
|
||||
|
||||
if (Get.context != null && Get.overlayContext != null
|
||||
&& destinationPath != null && moveAction == MoveAction.moveTo) {
|
||||
_appToast.showToastWithAction(
|
||||
Get.overlayContext!,
|
||||
AppLocalizations.of(Get.context!).moved_to_mailbox(destinationPath),
|
||||
AppLocalizations.of(Get.context!).undo_action,
|
||||
() {
|
||||
final newCurrentMailboxId = destinationMailboxId;
|
||||
final newDestinationMailboxId = currentMailboxId;
|
||||
if (newCurrentMailboxId != null && newDestinationMailboxId != null) {
|
||||
_undoMoveSelectedMultipleEmailToMailbox(MoveRequest(
|
||||
movedEmailIds,
|
||||
newCurrentMailboxId,
|
||||
newDestinationMailboxId,
|
||||
MoveAction.undo,
|
||||
destinationPath: destinationPath));
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
_refreshListEmail();
|
||||
}
|
||||
|
||||
void _undoMoveSelectedMultipleEmailToMailbox(MoveRequest moveRequest) {
|
||||
final accountId = mailboxDashBoardController.accountId.value;
|
||||
|
||||
if (accountId != null) {
|
||||
_moveSelectedEmailMultipleToMailbox(accountId, moveRequest);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -106,73 +106,14 @@ class ThreadView extends GetWidget<ThreadController> {
|
||||
.build();
|
||||
}
|
||||
|
||||
Widget _moveToTrashAction(BuildContext context, List<PresentationEmail> listEmail) {
|
||||
return (EmailContextMenuActionBuilder(
|
||||
Key('move_to_trash_context_menu_action'),
|
||||
SvgPicture.asset(imagePaths.icTrash, width: 24, height: 24, fit: BoxFit.fill),
|
||||
AppLocalizations.of(context).move_to_trash, listEmail)
|
||||
..onActionClick((data) => {}))
|
||||
.build();
|
||||
}
|
||||
|
||||
Widget _moveToMailboxAction(BuildContext context, List<PresentationEmail> listEmail) {
|
||||
return (EmailContextMenuActionBuilder(
|
||||
Key('move_to_mailbox_context_menu_action'),
|
||||
SvgPicture.asset(imagePaths.icFolder, width: 24, height: 24, fit: BoxFit.fill),
|
||||
AppLocalizations.of(context).move_to_mailbox, listEmail)
|
||||
..onActionClick((data) => {}))
|
||||
.build();
|
||||
}
|
||||
|
||||
Widget _markAsFlagAction(BuildContext context, List<PresentationEmail> listEmail) {
|
||||
return (EmailContextMenuActionBuilder(
|
||||
Key('mark_as_flag_context_menu_action'),
|
||||
SvgPicture.asset(imagePaths.icFlag, width: 24, height: 24, fit: BoxFit.fill),
|
||||
AppLocalizations.of(context).mark_as_flag, listEmail)
|
||||
..onActionClick((data) => {}))
|
||||
.build();
|
||||
}
|
||||
|
||||
Widget _moveToSpamAction(BuildContext context, List<PresentationEmail> listEmail) {
|
||||
return (EmailContextMenuActionBuilder(
|
||||
Key('move_to_spam_context_menu_action'),
|
||||
SvgPicture.asset(imagePaths.icMailboxSpam, width: 24, height: 24, fit: BoxFit.fill),
|
||||
AppLocalizations.of(context).move_to_spam, listEmail)
|
||||
..onActionClick((data) => {}))
|
||||
.build();
|
||||
}
|
||||
|
||||
List<Widget> _contextMenuActionList(BuildContext context, List<PresentationEmail> listEmail) {
|
||||
return [
|
||||
_moveToTrashAction(context, listEmail),
|
||||
_moveToMailboxAction(context, listEmail),
|
||||
_markAsReadAction(context, listEmail),
|
||||
_markAsFlagAction(context, listEmail),
|
||||
_moveToSpamAction(context, listEmail),
|
||||
SizedBox(height: 30),
|
||||
];
|
||||
}
|
||||
|
||||
Widget _markAsReadAction(BuildContext context, List<PresentationEmail> listEmail) {
|
||||
return (EmailContextMenuActionBuilder(
|
||||
Key('mark_as_read_context_menu_action'),
|
||||
SvgPicture.asset(imagePaths.icEyeDisable, width: 24, height: 24, fit: BoxFit.fill),
|
||||
controller.isEmailAllRead(listEmail)
|
||||
? AppLocalizations.of(context).mark_as_unread
|
||||
: AppLocalizations.of(context).mark_as_read,
|
||||
listEmail)
|
||||
..onActionClick((data) => controller.markAsSelectedEmailRead(data, fromContextMenuAction: true)))
|
||||
.build();
|
||||
}
|
||||
|
||||
Widget _moveToTrashAction(BuildContext context, List<PresentationEmail> listEmail) {
|
||||
return (EmailContextMenuActionBuilder(
|
||||
Key('move_to_trash_context_menu_action'),
|
||||
SvgPicture.asset(imagePaths.icTrash, width: 24, height: 24, fit: BoxFit.fill),
|
||||
AppLocalizations.of(context).move_to_trash,
|
||||
listEmail)
|
||||
..onActionClick((data) => {}))
|
||||
.build();
|
||||
..onActionClick((data) => {}))
|
||||
.build();
|
||||
}
|
||||
|
||||
Widget _moveToMailboxAction(BuildContext context, List<PresentationEmail> listEmail) {
|
||||
@@ -181,8 +122,8 @@ class ThreadView extends GetWidget<ThreadController> {
|
||||
SvgPicture.asset(imagePaths.icFolder, width: 24, height: 24, fit: BoxFit.fill),
|
||||
AppLocalizations.of(context).move_to_mailbox,
|
||||
listEmail)
|
||||
..onActionClick((emails) => controller.moveSelectedMultipleEmailToMailboxAction(emails)))
|
||||
.build();
|
||||
..onActionClick((data) => controller.moveSelectedMultipleEmailToMailboxAction(data)))
|
||||
.build();
|
||||
}
|
||||
|
||||
Widget _markAsFlagAction(BuildContext context, List<PresentationEmail> listEmail) {
|
||||
@@ -191,8 +132,8 @@ class ThreadView extends GetWidget<ThreadController> {
|
||||
SvgPicture.asset(imagePaths.icFlag, width: 24, height: 24, fit: BoxFit.fill),
|
||||
AppLocalizations.of(context).mark_as_flag,
|
||||
listEmail)
|
||||
..onActionClick((data) => {}))
|
||||
.build();
|
||||
..onActionClick((data) => {}))
|
||||
.build();
|
||||
}
|
||||
|
||||
Widget _moveToSpamAction(BuildContext context, List<PresentationEmail> listEmail) {
|
||||
@@ -201,8 +142,8 @@ class ThreadView extends GetWidget<ThreadController> {
|
||||
SvgPicture.asset(imagePaths.icMailboxSpam, width: 24, height: 24, fit: BoxFit.fill),
|
||||
AppLocalizations.of(context).move_to_spam,
|
||||
listEmail)
|
||||
..onActionClick((data) => {}))
|
||||
.build();
|
||||
..onActionClick((data) => {}))
|
||||
.build();
|
||||
}
|
||||
|
||||
Widget _buildLoadingView() {
|
||||
@@ -238,63 +179,59 @@ class ThreadView extends GetWidget<ThreadController> {
|
||||
padding: EdgeInsets.zero,
|
||||
color: responsiveUtils.isMobile(context) ? AppColor.bgMailboxListMail : Colors.white,
|
||||
child: Obx(() => controller.currentSelectMode.value == SelectMode.INACTIVE
|
||||
? RefreshIndicator(
|
||||
color: AppColor.primaryColor,
|
||||
onRefresh: () async => controller.refreshGetAllEmailAction(),
|
||||
child: _buildListEmailBody(context))
|
||||
: _buildListEmailBody(context)));
|
||||
? controller.emailList.isNotEmpty
|
||||
? RefreshIndicator(
|
||||
color: AppColor.primaryColor,
|
||||
onRefresh: () async => controller.refreshGetAllEmailAction(),
|
||||
child: _buildListEmailBody(context, controller.emailList))
|
||||
: RefreshIndicator(
|
||||
color: AppColor.primaryColor,
|
||||
onRefresh: () async => controller.refreshGetAllEmailAction(),
|
||||
child: _buildEmptyEmail(context))
|
||||
: controller.emailList.isNotEmpty
|
||||
? _buildListEmailBody(context, controller.emailList)
|
||||
: _buildEmptyEmail(context)));
|
||||
}
|
||||
|
||||
Widget _buildListEmailBody(BuildContext context) {
|
||||
return controller.emailList.isNotEmpty
|
||||
? _buildListEmailSuccess(context, controller.emailList)
|
||||
: _buildEmptyEmail(context);
|
||||
}
|
||||
Widget _buildListEmailSuccess(BuildContext context, List<PresentationEmail> listPresentationEmail) {
|
||||
if (listPresentationEmail.isEmpty) {
|
||||
return _buildEmptyEmail(context);
|
||||
} else {
|
||||
return NotificationListener<ScrollNotification>(
|
||||
onNotification: (ScrollNotification scrollInfo) {
|
||||
if (controller.loadMoreState.value == LoadMoreState.IDLE
|
||||
&& scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent) {
|
||||
controller.loadMoreEmailAction();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
child: ListView.builder(
|
||||
controller: controller.listEmailController,
|
||||
padding: EdgeInsets.only(top: 16),
|
||||
key: Key('presentation_email_list'),
|
||||
itemCount: listPresentationEmail.length,
|
||||
itemBuilder: (context, index) =>
|
||||
Obx(() => (EmailTileBuilder(
|
||||
context,
|
||||
imagePaths,
|
||||
controller.getSelectMode(listPresentationEmail[index], controller.mailboxDashBoardController.selectedEmail.value),
|
||||
listPresentationEmail[index],
|
||||
responsiveUtils,
|
||||
controller.currentSelectMode.value)
|
||||
..onOpenEmailAction((selectedEmail) => controller.previewEmail(context, selectedEmail))
|
||||
..onSelectEmailAction((selectedEmail) => controller.selectEmail(context, selectedEmail)))
|
||||
.build()),
|
||||
));
|
||||
}
|
||||
Widget _buildListEmailBody(BuildContext context, List<PresentationEmail> listPresentationEmail) {
|
||||
return NotificationListener<ScrollNotification>(
|
||||
onNotification: (ScrollNotification scrollInfo) {
|
||||
if (scrollInfo is ScrollEndNotification
|
||||
&& controller.loadMoreState.value == LoadMoreState.IDLE
|
||||
&& scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent) {
|
||||
controller.loadMoreEmailAction();
|
||||
}
|
||||
return false;
|
||||
},
|
||||
child: ListView.builder(
|
||||
controller: controller.listEmailController,
|
||||
physics: AlwaysScrollableScrollPhysics(),
|
||||
padding: EdgeInsets.only(top: 16),
|
||||
key: Key('presentation_email_list'),
|
||||
itemCount: listPresentationEmail.length,
|
||||
itemBuilder: (context, index) => Obx(() => (EmailTileBuilder(
|
||||
context,
|
||||
imagePaths,
|
||||
controller.getSelectMode(listPresentationEmail[index], controller.mailboxDashBoardController.selectedEmail.value),
|
||||
listPresentationEmail[index],
|
||||
responsiveUtils,
|
||||
controller.currentSelectMode.value)
|
||||
..onOpenEmailAction((selectedEmail) => controller.previewEmail(context, selectedEmail))
|
||||
..onSelectEmailAction((selectedEmail) => controller.selectEmail(context, selectedEmail)))
|
||||
.build()),
|
||||
));
|
||||
}
|
||||
|
||||
Widget _buildEmptyEmail(BuildContext context) {
|
||||
return Obx(() => controller.viewState.value.fold(
|
||||
(failure) => Text(
|
||||
AppLocalizations.of(context).no_emails,
|
||||
maxLines: 1,
|
||||
style: TextStyle(fontSize: 25, color: AppColor.mailboxTextColor, fontWeight: FontWeight.bold)),
|
||||
(success) => success is LoadingState
|
||||
? SizedBox.shrink()
|
||||
: Text(
|
||||
AppLocalizations.of(context).no_emails,
|
||||
maxLines: 1,
|
||||
style: TextStyle(fontSize: 25, color: AppColor.mailboxTextColor, fontWeight: FontWeight.bold)))
|
||||
(failure) => SizedBox.shrink(),
|
||||
(success) => !(success is LoadingState)
|
||||
? (BackgroundWidgetBuilder(context)
|
||||
..key(Key('empty_email_background'))
|
||||
..image(SvgPicture.asset(imagePaths.icEmptyImageDefault, width: 120, height: 120, fit: BoxFit.fill))
|
||||
..text(AppLocalizations.of(context).no_emails))
|
||||
.build()
|
||||
: SizedBox.shrink())
|
||||
);
|
||||
}
|
||||
}
|
||||
+33
-1
@@ -90,7 +90,7 @@
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
},
|
||||
"no_emails": "No emails",
|
||||
"no_emails": "No emails in this mailbox",
|
||||
"@no_emails": {
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
@@ -339,5 +339,37 @@
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
},
|
||||
"you_need_to_grant_files_permission_to_download_attachments": "You need to grant files permission to download attachments",
|
||||
"@you_need_to_grant_files_permission_to_download_attachments": {
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
},
|
||||
"count_attachment": "{count} attachments",
|
||||
"@count_attachment": {
|
||||
"type": "text",
|
||||
"placeholders_order": [
|
||||
"count"
|
||||
],
|
||||
"placeholders": {
|
||||
"count": {}
|
||||
}
|
||||
},
|
||||
"moved_to_mailbox": "Moved to {destinationMailboxPath}",
|
||||
"@moved_to_mailbox": {
|
||||
"type": "text",
|
||||
"placeholders_order": [
|
||||
"destinationMailboxPath"
|
||||
],
|
||||
"placeholders": {
|
||||
"destinationMailboxPath": {}
|
||||
}
|
||||
},
|
||||
"undo_action": "UNDO",
|
||||
"@undo_action": {
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
}
|
||||
}
|
||||
+33
-1
@@ -90,7 +90,7 @@
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
},
|
||||
"no_emails": "No emails",
|
||||
"no_emails": "No emails in this mailbox",
|
||||
"@no_emails": {
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
@@ -339,5 +339,37 @@
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
},
|
||||
"you_need_to_grant_files_permission_to_download_attachments": "You need to grant files permission to download attachments",
|
||||
"@you_need_to_grant_files_permission_to_download_attachments": {
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
},
|
||||
"count_attachment": "{count} attachments",
|
||||
"@count_attachment": {
|
||||
"type": "text",
|
||||
"placeholders_order": [
|
||||
"count"
|
||||
],
|
||||
"placeholders": {
|
||||
"count": {}
|
||||
}
|
||||
},
|
||||
"moved_to_mailbox": "Moved to {destinationMailboxPath}",
|
||||
"@moved_to_mailbox": {
|
||||
"type": "text",
|
||||
"placeholders_order": [
|
||||
"destinationMailboxPath"
|
||||
],
|
||||
"placeholders": {
|
||||
"destinationMailboxPath": {}
|
||||
}
|
||||
},
|
||||
"undo_action": "UNDO",
|
||||
"@undo_action": {
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"@@last_modified": "2021-09-17T17:37:57.805293",
|
||||
"@@last_modified": "2021-09-21T12:00:21.345356",
|
||||
"initializing_data": "Initializing data...",
|
||||
"@initializing_data": {
|
||||
"type": "text",
|
||||
@@ -90,7 +90,7 @@
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
},
|
||||
"no_emails": "No emails",
|
||||
"no_emails": "No emails in this mailbox",
|
||||
"@no_emails": {
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
@@ -339,5 +339,37 @@
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
},
|
||||
"you_need_to_grant_files_permission_to_download_attachments": "You need to grant files permission to download attachments",
|
||||
"@you_need_to_grant_files_permission_to_download_attachments": {
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
},
|
||||
"count_attachment": "{count} attachments",
|
||||
"@count_attachment": {
|
||||
"type": "text",
|
||||
"placeholders_order": [
|
||||
"count"
|
||||
],
|
||||
"placeholders": {
|
||||
"count": {}
|
||||
}
|
||||
},
|
||||
"moved_to_mailbox": "Moved to {destinationMailboxPath}",
|
||||
"@moved_to_mailbox": {
|
||||
"type": "text",
|
||||
"placeholders_order": [
|
||||
"destinationMailboxPath"
|
||||
],
|
||||
"placeholders": {
|
||||
"destinationMailboxPath": {}
|
||||
}
|
||||
},
|
||||
"undo_action": "UNDO",
|
||||
"@undo_action": {
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
}
|
||||
}
|
||||
+33
-1
@@ -90,7 +90,7 @@
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
},
|
||||
"no_emails": "No emails",
|
||||
"no_emails": "No emails in this mailbox",
|
||||
"@no_emails": {
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
@@ -339,5 +339,37 @@
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
},
|
||||
"you_need_to_grant_files_permission_to_download_attachments": "You need to grant files permission to download attachments",
|
||||
"@you_need_to_grant_files_permission_to_download_attachments": {
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
},
|
||||
"count_attachment": "{count} attachments",
|
||||
"@count_attachment": {
|
||||
"type": "text",
|
||||
"placeholders_order": [
|
||||
"count"
|
||||
],
|
||||
"placeholders": {
|
||||
"count": {}
|
||||
}
|
||||
},
|
||||
"moved_to_mailbox": "Moved to {destinationMailboxPath}",
|
||||
"@moved_to_mailbox": {
|
||||
"type": "text",
|
||||
"placeholders_order": [
|
||||
"destinationMailboxPath"
|
||||
],
|
||||
"placeholders": {
|
||||
"destinationMailboxPath": {}
|
||||
}
|
||||
},
|
||||
"undo_action": "UNDO",
|
||||
"@undo_action": {
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
}
|
||||
}
|
||||
+33
-1
@@ -90,7 +90,7 @@
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
},
|
||||
"no_emails": "No emails",
|
||||
"no_emails": "No emails in this mailbox",
|
||||
"@no_emails": {
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
@@ -339,5 +339,37 @@
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
},
|
||||
"you_need_to_grant_files_permission_to_download_attachments": "You need to grant files permission to download attachments",
|
||||
"@you_need_to_grant_files_permission_to_download_attachments": {
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
},
|
||||
"count_attachment": "{count} attachments",
|
||||
"@count_attachment": {
|
||||
"type": "text",
|
||||
"placeholders_order": [
|
||||
"count"
|
||||
],
|
||||
"placeholders": {
|
||||
"count": {}
|
||||
}
|
||||
},
|
||||
"moved_to_mailbox": "Moved to {destinationMailboxPath}",
|
||||
"@moved_to_mailbox": {
|
||||
"type": "text",
|
||||
"placeholders_order": [
|
||||
"destinationMailboxPath"
|
||||
],
|
||||
"placeholders": {
|
||||
"destinationMailboxPath": {}
|
||||
}
|
||||
},
|
||||
"undo_action": "UNDO",
|
||||
"@undo_action": {
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
}
|
||||
}
|
||||
@@ -110,7 +110,7 @@ class AppLocalizations {
|
||||
|
||||
String get no_emails {
|
||||
return Intl.message(
|
||||
'No emails',
|
||||
'No emails in this mailbox',
|
||||
name: 'no_emails',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
|
||||
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/email/keyword_identifier.dart';
|
||||
import 'package:model/model.dart';
|
||||
|
||||
extension EmailExtension on Email {
|
||||
|
||||
bool isUnReadEmail() => !(keywords?.containsKey(KeyWordIdentifier.emailSeen) == true);
|
||||
|
||||
bool isReadEmail() => keywords?.containsKey(KeyWordIdentifier.emailSeen) == true;
|
||||
|
||||
bool isFlaggedEmail() => keywords?.containsKey(KeyWordIdentifier.emailFlagged) == true;
|
||||
|
||||
Set<String> getRecipientEmailAddressList() {
|
||||
final listEmailAddress = Set<String>();
|
||||
final listToAddress = to.getListAddress() ?? [];
|
||||
@@ -22,6 +29,24 @@ extension EmailExtension on Email {
|
||||
);
|
||||
}
|
||||
|
||||
Email updatedEmail({Map<KeyWordIdentifier, bool>? newKeywords}) {
|
||||
return Email(
|
||||
id,
|
||||
keywords: newKeywords ?? keywords,
|
||||
size: size,
|
||||
receivedAt: receivedAt,
|
||||
hasAttachment: hasAttachment,
|
||||
preview: preview,
|
||||
subject: subject,
|
||||
sentAt: sentAt,
|
||||
from: from,
|
||||
to: to,
|
||||
cc: cc,
|
||||
bcc: bcc,
|
||||
replyTo: replyTo
|
||||
);
|
||||
}
|
||||
|
||||
PresentationEmail toPresentationEmail({SelectMode selectMode = SelectMode.INACTIVE}) {
|
||||
return PresentationEmail(
|
||||
id,
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import 'package:jmap_dart_client/jmap/core/id.dart';
|
||||
import 'package:jmap_dart_client/jmap/core/patch_object.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/email/keyword_identifier.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
|
||||
import 'package:model/model.dart';
|
||||
|
||||
extension ListEmailIdExtension on List<EmailId> {
|
||||
|
||||
List<Id> toIds() => map((emailId) => emailId.id).toList();
|
||||
|
||||
Map<Id, PatchObject> generateMapUpdateObjectMarkAsRead(ReadActions readActions) {
|
||||
final Map<Id, PatchObject> maps = {};
|
||||
forEach((emailId) {
|
||||
maps[emailId.id] = KeyWordIdentifier.emailSeen.generateReadActionPath(readActions);
|
||||
});
|
||||
return maps;
|
||||
}
|
||||
|
||||
Map<Id, PatchObject> generateMapUpdateObjectMoveToMailbox(MailboxId currentMailboxId, MailboxId destinationMailboxId) {
|
||||
final Map<Id, PatchObject> maps = {};
|
||||
forEach((emailId) {
|
||||
maps[emailId.id] = currentMailboxId.generateMoveToMailboxActionPath(destinationMailboxId);
|
||||
});
|
||||
return maps;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:core/core.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/email/keyword_identifier.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
||||
import 'package:model/model.dart';
|
||||
|
||||
extension PresentationEmailExtension on PresentationEmail {
|
||||
@@ -14,39 +14,6 @@ extension PresentationEmailExtension on PresentationEmail {
|
||||
return '';
|
||||
}
|
||||
|
||||
PresentationEmail markAsReadPresentationEmail(ReadActions readActions) {
|
||||
var newKeyWord = keywords;
|
||||
|
||||
if (readActions == ReadActions.markAsUnread) {
|
||||
newKeyWord?.removeWhere((key, value) => key == KeyWordIdentifier.emailSeen);
|
||||
} else {
|
||||
if (newKeyWord == null) {
|
||||
newKeyWord = {
|
||||
KeyWordIdentifier.emailSeen: true
|
||||
};
|
||||
} else {
|
||||
newKeyWord[KeyWordIdentifier.emailSeen] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return PresentationEmail(
|
||||
id,
|
||||
keywords: newKeyWord,
|
||||
size: size,
|
||||
receivedAt: receivedAt,
|
||||
hasAttachment: hasAttachment,
|
||||
preview: preview,
|
||||
subject: subject,
|
||||
sentAt: sentAt,
|
||||
from: from,
|
||||
to: to,
|
||||
cc: cc,
|
||||
bcc: bcc,
|
||||
replyTo: replyTo,
|
||||
selectMode: selectMode
|
||||
);
|
||||
}
|
||||
|
||||
PresentationEmail toggleSelect() {
|
||||
return PresentationEmail(
|
||||
id,
|
||||
@@ -84,4 +51,22 @@ extension PresentationEmailExtension on PresentationEmail {
|
||||
selectMode: selectMode
|
||||
);
|
||||
}
|
||||
|
||||
Email toEmail() {
|
||||
return Email(
|
||||
id,
|
||||
keywords: keywords,
|
||||
size: size,
|
||||
receivedAt: receivedAt,
|
||||
hasAttachment: hasAttachment,
|
||||
preview: preview,
|
||||
subject: subject,
|
||||
sentAt: sentAt,
|
||||
from: from,
|
||||
to: to,
|
||||
cc: cc,
|
||||
bcc: bcc,
|
||||
replyTo: replyTo
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
import 'package:jmap_dart_client/jmap/core/unsigned_int.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
|
||||
import 'package:model/model.dart';
|
||||
|
||||
extension PresentationMailboxExtension on PresentationMailbox {
|
||||
|
||||
PresentationMailbox toPresentationMailbox({required UnsignedInt countUnRead}) {
|
||||
PresentationMailbox toPresentationMailboxWithMailboxPath(String mailboxPath) {
|
||||
return PresentationMailbox(
|
||||
id,
|
||||
name: name,
|
||||
@@ -12,12 +11,29 @@ extension PresentationMailboxExtension on PresentationMailbox {
|
||||
role: role,
|
||||
sortOrder: sortOrder,
|
||||
totalEmails: totalEmails,
|
||||
unreadEmails: UnreadEmails(countUnRead),
|
||||
unreadEmails: unreadEmails,
|
||||
totalThreads: totalThreads,
|
||||
unreadThreads: unreadThreads,
|
||||
myRights: myRights,
|
||||
isSubscribed: isSubscribed,
|
||||
selectMode: selectMode
|
||||
selectMode: selectMode,
|
||||
mailboxPath: mailboxPath
|
||||
);
|
||||
}
|
||||
|
||||
Mailbox toMailbox() {
|
||||
return Mailbox(
|
||||
id,
|
||||
name,
|
||||
parentId,
|
||||
role,
|
||||
sortOrder,
|
||||
totalEmails,
|
||||
unreadEmails,
|
||||
totalThreads,
|
||||
unreadThreads,
|
||||
myRights,
|
||||
isSubscribed
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ class PresentationMailbox with EquatableMixin {
|
||||
final MailboxRights? myRights;
|
||||
final IsSubscribed? isSubscribed;
|
||||
final SelectMode selectMode;
|
||||
final String? mailboxPath;
|
||||
|
||||
PresentationMailbox(
|
||||
this.id,
|
||||
@@ -37,7 +38,8 @@ class PresentationMailbox with EquatableMixin {
|
||||
this.unreadThreads,
|
||||
this.myRights,
|
||||
this.isSubscribed,
|
||||
this.selectMode = SelectMode.INACTIVE
|
||||
this.selectMode = SelectMode.INACTIVE,
|
||||
this.mailboxPath,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -38,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/list_email_id_extension.dart';
|
||||
export 'extensions/mailbox_id_extension.dart';
|
||||
|
||||
// Download
|
||||
|
||||
Reference in New Issue
Block a user