diff --git a/packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.tsx b/packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.tsx index 9ee7244a7..c46dc6573 100644 --- a/packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.tsx +++ b/packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.tsx @@ -30,6 +30,7 @@ import shouldPasteResources from './utils/shouldPasteResources'; import lightTheme from '@joplin/lib/themes/light'; import { Options as NoteStyleOptions } from '@joplin/renderer/noteStyle'; import markupRenderOptions from '../../utils/markupRenderOptions'; +import { DropHandler } from '../../utils/useDropHandler'; const md5 = require('md5'); const { clipboard } = require('electron'); const supportedLocales = require('./supportedLocales'); @@ -93,7 +94,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => { const props_onMessage = useRef(null); props_onMessage.current = props.onMessage; - const props_onDrop = useRef(null); + const props_onDrop = useRef(null); props_onDrop.current = props.onDrop; const markupToHtml = useRef(null); @@ -706,16 +707,21 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => { if (editable) openEditDialog(editor, markupToHtml, dispatchDidUpdate, editable); }); - // This is triggered when an external file is dropped on the editor editor.on('drop', (event) => { - // Prevent the message "Dropped file type is not - // supported" to show up. It was added in a recent - // TinyMCE version and doesn't apply since we do support + // Prevent the message "Dropped file type is not supported" from showing up. + // It was added in TinyMCE 5.4 and doesn't apply since we do support // the file type. - // https://stackoverflow.com/questions/64782955/tinymce-inline-drag-and-drop-image-upload-not-working - event.preventDefault(); - - props_onDrop.current(event); + // + // See https://stackoverflow.com/questions/64782955/tinymce-inline-drag-and-drop-image-upload-not-working + // + // The other suggested solution, setting block_unsupported_drop to false, + // causes all dropped files to be placed at the top of the document. + // + // Because .preventDefault cancels TinyMCE's own drop handler, we only + // call .preventDefault if Joplin handled the event: + if (props_onDrop.current(event)) { + event.preventDefault(); + } }); editor.on('ObjectResized', (event) => { diff --git a/packages/app-desktop/gui/NoteEditor/utils/types.ts b/packages/app-desktop/gui/NoteEditor/utils/types.ts index 3ac9422a8..f15181e08 100644 --- a/packages/app-desktop/gui/NoteEditor/utils/types.ts +++ b/packages/app-desktop/gui/NoteEditor/utils/types.ts @@ -5,6 +5,7 @@ import { MarkupLanguage } from '@joplin/renderer'; import { RenderResult, RenderResultPluginAsset } from '@joplin/renderer/types'; import { Dispatch } from 'redux'; import { ProcessResultsRow } from '@joplin/lib/services/search/SearchEngine'; +import { DropHandler } from './useDropHandler'; export interface AllAssetsOptions { contentMaxWidthTarget?: string; @@ -113,7 +114,7 @@ export interface NoteBodyEditorProps { resourceDirectory: string; locale: string; // eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied - onDrop: Function; + onDrop: DropHandler; noteToolbarButtonInfos: ToolbarButtonInfo[]; plugins: PluginStates; fontSize: number; diff --git a/packages/app-desktop/gui/NoteEditor/utils/useDropHandler.ts b/packages/app-desktop/gui/NoteEditor/utils/useDropHandler.ts index 066969bfc..c76f19e62 100644 --- a/packages/app-desktop/gui/NoteEditor/utils/useDropHandler.ts +++ b/packages/app-desktop/gui/NoteEditor/utils/useDropHandler.ts @@ -1,36 +1,44 @@ import { useCallback } from 'react'; import Note from '@joplin/lib/models/Note'; +import { DragEvent as ReactDragEvent } from 'react'; interface HookDependencies { editorRef: any; } -export default function useDropHandler(dependencies: HookDependencies) { +// Returns true if Joplin handled the event +export type DropHandler = (event: DragEvent|ReactDragEvent)=> boolean; + +export default function useDropHandler(dependencies: HookDependencies): DropHandler { const { editorRef } = dependencies; - return useCallback(async (event: any) => { - if (!event.dataTransfer) return; + return useCallback((event: DragEvent|ReactDragEvent) => { + if (!event.dataTransfer) return false; const dt = event.dataTransfer; const createFileURL = event.altKey; if (dt.types.indexOf('text/x-jop-note-ids') >= 0) { const noteIds = JSON.parse(dt.getData('text/x-jop-note-ids')); - const noteMarkdownTags = []; - for (let i = 0; i < noteIds.length; i++) { - const note = await Note.load(noteIds[i]); - noteMarkdownTags.push(Note.markdownTag(note)); - } - editorRef.current.execCommand({ - name: 'dropItems', - value: { - type: 'notes', - markdownTags: noteMarkdownTags, - }, - }); + const dropNotes = async () => { + const noteMarkdownTags = []; + for (let i = 0; i < noteIds.length; i++) { + const note = await Note.load(noteIds[i]); + noteMarkdownTags.push(Note.markdownTag(note)); + } - return; + editorRef.current.execCommand({ + name: 'dropItems', + value: { + type: 'notes', + markdownTags: noteMarkdownTags, + }, + }); + }; + void dropNotes(); + + return true; } const files = dt.files; @@ -50,7 +58,10 @@ export default function useDropHandler(dependencies: HookDependencies) { createFileURL: createFileURL, }, }); + return true; } + + return false; // eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied }, []); }