mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-05-21 18:55:19 -04:00
Move toolbar functions into redux reducer (#1763)
Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
parent
a6a2251c88
commit
b30cc5b390
80 changed files with 2481 additions and 2303 deletions
|
@ -4,6 +4,7 @@
|
|||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import type { MutableRefObject } from 'react'
|
||||
import { useEffect, useRef } from 'react'
|
||||
import type { Editor } from 'codemirror'
|
||||
import type { ScrollState } from '../../synced-scroll/scroll-props'
|
||||
|
@ -11,12 +12,16 @@ import type { ScrollState } from '../../synced-scroll/scroll-props'
|
|||
/**
|
||||
* Monitors the given scroll state and scrolls the editor to the state if changed.
|
||||
*
|
||||
* @param editor The editor that should be manipulated
|
||||
* @param editorRef The editor that should be manipulated
|
||||
* @param scrollState The scroll state that should be monitored
|
||||
*/
|
||||
export const useApplyScrollState = (editor?: Editor, scrollState?: ScrollState): void => {
|
||||
export const useApplyScrollState = (
|
||||
editorRef: MutableRefObject<Editor | undefined>,
|
||||
scrollState?: ScrollState
|
||||
): void => {
|
||||
const lastScrollPosition = useRef<number>()
|
||||
useEffect(() => {
|
||||
const editor = editorRef.current
|
||||
if (!editor || !scrollState) {
|
||||
return
|
||||
}
|
||||
|
@ -28,5 +33,5 @@ export const useApplyScrollState = (editor?: Editor, scrollState?: ScrollState):
|
|||
lastScrollPosition.current = newPosition
|
||||
editor.scrollTo(0, newPosition)
|
||||
}
|
||||
}, [editor, scrollState])
|
||||
}, [editorRef, scrollState])
|
||||
}
|
||||
|
|
|
@ -5,34 +5,31 @@
|
|||
*/
|
||||
|
||||
import type { StatusBarInfo } from '../status-bar/status-bar'
|
||||
import { defaultState } from '../status-bar/status-bar'
|
||||
import type { Editor } from 'codemirror'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { useMemo } from 'react'
|
||||
import { useApplicationState } from '../../../../hooks/common/use-application-state'
|
||||
|
||||
/**
|
||||
* Provides a {@link StatusBarInfo} object and a function that can update this object using a {@link CodeMirror code mirror instance}.
|
||||
*/
|
||||
export const useCreateStatusBarInfo = (): [
|
||||
statusBarInfo: StatusBarInfo,
|
||||
updateStatusBarInfo: (editor: Editor) => void
|
||||
] => {
|
||||
export const useCreateStatusBarInfo = (): StatusBarInfo => {
|
||||
const maxDocumentLength = useApplicationState((state) => state.config.maxDocumentLength)
|
||||
const [statusBarInfo, setStatusBarInfo] = useState(defaultState)
|
||||
const selection = useApplicationState((state) => state.noteDetails.selection)
|
||||
const markdownContent = useApplicationState((state) => state.noteDetails.markdownContent)
|
||||
const markdownContentLines = useApplicationState((state) => state.noteDetails.markdownContentLines)
|
||||
|
||||
const updateStatusBarInfo = useCallback(
|
||||
(editor: Editor): void => {
|
||||
setStatusBarInfo({
|
||||
position: editor.getCursor(),
|
||||
charactersInDocument: editor.getValue().length,
|
||||
remainingCharacters: maxDocumentLength - editor.getValue().length,
|
||||
linesInDocument: editor.lineCount(),
|
||||
selectedColumns: editor.getSelection().length,
|
||||
selectedLines: editor.getSelection().split('\n').length
|
||||
})
|
||||
},
|
||||
[maxDocumentLength]
|
||||
)
|
||||
return useMemo(() => {
|
||||
const startCharacter = selection.from.character
|
||||
const endCharacter = selection.to?.character ?? 0
|
||||
const startLine = selection.from.line
|
||||
const endLine = selection.to?.line ?? 0
|
||||
|
||||
return [statusBarInfo, updateStatusBarInfo]
|
||||
return {
|
||||
position: { line: startLine, character: startCharacter },
|
||||
charactersInDocument: markdownContent.length,
|
||||
remainingCharacters: maxDocumentLength - markdownContent.length,
|
||||
linesInDocument: markdownContentLines.length,
|
||||
selectedColumns: endCharacter - startCharacter,
|
||||
selectedLines: endLine - startLine
|
||||
}
|
||||
}, [markdownContent.length, markdownContentLines.length, maxDocumentLength, selection])
|
||||
}
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import type { Editor } from 'codemirror'
|
||||
import { useCallback } from 'react'
|
||||
import type { CursorPosition } from '../../../../redux/editor/types'
|
||||
import { updateCursorPositions } from '../../../../redux/note-details/methods'
|
||||
|
||||
/**
|
||||
* Provides a callback for codemirror that handles cursor changes
|
||||
*
|
||||
* @return the generated callback
|
||||
*/
|
||||
export const useCursorActivityCallback = (): ((editor: Editor) => void) => {
|
||||
return useCallback((editor) => {
|
||||
const firstSelection = editor.listSelections()[0]
|
||||
if (firstSelection === undefined) {
|
||||
return
|
||||
}
|
||||
const start: CursorPosition = { line: firstSelection.from().line, character: firstSelection.from().ch }
|
||||
const end: CursorPosition = { line: firstSelection.to().line, character: firstSelection.to().ch }
|
||||
updateCursorPositions({
|
||||
from: start,
|
||||
to: start.line === end.line && start.character === end.character ? undefined : end
|
||||
})
|
||||
}, [])
|
||||
}
|
|
@ -39,7 +39,7 @@ export const useOnEditorFileDrop = (): DomEvent => {
|
|||
const newCursor = dropEditor.coordsChar({ top: event.pageY, left: event.pageX }, 'page')
|
||||
dropEditor.setCursor(newCursor)
|
||||
const files: FileList = event.dataTransfer.files
|
||||
handleUpload(files[0], dropEditor)
|
||||
handleUpload(files[0])
|
||||
}
|
||||
}, [])
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ import { useCallback } from 'react'
|
|||
import type { Editor } from 'codemirror'
|
||||
import type { PasteEvent } from '../tool-bar/utils/pasteHandlers'
|
||||
import { handleFilePaste, handleTablePaste } from '../tool-bar/utils/pasteHandlers'
|
||||
import { useApplicationState } from '../../../../hooks/common/use-application-state'
|
||||
import type { DomEvent } from 'react-codemirror2'
|
||||
|
||||
/**
|
||||
|
@ -17,18 +16,13 @@ import type { DomEvent } from 'react-codemirror2'
|
|||
* @return the created callback
|
||||
*/
|
||||
export const useOnEditorPasteCallback = (): DomEvent => {
|
||||
const smartPasteEnabled = useApplicationState((state) => state.editorConfig.smartPaste)
|
||||
|
||||
return useCallback(
|
||||
(pasteEditor: Editor, event: PasteEvent) => {
|
||||
if (!event || !event.clipboardData) {
|
||||
return
|
||||
}
|
||||
if (smartPasteEnabled && handleTablePaste(event, pasteEditor)) {
|
||||
return
|
||||
}
|
||||
handleFilePaste(event, pasteEditor)
|
||||
},
|
||||
[smartPasteEnabled]
|
||||
)
|
||||
return useCallback((pasteEditor: Editor, event: PasteEvent) => {
|
||||
if (!event || !event.clipboardData) {
|
||||
return
|
||||
}
|
||||
if (handleTablePaste(event) || handleFilePaste(event)) {
|
||||
event.preventDefault()
|
||||
return
|
||||
}
|
||||
}, [])
|
||||
}
|
||||
|
|
|
@ -8,56 +8,47 @@ import { useEditorReceiveHandler } from '../../../render-page/window-post-messag
|
|||
import type { ImageUploadMessage } from '../../../render-page/window-post-message-communicator/rendering-message'
|
||||
import { CommunicationMessageType } from '../../../render-page/window-post-message-communicator/rendering-message'
|
||||
import { useCallback } from 'react'
|
||||
import { store } from '../../../../redux'
|
||||
import { getGlobalState } from '../../../../redux'
|
||||
import { handleUpload } from '../upload-handler'
|
||||
import type { Editor, Position } from 'codemirror'
|
||||
import { Logger } from '../../../../utils/logger'
|
||||
import { findRegexMatchInText } from '../find-regex-match-in-text'
|
||||
import Optional from 'optional-js'
|
||||
import type { CursorSelection } from '../../../../redux/editor/types'
|
||||
|
||||
const log = new Logger('useOnImageUpload')
|
||||
const imageWithPlaceholderLinkRegex = /!\[([^\]]*)]\(https:\/\/([^)]*)\)/g
|
||||
|
||||
/**
|
||||
* Receives {@link CommunicationMessageType.IMAGE_UPLOAD image upload events} via iframe communication and processes the attached uploads.
|
||||
*
|
||||
* @param editor The {@link Editor codemirror editor} that should be used to change the markdown code
|
||||
*/
|
||||
export const useOnImageUploadFromRenderer = (editor: Editor | undefined): void => {
|
||||
export const useOnImageUploadFromRenderer = (): void => {
|
||||
useEditorReceiveHandler(
|
||||
CommunicationMessageType.IMAGE_UPLOAD,
|
||||
useCallback(
|
||||
(values: ImageUploadMessage) => {
|
||||
const { dataUri, fileName, lineIndex, placeholderIndexInLine } = values
|
||||
if (!editor) {
|
||||
return
|
||||
}
|
||||
if (!dataUri.startsWith('data:image/')) {
|
||||
log.error('Received uri is no data uri and image!')
|
||||
return
|
||||
}
|
||||
useCallback((values: ImageUploadMessage) => {
|
||||
const { dataUri, fileName, lineIndex, placeholderIndexInLine } = values
|
||||
if (!dataUri.startsWith('data:image/')) {
|
||||
log.error('Received uri is no data uri and image!')
|
||||
return
|
||||
}
|
||||
|
||||
fetch(dataUri)
|
||||
.then((result) => result.blob())
|
||||
.then((blob) => {
|
||||
const file = new File([blob], fileName, { type: blob.type })
|
||||
const { cursorFrom, cursorTo, description, additionalText } = Optional.ofNullable(lineIndex)
|
||||
.map((actualLineIndex) => findPlaceholderInMarkdownContent(actualLineIndex, placeholderIndexInLine))
|
||||
.orElseGet(() => calculateInsertAtCurrentCursorPosition(editor))
|
||||
handleUpload(file, editor, cursorFrom, cursorTo, description, additionalText)
|
||||
})
|
||||
.catch((error) => log.error(error))
|
||||
},
|
||||
[editor]
|
||||
)
|
||||
fetch(dataUri)
|
||||
.then((result) => result.blob())
|
||||
.then((blob) => {
|
||||
const file = new File([blob], fileName, { type: blob.type })
|
||||
const { cursorSelection, alt, title } = Optional.ofNullable(lineIndex)
|
||||
.map((actualLineIndex) => findPlaceholderInMarkdownContent(actualLineIndex, placeholderIndexInLine))
|
||||
.orElseGet(() => ({}))
|
||||
handleUpload(file, cursorSelection, alt, title)
|
||||
})
|
||||
.catch((error) => log.error(error))
|
||||
}, [])
|
||||
)
|
||||
}
|
||||
|
||||
export interface ExtractResult {
|
||||
cursorFrom: Position
|
||||
cursorTo: Position
|
||||
description?: string
|
||||
additionalText?: string
|
||||
cursorSelection?: CursorSelection
|
||||
alt?: string
|
||||
title?: string
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -68,7 +59,7 @@ export interface ExtractResult {
|
|||
* @return the calculated start and end position or undefined if no position could be determined
|
||||
*/
|
||||
const findPlaceholderInMarkdownContent = (lineIndex: number, replacementIndexInLine = 0): ExtractResult | undefined => {
|
||||
const currentMarkdownContentLines = store.getState().noteDetails.markdownContent.split('\n')
|
||||
const currentMarkdownContentLines = getGlobalState().noteDetails.markdownContent.split('\n')
|
||||
const lineAtIndex = currentMarkdownContentLines[lineIndex]
|
||||
if (lineAtIndex === undefined) {
|
||||
return
|
||||
|
@ -95,26 +86,17 @@ const findImagePlaceholderInLine = (
|
|||
}
|
||||
|
||||
return {
|
||||
cursorFrom: {
|
||||
ch: startOfImageTag.index,
|
||||
line: lineIndex
|
||||
cursorSelection: {
|
||||
from: {
|
||||
character: startOfImageTag.index,
|
||||
line: lineIndex
|
||||
},
|
||||
to: {
|
||||
character: startOfImageTag.index + startOfImageTag[0].length,
|
||||
line: lineIndex
|
||||
}
|
||||
},
|
||||
cursorTo: {
|
||||
ch: startOfImageTag.index + startOfImageTag[0].length,
|
||||
line: lineIndex
|
||||
},
|
||||
description: startOfImageTag[1],
|
||||
additionalText: startOfImageTag[2]
|
||||
alt: startOfImageTag[1],
|
||||
title: startOfImageTag[2]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates a fallback position that is the current editor cursor position.
|
||||
* This wouldn't replace anything and only insert.
|
||||
*
|
||||
* @param editor The editor whose cursor should be used
|
||||
*/
|
||||
const calculateInsertAtCurrentCursorPosition = (editor: Editor): ExtractResult => {
|
||||
const editorCursor = editor.getCursor()
|
||||
return { cursorFrom: editorCursor, cursorTo: editorCursor }
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue