mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2024-11-22 17:56:30 -05:00
fix: Move file upload logic into hook
Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
parent
ccbbaeb843
commit
b797f07aa5
4 changed files with 58 additions and 61 deletions
|
@ -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])
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
])
|
||||
})
|
||||
}, [])
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue