From 4e18ce38f3bfe9571ebf2a203010522cf3576768 Mon Sep 17 00:00:00 2001 From: Tilman Vatteroth Date: Thu, 21 Jul 2022 19:36:29 +0200 Subject: [PATCH] feat: add settings dialog Signed-off-by: Tilman Vatteroth --- cypress/e2e/language.spec.ts | 1 + locales/en.json | 53 ++++++++++-- .../initializers/load-dark-mode.ts | 57 ++++--------- .../common/icon-button/icon-button.tsx | 3 +- .../editor-page/app-bar/app-bar.tsx | 8 +- .../editor-page/app-bar/dark-mode-button.tsx | 42 ---------- .../editor-page/app-bar/navbar-branding.tsx | 4 +- .../buttonIcon.inkscape.svg | 84 ------------------- .../sync-scroll-buttons/disabledScroll.svg | 14 ---- .../sync-scroll-buttons/enabledScroll.svg | 12 --- .../sync-scroll-buttons.module.scss | 24 ------ .../sync-scroll-buttons.tsx | 42 ---------- .../revisions/revision-viewer.tsx | 4 +- .../editor-page/editor-pane/editor-pane.tsx | 4 +- .../editor-page/editor-pane/linter/linter.ts | 4 +- .../emoji-picker/emoji-picker-popover.tsx | 4 +- .../use-send-dark-mode-status-to-renderer.ts | 8 +- .../landing-layout/footer/footer.tsx | 2 - .../navigation/header-bar/header-bar.tsx | 2 + .../editor/editor-settings-tab-content.tsx | 34 ++++++++ .../editor/ligature-setting-button-group.tsx | 18 ++++ .../smart-paste-setting-button-group.tsx | 18 ++++ .../sync-scroll-setting-button-group.tsx | 18 ++++ .../global/dark-mode-setting-button-group.tsx | 49 +++++++++++ .../global/global-settings-tab-content.tsx | 27 ++++++ .../global}/language-picker.tsx | 7 +- .../settings-dialog/settings-button.tsx | 26 ++++++ .../layout/settings-dialog/settings-modal.tsx | 42 ++++++++++ .../on-off-button-group.test.tsx.snap | 57 +++++++++++++ .../utils/on-off-button-group.test.tsx | 34 ++++++++ .../utils/on-off-button-group.tsx | 57 +++++++++++++ .../settings-dialog/utils/setting-line.tsx | 36 ++++++++ .../utils/settings-toggle-button.tsx | 55 ++++++++++++ .../render-page/iframe-markdown-renderer.tsx | 4 +- .../rendering-message.ts | 3 +- .../flowchart/flowchart.test.tsx | 8 +- .../flowchart/flowchart.tsx | 4 +- src/hooks/common/use-apply-dark-mode.ts | 40 +++++++-- src/hooks/common/use-dark-mode-state.ts | 21 +++++ .../common/use-is-dark-mode-activated.ts | 16 ---- src/redux/dark-mode/methods.ts | 24 ++---- src/redux/dark-mode/reducers.ts | 25 +++--- src/redux/dark-mode/types.ts | 17 ++-- src/utils/cypress-attribute.ts | 10 +-- src/utils/test-id.ts | 10 ++- 45 files changed, 656 insertions(+), 376 deletions(-) delete mode 100644 src/components/editor-page/app-bar/dark-mode-button.tsx delete mode 100644 src/components/editor-page/app-bar/sync-scroll-buttons/buttonIcon.inkscape.svg delete mode 100644 src/components/editor-page/app-bar/sync-scroll-buttons/disabledScroll.svg delete mode 100644 src/components/editor-page/app-bar/sync-scroll-buttons/enabledScroll.svg delete mode 100644 src/components/editor-page/app-bar/sync-scroll-buttons/sync-scroll-buttons.module.scss delete mode 100644 src/components/editor-page/app-bar/sync-scroll-buttons/sync-scroll-buttons.tsx create mode 100644 src/components/layout/settings-dialog/editor/editor-settings-tab-content.tsx create mode 100644 src/components/layout/settings-dialog/editor/ligature-setting-button-group.tsx create mode 100644 src/components/layout/settings-dialog/editor/smart-paste-setting-button-group.tsx create mode 100644 src/components/layout/settings-dialog/editor/sync-scroll-setting-button-group.tsx create mode 100644 src/components/layout/settings-dialog/global/dark-mode-setting-button-group.tsx create mode 100644 src/components/layout/settings-dialog/global/global-settings-tab-content.tsx rename src/components/{landing-layout/footer => layout/settings-dialog/global}/language-picker.tsx (94%) create mode 100644 src/components/layout/settings-dialog/settings-button.tsx create mode 100644 src/components/layout/settings-dialog/settings-modal.tsx create mode 100644 src/components/layout/settings-dialog/utils/__snapshots__/on-off-button-group.test.tsx.snap create mode 100644 src/components/layout/settings-dialog/utils/on-off-button-group.test.tsx create mode 100644 src/components/layout/settings-dialog/utils/on-off-button-group.tsx create mode 100644 src/components/layout/settings-dialog/utils/setting-line.tsx create mode 100644 src/components/layout/settings-dialog/utils/settings-toggle-button.tsx create mode 100644 src/hooks/common/use-dark-mode-state.ts delete mode 100644 src/hooks/common/use-is-dark-mode-activated.ts diff --git a/cypress/e2e/language.spec.ts b/cypress/e2e/language.spec.ts index 74c363479..813521737 100644 --- a/cypress/e2e/language.spec.ts +++ b/cypress/e2e/language.spec.ts @@ -9,6 +9,7 @@ import { languages } from '../fixtures/languages' describe('Languages', () => { beforeEach(() => { cy.visitHome() + cy.getByCypressId('settingsButton').click() }) it('all languages are available', () => { diff --git a/locales/en.json b/locales/en.json index 8e03874a0..bd571664f 100644 --- a/locales/en.json +++ b/locales/en.json @@ -307,16 +307,8 @@ "view": "View", "both": "Both" }, - "darkMode": { - "switchToDark": "Switch to Dark Mode", - "switchToLight": "Switch to Light Mode" - }, "appBar": { - "new": "New", - "syncScroll": { - "disable": "Disable sync scroll", - "enable": "Enable sync scroll" - } + "new": "New" }, "editorToolbar": { "bold": "Bold", @@ -516,6 +508,8 @@ "common": { "yes": "Yes", "no": "No", + "on": "On", + "off": "Off", "import": "Import", "export": "Export", "refresh": "Refresh", @@ -591,5 +585,46 @@ "title": "Note '{{noteTitle}}' deleted", "text": "You were redirected to the history page, because the note you just edited was deleted." } + }, + "settings": { + "title": "Settings", + "editor": { + "label": "Editor", + "ligatures": { + "label": "Ligatures", + "help": "(De)Activates support for ligatures in the editor." + }, + "smartPaste": { + "label": "Smart Paste", + "help": "Smart paste detects common formats (like tables) when pasting content into the editor and formats them as markdown." + }, + "syncScroll": { + "label": "Sync Scrolling", + "help": "Synchronizes the scroll state of editor and rendering view." + } + }, + "global": { + "label": "Global", + "darkMode": { + "label": "Dark mode", + "help": "Enforces the (de)activation of the dark mode for the app or lets the browser decide.", + "dark": { + "tooltip": "Switch to dark mode", + "label": "Dark" + }, + "light": { + "tooltip": "Switch to light mode", + "label": "Light" + }, + "browser": { + "tooltip": "Let browser decide", + "label": "Auto" + } + }, + "language": { + "label": "Language", + "help": "The primary user interface language" + } + } } } diff --git a/src/components/application-loader/initializers/load-dark-mode.ts b/src/components/application-loader/initializers/load-dark-mode.ts index 24033fe50..fe558719f 100644 --- a/src/components/application-loader/initializers/load-dark-mode.ts +++ b/src/components/application-loader/initializers/load-dark-mode.ts @@ -3,10 +3,11 @@ * * SPDX-License-Identifier: AGPL-3.0-only */ -import { setDarkMode } from '../../../redux/dark-mode/methods' +import { setDarkModePreference } from '../../../redux/dark-mode/methods' import { Logger } from '../../../utils/logger' -import type { DarkModeConfig } from '../../../redux/dark-mode/types' import { isClientSideRendering } from '../../../utils/is-client-side-rendering' +import { DarkModePreference } from '../../../redux/dark-mode/types' +import { DARK_MODE_LOCAL_STORAGE_KEY } from '../../../hooks/common/use-apply-dark-mode' const logger = new Logger('Dark mode initializer') @@ -16,16 +17,9 @@ const logger = new Logger('Dark mode initializer') * * @return A promise that resolves as soon as the dark mode has been loaded. */ -export const loadDarkMode = async (): Promise => { - return new Promise((resolve) => { - setDarkMode( - fetchDarkModeFromLocalStorage() ?? - determineDarkModeBrowserSettings() ?? { - darkMode: false - } - ) - resolve() - }) +export const loadDarkMode = (): Promise => { + setDarkModePreference(fetchDarkModeFromLocalStorage()) + return Promise.resolve() } /** @@ -34,38 +28,21 @@ export const loadDarkMode = async (): Promise => { * @return {@link true} if the local storage has saved that the user prefers dark mode. * {@link false} if the user doesn't prefer dark mode or if the value couldn't be read from local storage. */ -const fetchDarkModeFromLocalStorage = (): boolean => { +const fetchDarkModeFromLocalStorage = (): DarkModePreference => { if (!isClientSideRendering()) { - return false + return DarkModePreference.AUTO } try { - return window.localStorage.getItem('nightMode') === 'true' + const colorScheme = window.localStorage.getItem(DARK_MODE_LOCAL_STORAGE_KEY) + if (colorScheme === 'dark') { + return DarkModePreference.DARK + } else if (colorScheme === 'light') { + return DarkModePreference.LIGHT + } else { + return DarkModePreference.AUTO + } } catch (error) { logger.error('Loading from local storage failed', error) - return false - } -} - -/** - * Tries to read the preferred dark mode setting from the browser settings. - * - * @return {@link true} if the browser has reported that the user prefers dark mode. - * {@link false} if the browser doesn't prefer dark mode. - * {@link undefined} if the browser doesn't support the `prefers-color-scheme` media query. - */ -const determineDarkModeBrowserSettings = (): DarkModeConfig | undefined => { - if (!isClientSideRendering()) { - return { - darkMode: false - } - } - try { - const mediaQueryResult = window.matchMedia('(prefers-color-scheme: dark)').matches - return { - darkMode: mediaQueryResult - } - } catch (error) { - logger.error('Can not determine setting from browser', error) - return undefined + return DarkModePreference.AUTO } } diff --git a/src/components/common/icon-button/icon-button.tsx b/src/components/common/icon-button/icon-button.tsx index 770eee249..4428e9527 100644 --- a/src/components/common/icon-button/icon-button.tsx +++ b/src/components/common/icon-button/icon-button.tsx @@ -11,9 +11,10 @@ import { ForkAwesomeIcon } from '../fork-awesome/fork-awesome-icon' import type { IconName } from '../fork-awesome/types' import { ShowIf } from '../show-if/show-if' import styles from './icon-button.module.scss' +import type { PropsWithDataTestId } from '../../../utils/test-id' import { testId } from '../../../utils/test-id' -export interface IconButtonProps extends ButtonProps { +export interface IconButtonProps extends ButtonProps, PropsWithDataTestId { icon: IconName onClick?: () => void border?: boolean diff --git a/src/components/editor-page/app-bar/app-bar.tsx b/src/components/editor-page/app-bar/app-bar.tsx index 7d8f35b7a..6aa6e8e68 100644 --- a/src/components/editor-page/app-bar/app-bar.tsx +++ b/src/components/editor-page/app-bar/app-bar.tsx @@ -9,15 +9,14 @@ import { Nav, Navbar } from 'react-bootstrap' 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' -import { DarkModeButton } from './dark-mode-button' import { HelpButton } from './help-button/help-button' import { NavbarBranding } from './navbar-branding' -import { SyncScrollButtons } from './sync-scroll-buttons/sync-scroll-buttons' 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' import { NoteType } from '../../../redux/note-details/types/note-details' +import { SettingsButton } from '../../layout/settings-dialog/settings-button' export enum AppBarMode { BASIC, @@ -41,10 +40,6 @@ export const AppBar: React.FC = ({ mode }) => {