TF-2989 Fix drag and drop text within composer is broken

This commit is contained in:
dab246
2024-07-12 17:46:58 +07:00
committed by Dat H. Pham
parent 01561e3167
commit 34cbb155bd
4 changed files with 162 additions and 133 deletions
@@ -385,22 +385,42 @@ class ComposerController extends BaseController with DragDropFileMixin implement
}); });
_subscriptionOnDragEnter = html.window.onDragEnter.listen((event) { _subscriptionOnDragEnter = html.window.onDragEnter.listen((event) {
mailboxDashBoardController.localFileDraggableAppState.value = DraggableAppState.active; event.preventDefault();
if (_validateFilesTransfer(event.dataTransfer.types)) {
mailboxDashBoardController.localFileDraggableAppState.value = DraggableAppState.active;
}
}); });
_subscriptionOnDragOver = html.window.onDragOver.listen((event) { _subscriptionOnDragOver = html.window.onDragOver.listen((event) {
mailboxDashBoardController.localFileDraggableAppState.value = DraggableAppState.active; event.preventDefault();
if (_validateFilesTransfer(event.dataTransfer.types)) {
mailboxDashBoardController.localFileDraggableAppState.value = DraggableAppState.active;
}
}); });
_subscriptionOnDragLeave = html.window.onDragLeave.listen((event) { _subscriptionOnDragLeave = html.window.onDragLeave.listen((event) {
mailboxDashBoardController.localFileDraggableAppState.value = DraggableAppState.inActive; event.preventDefault();
if (_validateFilesTransfer(event.dataTransfer.types)) {
mailboxDashBoardController.localFileDraggableAppState.value = DraggableAppState.inActive;
}
}); });
_subscriptionOnDrop = html.window.onDrop.listen((event) { _subscriptionOnDrop = html.window.onDrop.listen((event) {
mailboxDashBoardController.localFileDraggableAppState.value = DraggableAppState.inActive; event.preventDefault();
if (_validateFilesTransfer(event.dataTransfer.types)) {
mailboxDashBoardController.localFileDraggableAppState.value = DraggableAppState.inActive;
}
}); });
} }
bool _validateFilesTransfer(List<dynamic>? types) {
return types?.any((type) => type == 'Files') ?? false;
}
Future<void> _saveComposerCacheOnWebAction() async { Future<void> _saveComposerCacheOnWebAction() async {
_autoCreateEmailTag(); _autoCreateEmailTag();
@@ -2091,8 +2111,10 @@ class ComposerController extends BaseController with DragDropFileMixin implement
} }
} }
void handleOnDragEnterHtmlEditorWeb() { void handleOnDragEnterHtmlEditorWeb(List<dynamic>? types) {
mailboxDashBoardController.localFileDraggableAppState.value = DraggableAppState.active; if (_validateFilesTransfer(types)) {
mailboxDashBoardController.localFileDraggableAppState.value = DraggableAppState.active;
}
} }
void onLocalFileDropZoneListener({ void onLocalFileDropZoneListener({
@@ -653,133 +653,139 @@ class ComposerView extends GetWidget<ComposerController> {
), ),
Expanded( Expanded(
child: LayoutBuilder( child: LayoutBuilder(
builder: (context, constraintsEditor) { builder: (context, constraintsBody) {
return Container( return Stack(
decoration: const BoxDecoration( children: [
border: Border( Column(
bottom: BorderSide( children: [
color: ComposerStyle.borderColor, Expanded(
width: 1 child: Container(
) decoration: const BoxDecoration(
), border: Border(
color: ComposerStyle.backgroundEditorColor bottom: BorderSide(
), color: ComposerStyle.borderColor,
child: Stack( width: 1
children: [ )
Column( ),
children: [ color: ComposerStyle.backgroundEditorColor
Expanded( ),
child: Padding( child: Column(
padding: ComposerStyle.tabletEditorPadding, children: [
child: Obx(() => WebEditorView( Expanded(
editorController: controller.richTextWebController!.editorController, child: Padding(
arguments: controller.composerArguments.value, padding: ComposerStyle.tabletEditorPadding,
contentViewState: controller.emailContentsViewState.value, child: Obx(() => WebEditorView(
currentWebContent: controller.textEditorWeb, editorController: controller.richTextWebController!.editorController,
onInitial: controller.handleInitHtmlEditorWeb, arguments: controller.composerArguments.value,
onChangeContent: controller.onChangeTextEditorWeb, contentViewState: controller.emailContentsViewState.value,
onFocus: controller.handleOnFocusHtmlEditorWeb, currentWebContent: controller.textEditorWeb,
onMouseDown: controller.handleOnMouseDownHtmlEditorWeb, onInitial: controller.handleInitHtmlEditorWeb,
onEditorSettings: controller.richTextWebController!.onEditorSettingsChange, onChangeContent: controller.onChangeTextEditorWeb,
onEditorTextSizeChanged: controller.richTextWebController!.onEditorTextSizeChanged, onFocus: controller.handleOnFocusHtmlEditorWeb,
width: constraints.maxWidth, onMouseDown: controller.handleOnMouseDownHtmlEditorWeb,
height: constraints.maxHeight, onEditorSettings: controller.richTextWebController!.onEditorSettingsChange,
onDragEnter: controller.handleOnDragEnterHtmlEditorWeb, onEditorTextSizeChanged: controller.richTextWebController!.onEditorTextSizeChanged,
)), width: constraints.maxWidth,
height: constraints.maxHeight,
onDragEnter: controller.handleOnDragEnterHtmlEditorWeb,
)),
),
),
Obx(() {
if (controller.uploadController.listUploadAttachments.isNotEmpty) {
return AttachmentComposerWidget(
listFileUploaded: controller.uploadController.listUploadAttachments,
isCollapsed: controller.isAttachmentCollapsed,
onDeleteAttachmentAction: controller.deleteAttachmentUploaded,
onToggleExpandAttachmentAction: (isCollapsed) => controller.isAttachmentCollapsed = isCollapsed,
);
} else {
return const SizedBox.shrink();
}
}),
Obx(() {
if (controller.richTextWebController!.isFormattingOptionsEnabled) {
return ToolbarRichTextWebBuilder(
richTextWebController: controller.richTextWebController!,
padding: ComposerStyle.richToolbarPadding,
decoration: const BoxDecoration(
color: ComposerStyle.richToolbarColor,
boxShadow: ComposerStyle.richToolbarShadow
),
);
} else {
return const SizedBox.shrink();
}
})
],
), ),
), ),
Obx(() { ),
if (controller.uploadController.listUploadAttachments.isNotEmpty) { Obx(() => BottomBarComposerWidget(
return AttachmentComposerWidget( isCodeViewEnabled: controller.richTextWebController!.codeViewEnabled,
listFileUploaded: controller.uploadController.listUploadAttachments, isFormattingOptionsEnabled: controller.richTextWebController!.isFormattingOptionsEnabled,
isCollapsed: controller.isAttachmentCollapsed, hasReadReceipt: controller.hasRequestReadReceipt.value,
onDeleteAttachmentAction: controller.deleteAttachmentUploaded, openRichToolbarAction: controller.richTextWebController!.toggleFormattingOptions,
onToggleExpandAttachmentAction: (isCollapsed) => controller.isAttachmentCollapsed = isCollapsed, attachFileAction: () => controller.openFilePickerByType(context, FileType.any),
); insertImageAction: () => controller.insertImage(context, constraints.maxWidth),
} else { showCodeViewAction: controller.richTextWebController!.toggleCodeView,
return const SizedBox.shrink(); deleteComposerAction: () => controller.handleClickDeleteComposer(context),
} saveToDraftAction: () => controller.handleClickSaveAsDraftsButton(context),
}), sendMessageAction: () => controller.handleClickSendButton(context),
Obx(() { requestReadReceiptAction: () => controller.toggleRequestReadReceipt(context),
if (controller.richTextWebController!.isFormattingOptionsEnabled) {
return ToolbarRichTextWebBuilder(
richTextWebController: controller.richTextWebController!,
padding: ComposerStyle.richToolbarPadding,
decoration: const BoxDecoration(
color: ComposerStyle.richToolbarColor,
boxShadow: ComposerStyle.richToolbarShadow
),
);
} else {
return const SizedBox.shrink();
}
})
],
),
Align(
alignment: AlignmentDirectional.topCenter,
child: Obx(() => InsertImageLoadingBarWidget(
uploadInlineViewState: controller.uploadController.uploadInlineViewState.value,
viewState: controller.viewState.value,
padding: ComposerStyle.insertImageLoadingBarPadding,
)), )),
), ],
Obx(() { ),
if (controller.mailboxDashBoardController.isAttachmentDraggableAppActive) { Align(
return Positioned.fill( alignment: AlignmentDirectional.topCenter,
child: PointerInterceptor( child: Obx(() => InsertImageLoadingBarWidget(
child: AttachmentDropZoneWidget( uploadInlineViewState: controller.uploadController.uploadInlineViewState.value,
imagePaths: controller.imagePaths, viewState: controller.viewState.value,
width: constraintsEditor.maxWidth, padding: ComposerStyle.insertImageLoadingBarPadding,
height: constraintsEditor.maxHeight, )),
onAttachmentDropZoneListener: controller.onAttachmentDropZoneListener, ),
) Obx(() {
), if (controller.mailboxDashBoardController.isAttachmentDraggableAppActive) {
); return Positioned.fill(
} else { child: PointerInterceptor(
return const SizedBox.shrink(); child: AttachmentDropZoneWidget(
} imagePaths: controller.imagePaths,
}), width: constraintsBody.maxWidth,
Obx(() { height: constraintsBody.maxHeight,
if (controller.mailboxDashBoardController.isLocalFileDraggableAppActive) { onAttachmentDropZoneListener: controller.onAttachmentDropZoneListener,
return Positioned.fill( )
child: PointerInterceptor( ),
child: LocalFileDropZoneWidget( );
imagePaths: controller.imagePaths, } else {
width: constraintsEditor.maxWidth, return const SizedBox.shrink();
height: constraintsEditor.maxHeight, }
onLocalFileDropZoneListener: (details) => }),
controller.onLocalFileDropZoneListener( Obx(() {
context: context, if (controller.mailboxDashBoardController.isLocalFileDraggableAppActive) {
details: details, return Positioned.fill(
maxWidth: constraintsEditor.maxWidth, child: PointerInterceptor(
), child: LocalFileDropZoneWidget(
) imagePaths: controller.imagePaths,
), width: constraintsBody.maxWidth,
); height: constraintsBody.maxHeight,
} else { onLocalFileDropZoneListener: (details) =>
return const SizedBox.shrink(); controller.onLocalFileDropZoneListener(
} context: context,
}), details: details,
], maxWidth: constraintsBody.maxWidth,
), ),
)
),
);
} else {
return const SizedBox.shrink();
}
}),
],
); );
} },
), ),
), )
Obx(() => BottomBarComposerWidget(
isCodeViewEnabled: controller.richTextWebController!.codeViewEnabled,
isFormattingOptionsEnabled: controller.richTextWebController!.isFormattingOptionsEnabled,
hasReadReceipt: controller.hasRequestReadReceipt.value,
openRichToolbarAction: controller.richTextWebController!.toggleFormattingOptions,
attachFileAction: () => controller.openFilePickerByType(context, FileType.any),
insertImageAction: () => controller.insertImage(context, constraints.maxWidth),
showCodeViewAction: controller.richTextWebController!.toggleCodeView,
deleteComposerAction: () => controller.handleClickDeleteComposer(context),
saveToDraftAction: () => controller.handleClickSaveAsDraftsButton(context),
sendMessageAction: () => controller.handleClickSendButton(context),
requestReadReceiptAction: () => controller.toggleRequestReadReceipt(context),
)),
]), ]),
); );
}, },
@@ -14,6 +14,8 @@ import 'package:tmail_ui_user/features/email/domain/state/transform_html_email_c
import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart'; import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart';
import 'package:tmail_ui_user/main/utils/app_utils.dart'; import 'package:tmail_ui_user/main/utils/app_utils.dart';
typedef OnDragEnterListener = Function(List<dynamic>? types);
class WebEditorView extends StatelessWidget with EditorViewMixin { class WebEditorView extends StatelessWidget with EditorViewMixin {
final HtmlEditorController editorController; final HtmlEditorController editorController;
@@ -29,7 +31,7 @@ class WebEditorView extends StatelessWidget with EditorViewMixin {
final OnEditorTextSizeChanged? onEditorTextSizeChanged; final OnEditorTextSizeChanged? onEditorTextSizeChanged;
final double? width; final double? width;
final double? height; final double? height;
final VoidCallback? onDragEnter; final OnDragEnterListener? onDragEnter;
const WebEditorView({ const WebEditorView({
super.key, super.key,
@@ -5,6 +5,7 @@ import 'package:core/utils/app_logger.dart';
import 'package:core/utils/html/html_utils.dart'; import 'package:core/utils/html/html_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:html_editor_enhanced/html_editor.dart'; import 'package:html_editor_enhanced/html_editor.dart';
import 'package:tmail_ui_user/features/composer/presentation/view/web/web_editor_view.dart';
import 'package:universal_html/html.dart' hide VoidCallback; import 'package:universal_html/html.dart' hide VoidCallback;
typedef OnChangeContentEditorAction = Function(String? text); typedef OnChangeContentEditorAction = Function(String? text);
@@ -27,7 +28,7 @@ class WebEditorWidget extends StatefulWidget {
final OnEditorTextSizeChanged? onEditorTextSizeChanged; final OnEditorTextSizeChanged? onEditorTextSizeChanged;
final double? width; final double? width;
final double? height; final double? height;
final VoidCallback? onDragEnter; final OnDragEnterListener? onDragEnter;
const WebEditorWidget({ const WebEditorWidget({
super.key, super.key,
@@ -63,8 +64,6 @@ class _WebEditorState extends State<WebEditorWidget> {
bool _dropListenerRegistered = false; bool _dropListenerRegistered = false;
Function(Event)? _dropListener; Function(Event)? _dropListener;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@@ -175,7 +174,7 @@ class _WebEditorState extends State<WebEditorWidget> {
HtmlUtils.lineHeight100Percent.name HtmlUtils.lineHeight100Percent.name
), ),
onDragEnter: widget.onDragEnter, onDragEnter: widget.onDragEnter,
onDragLeave: () {}, onDragLeave: (_) {},
), ),
); );
} }