mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-01-14 12:51:50 +00:00
* Added editor-preferences to redux store * Add local-storage saving and retrieval of EditorConfig * Change import to be in a single line * Add equality check to redux-selector (as suggested by @mrdrogdrog) * Save and load editor-config to/from localStorage
This commit is contained in:
parent
a86d4cbc58
commit
f636e5ec10
6 changed files with 90 additions and 29 deletions
|
@ -20,6 +20,7 @@ import 'codemirror/keymap/emacs'
|
||||||
import 'codemirror/keymap/sublime'
|
import 'codemirror/keymap/sublime'
|
||||||
import 'codemirror/keymap/vim'
|
import 'codemirror/keymap/vim'
|
||||||
import 'codemirror/mode/gfm/gfm'
|
import 'codemirror/mode/gfm/gfm'
|
||||||
|
import equal from 'fast-deep-equal'
|
||||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||||
import { Controlled as ControlledCodeMirror } from 'react-codemirror2'
|
import { Controlled as ControlledCodeMirror } from 'react-codemirror2'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
@ -60,12 +61,7 @@ export const EditorPane: React.FC<EditorPaneProps & ScrollProps> = ({ onContentC
|
||||||
const maxLengthWarningAlreadyShown = useRef(false)
|
const maxLengthWarningAlreadyShown = useRef(false)
|
||||||
const [editor, setEditor] = useState<Editor>()
|
const [editor, setEditor] = useState<Editor>()
|
||||||
const [statusBarInfo, setStatusBarInfo] = useState<StatusBarInfo>(defaultState)
|
const [statusBarInfo, setStatusBarInfo] = useState<StatusBarInfo>(defaultState)
|
||||||
const [editorPreferences, setEditorPreferences] = useState<EditorConfiguration>({
|
const editorPreferences = useSelector((state: ApplicationState) => state.editorConfig.preferences, equal)
|
||||||
theme: 'one-dark',
|
|
||||||
keyMap: 'sublime',
|
|
||||||
indentUnit: 4,
|
|
||||||
indentWithTabs: false
|
|
||||||
})
|
|
||||||
|
|
||||||
const lastScrollPosition = useRef<number>()
|
const lastScrollPosition = useRef<number>()
|
||||||
const [editorScroll, setEditorScroll] = useState<ScrollInfo>()
|
const [editorScroll, setEditorScroll] = useState<ScrollInfo>()
|
||||||
|
@ -158,8 +154,6 @@ export const EditorPane: React.FC<EditorPaneProps & ScrollProps> = ({ onContentC
|
||||||
<MaxLengthWarningModal show={showMaxLengthWarning} onHide={() => setShowMaxLengthWarning(false)} maxLength={maxLength}/>
|
<MaxLengthWarningModal show={showMaxLengthWarning} onHide={() => setShowMaxLengthWarning(false)} maxLength={maxLength}/>
|
||||||
<ToolBar
|
<ToolBar
|
||||||
editor={editor}
|
editor={editor}
|
||||||
onPreferencesChange={config => setEditorPreferences(config)}
|
|
||||||
editorPreferences={editorPreferences}
|
|
||||||
/>
|
/>
|
||||||
<ControlledCodeMirror
|
<ControlledCodeMirror
|
||||||
className="overflow-hidden w-100 flex-fill"
|
className="overflow-hidden w-100 flex-fill"
|
||||||
|
|
|
@ -1,23 +1,23 @@
|
||||||
import { EditorConfiguration } from 'codemirror'
|
import { EditorConfiguration } from 'codemirror'
|
||||||
|
import equal from 'fast-deep-equal'
|
||||||
import React, { Fragment, useCallback, useState } from 'react'
|
import React, { Fragment, useCallback, useState } from 'react'
|
||||||
import { Button, Form, ListGroup } from 'react-bootstrap'
|
import { Button, Form, ListGroup } from 'react-bootstrap'
|
||||||
import { Trans, useTranslation } from 'react-i18next'
|
import { Trans, useTranslation } from 'react-i18next'
|
||||||
|
import { useSelector } from 'react-redux'
|
||||||
|
import { ApplicationState } from '../../../../../redux'
|
||||||
|
import { setEditorPreferences } from '../../../../../redux/editor/methods'
|
||||||
import { ForkAwesomeIcon } from '../../../../common/fork-awesome/fork-awesome-icon'
|
import { ForkAwesomeIcon } from '../../../../common/fork-awesome/fork-awesome-icon'
|
||||||
import { CommonModal } from '../../../../common/modals/common-modal'
|
import { CommonModal } from '../../../../common/modals/common-modal'
|
||||||
import { EditorPreferenceProperty, EditorPreferenceSelect } from './editor-preference-select'
|
import { EditorPreferenceProperty, EditorPreferenceSelect } from './editor-preference-select'
|
||||||
|
|
||||||
export interface EditorSettingsButtonProps {
|
export const EditorPreferences: React.FC = () => {
|
||||||
preferences: EditorConfiguration
|
|
||||||
onPreferencesChange: (config: EditorConfiguration) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
export const EditorPreferences: React.FC<EditorSettingsButtonProps> = ({ onPreferencesChange, preferences }) => {
|
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const [showModal, setShowModal] = useState(false)
|
const [showModal, setShowModal] = useState(false)
|
||||||
|
const preferences = useSelector((state: ApplicationState) => state.editorConfig.preferences, equal)
|
||||||
|
|
||||||
const sendPreferences = useCallback((newPreferences: EditorConfiguration) => {
|
const sendPreferences = useCallback((newPreferences: EditorConfiguration) => {
|
||||||
onPreferencesChange(newPreferences)
|
setEditorPreferences(newPreferences)
|
||||||
}, [onPreferencesChange])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Editor, EditorConfiguration } from 'codemirror'
|
import { Editor } from 'codemirror'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Button, ButtonGroup, ButtonToolbar } from 'react-bootstrap'
|
import { Button, ButtonGroup, ButtonToolbar } from 'react-bootstrap'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
@ -28,11 +28,9 @@ import {
|
||||||
|
|
||||||
export interface ToolBarProps {
|
export interface ToolBarProps {
|
||||||
editor: Editor | undefined
|
editor: Editor | undefined
|
||||||
onPreferencesChange: (config: EditorConfiguration) => void
|
|
||||||
editorPreferences: EditorConfiguration
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ToolBar: React.FC<ToolBarProps> = ({ editor, onPreferencesChange, editorPreferences }) => {
|
export const ToolBar: React.FC<ToolBarProps> = ({ editor }) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
const notImplemented = () => {
|
const notImplemented = () => {
|
||||||
|
@ -109,7 +107,7 @@ export const ToolBar: React.FC<ToolBarProps> = ({ editor, onPreferencesChange, e
|
||||||
<EmojiPickerButton editor={editor}/>
|
<EmojiPickerButton editor={editor}/>
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
<ButtonGroup className={'mx-1 flex-wrap'}>
|
<ButtonGroup className={'mx-1 flex-wrap'}>
|
||||||
<EditorPreferences onPreferencesChange={onPreferencesChange} preferences={editorPreferences}/>
|
<EditorPreferences/>
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
</ButtonToolbar>
|
</ButtonToolbar>
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,6 +1,34 @@
|
||||||
|
import { EditorConfiguration } from 'codemirror'
|
||||||
import { store } from '..'
|
import { store } from '..'
|
||||||
import { EditorMode } from '../../components/editor/app-bar/editor-view-mode'
|
import { EditorMode } from '../../components/editor/app-bar/editor-view-mode'
|
||||||
import { EditorConfigActionType, SetEditorConfigAction, SetEditorSyncScrollAction } from './types'
|
import {
|
||||||
|
EditorConfig,
|
||||||
|
EditorConfigActionType,
|
||||||
|
SetEditorConfigAction,
|
||||||
|
SetEditorPreferencesAction,
|
||||||
|
SetEditorSyncScrollAction
|
||||||
|
} from './types'
|
||||||
|
|
||||||
|
export const loadFromLocalStorage = (): EditorConfig | undefined => {
|
||||||
|
try {
|
||||||
|
const stored = window.localStorage.getItem('editorConfig')
|
||||||
|
if (!stored) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
return JSON.parse(stored) as EditorConfig
|
||||||
|
} catch (_) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const saveToLocalStorage = (editorConfig: EditorConfig): void => {
|
||||||
|
try {
|
||||||
|
const json = JSON.stringify(editorConfig)
|
||||||
|
localStorage.setItem('editorConfig', json)
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Can not persist editor config in local storage: ', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const setEditorMode = (editorMode: EditorMode): void => {
|
export const setEditorMode = (editorMode: EditorMode): void => {
|
||||||
const action: SetEditorConfigAction = {
|
const action: SetEditorConfigAction = {
|
||||||
|
@ -17,3 +45,13 @@ export const setEditorSyncScroll = (syncScroll: boolean): void => {
|
||||||
}
|
}
|
||||||
store.dispatch(action)
|
store.dispatch(action)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const setEditorPreferences = (preferences: EditorConfiguration): void => {
|
||||||
|
const action: SetEditorPreferencesAction = {
|
||||||
|
type: EditorConfigActionType.SET_EDITOR_PREFERENCES,
|
||||||
|
preferences: {
|
||||||
|
...preferences
|
||||||
|
}
|
||||||
|
}
|
||||||
|
store.dispatch(action)
|
||||||
|
}
|
||||||
|
|
|
@ -1,30 +1,54 @@
|
||||||
import { Reducer } from 'redux'
|
import { Reducer } from 'redux'
|
||||||
|
import { EditorMode } from '../../components/editor/app-bar/editor-view-mode'
|
||||||
|
import { loadFromLocalStorage, saveToLocalStorage } from './methods'
|
||||||
import {
|
import {
|
||||||
EditorConfig,
|
EditorConfig,
|
||||||
EditorConfigActions,
|
EditorConfigActions,
|
||||||
EditorConfigActionType,
|
EditorConfigActionType,
|
||||||
SetEditorConfigAction,
|
SetEditorConfigAction,
|
||||||
|
SetEditorPreferencesAction,
|
||||||
SetEditorSyncScrollAction
|
SetEditorSyncScrollAction
|
||||||
} from './types'
|
} from './types'
|
||||||
import { EditorMode } from '../../components/editor/app-bar/editor-view-mode'
|
|
||||||
|
|
||||||
export const initialState: EditorConfig = {
|
const initialState: EditorConfig = {
|
||||||
editorMode: EditorMode.BOTH,
|
editorMode: EditorMode.BOTH,
|
||||||
syncScroll: true
|
syncScroll: true,
|
||||||
|
preferences: {
|
||||||
|
theme: 'one-dark',
|
||||||
|
keyMap: 'sublime',
|
||||||
|
indentUnit: 4,
|
||||||
|
indentWithTabs: false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const EditorConfigReducer: Reducer<EditorConfig, EditorConfigActions> = (state: EditorConfig = initialState, action: EditorConfigActions) => {
|
const getInitialState = (): EditorConfig => {
|
||||||
|
return loadFromLocalStorage() ?? initialState
|
||||||
|
}
|
||||||
|
|
||||||
|
export const EditorConfigReducer: Reducer<EditorConfig, EditorConfigActions> = (state: EditorConfig = getInitialState(), action: EditorConfigActions) => {
|
||||||
|
let newState: EditorConfig
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case EditorConfigActionType.SET_EDITOR_VIEW_MODE:
|
case EditorConfigActionType.SET_EDITOR_VIEW_MODE:
|
||||||
return {
|
newState = {
|
||||||
...state,
|
...state,
|
||||||
editorMode: (action as SetEditorConfigAction).mode
|
editorMode: (action as SetEditorConfigAction).mode
|
||||||
}
|
}
|
||||||
|
saveToLocalStorage(newState)
|
||||||
|
return newState
|
||||||
case EditorConfigActionType.SET_SYNC_SCROLL:
|
case EditorConfigActionType.SET_SYNC_SCROLL:
|
||||||
return {
|
newState = {
|
||||||
...state,
|
...state,
|
||||||
syncScroll: (action as SetEditorSyncScrollAction).syncScroll
|
syncScroll: (action as SetEditorSyncScrollAction).syncScroll
|
||||||
}
|
}
|
||||||
|
saveToLocalStorage(newState)
|
||||||
|
return newState
|
||||||
|
case EditorConfigActionType.SET_EDITOR_PREFERENCES:
|
||||||
|
newState = {
|
||||||
|
...state,
|
||||||
|
preferences: (action as SetEditorPreferencesAction).preferences
|
||||||
|
}
|
||||||
|
saveToLocalStorage(newState)
|
||||||
|
return newState
|
||||||
default:
|
default:
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,17 @@
|
||||||
|
import { EditorConfiguration } from 'codemirror'
|
||||||
import { Action } from 'redux'
|
import { Action } from 'redux'
|
||||||
import { EditorMode } from '../../components/editor/app-bar/editor-view-mode'
|
import { EditorMode } from '../../components/editor/app-bar/editor-view-mode'
|
||||||
|
|
||||||
export enum EditorConfigActionType {
|
export enum EditorConfigActionType {
|
||||||
SET_EDITOR_VIEW_MODE = 'editor/mode/set',
|
SET_EDITOR_VIEW_MODE = 'editor/mode/set',
|
||||||
SET_SYNC_SCROLL = 'editor/syncScroll/set'
|
SET_SYNC_SCROLL = 'editor/syncScroll/set',
|
||||||
|
SET_EDITOR_PREFERENCES = 'editor/preferences/set'
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EditorConfig {
|
export interface EditorConfig {
|
||||||
editorMode: EditorMode;
|
editorMode: EditorMode;
|
||||||
syncScroll: boolean;
|
syncScroll: boolean;
|
||||||
|
preferences: EditorConfiguration
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EditorConfigActions extends Action<EditorConfigActionType> {
|
export interface EditorConfigActions extends Action<EditorConfigActionType> {
|
||||||
|
@ -22,3 +25,7 @@ export interface SetEditorSyncScrollAction extends EditorConfigActions {
|
||||||
export interface SetEditorConfigAction extends EditorConfigActions {
|
export interface SetEditorConfigAction extends EditorConfigActions {
|
||||||
mode: EditorMode
|
mode: EditorMode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SetEditorPreferencesAction extends EditorConfigActions {
|
||||||
|
preferences: EditorConfiguration
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue