diff --git a/assets/images/ic_delete_v2.svg b/assets/images/ic_delete_v2.svg new file mode 100644 index 000000000..e8c4e4d5e --- /dev/null +++ b/assets/images/ic_delete_v2.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/ic_flag_v2.svg b/assets/images/ic_flag_v2.svg new file mode 100644 index 000000000..03b215818 --- /dev/null +++ b/assets/images/ic_flag_v2.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/ic_move_v2.svg b/assets/images/ic_move_v2.svg new file mode 100644 index 000000000..3ea0a2060 --- /dev/null +++ b/assets/images/ic_move_v2.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/ic_read_v2.svg b/assets/images/ic_read_v2.svg new file mode 100644 index 000000000..5d2eb66fb --- /dev/null +++ b/assets/images/ic_read_v2.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/ic_selected_v2.svg b/assets/images/ic_selected_v2.svg new file mode 100644 index 000000000..94b3247de --- /dev/null +++ b/assets/images/ic_selected_v2.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/ic_spam_v2.svg b/assets/images/ic_spam_v2.svg new file mode 100644 index 000000000..871ece0a7 --- /dev/null +++ b/assets/images/ic_spam_v2.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/ic_unread_v2.svg b/assets/images/ic_unread_v2.svg new file mode 100644 index 000000000..88c8012db --- /dev/null +++ b/assets/images/ic_unread_v2.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/ic_unselected_v2.svg b/assets/images/ic_unselected_v2.svg new file mode 100644 index 000000000..179a226ef --- /dev/null +++ b/assets/images/ic_unselected_v2.svg @@ -0,0 +1,3 @@ + + + diff --git a/core/lib/presentation/resources/image_paths.dart b/core/lib/presentation/resources/image_paths.dart index 36b91564f..e5b4fa53c 100644 --- a/core/lib/presentation/resources/image_paths.dart +++ b/core/lib/presentation/resources/image_paths.dart @@ -51,6 +51,14 @@ class ImagePaths { String get icFilter => _getImagePath('ic_filter.svg'); String get icSearchBar => _getImagePath('ic_search_bar.svg'); String get icCompose => _getImagePath('ic_compose.svg'); + String get icReadV2 => _getImagePath('ic_read_v2.svg'); + String get icUnreadV2 => _getImagePath('ic_unread_v2.svg'); + String get icSelectedV2 => _getImagePath('ic_selected_v2.svg'); + String get icUnSelectedV2 => _getImagePath('ic_unselected_v2.svg'); + String get icDeleteV2 => _getImagePath('ic_delete_v2.svg'); + String get icSpamV2 => _getImagePath('ic_spam_v2.svg'); + String get icMoveV2 => _getImagePath('ic_move_v2.svg'); + String get icFlagV2 => _getImagePath('ic_flag_v2.svg'); String _getImagePath(String imageName) { return AssetsPaths.images + imageName; diff --git a/core/lib/presentation/views/button/button_builder.dart b/core/lib/presentation/views/button/button_builder.dart index 2e6a4fcbd..0e7954fd2 100644 --- a/core/lib/presentation/views/button/button_builder.dart +++ b/core/lib/presentation/views/button/button_builder.dart @@ -13,10 +13,11 @@ class ButtonBuilder { String? _icon; String? _text; double? _size; - double? _padding; + EdgeInsets? _paddingIcon; bool? _isVertical; Key? _key; - Color? _color; + Color? _iconColor; + TextStyle? _textStyle; void key(Key key) { _key = key; @@ -26,12 +27,16 @@ class ButtonBuilder { _size = size; } - void color(Color color) { - _color = color; + void iconColor(Color color) { + _iconColor = color; } - void padding(double padding) { - _padding = padding; + void textStyle(TextStyle style) { + _textStyle = style; + } + + void paddingIcon(EdgeInsets paddingIcon) { + _paddingIcon = paddingIcon; } void text(String text, {required bool isVertical}) { @@ -87,15 +92,15 @@ class ButtonBuilder { } Widget _buildIcon() => Padding( - padding: EdgeInsets.all(_padding ?? 10), - child: SvgPicture.asset(_icon ?? '', width: _size ?? 24, height: _size ?? 24, fit: BoxFit.fill, color: _color)); + padding: _paddingIcon ?? EdgeInsets.all(10), + child: SvgPicture.asset(_icon ?? '', width: _size ?? 24, height: _size ?? 24, fit: BoxFit.fill, color: _iconColor)); Widget _buildText() { return Text( '${_text ?? ''}', maxLines: 1, overflow: TextOverflow.ellipsis, - style: TextStyle(fontSize: 12, color: AppColor.textButtonColor, fontWeight: FontWeight.w500), + style: _textStyle ?? TextStyle(fontSize: 12, color: AppColor.textButtonColor, fontWeight: FontWeight.w500), ); } } \ No newline at end of file diff --git a/core/lib/presentation/views/floating_button/scrolling_floating_button_animated.dart b/core/lib/presentation/views/floating_button/scrolling_floating_button_animated.dart index b6a480a36..64d54802b 100644 --- a/core/lib/presentation/views/floating_button/scrolling_floating_button_animated.dart +++ b/core/lib/presentation/views/floating_button/scrolling_floating_button_animated.dart @@ -103,16 +103,20 @@ class _ScrollingFloatingButtonAnimatedState _scrollController.position.userScrollDirection == ScrollDirection.reverse) { if (widget.animateIcon!) _animationController.forward(); - setState(() { - _onTop = false; - }); + if (mounted) { + setState(() { + _onTop = false; + }); + } } else if (_scrollController.position.pixels <= widget.limitIndicator! && _scrollController.position.userScrollDirection == ScrollDirection.forward) { if (widget.animateIcon!) _animationController.reverse(); - setState(() { - _onTop = true; - }); + if (mounted) { + setState(() { + _onTop = true; + }); + } } }); } @@ -128,9 +132,11 @@ class _ScrollingFloatingButtonAnimatedState height: widget.height, width: _onTop ? widget.width : widget.height, onEnd: () { - setState(() { - _visibleText = !_visibleText; - }); + if (mounted) { + setState(() { + _visibleText = !_visibleText; + }); + } }, decoration: BoxDecoration(borderRadius: BorderRadius.all(Radius.circular(widget.height! / 2))), child: InkWell( diff --git a/core/lib/presentation/views/image/icon_builder.dart b/core/lib/presentation/views/image/icon_builder.dart index ee6fae814..3f4066e8f 100644 --- a/core/lib/presentation/views/image/icon_builder.dart +++ b/core/lib/presentation/views/image/icon_builder.dart @@ -7,6 +7,7 @@ class IconBuilder { Key? _key; double? _size; String? _icon; + EdgeInsets? _padding; OnPressIconActionClick? _onPressIconActionClick; IconBuilder(this._icon); @@ -19,6 +20,10 @@ class IconBuilder { _size = size; } + void padding(EdgeInsets padding) { + _padding = padding; + } + void addOnTapActionClick(OnPressIconActionClick onPressIconActionClick) { _onPressIconActionClick = onPressIconActionClick; } @@ -29,16 +34,21 @@ class IconBuilder { width: _size ?? 40, height: _size ?? 40, alignment: Alignment.center, - padding: EdgeInsets.all(3), - child: IconButton( - padding: EdgeInsets.zero, - iconSize: _size ?? 40, - icon: SvgPicture.asset(_icon!, width: _size ?? 40, height: _size ?? 40, fit: BoxFit.fill), - onPressed: () => { - if (_onPressIconActionClick != null) { - _onPressIconActionClick!() - } - }) + padding: _padding ?? EdgeInsets.all(3), + child: Material( + borderRadius: BorderRadius.circular((_size ?? 40) / 2), + color: Colors.transparent, + child: IconButton( + padding: EdgeInsets.zero, + iconSize: _size ?? 40, + icon: SvgPicture.asset(_icon!, width: _size ?? 40, height: _size ?? 40, fit: BoxFit.fill), + onPressed: () => { + if (_onPressIconActionClick != null) { + _onPressIconActionClick!() + } + } + ) + ) ); } } \ No newline at end of file diff --git a/lib/features/destination_picker/presentation/widgets/app_bar_destination_picker_builder.dart b/lib/features/destination_picker/presentation/widgets/app_bar_destination_picker_builder.dart index 96e88f4c9..3908c6700 100644 --- a/lib/features/destination_picker/presentation/widgets/app_bar_destination_picker_builder.dart +++ b/lib/features/destination_picker/presentation/widgets/app_bar_destination_picker_builder.dart @@ -41,7 +41,7 @@ class AppBarDestinationPickerBuilder { Widget _buildBackButton() { return (ButtonBuilder(_imagePaths.icComposerClose) - ..padding(5) + ..paddingIcon(EdgeInsets.all(5)) ..size(30) ..onPressActionClick(() { if (_onCloseActionClick != null) { diff --git a/lib/features/thread/presentation/thread_controller.dart b/lib/features/thread/presentation/thread_controller.dart index d7a8ae1ed..b98cdd8a8 100644 --- a/lib/features/thread/presentation/thread_controller.dart +++ b/lib/features/thread/presentation/thread_controller.dart @@ -320,6 +320,10 @@ class ThreadController extends BaseController { } } + void enableSelectionEmail() { + currentSelectMode.value = SelectMode.ACTIVE; + } + List getListEmailSelected() { if (isSearchActive()) { return emailListSearch.where((email) => email.selectMode == SelectMode.ACTIVE).toList(); @@ -604,6 +608,27 @@ class ThreadController extends BaseController { && mailboxDashBoardController.userProfile.value != null && mailboxDashBoardController.mapDefaultMailboxId.containsKey(PresentationMailbox.roleOutbox); + bool isSelectionEnabled() => currentSelectMode.value == SelectMode.ACTIVE; + + void pressEmailSelectionAction(BuildContext context, EmailActionType actionType, List selectionEmail) { + switch(actionType) { + case EmailActionType.markAsRead: + case EmailActionType.markAsUnread: + markAsSelectedEmailRead(selectionEmail); + break; + case EmailActionType.move: + moveSelectedMultipleEmailToMailboxAction(selectionEmail); + break; + case EmailActionType.markAsFlag: + case EmailActionType.markAsSpam: + case EmailActionType.delete: + _appToast.showToast(AppLocalizations.of(context).the_feature_is_under_development); + break; + default: + break; + } + } + void openMailboxLeftMenu() { mailboxDashBoardController.openDrawer(); } diff --git a/lib/features/thread/presentation/thread_view.dart b/lib/features/thread/presentation/thread_view.dart index 9fb2e425e..c6b9d9171 100644 --- a/lib/features/thread/presentation/thread_view.dart +++ b/lib/features/thread/presentation/thread_view.dart @@ -6,9 +6,8 @@ import 'package:model/model.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/thread_controller.dart'; -import 'package:tmail_ui_user/features/thread/presentation/widgets/app_bar_thread_select_mode_active_builder.dart'; import 'package:tmail_ui_user/features/thread/presentation/widgets/app_bar_thread_widget_builder.dart'; -import 'package:tmail_ui_user/features/thread/presentation/widgets/email_context_menu_action_builder.dart'; +import 'package:tmail_ui_user/features/thread/presentation/widgets/bottom_bar_thread_selection_widget.dart'; import 'package:tmail_ui_user/features/thread/presentation/widgets/email_tile_builder.dart'; import 'package:tmail_ui_user/features/thread/presentation/widgets/search_app_bar_widget.dart'; import 'package:tmail_ui_user/features/thread/presentation/widgets/search_bar_thread_view_widget.dart'; @@ -63,26 +62,36 @@ class ThreadView extends GetWidget { _buildSuggestionBox(context) ], )), + Obx(() => controller.isSelectionEnabled() + ? Column(children: [ + Divider(color: AppColor.lineItemListColor, height: 1, thickness: 0.2), + _buildOptionSelectionBottomBar(context) + ]) + : SizedBox.shrink()), ] ) ) ), - floatingActionButton: Obx(() => !controller.isSearchActive() - ? ScrollingFloatingButtonAnimated( - icon: SvgPicture.asset(imagePaths.icCompose, width: 20, height: 20, fit: BoxFit.fill), - text: Padding( - padding: EdgeInsets.only(right: 10), - child: Text( - AppLocalizations.of(context).compose, - style: TextStyle(color: AppColor.colorTextButton, fontSize: 15.0, fontWeight: FontWeight.w500)) - ), - onPress: () => controller.composeEmailAction(), - scrollController: controller.listEmailController, - color: Colors.white, - elevation: 4.0, - width: 140, - animateIcon: false) - : SizedBox.shrink()) + floatingActionButton: Obx(() => !controller.isSearchActive() && !controller.isSelectionEnabled() + ? Container( + padding: EdgeInsets.only(bottom: controller.isSelectionEnabled() ? 80 : 0), + child: Align( + alignment: Alignment.bottomRight, + child: ScrollingFloatingButtonAnimated( + icon: SvgPicture.asset(imagePaths.icCompose, width: 20, height: 20, fit: BoxFit.fill), + text: Padding( + padding: EdgeInsets.only(right: 10), + child: Text( + AppLocalizations.of(context).compose, + style: TextStyle(color: AppColor.colorTextButton, fontSize: 15.0, fontWeight: FontWeight.w500)) + ), + onPress: () => controller.composeEmailAction(), + scrollController: controller.listEmailController, + color: Colors.white, + elevation: 4.0, + width: 140, + animateIcon: false))) + : SizedBox.shrink()), ), ); } @@ -93,12 +102,24 @@ class ThreadView extends GetWidget { children: [ if (!controller.isSearchActive()) _buildAppBarNormal(context), if (controller.isSearchActive()) _buildSearchForm(context), - if (controller.currentSelectMode.value == SelectMode.ACTIVE) _buildAppBarSelectModeActive(context), ], ); }); } + Widget _buildOptionSelectionBottomBar(BuildContext context) { + return Padding( + padding: EdgeInsets.all(10), + child: (BottomBarThreadSelectionWidget( + context, + imagePaths, + responsiveUtils, + controller.getListEmailSelected()) + ..addOnPressEmailSelectionActionClick((actionType, selectionEmail) => + controller.pressEmailSelectionAction(context, actionType, selectionEmail))) + .build()); + } + Widget _buildSearchForm(BuildContext context) { return (SearchAppBarWidget( context, @@ -120,49 +141,53 @@ class ThreadView extends GetWidget { context, imagePaths, responsiveUtils, - controller.mailboxDashBoardController.selectedMailbox.value) - ..addOpenListMailboxActionClick(() => controller.openMailboxLeftMenu())) - .build(); - } - - Widget _buildAppBarSelectModeActive(BuildContext context) { - return (AppBarThreadSelectModeActiveBuilder( - context, - imagePaths, + controller.mailboxDashBoardController.selectedMailbox.value, controller.getListEmailSelected(), - responsiveUtils) - ..addCloseActionClick(() => controller.cancelSelectEmail()) - // ..addRemoveEmailActionClick((listEmail) => {}) - ..addOnMarkAsEmailReadActionClick((listEmail) => controller.markAsSelectedEmailRead(listEmail)) - ..addOpenContextMenuActionClick((listEmail) => - controller.openContextMenuSelectedEmail(context, _contextMenuEmailActionTile(context, listEmail))) - ..addOnOpenPopupMenuActionClick((listEmail, position) => - controller.openPopupMenuSelectedEmail(context, position, _popupMenuEmailActionTile(context, listEmail)))) + controller.currentSelectMode.value) + ..addOpenListMailboxActionClick(() => controller.openMailboxLeftMenu()) + ..addOnEditThreadAction(() => controller.enableSelectionEmail()) + ..addOnCancelEditThread(() => controller.cancelSelectEmail())) .build(); } - List _contextMenuEmailActionTile(BuildContext context, List listEmail) { - return [ - // _moveToTrashAction(context, listEmail), - _moveToMailboxAction(context, listEmail), - _markAsReadAction(context, listEmail), - _markAsStarAction(context, listEmail), - // _moveToSpamAction(context, listEmail), - SizedBox(height: 20), - ]; - } + // Widget _buildAppBarSelectModeActive(BuildContext context) { + // return (AppBarThreadSelectModeActiveBuilder( + // context, + // imagePaths, + // controller.getListEmailSelected(), + // responsiveUtils) + // ..addCloseActionClick(() => controller.cancelSelectEmail()) + // // ..addRemoveEmailActionClick((listEmail) => {}) + // ..addOnMarkAsEmailReadActionClick((listEmail) => controller.markAsSelectedEmailRead(listEmail)) + // ..addOpenContextMenuActionClick((listEmail) => + // controller.openContextMenuSelectedEmail(context, _contextMenuEmailActionTile(context, listEmail))) + // ..addOnOpenPopupMenuActionClick((listEmail, position) => + // controller.openPopupMenuSelectedEmail(context, position, _popupMenuEmailActionTile(context, listEmail)))) + // .build(); + // } - Widget _markAsReadAction(BuildContext context, List listEmail) { - return (EmailContextMenuActionBuilder( - Key('mark_as_read_context_menu_action'), - SvgPicture.asset(imagePaths.icEyeDisable, width: 24, height: 24, fit: BoxFit.fill), - controller.isAllEmailRead(listEmail) - ? AppLocalizations.of(context).mark_as_unread - : AppLocalizations.of(context).mark_as_read, - listEmail) - ..onActionClick((data) => controller.markAsSelectedEmailRead(data, fromContextMenuAction: true))) - .build(); - } + // List _contextMenuEmailActionTile(BuildContext context, List listEmail) { + // return [ + // // _moveToTrashAction(context, listEmail), + // _moveToMailboxAction(context, listEmail), + // _markAsReadAction(context, listEmail), + // _markAsStarAction(context, listEmail), + // // _moveToSpamAction(context, listEmail), + // SizedBox(height: 20), + // ]; + // } + + // Widget _markAsReadAction(BuildContext context, List listEmail) { + // return (EmailContextMenuActionBuilder( + // Key('mark_as_read_context_menu_action'), + // SvgPicture.asset(imagePaths.icEyeDisable, width: 24, height: 24, fit: BoxFit.fill), + // controller.isAllEmailRead(listEmail) + // ? AppLocalizations.of(context).mark_as_unread + // : AppLocalizations.of(context).mark_as_read, + // listEmail) + // ..onActionClick((data) => controller.markAsSelectedEmailRead(data, fromContextMenuAction: true))) + // .build(); + // } // Widget _moveToTrashAction(BuildContext context, List listEmail) { // return (EmailContextMenuActionBuilder( @@ -174,33 +199,33 @@ class ThreadView extends GetWidget { // .build(); // } - Widget _moveToMailboxAction(BuildContext context, List listEmail) { - return (EmailContextMenuActionBuilder( - Key('move_to_mailbox_context_menu_action'), - SvgPicture.asset(imagePaths.icFolder, width: 24, height: 24, fit: BoxFit.fill), - AppLocalizations.of(context).move_to_mailbox, - listEmail) - ..onActionClick((data) => controller.moveSelectedMultipleEmailToMailboxAction(data))) - .build(); - } - - Widget _markAsStarAction(BuildContext context, List listEmail) { - final markStarAction = controller.isAllEmailMarkAsStar(listEmail) ? MarkStarAction.unMarkStar : MarkStarAction.markStar; - - return (EmailContextMenuActionBuilder( - Key('mark_as_star_context_menu_action'), - SvgPicture.asset( - markStarAction == MarkStarAction.markStar ? imagePaths.icFlag : imagePaths.icFlagged, - width: 24, - height: 24, - fit: BoxFit.fill), - markStarAction == MarkStarAction.markStar - ? AppLocalizations.of(context).mark_as_star - : AppLocalizations.of(context).mark_as_unstar, - listEmail) - ..onActionClick((data) => controller.markAsStarSelectedMultipleEmail(data, markStarAction))) - .build(); - } + // Widget _moveToMailboxAction(BuildContext context, List listEmail) { + // return (EmailContextMenuActionBuilder( + // Key('move_to_mailbox_context_menu_action'), + // SvgPicture.asset(imagePaths.icFolder, width: 24, height: 24, fit: BoxFit.fill), + // AppLocalizations.of(context).move_to_mailbox, + // listEmail) + // ..onActionClick((data) => controller.moveSelectedMultipleEmailToMailboxAction(data))) + // .build(); + // } + // + // Widget _markAsStarAction(BuildContext context, List listEmail) { + // final markStarAction = controller.isAllEmailMarkAsStar(listEmail) ? MarkStarAction.unMarkStar : MarkStarAction.markStar; + // + // return (EmailContextMenuActionBuilder( + // Key('mark_as_star_context_menu_action'), + // SvgPicture.asset( + // markStarAction == MarkStarAction.markStar ? imagePaths.icFlag : imagePaths.icFlagged, + // width: 24, + // height: 24, + // fit: BoxFit.fill), + // markStarAction == MarkStarAction.markStar + // ? AppLocalizations.of(context).mark_as_star + // : AppLocalizations.of(context).mark_as_unstar, + // listEmail) + // ..onActionClick((data) => controller.markAsStarSelectedMultipleEmail(data, markStarAction))) + // .build(); + // } // Widget _moveToSpamAction(BuildContext context, List listEmail) { // return (EmailContextMenuActionBuilder( @@ -212,15 +237,15 @@ class ThreadView extends GetWidget { // .build(); // } - List _popupMenuEmailActionTile(BuildContext context, List listEmail) { - return [ - // PopupMenuItem(child: _moveToTrashAction(context, listEmail)), - PopupMenuItem(child: _moveToMailboxAction(context, listEmail)), - PopupMenuItem(child: _markAsReadAction(context, listEmail)), - PopupMenuItem(child: _markAsStarAction(context, listEmail)), - // PopupMenuItem(child: _moveToSpamAction(context, listEmail)), - ]; - } + // List _popupMenuEmailActionTile(BuildContext context, List listEmail) { + // return [ + // // PopupMenuItem(child: _moveToTrashAction(context, listEmail)), + // PopupMenuItem(child: _moveToMailboxAction(context, listEmail)), + // PopupMenuItem(child: _markAsReadAction(context, listEmail)), + // PopupMenuItem(child: _markAsStarAction(context, listEmail)), + // // PopupMenuItem(child: _moveToSpamAction(context, listEmail)), + // ]; + // } Widget _buildLoadingView() { return Obx(() => controller.viewState.value.fold( @@ -348,8 +373,7 @@ class ThreadView extends GetWidget { controller.mailboxDashBoardController.searchState.value.searchStatus, controller.searchQuery) ..onOpenEmailAction((selectedEmail) => controller.previewEmail(context, selectedEmail)) - ..onSelectEmailAction((selectedEmail) => controller.selectEmail(context, selectedEmail)) - ..addOnMarkAsStarEmailActionClick((selectedEmail) => controller.markAsStarEmail(selectedEmail))) + ..onSelectEmailAction((selectedEmail) => controller.selectEmail(context, selectedEmail))) .build()), )); } diff --git a/lib/features/thread/presentation/widgets/app_bar_thread_widget_builder.dart b/lib/features/thread/presentation/widgets/app_bar_thread_widget_builder.dart index 9c9c19831..4f914a93b 100644 --- a/lib/features/thread/presentation/widgets/app_bar_thread_widget_builder.dart +++ b/lib/features/thread/presentation/widgets/app_bar_thread_widget_builder.dart @@ -7,22 +7,30 @@ import 'package:model/model.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; typedef OnFilterEmailAction = void Function(); +typedef OnEditThreadAction = void Function(); typedef OnOpenListMailboxActionClick = void Function(); +typedef OnCancelEditThread = void Function(); class AppBarThreadWidgetBuilder { OnFilterEmailAction? _onFilterEmailAction; OnOpenListMailboxActionClick? _onOpenListMailboxActionClick; + OnEditThreadAction? _onEditThreadAction; + OnCancelEditThread? _onCancelEditThread; final BuildContext _context; final ImagePaths _imagePaths; final ResponsiveUtils _responsiveUtils; final PresentationMailbox? _presentationMailbox; + final List _listSelectionEmail; + final SelectMode _selectMode; AppBarThreadWidgetBuilder( this._context, this._imagePaths, this._responsiveUtils, this._presentationMailbox, + this._listSelectionEmail, + this._selectMode, ); void addOnFilterEmailAction(OnFilterEmailAction onFilterEmailAction) { @@ -33,6 +41,14 @@ class AppBarThreadWidgetBuilder { _onOpenListMailboxActionClick = onOpenListMailboxActionClick; } + void addOnEditThreadAction(OnEditThreadAction onEditThreadAction) { + _onEditThreadAction = onEditThreadAction; + } + + void addOnCancelEditThread(OnCancelEditThread onCancelEditThread) { + _onCancelEditThread = onCancelEditThread; + } + Widget build() { return Container( key: Key('app_bar_thread_widget'), @@ -44,7 +60,9 @@ class AppBarThreadWidgetBuilder { data: MediaQueryData(padding: EdgeInsets.zero), child: Row( children: [ - _buildEditButton(), + if (_selectMode != SelectMode.ACTIVE) _buildEditButton(), + if (_selectMode == SelectMode.ACTIVE) _buildBackButton(), + if (_selectMode == SelectMode.ACTIVE) _buildCountItemSelected(), Expanded(child: _buildContentCenterAppBar()), _buildFilterButton(), ] @@ -60,7 +78,7 @@ class AppBarThreadWidgetBuilder { child: Padding( padding: EdgeInsets.only(right: 16), child: TextButton( - onPressed: () {}, + onPressed: () => _onEditThreadAction?.call(), child: Text( AppLocalizations.of(_context).edit, style: TextStyle(fontSize: 17, color: AppColor.colorTextButton), @@ -78,13 +96,40 @@ class AppBarThreadWidgetBuilder { padding: EdgeInsets.only(left: 16), child: IconButton( color: AppColor.baseTextColor, - icon: SvgPicture.asset(_imagePaths.icFilter, color: AppColor.baseTextColor, fit: BoxFit.fill), + icon: SvgPicture.asset(_imagePaths.icFilter, fit: BoxFit.fill), onPressed: () => _onFilterEmailAction?.call() ) ) ); } + Widget _buildBackButton() { + return Material( + borderRadius: BorderRadius.circular(12), + color: Colors.transparent, + child: IconButton( + color: AppColor.baseTextColor, + icon: SvgPicture.asset( + _imagePaths.icBack, + width: 20, + height: 20, + color: AppColor.colorTextButton, + fit: BoxFit.fill), + onPressed: () => _onCancelEditThread?.call() + ) + ); + } + + Widget _buildCountItemSelected() { + return Padding( + padding: EdgeInsets.zero, + child: Text( + '${_listSelectionEmail.length}', + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle(fontSize: 17, color: AppColor.colorTextButton))); + } + Widget _buildContentCenterAppBar() { return Row( mainAxisSize: MainAxisSize.min, diff --git a/lib/features/thread/presentation/widgets/bottom_bar_thread_selection_widget.dart b/lib/features/thread/presentation/widgets/bottom_bar_thread_selection_widget.dart new file mode 100644 index 000000000..3aa622287 --- /dev/null +++ b/lib/features/thread/presentation/widgets/bottom_bar_thread_selection_widget.dart @@ -0,0 +1,102 @@ +import 'package:core/core.dart'; +import 'package:flutter/material.dart'; +import 'package:model/model.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; + +typedef OnPressEmailSelectionActionClick = void Function(EmailActionType, List); + +class BottomBarThreadSelectionWidget { + + final BuildContext _context; + final ImagePaths _imagePaths; + final ResponsiveUtils _responsiveUtils; + final List _listSelectionEmail; + + OnPressEmailSelectionActionClick? _onPressEmailSelectionActionClick; + + BottomBarThreadSelectionWidget( + this._context, + this._imagePaths, + this._responsiveUtils, + this._listSelectionEmail, + ); + + void addOnPressEmailSelectionActionClick(OnPressEmailSelectionActionClick onPressEmailSelectionActionClick) { + _onPressEmailSelectionActionClick = onPressEmailSelectionActionClick; + } + + Widget build() { + return Container( + key: Key('bottom_bar_thread_selection_widget'), + alignment: Alignment.center, + color: Colors.white, + child: MediaQuery( + data: MediaQueryData(padding: EdgeInsets.zero), + child: _buildListOptionButton() + ) + ); + } + + Widget _buildListOptionButton() { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + (ButtonBuilder(_listSelectionEmail.isAllEmailRead ? _imagePaths.icUnreadV2 : _imagePaths.icReadV2) + ..key(Key('button_mark_read_email')) + ..paddingIcon(EdgeInsets.all(8)) + ..textStyle(TextStyle(fontSize: 12, color: AppColor.colorTextButton)) + ..onPressActionClick(() { + if (_onPressEmailSelectionActionClick != null) { + _onPressEmailSelectionActionClick!( + _listSelectionEmail.isAllEmailRead ? EmailActionType.markAsUnread : EmailActionType.markAsRead, + _listSelectionEmail); + }}) + ..text( + _listSelectionEmail.isAllEmailRead ? AppLocalizations.of(_context).unread : AppLocalizations.of(_context).read, + isVertical: _responsiveUtils.isMobile(_context))) + .build(), + (ButtonBuilder(_imagePaths.icFlagV2) + ..key(Key('button_flag_email')) + ..paddingIcon(EdgeInsets.all(8)) + ..textStyle(TextStyle(fontSize: 12, color: AppColor.colorTextButton)) + ..onPressActionClick(() { + if (_onPressEmailSelectionActionClick != null) { + _onPressEmailSelectionActionClick!(EmailActionType.markAsFlag, _listSelectionEmail); + }}) + ..text(AppLocalizations.of(_context).flag, isVertical: _responsiveUtils.isMobile(_context))) + .build(), + (ButtonBuilder(_imagePaths.icMoveV2) + ..key(Key('button_move_email')) + ..paddingIcon(EdgeInsets.all(8)) + ..textStyle(TextStyle(fontSize: 12, color: AppColor.colorTextButton)) + ..onPressActionClick(() { + if (_onPressEmailSelectionActionClick != null) { + _onPressEmailSelectionActionClick!(EmailActionType.move, _listSelectionEmail); + }}) + ..text(AppLocalizations.of(_context).move, isVertical: _responsiveUtils.isMobile(_context))) + .build(), + (ButtonBuilder(_imagePaths.icSpamV2) + ..key(Key('button_spam_email')) + ..paddingIcon(EdgeInsets.all(8)) + ..textStyle(TextStyle(fontSize: 12, color: AppColor.colorTextButton)) + ..onPressActionClick(() { + if (_onPressEmailSelectionActionClick != null) { + _onPressEmailSelectionActionClick!(EmailActionType.markAsSpam, _listSelectionEmail); + }}) + ..text(AppLocalizations.of(_context).spam, isVertical: _responsiveUtils.isMobile(_context))) + .build(), + (ButtonBuilder(_imagePaths.icDeleteV2) + ..key(Key('button_delete_email')) + ..paddingIcon(EdgeInsets.all(8)) + ..textStyle(TextStyle(fontSize: 12, color: AppColor.colorTextButton)) + ..onPressActionClick(() { + if (_onPressEmailSelectionActionClick != null) { + _onPressEmailSelectionActionClick!(EmailActionType.delete, _listSelectionEmail); + }}) + ..text(AppLocalizations.of(_context).delete, isVertical: _responsiveUtils.isMobile(_context))) + .build() + ] + ); + } +} \ No newline at end of file diff --git a/lib/features/thread/presentation/widgets/email_tile_builder.dart b/lib/features/thread/presentation/widgets/email_tile_builder.dart index 6b29a0577..14e474b2e 100644 --- a/lib/features/thread/presentation/widgets/email_tile_builder.dart +++ b/lib/features/thread/presentation/widgets/email_tile_builder.dart @@ -10,7 +10,6 @@ import 'package:tmail_ui_user/features/thread/presentation/model/search_status.d typedef OnOpenEmailActionClick = void Function(PresentationEmail selectedEmail); typedef OnSelectEmailActionClick = void Function(PresentationEmail selectedEmail); -typedef OnMarkAsStarEmailActionClick = void Function(PresentationEmail selectedEmail); class EmailTileBuilder { @@ -24,7 +23,6 @@ class EmailTileBuilder { OnOpenEmailActionClick? _onOpenEmailActionClick; OnSelectEmailActionClick? _onSelectEmailActionClick; - OnMarkAsStarEmailActionClick? _onMarkAsStarEmailActionClick; EmailTileBuilder( this._context, @@ -44,10 +42,6 @@ class EmailTileBuilder { _onSelectEmailActionClick = onSelectEmailActionClick; } - void addOnMarkAsStarEmailActionClick(OnMarkAsStarEmailActionClick onMarkAsStarEmailActionClick) { - _onMarkAsStarEmailActionClick = onMarkAsStarEmailActionClick; - } - Widget build() { return Theme( data: ThemeData( @@ -104,7 +98,7 @@ class EmailTileBuilder { Padding( padding: EdgeInsets.only(left: 8), child: (ButtonBuilder(_imagePaths.icAttachment) - ..padding(0) + ..paddingIcon(EdgeInsets.zero) ..size(16)) .build()), Padding( @@ -115,7 +109,7 @@ class EmailTileBuilder { overflow:TextOverflow.ellipsis, style: TextStyle(fontSize: 13, color: AppColor.colorContentEmail))), (ButtonBuilder(_imagePaths.icChevron) - ..padding(0) + ..paddingIcon(EdgeInsets.zero) ..size(16)) .build(), ], @@ -162,7 +156,7 @@ class EmailTileBuilder { ), if (_presentationEmail.isFlaggedEmail() ) (ButtonBuilder(_imagePaths.icStar) - ..padding(0) + ..paddingIcon(EdgeInsets.zero) ..size(15)) .build(), ], @@ -201,24 +195,15 @@ class EmailTileBuilder { return AnimatedSwitcher( duration: Duration(milliseconds: 600), transitionBuilder: _transitionBuilder, - child: _presentationEmail.selectMode == SelectMode.ACTIVE - ? (IconBuilder(_imagePaths.icSelected) - ..addOnTapActionClick(() { - if (_selectModeAll == SelectMode.ACTIVE && _onSelectEmailActionClick != null) { - _onSelectEmailActionClick!(_presentationEmail); - }})) - .build() - : (AvatarBuilder() - ..text('${_presentationEmail.getAvatarText()}') - ..size(56) - ..textColor(Colors.white) - ..avatarColor(_presentationEmail.avatarColors) - // .iconStatus(_imagePaths.icOffline) - ..addOnTapActionClick(() { - if (_selectModeAll == SelectMode.ACTIVE && _onSelectEmailActionClick != null) { - _onSelectEmailActionClick!(_presentationEmail); - }})) - .build() + child: (IconBuilder(_presentationEmail.selectMode == SelectMode.ACTIVE ? _imagePaths.icSelectedV2 : _imagePaths.icUnSelectedV2) + ..padding(EdgeInsets.all(14)) + ..size(55) + ..addOnTapActionClick(() { + if (_selectModeAll == SelectMode.ACTIVE && _onSelectEmailActionClick != null) { + _onSelectEmailActionClick!(_presentationEmail); + }})) + .build() + ); } else { return (AvatarBuilder() diff --git a/lib/main/localizations/app_localizations.dart b/lib/main/localizations/app_localizations.dart index aec86df02..83ed644d9 100644 --- a/lib/main/localizations/app_localizations.dart +++ b/lib/main/localizations/app_localizations.dart @@ -476,5 +476,46 @@ class AppLocalizations { 'Compose', name: 'compose'); } -} + String get delete { + return Intl.message( + 'Delete', + name: 'delete'); + } + + String get move { + return Intl.message( + 'Move', + name: 'move'); + } + + String get spam { + return Intl.message( + 'Spam', + name: 'spam'); + } + + String get flag { + return Intl.message( + 'Flag', + name: 'flag'); + } + + String get read { + return Intl.message( + 'Read', + name: 'read'); + } + + String get unread { + return Intl.message( + 'Unread', + name: 'unread'); + } + + String get the_feature_is_under_development { + return Intl.message( + 'This feature is under development.', + name: 'the_feature_is_under_development'); + } +} \ No newline at end of file diff --git a/model/lib/email/email_action_type.dart b/model/lib/email/email_action_type.dart index 5009b6ebc..c6e042fb6 100644 --- a/model/lib/email/email_action_type.dart +++ b/model/lib/email/email_action_type.dart @@ -4,4 +4,10 @@ enum EmailActionType { forward, replyAll, compose, + markAsRead, + markAsUnread, + markAsFlag, + move, + markAsSpam, + delete, } \ No newline at end of file diff --git a/model/lib/extensions/list_presentation_email_extension.dart b/model/lib/extensions/list_presentation_email_extension.dart new file mode 100644 index 000000000..8acb693de --- /dev/null +++ b/model/lib/extensions/list_presentation_email_extension.dart @@ -0,0 +1,6 @@ + +import 'package:model/email/presentation_email.dart'; + +extension ListPresentationEmailExtension on List { + bool get isAllEmailRead => every((email) => email.isReadEmail()); +} \ No newline at end of file diff --git a/model/lib/model.dart b/model/lib/model.dart index 606bf7ce0..53b52546d 100644 --- a/model/lib/model.dart +++ b/model/lib/model.dart @@ -53,6 +53,7 @@ export 'extensions/list_extension.dart'; export 'extensions/list_email_extension.dart'; export 'extensions/media_type_extension.dart'; export 'extensions/list_attachment_extension.dart'; +export 'extensions/list_presentation_email_extension.dart'; // Download export 'download/download_task_id.dart';