fix: Move content into to frontend directory

Doing this BEFORE the merge prevents a lot of merge conflicts.

Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
Tilman Vatteroth 2022-11-11 11:16:18 +01:00
parent 4e18ce38f3
commit 762a0a850e
No known key found for this signature in database
GPG key ID: B97799103358209B
1051 changed files with 0 additions and 35 deletions

View file

@ -0,0 +1,21 @@
/*
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { useMemo } from 'react'
import { useApplicationState } from './use-application-state'
/**
* Calculates the app title with branding if set.
*
* @return The app title with branding.
*/
export const useAppTitle = (): string => {
const brandingName = useApplicationState((state) => state.config.branding.name)
return useMemo(() => {
return 'HedgeDoc' + (brandingName ? ` @ ${brandingName}` : '')
}, [brandingName])
}

View file

@ -0,0 +1,23 @@
/*
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { useSelector } from 'react-redux'
import equal from 'fast-deep-equal'
import type { ApplicationState } from '../../redux/application-state'
/**
* Accesses the global application state to retrieve information.
*
* @param selector A selector function that extracts the needed information from the state.
* @param checkForEquality An optional custom equality function. If not provided then {@link equal equal from fast-deep-equal} will be used.
* @return The requested information
*/
export const useApplicationState = <TSelected>(
selector: (state: ApplicationState) => TSelected,
checkForEquality?: (a: TSelected, b: TSelected) => boolean
): TSelected => {
return useSelector<ApplicationState, TSelected>(selector, checkForEquality ? checkForEquality : equal)
}

View file

@ -0,0 +1,52 @@
/*
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { useEffect } from 'react'
import { useApplicationState } from './use-application-state'
import { isClientSideRendering } from '../../utils/is-client-side-rendering'
import { Logger } from '../../utils/logger'
import useMediaQuery from '@restart/hooks/useMediaQuery'
import { DarkModePreference } from '../../redux/dark-mode/types'
const logger = new Logger('useApplyDarkMode')
export const DARK_MODE_LOCAL_STORAGE_KEY = 'forcedDarkMode'
/**
* Applies the `dark` css class to the body tag according to the dark mode state.
*/
export const useApplyDarkMode = (): void => {
const preference = useApplicationState((state) => state.darkMode.darkModePreference)
const isBrowserPreferringDark = useMediaQuery('(prefers-color-scheme: dark)')
useEffect(() => saveToLocalStorage(preference), [preference])
useEffect(() => {
if (preference === DarkModePreference.DARK || (preference === DarkModePreference.AUTO && isBrowserPreferringDark)) {
window.document.body.classList.add('dark')
} else {
window.document.body.classList.remove('dark')
}
}, [isBrowserPreferringDark, preference])
useEffect(() => () => window.document.body.classList.remove('dark'), [])
}
export const saveToLocalStorage = (preference: DarkModePreference): void => {
if (!isClientSideRendering()) {
return
}
try {
if (preference === DarkModePreference.DARK) {
window.localStorage.setItem(DARK_MODE_LOCAL_STORAGE_KEY, 'dark')
} else if (preference === DarkModePreference.LIGHT) {
window.localStorage.setItem(DARK_MODE_LOCAL_STORAGE_KEY, 'light')
} else if (preference === DarkModePreference.AUTO) {
window.localStorage.removeItem(DARK_MODE_LOCAL_STORAGE_KEY)
}
} catch (error) {
logger.error('Saving to local storage failed', error)
}
}

View file

@ -0,0 +1,23 @@
/*
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { useRouter } from 'next/router'
import { useMemo } from 'react'
/**
* Extracts the parameter from the router expected to be an array of values.
*
* @param parameter The parameter to extract
* @return An array of values extracted from the router.
*/
export const useArrayStringUrlParameter = (parameter: string): string[] => {
const router = useRouter()
return useMemo(() => {
const value = router.query[parameter]
return (typeof value === 'string' ? [value] : value) ?? []
}, [parameter, router.query])
}

View file

@ -0,0 +1,33 @@
/*
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { useContext, useMemo } from 'react'
import { useRouter } from 'next/router'
import { baseUrlContext } from '../../components/common/base-url/base-url-context-provider'
export enum ORIGIN {
EDITOR,
RENDERER,
CURRENT_PAGE
}
/**
* Provides the base urls for the editor and the renderer.
*/
export const useBaseUrl = (origin = ORIGIN.CURRENT_PAGE): string => {
const baseUrls = useContext(baseUrlContext)
if (!baseUrls) {
throw new Error('No base url context received. Did you forget to use the provider component?')
}
const router = useRouter()
return useMemo(() => {
return (router.route === '/render' && origin === ORIGIN.CURRENT_PAGE) || origin === ORIGIN.RENDERER
? baseUrls.renderer
: baseUrls.editor
}, [origin, baseUrls.renderer, baseUrls.editor, router.route])
}

View file

@ -0,0 +1,36 @@
/*
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { useEffect } from 'react'
/**
* Registers event listener for pointer movement and pointer release on the window object.
*
* @param onPointerMovement is triggered if the user moves the pointer over the window
* @param onPointerRelease is triggered if the pointer is released (touch end or mouse up)
*/
export const useBindPointerMovementEventOnWindow = (
onPointerMovement: (event: MouseEvent | TouchEvent) => void,
onPointerRelease: () => void
): void => {
useEffect(() => {
const pointerMovement = onPointerMovement
const pointerRelease = onPointerRelease
window.addEventListener('touchmove', pointerMovement)
window.addEventListener('mousemove', pointerMovement)
window.addEventListener('touchcancel', pointerRelease)
window.addEventListener('touchend', pointerRelease)
window.addEventListener('mouseup', pointerRelease)
return () => {
window.removeEventListener('touchmove', pointerMovement)
window.removeEventListener('mousemove', pointerMovement)
window.removeEventListener('touchcancel', pointerRelease)
window.removeEventListener('touchend', pointerRelease)
window.removeEventListener('mouseup', pointerRelease)
}
}, [onPointerMovement, onPointerRelease])
}

