fix: Move file upload logic into hook

Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
Tilman Vatteroth 2022-08-21 12:51:04 +02:00
parent ccbbaeb843
commit b797f07aa5
4 changed files with 58 additions and 61 deletions

View file

@ -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<File> => {
* @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])
}

View file

@ -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]
)
)
}

View file

@ -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
])
})
}, [])
}

View file

@ -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]
)