Add application state hook (#1308)

* Add application state hook

Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>

* Add docs

Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
Tilman Vatteroth 2021-06-11 15:21:24 +02:00 committed by GitHub
parent 4720f2d36b
commit 829cc2fe48
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
47 changed files with 149 additions and 195 deletions

View file

@ -5,10 +5,7 @@
*/
import React from 'react'
import equal from 'fast-deep-equal'
import { Nav, Navbar } from 'react-bootstrap'
import { useSelector } from 'react-redux'
import { ApplicationState } from '../../../redux'
import { ShowIf } from '../../common/show-if/show-if'
import { SignInButton } from '../../landing-layout/navigation/sign-in-button'
import { UserDropdown } from '../../landing-layout/navigation/user-dropdown'
@ -21,6 +18,7 @@ import { NoteType } from '../note-frontmatter/note-frontmatter'
import { SlideModeButton } from './slide-mode-button'
import { ReadOnlyModeButton } from './read-only-mode-button'
import { NewNoteButton } from './new-note-button'
import { useApplicationState } from '../../../hooks/common/use-application-state'
export enum AppBarMode {
BASIC,
@ -32,8 +30,8 @@ export interface AppBarProps {
}
export const AppBar: React.FC<AppBarProps> = ({ mode }) => {
const userExists = useSelector((state: ApplicationState) => !!state.user)
const noteFrontmatter = useSelector((state: ApplicationState) => state.noteDetails.frontmatter, equal)
const userExists = useApplicationState((state) => !!state.user)
const noteFrontmatter = useApplicationState((state) => state.noteDetails.frontmatter)
return (
<Navbar bg={'light'}>

View file

@ -7,10 +7,9 @@
import React from 'react'
import { ToggleButton, ToggleButtonGroup } from 'react-bootstrap'
import { useTranslation } from 'react-i18next'
import { useSelector } from 'react-redux'
import { ApplicationState } from '../../../redux'
import { setEditorMode } from '../../../redux/editor/methods'
import { ForkAwesomeIcon } from '../../common/fork-awesome/fork-awesome-icon'
import { useApplicationState } from '../../../hooks/common/use-application-state'
export enum EditorMode {
PREVIEW = 'view',
@ -20,7 +19,8 @@ export enum EditorMode {
export const EditorViewMode: React.FC = () => {
const { t } = useTranslation()
const editorMode = useSelector((state: ApplicationState) => state.editorConfig.editorMode)
const editorMode = useApplicationState((state) => state.editorConfig.editorMode)
return (
<ToggleButtonGroup
type='radio'

View file

@ -7,12 +7,11 @@
import React from 'react'
import { ToggleButton, ToggleButtonGroup } from 'react-bootstrap'
import { useTranslation } from 'react-i18next'
import { useSelector } from 'react-redux'
import { ApplicationState } from '../../../../redux'
import { setEditorSyncScroll } from '../../../../redux/editor/methods'
import { ReactComponent as DisabledScrollIcon } from './disabledScroll.svg'
import { ReactComponent as EnabledScrollIcon } from './enabledScroll.svg'
import './sync-scroll-buttons.scss'
import { useApplicationState } from '../../../../hooks/common/use-application-state'
enum SyncScrollState {
SYNCED,
@ -20,7 +19,7 @@ enum SyncScrollState {
}
export const SyncScrollButtons: React.FC = () => {
const syncScrollEnabled = useSelector((state: ApplicationState) => state.editorConfig.syncScroll)
const syncScrollEnabled = useApplicationState((state) => state.editorConfig.syncScroll)
? SyncScrollState.SYNCED
: SyncScrollState.UNSYNCED
const { t } = useTranslation()

View file

@ -4,19 +4,17 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import equal from 'fast-deep-equal'
import React from 'react'
import { Modal } from 'react-bootstrap'
import { Trans, useTranslation } from 'react-i18next'
import { useSelector } from 'react-redux'
import { useParams } from 'react-router-dom'
import { useFrontendBaseUrl } from '../../../../hooks/common/use-frontend-base-url'
import { ApplicationState } from '../../../../redux'
import { CopyableField } from '../../../common/copyable/copyable-field/copyable-field'
import { CommonModal } from '../../../common/modals/common-modal'
import { ShowIf } from '../../../common/show-if/show-if'
import { EditorPagePathParams } from '../../editor-page'
import { NoteType } from '../../note-frontmatter/note-frontmatter'
import { useApplicationState } from '../../../../hooks/common/use-application-state'
export interface ShareModalProps {
show: boolean
@ -25,8 +23,8 @@ export interface ShareModalProps {
export const ShareModal: React.FC<ShareModalProps> = ({ show, onHide }) => {
useTranslation()
const noteFrontmatter = useSelector((state: ApplicationState) => state.noteDetails.frontmatter, equal)
const editorMode = useSelector((state: ApplicationState) => state.editorConfig.editorMode)
const noteFrontmatter = useApplicationState((state) => state.noteDetails.frontmatter)
const editorMode = useApplicationState((state) => state.editorConfig.editorMode)
const baseUrl = useFrontendBaseUrl()
const { id } = useParams<EditorPagePathParams>()

View file

@ -6,11 +6,9 @@
import React, { useCallback, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useSelector } from 'react-redux'
import { useApplyDarkMode } from '../../hooks/common/use-apply-dark-mode'
import { useDocumentTitleWithNoteTitle } from '../../hooks/common/use-document-title-with-note-title'
import { useNoteMarkdownContent } from '../../hooks/common/use-note-markdown-content'
import { ApplicationState } from '../../redux'
import {
SetCheckboxInMarkdownContent,
setNoteFrontmatter,
@ -36,6 +34,7 @@ import { UiNotifications } from '../notifications/ui-notifications'
import { useNotificationTest } from './use-notification-test'
import { IframeCommunicatorContextProvider } from './render-context/iframe-communicator-context-provider'
import { useUpdateLocalHistoryEntry } from './hooks/useUpdateLocalHistoryEntry'
import { useApplicationState } from '../../hooks/common/use-application-state'
export interface EditorPagePathParams {
id: string
@ -51,8 +50,8 @@ export const EditorPage: React.FC = () => {
const markdownContent = useNoteMarkdownContent()
const scrollSource = useRef<ScrollSource>(ScrollSource.EDITOR)
const editorMode: EditorMode = useSelector((state: ApplicationState) => state.editorConfig.editorMode)
const editorSyncScroll: boolean = useSelector((state: ApplicationState) => state.editorConfig.syncScroll)
const editorMode: EditorMode = useApplicationState((state) => state.editorConfig.editorMode)
const editorSyncScroll: boolean = useApplicationState((state) => state.editorConfig.syncScroll)
const [scrollState, setScrollState] = useState<DualScrollState>(() => ({
editorScrollState: { firstLineInView: 1, scrolledPercentage: 0 },

View file

@ -27,12 +27,9 @@ import 'codemirror/keymap/emacs'
import 'codemirror/keymap/sublime'
import 'codemirror/keymap/vim'
import 'codemirror/mode/gfm/gfm'
import equal from 'fast-deep-equal'
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { Controlled as ControlledCodeMirror } from 'react-codemirror2'
import { useTranslation } from 'react-i18next'
import { useSelector } from 'react-redux'
import { ApplicationState } from '../../../redux'
import { MaxLengthWarningModal } from '../editor-modals/max-length-warning-modal'
import { ScrollProps, ScrollState } from '../synced-scroll/scroll-props'
import { allHinters, findWordAtCursor } from './autocompletion'
@ -42,6 +39,7 @@ import { createStatusInfo, defaultState, StatusBar, StatusBarInfo } from './stat
import { ToolBar } from './tool-bar/tool-bar'
import { handleUpload } from './upload-handler'
import { handleFilePaste, handleTablePaste, PasteEvent } from './tool-bar/utils/pasteHandlers'
import { useApplicationState } from '../../../hooks/common/use-application-state'
export interface EditorPaneProps {
onContentChange: (content: string) => void
@ -81,14 +79,14 @@ export const EditorPane: React.FC<EditorPaneProps & ScrollProps> = ({
onMakeScrollSource
}) => {
const { t } = useTranslation()
const maxLength = useSelector((state: ApplicationState) => state.config.maxDocumentLength)
const smartPasteEnabled = useSelector((state: ApplicationState) => state.editorConfig.smartPaste)
const maxLength = useApplicationState((state) => state.config.maxDocumentLength)
const smartPasteEnabled = useApplicationState((state) => state.editorConfig.smartPaste)
const [showMaxLengthWarning, setShowMaxLengthWarning] = useState(false)
const maxLengthWarningAlreadyShown = useRef(false)
const [editor, setEditor] = useState<Editor>()
const [statusBarInfo, setStatusBarInfo] = useState<StatusBarInfo>(defaultState)
const editorPreferences = useSelector((state: ApplicationState) => state.editorConfig.preferences, equal)
const ligaturesEnabled = useSelector((state: ApplicationState) => state.editorConfig.ligatures, equal)
const editorPreferences = useApplicationState((state) => state.editorConfig.preferences)
const ligaturesEnabled = useApplicationState((state) => state.editorConfig.ligatures)
const lastScrollPosition = useRef<number>()
const [editorScroll, setEditorScroll] = useState<ScrollInfo>()

View file

@ -5,24 +5,19 @@
*/
import { EditorConfiguration } from 'codemirror'
import equal from 'fast-deep-equal'
import React, { ChangeEvent, useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import { useSelector } from 'react-redux'
import { ApplicationState } from '../../../../../redux'
import { mergeEditorPreferences } from '../../../../../redux/editor/methods'
import { EditorPreferenceInput, EditorPreferenceInputType } from './editor-preference-input'
import { EditorPreferenceProperty } from './editor-preference-property'
import { useApplicationState } from '../../../../../hooks/common/use-application-state'
export interface EditorPreferenceBooleanProps {
property: EditorPreferenceProperty
}
export const EditorPreferenceBooleanProperty: React.FC<EditorPreferenceBooleanProps> = ({ property }) => {
const preference = useSelector(
(state: ApplicationState) => state.editorConfig.preferences[property]?.toString() || '',
equal
)
const preference = useApplicationState((state) => state.editorConfig.preferences[property]?.toString() ?? '')
const { t } = useTranslation()
const selectItem = useCallback(

View file

@ -5,13 +5,12 @@
*/
import React, { ChangeEvent, useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import { useSelector } from 'react-redux'
import { ApplicationState } from '../../../../../redux'
import { setEditorLigatures } from '../../../../../redux/editor/methods'
import { EditorPreferenceInput, EditorPreferenceInputType } from './editor-preference-input'
import { useApplicationState } from '../../../../../hooks/common/use-application-state'
export const EditorPreferenceLigaturesSelect: React.FC = () => {
const ligaturesEnabled = useSelector((state: ApplicationState) => Boolean(state.editorConfig.ligatures).toString())
const ligaturesEnabled = useApplicationState((state) => Boolean(state.editorConfig.ligatures).toString())
const saveLigatures = useCallback((event: ChangeEvent<HTMLSelectElement>) => {
const ligaturesActivated: boolean = event.target.value === 'true'
setEditorLigatures(ligaturesActivated)

View file

@ -5,23 +5,18 @@
*/
import { EditorConfiguration } from 'codemirror'
import equal from 'fast-deep-equal'
import React, { ChangeEvent, useCallback } from 'react'
import { useSelector } from 'react-redux'
import { ApplicationState } from '../../../../../redux'
import { mergeEditorPreferences } from '../../../../../redux/editor/methods'
import { EditorPreferenceInput, EditorPreferenceInputType } from './editor-preference-input'
import { EditorPreferenceProperty } from './editor-preference-property'
import { useApplicationState } from '../../../../../hooks/common/use-application-state'
export interface EditorPreferenceNumberProps {
property: EditorPreferenceProperty
}
export const EditorPreferenceNumberProperty: React.FC<EditorPreferenceNumberProps> = ({ property }) => {
const preference = useSelector(
(state: ApplicationState) => state.editorConfig.preferences[property]?.toString() || '',
equal
)
const preference = useApplicationState((state) => state.editorConfig.preferences[property]?.toString() ?? '')
const selectItem = useCallback(
(event: ChangeEvent<HTMLSelectElement>) => {

View file

@ -5,14 +5,12 @@
*/
import { EditorConfiguration } from 'codemirror'
import equal from 'fast-deep-equal'
import React, { ChangeEvent, useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import { useSelector } from 'react-redux'
import { ApplicationState } from '../../../../../redux'
import { mergeEditorPreferences } from '../../../../../redux/editor/methods'
import { EditorPreferenceInput, EditorPreferenceInputType } from './editor-preference-input'
import { EditorPreferenceProperty } from './editor-preference-property'
import { useApplicationState } from '../../../../../hooks/common/use-application-state'
export interface EditorPreferenceSelectPropertyProps {
property: EditorPreferenceProperty
@ -23,10 +21,7 @@ export const EditorPreferenceSelectProperty: React.FC<EditorPreferenceSelectProp
property,
selections
}) => {
const preference = useSelector(
(state: ApplicationState) => state.editorConfig.preferences[property]?.toString() || '',
equal
)
const preference = useApplicationState((state) => state.editorConfig.preferences[property]?.toString() ?? '')
const { t } = useTranslation()

View file

@ -5,13 +5,12 @@
*/
import React, { ChangeEvent, useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import { useSelector } from 'react-redux'
import { ApplicationState } from '../../../../../redux'
import { useApplicationState } from '../../../../../hooks/common/use-application-state'
import { setEditorSmartPaste } from '../../../../../redux/editor/methods'
import { EditorPreferenceInput, EditorPreferenceInputType } from './editor-preference-input'
export const EditorPreferenceSmartPasteSelect: React.FC = () => {
const smartPasteEnabled = useSelector((state: ApplicationState) => Boolean(state.editorConfig.smartPaste).toString())
const smartPasteEnabled = useApplicationState((state) => Boolean(state.editorConfig.smartPaste).toString())
const saveSmartPaste = useCallback((event: ChangeEvent<HTMLSelectElement>) => {
const smartPasteActivated: boolean = event.target.value === 'true'
setEditorSmartPaste(smartPasteActivated)

View file

@ -4,12 +4,9 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import equal from 'fast-deep-equal'
import React, { Fragment, useState } from 'react'
import { Button, Form, ListGroup } from 'react-bootstrap'
import { useTranslation } from 'react-i18next'
import { useSelector } from 'react-redux'
import { ApplicationState } from '../../../../../redux'
import { ForkAwesomeIcon } from '../../../../common/fork-awesome/fork-awesome-icon'
import { CommonModal } from '../../../../common/modals/common-modal'
import { ShowIf } from '../../../../common/show-if/show-if'
@ -20,14 +17,12 @@ import { EditorPreferenceNumberProperty } from './editor-preference-number-prope
import { EditorPreferenceProperty } from './editor-preference-property'
import { EditorPreferenceSelectProperty } from './editor-preference-select-property'
import { EditorPreferenceSmartPasteSelect } from './editor-preference-smart-paste-select'
import { useApplicationState } from '../../../../../hooks/common/use-application-state'
export const EditorPreferences: React.FC = () => {
const { t } = useTranslation()
const [showModal, setShowModal] = useState(false)
const indentWithTabs = useSelector(
(state: ApplicationState) => state.editorConfig.preferences.indentWithTabs ?? false,
equal
)
const indentWithTabs = useApplicationState((state) => state.editorConfig.preferences.indentWithTabs ?? false)
return (
<Fragment>

View file

@ -6,18 +6,18 @@
import equal from 'fast-deep-equal'
import { useEffect, useRef } from 'react'
import { useSelector } from 'react-redux'
import { ApplicationState, store } from '../../../redux'
import { store } from '../../../redux'
import { useParams } from 'react-router-dom'
import { EditorPagePathParams } from '../editor-page'
import { HistoryEntry, HistoryEntryOrigin } from '../../../redux/history/types'
import { updateLocalHistoryEntry } from '../../../redux/history/methods'
import { useApplicationState } from '../../../hooks/common/use-application-state'
export const useUpdateLocalHistoryEntry = (updateReady: boolean): void => {
const { id } = useParams<EditorPagePathParams>()
const userExists = useSelector((state: ApplicationState) => !!state.user)
const currentNoteTitle = useSelector((state: ApplicationState) => state.noteDetails.noteTitle)
const currentNoteTags = useSelector((state: ApplicationState) => state.noteDetails.frontmatter.tags)
const userExists = useApplicationState((state) => !!state.user)
const currentNoteTitle = useApplicationState((state) => state.noteDetails.noteTitle)
const currentNoteTags = useApplicationState((state) => state.noteDetails.frontmatter.tags)
const lastNoteTitle = useRef('')
const lastNoteTags = useRef<string[]>([])

View file

@ -5,9 +5,8 @@
*/
import equal from 'fast-deep-equal'
import React, { Fragment, useCallback, useEffect, useRef, useState } from 'react'
import { useSelector } from 'react-redux'
import { useApplicationState } from '../../../hooks/common/use-application-state'
import { useIsDarkModeActivated } from '../../../hooks/common/use-is-dark-mode-activated'
import { ApplicationState } from '../../../redux'
import { isTestMode } from '../../../utils/test-modes'
import { RendererProps } from '../../render-page/markdown-document'
import { ImageDetails, RendererType } from '../../render-page/rendering-message'
@ -42,7 +41,7 @@ export const RenderIframe: React.FC<RenderIframeProps> = ({
const [lightboxDetails, setLightboxDetails] = useState<ImageDetails | undefined>(undefined)
const frameReference = useRef<HTMLIFrameElement>(null)
const rendererOrigin = useSelector((state: ApplicationState) => state.config.iframeCommunication.rendererOrigin)
const rendererOrigin = useApplicationState((state) => state.config.iframeCommunication.rendererOrigin)
const renderPageUrl = `${rendererOrigin}render`
const resetRendererReady = useCallback(() => setRendererReady(false), [])
const iframeCommunicator = useContextOrStandaloneIframeCommunicator()

View file

@ -7,17 +7,14 @@
import React from 'react'
import { Alert } from 'react-bootstrap'
import { Trans, useTranslation } from 'react-i18next'
import { useSelector } from 'react-redux'
import links from '../../../links.json'
import { ApplicationState } from '../../../redux'
import { TranslatedExternalLink } from '../../common/links/translated-external-link'
import { ShowIf } from '../../common/show-if/show-if'
import { useApplicationState } from '../../../hooks/common/use-application-state'
export const YamlArrayDeprecationAlert: React.FC = () => {
useTranslation()
const yamlDeprecatedTags = useSelector(
(state: ApplicationState) => state.noteDetails.frontmatter.deprecatedTagsSyntax
)
const yamlDeprecatedTags = useApplicationState((state) => state.noteDetails.frontmatter.deprecatedTagsSyntax)
return (
<ShowIf condition={yamlDeprecatedTags}>

View file

@ -10,15 +10,14 @@ import { SidebarButton } from './sidebar-button'
import { SpecificSidebarEntryProps } from './types'
import { useParams } from 'react-router-dom'
import { EditorPagePathParams } from '../editor-page'
import { useSelector } from 'react-redux'
import { ApplicationState } from '../../../redux'
import { toggleHistoryEntryPinning } from '../../../redux/history/methods'
import { showErrorNotification } from '../../../redux/ui-notifications/methods'
import { useApplicationState } from '../../../hooks/common/use-application-state'
export const PinNoteSidebarEntry: React.FC<SpecificSidebarEntryProps> = ({ className, hide }) => {
const { t } = useTranslation()
const { id } = useParams<EditorPagePathParams>()
const history = useSelector((state: ApplicationState) => state.history)
const history = useApplicationState((state) => state.history)
const isPinned = useMemo(() => {
const entry = history.find((entry) => entry.identifier === id)