View file

@ -0,0 +1,23 @@
/*
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { useCallback, useState } from 'react'
/**
* Provides a boolean state and two functions that set the boolean to true or false.
*
* @param initialState The initial value of the state
* @return An array containing the state, and two functions that set the state value to true or false.
*/
export const useBooleanState = (
initialState: boolean | (() => boolean) = false
): [state: boolean, setToTrue: () => void, setToFalse: () => void] => {
const [state, setState] = useState(initialState)
const setToFalse = useCallback(() => setState(false), [])
const setToTrue = useCallback(() => setState(true), [])
return [state, setToTrue, setToFalse]
}

View file

@ -0,0 +1,21 @@
/*
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { useApplicationState } from './use-application-state'
import useMediaQuery from '@restart/hooks/useMediaQuery'
import { DarkModePreference } from '../../redux/dark-mode/types'
/**
* Uses the user settings and the browser preference to determine if dark mode should be used.
*
* @return The current state of the dark mode.
*/
export const useDarkModeState = (): boolean => {
const preference = useApplicationState((state) => state.darkMode.darkModePreference)
const isBrowserPreferringDark = useMediaQuery('(prefers-color-scheme: dark)')
return preference === DarkModePreference.DARK || (preference === DarkModePreference.AUTO && isBrowserPreferringDark)
}

View file

@ -0,0 +1,30 @@
/*
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import type { DependencyList, EffectCallback } from 'react'
import { useEffect, useState } from 'react'
/**
* Executes a side effects but catches any thrown error.
*
* @param effect The side effect to execute
* @param deps The dependencies of the effect
* @return The produced error (if occurred)
*/
export const useEffectWithCatch = (effect: EffectCallback, deps: DependencyList = []): Error | undefined => {
const [error, setError] = useState<Error | undefined>(undefined)
useEffect(() => {
try {
return effect()
} catch (error) {
setError(error as Error)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, deps)
return error
}

View file

@ -0,0 +1,16 @@
/*
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { useApplicationState } from './use-application-state'
/**
* Extracts the markdown content of the current note from the global application state.
*
* @return The markdown content of the note
*/
export const useNoteMarkdownContent = (): string => {
return useApplicationState((state) => state.noteDetails.markdownContent.plain)
}

View file

@ -0,0 +1,22 @@
/*
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { useTranslation } from 'react-i18next'
import { useApplicationState } from './use-application-state'
import { useMemo } from 'react'
/**
* Retrieves the title of the note or a placeholder text, if no title is set.
*
* @return The title of the note
*/
export const useNoteTitle = (): string => {
const { t } = useTranslation()
const untitledNote = useMemo(() => t('editor.untitledNote'), [t])
const noteTitle = useApplicationState((state) => state.noteDetails.title)
return useMemo(() => (noteTitle === '' ? untitledNote : noteTitle), [noteTitle, untitledNote])
}

View file

@ -0,0 +1,23 @@
/*
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import type { ChangeEvent } from 'react'
import { useCallback } from 'react'
/**
* Takes an input change event and sends the event value to a state setter.
*
* @param setter The setter method for the state.
* @return Hook that can be used as callback for onChange.
*/
export const useOnInputChange = (setter: (value: string) => void): ((event: ChangeEvent<HTMLInputElement>) => void) => {
return useCallback(
(event) => {
setter(event.target.value)
},
[setter]
)
}

View file

@ -0,0 +1,24 @@
/*
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { useRouter } from 'next/router'
import { useMemo } from 'react'
/**
* Extracts the parameter from the router expected to be a single value.
*
* @param parameter The parameter to extract
* @param fallback The fallback returned if there is no value.
* @return A value extracted from the router.
*/
export const useSingleStringUrlParameter = <T>(parameter: string, fallback: T): string | T => {
const router = useRouter()
return useMemo(() => {
const value = router.query[parameter]
return (typeof value === 'string' ? value : value?.[0]) ?? fallback
}, [fallback, parameter, router.query])
}

View file

@ -0,0 +1,34 @@
/*
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { useMemo } from 'react'
import { useApplicationState } from './use-application-state'
/**
* Returns the markdown content from the global application state trimmed to the maximal note length and without the frontmatter lines.
*
* @return The array of markdown content lines
*/
export const useTrimmedNoteMarkdownContentWithoutFrontmatter = (): string[] => {
const maxLength = useApplicationState((state) => state.config.maxDocumentLength)
const markdownContent = useApplicationState((state) => ({
lines: state.noteDetails.markdownContent.lines,
content: state.noteDetails.markdownContent.plain
}))
const lineOffset = useApplicationState((state) => state.noteDetails.frontmatterRendererInfo.lineOffset)
const trimmedLines = useMemo(() => {
if (markdownContent.content.length > maxLength) {
return markdownContent.content.slice(0, maxLength).split('\n')
} else {
return markdownContent.lines
}
}, [markdownContent, maxLength])
return useMemo(() => {
return trimmedLines.slice(lineOffset)
}, [lineOffset, trimmedLines])
}