diff --git a/lib/features/mailbox_dashboard/presentation/controller/advanced_filter_controller.dart b/lib/features/mailbox_dashboard/presentation/controller/advanced_filter_controller.dart index 66c4c830c9..be86cd3ef9 100644 --- a/lib/features/mailbox_dashboard/presentation/controller/advanced_filter_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/controller/advanced_filter_controller.dart @@ -9,6 +9,7 @@ import 'package:super_tag_editor/tag_editor.dart'; import 'package:tmail_ui_user/features/base/base_controller.dart'; import 'package:tmail_ui_user/features/composer/domain/state/get_autocomplete_state.dart'; import 'package:tmail_ui_user/features/composer/domain/usecases/get_autocomplete_interactor.dart'; +import 'package:tmail_ui_user/features/composer/presentation/model/draggable_email_address.dart'; import 'package:tmail_ui_user/features/destination_picker/presentation/model/destination_picker_arguments.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/extensions/presentation_mailbox_extension.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart'; @@ -477,6 +478,24 @@ class AdvancedFilterController extends BaseController { } } + void removeDraggableEmailAddress(DraggableEmailAddress draggableEmailAddress) { + log('AdvancedFilterController::removeDraggableEmailAddress:removeDraggableEmailAddress: $draggableEmailAddress'); + switch(draggableEmailAddress.prefix) { + case PrefixEmailAddress.to: + listToEmailAddress.remove(draggableEmailAddress.emailAddress); + toAddressExpandMode.value = ExpandMode.EXPAND; + toAddressExpandMode.refresh(); + break; + case PrefixEmailAddress.from: + listFromEmailAddress.remove(draggableEmailAddress.emailAddress); + fromAddressExpandMode.value = ExpandMode.EXPAND; + fromAddressExpandMode.refresh(); + break; + default: + break; + } + } + @override void onClose() { _removeFocusListener(); diff --git a/lib/features/mailbox_dashboard/presentation/model/search/advanced_search_filter.dart b/lib/features/mailbox_dashboard/presentation/model/search/advanced_search_filter.dart index 535faae3a4..a548b55f07 100644 --- a/lib/features/mailbox_dashboard/presentation/model/search/advanced_search_filter.dart +++ b/lib/features/mailbox_dashboard/presentation/model/search/advanced_search_filter.dart @@ -1,5 +1,6 @@ import 'package:core/core.dart'; import 'package:flutter/cupertino.dart'; +import 'package:model/email/prefix_email_address.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; enum AdvancedSearchFilterField { @@ -64,6 +65,17 @@ enum AdvancedSearchFilterField { color: AppColor.colorContentEmail); } + PrefixEmailAddress getPrefixEmailAddress() { + switch (this) { + case AdvancedSearchFilterField.from: + return PrefixEmailAddress.from; + case AdvancedSearchFilterField.to: + return PrefixEmailAddress.to; + default: + return PrefixEmailAddress.cc; + } + } + TextStyle getHintTextStyle() { return const TextStyle( fontSize: 16, diff --git a/lib/features/mailbox_dashboard/presentation/styles/text_field_autocomplete_email_address_style.dart b/lib/features/mailbox_dashboard/presentation/styles/text_field_autocomplete_email_address_style.dart deleted file mode 100644 index 75290d2bcd..0000000000 --- a/lib/features/mailbox_dashboard/presentation/styles/text_field_autocomplete_email_address_style.dart +++ /dev/null @@ -1,9 +0,0 @@ -import 'package:flutter/material.dart'; - -class TextFieldAutocompleteEmailAddressStyle { - static const TextStyle inputTextStyle = TextStyle( - color: Colors.black, - fontSize: 16, - fontWeight: FontWeight.w400, - ); -} \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/presentation/widgets/advanced_search/advanced_search_input_form.dart b/lib/features/mailbox_dashboard/presentation/widgets/advanced_search/advanced_search_input_form.dart index 87fd5acb7d..212ee0b41e 100644 --- a/lib/features/mailbox_dashboard/presentation/widgets/advanced_search/advanced_search_input_form.dart +++ b/lib/features/mailbox_dashboard/presentation/widgets/advanced_search/advanced_search_input_form.dart @@ -39,6 +39,7 @@ class AdvancedSearchInputForm extends GetWidget onUpdateListEmailAddressAction: controller.updateListEmailAddress, onSuggestionEmailAddress: controller.getAutoCompleteSuggestion, onSearchAction: controller.onSearchAction, + onRemoveDraggableEmailAddressAction: controller.removeDraggableEmailAddress, )), Obx(() => TextFieldAutocompleteEmailAddressWeb( field: AdvancedSearchFilterField.to, @@ -52,6 +53,7 @@ class AdvancedSearchInputForm extends GetWidget onUpdateListEmailAddressAction: controller.updateListEmailAddress, onSuggestionEmailAddress: controller.getAutoCompleteSuggestion, onSearchAction: controller.onSearchAction, + onRemoveDraggableEmailAddressAction: controller.removeDraggableEmailAddress, )), _buildFilterField( textEditingController: controller.subjectFilterInputController, diff --git a/lib/features/mailbox_dashboard/presentation/widgets/advanced_search/autocomplete_tag_item_widget_web.dart b/lib/features/mailbox_dashboard/presentation/widgets/advanced_search/autocomplete_tag_item_widget_web.dart index 98b3bdf23a..16cc1fe486 100644 --- a/lib/features/mailbox_dashboard/presentation/widgets/advanced_search/autocomplete_tag_item_widget_web.dart +++ b/lib/features/mailbox_dashboard/presentation/widgets/advanced_search/autocomplete_tag_item_widget_web.dart @@ -6,6 +6,8 @@ import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; import 'package:jmap_dart_client/jmap/mail/email/email_address.dart'; import 'package:model/extensions/email_address_extension.dart'; +import 'package:tmail_ui_user/features/composer/presentation/model/draggable_email_address.dart'; +import 'package:tmail_ui_user/features/composer/presentation/widgets/draggable_recipient_tag_widget.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/advanced_search_filter.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/styles/autocomplete_tag_item_web_style.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/advanced_search/avatar_tag_item_widget.dart'; @@ -48,39 +50,47 @@ class AutoCompleteTagItemWidgetWeb extends StatelessWidget { top: AutoCompleteTagItemWebStyle.paddingTop, end: isCollapsed ? AutoCompleteTagItemWebStyle.paddingEnd : 0, ), - child: TextFieldTapRegion( - child: InkWell( - onTap: () => isCollapsed - ? onShowFullAction?.call(field) - : null, - child: MouseRegion( - cursor: SystemMouseCursors.grab, - child: Chip( - labelPadding: EdgeInsetsDirectional.symmetric( - horizontal: AutoCompleteTagItemWebStyle.labelPaddingHorizontal, - vertical: DirectionUtils.isDirectionRTLByHasAnyRtl(currentEmailAddress.asString()) ? 0 : 2 - ), - label: Text( - currentEmailAddress.asString(), - maxLines: 1, - overflow: CommonTextStyle.defaultTextOverFlow, - softWrap: CommonTextStyle.defaultSoftWrap, - ), - deleteIcon: SvgPicture.asset( - _imagePaths.icClose, - fit: BoxFit.fill, - ), - padding: EdgeInsets.zero, - labelStyle: AutoCompleteTagItemWebStyle.labelTextStyle, - backgroundColor: _getTagBackgroundColor(), - shape: RoundedRectangleBorder( - borderRadius: AutoCompleteTagItemWebStyle.shapeBorderRadius, - side: _getTagBorderSide(), + child: Draggable( + data: DraggableEmailAddress( + emailAddress: currentEmailAddress, + prefix: field.getPrefixEmailAddress() + ), + feedback: DraggableRecipientTagWidget(emailAddress: currentEmailAddress), + childWhenDragging: DraggableRecipientTagWidget(emailAddress: currentEmailAddress), + child: TextFieldTapRegion( + child: InkWell( + onTap: () => isCollapsed + ? onShowFullAction?.call(field) + : null, + child: MouseRegion( + cursor: SystemMouseCursors.grab, + child: Chip( + labelPadding: EdgeInsetsDirectional.symmetric( + horizontal: AutoCompleteTagItemWebStyle.labelPaddingHorizontal, + vertical: DirectionUtils.isDirectionRTLByHasAnyRtl(currentEmailAddress.asString()) ? 0 : 2 + ), + label: Text( + currentEmailAddress.asString(), + maxLines: 1, + overflow: CommonTextStyle.defaultTextOverFlow, + softWrap: CommonTextStyle.defaultSoftWrap, + ), + deleteIcon: SvgPicture.asset( + _imagePaths.icClose, + fit: BoxFit.fill, + ), + padding: EdgeInsets.zero, + labelStyle: AutoCompleteTagItemWebStyle.labelTextStyle, + backgroundColor: _getTagBackgroundColor(), + shape: RoundedRectangleBorder( + borderRadius: AutoCompleteTagItemWebStyle.shapeBorderRadius, + side: _getTagBorderSide(), + ), + avatar: currentEmailAddress.emailAddress.isNotEmpty + ? AvatarTagItemWidget(tagName: currentEmailAddress.emailAddress) + : null, + onDeleted: () => onDeleteTagAction?.call(currentEmailAddress), ), - avatar: currentEmailAddress.emailAddress.isNotEmpty - ? AvatarTagItemWidget(tagName: currentEmailAddress.emailAddress) - : null, - onDeleted: () => onDeleteTagAction?.call(currentEmailAddress), ), ), ), diff --git a/lib/features/mailbox_dashboard/presentation/widgets/advanced_search/text_field_autocomplete_email_address_web.dart b/lib/features/mailbox_dashboard/presentation/widgets/advanced_search/text_field_autocomplete_email_address_web.dart index f6741643b0..a8f7629834 100644 --- a/lib/features/mailbox_dashboard/presentation/widgets/advanced_search/text_field_autocomplete_email_address_web.dart +++ b/lib/features/mailbox_dashboard/presentation/widgets/advanced_search/text_field_autocomplete_email_address_web.dart @@ -11,6 +11,8 @@ import 'package:jmap_dart_client/jmap/mail/email/email_address.dart'; import 'package:model/extensions/email_address_extension.dart'; import 'package:model/mailbox/expand_mode.dart'; import 'package:super_tag_editor/tag_editor.dart'; +import 'package:tmail_ui_user/features/composer/presentation/model/draggable_email_address.dart'; +import 'package:tmail_ui_user/features/composer/presentation/widgets/recipient_composer_widget.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/advanced_search_filter.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/suggesstion_email_address.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/styles/advanced_search_input_form_style.dart'; @@ -40,6 +42,7 @@ class TextFieldAutocompleteEmailAddressWeb extends StatefulWidget { final OnUpdateListEmailAddressAction? onUpdateListEmailAddressAction; final OnDeleteEmailAddressTypeAction? onDeleteEmailAddressTypeAction; final OnShowFullListEmailAddressAction? onShowFullListEmailAddressAction; + final OnRemoveDraggableEmailAddressAction? onRemoveDraggableEmailAddressAction; final TextEditingController? controller; final VoidCallback? onSearchAction; @@ -58,6 +61,7 @@ class TextFieldAutocompleteEmailAddressWeb extends StatefulWidget { this.onUpdateListEmailAddressAction, this.onDeleteEmailAddressTypeAction, this.onShowFullListEmailAddressAction, + this.onRemoveDraggableEmailAddressAction, this.controller, this.onSearchAction, }) : super(key: key); @@ -68,6 +72,7 @@ class TextFieldAutocompleteEmailAddressWeb extends StatefulWidget { class _TextFieldAutocompleteEmailAddressWebState extends State { bool _lastTagFocused = false; + bool _isDragging = false; late List _currentListEmailAddress; Timer? _gapBetweenTagChangedAndFindSuggestion; @@ -105,89 +110,110 @@ class _TextFieldAutocompleteEmailAddressWebState extends State( - key: widget.keyTagEditor, - length: _collapsedListEmailAddress.length, - controller: widget.controller, - focusNodeKeyboard: widget.focusNode, - keyboardType: TextInputType.emailAddress, - textInputAction: TextInputAction.done, - cursorColor: TextFieldAutoCompleteEmailAddressWebStyles.cursorColor, - debounceDuration: TextFieldAutoCompleteEmailAddressWebStyles.debounceDuration, - inputDecoration: InputDecoration( - filled: true, - fillColor: TextFieldAutoCompleteEmailAddressWebStyles.textInputFillColor, - border: TextFieldAutoCompleteEmailAddressWebStyles.textInputBorder, - hintText: widget.field.getHintText(context), - hintStyle: TextFieldAutoCompleteEmailAddressWebStyles.textInputHintStyle, - isDense: true, - contentPadding: _currentListEmailAddress.isNotEmpty - ? TextFieldAutoCompleteEmailAddressWebStyles.textInputContentPaddingWithSomeTag - : TextFieldAutoCompleteEmailAddressWebStyles.textInputContentPadding - ), - padding: _currentListEmailAddress.isNotEmpty - ? TextFieldAutoCompleteEmailAddressWebStyles.tagEditorPadding - : EdgeInsets.zero, - borderRadius: TextFieldAutoCompleteEmailAddressWebStyles.borderRadius, - borderSize: TextFieldAutoCompleteEmailAddressWebStyles.borderWidth, - focusedBorderColor: TextFieldAutoCompleteEmailAddressWebStyles.focusedBorderColor, - enableBorder: true, - enableBorderColor: AppColor.colorInputBorderCreateMailbox, - minTextFieldWidth: TextFieldAutoCompleteEmailAddressWebStyles.minTextFieldWidth, - resetTextOnSubmitted: true, - autoScrollToInput: false, - suggestionsBoxElevation: TextFieldAutoCompleteEmailAddressWebStyles.suggestionBoxElevation, - suggestionsBoxBackgroundColor: TextFieldAutoCompleteEmailAddressWebStyles.suggestionBoxBackgroundColor, - suggestionsBoxRadius: TextFieldAutoCompleteEmailAddressWebStyles.suggestionBoxRadius, - suggestionsBoxMaxHeight: TextFieldAutoCompleteEmailAddressWebStyles.suggestionBoxMaxHeight, - suggestionBoxWidth: _getSuggestionBoxWidth(constraints.maxWidth), - textStyle: AdvancedSearchInputFormStyle.inputTextStyle, - onFocusTagAction: (focused) => _handleFocusTagAction.call(focused, setState), - onDeleteTagAction: () => _handleDeleteLatestTagAction.call(setState), - onSelectOptionAction: (item) => _handleSelectOptionAction.call(item, setState), - onSubmitted: (value) => _handleSubmitTagAction.call(value, setState), - tagBuilder: (context, index) { - final currentEmailAddress = _currentListEmailAddress.elementAt(index); - final isLatestEmail = currentEmailAddress == _currentListEmailAddress.last; - return AutoCompleteTagItemWidgetWeb( - field: widget.field, - currentEmailAddress: currentEmailAddress, - currentListEmailAddress: _currentListEmailAddress, - collapsedListEmailAddress: _collapsedListEmailAddress, - isLatestEmail: isLatestEmail, - isCollapsed: _isCollapse, - isLatestTagFocused: _lastTagFocused, - onDeleteTagAction: (emailAddress) => _handleDeleteTagAction.call(emailAddress, setState), - onShowFullAction: widget.onShowFullListEmailAddressAction, - ); - }, - onTagChanged: (tag) => _handleOnTagChangeAction.call(tag, setState), - findSuggestions: _findSuggestions, - suggestionBuilder: (context, tagEditorState, suggestionEmailAddress, index, length, highlight, suggestionValid) { - return AutoCompleteSuggestionItemWidgetWeb( - suggestionState: suggestionEmailAddress.state, - emailAddress: suggestionEmailAddress.emailAddress, - suggestionValid: suggestionValid, - highlight: highlight, - onSelectedAction: (emailAddress) { - setState(() => _currentListEmailAddress.add(emailAddress)); - _updateListEmailAddressAction(); - tagEditorState.resetTextField(); - tagEditorState.closeSuggestionBox(); - }, - ); + return DragTarget( + onAcceptWithDetails: (draggableEmailAddress) => + _handleAcceptDraggableEmailAddressAction( + draggableEmailAddress.data, + setState + ), + onLeave: (draggableEmailAddress) { + if (_isDragging) { + setState(() => _isDragging = false); + } }, - onHandleKeyEventAction: (event) { - if (event is KeyDownEvent) { - switch (event.logicalKey) { - case LogicalKeyboardKey.tab: - widget.nextFocusNode?.requestFocus(); - break; - default: - break; - } + onMove: (details) { + if (!_isDragging) { + setState(() => _isDragging = true); } }, + builder: (_, __, ___) { + return TagEditor( + key: widget.keyTagEditor, + length: _collapsedListEmailAddress.length, + controller: widget.controller, + focusNodeKeyboard: widget.focusNode, + keyboardType: TextInputType.emailAddress, + textInputAction: TextInputAction.done, + cursorColor: TextFieldAutoCompleteEmailAddressWebStyles.cursorColor, + debounceDuration: TextFieldAutoCompleteEmailAddressWebStyles.debounceDuration, + inputDecoration: InputDecoration( + filled: true, + fillColor: TextFieldAutoCompleteEmailAddressWebStyles.textInputFillColor, + border: TextFieldAutoCompleteEmailAddressWebStyles.textInputBorder, + hintText: widget.field.getHintText(context), + hintStyle: TextFieldAutoCompleteEmailAddressWebStyles.textInputHintStyle, + isDense: true, + contentPadding: _currentListEmailAddress.isNotEmpty + ? TextFieldAutoCompleteEmailAddressWebStyles.textInputContentPaddingWithSomeTag + : TextFieldAutoCompleteEmailAddressWebStyles.textInputContentPadding + ), + padding: _currentListEmailAddress.isNotEmpty + ? TextFieldAutoCompleteEmailAddressWebStyles.tagEditorPadding + : EdgeInsets.zero, + borderRadius: TextFieldAutoCompleteEmailAddressWebStyles.borderRadius, + borderSize: TextFieldAutoCompleteEmailAddressWebStyles.borderWidth, + focusedBorderColor: TextFieldAutoCompleteEmailAddressWebStyles.focusedBorderColor, + enableBorder: true, + enableBorderColor: _isDragging + ? AppColor.primaryColor + : AppColor.colorInputBorderCreateMailbox, + minTextFieldWidth: TextFieldAutoCompleteEmailAddressWebStyles.minTextFieldWidth, + resetTextOnSubmitted: true, + autoScrollToInput: false, + suggestionsBoxElevation: TextFieldAutoCompleteEmailAddressWebStyles.suggestionBoxElevation, + suggestionsBoxBackgroundColor: TextFieldAutoCompleteEmailAddressWebStyles.suggestionBoxBackgroundColor, + suggestionsBoxRadius: TextFieldAutoCompleteEmailAddressWebStyles.suggestionBoxRadius, + suggestionsBoxMaxHeight: TextFieldAutoCompleteEmailAddressWebStyles.suggestionBoxMaxHeight, + suggestionBoxWidth: _getSuggestionBoxWidth(constraints.maxWidth), + textStyle: AdvancedSearchInputFormStyle.inputTextStyle, + onFocusTagAction: (focused) => _handleFocusTagAction.call(focused, setState), + onDeleteTagAction: () => _handleDeleteLatestTagAction.call(setState), + onSelectOptionAction: (item) => _handleSelectOptionAction.call(item, setState), + onSubmitted: (value) => _handleSubmitTagAction.call(value, setState), + tagBuilder: (context, index) { + final currentEmailAddress = _currentListEmailAddress.elementAt(index); + final isLatestEmail = currentEmailAddress == _currentListEmailAddress.last; + return AutoCompleteTagItemWidgetWeb( + field: widget.field, + currentEmailAddress: currentEmailAddress, + currentListEmailAddress: _currentListEmailAddress, + collapsedListEmailAddress: _collapsedListEmailAddress, + isLatestEmail: isLatestEmail, + isCollapsed: _isCollapse, + isLatestTagFocused: _lastTagFocused, + onDeleteTagAction: (emailAddress) => _handleDeleteTagAction.call(emailAddress, setState), + onShowFullAction: widget.onShowFullListEmailAddressAction, + ); + }, + onTagChanged: (tag) => _handleOnTagChangeAction.call(tag, setState), + findSuggestions: _findSuggestions, + suggestionBuilder: (context, tagEditorState, suggestionEmailAddress, index, length, highlight, suggestionValid) { + return AutoCompleteSuggestionItemWidgetWeb( + suggestionState: suggestionEmailAddress.state, + emailAddress: suggestionEmailAddress.emailAddress, + suggestionValid: suggestionValid, + highlight: highlight, + onSelectedAction: (emailAddress) { + setState(() => _currentListEmailAddress.add(emailAddress)); + _updateListEmailAddressAction(); + tagEditorState.resetTextField(); + tagEditorState.closeSuggestionBox(); + }, + ); + }, + onHandleKeyEventAction: (event) { + if (event is KeyDownEvent) { + switch (event.logicalKey) { + case LogicalKeyboardKey.tab: + widget.nextFocusNode?.requestFocus(); + break; + default: + break; + } + } + }, + ); + } ); }) ), @@ -340,4 +366,25 @@ class _TextFieldAutocompleteEmailAddressWebState extends State _isDragging = false); + } + widget.onRemoveDraggableEmailAddressAction?.call(draggableEmailAddress); + } else if (_isDragging) { + stateSetter(() => _isDragging = false); + } + } } \ No newline at end of file