TF-460 Separate the header of MailboxDashboard on the browser

This commit is contained in:
dab246
2022-04-25 15:56:36 +07:00
committed by Dat H. Pham
parent d0a54a2652
commit 52fd989272
36 changed files with 1033 additions and 643 deletions
+3
View File
@@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M3 3C2.73478 3 2.48043 3.10536 2.29289 3.29289C2.10536 3.48043 2 3.73478 2 4V16C2 16.2652 2.10536 16.5196 2.29289 16.7071C2.48043 16.8946 2.73478 17 3 17C3.26522 17 3.51957 16.8946 3.70711 16.7071C3.89464 16.5196 4 16.2652 4 16V4C4 3.73478 3.89464 3.48043 3.70711 3.29289C3.51957 3.10536 3.26522 3 3 3ZM13.293 12.293C13.1108 12.4816 13.01 12.7342 13.0123 12.9964C13.0146 13.2586 13.1198 13.5094 13.3052 13.6948C13.4906 13.8802 13.7414 13.9854 14.0036 13.9877C14.2658 13.99 14.5184 13.8892 14.707 13.707L17.707 10.707C17.8945 10.5195 17.9998 10.2652 17.9998 10C17.9998 9.73484 17.8945 9.48053 17.707 9.293L14.707 6.293C14.6148 6.19749 14.5044 6.12131 14.3824 6.0689C14.2604 6.01649 14.1292 5.9889 13.9964 5.98775C13.8636 5.9866 13.7319 6.0119 13.609 6.06218C13.4861 6.11246 13.3745 6.18671 13.2806 6.2806C13.1867 6.3745 13.1125 6.48615 13.0622 6.60905C13.0119 6.73194 12.9866 6.86362 12.9877 6.9964C12.9889 7.12918 13.0165 7.2604 13.0689 7.3824C13.1213 7.50441 13.1975 7.61475 13.293 7.707L14.586 9H7C6.73478 9 6.48043 9.10536 6.29289 9.29289C6.10536 9.48043 6 9.73478 6 10C6 10.2652 6.10536 10.5196 6.29289 10.7071C6.48043 10.8946 6.73478 11 7 11H14.586L13.293 12.293Z" fill="#007AFF"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

+3
View File
@@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.49 3.17C11.11 1.61 8.88999 1.61 8.50999 3.17C8.45326 3.40442 8.34198 3.62212 8.18522 3.80541C8.02845 3.9887 7.83062 4.13238 7.60784 4.22477C7.38505 4.31716 7.1436 4.35564 6.90313 4.33709C6.66266 4.31854 6.42997 4.24347 6.22399 4.118C4.85199 3.282 3.28199 4.852 4.11799 6.224C4.65799 7.11 4.17899 8.266 3.17099 8.511C1.60999 8.89 1.60999 11.111 3.17099 11.489C3.40547 11.5458 3.62322 11.6572 3.80651 11.8141C3.98979 11.971 4.13343 12.1689 4.22573 12.3918C4.31803 12.6147 4.35639 12.8563 4.33766 13.0968C4.31894 13.3373 4.24368 13.5701 4.11799 13.776C3.28199 15.148 4.85199 16.718 6.22399 15.882C6.42993 15.7563 6.66265 15.6811 6.90318 15.6623C7.14371 15.6436 7.38527 15.682 7.60817 15.7743C7.83108 15.8666 8.02904 16.0102 8.18592 16.1935C8.34281 16.3768 8.45419 16.5945 8.51099 16.829C8.88999 18.39 11.111 18.39 11.489 16.829C11.546 16.5946 11.6575 16.377 11.8144 16.1939C11.9713 16.0107 12.1692 15.8672 12.3921 15.7749C12.6149 15.6826 12.8564 15.6442 13.0969 15.6628C13.3373 15.6815 13.57 15.7565 13.776 15.882C15.148 16.718 16.718 15.148 15.882 13.776C15.7565 13.57 15.6815 13.3373 15.6628 13.0969C15.6442 12.8564 15.6826 12.6149 15.7749 12.3921C15.8672 12.1692 16.0107 11.9713 16.1939 11.8144C16.377 11.6575 16.5946 11.546 16.829 11.489C18.39 11.11 18.39 8.889 16.829 8.511C16.5945 8.45419 16.3768 8.34281 16.1935 8.18593C16.0102 8.02904 15.8666 7.83109 15.7743 7.60818C15.682 7.38527 15.6436 7.14372 15.6623 6.90318C15.681 6.66265 15.7563 6.42994 15.882 6.224C16.718 4.852 15.148 3.282 13.776 4.118C13.5701 4.24368 13.3373 4.31895 13.0968 4.33767C12.8563 4.35639 12.6147 4.31804 12.3918 4.22574C12.1689 4.13344 11.971 3.9898 11.8141 3.80651C11.6572 3.62323 11.5458 3.40548 11.489 3.171L11.49 3.17ZM9.99999 13C10.7956 13 11.5587 12.6839 12.1213 12.1213C12.6839 11.5587 13 10.7956 13 10C13 9.20435 12.6839 8.44129 12.1213 7.87868C11.5587 7.31607 10.7956 7 9.99999 7C9.20434 7 8.44128 7.31607 7.87867 7.87868C7.31606 8.44129 6.99999 9.20435 6.99999 10C6.99999 10.7956 7.31606 11.5587 7.87867 12.1213C8.44128 12.6839 9.20434 13 9.99999 13V13Z" stroke="#007AFF" stroke-width="1.5"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

