mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2024-11-26 11:43:59 -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 { useMemo } from 'react'
|
||||||
import { EditorView } from '@codemirror/view'
|
import { EditorView } from '@codemirror/view'
|
||||||
import type { Extension } from '@codemirror/state'
|
import type { Extension } from '@codemirror/state'
|
||||||
import { handleUpload } from '../use-handle-upload'
|
import { useHandleUpload } from '../use-handle-upload'
|
||||||
import { Optional } from '@mrdrogdrog/optional'
|
import { Optional } from '@mrdrogdrog/optional'
|
||||||
|
|
||||||
const calculateCursorPositionInEditor = (view: EditorView, event: MouseEvent): number => {
|
const calculateCursorPositionInEditor = (view: EditorView, event: MouseEvent): number => {
|
||||||
|
@ -32,6 +32,8 @@ const extractFirstFile = (fileList?: FileList): Optional<File> => {
|
||||||
* @return the code mirror callback
|
* @return the code mirror callback
|
||||||
*/
|
*/
|
||||||
export const useCodeMirrorFileInsertExtension = (): Extension => {
|
export const useCodeMirrorFileInsertExtension = (): Extension => {
|
||||||
|
const handleUpload = useHandleUpload()
|
||||||
|
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
return EditorView.domEventHandlers({
|
return EditorView.domEventHandlers({
|
||||||
drop: (event, view) => {
|
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 { Optional } from '@mrdrogdrog/optional'
|
||||||
import { useHandleUpload } from '../use-handle-upload'
|
import { useHandleUpload } from '../use-handle-upload'
|
||||||
import type { CursorSelection } from '../../tool-bar/formatters/types/cursor-selection'
|
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 log = new Logger('useOnImageUpload')
|
||||||
const imageWithPlaceholderLinkRegex = /!\[([^\]]*)]\(https:\/\/([^)]*)\)/g
|
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.
|
* Receives {@link CommunicationMessageType.IMAGE_UPLOAD image upload events} via iframe communication and processes the attached uploads.
|
||||||
*/
|
*/
|
||||||
export const useOnImageUploadFromRenderer = (): void => {
|
export const useOnImageUploadFromRenderer = (): void => {
|
||||||
|
const codeMirrorReference = useCodeMirrorReference()
|
||||||
const handleUpload = useHandleUpload()
|
const handleUpload = useHandleUpload()
|
||||||
|
|
||||||
useEditorReceiveHandler(
|
useEditorReceiveHandler(
|
||||||
CommunicationMessageType.IMAGE_UPLOAD,
|
CommunicationMessageType.IMAGE_UPLOAD,
|
||||||
useCallback(
|
useCallback(
|
||||||
(values: ImageUploadMessage) => {
|
(values: ImageUploadMessage) => {
|
||||||
|
if (codeMirrorReference === undefined) {
|
||||||
|
log.error("Can't upload image without codemirror reference")
|
||||||
|
return
|
||||||
|
}
|
||||||
const { dataUri, fileName, lineIndex, placeholderIndexInLine } = values
|
const { dataUri, fileName, lineIndex, placeholderIndexInLine } = values
|
||||||
if (!dataUri.startsWith('data:image/')) {
|
if (!dataUri.startsWith('data:image/')) {
|
||||||
log.error('Received uri is no data uri and image!')
|
log.error('Received uri is no data uri and image!')
|
||||||
|
@ -44,11 +50,11 @@ export const useOnImageUploadFromRenderer = (): void => {
|
||||||
return findPlaceholderInMarkdownContent(actualLineIndex + lineOffset, placeholderIndexInLine)
|
return findPlaceholderInMarkdownContent(actualLineIndex + lineOffset, placeholderIndexInLine)
|
||||||
})
|
})
|
||||||
.orElse({} as ExtractResult)
|
.orElse({} as ExtractResult)
|
||||||
handleUpload(file, cursorSelection, alt, title)
|
handleUpload(codeMirrorReference, file, cursorSelection, alt, title)
|
||||||
})
|
})
|
||||||
.catch((error) => log.error(error))
|
.catch((error) => log.error(error))
|
||||||
},
|
},
|
||||||
[handleUpload]
|
[codeMirrorReference, handleUpload]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ import { uploadFile } from '../../../../api/media'
|
||||||
import { getGlobalState } from '../../../../redux'
|
import { getGlobalState } from '../../../../redux'
|
||||||
import { supportedMimeTypes } from '../../../common/upload-image-mimetypes'
|
import { supportedMimeTypes } from '../../../common/upload-image-mimetypes'
|
||||||
import { t } from 'i18next'
|
import { t } from 'i18next'
|
||||||
import { showErrorNotification } from '../../../../redux/ui-notifications/methods'
|
|
||||||
import { useCallback } from 'react'
|
import { useCallback } from 'react'
|
||||||
import { changeEditorContent } from '../../change-content-context/use-change-editor-content-callback'
|
import { changeEditorContent } from '../../change-content-context/use-change-editor-content-callback'
|
||||||
import { replaceSelection } from '../tool-bar/formatters/replace-selection'
|
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 { CursorSelection } from '../tool-bar/formatters/types/cursor-selection'
|
||||||
import type { EditorView } from '@codemirror/view'
|
import type { EditorView } from '@codemirror/view'
|
||||||
import type { ContentFormatter } from '../../change-content-context/change-content-context'
|
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 view the codemirror instance that is used to insert the Markdown code
|
||||||
* @param file The file to upload
|
* @param file The file to upload
|
||||||
* @param cursorSelection The position where the progress message should be placed
|
* @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 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
|
* @param additionalUrlText Additional text that should be inserted behind the link but within the tag
|
||||||
*/
|
*/
|
||||||
export const handleUpload = (
|
type handleUploadSignature = (
|
||||||
view: EditorView,
|
view: EditorView,
|
||||||
file: File,
|
file: File,
|
||||||
cursorSelection?: CursorSelection,
|
cursorSelection?: CursorSelection,
|
||||||
description?: string,
|
description?: string,
|
||||||
additionalUrlText?: string
|
additionalUrlText?: string
|
||||||
): void => {
|
) => 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
|
|
||||||
])
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides a callback that uploads the given file and writes the progress into the given editor at the given cursor positions.
|
* Provides a callback that uploads a given file and inserts the correct Markdown code into the current editor.
|
||||||
*
|
|
||||||
* @return The generated callback
|
|
||||||
*/
|
*/
|
||||||
export const useHandleUpload = (): ((
|
export const useHandleUpload = (): handleUploadSignature => {
|
||||||
file: File,
|
return useCallback((view, file, cursorSelection, description, additionalUrlText) => {
|
||||||
cursorSelection?: CursorSelection,
|
const changeContent = (callback: ContentFormatter) => changeEditorContent(view, callback)
|
||||||
description?: string,
|
if (!file || !supportedMimeTypes.includes(file.type) || !changeContent) {
|
||||||
additionalUrlText?: string
|
return
|
||||||
) => void) => {
|
}
|
||||||
const codeMirrorReference = useCodeMirrorReference()
|
const randomId = Math.random().toString(36).slice(7)
|
||||||
return useCallback(
|
const uploadFileInfo = description
|
||||||
(file: File, cursorSelection?: CursorSelection, description?: string, additionalUrlText?: string): void => {
|
? t('editor.upload.uploadFile.withDescription', { fileName: file.name, description: description })
|
||||||
if (codeMirrorReference) {
|
: t('editor.upload.uploadFile.withoutDescription', { fileName: file.name })
|
||||||
handleUpload(codeMirrorReference, file, cursorSelection, description, additionalUrlText)
|
|
||||||
}
|
const uploadPlaceholder = `![${uploadFileInfo}](upload-${randomId}${additionalUrlText ?? ''})`
|
||||||
},
|
const noteId = getGlobalState().noteDetails.id
|
||||||
[codeMirrorReference]
|
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 { useCodeMirrorReference } from '../../../change-content-context/change-content-context'
|
||||||
import { extractSelectedText } from './extract-selected-text'
|
import { extractSelectedText } from './extract-selected-text'
|
||||||
import { Optional } from '@mrdrogdrog/optional'
|
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.
|
* 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?.()
|
clickRef.current?.()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const handleUpload = useHandleUpload()
|
|
||||||
const codeMirror = useCodeMirrorReference()
|
const codeMirror = useCodeMirrorReference()
|
||||||
|
const handleUpload = useHandleUpload()
|
||||||
|
|
||||||
const onUploadImage = useCallback(
|
const onUploadImage = useCallback(
|
||||||
(file: File) => {
|
(file: File) => {
|
||||||
|
if (codeMirror === undefined) {
|
||||||
|
logger.error("can't upload image without codemirror reference")
|
||||||
|
return
|
||||||
|
}
|
||||||
const description = Optional.ofNullable(codeMirror?.state)
|
const description = Optional.ofNullable(codeMirror?.state)
|
||||||
.map((state) => extractSelectedText(state))
|
.map((state) => extractSelectedText(state))
|
||||||
.orElse(undefined)
|
.orElse(undefined)
|
||||||
handleUpload(file, undefined, description)
|
handleUpload(codeMirror, file, undefined, description)
|
||||||
},
|
},
|
||||||
[codeMirror, handleUpload]
|
[codeMirror, handleUpload]
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in a new issue