diff --git a/frontend/src/components/application-loader/initializers/load-dark-mode.ts b/frontend/src/components/application-loader/initializers/load-dark-mode.ts index 42fc157b1..20f2d9404 100644 --- a/frontend/src/components/application-loader/initializers/load-dark-mode.ts +++ b/frontend/src/components/application-loader/initializers/load-dark-mode.ts @@ -3,7 +3,7 @@ * * SPDX-License-Identifier: AGPL-3.0-only */ -import { DARK_MODE_LOCAL_STORAGE_KEY } from '../../../hooks/dark-mode/use-apply-dark-mode' +import { DARK_MODE_LOCAL_STORAGE_KEY } from '../../../hooks/dark-mode/use-save-dark-mode-preference-to-local-storage' import { setDarkModePreference } from '../../../redux/dark-mode/methods' import { DarkModePreference } from '../../../redux/dark-mode/types' import { isClientSideRendering } from '../../../utils/is-client-side-rendering' diff --git a/frontend/src/components/editor-page/editor-page-content.tsx b/frontend/src/components/editor-page/editor-page-content.tsx index 130433edd..fa5f9b14d 100644 --- a/frontend/src/components/editor-page/editor-page-content.tsx +++ b/frontend/src/components/editor-page/editor-page-content.tsx @@ -4,7 +4,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ import { useApplicationState } from '../../hooks/common/use-application-state' -import { useApplyDarkMode } from '../../hooks/dark-mode/use-apply-dark-mode' +import { useApplyDarkModeStyle } from '../../hooks/dark-mode/use-apply-dark-mode-style' +import { useSaveDarkModePreferenceToLocalStorage } from '../../hooks/dark-mode/use-save-dark-mode-preference-to-local-storage' import { Logger } from '../../utils/logger' import { MotdModal } from '../common/motd-modal/motd-modal' import { CommunicatorImageLightbox } from '../markdown-renderer/extensions/image/communicator-image-lightbox' @@ -78,7 +79,8 @@ export const EditorPageContent: React.FC = () => { [editorSyncScroll] ) - useApplyDarkMode() + useApplyDarkModeStyle() + useSaveDarkModePreferenceToLocalStorage() useUpdateLocalHistoryEntry() const setRendererToScrollSource = useCallback(() => { diff --git a/frontend/src/hooks/dark-mode/use-apply-dark-mode-style.ts b/frontend/src/hooks/dark-mode/use-apply-dark-mode-style.ts new file mode 100644 index 000000000..4b678a427 --- /dev/null +++ b/frontend/src/hooks/dark-mode/use-apply-dark-mode-style.ts @@ -0,0 +1,23 @@ +/* + * SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file) + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { useDarkModeState } from './use-dark-mode-state' +import { useEffect } from 'react' + +/** + * Applies the dark mode by adding a css class to the body tag. + */ +export const useApplyDarkModeStyle = (): void => { + const darkMode = useDarkModeState() + useEffect(() => { + if (darkMode) { + window.document.body.classList.add('dark') + } else { + window.document.body.classList.remove('dark') + } + }, [darkMode]) + + useEffect(() => () => window.document.body.classList.remove('dark'), []) +} diff --git a/frontend/src/hooks/dark-mode/use-apply-dark-mode.ts b/frontend/src/hooks/dark-mode/use-apply-dark-mode.ts deleted file mode 100644 index 074820ff0..000000000 --- a/frontend/src/hooks/dark-mode/use-apply-dark-mode.ts +++ /dev/null @@ -1,51 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file) - * - * SPDX-License-Identifier: AGPL-3.0-only - */ -import { DarkModePreference } from '../../redux/dark-mode/types' -import { isClientSideRendering } from '../../utils/is-client-side-rendering' -import { Logger } from '../../utils/logger' -import { useApplicationState } from '../common/use-application-state' -import useMediaQuery from '@restart/hooks/useMediaQuery' -import { useEffect } from 'react' - -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) - } -} diff --git a/frontend/src/hooks/dark-mode/use-save-dark-mode-preference-to-local-storage.ts b/frontend/src/hooks/dark-mode/use-save-dark-mode-preference-to-local-storage.ts new file mode 100644 index 000000000..a647c78d3 --- /dev/null +++ b/frontend/src/hooks/dark-mode/use-save-dark-mode-preference-to-local-storage.ts @@ -0,0 +1,33 @@ +/* + * SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file) + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { DarkModePreference } from '../../redux/dark-mode/types' +import { Logger } from '../../utils/logger' +import { useApplicationState } from '../common/use-application-state' +import { useEffect } from 'react' + +const logger = new Logger('useSaveDarkModeToLocalStorage') + +export const DARK_MODE_LOCAL_STORAGE_KEY = 'forcedDarkMode' + +/** + * Saves the dark mode preference of the user in the browser's local storage. + */ +export const useSaveDarkModePreferenceToLocalStorage = () => { + const preference = useApplicationState((state) => state.darkMode.darkModePreference) + useEffect(() => { + 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) + } + }, [preference]) +} diff --git a/frontend/src/pages/cheatsheet.tsx b/frontend/src/pages/cheatsheet.tsx index ad935d1a5..0b3969cfe 100644 --- a/frontend/src/pages/cheatsheet.tsx +++ b/frontend/src/pages/cheatsheet.tsx @@ -4,12 +4,12 @@ * SPDX-License-Identifier: AGPL-3.0-only */ import { CheatsheetContent } from '../components/editor-page/app-bar/cheatsheet/cheatsheet-content' -import { useApplyDarkMode } from '../hooks/dark-mode/use-apply-dark-mode' +import { useApplyDarkModeStyle } from '../hooks/dark-mode/use-apply-dark-mode-style' import type { NextPage } from 'next' import { Container } from 'react-bootstrap' const CheatsheetPage: NextPage = () => { - useApplyDarkMode() + useApplyDarkModeStyle() return ( diff --git a/frontend/src/pages/render.tsx b/frontend/src/pages/render.tsx index df78952ce..75de9cd0e 100644 --- a/frontend/src/pages/render.tsx +++ b/frontend/src/pages/render.tsx @@ -5,7 +5,7 @@ */ import { RendererToEditorCommunicatorContextProvider } from '../components/editor-page/render-context/renderer-to-editor-communicator-context-provider' import { RenderPageContent } from '../components/render-page/render-page-content' -import { useApplyDarkMode } from '../hooks/dark-mode/use-apply-dark-mode' +import { useApplyDarkModeStyle } from '../hooks/dark-mode/use-apply-dark-mode-style' import type { NextPage } from 'next' import React from 'react' @@ -13,7 +13,7 @@ import React from 'react' * Renders the actual markdown renderer that receives the content and metadata via iframe communication. */ export const RenderPage: NextPage = () => { - useApplyDarkMode() + useApplyDarkModeStyle() return ( diff --git a/frontend/src/pages/s/[noteId].tsx b/frontend/src/pages/s/[noteId].tsx index b4a810533..255475b93 100644 --- a/frontend/src/pages/s/[noteId].tsx +++ b/frontend/src/pages/s/[noteId].tsx @@ -9,14 +9,17 @@ import { DocumentReadOnlyPageContent } from '../../components/document-read-only import { AppBar, AppBarMode } from '../../components/editor-page/app-bar/app-bar' import { HeadMetaProperties } from '../../components/editor-page/head-meta-properties/head-meta-properties' import { EditorToRendererCommunicatorContextProvider } from '../../components/editor-page/render-context/editor-to-renderer-communicator-context-provider' -import { useApplyDarkMode } from '../../hooks/dark-mode/use-apply-dark-mode' +import { useApplyDarkModeStyle } from '../../hooks/dark-mode/use-apply-dark-mode-style' +import { useSaveDarkModePreferenceToLocalStorage } from '../../hooks/dark-mode/use-save-dark-mode-preference-to-local-storage' import React from 'react' /** * Renders a page that contains only the rendered document without an editor or realtime updates. */ export const DocumentReadOnlyPage: React.FC = () => { - useApplyDarkMode() + useApplyDarkModeStyle() + useSaveDarkModePreferenceToLocalStorage() + return (