+1
View File
@@ -61,6 +61,7 @@ export 'presentation/views/bottom_popup/cupertino_action_sheet_builder.dart';
export 'presentation/views/bottom_popup/confirmation_dialog_action_sheet_builder.dart';
export 'presentation/views/modal_sheets/edit_text_modal_sheet_builder.dart';
export 'presentation/views/search/search_bar_view.dart';
export 'presentation/views/popup_menu/popup_menu_item_widget.dart';
// Resources
export 'presentation/resources/assets_paths.dart';
@@ -92,6 +92,8 @@ class ImagePaths {
String get icToastNotSpam => _getImagePath('ic_toast_not_spam.svg');
String get icNotSpam => _getImagePath('ic_not_spam.svg');
String get icAvatarSpam => _getImagePath('ic_avatar_spam.svg');
String get icLogout => _getImagePath('ic_logout.svg');
String get icSetting => _getImagePath('ic_setting.svg');
String _getImagePath(String imageName) {
return AssetsPaths.images + imageName;
@@ -78,6 +78,7 @@ class AvatarBuilder {
_onTapAvatarActionWithPositionClick?.call(position);
}
},
borderRadius: BorderRadius.circular((_size ?? 40) / 2),
child: Container(
key: _key,
width: _size ?? 40,
@@ -0,0 +1,37 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
@immutable
class PopupMenuItemWidget extends StatelessWidget {
final Function onCallBack;
final String icon;
final String name;
final Color? iconColor;
final String? iconSelection;
PopupMenuItemWidget(this.icon, this.name, this.onCallBack, {this.iconSelection, this.iconColor});
@override
Widget build(BuildContext context) {
return InkWell(
onTap: () => onCallBack.call(),
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 16),
child: SizedBox(
child: Row(children: [
SvgPicture.asset(icon, width: 20, height: 20, fit: BoxFit.fill, color: iconColor),
SizedBox(width: 12),
Expanded(child: Text(name,
style: TextStyle(fontSize: 15, color: Colors.black, fontWeight: FontWeight.w500))),
if (iconSelection != null)
Padding(
padding: EdgeInsets.only(left: 12),
child: SvgPicture.asset(iconSelection!, width: 16, height: 16, fit: BoxFit.fill)),
])
),
)
);
}
}
+6
View File
@@ -0,0 +1,6 @@
import 'package:equatable/equatable.dart';
abstract class Action with EquatableMixin {}
abstract class UIAction extends Action {}
@@ -0,0 +1,13 @@
import 'package:core/core.dart';
import 'package:flutter/cupertino.dart';
mixin AppLoaderMixin {
Widget get loadingWidget {
return Center(child: SizedBox(
width: 24,
height: 24,
child: CupertinoActivityIndicator(color: AppColor.colorLoading)));
}
}
@@ -19,8 +19,8 @@ mixin PopupContextMenuActionMixin {
context: context,
position: position ?? RelativeRect.fromLTRB(16, 40, 16, 16),
color: Colors.white,
elevation: 5,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)),
elevation: 4,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
items: popupMenuItems);
}
@@ -42,8 +42,8 @@ import 'package:tmail_ui_user/features/composer/presentation/model/screen_displa
import 'package:tmail_ui_user/features/email/domain/state/get_email_content_state.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/get_email_content_interactor.dart';
import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/dashboard_action.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/dashboard_action.dart';
import 'package:tmail_ui_user/features/upload/domain/state/local_file_picker_state.dart';
import 'package:tmail_ui_user/features/upload/domain/usecases/local_file_picker_interactor.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
@@ -782,7 +782,7 @@ class ComposerController extends BaseController {
void closeComposerWeb() {
FocusManager.instance.primaryFocus?.unfocus();
mailboxDashBoardController.dispatchDashBoardAction(DashBoardAction.none);
mailboxDashBoardController.dispatchAction(CloseComposeEmailAction());
}
void displayScreenTypeComposerAction(ScreenDisplayMode displayMode) {
@@ -798,7 +798,7 @@ class ComposerController extends BaseController {
void deleteComposer() {
FocusManager.instance.primaryFocus?.unfocus();
mailboxDashBoardController.dispatchDashBoardAction(DashBoardAction.none);
mailboxDashBoardController.dispatchAction(CloseComposeEmailAction());
}
void toggleDisplayAttachments() {
@@ -23,11 +23,6 @@ import 'package:tmail_ui_user/features/login/domain/repository/credential_reposi
class EmailBindings extends BaseBindings {
@override
void dependencies() {
super.dependencies();
}
@override
void bindingsController() {
Get.put(EmailController(
@@ -35,9 +35,9 @@ import 'package:tmail_ui_user/features/email/presentation/model/composer_argumen
import 'package:tmail_ui_user/features/email/presentation/widgets/email_address_bottom_sheet_builder.dart';
import 'package:tmail_ui_user/features/email/presentation/widgets/email_address_dialog_builder.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/dashboard_action.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart';
import 'package:tmail_ui_user/features/composer/presentation/extensions/email_action_type_extension.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/dashboard_action.dart';
import 'package:tmail_ui_user/features/thread/presentation/model/delete_action_type.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
import 'package:tmail_ui_user/main/routes/app_routes.dart';
@@ -86,7 +86,7 @@ class EmailController extends BaseController {
@override
void onInit() {
mailboxDashBoardController.selectedEmail.listen((presentationEmail) {
log('EmailController::onReady(): ${presentationEmail.toString()}');
log('EmailController::onReady(): selectedEmail: ${presentationEmail.toString()}');
if (_currentEmailId != presentationEmail?.id) {
_currentEmailId = presentationEmail?.id;
_resetToOriginalValue();
@@ -546,8 +546,8 @@ class EmailController extends BaseController {
emailAddress: emailAddress,
mailboxRole: mailboxDashBoardController.selectedMailbox.value?.role);
if (kIsWeb) {
if (mailboxDashBoardController.dashBoardAction != DashBoardAction.compose) {
mailboxDashBoardController.dispatchDashBoardAction(DashBoardAction.compose, arguments: arguments);
if (mailboxDashBoardController.dashBoardAction.value is! ComposeEmailAction) {
mailboxDashBoardController.dispatchAction(ComposeEmailAction(arguments: arguments));
}
} else {
push(AppRoutes.COMPOSER, arguments: arguments);
@@ -565,8 +565,8 @@ class EmailController extends BaseController {
emailAddress: emailAddress,
mailboxRole: mailboxDashBoardController.selectedMailbox.value?.role);
if (kIsWeb) {
if (mailboxDashBoardController.dashBoardAction != DashBoardAction.compose) {
mailboxDashBoardController.dispatchDashBoardAction(DashBoardAction.compose, arguments: arguments);
if (mailboxDashBoardController.dashBoardAction.value is! ComposeEmailAction) {
mailboxDashBoardController.dispatchAction(ComposeEmailAction(arguments: arguments));
}
if (Get.currentRoute == AppRoutes.EMAIL) {
popBack();
@@ -629,8 +629,8 @@ class EmailController extends BaseController {
mailboxRole: mailboxDashBoardController.selectedMailbox.value?.role);
if (kIsWeb) {
if (mailboxDashBoardController.dashBoardAction != DashBoardAction.compose) {
mailboxDashBoardController.dispatchDashBoardAction(DashBoardAction.compose, arguments: arguments);
if (mailboxDashBoardController.dashBoardAction.value is! ComposeEmailAction) {
mailboxDashBoardController.dispatchAction(ComposeEmailAction(arguments: arguments));
}
if (Get.currentRoute == AppRoutes.EMAIL) {
popBack();
@@ -643,8 +643,8 @@ class EmailController extends BaseController {
void composeEmailAction() {
if (kIsWeb) {
if (mailboxDashBoardController.dashBoardAction != DashBoardAction.compose) {
mailboxDashBoardController.dispatchDashBoardAction(DashBoardAction.compose, arguments: ComposerArguments());
if (mailboxDashBoardController.dashBoardAction.value is! ComposeEmailAction) {
mailboxDashBoardController.dispatchAction(ComposeEmailAction(arguments: ComposerArguments()));
}
if (Get.currentRoute == AppRoutes.EMAIL) {
popBack();
+72 -107
View File
@@ -15,11 +15,10 @@ import 'package:tmail_ui_user/features/email/presentation/widgets/app_bar_mail_w
import 'package:tmail_ui_user/features/email/presentation/widgets/attachment_file_tile_builder.dart';
import 'package:tmail_ui_user/features/email/presentation/widgets/bottom_bar_mail_widget_builder.dart';
import 'package:tmail_ui_user/features/email/presentation/widgets/email_action_cupertino_action_sheet_action_builder.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/user_setting_popup_menu_mixin.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
import 'package:filesize/filesize.dart';
class EmailView extends GetView with UserSettingPopupMenuMixin, NetworkConnectionMixin {
class EmailView extends GetView with NetworkConnectionMixin {
final emailController = Get.find<EmailController>();
final responsiveUtils = Get.find<ResponsiveUtils>();
@@ -27,6 +26,8 @@ class EmailView extends GetView with UserSettingPopupMenuMixin, NetworkConnectio
static const int LIMIT_ADDRESS_DISPLAY = 1;
EmailView({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return WillPopScope(
@@ -37,58 +38,22 @@ class EmailView extends GetView with UserSettingPopupMenuMixin, NetworkConnectio
child: Scaffold(
backgroundColor: responsiveUtils.isDesktop(context) ? AppColor.colorBgDesktop : Colors.white,
body: Stack(children: [
Container(
padding: EdgeInsets.zero,
margin: EdgeInsets.zero,
decoration: responsiveUtils.isTabletLarge(context)
? BoxDecoration(border: Border(left: BorderSide(color: AppColor.colorLineLeftEmailView, width: 1.0)))
: null,
child: SafeArea(
right: responsiveUtils.isMobileDevice(context) && responsiveUtils.isLandscape(context),
left: responsiveUtils.isMobileDevice(context) && responsiveUtils.isLandscape(context),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (responsiveUtils.isDesktop(context))
Container(
color: Colors.white,
padding: EdgeInsets.only(right: 10, top: 16, bottom: 10),
child: _buildHeader(context)),
Expanded(child: _buildBody(context)),
]
)
),
),
Row(children: [
if ((!kIsWeb && !responsiveUtils.isMobile(context)) || (kIsWeb && responsiveUtils.isTabletLarge(context)))
const VerticalDivider(color: AppColor.lineItemListColor, width: 1, thickness: 0.2),
Expanded(child: SafeArea(
right: responsiveUtils.isLandscapeMobile(context),
left: responsiveUtils.isLandscapeMobile(context),
child: _buildBody(context)))
]),
Obx(() => !emailController.mailboxDashBoardController.isNetworkConnectionAvailable() && (responsiveUtils.isMobile(context) || responsiveUtils.isTablet(context))
? Align(alignment: Alignment.bottomCenter, child: buildNetworkConnectionWidget(context))
: SizedBox.shrink()),
: const SizedBox.shrink()),
])
)
);
}
Widget _buildHeader(BuildContext context) {
return Row(children: [
Spacer(),
Padding(
padding: EdgeInsets.only(right: 16),
child: (AvatarBuilder()
..text(emailController.mailboxDashBoardController.userProfile.value?.getAvatarText() ?? '')
..backgroundColor(Colors.white)
..textColor(Colors.black)
..context(context)
..addOnTapAvatarActionWithPositionClick((position) => openUserSettingAction(
context,
position,
popupMenuUserSettingActionTile(context, () => emailController.mailboxDashBoardController.logoutAction())))
..addBoxShadows([BoxShadow(
color: AppColor.colorShadowBgContentEmail,
spreadRadius: 1, blurRadius: 1, offset: Offset(0, 0.5))])
..size(48))
.build()),
]);
}
Widget _buildBody(BuildContext context) {
return Container(
decoration: responsiveUtils.isDesktop(context)
@@ -97,17 +62,17 @@ class EmailView extends GetView with UserSettingPopupMenuMixin, NetworkConnectio
border: Border.all(color: AppColor.colorBorderBodyThread, width: 1),
color: Colors.white)
: null,
margin: responsiveUtils.isDesktop(context) ? EdgeInsets.all(16) : EdgeInsets.zero,
padding: responsiveUtils.isDesktop(context) ? EdgeInsets.only(top: 5, bottom: 5, left: 5, right: 3) : EdgeInsets.zero,
margin: responsiveUtils.isDesktop(context) ? const EdgeInsets.all(16) : EdgeInsets.zero,
padding: responsiveUtils.isDesktop(context) ? const EdgeInsets.only(top: 5, bottom: 5, left: 5, right: 3) : EdgeInsets.zero,
child: Column(children: [
_buildAppBar(context),
if (responsiveUtils.isDesktop(context)) SizedBox(height: 5),
if (responsiveUtils.isDesktop(context)) const SizedBox(height: 5),
Obx(() => emailController.currentEmail != null && !responsiveUtils.isDesktop(context)
? _buildDivider() : SizedBox.shrink()),
? _buildDivider() : const SizedBox.shrink()),
Expanded(child: _buildEmailBody(context)),
Obx(() => emailController.currentEmail != null
? Divider(color: AppColor.colorDividerEmailView, height: 1)
: SizedBox.shrink()),
? const Divider(color: AppColor.colorDividerEmailView, height: 1)
: const SizedBox.shrink()),
_buildBottomBar(context),
])
);
@@ -115,13 +80,13 @@ class EmailView extends GetView with UserSettingPopupMenuMixin, NetworkConnectio
Widget _buildDivider({EdgeInsets? edgeInsets}){
return Padding(
padding: edgeInsets ?? EdgeInsets.symmetric(horizontal: 16),
child: Divider(color: AppColor.colorDividerEmailView, height: 0.5));
padding: edgeInsets ?? const EdgeInsets.symmetric(horizontal: 16),
child: const Divider(color: AppColor.colorDividerEmailView, height: 0.5));
}
Widget _buildAppBar(BuildContext context) {
return Obx(() => Padding(
padding: EdgeInsets.only(top: 6),
padding: const EdgeInsets.only(top: 6),
child: (AppBarMailWidgetBuilder(context, imagePaths, responsiveUtils, emailController.currentEmail, emailController.currentMailbox)
..onBackActionClick(() => emailController.backToThreadView(context))
..addOnEmailActionClick((email, action) => emailController.handleEmailAction(context, email, action))
@@ -154,7 +119,7 @@ class EmailView extends GetView with UserSettingPopupMenuMixin, NetworkConnectio
return _buildEmailMessage(context);
} else {
return SingleChildScrollView(
physics : ClampingScrollPhysics(),
physics : const ClampingScrollPhysics(),
child: Container(
margin: EdgeInsets.zero,
width: double.infinity,
@@ -176,12 +141,12 @@ class EmailView extends GetView with UserSettingPopupMenuMixin, NetworkConnectio
return Text(
AppLocalizations.of(context).no_mail_selected,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 25, color: AppColor.mailboxTextColor, fontWeight: FontWeight.bold));
style: const TextStyle(fontSize: 25, color: AppColor.mailboxTextColor, fontWeight: FontWeight.bold));
}
Widget _buildEmailSubject(BuildContext context) {
return Padding(
padding: EdgeInsets.only(right: 16),
padding: const EdgeInsets.only(right: 16),
child: SelectableText(
'${emailController.mailboxDashBoardController.selectedEmail.value?.getEmailTitle()}',
maxLines: kIsWeb ? 3 : null,
@@ -196,7 +161,7 @@ class EmailView extends GetView with UserSettingPopupMenuMixin, NetworkConnectio
Widget _buildEmailMessage(BuildContext context) {
return Container(
padding: EdgeInsets.only(left: 16, right: 16, top: 10),
padding: const EdgeInsets.only(left: 16, right: 16, top: 10),
alignment: Alignment.center,
color: Colors.white,
child: LayoutBuilder(
@@ -209,22 +174,22 @@ class EmailView extends GetView with UserSettingPopupMenuMixin, NetworkConnectio
Expanded(child: _buildEmailSubject(context)),
_buildEmailTime(context),
]),
if (responsiveUtils.isDesktop(context)) SizedBox(height: 20),
if (!responsiveUtils.isDesktop(context)) _buildDivider(edgeInsets: EdgeInsets.only(top: 16)),
if (responsiveUtils.isDesktop(context)) const SizedBox(height: 20),
if (!responsiveUtils.isDesktop(context)) _buildDivider(edgeInsets: const EdgeInsets.only(top: 16)),
Obx(() => emailController.currentEmail != null
? _buildEmailAddress(
context,
emailController.currentEmail!,
emailController.emailAddressExpandMode.value,
emailController.isDisplayFullEmailAddress.value)
: SizedBox.shrink()),
_buildDivider(edgeInsets: EdgeInsets.only(top: 8)),
: const SizedBox.shrink()),
_buildDivider(edgeInsets: const EdgeInsets.only(top: 8)),
_buildAttachments(context),
if (kIsWeb)
Expanded(child: _buildEmailContent(context, constraints))
else
_buildEmailContent(context, constraints),
SizedBox(height: 16),
const SizedBox(height: 16),
],
);
})
@@ -240,7 +205,7 @@ class EmailView extends GetView with UserSettingPopupMenuMixin, NetworkConnectio
pattern: emailController.currentEmail?.receivedAt?.value.toLocal().toPatternForEmailView())}',
maxLines: 1,
overflow:TextOverflow.ellipsis,
style: TextStyle(fontSize: 13, color: AppColor.colorTime)));
style: const TextStyle(fontSize: 13, color: AppColor.colorTime)));
}
Widget _buildEmailAddress(BuildContext context, PresentationEmail email, ExpandMode expandMode, bool isDisplayFull) {
@@ -251,15 +216,15 @@ class EmailView extends GetView with UserSettingPopupMenuMixin, NetworkConnectio
if (email.from.numberEmailAddress() > 0)
_buildEmailAddressByPrefix(context, email, PrefixEmailAddress.from, isDisplayFull),
if (email.to.numberEmailAddress() > 0 && expandMode == ExpandMode.EXPAND)
_buildDivider(edgeInsets: EdgeInsets.only(top: kIsWeb ? 8 : 4, bottom: 4)),
_buildDivider(edgeInsets: const EdgeInsets.only(top: kIsWeb ? 8 : 4, bottom: 4)),
if (email.to.numberEmailAddress() > 0 && expandMode == ExpandMode.EXPAND)
_buildEmailAddressByPrefix(context, email, PrefixEmailAddress.to, isDisplayFull),
if (email.cc.numberEmailAddress() > 0 && expandMode == ExpandMode.EXPAND && isDisplayFull)
_buildDivider(edgeInsets: EdgeInsets.only(top: kIsWeb ? 8 : 4, bottom: 4)),
_buildDivider(edgeInsets: const EdgeInsets.only(top: kIsWeb ? 8 : 4, bottom: 4)),
if (email.cc.numberEmailAddress() > 0 && expandMode == ExpandMode.EXPAND && isDisplayFull)
_buildEmailAddressByPrefix(context, email, PrefixEmailAddress.cc, isDisplayFull),
if (email.bcc.numberEmailAddress() > 0 && expandMode == ExpandMode.EXPAND && isDisplayFull)
_buildDivider(edgeInsets: EdgeInsets.only(top: kIsWeb ? 8 : 4, bottom: 4)),
_buildDivider(edgeInsets: const EdgeInsets.only(top: kIsWeb ? 8 : 4, bottom: 4)),
if (email.bcc.numberEmailAddress() > 0 && expandMode == ExpandMode.EXPAND && isDisplayFull)
_buildEmailAddressByPrefix(context, email, PrefixEmailAddress.bcc, isDisplayFull),
],
@@ -277,10 +242,10 @@ class EmailView extends GetView with UserSettingPopupMenuMixin, NetworkConnectio
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: EdgeInsets.only(top: kIsWeb ? 10: 20),
padding: const EdgeInsets.only(top: kIsWeb ? 10: 20),
child: Text(
'${prefixEmailAddress.asName(context)}:',
style: TextStyle(fontSize: 14, color: AppColor.colorEmailAddressPrefix))),
style: const TextStyle(fontSize: 14, color: AppColor.colorEmailAddressPrefix))),
Expanded(child: _buildEmailAddressWidget(
context,
presentationEmail,
@@ -302,26 +267,26 @@ class EmailView extends GetView with UserSettingPopupMenuMixin, NetworkConnectio
) {
final displayedEmailAddress = isDisplayFull ? listEmailAddress : listEmailAddress.sublist(0, LIMIT_ADDRESS_DISPLAY);
return Padding(
padding: EdgeInsets.only(top: 4),
padding: const EdgeInsets.only(top: 4),
child: Wrap(
children: [
...displayedEmailAddress.map((emailAddress) {
return Padding(
padding: EdgeInsets.only(left: 10),
padding: const EdgeInsets.only(left: 10),
child: GestureDetector(
onTap: () => emailController.openEmailAddressDialog(context, emailAddress),
child: Chip(
labelPadding: EdgeInsets.only(left: 8, right: 8, bottom: 2),
label: Text('${emailAddress.asString()}', maxLines: 1, overflow: TextOverflow.ellipsis),
labelStyle: TextStyle(color: AppColor.colorNameEmail, fontSize: 15, fontWeight: FontWeight.normal),
labelPadding: const EdgeInsets.only(left: 8, right: 8, bottom: 2),
label: Text(emailAddress.asString(), maxLines: 1, overflow: TextOverflow.ellipsis),
labelStyle: const TextStyle(color: AppColor.colorNameEmail, fontSize: 15, fontWeight: FontWeight.normal),
backgroundColor: AppColor.colorEmailAddressTag,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
side: BorderSide(width: 0, color: AppColor.colorEmailAddressTag),
side: const BorderSide(width: 0, color: AppColor.colorEmailAddressTag),
),
avatar: (AvatarBuilder()
..text('${emailAddress.asString().characters.first.toUpperCase()}')
..addTextStyle(TextStyle(fontSize: 12, color: Colors.white, fontWeight: FontWeight.w600))
..text(emailAddress.asString().characters.first.toUpperCase())
..addTextStyle(const TextStyle(fontSize: 12, color: Colors.white, fontWeight: FontWeight.w600))
..avatarColor(emailAddress.avatarColors))
.build(),
),
@@ -341,14 +306,14 @@ class EmailView extends GetView with UserSettingPopupMenuMixin, NetworkConnectio
borderRadius: BorderRadius.circular(12),
color: Colors.transparent,
child: Padding(
padding: EdgeInsets.only(top: 4, left: 16),
padding: const EdgeInsets.only(top: 4, left: 16),
child: TextButton(
onPressed: () => emailController.toggleDisplayEmailAddressAction(),
child: Text(
emailController.isExpandEmailAddress
? AppLocalizations.of(context).hide
: AppLocalizations.of(context).details,
style: TextStyle(fontSize: 15, color: AppColor.colorTextButton, fontWeight: FontWeight.normal),
style: const TextStyle(fontSize: 15, color: AppColor.colorTextButton, fontWeight: FontWeight.normal),
),
)
)
@@ -366,15 +331,15 @@ class EmailView extends GetView with UserSettingPopupMenuMixin, NetworkConnectio
return GestureDetector(
onTap: () => emailController.showFullEmailAddress(),
child: Padding(
padding: EdgeInsets.only(left: 8),
padding: const EdgeInsets.only(left: 8),
child: Chip(
labelPadding: EdgeInsets.symmetric(horizontal: 8),
labelPadding: const EdgeInsets.symmetric(horizontal: 8),
label: Text('+${_getRemainCountAddressReceiver(email)}', maxLines: 1, overflow: TextOverflow.ellipsis),
labelStyle: TextStyle(color: AppColor.colorTextButton, fontSize: 15, fontWeight: FontWeight.normal),
labelStyle: const TextStyle(color: AppColor.colorTextButton, fontSize: 15, fontWeight: FontWeight.normal),
backgroundColor: AppColor.colorEmailAddressTag,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
side: BorderSide(width: 0, color: AppColor.colorEmailAddressTag),
side: const BorderSide(width: 0, color: AppColor.colorEmailAddressTag),
),
),
),
@@ -383,15 +348,15 @@ class EmailView extends GetView with UserSettingPopupMenuMixin, NetworkConnectio
Widget _buildAttachments(BuildContext context) {
return Obx(() => emailController.viewState.value.fold(
(failure) => SizedBox.shrink(),
(failure) => const SizedBox.shrink(),
(success) {
if (success is LoadingState) {
return SizedBox.shrink();
return const SizedBox.shrink();
} else {
final attachments = emailController.attachments.listAttachmentsDisplayedOutSide;
return attachments.isNotEmpty
? _buildAttachmentsBody(context, attachments)
: SizedBox.shrink();
: const SizedBox.shrink();
}
}));
}
@@ -424,7 +389,7 @@ class EmailView extends GetView with UserSettingPopupMenuMixin, NetworkConnectio
padding: EdgeInsets.zero,
child: _buildAttachmentsHeader(context, attachments)),
GridView.builder(
key: Key('list_attachment'),
key: const Key('list_attachment'),
primary: false,
shrinkWrap: true,
padding: EdgeInsets.only(top: isExpand ? 0 : 16, bottom: 16),
@@ -483,18 +448,18 @@ class EmailView extends GetView with UserSettingPopupMenuMixin, NetworkConnectio
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${AppLocalizations.of(context).count_attachment(attachments.length)}',
style: TextStyle(fontSize: 12, color: AppColor.baseTextColor)),
AppLocalizations.of(context).count_attachment(attachments.length),
style: const TextStyle(fontSize: 12, color: AppColor.baseTextColor)),
Padding(
padding: EdgeInsets.only(left: 8),
padding: const EdgeInsets.only(left: 8),
child: Text(
'(${filesize(attachments.totalSize(), 1)})',
style: TextStyle(fontSize: 12, color: AppColor.nameUserColor, fontWeight: FontWeight.w500)))
style: const TextStyle(fontSize: 12, color: AppColor.nameUserColor, fontWeight: FontWeight.w500)))
],
),
if (attachments.length > 2)
Padding(
padding: EdgeInsets.only(left: 8),
padding: const EdgeInsets.only(left: 8),
child: IconButton(
icon: SvgPicture.asset(imagePaths.icExpandAttachment,
width: 20,
@@ -508,10 +473,10 @@ class EmailView extends GetView with UserSettingPopupMenuMixin, NetworkConnectio
Widget _buildEmailContent(BuildContext context, BoxConstraints constraints) {
return Obx(() => emailController.viewState.value.fold(
(failure) => SizedBox.shrink(),
(failure) => const SizedBox.shrink(),
(success) {
if (success is LoadingState) {
return Align(alignment: Alignment.topCenter, child: Padding(
return const Align(alignment: Alignment.topCenter, child: Padding(
padding: EdgeInsets.all(16),
child: SizedBox(
width: 30,
@@ -535,7 +500,7 @@ class EmailView extends GetView with UserSettingPopupMenuMixin, NetworkConnectio
mailtoDelegate: (uri) async => emailController.openMailToLink(uri));
}
} else {
return SizedBox.shrink();
return const SizedBox.shrink();
}
}
}));
@@ -550,15 +515,15 @@ class EmailView extends GetView with UserSettingPopupMenuMixin, NetworkConnectio
Widget _markAsEmailUnreadAction(BuildContext context, PresentationEmail email) {
return (EmailActionCupertinoActionSheetActionBuilder(
Key('mark_as_unread_action'),
const Key('mark_as_unread_action'),
SvgPicture.asset(imagePaths.icUnreadEmail, width: 28, height: 28, fit: BoxFit.fill, color: AppColor.colorTextButton),
AppLocalizations.of(context).mark_as_unread,
email,
iconLeftPadding: responsiveUtils.isMobile(context)
? EdgeInsets.only(left: 12, right: 16)
: EdgeInsets.only(right: 12),
? const EdgeInsets.only(left: 12, right: 16)
: const EdgeInsets.only(right: 12),
iconRightPadding: responsiveUtils.isMobile(context)
? EdgeInsets.only(right: 12)
? const EdgeInsets.only(right: 12)
: EdgeInsets.zero)
..onActionClick((email) => emailController.handleEmailAction(context, email, EmailActionType.markAsUnread)))
.build();
@@ -566,7 +531,7 @@ class EmailView extends GetView with UserSettingPopupMenuMixin, NetworkConnectio
Widget _markAsEmailSpamOrUnSpamAction(BuildContext context, PresentationEmail email) {
return (EmailActionCupertinoActionSheetActionBuilder(
Key('mark_as_spam_or_un_spam_action'),
const Key('mark_as_spam_or_un_spam_action'),
SvgPicture.asset(
emailController.currentMailbox?.isSpam == true ? imagePaths.icNotSpam : imagePaths.icSpam,
width: 28, height: 28, fit: BoxFit.fill, color: AppColor.colorTextButton),
@@ -575,10 +540,10 @@ class EmailView extends GetView with UserSettingPopupMenuMixin, NetworkConnectio
: AppLocalizations.of(context).mark_as_spam,
email,
iconLeftPadding: responsiveUtils.isMobile(context)
? EdgeInsets.only(left: 12, right: 16)
: EdgeInsets.only(right: 12),
? const EdgeInsets.only(left: 12, right: 16)
: const EdgeInsets.only(right: 12),
iconRightPadding: responsiveUtils.isMobile(context)
? EdgeInsets.only(right: 12)
? const EdgeInsets.only(right: 12)
: EdgeInsets.zero)
..onActionClick((email) => emailController.handleEmailAction(context, email,
emailController.currentMailbox?.isSpam == true ? EmailActionType.unSpam : EmailActionType.moveToSpam)))
@@ -587,8 +552,8 @@ class EmailView extends GetView with UserSettingPopupMenuMixin, NetworkConnectio
List<PopupMenuEntry> _popupMenuEmailActionTile(BuildContext context, PresentationEmail email) {
return [
PopupMenuItem(padding: EdgeInsets.symmetric(horizontal: 8), child: _markAsEmailUnreadAction(context, email)),
PopupMenuItem(padding: EdgeInsets.symmetric(horizontal: 8), child: _markAsEmailSpamOrUnSpamAction(context, email)),
PopupMenuItem(padding: const EdgeInsets.symmetric(horizontal: 8), child: _markAsEmailUnreadAction(context, email)),
PopupMenuItem(padding: const EdgeInsets.symmetric(horizontal: 8), child: _markAsEmailSpamOrUnSpamAction(context, email)),
];
}
}
@@ -3,8 +3,8 @@ import 'package:core/core.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:model/model.dart';
import 'package:tmail_ui_user/features/setting/presentation/model/app_setting.dart';
import 'package:tmail_ui_user/features/setting/presentation/model/reading_pane.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/model/app_setting.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/model/reading_pane.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
typedef OnBackActionClick = void Function();
@@ -44,12 +44,12 @@ class AppBarMailWidgetBuilder {
Widget build() {
return Container(
key: Key('app_bar_messenger_widget'),
key: const Key('app_bar_messenger_widget'),
alignment: Alignment.center,
padding: EdgeInsets.zero,
margin: EdgeInsets.zero,
child: MediaQuery(
data: MediaQueryData(padding: EdgeInsets.zero),
data: const MediaQueryData(padding: EdgeInsets.zero),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
@@ -83,10 +83,10 @@ class AppBarMailWidgetBuilder {
return Transform(
transform: Matrix4.translationValues(-10.0, 0.0, 0.0),
child: Text(
'${_currentMailbox?.name?.name.capitalizeFirstEach ?? ''}',
_currentMailbox?.name?.name.capitalizeFirstEach ?? '',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 17, color: AppColor.colorTextButton))
style: const TextStyle(fontSize: 17, color: AppColor.colorTextButton))
);
}
@@ -115,7 +115,7 @@ class AppBarMailWidgetBuilder {
}
}),
Padding(
padding: EdgeInsets.only(left: 10, right: 16),
padding: const EdgeInsets.only(left: 10, right: 16),
child: buildIconWebHasPosition(
_context,
tooltip: AppLocalizations.of(_context).more,
@@ -106,64 +106,24 @@ class MailboxController extends BaseMailboxController {
@override
void onInit() {
mailboxDashBoardController.accountId.listen((accountId) {
if (accountId != null) {
getAllMailboxAction(accountId);
}
});
_onMailboxDashBoardListener();
super.onInit();
}
@override
void onReady() {
super.onReady();
mailboxDashBoardController.viewState.listen((state) {
state.fold(
(failure) {
if (failure is EmptyTrashFolderFailure) {
mailboxDashBoardController.clearState();
refreshMailboxChanges();
}
},
(success) {
if (success is MarkAsMultipleEmailReadAllSuccess
|| success is MarkAsMultipleEmailReadHasSomeEmailFailure) {
mailboxDashBoardController.clearState();
refreshMailboxChanges();
} else if (success is MoveMultipleEmailToMailboxAllSuccess
|| success is MoveMultipleEmailToMailboxHasSomeEmailFailure) {
mailboxDashBoardController.clearState();
refreshMailboxChanges();
} else if (success is DeleteMultipleEmailsPermanentlyAllSuccess
|| success is DeleteMultipleEmailsPermanentlyHasSomeEmailFailure) {
mailboxDashBoardController.clearState();
refreshMailboxChanges();
} else if (success is EmptyTrashFolderSuccess) {
mailboxDashBoardController.clearState();
refreshMailboxChanges();
} else if (success is MarkAsEmailReadSuccess
|| success is MoveToMailboxSuccess
|| success is DeleteEmailPermanentlySuccess
|| success is SaveEmailAsDraftsSuccess
|| success is RemoveEmailDraftsSuccess
|| success is SendEmailSuccess
|| success is UpdateEmailDraftsSuccess) {
refreshMailboxChanges();
}
}
);
});
_openMailboxEventController.stream.throttleTime(const Duration(milliseconds: 800)).listen((event) {
_openMailboxEventController.stream.throttleTime(Duration(milliseconds: 800)).listen((event) {
_handleOpenMailbox(event.buildContext, event.presentationMailbox);
});
_initCollapseMailboxCategories();
super.onReady();
}
@override
void onClose() {
mailboxDashBoardController.accountId.close();
mailboxDashBoardController.viewState.close();
searchInputController.dispose();
searchFocus.dispose();
super.onClose();
@@ -216,6 +176,50 @@ class MailboxController extends BaseMailboxController {
@override
void onError(error) {}
void _onMailboxDashBoardListener() {
mailboxDashBoardController.accountId.listen((accountId) {
log('MailboxController::_onMailboxDashBoardListener(): accountId: $accountId');
if (accountId != null) {
getAllMailboxAction(accountId);
}
});
mailboxDashBoardController.viewState.listen((state) {
log('MailboxController::_onMailboxDashBoardListener(): viewState: $viewState');
state.fold((failure) {
if (failure is EmptyTrashFolderFailure) {
mailboxDashBoardController.clearState();
refreshMailboxChanges();
}
}, (success) {
if (success is MarkAsMultipleEmailReadAllSuccess ||
success is MarkAsMultipleEmailReadHasSomeEmailFailure) {
mailboxDashBoardController.clearState();
refreshMailboxChanges();
} else if (success is MoveMultipleEmailToMailboxAllSuccess ||
success is MoveMultipleEmailToMailboxHasSomeEmailFailure) {
mailboxDashBoardController.clearState();
refreshMailboxChanges();
} else if (success is DeleteMultipleEmailsPermanentlyAllSuccess ||
success is DeleteMultipleEmailsPermanentlyHasSomeEmailFailure) {
mailboxDashBoardController.clearState();
refreshMailboxChanges();
} else if (success is EmptyTrashFolderSuccess) {
mailboxDashBoardController.clearState();
refreshMailboxChanges();
} else if (success is MarkAsEmailReadSuccess ||
success is MoveToMailboxSuccess ||
success is DeleteEmailPermanentlySuccess ||
success is SaveEmailAsDraftsSuccess ||
success is RemoveEmailDraftsSuccess ||
success is SendEmailSuccess ||
success is UpdateEmailDraftsSuccess) {
refreshMailboxChanges();
}
});
});
}
void _initCollapseMailboxCategories() {
if (currentContext != null) {
if (kIsWeb && (_responsiveUtils.isMobile(currentContext!)) || _responsiveUtils.isTablet(currentContext!)) {
@@ -25,11 +25,12 @@ class MailboxView extends GetWidget<MailboxController> {
@override
Widget build(BuildContext context) {
return Drawer(
elevation: _responsiveUtils.isDesktop(context) ? 0 : 16.0,
child: Scaffold(
backgroundColor: Colors.white,
body: Column(
children: [
_buildLogoApp(context),
if (!_responsiveUtils.isDesktop(context)) _buildLogoApp(context),
if (!_responsiveUtils.isDesktop(context)) const Divider(color: AppColor.colorDividerMailbox, height: 0.5, thickness: 0.2),
Expanded(child: Container(
padding: EdgeInsets.zero,
@@ -75,7 +76,7 @@ class MailboxView extends GetWidget<MailboxController> {
bottom: _responsiveUtils.isDesktop(context) ? 25 : 16,
left: _responsiveUtils.isDesktop(context) ? 32 : 16),
child: Row(children: [
(SloganBuilder(arrangedByHorizontal: true)
(SloganBuilder(arrangedByHorizontal: true)
..setSloganText(AppLocalizations.of(context).app_name)
..setSloganTextAlign(TextAlign.center)
..setSloganTextStyle(const TextStyle(color: Colors.black, fontSize: 20, fontWeight: FontWeight.bold))
@@ -65,7 +65,7 @@ class UserInformationWidgetBuilder {
child: TextButton(
onPressed: () => _onLogoutActionClick?.call(),
child: Text(
AppLocalizations.of(_context).logout,
AppLocalizations.of(_context).manage_account,
style: const TextStyle(fontSize: 14, color: AppColor.colorTextButton),
),
)
@@ -0,0 +1,97 @@
import 'package:flutter/cupertino.dart';
import 'package:model/email/email_action_type.dart';
import 'package:model/email/filter_message_option.dart';
import 'package:model/email/presentation_email.dart';
import 'package:tmail_ui_user/features/base/action/ui_action.dart';
import 'package:tmail_ui_user/main/routes/router_arguments.dart';
class DashBoardAction extends UIAction {
static final idle = DashBoardAction();
DashBoardAction() : super();
@override
List<Object?> get props => [];
}
class ComposeEmailAction extends DashBoardAction {
RouterArguments? arguments;
ComposeEmailAction({this.arguments});
@override
List<Object?> get props => [arguments];
}
class CloseComposeEmailAction extends DashBoardAction {
CloseComposeEmailAction();
@override
List<Object?> get props => [];
}
class RefreshAllEmailAction extends DashBoardAction {
RefreshAllEmailAction();
@override
List<Object?> get props => [];
}
class SelectionAllEmailAction extends DashBoardAction {
SelectionAllEmailAction();
@override
List<Object?> get props => [];
}
class CancelSelectionAllEmailAction extends DashBoardAction {
CancelSelectionAllEmailAction();
@override
List<Object?> get props => [];
}
class MarkAsReadAllEmailAction extends DashBoardAction {
MarkAsReadAllEmailAction();
@override
List<Object?> get props => [];
}
class FilterMessageAction extends DashBoardAction {
BuildContext context;
FilterMessageOption option;
FilterMessageAction(this.context, this.option);
@override
List<Object?> get props => [option];
}
class HandleEmailActionTypeAction extends DashBoardAction {
BuildContext context;
EmailActionType emailAction;
List<PresentationEmail> listEmailSelected;
HandleEmailActionTypeAction(this.context, this.listEmailSelected, this.emailAction);
@override
List<Object> get props => [listEmailSelected, emailAction];
}
class GoToSettingsAction extends DashBoardAction {
GoToSettingsAction();
@override
List<Object?> get props => [];
}
@@ -0,0 +1,34 @@
import 'package:core/core.dart';
import 'package:flutter/cupertino.dart';
import 'package:model/model.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
extension FilterMessageOptionExtension on FilterMessageOption {
String getName(BuildContext context) {
switch(this) {
case FilterMessageOption.all:
return '';
case FilterMessageOption.unread:
return AppLocalizations.of(context).unread;
case FilterMessageOption.attachments:
return AppLocalizations.of(context).with_attachments;
case FilterMessageOption.starred:
return AppLocalizations.of(context).starred;
}
}
String getIcon(ImagePaths imagePaths) {
switch(this) {
case FilterMessageOption.all:
return '';
case FilterMessageOption.unread:
return imagePaths.icUnread;
case FilterMessageOption.attachments:
return imagePaths.icAttachment;
case FilterMessageOption.starred:
return imagePaths.icStar;
}
}
}
@@ -15,6 +15,7 @@ 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:package_info_plus/package_info_plus.dart';
import 'package:tmail_ui_user/features/base/action/ui_action.dart';
import 'package:tmail_ui_user/features/base/reloadable/reloadable_controller.dart';
import 'package:tmail_ui_user/features/caching/caching_manager.dart';
import 'package:tmail_ui_user/features/composer/domain/state/save_email_as_drafts_state.dart';
@@ -35,7 +36,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/get_user_p
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/remove_email_drafts_state.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/get_user_profile_interactor.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/remove_email_drafts_interactor.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/dashboard_action.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/dashboard_action.dart';
import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart';
import 'package:tmail_ui_user/features/thread/domain/state/search_email_state.dart';
import 'package:tmail_ui_user/features/thread/presentation/model/search_state.dart';
@@ -68,9 +69,12 @@ class MailboxDashBoardController extends ReloadableController {
final userProfile = Rxn<UserProfile>();
final searchState = SearchState.initial().obs;
final suggestionSearch = <String>[].obs;
final dashBoardAction = DashBoardAction.none.obs;
final dashBoardAction = Rxn<UIAction>();
final routePath = AppRoutes.MAILBOX_DASHBOARD.obs;
final appInformation = Rxn<PackageInfo>();
final currentSelectMode = SelectMode.INACTIVE.obs;
final filterMessageOption = FilterMessageOption.all.obs;
final listEmailSelected = <PresentationEmail>[].obs;
SearchQuery? searchQuery;
Session? sessionCurrent;
@@ -96,12 +100,12 @@ class MailboxDashBoardController extends ReloadableController {
@override
void onReady() {
super.onReady();
log('MailboxDashBoardController::onReady()');
dispatchRoute(AppRoutes.THREAD);
_setSessionCurrent();
_getUserProfile();
_initPackageInfo();
super.onReady();
}
@override
@@ -257,9 +261,9 @@ class MailboxDashBoardController extends ReloadableController {
bool get isDrawerOpen => scaffoldKey.currentState?.isDrawerOpen == true;
bool isSearchActive() {
return searchState.value.searchStatus == SearchStatus.ACTIVE;
}
bool isSearchActive() => searchState.value.searchStatus == SearchStatus.ACTIVE;
bool isSelectionEnabled() => currentSelectMode.value == SelectMode.ACTIVE;
void enableSearch() {
searchState.value = searchState.value.enableSearchState();
@@ -361,16 +365,14 @@ class MailboxDashBoardController extends ReloadableController {
}
}
void dispatchDashBoardAction(DashBoardAction action, {RouterArguments? arguments}) {
switch(action) {
case DashBoardAction.none:
routerArguments = null;
Get.delete<ComposerController>();
break;
case DashBoardAction.compose:
routerArguments = arguments;
ComposerBindings().dependencies();
break;
void dispatchAction(UIAction action) {
log('MailboxDashBoardController::dispatchAction(): ${action.runtimeType}');
if (action is ComposeEmailAction) {
routerArguments = action.arguments;
ComposerBindings().dependencies();
} else if (action is CloseComposeEmailAction) {
routerArguments = null;
Get.delete<ComposerController>();
}
dashBoardAction.value = action;
}
@@ -402,8 +404,8 @@ class MailboxDashBoardController extends ReloadableController {
void composeEmailAction() {
if (kIsWeb) {
if (dashBoardAction.value != DashBoardAction.compose) {
dispatchDashBoardAction(DashBoardAction.compose, arguments: ComposerArguments());
if (dashBoardAction.value is! ComposeEmailAction) {
dispatchAction(ComposeEmailAction(arguments: ComposerArguments()));
}
} else {
push(AppRoutes.COMPOSER, arguments: ComposerArguments());
@@ -412,8 +414,8 @@ class MailboxDashBoardController extends ReloadableController {
void goToComposer(ComposerArguments arguments) {
if (kIsWeb) {
if (dashBoardAction.value != DashBoardAction.compose) {
dispatchDashBoardAction(DashBoardAction.compose, arguments: arguments);
if (dashBoardAction.value is! ComposeEmailAction) {
dispatchAction(ComposeEmailAction(arguments: arguments));
}
} else {
push(AppRoutes.COMPOSER, arguments: arguments);
@@ -434,6 +436,14 @@ class MailboxDashBoardController extends ReloadableController {
goToLogin();
}
void clearDashBoardAction() {
dashBoardAction.value = DashBoardAction.idle;
}
void goToSettings() {
dispatchAction(GoToSettingsAction());
}
@override
void onClose() {
_emailReceiveManager.closeEmailReceiveManagerStream();
@@ -5,10 +5,10 @@ import 'package:tmail_ui_user/features/base/mixin/network_connection_mixin.dart'
import 'package:tmail_ui_user/features/composer/presentation/composer_view.dart';
import 'package:tmail_ui_user/features/email/presentation/email_view.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/mailbox_view.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/dashboard_action.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/dashboard_action.dart';
import 'package:tmail_ui_user/features/setting/presentation/model/app_setting.dart';
import 'package:tmail_ui_user/features/setting/presentation/model/reading_pane.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/model/app_setting.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/model/reading_pane.dart';
import 'package:tmail_ui_user/features/thread/presentation/thread_view.dart';
import 'package:tmail_ui_user/main/routes/app_routes.dart';
@@ -28,19 +28,16 @@ class MailboxDashBoardView extends GetWidget<MailboxDashBoardController> with Ne
? SizedBox(child: MailboxView(), width: double.infinity)
: SizedBox(child: MailboxView(), width: _responsiveUtils.defaultSizeDrawerWidthMobileTablet),
tablet: SizedBox(child: MailboxView(), width: _responsiveUtils.defaultSizeDrawerWidthMobileTablet),
tabletLarge: const SizedBox.shrink(),
desktop: const SizedBox.shrink()
tabletLarge: SizedBox.shrink(),
),
drawerEnableOpenDragGesture: _responsiveUtils.isMobile(context) || _responsiveUtils.isTablet(context),
body: Stack(children: [
ResponsiveWidget(
responsiveUtils: _responsiveUtils,
desktop: _buildLargeScreenView(context),
tabletLarge: _buildLargeScreenView(context),
tablet: ThreadView(),
mobile: ThreadView()
),
Obx(() => controller.dashBoardAction.value == DashBoardAction.compose
Obx(() => controller.dashBoardAction.value is ComposeEmailAction
? ComposerView()
: const SizedBox.shrink()),
Obx(() => controller.isNetworkConnectionAvailable()
@@ -50,7 +47,7 @@ class MailboxDashBoardView extends GetWidget<MailboxDashBoardController> with Ne
);
}
Widget _buildThreadAndEmailContainer(BuildContext context) {
Widget _wrapContainerForThreadAndEmail(BuildContext context) {
switch(AppSetting.readingPane) {
case ReadingPane.noSplit:
return Obx(() {
@@ -97,7 +94,7 @@ class MailboxDashBoardView extends GetWidget<MailboxDashBoardController> with Ne
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(child: MailboxView(), width: _responsiveUtils.defaultSizeDrawerWidthMobileTablet),
Expanded(child: _buildThreadAndEmailContainer(context))
Expanded(child: _wrapContainerForThreadAndEmail(context))
],
);
}
@@ -1,44 +1,100 @@
import 'package:core/core.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:model/model.dart';
import 'package:tmail_ui_user/features/base/mixin/network_connection_mixin.dart';
import 'package:tmail_ui_user/features/composer/presentation/composer_view_web.dart';
import 'package:tmail_ui_user/features/email/presentation/email_view.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/mailbox_view_web.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/dashboard_action.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/dashboard_action.dart';
import 'package:tmail_ui_user/features/setting/presentation/model/app_setting.dart';
import 'package:tmail_ui_user/features/setting/presentation/model/reading_pane.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mixin/filter_email_popup_menu_mixin.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mixin/user_setting_popup_menu_mixin.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/model/app_setting.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/model/reading_pane.dart';
import 'package:tmail_ui_user/features/thread/presentation/extensions/filter_message_option_extension.dart';
import 'package:tmail_ui_user/features/thread/presentation/thread_view.dart';
import 'package:tmail_ui_user/features/thread/presentation/widgets/search_app_bar_widget.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
import 'package:tmail_ui_user/main/routes/app_routes.dart';
class MailboxDashBoardView extends GetWidget<MailboxDashBoardController> with NetworkConnectionMixin {
class MailboxDashBoardView extends GetWidget<MailboxDashBoardController> with NetworkConnectionMixin,
UserSettingPopupMenuMixin, FilterEmailPopupMenuMixin {
final _responsiveUtils = Get.find<ResponsiveUtils>();
final _imagePaths = Get.find<ImagePaths>();
MailboxDashBoardView({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
if (controller.isDrawerOpen && (_responsiveUtils.isDesktop(context) || _responsiveUtils.isTabletLarge(context))) {
WidgetsBinding.instance?.addPostFrameCallback((_) {
controller.closeDrawer();
});
}
return Scaffold(
key: controller.scaffoldKey,
backgroundColor: Colors.white,
drawer: ResponsiveWidget(
responsiveUtils: _responsiveUtils,
mobile: SizedBox(child: MailboxView(), width: _responsiveUtils.defaultSizeDrawerWidthWeb),
tablet: SizedBox(child: MailboxView(), width: _responsiveUtils.defaultSizeDrawerWidthWeb),
tabletLarge: const SizedBox.shrink(),
desktop: const SizedBox.shrink()
tabletLarge: const SizedBox.shrink()
),
drawerEnableOpenDragGesture: _responsiveUtils.isMobile(context) || _responsiveUtils.isTablet(context),
body: Stack(children: [
ResponsiveWidget(
responsiveUtils: _responsiveUtils,
desktop: _buildLargeScreenView(context),
tabletLarge: _buildLargeScreenView(context),
tablet: ThreadView(),
desktop: Column(children: [
Row(children: [
Container(width: 256, color: Colors.white,
padding: const EdgeInsets.only(top: 25, bottom: 25, left: 32),
child: Row(children: [
(SloganBuilder(arrangedByHorizontal: true)
..setSloganText(AppLocalizations.of(context).app_name)
..setSloganTextAlign(TextAlign.center)
..setSloganTextStyle(const TextStyle(color: Colors.black, fontSize: 20, fontWeight: FontWeight.bold))
..setSizeLogo(24)
..setLogo(_imagePaths.icLogoTMail))
.build(),
Obx(() {
if (controller.appInformation.value != null) {
return Padding(padding: const EdgeInsets.only(top: 6),
child: Text(
'v.${controller.appInformation.value!.version}',
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 13, color: AppColor.colorContentEmail, fontWeight: FontWeight.w500),
));
} else {
return const SizedBox.shrink();
}
}),
])
),
Expanded(child: Padding(
padding: const EdgeInsets.only(right: 10, top: 16, bottom: 10, left: 48),
child: _buildRightHeader(context)))
]),
Expanded(child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(child: MailboxView(), width: _responsiveUtils.defaultSizeMenuWidthWeb),
Expanded(child: _wrapContainerForThreadAndEmail(context))
],
))
]),
tabletLarge: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(child: MailboxView(), width: _responsiveUtils.defaultSizeMenuWidthWeb),
Expanded(child: _wrapContainerForThreadAndEmail(context))
],
),
mobile: ThreadView()
),
Obx(() => controller.dashBoardAction.value == DashBoardAction.compose
Obx(() => controller.dashBoardAction.value is ComposeEmailAction
? ComposerView()
: const SizedBox.shrink()),
Obx(() => controller.isNetworkConnectionAvailable()
@@ -48,7 +104,7 @@ class MailboxDashBoardView extends GetWidget<MailboxDashBoardController> with Ne
);
}
Widget _buildThreadAndEmailContainer(BuildContext context) {
Widget _wrapContainerForThreadAndEmail(BuildContext context) {
switch(AppSetting.readingPane) {
case ReadingPane.noSplit:
return Obx(() {
@@ -84,19 +140,267 @@ class MailboxDashBoardView extends GetWidget<MailboxDashBoardController> with Ne
}
}
Widget _buildLargeScreenView(BuildContext context) {
if (controller.isDrawerOpen && (_responsiveUtils.isDesktop(context) || _responsiveUtils.isTabletLarge(context))) {
WidgetsBinding.instance?.addPostFrameCallback((_) {
controller.closeDrawer();
});
}
Widget _buildRightHeader(BuildContext context) {
return Row(children: [
Obx(() {
if (controller.isSelectionEnabled()) {
return _buildListButtonTopBarSelection(context);
} else {
return controller.isSearchActive()
? _buildListButtonTopBarSearchActive(context)
: _buildListButtonTopBar(context);
}
}),
const SizedBox(width: 16),
Obx(() => !controller.isSearchActive() ? const Spacer() : const SizedBox.shrink()),
Obx(() => controller.isSearchActive() ? Expanded(child: _buildSearchForm(context)) : const SizedBox.shrink()),
Obx(() => !controller.isSearchActive()
? (SearchBarView(_imagePaths)
..hintTextSearch(AppLocalizations.of(context).search_emails)
..maxSizeWidth(240)
..addOnOpenSearchViewAction(() => controller.enableSearch()))
.build()
: const SizedBox.shrink()),
Obx(() => !controller.isSearchActive() ? const SizedBox(width: 16) : const SizedBox.shrink()),
(AvatarBuilder()
..text(controller.userProfile.value?.getAvatarText() ?? '')
..backgroundColor(Colors.white)
..textColor(Colors.black)
..context(context)
..addOnTapAvatarActionWithPositionClick((position) =>
controller.openPopupMenuAction(context, position, popupMenuUserSettingActionTile(context,
controller.userProfile.value,
onLogoutAction: () => controller.logoutAction(),
onSettingAction: () => controller.goToSettings())))
..addBoxShadows([const BoxShadow(
color: AppColor.colorShadowBgContentEmail,
spreadRadius: 1, blurRadius: 1, offset: Offset(0, 0.5))])
..size(48))
.build(),
const SizedBox(width: 16)
]);
}
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(child: MailboxView(), width: _responsiveUtils.defaultSizeMenuWidthWeb),
Expanded(child: _buildThreadAndEmailContainer(context))
],
);
Widget _buildListButtonTopBar(BuildContext context) {
return Row(children: [
(ButtonBuilder(_imagePaths.icRefresh)
..key(const Key('button_reload_thread'))
..decoration(BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: AppColor.colorButtonHeaderThread))
..paddingIcon(EdgeInsets.zero)
..size(16)
..radiusSplash(10)
..padding(const EdgeInsets.symmetric(horizontal: 8, vertical: 8))
..onPressActionClick(() => controller.dispatchAction(RefreshAllEmailAction())))
.build(),
const SizedBox(width: 16),
(ButtonBuilder(_imagePaths.icSelectAll)
..key(const Key('button_select_all'))
..decoration(BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: AppColor.colorButtonHeaderThread))
..paddingIcon(const EdgeInsets.only(right: 8))
..size(16)
..radiusSplash(10)
..padding(const EdgeInsets.symmetric(horizontal: 12, vertical: 8))
..textStyle(const TextStyle(fontSize: 12, color: AppColor.colorTextButtonHeaderThread))
..onPressActionClick(() => controller.dispatchAction(SelectionAllEmailAction()))
..text(AppLocalizations.of(context).select_all, isVertical: false))
.build(),
const SizedBox(width: 16),
(ButtonBuilder(_imagePaths.icMarkAllAsRead)
..key(const Key('button_mark_all_as_read'))
..decoration(BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: AppColor.colorButtonHeaderThread))
..paddingIcon(const EdgeInsets.only(right: 8))
..size(16)
..padding(const EdgeInsets.symmetric(horizontal: 12, vertical: 8))
..radiusSplash(10)
..textStyle(const TextStyle(fontSize: 12, color: AppColor.colorTextButtonHeaderThread))
..onPressActionClick(() => controller.dispatchAction(MarkAsReadAllEmailAction()))
..text(AppLocalizations.of(context).mark_all_as_read, isVertical: false))
.build(),
const SizedBox(width: 16),
Obx(() => (ButtonBuilder(_imagePaths.icFilterWeb)
..key(const Key('button_filter_messages'))
..context(context)
..decoration(BoxDecoration(borderRadius: BorderRadius.circular(10), color: AppColor.colorButtonHeaderThread))
..paddingIcon(const EdgeInsets.only(right: 8))
..size(16)
..padding(const EdgeInsets.symmetric(horizontal: 12, vertical: 8))
..radiusSplash(10)
..textStyle(TextStyle(
fontSize: 12,
color: controller.filterMessageOption.value == FilterMessageOption.all
? AppColor.colorTextButtonHeaderThread
: AppColor.colorNameEmail,
fontWeight: controller.filterMessageOption.value == FilterMessageOption.all
? FontWeight.normal
: FontWeight.w500))
..addIconAction(Padding(
padding: const EdgeInsets.only(left: 8),
child: SvgPicture.asset(_imagePaths.icArrowDown, fit: BoxFit.fill)))
..addOnPressActionWithPositionClick((position) => controller.openPopupMenuAction(context, position,
popupMenuFilterEmailActionTile(context,
controller.filterMessageOption.value,
(option) => controller.dispatchAction(FilterMessageAction(context, option)))))
..text(controller.filterMessageOption.value == FilterMessageOption.all
? AppLocalizations.of(context).filter_messages
: controller.filterMessageOption.value.getTitle(context), isVertical: false))
.build()),
]);
}
Widget _buildListButtonTopBarSearchActive(BuildContext context) {
return Row(children: [
(ButtonBuilder(_imagePaths.icSelectAll)
..key(const Key('button_select_all'))
..decoration(BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: AppColor.colorButtonHeaderThread))
..paddingIcon(const EdgeInsets.only(right: 8))
..size(16)
..radiusSplash(10)
..padding(const EdgeInsets.symmetric(horizontal: 12, vertical: 8))
..textStyle(const TextStyle(fontSize: 12, color: AppColor.colorTextButtonHeaderThread))
..onPressActionClick(() => controller.dispatchAction(SelectionAllEmailAction()))
..text(AppLocalizations.of(context).select_all, isVertical: false))
.build(),
const SizedBox(width: 16),
(ButtonBuilder(_imagePaths.icMarkAllAsRead)
..key(const Key('button_mark_all_as_read'))
..decoration(BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: AppColor.colorButtonHeaderThread))
..paddingIcon(const EdgeInsets.only(right: 8))
..size(16)
..padding(const EdgeInsets.symmetric(horizontal: 12, vertical: 8))
..radiusSplash(10)
..textStyle(const TextStyle(fontSize: 12, color: AppColor.colorTextButtonHeaderThread))
..onPressActionClick(() => controller.dispatchAction(MarkAsReadAllEmailAction()))
..text(AppLocalizations.of(context).mark_all_as_read, isVertical: false))
.build(),
]);
}
Widget _buildListButtonTopBarSelection(BuildContext context) {
return Row(children: [
buildIconWeb(
icon: SvgPicture.asset(_imagePaths.icCloseComposer, color: AppColor.colorTextButton, fit: BoxFit.fill),
tooltip: AppLocalizations.of(context).cancel,
onTap: () => controller.dispatchAction(CancelSelectionAllEmailAction())),
Obx(() => Text(
AppLocalizations.of(context).count_email_selected(controller.listEmailSelected.length),
style: const TextStyle(fontSize: 17, fontWeight: FontWeight.w500, color: AppColor.colorTextButton))),
const SizedBox(width: 30),
Obx(() => buildIconWeb(
icon: SvgPicture.asset(
controller.listEmailSelected.isAllEmailRead
? _imagePaths.icRead
: _imagePaths.icUnread,
fit: BoxFit.fill),
tooltip: controller.listEmailSelected.isAllEmailRead
? AppLocalizations.of(context).mark_as_unread
: AppLocalizations.of(context).mark_as_read,
onTap: () => controller.dispatchAction(
HandleEmailActionTypeAction(context,
controller.listEmailSelected,
controller.listEmailSelected.isAllEmailRead
? EmailActionType.markAsUnread
: EmailActionType.markAsRead)))),
Obx(() => buildIconWeb(
icon: SvgPicture.asset(
controller.listEmailSelected.isAllEmailStarred
? _imagePaths.icUnStar
: _imagePaths.icStar,
fit: BoxFit.fill),
tooltip: controller.listEmailSelected.isAllEmailStarred
? AppLocalizations.of(context).un_star
: AppLocalizations.of(context).star,
onTap: () => controller.dispatchAction(
HandleEmailActionTypeAction(context,
controller.listEmailSelected,
controller.listEmailSelected.isAllEmailStarred
? EmailActionType.markAsStarred
: EmailActionType.unMarkAsStarred)))),
Obx(() {
if (controller.selectedMailbox.value?.isDrafts == false) {
return buildIconWeb(
icon: SvgPicture.asset(_imagePaths.icMove, fit: BoxFit.fill),
tooltip: AppLocalizations.of(context).move,
onTap: () => controller.dispatchAction(
HandleEmailActionTypeAction(context,
controller.listEmailSelected,
EmailActionType.moveToMailbox)));
} else {
return const SizedBox.shrink();
}
}),
Obx(() {
if (controller.selectedMailbox.value?.isDrafts == false) {
return buildIconWeb(
icon: SvgPicture.asset(
controller.selectedMailbox.value?.isSpam == true
? _imagePaths.icNotSpam
: _imagePaths.icSpam,
fit: BoxFit.fill),
tooltip: controller.selectedMailbox.value?.isSpam == true
? AppLocalizations.of(context).un_spam
: AppLocalizations.of(context).mark_as_spam,
onTap: () {
if (controller.selectedMailbox.value?.isSpam == true) {
return controller.dispatchAction(
HandleEmailActionTypeAction(context,
controller.listEmailSelected,
EmailActionType.unSpam));
} else {
return controller.dispatchAction(
HandleEmailActionTypeAction(context,
controller.listEmailSelected,
EmailActionType.moveToSpam));
}
});
} else {
return const SizedBox.shrink();
}
}),
Obx(() => buildIconWeb(
icon: SvgPicture.asset(_imagePaths.icDelete, fit: BoxFit.fill),
tooltip: controller.selectedMailbox.value?.isTrash == true
? AppLocalizations.of(context).delete_permanently
: AppLocalizations.of(context).move_to_trash,
onTap: () {
if (controller.selectedMailbox.value?.isTrash == true) {
return controller.dispatchAction(HandleEmailActionTypeAction(context,
controller.listEmailSelected,
EmailActionType.deletePermanently));
} else {
return controller.dispatchAction(HandleEmailActionTypeAction(context,
controller.listEmailSelected,
EmailActionType.moveToTrash));
}
})),
]);
}
Widget _buildSearchForm(BuildContext context) {
return (SearchAppBarWidget(context, _imagePaths, _responsiveUtils,
controller.searchQuery,
controller.searchFocus,
controller.searchInputController,
suggestionSearch: controller.suggestionSearch)
..addDecoration(const BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(10)),
color: AppColor.colorBgSearchBar))
..setMargin(const EdgeInsets.only(right: 10))
..setHeightSearchBar(45)
..setHintText(AppLocalizations.of(context).search_mail)
..addOnCancelSearchPressed(() => controller.disableSearch())
..addOnClearTextSearchAction(() => controller.clearSearchText())
..addOnTextChangeSearchAction((query) => controller.addSuggestionSearch(query))
..addOnSearchTextAction((query) => controller.searchEmail(context, query)))
.build();
}
}
@@ -0,0 +1,55 @@
import 'package:core/core.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:model/model.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/filter_message_option_extension.dart';
mixin FilterEmailPopupMenuMixin {
final _imagePaths = Get.find<ImagePaths>();
List<PopupMenuEntry> popupMenuFilterEmailActionTile(BuildContext context,
FilterMessageOption optionSelected, Function(FilterMessageOption)? onCallBack) {
return [
PopupMenuItem(
padding: EdgeInsets.zero,
child: _filterEmailAction(context, optionSelected, FilterMessageOption.attachments, onCallBack)),
PopupMenuDivider(height: 0.5),
PopupMenuItem(
padding: EdgeInsets.zero,
child: _filterEmailAction(context, optionSelected, FilterMessageOption.unread, onCallBack)),
PopupMenuDivider(height: 0.5),
PopupMenuItem(
padding: EdgeInsets.zero,
child: _filterEmailAction(context, optionSelected, FilterMessageOption.starred, onCallBack)),
];
}
Widget _filterEmailAction(BuildContext context, FilterMessageOption optionSelected,
FilterMessageOption option, Function(FilterMessageOption)? onCallBack) {
return InkWell(
onTap: () => onCallBack?.call(option == optionSelected ? FilterMessageOption.all : option),
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 16),
child: SizedBox(
child: Row(children: [
SvgPicture.asset(option.getIcon(_imagePaths),
width: 20,
height: 20,
fit: BoxFit.fill,
color: option != FilterMessageOption.starred ? AppColor.colorTextButton : null),
SizedBox(width: 12),
Expanded(child: Text(
option.getName(context),
style: TextStyle(fontSize: 15, color: Colors.black, fontWeight: FontWeight.w500))),
if (optionSelected == option)
SizedBox(width: 12),
if (optionSelected == option)
SvgPicture.asset(_imagePaths.icFilterSelected, width: 16, height: 16, fit: BoxFit.fill),
])
),
)
);
}
}
@@ -0,0 +1,57 @@
import 'package:core/core.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:model/model.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
mixin UserSettingPopupMenuMixin {
final _imagePaths = Get.find<ImagePaths>();
List<PopupMenuEntry> popupMenuUserSettingActionTile(BuildContext context, UserProfile? userProfile,
{Function? onLogoutAction, Function? onSettingAction}) {
return [
PopupMenuItem(
enabled: false,
padding: EdgeInsets.zero,
child: _userInformation(context, userProfile)),
PopupMenuDivider(height: 0.5),
PopupMenuItem(
padding: EdgeInsets.zero,
child: _settingAction(context, onSettingAction)),
PopupMenuDivider(height: 0.5),
PopupMenuItem(
padding: EdgeInsets.zero,
child: _logoutAction(context, onLogoutAction)),
];
}
Widget _userInformation(BuildContext context, UserProfile? userProfile) {
if (userProfile != null) {
return SizedBox(
width: 300,
child: ListTile(
contentPadding: EdgeInsets.symmetric(horizontal: 20),
title: Text(userProfile.email, maxLines: 1, style: TextStyle(
fontSize: 15,
color: AppColor.colorHintSearchBar,
fontWeight: FontWeight.normal))),
);
}
return SizedBox.shrink();
}
Widget _settingAction(BuildContext context, Function? onCallBack) {
return PopupMenuItemWidget(
_imagePaths.icSetting,
AppLocalizations.of(context).settings,
() => onCallBack?.call());
}
Widget _logoutAction(BuildContext context, Function? onCallBack) {
return PopupMenuItemWidget(
_imagePaths.icLogout,
AppLocalizations.of(context).sign_out,
() => onCallBack?.call());
}
}
@@ -1,5 +0,0 @@
enum DashBoardAction {
none,
compose
}
@@ -1,37 +0,0 @@
import 'package:core/core.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:tmail_ui_user/features/thread/presentation/widgets/app_action_sheet_action_builder.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
mixin UserSettingPopupMenuMixin {
final _imagePaths = Get.find<ImagePaths>();
void openUserSettingAction(BuildContext context, RelativeRect? position, List<PopupMenuEntry> popupMenuItems) async {
await showMenu(
context: context,
position: position ?? const RelativeRect.fromLTRB(16, 40, 16, 16),
color: Colors.white,
elevation: 5,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)),
items: popupMenuItems);
}
List<PopupMenuEntry> popupMenuUserSettingActionTile(BuildContext context, Function? onCallBack) {
return [
PopupMenuItem(padding: const EdgeInsets.symmetric(horizontal: 8), child: logoutAction(context, onCallBack)),
];
}
Widget logoutAction(BuildContext context, Function? onCallBack) {
return (AppActionSheetActionBuilder(
const Key('logout_action'),
SvgPicture.asset(_imagePaths.icCloseMailbox, color: AppColor.colorTextButton, fit: BoxFit.fill),
AppLocalizations.of(context).logout,
iconLeftPadding:const EdgeInsets.only(right: 12))
..onActionClick((option) => onCallBack?.call()))
.build();
}
}
@@ -0,0 +1,6 @@
import 'package:tmail_ui_user/features/manage_account/presentation/model/reading_pane.dart';
class AppSetting {
static ReadingPane readingPane = ReadingPane.noSplit;
}
@@ -1,6 +0,0 @@
import 'package:tmail_ui_user/features/setting/presentation/model/reading_pane.dart';
class AppSetting {
static ReadingPane readingPane = ReadingPane.noSplit;
}
@@ -36,8 +36,8 @@ import 'package:tmail_ui_user/features/email/domain/usecases/move_to_mailbox_int
import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/remove_email_drafts_state.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/dashboard_action.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/dashboard_action.dart';
import 'package:tmail_ui_user/features/thread/domain/constants/thread_constants.dart';
import 'package:tmail_ui_user/features/thread/domain/model/get_email_request.dart';
import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart';
@@ -91,8 +91,6 @@ class ThreadController extends BaseController {
final emailList = <PresentationEmail>[].obs;
final emailListSearch = <PresentationEmail>[].obs;
final currentSelectMode = SelectMode.INACTIVE.obs;
final filterMessageOption = FilterMessageOption.all.obs;
bool canLoadMore = true;
bool canSearchMore = true;
@@ -131,50 +129,20 @@ class ThreadController extends BaseController {
@override
void onInit() {
_onMailboxDashBoardListener();
super.onInit();
mailboxDashBoardController.selectedMailbox.listen((selectedMailbox) {
if (_currentMailboxId != selectedMailbox?.id) {
log('ThreadController::onReady(): selectMailbox: ${selectedMailbox?.name?.name}(${selectedMailbox?.id})');
_currentMailboxId = selectedMailbox?.id;
_resetToOriginalValue();
_getAllEmail();
}
});
super.onInit();
dispatchState(Right(LoadingState()));
}
@override
void onReady() {
dispatchState(Right(LoadingState()));
super.onReady();
mailboxDashBoardController.viewState.listen((state) {
state.map((success) {
if (success is SearchEmailNewQuery){
mailboxDashBoardController.clearState();
_searchEmail();
} else if (success is MarkAsEmailReadSuccess
|| success is MoveToMailboxSuccess
|| success is MarkAsStarEmailSuccess
|| success is DeleteEmailPermanentlySuccess
|| success is SaveEmailAsDraftsSuccess
|| success is RemoveEmailDraftsSuccess
|| success is SendEmailSuccess
|| success is UpdateEmailDraftsSuccess) {
_refreshEmailChanges();
}
});
});
mailboxDashBoardController.searchState.listen((state) {
if (state.searchStatus == SearchStatus.INACTIVE) {
emailListSearch.clear();
}
});
}
@override
void onClose() {
mailboxDashBoardController.selectedMailbox.close();
mailboxDashBoardController.dashBoardAction.close();
mailboxDashBoardController.viewState.close();
mailboxDashBoardController.searchState.close();
listEmailController.dispose();
@@ -214,6 +182,7 @@ class ThreadController extends BaseController {
void onDone() {
viewState.value.fold(
(failure) {
log('ThreadController::onDone(): failure: $failure');
if (failure is MarkAsMultipleEmailReadAllFailure
|| failure is MarkAsMultipleEmailReadFailure) {
_markAsReadSelectedMultipleEmailFailure(failure);
@@ -229,6 +198,7 @@ class ThreadController extends BaseController {
(success) {
if (success is MarkAsMultipleEmailReadAllSuccess
|| success is MarkAsMultipleEmailReadHasSomeEmailFailure) {
log('ThreadController::onDone(): success: $success');
_markAsReadSelectedMultipleEmailSuccess(success);
} else if (success is MoveMultipleEmailToMailboxAllSuccess
|| success is MoveMultipleEmailToMailboxHasSomeEmailFailure) {
@@ -257,6 +227,69 @@ class ThreadController extends BaseController {
@override
void onError(error) {}
void _onMailboxDashBoardListener() {
mailboxDashBoardController.selectedMailbox.listen((selectedMailbox) {
log('ThreadController::_onMailboxDashBoardListener(): selectMailbox: ${selectedMailbox?.name?.name}(${selectedMailbox?.id})');
if (_currentMailboxId != selectedMailbox?.id) {
_currentMailboxId = selectedMailbox?.id;
_resetToOriginalValue();
_getAllEmail();
}
});
mailboxDashBoardController.searchState.listen((state) {
log('ThreadController::_onMailboxDashBoardListener(): searchState: $state');
if (state.searchStatus == SearchStatus.INACTIVE) {
emailListSearch.clear();
} else if (state.searchStatus == SearchStatus.ACTIVE) {
cancelSelectEmail();
}
});
mailboxDashBoardController.dashBoardAction.listen((action) {
log('ThreadController::_onMailboxDashBoardListener(): dashBoardAction: ${action.runtimeType}');
if (action is RefreshAllEmailAction) {
refreshAllEmail();
mailboxDashBoardController.clearDashBoardAction();
} else if (action is MarkAsReadAllEmailAction) {
markAsReadAllEmails();
mailboxDashBoardController.clearDashBoardAction();
} if (action is SelectionAllEmailAction) {
setSelectAllEmailAction();
mailboxDashBoardController.clearDashBoardAction();
} if (action is CancelSelectionAllEmailAction) {
cancelSelectEmail();
mailboxDashBoardController.clearDashBoardAction();
} if (action is FilterMessageAction) {
filterMessagesAction(action.context, action.option);
mailboxDashBoardController.clearDashBoardAction();
} if (action is HandleEmailActionTypeAction) {
log('ThreadController::_onMailboxDashBoardListener(): ${action.listEmailSelected.length}');
pressEmailSelectionAction(action.context, action.emailAction, action.listEmailSelected);
mailboxDashBoardController.clearDashBoardAction();
}
});
mailboxDashBoardController.viewState.listen((state) {
log('ThreadController::_onMailboxDashBoardListener(): viewState: $state');
state.map((success) {
if (success is SearchEmailNewQuery){
mailboxDashBoardController.clearState();
_searchEmail();
} else if (success is MarkAsEmailReadSuccess
|| success is MoveToMailboxSuccess
|| success is MarkAsStarEmailSuccess
|| success is DeleteEmailPermanentlySuccess
|| success is SaveEmailAsDraftsSuccess
|| success is RemoveEmailDraftsSuccess
|| success is SendEmailSuccess
|| success is UpdateEmailDraftsSuccess) {
_refreshEmailChanges();
}
});
});
}
void _getAllEmail() {
if (_accountId != null) {
_getAllEmailAction(_accountId!, mailboxId: _currentMailboxId);
@@ -303,7 +336,7 @@ class ThreadController extends BaseController {
sort: _sortOrder,
emailFilter: EmailFilter(
filter: _getFilterCondition(),
filterOption: filterMessageOption.value,
filterOption: mailboxDashBoardController.filterMessageOption.value,
mailboxId: mailboxId ?? _currentMailboxId),
propertiesCreated: ThreadConstants.propertiesDefault,
propertiesUpdated: ThreadConstants.propertiesUpdatedDefault,
@@ -311,7 +344,7 @@ class ThreadController extends BaseController {
}
EmailFilterCondition _getFilterCondition({bool isLoadMore = false}) {
switch(filterMessageOption.value) {
switch(mailboxDashBoardController.filterMessageOption.value) {
case FilterMessageOption.all:
return EmailFilterCondition(
inMailbox: mailboxDashBoardController.selectedMailbox.value?.id,
@@ -350,7 +383,7 @@ class ThreadController extends BaseController {
sort: _sortOrder,
emailFilter: EmailFilter(
filter: _getFilterCondition(),
filterOption: filterMessageOption.value,
filterOption: mailboxDashBoardController.filterMessageOption.value,
mailboxId: _currentMailboxId),
propertiesCreated: ThreadConstants.propertiesDefault,
propertiesUpdated: ThreadConstants.propertiesUpdatedDefault,
@@ -359,6 +392,7 @@ class ThreadController extends BaseController {
}
void _refreshEmailChanges() {
log('ThreadController::_refreshEmailChanges(): ');
if (isSearchActive()) {
if (_accountId != null && searchQuery != null) {
final limit = emailListSearch.isNotEmpty ? UnsignedInt(emailListSearch.length) : ThreadConstants.defaultLimit;
@@ -379,7 +413,7 @@ class ThreadController extends BaseController {
propertiesCreated: ThreadConstants.propertiesDefault,
propertiesUpdated: ThreadConstants.propertiesUpdatedDefault,
inMailboxId: _currentMailboxId,
filterOption: filterMessageOption.value
filterOption: mailboxDashBoardController.filterMessageOption.value
));
}
}
@@ -439,16 +473,18 @@ class ThreadController extends BaseController {
}
if (_isUnSelectedAll()) {
currentSelectMode.value = SelectMode.INACTIVE;
mailboxDashBoardController.currentSelectMode.value = SelectMode.INACTIVE;
mailboxDashBoardController.listEmailSelected.clear();
} else {
if (currentSelectMode.value == SelectMode.INACTIVE) {
currentSelectMode.value = SelectMode.ACTIVE;
if (mailboxDashBoardController.currentSelectMode.value == SelectMode.INACTIVE) {
mailboxDashBoardController.currentSelectMode.value = SelectMode.ACTIVE;
}
mailboxDashBoardController.listEmailSelected.value = listEmailSelected;
}
}
void enableSelectionEmail() {
currentSelectMode.value = SelectMode.ACTIVE;
mailboxDashBoardController.currentSelectMode.value = SelectMode.ACTIVE;
}
void setSelectAllEmailAction() {
@@ -457,7 +493,8 @@ class ThreadController extends BaseController {
} else {
emailList.value = emailList.map((email) => email.toSelectedEmail(selectMode: SelectMode.ACTIVE)).toList();
}
currentSelectMode.value = SelectMode.ACTIVE;
mailboxDashBoardController.currentSelectMode.value = SelectMode.ACTIVE;
mailboxDashBoardController.listEmailSelected.value = listEmailSelected;
}
List<PresentationEmail> get listEmailSelected {
@@ -477,20 +514,28 @@ class ThreadController extends BaseController {
}
void cancelSelectEmail() {
if (isSearchActive()) {
emailListSearch.value = emailListSearch.map((email) => email.toSelectedEmail(selectMode: SelectMode.INACTIVE)).toList();
} else {
emailList.value = emailList.map((email) => email.toSelectedEmail(selectMode: SelectMode.INACTIVE)).toList();
emailListSearch.value = emailListSearch.map((email) => email.toSelectedEmail(selectMode: SelectMode.INACTIVE)).toList();
emailList.value = emailList.map((email) => email.toSelectedEmail(selectMode: SelectMode.INACTIVE)).toList();
mailboxDashBoardController.currentSelectMode.value = SelectMode.INACTIVE;
mailboxDashBoardController.listEmailSelected.clear();
}
void markAsReadAllEmails() {
final listEmail = isSearchActive() ? emailListSearch.allEmailUnread : emailList.allEmailUnread;
log('ThreadController::markAsReadAllEmails(): ${listEmail.length}');
if (listEmail.isNotEmpty) {
markAsReadSelectedMultipleEmail(listEmail);
}
currentSelectMode.value = SelectMode.INACTIVE;
}
void markAsReadSelectedMultipleEmail(List<PresentationEmail> listPresentationEmail) {
final listEmail = listPresentationEmail.map((presentationEmail) => presentationEmail.toEmail()).toList();
log('ThreadController::markAsReadSelectedMultipleEmail(): listPresentationEmail: ${listPresentationEmail.length}');
final readAction = listPresentationEmail.isAllEmailRead ? ReadActions.markAsUnread : ReadActions.markAsRead;
final mailboxCurrent = mailboxDashBoardController.selectedMailbox.value;
if (_accountId != null && mailboxCurrent != null) {
cancelSelectEmail();
final listEmail = listPresentationEmail.map((presentationEmail) => presentationEmail.toEmail()).toList();
log('ThreadController::markAsReadSelectedMultipleEmail(): listEmail: ${listEmail.length}');
consumeState(_markAsMultipleEmailReadInteractor.execute(_accountId!, listEmail, readAction));
}
}
@@ -515,7 +560,7 @@ class ThreadController extends BaseController {
message: message,
icon: readActions == ReadActions.markAsUnread ? _imagePaths.icUnreadToast : _imagePaths.icReadToast);
}
log('ThreadController::_markAsReadSelectedMultipleEmailSuccess(): _refreshEmailChanges');
_refreshEmailChanges();
}
@@ -549,8 +594,15 @@ class ThreadController extends BaseController {
void filterMessagesAction(BuildContext context, FilterMessageOption filterOption) {
popBack();
final newFilterOption = filterMessageOption.value == filterOption ? FilterMessageOption.all : filterOption;
filterMessageOption.value = newFilterOption;
final newFilterOption = mailboxDashBoardController.filterMessageOption.value == filterOption
? FilterMessageOption.all
: filterOption;
log('ThreadController::filterMessagesAction(): newFilterOption: $newFilterOption');
mailboxDashBoardController.filterMessageOption.value = newFilterOption;
log('ThreadController::filterMessagesAction(): filterMessageOption: ${mailboxDashBoardController.filterMessageOption}');
_appToast.showToastWithIcon(
currentOverlayContext!,
@@ -737,10 +789,10 @@ class ThreadController extends BaseController {
}
void markAsStarSelectedMultipleEmail(List<PresentationEmail> listPresentationEmail) {
final listEmail = listPresentationEmail.map((presentationEmail) => presentationEmail.toEmail()).toList();
final starAction = listPresentationEmail.isAllEmailStarred ? MarkStarAction.unMarkStar : MarkStarAction.markStar;
if (_accountId != null) {
cancelSelectEmail();
final listEmail = listPresentationEmail.map((presentationEmail) => presentationEmail.toEmail()).toList();
consumeState(_markAsStarMultipleEmailInteractor.execute(_accountId!, listEmail, starAction));
}
}
@@ -840,9 +892,10 @@ class ThreadController extends BaseController {
_isLoadingMore = false;
}
bool isSelectionEnabled() => currentSelectMode.value == SelectMode.ACTIVE;
bool isSelectionEnabled() => mailboxDashBoardController.currentSelectMode.value == SelectMode.ACTIVE;
void pressEmailSelectionAction(BuildContext context, EmailActionType actionType, List<PresentationEmail> selectionEmail) {
log('ThreadController::pressEmailSelectionAction(): selectionEmail: ${selectionEmail.length}');
switch(actionType) {
case EmailActionType.markAsRead:
case EmailActionType.markAsUnread:
@@ -1115,9 +1168,8 @@ class ThreadController extends BaseController {
}
void _deleteMultipleEmailsPermanently(List<PresentationEmail> emailList) {
cancelSelectEmail();
final listEmailIds = emailList.map((email) => email.id).toList();
cancelSelectEmail();
if ( _accountId != null && listEmailIds.isNotEmpty) {
consumeState(_deleteMultipleEmailsPermanentlyInteractor.execute(_accountId!, listEmailIds));
}
@@ -1188,7 +1240,7 @@ class ThreadController extends BaseController {
mailboxRole: mailboxDashBoardController.selectedMailbox.value?.role);
if (kIsWeb) {
mailboxDashBoardController.dispatchDashBoardAction(DashBoardAction.compose, arguments: arguments);
mailboxDashBoardController.dispatchAction(ComposeEmailAction(arguments: arguments));
} else {
push(AppRoutes.COMPOSER, arguments: arguments);
}
+13 -235
View File
@@ -1,15 +1,13 @@
import 'package:core/core.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:model/model.dart';
import 'package:tmail_ui_user/features/base/mixin/app_loader_mixin.dart';
import 'package:tmail_ui_user/features/email/presentation/widgets/email_action_cupertino_action_sheet_action_builder.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/user_setting_popup_menu_mixin.dart';
import 'package:tmail_ui_user/features/thread/domain/state/search_email_state.dart';
import 'package:tmail_ui_user/features/thread/domain/state/search_more_email_state.dart';
import 'package:tmail_ui_user/features/thread/presentation/extensions/filter_message_option_extension.dart';
import 'package:tmail_ui_user/features/thread/presentation/model/delete_action_type.dart';
import 'package:tmail_ui_user/features/thread/presentation/thread_controller.dart';
import 'package:tmail_ui_user/features/thread/presentation/widgets/app_bar_thread_widget_builder.dart';
@@ -21,7 +19,7 @@ import 'package:tmail_ui_user/features/thread/presentation/widgets/search_app_ba
import 'package:tmail_ui_user/features/thread/presentation/widgets/suggestion_box_widget.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class ThreadView extends GetWidget<ThreadController> with UserSettingPopupMenuMixin {
class ThreadView extends GetWidget<ThreadController> with AppLoaderMixin {
final _responsiveUtils = Get.find<ResponsiveUtils>();
final _imagePaths = Get.find<ImagePaths>();
@@ -44,11 +42,6 @@ class ThreadView extends GetWidget<ThreadController> with UserSettingPopupMenuMi
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (_responsiveUtils.isDesktop(context))
Container(
color: Colors.white,
padding: const EdgeInsets.only(right: 10, top: 16, bottom: 10, left: 32),
child: _buildHeader(context)),
Obx(() => !controller.isSearchActive() && !_responsiveUtils.isDesktop(context)
? _buildAppBarNormal(context)
: const SizedBox.shrink()),
@@ -92,154 +85,6 @@ class ThreadView extends GetWidget<ThreadController> with UserSettingPopupMenuMi
);
}
Widget _buildHeader(BuildContext context) {
return Row(children: [
Obx(() => !controller.isSelectionEnabled() ? _buildListButtonTopBar(context) : const SizedBox.shrink()),
Obx(() => controller.isSelectionEnabled() ? _buildListButtonSelectionForDesktop(context) : const SizedBox.shrink()),
const SizedBox(width: 16),
Obx(() => !controller.isSearchActive() ? const Spacer() : const SizedBox.shrink()),
Expanded(child: _buildSearchInputFormForDesktop(context)),
_buildSearchButtonViewForDesktop(context),
Padding(
padding: const EdgeInsets.only(right: 16),
child: (AvatarBuilder()
..text(controller.mailboxDashBoardController.userProfile.value?.getAvatarText() ?? '')
..backgroundColor(Colors.white)
..textColor(Colors.black)
..context(context)
..addOnTapAvatarActionWithPositionClick((position) => openUserSettingAction(
context,
position,
popupMenuUserSettingActionTile(context, () => controller.mailboxDashBoardController.logoutAction())))
..addBoxShadows([const BoxShadow(
color: AppColor.colorShadowBgContentEmail,
spreadRadius: 1, blurRadius: 1, offset: Offset(0, 0.5))])
..size(48))
.build()),
]);
}
Widget _buildListButtonTopBar(BuildContext context) {
return Row(children: [
if (!controller.isSearchActive())
(ButtonBuilder(_imagePaths.icRefresh)
..key(const Key('button_reload_thread'))
..decoration(BoxDecoration(borderRadius: BorderRadius.circular(10), color: AppColor.colorButtonHeaderThread))
..paddingIcon(EdgeInsets.zero)
..size(16)
..radiusSplash(10)
..padding(const EdgeInsets.symmetric(horizontal: 8, vertical: 8))
..onPressActionClick(() => controller.refreshAllEmail()))
.build(),
if (!controller.isSearchActive()) const SizedBox(width: 16),
(ButtonBuilder(_imagePaths.icSelectAll)
..key(const Key('button_select_all'))
..decoration(BoxDecoration(borderRadius: BorderRadius.circular(10), color: AppColor.colorButtonHeaderThread))
..paddingIcon(const EdgeInsets.only(right: 8))
..size(16)
..radiusSplash(10)
..padding(const EdgeInsets.symmetric(horizontal: 12, vertical: 8))
..textStyle(const TextStyle(fontSize: 12, color: AppColor.colorTextButtonHeaderThread))
..onPressActionClick(() => controller.setSelectAllEmailAction())
..text(AppLocalizations.of(context).select_all, isVertical: false))
.build(),
const SizedBox(width: 16),
(ButtonBuilder(_imagePaths.icMarkAllAsRead)
..key(const Key('button_mark_all_as_read'))
..decoration(BoxDecoration(borderRadius: BorderRadius.circular(10), color: AppColor.colorButtonHeaderThread))
..paddingIcon(const EdgeInsets.only(right: 8))
..size(16)
..padding(const EdgeInsets.symmetric(horizontal: 12, vertical: 8))
..radiusSplash(10)
..textStyle(const TextStyle(fontSize: 12, color: AppColor.colorTextButtonHeaderThread))
..onPressActionClick(() {
final listEmail = controller.isSearchActive()
? controller.emailListSearch.allEmailUnread
: controller.emailList.allEmailUnread;
if (listEmail.isNotEmpty) {
controller.markAsReadSelectedMultipleEmail(listEmail);
}
})
..text(AppLocalizations.of(context).mark_all_as_read, isVertical: false))
.build(),
if (!controller.isSearchActive()) const SizedBox(width: 16),
if (!controller.isSearchActive())
(ButtonBuilder(_imagePaths.icFilterWeb)
..key(const Key('button_filter_messages'))
..context(context)
..decoration(BoxDecoration(borderRadius: BorderRadius.circular(10), color: AppColor.colorButtonHeaderThread))
..paddingIcon(const EdgeInsets.only(right: 8))
..size(16)
..padding(const EdgeInsets.symmetric(horizontal: 12, vertical: 8))
..radiusSplash(10)
..textStyle(TextStyle(
fontSize: 12,
color: controller.filterMessageOption.value == FilterMessageOption.all ? AppColor.colorTextButtonHeaderThread : AppColor.colorNameEmail,
fontWeight: controller.filterMessageOption.value == FilterMessageOption.all ? FontWeight.normal : FontWeight.w500))
..addIconAction(Padding(
padding: const EdgeInsets.only(left: 8),
child: SvgPicture.asset(_imagePaths.icArrowDown, fit: BoxFit.fill)))
..addOnPressActionWithPositionClick((position) =>
controller.openFilterMessagesForTablet(
context,
position,
_popupMenuEmailActionTile(context, controller.filterMessageOption.value)))
..text(controller.filterMessageOption.value == FilterMessageOption.all
? AppLocalizations.of(context).filter_messages
: controller.filterMessageOption.value.getTitle(context), isVertical: false))
.build(),
]);
}
Widget _buildListButtonSelectionForDesktop(BuildContext context) {
return Row(children: [
buildIconWeb(
icon: SvgPicture.asset(_imagePaths.icCloseComposer, color: AppColor.colorTextButton, fit: BoxFit.fill),
tooltip: AppLocalizations.of(context).cancel,
onTap: () => controller.cancelSelectEmail()),
Text(
AppLocalizations.of(context).count_email_selected(controller.listEmailSelected.length),
style: const TextStyle(fontSize: 17, fontWeight: FontWeight.w500, color: AppColor.colorTextButton),),
const SizedBox(width: 30),
buildIconWeb(
icon: SvgPicture.asset(controller.listEmailSelected.isAllEmailRead ? _imagePaths.icUnread : _imagePaths.icRead, fit: BoxFit.fill),
tooltip: controller.listEmailSelected.isAllEmailRead ? AppLocalizations.of(context).mark_as_unread : AppLocalizations.of(context).mark_as_read,
onTap: () => controller.pressEmailSelectionAction(
context,
controller.listEmailSelected.isAllEmailRead ? EmailActionType.markAsUnread : EmailActionType.markAsRead,
controller.listEmailSelected)),
buildIconWeb(
icon: SvgPicture.asset(controller.listEmailSelected.isAllEmailStarred ? _imagePaths.icUnStar : _imagePaths.icStar, fit: BoxFit.fill),
tooltip: controller.listEmailSelected.isAllEmailStarred ? AppLocalizations.of(context).un_star : AppLocalizations.of(context).star,
onTap: () => controller.pressEmailSelectionAction(
context,
controller.listEmailSelected.isAllEmailStarred ? EmailActionType.markAsStarred : EmailActionType.unMarkAsStarred,
controller.listEmailSelected)),
if (controller.currentMailbox?.isDrafts == false)
buildIconWeb(
icon: SvgPicture.asset(_imagePaths.icMove, fit: BoxFit.fill),
tooltip: AppLocalizations.of(context).move,
onTap: () => controller.pressEmailSelectionAction(context, EmailActionType.moveToMailbox, controller.listEmailSelected)),
if (controller.currentMailbox?.isDrafts == false)
buildIconWeb(
icon: SvgPicture.asset(controller.currentMailbox?.isSpam == true ? _imagePaths.icNotSpam : _imagePaths.icSpam,
fit: BoxFit.fill),
tooltip: controller.currentMailbox?.isSpam == true ? AppLocalizations.of(context).un_spam : AppLocalizations.of(context).mark_as_spam,
onTap: () => controller.currentMailbox?.isSpam == true
? controller.pressEmailSelectionAction(context, EmailActionType.unSpam, controller.listEmailSelected)
: controller.pressEmailSelectionAction(context, EmailActionType.moveToSpam, controller.listEmailSelected)),
buildIconWeb(
icon: SvgPicture.asset(_imagePaths.icDelete, fit: BoxFit.fill),
tooltip: controller.currentMailbox?.isTrash == true
? AppLocalizations.of(context).delete_permanently
: AppLocalizations.of(context).move_to_trash,
onTap: () => controller.currentMailbox?.isTrash == true
? controller.deleteSelectionEmailsPermanently(context, DeleteActionType.multiple, selectedEmails: controller.listEmailSelected)
: controller.pressEmailSelectionAction(context, EmailActionType.moveToTrash, controller.listEmailSelected)),
]);
}
Widget _buildListButtonSelectionForMobile(BuildContext context) {
return Obx(() {
if (controller.isSelectionEnabled() && !_responsiveUtils.isDesktop(context) && controller.listEmailSelected.isNotEmpty) {
@@ -292,54 +137,6 @@ class ThreadView extends GetWidget<ThreadController> with UserSettingPopupMenuMi
});
}
Widget _buildSearchButtonViewForDesktop(BuildContext context) {
return Obx(() {
if (!controller.isSearchActive() && _responsiveUtils.isDesktop(context)) {
return Padding(
padding: const EdgeInsets.only(right: 16),
child: (SearchBarView(_imagePaths)
..hintTextSearch(AppLocalizations.of(context).search_emails)
..maxSizeWidth(240)
..addOnOpenSearchViewAction(() => controller.enableSearch(context)))
.build());
} else {
return const SizedBox.shrink();
}
});
}
Widget _buildSearchInputFormForDesktop(BuildContext context) {
return Obx(() {
if (controller.isSearchActive() && _responsiveUtils.isDesktop(context)) {
return _buildSearchFormForDesktop(context);
} else {
return const SizedBox.shrink();
}
});
}
Widget _buildSearchFormForDesktop(BuildContext context) {
return (SearchAppBarWidget(
context,
_imagePaths,
_responsiveUtils,
controller.searchQuery,
controller.mailboxDashBoardController.searchFocus,
controller.mailboxDashBoardController.searchInputController,
suggestionSearch: controller.mailboxDashBoardController.suggestionSearch,)
..addDecoration(const BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(10)),
color: AppColor.colorBgSearchBar))
..setMargin(const EdgeInsets.only(right: 10))
..setHeightSearchBar(45)
..setHintText(AppLocalizations.of(context).search_mail)
..addOnCancelSearchPressed(() => controller.disableSearch())
..addOnClearTextSearchAction(() => controller.mailboxDashBoardController.clearSearchText())
..addOnTextChangeSearchAction((query) => controller.mailboxDashBoardController.addSuggestionSearch(query))
..addOnSearchTextAction((query) => controller.mailboxDashBoardController.searchEmail(context, query)))
.build();
}
Widget _buildSearchForm(BuildContext context) {
return (SearchAppBarWidget(
context,
@@ -366,19 +163,18 @@ class ThreadView extends GetWidget<ThreadController> with UserSettingPopupMenuMi
_responsiveUtils,
controller.mailboxDashBoardController.selectedMailbox.value,
controller.listEmailSelected,
controller.currentSelectMode.value,
controller.filterMessageOption.value)
controller.mailboxDashBoardController.currentSelectMode.value,
controller.mailboxDashBoardController.filterMessageOption.value)
..addOpenMailboxMenuActionClick(() => controller.openMailboxLeftMenu())
..addOnEditThreadAction(() => controller.enableSelectionEmail())
..addOnEmailSelectionAction((actionType, selectionEmail) =>
controller.pressEmailSelectionAction(context, actionType, selectionEmail))
..addOnFilterEmailAction((filterMessageOption, position) =>
_responsiveUtils.isMobileDevice(context)
? controller.openFilterMessagesCupertinoActionSheet(
? controller.openContextMenuAction(
context,
_filterMessagesCupertinoActionTile(context, filterMessageOption),
cancelButton: _buildCupertinoActionCancelButton(context))
: controller.openFilterMessagesForTablet(
_filterMessagesCupertinoActionTile(context, filterMessageOption))
: controller.openPopupMenuAction(
context,
position,
_popupMenuEmailActionTile(context, filterMessageOption)))
@@ -423,15 +219,6 @@ class ThreadView extends GetWidget<ThreadController> with UserSettingPopupMenuMi
];
}
Widget _buildCupertinoActionCancelButton(BuildContext context) {
return CupertinoActionSheetAction(
child: Text(
AppLocalizations.of(context).cancel,
style: const TextStyle(fontWeight: FontWeight.w500, fontSize: 20, color: AppColor.colorTextButton)),
onPressed: () => controller.closeFilterMessageActionSheet(),
);
}
Widget _filterMessageWithAttachmentsAction(BuildContext context, FilterMessageOption optionCurrent) {
return (FilterMessageCupertinoActionSheetActionBuilder(
const Key('filter_attachment_action'),
@@ -502,36 +289,27 @@ class ThreadView extends GetWidget<ThreadController> with UserSettingPopupMenuMi
(success) {
if (controller.isSearchActive()) {
return success is SearchingState
? Padding(padding: const EdgeInsets.symmetric(vertical: 16), child: _loadingWidget)
? Padding(padding: const EdgeInsets.symmetric(vertical: 16), child: loadingWidget)
: const SizedBox.shrink();
} else {
return success is LoadingState
? Padding(padding: const EdgeInsets.symmetric(vertical: 16), child: _loadingWidget)
? Padding(padding: const EdgeInsets.symmetric(vertical: 16), child: loadingWidget)
: const SizedBox.shrink();
}
}));
}
Widget get _loadingWidget {
return const Center(child: Padding(
padding: EdgeInsets.zero,
child: SizedBox(
width: 24,
height: 24,
child: CupertinoActivityIndicator(color: AppColor.colorTextButton))));
}
Widget _buildLoadingViewLoadMore() {
return Obx(() => controller.viewState.value.fold(
(failure) => const SizedBox.shrink(),
(success) {
if (controller.isSearchActive()) {
return success is SearchingMoreState
? Padding(padding: const EdgeInsets.only(bottom: 16), child: _loadingWidget)
? Padding(padding: const EdgeInsets.only(bottom: 16), child: loadingWidget)
: const SizedBox.shrink();
} else {
return success is LoadingMoreState
? Padding(padding: const EdgeInsets.only(bottom: 16), child: _loadingWidget)
? Padding(padding: const EdgeInsets.only(bottom: 16), child: loadingWidget)
: const SizedBox.shrink();
}
}));
@@ -566,7 +344,7 @@ class ThreadView extends GetWidget<ThreadController> with UserSettingPopupMenuMi
}
Widget _buildResultListEmail(BuildContext context, List<PresentationEmail> listPresentationEmail) {
if (controller.currentSelectMode.value == SelectMode.INACTIVE) {
if (controller.mailboxDashBoardController.currentSelectMode.value == SelectMode.INACTIVE) {
return listPresentationEmail.isNotEmpty
? RefreshIndicator(
color: AppColor.colorTextButton,
@@ -608,7 +386,7 @@ class ThreadView extends GetWidget<ThreadController> with UserSettingPopupMenuMi
context,
listPresentationEmail[index],
controller.currentMailbox?.role,
controller.currentSelectMode.value,
controller.mailboxDashBoardController.currentSelectMode.value,
controller.mailboxDashBoardController.searchState.value.searchStatus,
controller.searchQuery)
..addOnPressEmailActionClick((action, email) => controller.pressEmailAction(context, action, email))
@@ -98,7 +98,7 @@ class AppBarThreadWidgetBuilder {
AppLocalizations.of(_context).count_email_selected(_listSelectionEmail.length),
style: const TextStyle(fontSize: 17, fontWeight: FontWeight.w500, color: AppColor.colorTextButton))),
buildIconWeb(
icon: SvgPicture.asset(_listSelectionEmail.isAllEmailRead ? _imagePaths.icUnread : _imagePaths.icRead, fit: BoxFit.fill),
icon: SvgPicture.asset(_listSelectionEmail.isAllEmailRead ? _imagePaths.icRead : _imagePaths.icUnread, fit: BoxFit.fill),
tooltip: _listSelectionEmail.isAllEmailRead ? AppLocalizations.of(_context).unread : AppLocalizations.of(_context).read,
onTap: () => _onEmailSelectionAction?.call(
_listSelectionEmail.isAllEmailRead ? EmailActionType.markAsUnread : EmailActionType.markAsRead,
@@ -52,21 +52,31 @@ class EmailTileBuilder {
data: ThemeData(splashColor: Colors.transparent, highlightColor: Colors.transparent),
child: ResponsiveWidget(
responsiveUtils: _responsiveUtils,
mobile: _buildListTile(),
desktop: Container(
margin: _selectModeAll == SelectMode.ACTIVE ? const EdgeInsets.only(top: 3, left: 8, right: 8) : EdgeInsets.zero,
padding: _selectModeAll == SelectMode.ACTIVE ? const EdgeInsets.symmetric(vertical: 8) : EdgeInsets.zero,
decoration: _selectModeAll == SelectMode.ACTIVE && _presentationEmail.selectMode == SelectMode.ACTIVE
? BoxDecoration(
borderRadius: BorderRadius.circular(14),
color: AppColor.colorItemEmailSelectedDesktop)
: null,
child: _buildListTileForDesktop(),
),
mobile: _wrapContainerForTile(_buildListTile()),
desktop: _wrapContainerForTile(_buildListTileForDesktop()),
)
);
}
Widget _wrapContainerForTile(Widget tile) {
if (_responsiveUtils.isDesktop(_context)) {
return Container(
margin: _selectModeAll == SelectMode.ACTIVE ? EdgeInsets.only(top: 3, left: 8, right: 8) : EdgeInsets.zero,
padding: _selectModeAll == SelectMode.ACTIVE ? EdgeInsets.symmetric(vertical: 8) : EdgeInsets.zero,
decoration: _selectModeAll == SelectMode.ACTIVE && _presentationEmail.selectMode == SelectMode.ACTIVE
? BoxDecoration(borderRadius: BorderRadius.circular(14), color: AppColor.colorItemEmailSelectedDesktop)
: null,
child: tile);
} else {
return Container(
margin: EdgeInsets.zero,
padding: EdgeInsets.only(bottom: 10, left: 16, right: 16),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(0), color: Colors.white),
alignment: Alignment.center,
child: tile);
}
}
Widget _buildListTile() {
return Container(
margin: EdgeInsets.zero,
@@ -51,54 +51,37 @@ class EmailTileBuilder {
data: ThemeData(splashColor: Colors.transparent, highlightColor: Colors.transparent),
child: ResponsiveWidget(
responsiveUtils: _responsiveUtils,
mobile: Container(
mobile: _wrapContainerForTile(_buildListTile()),
tablet: _wrapContainerForTile(_buildListTileTablet()),
desktop: _wrapContainerForTile(_buildListTileForDesktop()),
)
);
}
Widget _wrapContainerForTile(Widget tile) {
if (_responsiveUtils.isDesktop(_context)) {
return Container(
margin: _selectModeAll == SelectMode.ACTIVE ? EdgeInsets.only(top: 3, left: 8, right: 8) : EdgeInsets.zero,
padding: _selectModeAll == SelectMode.ACTIVE ? EdgeInsets.symmetric(vertical: 8) : EdgeInsets.zero,
decoration: _selectModeAll == SelectMode.ACTIVE && _presentationEmail.selectMode == SelectMode.ACTIVE
? BoxDecoration(borderRadius: BorderRadius.circular(14), color: AppColor.colorItemEmailSelectedDesktop)
: null,
child: tile,
);
} else {
return Container(
margin: _selectModeAll == SelectMode.ACTIVE
? const EdgeInsets.only(top: 3, left: 16, right: 16)
? EdgeInsets.only(top: 3, left: 16, right: 16)
: EdgeInsets.zero,
padding: _selectModeAll == SelectMode.ACTIVE
? const EdgeInsets.only(top: 8, bottom: 16, right: 8)
: const EdgeInsets.only(bottom: 10, left: 16, right: 16),
? EdgeInsets.only(top: 8, bottom: 16, right: 8)
: EdgeInsets.only(bottom: 10, left: 16, right: 16),
decoration: _selectModeAll == SelectMode.ACTIVE && _presentationEmail.selectMode == SelectMode.ACTIVE
? BoxDecoration(borderRadius: BorderRadius.circular(14), color: AppColor.colorItemEmailSelectedDesktop)
: BoxDecoration(borderRadius: BorderRadius.circular(0), color: Colors.white),
alignment: Alignment.center,
child: _buildListTile()),
tablet: Container(
margin: _selectModeAll == SelectMode.ACTIVE
? const EdgeInsets.only(top: 3, left: 16, right: 16)
: EdgeInsets.zero,
padding: _selectModeAll == SelectMode.ACTIVE
? const EdgeInsets.only(top: 8, bottom: 16, right: 8)
: const EdgeInsets.only(bottom: 10, left: 16, right: 16),
decoration: _selectModeAll == SelectMode.ACTIVE && _presentationEmail.selectMode == SelectMode.ACTIVE
? BoxDecoration(borderRadius: BorderRadius.circular(14), color: AppColor.colorItemEmailSelectedDesktop)
: BoxDecoration(borderRadius: BorderRadius.circular(0), color: Colors.white),
alignment: Alignment.center,
child: _buildListTileTabletLarge()),
tabletLarge: Container(
margin: _selectModeAll == SelectMode.ACTIVE
? const EdgeInsets.only(top: 3, left: 16, right: 16)
: EdgeInsets.zero,
padding: _selectModeAll == SelectMode.ACTIVE
? const EdgeInsets.only(top: 8, bottom: 16, right: 8)
: const EdgeInsets.only(bottom: 10, left: 16, right: 16),
decoration: _selectModeAll == SelectMode.ACTIVE && _presentationEmail.selectMode == SelectMode.ACTIVE
? BoxDecoration(borderRadius: BorderRadius.circular(14), color: AppColor.colorItemEmailSelectedDesktop)
: BoxDecoration(borderRadius: BorderRadius.circular(0), color: Colors.white),
alignment: Alignment.center,
child: _buildListTileTabletLarge()),
desktop: Container(
margin: _selectModeAll == SelectMode.ACTIVE ? const EdgeInsets.only(top: 3, left: 8, right: 8) : EdgeInsets.zero,
padding: _selectModeAll == SelectMode.ACTIVE ? const EdgeInsets.symmetric(vertical: 8) : EdgeInsets.zero,
decoration: _selectModeAll == SelectMode.ACTIVE && _presentationEmail.selectMode == SelectMode.ACTIVE
? BoxDecoration(
borderRadius: BorderRadius.circular(14),
color: AppColor.colorItemEmailSelectedDesktop)
: null,
child: _buildListTileForDesktop(),
),
)
);
child: tile);
}
}
Widget _buildListTile() {
@@ -208,7 +191,7 @@ class EmailTileBuilder {
]);
}
Widget _buildListTileTabletLarge() {
Widget _buildListTileTablet() {
return StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
return InkWell(
onTap: () => _emailActionClick?.call(EmailActionType.preview, _presentationEmail),
@@ -523,11 +506,13 @@ class EmailTileBuilder {
TextStyle? textStyle
}) {
if (_selectModeAll == SelectMode.ACTIVE) {
return Tooltip(
child: Padding(
padding: const EdgeInsets.all(12),
child: SvgPicture.asset(_presentationEmail.selectMode == SelectMode.ACTIVE ? _imagePaths.icSelected : _imagePaths.icUnSelected, fit: BoxFit.fill)),
message: AppLocalizations.of(_context).select);
return Padding(
padding: EdgeInsets.all(12),
child: SvgPicture.asset(
_presentationEmail.selectMode == SelectMode.ACTIVE
? _imagePaths.icSelected
: _imagePaths.icUnSelected,
fit: BoxFit.fill));
} else {
return Container(
width: iconSize ?? 48,
+15 -3
View File
@@ -1,5 +1,5 @@
{
"@@last_modified": "2022-04-15T16:53:02.138499",
"@@last_modified": "2022-04-25T09:26:28.013781",
"initializing_data": "Initializing data...",
"@initializing_data": {
"type": "text",
@@ -474,8 +474,8 @@
"placeholders_order": [],
"placeholders": {}
},
"logout": "Logout",
"@logout": {
"sign_out": "Sign out",
"@sign_out": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
@@ -1095,5 +1095,17 @@
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"settings": "Settings",
"@settings": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"manage_account": "Manage account",
"@manage_account": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
}
}
+13 -3
View File
@@ -506,9 +506,9 @@ class AppLocalizations {
);
}
String get logout {
return Intl.message('Logout',
name: 'logout');
String get sign_out {
return Intl.message('Sign out',
name: 'sign_out');
}
String get hint_search_mailboxes {
@@ -1153,4 +1153,14 @@ class AppLocalizations {
name: 'un_spam',
);
}
String get settings {
return Intl.message('Settings',
name: 'settings');
}
String get manage_account {
return Intl.message('Manage account',
name: 'manage_account');
}
}