diff --git a/src/components/editor-page/editor-pane/hooks/code-mirror-extensions/use-code-mirror-file-insert-extension.ts b/src/components/editor-page/editor-pane/hooks/code-mirror-extensions/use-code-mirror-file-insert-extension.ts index 74b04a537..13d203b12 100644 --- a/src/components/editor-page/editor-pane/hooks/code-mirror-extensions/use-code-mirror-file-insert-extension.ts +++ b/src/components/editor-page/editor-pane/hooks/code-mirror-extensions/use-code-mirror-file-insert-extension.ts @@ -7,7 +7,7 @@ import { useMemo } from 'react' import { EditorView } from '@codemirror/view' import type { Extension } from '@codemirror/state' -import { handleUpload } from '../use-handle-upload' +import { useHandleUpload } from '../use-handle-upload' import { Optional } from '@mrdrogdrog/optional' const calculateCursorPositionInEditor = (view: EditorView, event: MouseEvent): number => { @@ -32,6 +32,8 @@ const extractFirstFile = (fileList?: FileList): Optional => { * @return the code mirror callback */ export const useCodeMirrorFileInsertExtension = (): Extension => { + const handleUpload = useHandleUpload() + return useMemo(() => { return EditorView.domEventHandlers({ drop: (event, view) => { @@ -47,5 +49,5 @@ export const useCodeMirrorFileInsertExtension = (): Extension => { }) } }) - }, []) + }, [handleUpload]) } diff --git a/src/components/editor-page/editor-pane/hooks/image-upload-from-renderer/use-on-image-upload-from-renderer.ts b/src/components/editor-page/editor-pane/hooks/image-upload-from-renderer/use-on-image-upload-from-renderer.ts index 3ffed9fe2..7b55d3087 100644 --- a/src/components/editor-page/editor-pane/hooks/image-upload-from-renderer/use-on-image-upload-from-renderer.ts +++ b/src/components/editor-page/editor-pane/hooks/image-upload-from-renderer/use-on-image-upload-from-renderer.ts @@ -14,6 +14,7 @@ import { findRegexMatchInText } from './find-regex-match-in-text' import { Optional } from '@mrdrogdrog/optional' import { useHandleUpload } from '../use-handle-upload' import type { CursorSelection } from '../../tool-bar/formatters/types/cursor-selection' +import { useCodeMirrorReference } from '../../../change-content-context/change-content-context' const log = new Logger('useOnImageUpload') const imageWithPlaceholderLinkRegex = /!\[([^\]]*)]\(https:\/\/([^)]*)\)/g @@ -22,12 +23,17 @@ const imageWithPlaceholderLinkRegex = /!\[([^\]]*)]\(https:\/\/([^)]*)\)/g * Receives {@link CommunicationMessageType.IMAGE_UPLOAD image upload events} via iframe communication and processes the attached uploads. */ export const useOnImageUploadFromRenderer = (): void => { + const codeMirrorReference = useCodeMirrorReference() const handleUpload = useHandleUpload() useEditorReceiveHandler( CommunicationMessageType.IMAGE_UPLOAD, useCallback( (values: ImageUploadMessage) => { + if (codeMirrorReference === undefined) { + log.error("Can't upload image without codemirror reference") + return + } const { dataUri, fileName, lineIndex, placeholderIndexInLine } = values if (!dataUri.startsWith('data:image/')) { log.error('Received uri is no data uri and image!') @@ -44,11 +50,11 @@ export const useOnImageUploadFromRenderer = (): void => { return findPlaceholderInMarkdownContent(actualLineIndex + lineOffset, placeholderIndexInLine) }) .orElse({} as ExtractResult) - handleUpload(file, cursorSelection, alt, title) + handleUpload(codeMirrorReference, file, cursorSelection, alt, title) }) .catch((error) => log.error(error)) }, - [handleUpload] + [codeMirrorReference, handleUpload] ) ) } diff --git a/src/components/editor-page/editor-pane/hooks/use-handle-upload.tsx b/src/components/editor-page/editor-pane/hooks/use-handle-upload.tsx index ce36f8d81..e197ce6e9 100644 --- a/src/components/editor-page/editor-pane/hooks/use-handle-upload.tsx +++ b/src/components/editor-page/editor-pane/hooks/use-handle-upload.tsx @@ -8,7 +8,6 @@ import { uploadFile } from '../../../../api/media' import { getGlobalState } from '../../../../redux' import { supportedMimeTypes } from '../../../common/upload-image-mimetypes' import { t } from 'i18next' -import { showErrorNotification } from '../../../../redux/ui-notifications/methods' import { useCallback } from 'react' import { changeEditorContent } from '../../change-content-context/use-change-editor-content-callback' import { replaceSelection } from '../tool-bar/formatters/replace-selection' @@ -16,74 +15,57 @@ import { replaceInContent } from '../tool-bar/formatters/replace-in-content' import type { CursorSelection } from '../tool-bar/formatters/types/cursor-selection' import type { EditorView } from '@codemirror/view' import type { ContentFormatter } from '../../change-content-context/change-content-context' -import { useCodeMirrorReference } from '../../change-content-context/change-content-context' +import { showErrorNotification } from '../../../../redux/ui-notifications/methods' /** - * Processes the upload of the given file and inserts the correct Markdown code. - * * @param view the codemirror instance that is used to insert the Markdown code * @param file The file to upload * @param cursorSelection The position where the progress message should be placed * @param description The text that should be used in the description part of the resulting image tag * @param additionalUrlText Additional text that should be inserted behind the link but within the tag */ -export const handleUpload = ( +type handleUploadSignature = ( view: EditorView, file: File, cursorSelection?: CursorSelection, description?: string, additionalUrlText?: string -): void => { - const changeContent = (callback: ContentFormatter) => changeEditorContent(view, callback) - if (!file || !supportedMimeTypes.includes(file.type) || !changeContent) { - return - } - const randomId = Math.random().toString(36).slice(7) - const uploadFileInfo = description - ? t('editor.upload.uploadFile.withDescription', { fileName: file.name, description: description }) - : t('editor.upload.uploadFile.withoutDescription', { fileName: file.name }) - - const uploadPlaceholder = `![${uploadFileInfo}](upload-${randomId}${additionalUrlText ?? ''})` - const noteId = getGlobalState().noteDetails.id - changeContent(({ currentSelection }) => { - return replaceSelection(cursorSelection ?? currentSelection, uploadPlaceholder, false) - }) - uploadFile(noteId, file) - .then(({ url }) => { - const replacement = `![${description ?? file.name ?? ''}](${url}${additionalUrlText ?? ''})` - changeContent(({ markdownContent }) => [ - replaceInContent(markdownContent, uploadPlaceholder, replacement), - undefined - ]) - }) - .catch((error: Error) => { - showErrorNotification('editor.upload.failed', { fileName: file.name })(error) - const replacement = `![upload of ${file.name} failed]()` - changeContent(({ markdownContent }) => [ - replaceInContent(markdownContent, uploadPlaceholder, replacement), - undefined - ]) - }) -} +) => void /** - * Provides a callback that uploads the given file and writes the progress into the given editor at the given cursor positions. - * - * @return The generated callback + * Provides a callback that uploads a given file and inserts the correct Markdown code into the current editor. */ -export const useHandleUpload = (): (( - file: File, - cursorSelection?: CursorSelection, - description?: string, - additionalUrlText?: string -) => void) => { - const codeMirrorReference = useCodeMirrorReference() - return useCallback( - (file: File, cursorSelection?: CursorSelection, description?: string, additionalUrlText?: string): void => { - if (codeMirrorReference) { - handleUpload(codeMirrorReference, file, cursorSelection, description, additionalUrlText) - } - }, - [codeMirrorReference] - ) +export const useHandleUpload = (): handleUploadSignature => { + return useCallback((view, file, cursorSelection, description, additionalUrlText) => { + const changeContent = (callback: ContentFormatter) => changeEditorContent(view, callback) + if (!file || !supportedMimeTypes.includes(file.type) || !changeContent) { + return + } + const randomId = Math.random().toString(36).slice(7) + const uploadFileInfo = description + ? t('editor.upload.uploadFile.withDescription', { fileName: file.name, description: description }) + : t('editor.upload.uploadFile.withoutDescription', { fileName: file.name }) + + const uploadPlaceholder = `![${uploadFileInfo}](upload-${randomId}${additionalUrlText ?? ''})` + const noteId = getGlobalState().noteDetails.id + changeContent(({ currentSelection }) => { + return replaceSelection(cursorSelection ?? currentSelection, uploadPlaceholder, false) + }) + uploadFile(noteId, file) + .then(({ url }) => { + const replacement = `![${description ?? file.name ?? ''}](${url}${additionalUrlText ?? ''})` + changeContent(({ markdownContent }) => [ + replaceInContent(markdownContent, uploadPlaceholder, replacement), + undefined + ]) + }) + .catch((error: Error) => { + showErrorNotification('editor.upload.failed', { fileName: file.name })(error) + const replacement = `![upload of ${file.name} failed]()` + changeContent(({ markdownContent }) => [ + replaceInContent(markdownContent, uploadPlaceholder, replacement), + undefined + ]) + }) + }, []) } diff --git a/src/components/editor-page/editor-pane/tool-bar/upload-image-button/upload-image-button.tsx b/src/components/editor-page/editor-pane/tool-bar/upload-image-button/upload-image-button.tsx index 5f5300a57..1d6297c9c 100644 --- a/src/components/editor-page/editor-pane/tool-bar/upload-image-button/upload-image-button.tsx +++ b/src/components/editor-page/editor-pane/tool-bar/upload-image-button/upload-image-button.tsx @@ -16,6 +16,9 @@ import { ShowIf } from '../../../../common/show-if/show-if' import { useCodeMirrorReference } from '../../../change-content-context/change-content-context' import { extractSelectedText } from './extract-selected-text' import { Optional } from '@mrdrogdrog/optional' +import { Logger } from '../../../../../utils/logger' + +const logger = new Logger('Upload image button') /** * Shows a button that uploads a chosen file to the backend and adds the link to the note. @@ -27,15 +30,19 @@ export const UploadImageButton: React.FC = () => { clickRef.current?.() }, []) - const handleUpload = useHandleUpload() const codeMirror = useCodeMirrorReference() + const handleUpload = useHandleUpload() const onUploadImage = useCallback( (file: File) => { + if (codeMirror === undefined) { + logger.error("can't upload image without codemirror reference") + return + } const description = Optional.ofNullable(codeMirror?.state) .map((state) => extractSelectedText(state)) .orElse(undefined) - handleUpload(file, undefined, description) + handleUpload(codeMirror, file, undefined, description) }, [codeMirror, handleUpload] )