TF-4002 Apply new design for folder creation modal on web

This commit is contained in:
dab246
2025-09-04 15:06:28 +07:00
committed by Dat H. Pham
parent c09a2c92b2
commit 36e58fc990
23 changed files with 689 additions and 498 deletions
@@ -52,24 +52,20 @@ class _TreeViewData extends StatelessWidget {
} }
class TreeViewChild { class TreeViewChild {
final BuildContext context;
final bool? isExpanded; final bool? isExpanded;
final Widget parent; final Widget parent;
final List<Widget> children; final List<Widget> children;
final VoidCallback? onTap; final VoidCallback? onTap;
final EdgeInsetsGeometry? paddingChild; final EdgeInsetsGeometry? paddingChild;
TreeViewChild( TreeViewChild({
this.context, required this.parent,
{ required this.children,
required this.parent, this.isExpanded,
required this.children, this.onTap,
this.isExpanded, this.paddingChild,
this.onTap, Key? key,
this.paddingChild, });
Key? key,
}
);
Widget build() { Widget build() {
return Column( return Column(
+3 -39
View File
@@ -19,6 +19,7 @@ import 'package:tmail_ui_user/features/base/action/update_mailbox_properties_act
import 'package:tmail_ui_user/features/base/action/update_mailbox_properties_action/update_mailbox_total_emails_count_action.dart'; import 'package:tmail_ui_user/features/base/action/update_mailbox_properties_action/update_mailbox_total_emails_count_action.dart';
import 'package:tmail_ui_user/features/base/action/update_mailbox_properties_action/update_mailbox_unread_count_action.dart'; import 'package:tmail_ui_user/features/base/action/update_mailbox_properties_action/update_mailbox_unread_count_action.dart';
import 'package:tmail_ui_user/features/base/base_controller.dart'; import 'package:tmail_ui_user/features/base/base_controller.dart';
import 'package:tmail_ui_user/features/base/mixin/expand_folder_trigger_scrollable_mixin.dart';
import 'package:tmail_ui_user/features/base/mixin/message_dialog_action_manager.dart'; import 'package:tmail_ui_user/features/base/mixin/message_dialog_action_manager.dart';
import 'package:tmail_ui_user/features/destination_picker/presentation/model/destination_picker_arguments.dart'; import 'package:tmail_ui_user/features/destination_picker/presentation/model/destination_picker_arguments.dart';
import 'package:tmail_ui_user/features/mailbox/domain/model/mailbox_subscribe_action_state.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/mailbox_subscribe_action_state.dart';
@@ -55,7 +56,8 @@ typedef MovingMailboxActionCallback = void Function(PresentationMailbox mailboxS
typedef DeleteMailboxActionCallback = void Function(PresentationMailbox mailbox); typedef DeleteMailboxActionCallback = void Function(PresentationMailbox mailbox);
typedef AllowSubaddressingActionCallback = void Function(MailboxId, Map<String, List<String>?>?, MailboxActions); typedef AllowSubaddressingActionCallback = void Function(MailboxId, Map<String, List<String>?>?, MailboxActions);
abstract class BaseMailboxController extends BaseController { abstract class BaseMailboxController extends BaseController
with ExpandFolderTriggerScrollableMixin {
final TreeBuilder _treeBuilder; final TreeBuilder _treeBuilder;
final VerifyNameInteractor verifyNameInteractor; final VerifyNameInteractor verifyNameInteractor;
final GetAllMailboxInteractor? getAllMailboxInteractor; final GetAllMailboxInteractor? getAllMailboxInteractor;
@@ -159,44 +161,6 @@ abstract class BaseMailboxController extends BaseController {
} }
} }
void triggerScrollWhenExpandFolder(
ExpandMode expandMode,
GlobalKey itemKey,
ScrollController scrollController,
) {
if (expandMode == ExpandMode.COLLAPSE) return;
final context = itemKey.currentContext;
if (context == null) return;
WidgetsBinding.instance.addPostFrameCallback((_) {
_scrollToSelfIfNeeded(context, scrollController);
});
}
void _scrollToSelfIfNeeded(
BuildContext context,
ScrollController scrollController,
) {
final renderBox = context.findRenderObject() as RenderBox?;
if (renderBox == null) return;
final position = renderBox.localToGlobal(Offset.zero);
final screenHeight = MediaQuery.of(context).size.height;
const offsetY = 200;
final bottomY = position.dy + renderBox.size.height + offsetY;
log('BaseMailboxController::_scrollToSelfIfNeeded:position = $position |screenHeight = $screenHeight | bottomY = $bottomY');
if (bottomY > screenHeight) {
final scrollOffset = scrollController.offset + bottomY - screenHeight + 40;
log('BaseMailboxController::_scrollToSelfIfNeeded:scrollOffset = $scrollOffset:');
scrollController.animateTo(
scrollOffset,
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
}
}
void selectMailboxNode(MailboxNode mailboxNodeSelected) { void selectMailboxNode(MailboxNode mailboxNodeSelected) {
final newSelectMode = mailboxNodeSelected.selectMode == SelectMode.INACTIVE final newSelectMode = mailboxNodeSelected.selectMode == SelectMode.INACTIVE
? SelectMode.ACTIVE ? SelectMode.ACTIVE
@@ -0,0 +1,44 @@
import 'package:core/utils/app_logger.dart';
import 'package:flutter/material.dart';
import 'package:model/mailbox/expand_mode.dart';
mixin ExpandFolderTriggerScrollableMixin {
void triggerScrollWhenExpandFolder(
ExpandMode expandMode,
GlobalKey itemKey,
ScrollController scrollController,
) {
if (expandMode == ExpandMode.COLLAPSE) return;
final context = itemKey.currentContext;
if (context == null) return;
WidgetsBinding.instance.addPostFrameCallback((_) {
_scrollToSelfIfNeeded(context, scrollController);
});
}
void _scrollToSelfIfNeeded(
BuildContext context,
ScrollController scrollController,
) {
final renderBox = context.findRenderObject() as RenderBox?;
if (renderBox == null) return;
final position = renderBox.localToGlobal(Offset.zero);
final screenHeight = MediaQuery.of(context).size.height;
const offsetY = 200;
final bottomY = position.dy + renderBox.size.height + offsetY;
log('$runtimeType::_scrollToSelfIfNeeded:position = $position |screenHeight = $screenHeight | bottomY = $bottomY');
if (bottomY > screenHeight) {
final scrollOffset =
scrollController.offset + bottomY - screenHeight + 40;
log('$runtimeType::_scrollToSelfIfNeeded:scrollOffset = $scrollOffset:');
scrollController.animateTo(
scrollOffset,
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
}
}
}
@@ -7,12 +7,14 @@ class DefaultButtonArrowDownFieldWidget extends StatelessWidget {
final String text; final String text;
final String iconArrowDown; final String iconArrowDown;
final VoidCallback onTap; final VoidCallback onTap;
final Widget? icon;
const DefaultButtonArrowDownFieldWidget({ const DefaultButtonArrowDownFieldWidget({
super.key, super.key,
required this.text, required this.text,
required this.iconArrowDown, required this.iconArrowDown,
required this.onTap, required this.onTap,
this.icon,
}); });
@override @override
@@ -35,6 +37,7 @@ class DefaultButtonArrowDownFieldWidget extends StatelessWidget {
padding: const EdgeInsetsDirectional.only(start: 12, end: 8), padding: const EdgeInsetsDirectional.only(start: 12, end: 8),
child: Row( child: Row(
children: [ children: [
if (icon != null) icon!,
Expanded( Expanded(
child: Text( child: Text(
text, text,
@@ -0,0 +1,58 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/views/dialog/confirm_dialog_button.dart';
import 'package:flutter/material.dart';
class ModalListActionButtonWidget extends StatelessWidget {
final String positiveLabel;
final String negativeLabel;
final VoidCallback onNegativeAction;
final VoidCallback onPositiveAction;
final EdgeInsetsGeometry? padding;
const ModalListActionButtonWidget({
super.key,
required this.positiveLabel,
required this.negativeLabel,
required this.onPositiveAction,
required this.onNegativeAction,
this.padding,
});
@override
Widget build(BuildContext context) {
Widget negativeButton = Container(
constraints: const BoxConstraints(minWidth: 67),
height: 48,
child: ConfirmDialogButton(
label: negativeLabel,
onTapAction: onNegativeAction,
),
);
Widget positiveButton = Container(
constraints: const BoxConstraints(minWidth: 153),
height: 48,
child: ConfirmDialogButton(
label: positiveLabel,
backgroundColor: AppColor.primaryMain,
textColor: Colors.white,
onTapAction: onPositiveAction,
),
);
Widget bodyWidget = Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Flexible(child: negativeButton),
const SizedBox(width: 8),
Flexible(child: positiveButton),
],
);
if (padding != null) {
return Padding(padding: padding!, child: bodyWidget);
} else {
return bodyWidget;
}
}
}
@@ -32,7 +32,7 @@ import 'package:tmail_ui_user/features/mailbox/presentation/styles/mailbox_icon_
import 'package:tmail_ui_user/features/mailbox/presentation/styles/mailbox_item_widget_styles.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/styles/mailbox_item_widget_styles.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/widgets/mailbox_category_widget.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/widgets/mailbox_category_widget.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/widgets/mailbox_item_widget.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/widgets/mailbox_item_widget.dart';
import 'package:tmail_ui_user/features/mailbox_creator/presentation/widgets/create_mailbox_name_input_decoration_builder.dart'; import 'package:tmail_ui_user/features/destination_picker/presentation/widgets/create_mailbox_name_input_decoration_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_app_bar_widget.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
import 'package:tmail_ui_user/main/utils/app_utils.dart'; import 'package:tmail_ui_user/main/utils/app_utils.dart';
@@ -413,7 +413,6 @@ class DestinationPickerView extends GetWidget<DestinationPickerController>
?.map((mailboxNode) { ?.map((mailboxNode) {
if (mailboxNode.hasChildren()) { if (mailboxNode.hasChildren()) {
return TreeViewChild( return TreeViewChild(
context,
key: const Key('children_tree_mailbox_child'), key: const Key('children_tree_mailbox_child'),
isExpanded: mailboxNode.expandMode == ExpandMode.EXPAND, isExpanded: mailboxNode.expandMode == ExpandMode.EXPAND,
paddingChild: const EdgeInsetsDirectional.only(start: 14), paddingChild: const EdgeInsetsDirectional.only(start: 14),
@@ -422,7 +421,9 @@ class DestinationPickerView extends GetWidget<DestinationPickerController>
mailboxActions: actions, mailboxActions: actions,
mailboxIdAlreadySelected: mailboxIdSelected, mailboxIdAlreadySelected: mailboxIdSelected,
mailboxDisplayed: MailboxDisplayed.destinationPicker, mailboxDisplayed: MailboxDisplayed.destinationPicker,
onOpenMailboxFolderClick: (node) => _pickMailboxNode(context, node), onOpenMailboxFolderClick: (node) => node != null
? _pickMailboxNode(context, node)
: null,
onExpandFolderActionClick: (mailboxNode, itemKey) => onExpandFolderActionClick: (mailboxNode, itemKey) =>
controller.toggleMailboxFolder( controller.toggleMailboxFolder(
mailboxNode, mailboxNode,
@@ -443,7 +444,9 @@ class DestinationPickerView extends GetWidget<DestinationPickerController>
mailboxDisplayed: MailboxDisplayed.destinationPicker, mailboxDisplayed: MailboxDisplayed.destinationPicker,
mailboxIdAlreadySelected: mailboxIdSelected, mailboxIdAlreadySelected: mailboxIdSelected,
mailboxActions: actions, mailboxActions: actions,
onOpenMailboxFolderClick: (node) => _pickMailboxNode(context, node), onOpenMailboxFolderClick: (node) => node != null
? _pickMailboxNode(context, node)
: null,
); );
}}) }})
.toList() ?? <Widget>[]; .toList() ?? <Widget>[];
@@ -199,7 +199,9 @@ abstract class BaseMailboxView extends GetWidget<MailboxController>
.selectedMailbox .selectedMailbox
.value, .value,
onOpenMailboxFolderClick: (mailboxNode) => onOpenMailboxFolderClick: (mailboxNode) =>
controller.openMailbox(context, mailboxNode.item), mailboxNode != null
? controller.openMailbox(context, mailboxNode.item)
: null,
onExpandFolderActionClick: mailboxNode.hasChildren() onExpandFolderActionClick: mailboxNode.hasChildren()
? (mailboxNode, itemKey) => controller.toggleMailboxFolder( ? (mailboxNode, itemKey) => controller.toggleMailboxFolder(
mailboxNode, mailboxNode,
@@ -228,7 +230,6 @@ abstract class BaseMailboxView extends GetWidget<MailboxController>
if (mailboxNode.hasChildren()) { if (mailboxNode.hasChildren()) {
return TreeViewChild( return TreeViewChild(
context,
key: const Key('children_tree_mailbox_child'), key: const Key('children_tree_mailbox_child'),
isExpanded: mailboxNode.expandMode == ExpandMode.EXPAND, isExpanded: mailboxNode.expandMode == ExpandMode.EXPAND,
paddingChild: const EdgeInsetsDirectional.only(start: 12), paddingChild: const EdgeInsetsDirectional.only(start: 12),
@@ -843,15 +843,12 @@ class MailboxController extends BaseMailboxController
} }
void goToCreateNewMailboxView(BuildContext context, {PresentationMailbox? parentMailbox}) async { void goToCreateNewMailboxView(BuildContext context, {PresentationMailbox? parentMailbox}) async {
if (session !=null && accountId != null) { if (session != null && accountId != null) {
final arguments = MailboxCreatorArguments( final arguments = MailboxCreatorArguments(
accountId!, defaultMailboxTree.value,
defaultMailboxTree.value, personalMailboxTree.value,
personalMailboxTree.value, parentMailbox,
teamMailboxesTree.value, );
mailboxDashBoardController.sessionCurrent!,
parentMailbox
);
final result = PlatformInfo.isWeb final result = PlatformInfo.isWeb
? await DialogRouter.pushGeneralDialog(routeName: AppRoutes.mailboxCreator, arguments: arguments) ? await DialogRouter.pushGeneralDialog(routeName: AppRoutes.mailboxCreator, arguments: arguments)
@@ -1,5 +1,6 @@
enum MailboxDisplayed { enum MailboxDisplayed {
mailbox, mailbox,
destinationPicker destinationPicker,
modalFolder;
} }
@@ -11,7 +11,7 @@ typedef OnDragEmailToMailboxAccepted = void Function(List<PresentationEmail>, Pr
typedef OnLongPressMailboxAction = void Function(PresentationMailbox); typedef OnLongPressMailboxAction = void Function(PresentationMailbox);
typedef OnClickExpandMailboxNodeAction = void Function(MailboxNode, GlobalKey itemKey); typedef OnClickExpandMailboxNodeAction = void Function(MailboxNode, GlobalKey itemKey);
typedef OnClickOpenMailboxNodeAction = void Function(MailboxNode); typedef OnClickOpenMailboxNodeAction = void Function(MailboxNode?);
typedef OnSelectMailboxNodeAction = void Function(MailboxNode); typedef OnSelectMailboxNodeAction = void Function(MailboxNode);
typedef OnClickOpenMenuMailboxNodeAction = Future<void> Function(RelativeRect, MailboxNode); typedef OnClickOpenMenuMailboxNodeAction = Future<void> Function(RelativeRect, MailboxNode);
typedef OnLongPressMailboxNodeAction = void Function(MailboxNode); typedef OnLongPressMailboxNodeAction = void Function(MailboxNode);
@@ -0,0 +1,84 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/utils/theme_utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/styles/mailbox_icon_widget_styles.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/styles/mailbox_item_widget_styles.dart';
class FolderItemWidget extends StatelessWidget {
final ImagePaths imagePaths;
final String folderName;
final bool isSelected;
final VoidCallback? onTapAction;
const FolderItemWidget({
super.key,
required this.imagePaths,
required this.folderName,
this.isSelected = false,
this.onTapAction,
});
@override
Widget build(BuildContext context) {
Widget itemWidget = Container(
padding: const EdgeInsetsDirectional.symmetric(
horizontal: MailboxItemWidgetStyles.itemPadding,
),
height: MailboxItemWidgetStyles.height,
child: Row(
children: [
SvgPicture.asset(
imagePaths.icFolderMailbox,
width: MailboxIconWidgetStyles.iconSize,
height: MailboxIconWidgetStyles.iconSize,
colorFilter: AppColor.primaryLinShare.asFilter(),
fit: BoxFit.fill,
),
const SizedBox(width: MailboxItemWidgetStyles.labelIconSpace),
Expanded(
child: Text(
folderName,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: isSelected
? ThemeUtils.textStyleInter700(
color: Colors.black,
fontSize: 14,
)
: ThemeUtils.textStyleBodyBody3(
color: Colors.black,
),
),
),
const SizedBox(width: MailboxItemWidgetStyles.labelIconSpace),
if (isSelected)
SvgPicture.asset(
imagePaths.icCheck,
width: MailboxIconWidgetStyles.iconSize,
height: MailboxIconWidgetStyles.iconSize,
colorFilter: AppColor.primaryLinShare.asFilter(),
fit: BoxFit.fill,
),
],
),
);
if (onTapAction != null) {
return Material(
type: MaterialType.transparency,
child: InkWell(
borderRadius: const BorderRadius.all(
Radius.circular(MailboxItemWidgetStyles.borderRadius),
),
hoverColor: AppColor.blue100,
onTap: onTapAction,
child: itemWidget,
),
);
} else {
return itemWidget;
}
}
}
@@ -24,6 +24,7 @@ class LabelMailboxItemWidget extends StatefulWidget {
final bool showTrailing; final bool showTrailing;
final bool isItemHovered; final bool isItemHovered;
final bool isSelected; final bool isSelected;
final bool isSelectedFolderInModal;
final OnClickOpenMenuMailboxNodeAction? onMenuActionClick; final OnClickOpenMenuMailboxNodeAction? onMenuActionClick;
final OnEmptyMailboxActionCallback? onEmptyMailboxActionCallback; final OnEmptyMailboxActionCallback? onEmptyMailboxActionCallback;
final OnClickExpandMailboxNodeAction? onClickExpandMailboxNodeAction; final OnClickExpandMailboxNodeAction? onClickExpandMailboxNodeAction;
@@ -35,6 +36,7 @@ class LabelMailboxItemWidget extends StatefulWidget {
this.showTrailing = true, this.showTrailing = true,
this.isItemHovered = false, this.isItemHovered = false,
this.isSelected = false, this.isSelected = false,
this.isSelectedFolderInModal = false,
this.onMenuActionClick, this.onMenuActionClick,
this.onEmptyMailboxActionCallback, this.onEmptyMailboxActionCallback,
this.onClickExpandMailboxNodeAction, this.onClickExpandMailboxNodeAction,
@@ -219,6 +221,11 @@ class _LabelMailboxItemWidgetState extends State<LabelMailboxItemWidget> {
color: _responsiveUtils.isDesktop(context) ? null : AppColor.iconFolder, color: _responsiveUtils.isDesktop(context) ? null : AppColor.iconFolder,
fontSize: 14, fontSize: 14,
); );
} else if (widget.isSelectedFolderInModal) {
return ThemeUtils.textStyleInter700(
color: Colors.black,
fontSize: 14,
);
} else { } else {
return _responsiveUtils.isWebDesktop(context) return _responsiveUtils.isWebDesktop(context)
? ThemeUtils.textStyleBodyBody3(color: Colors.black) ? ThemeUtils.textStyleBodyBody3(color: Colors.black)
@@ -25,6 +25,7 @@ class MailboxItemWidget extends StatefulWidget {
final PresentationMailbox? mailboxNodeSelected; final PresentationMailbox? mailboxNodeSelected;
final MailboxActions? mailboxActions; final MailboxActions? mailboxActions;
final MailboxId? mailboxIdAlreadySelected; final MailboxId? mailboxIdAlreadySelected;
final Color? hoverColor;
final OnClickExpandMailboxNodeAction? onExpandFolderActionClick; final OnClickExpandMailboxNodeAction? onExpandFolderActionClick;
final OnClickOpenMailboxNodeAction? onOpenMailboxFolderClick; final OnClickOpenMailboxNodeAction? onOpenMailboxFolderClick;
@@ -41,6 +42,7 @@ class MailboxItemWidget extends StatefulWidget {
this.mailboxNodeSelected, this.mailboxNodeSelected,
this.mailboxActions, this.mailboxActions,
this.mailboxIdAlreadySelected, this.mailboxIdAlreadySelected,
this.hoverColor,
this.onExpandFolderActionClick, this.onExpandFolderActionClick,
this.onOpenMailboxFolderClick, this.onOpenMailboxFolderClick,
this.onSelectMailboxFolderClick, this.onSelectMailboxFolderClick,
@@ -229,11 +231,11 @@ class _MailboxItemWidgetState extends State<MailboxItemWidget> {
child: Material( child: Material(
color: Colors.transparent, color: Colors.transparent,
child: InkWell( child: InkWell(
onTap: () => !_isSelectActionNoValid onTap: !isSelectedFolderInModal
? widget.onOpenMailboxFolderClick?.call(widget.mailboxNode) ? () => widget.onOpenMailboxFolderClick?.call(widget.mailboxNode)
: null, : null,
customBorder: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(8))), customBorder: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(8))),
hoverColor: AppColor.colorMailboxHovered, hoverColor: widget.hoverColor ?? AppColor.colorMailboxHovered,
child: Container( child: Container(
padding: const EdgeInsetsDirectional.symmetric( padding: const EdgeInsetsDirectional.symmetric(
horizontal: MailboxItemWidgetStyles.itemPadding, horizontal: MailboxItemWidgetStyles.itemPadding,
@@ -255,14 +257,17 @@ class _MailboxItemWidgetState extends State<MailboxItemWidget> {
showTrailing: false, showTrailing: false,
isSelected: _isSelected, isSelected: _isSelected,
isItemHovered: _isItemHovered, isItemHovered: _isItemHovered,
isSelectedFolderInModal: isSelectedFolderInModal,
onMenuActionClick: widget.onMenuActionClick, onMenuActionClick: widget.onMenuActionClick,
onEmptyMailboxActionCallback: widget.onEmptyMailboxActionCallback, onEmptyMailboxActionCallback: widget.onEmptyMailboxActionCallback,
onClickExpandMailboxNodeAction: widget.onExpandFolderActionClick, onClickExpandMailboxNodeAction: widget.onExpandFolderActionClick,
) )
), ),
if (_isSelectActionNoValid) if (isSelectedFolderInModal)
SvgPicture.asset( SvgPicture.asset(
_imagePaths.icSelectedSB, isFolderModalActive
? _imagePaths.icCheck
: _imagePaths.icSelectedSB,
width: MailboxItemWidgetStyles.selectionIconSize, width: MailboxItemWidgetStyles.selectionIconSize,
height: MailboxItemWidgetStyles.selectionIconSize, height: MailboxItemWidgetStyles.selectionIconSize,
fit: BoxFit.fill fit: BoxFit.fill
@@ -282,24 +287,25 @@ class _MailboxItemWidgetState extends State<MailboxItemWidget> {
widget.mailboxNodeSelected?.id == widget.mailboxNode.item.id; widget.mailboxNodeSelected?.id == widget.mailboxNode.item.id;
Color get backgroundColorItem { Color get backgroundColorItem {
if (widget.mailboxDisplayed == MailboxDisplayed.destinationPicker) { if (widget.mailboxDisplayed == MailboxDisplayed.mailbox) {
return Colors.white; return _isSelected ? AppColor.blue100 : Colors.transparent;
} else {
if (_isSelected) {
return AppColor.blue100;
} else {
return Colors.transparent;
}
} }
return Colors.white;
} }
bool get _isSelectActionNoValid => widget.mailboxNode.item.id == widget.mailboxIdAlreadySelected && bool get isFolderModalActive =>
widget.mailboxDisplayed == MailboxDisplayed.destinationPicker && widget.mailboxDisplayed == MailboxDisplayed.modalFolder;
(
widget.mailboxActions == MailboxActions.select || bool get isSelectedFolderInModal {
widget.mailboxActions == MailboxActions.create || final isDestinationPickerActive =
widget.mailboxActions == MailboxActions.moveEmail widget.mailboxDisplayed == MailboxDisplayed.destinationPicker &&
); (widget.mailboxActions == MailboxActions.select ||
widget.mailboxActions == MailboxActions.create ||
widget.mailboxActions == MailboxActions.moveEmail);
return widget.mailboxNode.item.id == widget.mailboxIdAlreadySelected &&
(isDestinationPickerActive || isFolderModalActive);
}
bool get _isIconDisplayed => bool get _isIconDisplayed =>
widget.mailboxNode.item.isPersonal || widget.mailboxNode.item.isPersonal ||
@@ -1,14 +1,10 @@
import 'package:core/core.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
import 'package:model/model.dart'; import 'package:model/model.dart';
import 'package:tmail_ui_user/features/base/base_controller.dart'; import 'package:tmail_ui_user/features/base/base_controller.dart';
import 'package:tmail_ui_user/features/destination_picker/presentation/model/destination_picker_arguments.dart'; import 'package:tmail_ui_user/features/base/mixin/expand_folder_trigger_scrollable_mixin.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/extensions/expand_mode_extension.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_node.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_node.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_tree.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_tree.dart';
import 'package:tmail_ui_user/features/mailbox_creator/domain/model/verification/duplicate_name_validator.dart'; import 'package:tmail_ui_user/features/mailbox_creator/domain/model/verification/duplicate_name_validator.dart';
@@ -20,27 +16,24 @@ import 'package:tmail_ui_user/features/mailbox_creator/domain/usecases/verify_na
import 'package:tmail_ui_user/features/mailbox_creator/presentation/extensions/validator_failure_extension.dart'; import 'package:tmail_ui_user/features/mailbox_creator/presentation/extensions/validator_failure_extension.dart';
import 'package:tmail_ui_user/features/mailbox_creator/presentation/model/mailbox_creator_arguments.dart'; import 'package:tmail_ui_user/features/mailbox_creator/presentation/model/mailbox_creator_arguments.dart';
import 'package:tmail_ui_user/features/mailbox_creator/presentation/model/new_mailbox_arguments.dart'; import 'package:tmail_ui_user/features/mailbox_creator/presentation/model/new_mailbox_arguments.dart';
import 'package:tmail_ui_user/main/routes/app_routes.dart';
import 'package:tmail_ui_user/main/routes/dialog_router.dart';
import 'package:tmail_ui_user/main/routes/route_navigation.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart';
class MailboxCreatorController extends BaseController { class MailboxCreatorController extends BaseController
with ExpandFolderTriggerScrollableMixin {
final VerifyNameInteractor _verifyNameInteractor; final VerifyNameInteractor _verifyNameInteractor;
final selectedMailbox = Rxn<PresentationMailbox>(); final selectedMailbox = Rxn<PresentationMailbox>();
final newNameMailbox = Rxn<String>(); final newNameMailbox = Rxn<String>();
final isFolderModalEnabled = RxBool(false);
bool _createdMailbox = false; bool _createdMailbox = false;
final personalMailboxTree = MailboxTree(MailboxNode.root()).obs;
final defaultMailboxTree = MailboxTree(MailboxNode.root()).obs;
final FocusNode nameInputFocusNode = FocusNode(); final FocusNode nameInputFocusNode = FocusNode();
final TextEditingController nameInputController = TextEditingController(); final TextEditingController nameInputController = TextEditingController();
final ScrollController listFolderScrollController = ScrollController();
MailboxCreatorArguments? arguments;
AccountId? accountId;
Session? _session;
MailboxTree? defaultMailboxTree;
MailboxTree? personalMailboxTree;
MailboxTree? teamMailboxesTre;
List<String> listMailboxNameAsStringExist = <String>[]; List<String> listMailboxNameAsStringExist = <String>[];
@@ -51,47 +44,35 @@ class MailboxCreatorController extends BaseController {
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
log('MailboxCreatorController::onInit():arguments: ${Get.arguments}'); MailboxCreatorArguments? arguments = Get.arguments;
arguments = Get.arguments;
}
@override
void onReady() {
super.onReady();
log('MailboxCreatorController::onReady():');
if (arguments != null) { if (arguments != null) {
personalMailboxTree = arguments!.personalMailboxTree; personalMailboxTree.value = arguments.personalMailboxTree;
defaultMailboxTree = arguments!.defaultMailboxTree; defaultMailboxTree.value = arguments.defaultMailboxTree;
teamMailboxesTre = arguments!.teamMailboxesTree; selectedMailbox.value = arguments.selectedMailbox;
accountId = arguments!.accountId;
_session = arguments!.session;
if (arguments!.selectedMailbox != null) {
selectedMailbox.value = arguments!.selectedMailbox;
}
_createListMailboxNameAsStringInMailboxLocation(); _createListMailboxNameAsStringInMailboxLocation();
} }
} }
@override @override
void onClose() { void onClose() {
log('MailboxCreatorController::onClose():'); listMailboxNameAsStringExist = [];
_createdMailbox = false;
nameInputFocusNode.dispose(); nameInputFocusNode.dispose();
nameInputController.dispose(); nameInputController.dispose();
listFolderScrollController.dispose();
super.onClose(); super.onClose();
} }
MailboxNode? _findMailboxNodeById(MailboxId mailboxId) { MailboxNode? _findMailboxNodeById(MailboxId mailboxId) {
final mailboxNode = defaultMailboxTree?.findNode((node) => node.item.id == mailboxId) final mailboxNode = defaultMailboxTree.value.findNode((node) => node.item.id == mailboxId)
?? personalMailboxTree?.findNode((node) => node.item.id == mailboxId) ?? personalMailboxTree.value.findNode((node) => node.item.id == mailboxId);
?? teamMailboxesTre?.findNode((node) => node.item.id == mailboxId);
return mailboxNode; return mailboxNode;
} }
void _createListMailboxNameAsStringInMailboxLocation() { void _createListMailboxNameAsStringInMailboxLocation() {
if (selectedMailbox.value == null) { if (selectedMailbox.value == null) {
final allChildrenAtMailboxLocation = (defaultMailboxTree?.root.childrenItems ?? <MailboxNode>[]) final allChildrenAtMailboxLocation = (defaultMailboxTree.value.root.childrenItems ?? <MailboxNode>[])
+ (personalMailboxTree?.root.childrenItems ?? <MailboxNode>[]) + (personalMailboxTree.value.root.childrenItems ?? <MailboxNode>[]);
+ (teamMailboxesTre?.root.childrenItems ?? <MailboxNode>[]);
if (allChildrenAtMailboxLocation.isNotEmpty) { if (allChildrenAtMailboxLocation.isNotEmpty) {
listMailboxNameAsStringExist = allChildrenAtMailboxLocation listMailboxNameAsStringExist = allChildrenAtMailboxLocation
.where((mailboxNode) => mailboxNode.nameNotEmpty) .where((mailboxNode) => mailboxNode.nameNotEmpty)
@@ -140,33 +121,22 @@ class MailboxCreatorController extends BaseController {
); );
} }
void selectMailboxLocation(BuildContext context) async { void openFolderModal(BuildContext context) {
KeyboardUtils.hideKeyboard(context); nameInputFocusNode.unfocus();
if (accountId != null) { if (!responsiveUtils.isMobile(context)) {
final arguments = DestinationPickerArguments( isFolderModalEnabled.value = true;
accountId!,
MailboxActions.create,
_session,
mailboxIdSelected: selectedMailbox.value?.id);
final destinationMailbox = PlatformInfo.isWeb
? await DialogRouter.pushGeneralDialog(routeName: AppRoutes.destinationPicker, arguments: arguments)
: await push(AppRoutes.destinationPicker, arguments: arguments);
if (destinationMailbox is PresentationMailbox) {
final mailboxDestination = destinationMailbox == PresentationMailbox.unifiedMailbox
? null
: destinationMailbox;
selectedMailbox.value = mailboxDestination;
_createListMailboxNameAsStringInMailboxLocation();
}
} }
} }
void selectMailboxLocation(MailboxNode? mailboxDestination) {
isFolderModalEnabled.value = false;
selectedMailbox.value = mailboxDestination?.item;
_createListMailboxNameAsStringInMailboxLocation();
}
void createNewMailbox(BuildContext context) { void createNewMailbox(BuildContext context) {
KeyboardUtils.hideKeyboard(context); nameInputFocusNode.unfocus();
final nameMailbox = newNameMailbox.value; final nameMailbox = newNameMailbox.value;
final nameValidated = getErrorInputNameString(context); final nameValidated = getErrorInputNameString(context);
@@ -180,11 +150,35 @@ class MailboxCreatorController extends BaseController {
MailboxName(nameMailbox), MailboxName(nameMailbox),
mailboxLocation: selectedMailbox.value); mailboxLocation: selectedMailbox.value);
popBack(result: newMailboxArguments); popBack(result: newMailboxArguments);
} else {
newNameMailbox.refresh();
} }
} }
void closeMailboxCreator(BuildContext context) { void toggleFolder(MailboxNode selectedMailboxNode, GlobalKey itemKey) {
KeyboardUtils.hideKeyboard(context); final newExpandMode = selectedMailboxNode.expandMode.toggle();
if (defaultMailboxTree.value.updateExpandedNode(selectedMailboxNode, newExpandMode) != null) {
defaultMailboxTree.refresh();
triggerScrollWhenExpandFolder(
selectedMailboxNode.expandMode,
itemKey,
listFolderScrollController,
);
}
if (personalMailboxTree.value.updateExpandedNode(selectedMailboxNode, newExpandMode) != null) {
personalMailboxTree.refresh();
triggerScrollWhenExpandFolder(
selectedMailboxNode.expandMode,
itemKey,
listFolderScrollController,
);
}
}
void closeView() {
nameInputFocusNode.unfocus();
popBack(); popBack();
} }
} }
@@ -1,24 +1,20 @@
import 'dart:math';
import 'package:core/presentation/extensions/color_extension.dart'; import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/utils/style_utils.dart';
import 'package:core/presentation/utils/theme_utils.dart'; import 'package:core/presentation/utils/theme_utils.dart';
import 'package:core/presentation/views/text/text_field_builder.dart';
import 'package:core/utils/app_logger.dart';
import 'package:core/utils/direction_utils.dart';
import 'package:core/utils/platform_info.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:pointer_interceptor/pointer_interceptor.dart'; import 'package:tmail_ui_user/features/base/widget/default_field/default_button_arrow_down_field_widget.dart';
import 'package:tmail_ui_user/features/base/widget/default_field/default_close_button_widget.dart';
import 'package:tmail_ui_user/features/base/widget/label_input_field_builder.dart';
import 'package:tmail_ui_user/features/base/widget/modal_list_action_button_widget.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/extensions/presentation_mailbox_extension.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/extensions/presentation_mailbox_extension.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/widgets/mailbox_icon_widget.dart';
import 'package:tmail_ui_user/features/mailbox_creator/presentation/mailbox_creator_controller.dart'; import 'package:tmail_ui_user/features/mailbox_creator/presentation/mailbox_creator_controller.dart';
import 'package:tmail_ui_user/features/mailbox_creator/presentation/widgets/app_bar_mailbox_creator_builder.dart'; import 'package:tmail_ui_user/features/mailbox_creator/presentation/widgets/modal_folder_tree_list_widget.dart';
import 'package:tmail_ui_user/features/mailbox_creator/presentation/widgets/create_mailbox_name_input_decoration_builder.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class MailboxCreatorView extends GetWidget<MailboxCreatorController> { class MailboxCreatorView extends GetWidget<MailboxCreatorController> {
final _maxHeight = 656.0;
@override @override
final controller = Get.find<MailboxCreatorController>(); final controller = Get.find<MailboxCreatorController>();
@@ -26,234 +22,181 @@ class MailboxCreatorView extends GetWidget<MailboxCreatorController> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
log('MailboxCreatorView::build():'); final appLocalizations = AppLocalizations.of(context);
return PointerInterceptor( final theme = Theme.of(context);
child: GestureDetector(
onTap: () => controller.closeMailboxCreator(context), Widget bodyWidget = Container(
child: Card( decoration: BoxDecoration(
margin: EdgeInsets.zero, color: Colors.white,
borderOnForeground: false, borderRadius: const BorderRadius.all(Radius.circular(16)),
color: Colors.transparent, boxShadow: [
child: SafeArea( BoxShadow(
top: PlatformInfo.isMobile && controller.responsiveUtils.isPortraitMobile(context), color: Colors.black.withValues(alpha: 0.08),
bottom: false, blurRadius: 24,
left: false, offset: const Offset(0, 2),
right: false,
child: Center(
child: GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
child: Container(
margin: _getMarginView(context),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: _getRadiusView(context),
boxShadow: const [
BoxShadow(
color: AppColor.colorShadowLayerBottom,
blurRadius: 96,
spreadRadius: 96,
offset: Offset.zero),
BoxShadow(
color: AppColor.colorShadowLayerTop,
blurRadius: 2,
spreadRadius: 2,
offset: Offset.zero),
]),
width: _getWidthView(context),
height: _getHeightView(context),
child: ClipRRect(
borderRadius: _getRadiusView(context),
child: SafeArea(
top: false,
bottom: false,
left: PlatformInfo.isMobile && controller.responsiveUtils.isLandscapeMobile(context),
right: PlatformInfo.isMobile && controller.responsiveUtils.isLandscapeMobile(context),
child: Column(children: [
_buildAppBar(context),
const Divider(color: AppColor.colorDividerDestinationPicker, height: 1),
_buildCreateMailboxNameInput(context),
_buildMailboxLocation(context),
]),
)
)
),
)
),
), ),
BoxShadow(
color: Colors.black.withValues(alpha: 0.08),
blurRadius: 2,
),
],
),
width: min(
controller.responsiveUtils.getSizeScreenWidth(context) - 32,
554,
),
constraints: BoxConstraints(
maxHeight: min(
controller.responsiveUtils.getSizeScreenHeight(context) - 100,
671,
), ),
), ),
); clipBehavior: Clip.antiAlias,
} child: Stack(
children: [
Padding(
padding: EdgeInsetsDirectional.only(
start: 32,
end: 32,
bottom: controller.responsiveUtils.isMobile(context) ? 0 : 16,
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
height: 64,
alignment: Alignment.center,
child: Text(
appLocalizations.createANewFolder,
style: theme.textTheme.headlineSmall?.copyWith(
color: AppColor.m3SurfaceBackground,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
Obx(() {
final folderName =
controller.selectedMailbox.value?.getDisplayName(context) ??
appLocalizations.personalFolders;
return Center(
child: Text(
appLocalizations
.subtitleDisplayTheFolderNameLocationInFolderCreationModal(
folderName,
),
style: ThemeUtils.textStyleInter400.copyWith(
color: AppColor.steelGrayA540,
fontSize: 13,
height: 20 / 13,
),
textAlign: TextAlign.center,
),
);
}),
const SizedBox(height: 32),
Obx(
() => LabelInputFieldBuilder(
label: AppLocalizations.of(context).folderName,
hintText: AppLocalizations.of(context).enterTheFolderName,
textEditingController: controller.nameInputController,
focusNode: controller.nameInputFocusNode,
errorText: controller.getErrorInputNameString(context),
arrangeHorizontally: false,
isLabelHasColon: false,
labelStyle: ThemeUtils.textStyleInter600().copyWith(
fontSize: 14,
height: 18 / 14,
color: Colors.black,
),
runSpacing: 16,
inputFieldMaxWidth: double.infinity,
onTextChange: controller.setNewNameMailbox,
),
),
Padding(
padding: const EdgeInsets.only(top: 26, bottom: 16),
child: Text(
appLocalizations.selectTheFolderLocation,
style: ThemeUtils.textStyleInter600().copyWith(
fontSize: 14,
height: 18 / 14,
color: Colors.black,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
Obx(() {
final selectedMailbox = controller.selectedMailbox.value;
final isFolderModalEnabled =
controller.isFolderModalEnabled.isTrue;
Widget _buildAppBar(BuildContext context) { return Flexible(
return (AppBarMailboxCreatorBuilder( child: AnimatedSize(
context, duration: const Duration(milliseconds: 300),
title: AppLocalizations.of(context).newFolder, curve: Curves.easeInOut,
isValidated: true) alignment: Alignment.topCenter,
..addOnCancelActionClick(() => controller.closeMailboxCreator(context)) child: isFolderModalEnabled
..addOnDoneActionClick(() => controller.createNewMailbox(context))) ? Container(
.build(); decoration: BoxDecoration(
} borderRadius: const BorderRadius.all(
Radius.circular(10),
Widget _buildCreateMailboxNameInput(BuildContext context) { ),
return Padding( border: Border.all(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), color: AppColor.m3Neutral90,
child: Obx(() => TextFieldBuilder( width: 1,
onTextChange: controller.setNewNameMailbox, ),
keyboardType: TextInputType.visiblePassword, ),
cursorColor: AppColor.colorTextButton, padding: const EdgeInsetsDirectional.only(
controller: controller.nameInputController, start: 14,
textDirection: DirectionUtils.getDirectionByLanguage(context), top: 13,
maxLines: 1, bottom: 13,
textStyle: ThemeUtils.defaultTextStyleInterFont.copyWith( end: 4,
color: AppColor.colorNameEmail, ),
fontSize: 16, child: Obx(() => ModalFolderTreeListWidget(
overflow: CommonTextStyle.defaultTextOverFlow), defaultTree: controller.defaultMailboxTree.value,
focusNode: controller.nameInputFocusNode, personalTree: controller.personalMailboxTree.value,
decoration: (CreateMailboxNameInputDecorationBuilder() imagePaths: controller.imagePaths,
..setHintText(AppLocalizations.of(context).hintInputCreateNewFolder) listScrollController:
..setErrorText(controller.getErrorInputNameString(context))) controller.listFolderScrollController,
.build(), mailboxIdSelected: selectedMailbox?.id,
)) onSelectFolderAction: controller.selectMailboxLocation,
); onToggleFolderAction: controller.toggleFolder,
} )),
)
Widget _buildMailboxLocation(BuildContext context) { : DefaultButtonArrowDownFieldWidget(
return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ text: selectedMailbox?.getDisplayName(context) ??
Padding( appLocalizations.personalFolders,
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12), icon: MailboxIconWidget(
child: Text( icon: selectedMailbox?.getMailboxIcon(controller.imagePaths)
AppLocalizations.of(context).selectParentFolder, ?? controller.imagePaths.icFolderMailbox,
textAlign: TextAlign.left, ),
style: ThemeUtils.defaultTextStyleInterFont.copyWith( iconArrowDown: controller.imagePaths.icDropDown,
fontSize: 13, onTap: () => controller.openFolderModal(context),
color: AppColor.colorHintSearchBar, ),
fontWeight: FontWeight.normal)), ),
), );
Container( }),
alignment: Alignment.center, ModalListActionButtonWidget(
margin: const EdgeInsets.symmetric(horizontal: 16), positiveLabel: appLocalizations.createFolder,
decoration: BoxDecoration( negativeLabel: appLocalizations.cancel,
borderRadius: BorderRadius.circular(14), padding: const EdgeInsets.symmetric(vertical: 25),
color: AppColor.colorBgMailbox onPositiveAction: () => controller.createNewMailbox(context),
onNegativeAction: controller.closeView,
)
],
),
), ),
child: Material( DefaultCloseButtonWidget(
color: Colors.transparent, iconClose: controller.imagePaths.icCloseDialog,
child: InkWell( onTapActionCallback: controller.closeView,
onTap: () => controller.selectMailboxLocation(context), ),
customBorder: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)), ],
child: Row(children: [ ),
const SizedBox(width: 12), );
Obx(() => SvgPicture.asset(
controller.selectedMailbox.value?.getMailboxIcon(controller.imagePaths) ?? controller.imagePaths.icFolderMailbox,
width: PlatformInfo.isWeb ? 20 : 24,
height: PlatformInfo.isWeb ? 20 : 24,
fit: BoxFit.fill)),
const SizedBox(width: 12),
Expanded(child: Obx(() => Text(
controller.selectedMailbox.value?.getDisplayName(context) ?? AppLocalizations.of(context).allFolders,
maxLines: 1,
softWrap: CommonTextStyle.defaultSoftWrap,
overflow: CommonTextStyle.defaultTextOverFlow,
style: ThemeUtils.defaultTextStyleInterFont.copyWith(
fontSize: 15,
color: controller.selectedMailbox.value == null
? AppColor.colorHintSearchBar
: AppColor.colorNameEmail),
))),
const SizedBox(width: 12),
IconButton(
color: AppColor.primaryColor,
icon: SvgPicture.asset(
DirectionUtils.isDirectionRTLByLanguage(context)
? controller.imagePaths.icBack
: controller.imagePaths.icCollapseFolder,
colorFilter: AppColor.colorCollapseMailbox.asFilter(),
fit: BoxFit.fill),
onPressed: () => controller.selectMailboxLocation(context))
])),
)
)
]);
}
double _getWidthView(BuildContext context) { return Center(child: bodyWidget);
if (PlatformInfo.isWeb) {
if (controller.responsiveUtils.isMobile(context)) {
return double.infinity;
} else {
return 556;
}
} else {
if (controller.responsiveUtils.isLandscapeMobile(context) ||
controller.responsiveUtils.isPortraitMobile(context)) {
return double.infinity;
} else {
return 556;
}
}
}
double _getHeightView(BuildContext context) {
if (PlatformInfo.isWeb) {
if (controller.responsiveUtils.isMobile(context)) {
return double.infinity;
} else {
if (controller.responsiveUtils.getSizeScreenHeight(context) > _maxHeight) {
return _maxHeight;
} else {
return double.infinity;
}
}
} else {
if (controller.responsiveUtils.isLandscapeMobile(context) ||
controller.responsiveUtils.isPortraitMobile(context)) {
return double.infinity;
} else {
if (controller.responsiveUtils.getSizeScreenHeight(context) > _maxHeight) {
return _maxHeight;
} else {
return double.infinity;
}
}
}
}
EdgeInsets _getMarginView(BuildContext context) {
if (PlatformInfo.isWeb) {
if (controller.responsiveUtils.isMobile(context)) {
return EdgeInsets.zero;
} else {
if (controller.responsiveUtils.getSizeScreenHeight(context) > _maxHeight) {
return const EdgeInsets.symmetric(vertical: 12);
} else {
return const EdgeInsets.symmetric(vertical: 50);
}
}
} else {
if (controller.responsiveUtils.isLandscapeMobile(context) ||
controller.responsiveUtils.isPortraitMobile(context)) {
return EdgeInsets.zero;
} else {
if (controller.responsiveUtils.getSizeScreenHeight(context) > _maxHeight) {
return const EdgeInsets.symmetric(vertical: 12);
} else {
return const EdgeInsets.symmetric(vertical: 50);
}
}
}
}
BorderRadius _getRadiusView(BuildContext context) {
if (PlatformInfo.isMobile && controller.responsiveUtils.isLandscapeMobile(context)) {
return BorderRadius.zero;
} else if (controller.responsiveUtils.isMobile(context)) {
return const BorderRadius.only(
topRight: Radius.circular(16),
topLeft: Radius.circular(16));
} else {
return const BorderRadius.all(Radius.circular(16));
}
} }
} }
@@ -1,34 +1,23 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:model/mailbox/presentation_mailbox.dart'; import 'package:model/mailbox/presentation_mailbox.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_tree.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_tree.dart';
class MailboxCreatorArguments with EquatableMixin{ class MailboxCreatorArguments with EquatableMixin{
final AccountId accountId;
final MailboxTree personalMailboxTree; final MailboxTree personalMailboxTree;
final MailboxTree defaultMailboxTree; final MailboxTree defaultMailboxTree;
final MailboxTree teamMailboxesTree;
final Session session;
final PresentationMailbox? selectedMailbox; final PresentationMailbox? selectedMailbox;
MailboxCreatorArguments( MailboxCreatorArguments(
this.accountId,
this.defaultMailboxTree, this.defaultMailboxTree,
this.personalMailboxTree, this.personalMailboxTree,
this.teamMailboxesTree, this.selectedMailbox,
this.session,
this.selectedMailbox
); );
@override @override
List<Object?> get props => [ List<Object?> get props => [
accountId,
defaultMailboxTree, defaultMailboxTree,
personalMailboxTree, personalMailboxTree,
teamMailboxesTree, selectedMailbox,
session,
selectedMailbox
]; ];
} }
@@ -1,92 +0,0 @@
import 'package:core/core.dart';
import 'package:flutter/material.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
typedef OnCancelActionClick = void Function();
typedef OnDoneActionClick = void Function();
class AppBarMailboxCreatorBuilder {
OnCancelActionClick? _cancelActionClick;
OnDoneActionClick? _onDoneActionClick;
final BuildContext _context;
final bool isValidated;
final String? title;
AppBarMailboxCreatorBuilder(
this._context,
{
this.title,
this.isValidated = false
}
);
void addOnCancelActionClick(OnCancelActionClick onCancelActionClick) {
_cancelActionClick = onCancelActionClick;
}
void addOnDoneActionClick(OnDoneActionClick onDoneActionClick) {
_onDoneActionClick = onDoneActionClick;
}
Widget build() {
return Container(
color: Colors.white,
height: 52,
padding: const EdgeInsets.symmetric(horizontal: 12),
child: Row(children: [
_buildCancelButton(),
Expanded(child: _buildTitle()),
_buildCreateButton()
])
);
}
Widget _buildCancelButton() {
return Material(
borderRadius: BorderRadius.circular(20),
color: Colors.transparent,
child: TextButton(
child: Text(
AppLocalizations.of(_context).cancel,
style: ThemeUtils.defaultTextStyleInterFont.copyWith(fontSize: 17, color: AppColor.colorTextButton),
),
onPressed: () => _cancelActionClick?.call()
)
);
}
Widget _buildCreateButton() {
return Material(
borderRadius: BorderRadius.circular(20),
color: Colors.transparent,
child: TextButton(
child: Text(
AppLocalizations.of(_context).done,
style: ThemeUtils.defaultTextStyleInterFont.copyWith(
fontSize: 17,
color: isValidated
? AppColor.colorTextButton
: AppColor.colorDisableMailboxCreateButton),
),
onPressed: () => isValidated ? _onDoneActionClick?.call() : null
)
);
}
Widget _buildTitle() {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Text(
title ?? '',
textAlign: TextAlign.center,
maxLines: 1,
softWrap: CommonTextStyle.defaultSoftWrap,
overflow: CommonTextStyle.defaultTextOverFlow,
style: ThemeUtils.defaultTextStyleInterFont.copyWith(
fontSize: 20,
color: AppColor.colorNameEmail,
fontWeight: FontWeight.bold)));
}
}
@@ -0,0 +1,114 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/views/list/tree_view.dart';
import 'package:flutter/material.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
import 'package:model/mailbox/expand_mode.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_displayed.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_node.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_tree.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/utils/mailbox_method_action_define.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/widgets/folder_item_widget.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/widgets/mailbox_item_widget.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class ModalFolderTreeListWidget extends StatelessWidget {
final MailboxTree defaultTree;
final MailboxTree personalTree;
final ScrollController listScrollController;
final ImagePaths imagePaths;
final OnClickOpenMailboxNodeAction onSelectFolderAction;
final OnClickExpandMailboxNodeAction onToggleFolderAction;
final MailboxId? mailboxIdSelected;
const ModalFolderTreeListWidget({
super.key,
required this.defaultTree,
required this.personalTree,
required this.listScrollController,
required this.imagePaths,
required this.onSelectFolderAction,
required this.onToggleFolderAction,
this.mailboxIdSelected,
});
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
physics: const ClampingScrollPhysics(),
controller: listScrollController,
padding: const EdgeInsetsDirectional.only(end: 14),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
FolderItemWidget(
folderName: AppLocalizations.of(context).personalFolders,
imagePaths: imagePaths,
isSelected: mailboxIdSelected == null,
onTapAction: () => onSelectFolderAction(null),
),
if (defaultTree.root.hasChildren())
_buildFolderTree(
mailboxTree: defaultTree,
mailboxIdSelected: mailboxIdSelected,
),
if (personalTree.root.hasChildren())
_buildFolderTree(
mailboxTree: personalTree,
mailboxIdSelected: mailboxIdSelected,
),
],
),
);
}
Widget _buildFolderTree({
required MailboxTree mailboxTree,
MailboxId? mailboxIdSelected,
}) {
return TreeView(
children: _buildListChildTileWidget(
parentNode: mailboxTree.root,
mailboxIdSelected: mailboxIdSelected,
),
);
}
List<Widget> _buildListChildTileWidget({
required MailboxNode parentNode,
MailboxId? mailboxIdSelected,
}) {
return parentNode.childrenItems?.map(
(mailboxNode) {
if (mailboxNode.hasChildren()) {
return TreeViewChild(
isExpanded: mailboxNode.expandMode == ExpandMode.EXPAND,
paddingChild: const EdgeInsetsDirectional.only(start: 14),
parent: MailboxItemWidget(
mailboxNode: mailboxNode,
mailboxIdAlreadySelected: mailboxIdSelected,
mailboxDisplayed: MailboxDisplayed.modalFolder,
hoverColor: AppColor.blue100,
onOpenMailboxFolderClick: onSelectFolderAction,
onExpandFolderActionClick: onToggleFolderAction,
),
children: _buildListChildTileWidget(
parentNode: mailboxNode,
mailboxIdSelected: mailboxIdSelected,
),
).build();
} else {
return MailboxItemWidget(
mailboxNode: mailboxNode,
mailboxDisplayed: MailboxDisplayed.modalFolder,
mailboxIdAlreadySelected: mailboxIdSelected,
hoverColor: AppColor.blue100,
onOpenMailboxFolderClick: onSelectFolderAction,
onExpandFolderActionClick: onToggleFolderAction,
);
}
},
).toList() ?? [];
}
}
@@ -272,7 +272,6 @@ class MailboxVisibilityView extends GetWidget<MailboxVisibilityController>
return parentNode.childrenItems return parentNode.childrenItems
?.map((mailboxNode) => mailboxNode.hasChildren() ?.map((mailboxNode) => mailboxNode.hasChildren()
? TreeViewChild( ? TreeViewChild(
context,
key: const Key('children_tree_mailbox_child'), key: const Key('children_tree_mailbox_child'),
isExpanded: mailboxNode.expandMode == ExpandMode.EXPAND, isExpanded: mailboxNode.expandMode == ExpandMode.EXPAND,
paddingChild: const EdgeInsetsDirectional.only(start: 10), paddingChild: const EdgeInsetsDirectional.only(start: 10),
@@ -756,12 +756,9 @@ class SearchMailboxController extends BaseMailboxController with MailboxActionHa
void goToCreateNewMailboxView(BuildContext context, {PresentationMailbox? parentMailbox}) async { void goToCreateNewMailboxView(BuildContext context, {PresentationMailbox? parentMailbox}) async {
if (session != null && accountId != null) { if (session != null && accountId != null) {
final arguments = MailboxCreatorArguments( final arguments = MailboxCreatorArguments(
accountId!,
defaultMailboxTree.value, defaultMailboxTree.value,
personalMailboxTree.value, personalMailboxTree.value,
teamMailboxesTree.value, parentMailbox,
session!,
parentMailbox
); );
final result = PlatformInfo.isWeb final result = PlatformInfo.isWeb
+40
View File
@@ -4789,5 +4789,45 @@
"type": "text", "type": "text",
"placeholders_order": [], "placeholders_order": [],
"placeholders": {} "placeholders": {}
},
"createANewFolder": "Create a New Folder",
"@createANewFolder": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"subtitleDisplayTheFolderNameLocationInFolderCreationModal": "New folder will be created inside {folderName}",
"@subtitleDisplayTheFolderNameLocationInFolderCreationModal": {
"type": "text",
"placeholders_order": [
"folderName"
],
"placeholders": {
"folderName": {}
}
},
"folderName": "Folder Name",
"@folderName": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"enterTheFolderName": "Enter the folder name",
"@enterTheFolderName": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"selectTheFolderLocation": "Select the folder location",
"@selectTheFolderLocation": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"createFolder": "Create folder",
"@createFolder": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
} }
} }
@@ -5059,4 +5059,47 @@ class AppLocalizations {
name: 'actions', name: 'actions',
); );
} }
String get createANewFolder {
return Intl.message(
'Create a New Folder',
name: 'createANewFolder',
);
}
String subtitleDisplayTheFolderNameLocationInFolderCreationModal(String folderName) {
return Intl.message(
'New folder will be created inside $folderName',
name: 'subtitleDisplayTheFolderNameLocationInFolderCreationModal',
args: [folderName]
);
}
String get folderName {
return Intl.message(
'Folder Name',
name: 'folderName',
);
}
String get enterTheFolderName {
return Intl.message(
'Enter the folder name',
name: 'enterTheFolderName',
);
}
String get selectTheFolderLocation {
return Intl.message(
'Select the folder location',
name: 'selectTheFolderLocation',
);
}
String get createFolder {
return Intl.message(
'Create folder',
name: 'createFolder',
);
}
} }