From 03b2cda845a25fbb6985b56c41cda6e3fcc3cea4 Mon Sep 17 00:00:00 2001 From: JimmFly Date: Wed, 28 Aug 2024 02:35:24 +0000 Subject: [PATCH] refactor(core): move fontFamily and fullWidthLayout to editor settings (#7988) --- packages/common/infra/src/atom/settings.ts | 18 ----- .../src/modules/feature-flag/constant.ts | 2 +- .../core/src/commands/affine-settings.tsx | 43 ++++------ .../general-setting/appearance/index.tsx | 11 --- .../general-setting/editor/general.tsx | 81 +++++++------------ .../general-setting/editor/page.tsx | 43 ++++++++-- .../src/components/page-detail-editor.tsx | 23 +++--- .../hooks/use-register-workspace-commands.ts | 6 +- .../core/src/modules/editor-settting/index.ts | 3 + .../src/modules/editor-settting/schema.ts | 19 ++++- .../peek-view/view/modal-container.tsx | 8 +- tests/affine-local/e2e/settings.spec.ts | 4 +- tests/kit/utils/setting.ts | 5 ++ 13 files changed, 131 insertions(+), 135 deletions(-) diff --git a/packages/common/infra/src/atom/settings.ts b/packages/common/infra/src/atom/settings.ts index 30566056a6..b85d5f34db 100644 --- a/packages/common/infra/src/atom/settings.ts +++ b/packages/common/infra/src/atom/settings.ts @@ -19,10 +19,7 @@ export type DateFormats = export type AppSetting = { clientBorder: boolean; - fullWidthLayout: boolean; windowFrameStyle: 'frameless' | 'NativeTitleBar'; - fontStyle: FontFamily; - customFontFamily: string; dateFormat: DateFormats; startWeekOnMonday: boolean; enableBlurBackground: boolean; @@ -46,24 +43,9 @@ export const dateFormatOptions: DateFormats[] = [ 'dd MMMM YYYY', ]; -export type FontFamily = 'Sans' | 'Serif' | 'Mono' | 'Custom'; - -export const fontStyleOptions = [ - { key: 'Sans', value: 'var(--affine-font-sans-family)' }, - { key: 'Serif', value: 'var(--affine-font-serif-family)' }, - { key: 'Mono', value: 'var(--affine-font-mono-family)' }, - { key: 'Custom', value: 'var(--affine-font-sans-family)' }, -] satisfies { - key: FontFamily; - value: string; -}[]; - const appSettingBaseAtom = atomWithStorage('affine-settings', { clientBorder: environment.isDesktop && !environment.isWindows, - fullWidthLayout: false, windowFrameStyle: 'frameless', - fontStyle: 'Sans', - customFontFamily: '', dateFormat: dateFormatOptions[0], startWeekOnMonday: false, enableBlurBackground: true, diff --git a/packages/common/infra/src/modules/feature-flag/constant.ts b/packages/common/infra/src/modules/feature-flag/constant.ts index 2368ed8610..2d23e0cdfa 100644 --- a/packages/common/infra/src/modules/feature-flag/constant.ts +++ b/packages/common/infra/src/modules/feature-flag/constant.ts @@ -96,7 +96,7 @@ export const AFFINE_FLAGS = { displayName: 'Editor Settings', description: 'Enables editor settings.', configurable: isCanaryBuild, - defaultState: false, + defaultState: isCanaryBuild, }, } satisfies { [key in string]: FlagInfo }; diff --git a/packages/frontend/core/src/commands/affine-settings.tsx b/packages/frontend/core/src/commands/affine-settings.tsx index 6e0164374a..ebc7af64bd 100644 --- a/packages/frontend/core/src/commands/affine-settings.tsx +++ b/packages/frontend/core/src/commands/affine-settings.tsx @@ -1,12 +1,12 @@ import type { useI18n } from '@affine/i18n'; import { SettingsIcon } from '@blocksuite/icons/rc'; -import type { AffineEditorContainer } from '@blocksuite/presets'; import { appSettingAtom } from '@toeverything/infra'; import type { createStore } from 'jotai'; import type { useTheme } from 'next-themes'; import type { useLanguageHelper } from '../hooks/affine/use-language-helper'; import { track } from '../mixpanel'; +import type { EditorSettingService } from '../modules/editor-settting'; import { registerAffineCommand } from './registry'; export function registerAffineSettingsCommands({ @@ -14,15 +14,20 @@ export function registerAffineSettingsCommands({ store, theme, languageHelper, + editorSettingService, }: { t: ReturnType; store: ReturnType; theme: ReturnType; languageHelper: ReturnType; - editor: AffineEditorContainer | null; + editorSettingService: EditorSettingService; }) { const unsubs: Array<() => void> = []; const { onLanguageChange, languagesList, currentLanguage } = languageHelper; + const updateSettings = editorSettingService.editorSetting.set.bind( + editorSettingService.editorSetting + ); + const settings$ = editorSettingService.editorSetting.settings$; // color modes unsubs.push( @@ -91,18 +96,14 @@ export function registerAffineSettingsCommands({ ]()}`, category: 'affine:settings', icon: , - preconditionStrategy: () => - store.get(appSettingAtom).fontStyle !== 'Sans', + preconditionStrategy: () => settings$.value.fontFamily !== 'Sans', run() { track.$.cmdk.settings.changeAppSetting({ key: 'fontStyle', value: 'Sans', }); - store.set(appSettingAtom, prev => ({ - ...prev, - fontStyle: 'Sans', - })); + updateSettings('fontFamily', 'Sans'); }, }) ); @@ -115,18 +116,14 @@ export function registerAffineSettingsCommands({ ]()}`, category: 'affine:settings', icon: , - preconditionStrategy: () => - store.get(appSettingAtom).fontStyle !== 'Serif', + preconditionStrategy: () => settings$.value.fontFamily !== 'Serif', run() { track.$.cmdk.settings.changeAppSetting({ key: 'fontStyle', value: 'Serif', }); - store.set(appSettingAtom, prev => ({ - ...prev, - fontStyle: 'Serif', - })); + updateSettings('fontFamily', 'Serif'); }, }) ); @@ -139,18 +136,14 @@ export function registerAffineSettingsCommands({ ]()}`, category: 'affine:settings', icon: , - preconditionStrategy: () => - store.get(appSettingAtom).fontStyle !== 'Mono', + preconditionStrategy: () => settings$.value.fontFamily !== 'Mono', run() { track.$.cmdk.settings.changeAppSetting({ key: 'fontStyle', value: 'Mono', }); - store.set(appSettingAtom, prev => ({ - ...prev, - fontStyle: 'Mono', - })); + updateSettings('fontFamily', 'Mono'); }, }) ); @@ -209,7 +202,7 @@ export function registerAffineSettingsCommands({ id: `affine:change-full-width-layout`, label: () => `${t['com.affine.cmdk.affine.full-width-layout.to']()} ${t[ - store.get(appSettingAtom).fullWidthLayout + settings$.value.fullWidthLayout ? 'com.affine.cmdk.affine.switch-state.off' : 'com.affine.cmdk.affine.switch-state.on' ]()}`, @@ -218,13 +211,9 @@ export function registerAffineSettingsCommands({ run() { track.$.cmdk.settings.changeAppSetting({ key: 'fullWidthLayout', - value: store.get(appSettingAtom).fullWidthLayout ? 'off' : 'on', + value: settings$.value.fullWidthLayout ? 'off' : 'on', }); - - store.set(appSettingAtom, prev => ({ - ...prev, - fullWidthLayout: !prev.fullWidthLayout, - })); + updateSettings('fullWidthLayout', !settings$.value.fullWidthLayout); }, }) ); diff --git a/packages/frontend/core/src/components/affine/setting-modal/general-setting/appearance/index.tsx b/packages/frontend/core/src/components/affine/setting-modal/general-setting/appearance/index.tsx index b7e0c54f25..a6186bbb13 100644 --- a/packages/frontend/core/src/components/affine/setting-modal/general-setting/appearance/index.tsx +++ b/packages/frontend/core/src/components/affine/setting-modal/general-setting/appearance/index.tsx @@ -97,17 +97,6 @@ export const AppearanceSettings = () => { /> ) : null} - - - updateSettings('fullWidthLayout', checked)} - /> - {runtimeConfig.enableNewSettingUnstableApi && environment.isDesktop ? ( { const t = useI18n(); - const { appSettings, updateSettings } = useAppSettingHelper(); + const { editorSettingService } = useServices({ EditorSettingService }); + const settings = useLiveData(editorSettingService.editorSetting.settings$); + const getLabel = useCallback( (fontKey: FontFamily) => { switch (fontKey) { @@ -70,8 +74,8 @@ const FontFamilySettings = () => { } const label = getLabel(key); let fontFamily = value; - if (key === 'Custom' && appSettings.customFontFamily) { - fontFamily = `${appSettings.customFontFamily}, ${value}`; + if (key === 'Custom' && settings.customFontFamily) { + fontFamily = `${settings.customFontFamily}, ${value}`; } return { value: key, @@ -83,20 +87,22 @@ const FontFamilySettings = () => { } satisfies RadioItem; }) .filter(item => item !== null); - }, [appSettings.customFontFamily, getLabel]); + }, [getLabel, settings.customFontFamily]); + + const handleFontFamilyChange = useCallback( + (value: FontFamily) => { + editorSettingService.editorSetting.set('fontFamily', value); + }, + [editorSettingService.editorSetting] + ); return ( { - updateSettings('fontStyle', value); - }, - [updateSettings] - )} + onChange={handleFontFamilyChange} /> ); }; @@ -211,15 +217,17 @@ const FontMenuItem = ({ const CustomFontFamilySettings = () => { const t = useI18n(); - const { appSettings, updateSettings } = useAppSettingHelper(); - const fontFamily = getFontFamily(appSettings.customFontFamily); + const { editorSettingService } = useServices({ EditorSettingService }); + const settings = useLiveData(editorSettingService.editorSetting.settings$); + + const fontFamily = getFontFamily(settings.customFontFamily); const onCustomFontFamilyChange = useCallback( (fontFamily: string) => { - updateSettings('customFontFamily', fontFamily); + editorSettingService.editorSetting.set('customFontFamily', fontFamily); }, - [updateSettings] + [editorSettingService.editorSetting] ); - if (appSettings.fontStyle !== 'Custom' || !environment.isDesktop) { + if (settings.fontFamily !== 'Custom' || !environment.isDesktop) { return null; } return ( @@ -239,7 +247,7 @@ const CustomFontFamilySettings = () => { }} > - {appSettings.customFontFamily || 'Select a font'} + {settings.customFontFamily || 'Select a font'} @@ -291,37 +299,6 @@ export const General = () => { - - inter}> - - inter - - - - - 15} - > - - 15 - - - { const t = useI18n(); - const { appSettings, updateSettings } = useAppSettingHelper(); + const editorSetting = useService(EditorSettingService).editorSetting; + const settings = useLiveData(editorSetting.settings$); + + const handleFullWidthLayoutChange = useCallback( + (checked: boolean) => { + editorSetting.set('fullWidthLayout', checked); + }, + [editorSetting] + ); + const handleDisplayDocInfoChange = useCallback( + (checked: boolean) => { + editorSetting.set('displayDocInfo', checked); + }, + [editorSetting] + ); + const handleDisplayBiDirectionalLinkChange = useCallback( + (checked: boolean) => { + editorSetting.set('displayBiDirectionalLink', checked); + }, + [editorSetting] + ); + return ( { > updateSettings('fullWidthLayout', checked)} + checked={settings.fullWidthLayout} + onChange={handleFullWidthLayoutChange} /> { 'com.affine.settings.editorSettings.page.display-doc-info.description' ]()} > - + { 'com.affine.settings.editorSettings.page.display-bi-link.description' ]()} > - + ); diff --git a/packages/frontend/core/src/components/page-detail-editor.tsx b/packages/frontend/core/src/components/page-detail-editor.tsx index 384d460779..612d0e2aef 100644 --- a/packages/frontend/core/src/components/page-detail-editor.tsx +++ b/packages/frontend/core/src/components/page-detail-editor.tsx @@ -4,20 +4,18 @@ import { useDocCollectionPage } from '@affine/core/hooks/use-block-suite-workspa import { DisposableGroup } from '@blocksuite/global/utils'; import type { AffineEditorContainer } from '@blocksuite/presets'; import type { Doc as BlockSuiteDoc, DocCollection } from '@blocksuite/store'; -import { - type DocMode, - fontStyleOptions, - useLiveData, - useService, -} from '@toeverything/infra'; +import { type DocMode, useLiveData, useService } from '@toeverything/infra'; import { cssVar } from '@toeverything/theme'; import clsx from 'clsx'; import type { CSSProperties } from 'react'; import { memo, Suspense, useCallback, useMemo } from 'react'; import { useLocation } from 'react-router-dom'; -import { useAppSettingHelper } from '../hooks/affine/use-app-setting-helper'; import { EditorService } from '../modules/editor'; +import { + EditorSettingService, + fontStyleOptions, +} from '../modules/editor-settting'; import { BlockSuiteEditor as Editor } from './blocksuite/block-suite-editor'; import * as styles from './page-detail-editor.css'; @@ -51,21 +49,22 @@ const PageDetailEditorMain = memo(function PageDetailEditorMain({ const mode = useLiveData(editor.mode$); const isSharedMode = editor.isSharedMode; - const { appSettings } = useAppSettingHelper(); + const editorSetting = useService(EditorSettingService).editorSetting; + const settings = useLiveData(editorSetting.settings$); const value = useMemo(() => { const fontStyle = fontStyleOptions.find( - option => option.key === appSettings.fontStyle + option => option.key === settings.fontFamily ); if (!fontStyle) { return cssVar('fontSansFamily'); } - const customFontFamily = appSettings.customFontFamily; + const customFontFamily = settings.customFontFamily; return customFontFamily && fontStyle.key === 'Custom' ? `${customFontFamily}, ${fontStyle.value}` : fontStyle.value; - }, [appSettings.customFontFamily, appSettings.fontStyle]); + }, [settings.customFontFamily, settings.fontFamily]); const blockId = useRouterHash(); @@ -96,7 +95,7 @@ const PageDetailEditorMain = memo(function PageDetailEditorMain({ return ( { const unsub = registerCMDKCommand(cmdkQuickSearchService, editor); @@ -109,13 +111,13 @@ export function useRegisterWorkspaceCommands() { t, theme, languageHelper, - editor, + editorSettingService, }); return () => { unsub(); }; - }, [store, t, theme, languageHelper, editor]); + }, [editorSettingService, languageHelper, store, t, theme]); // register AffineLayoutCommands useEffect(() => { diff --git a/packages/frontend/core/src/modules/editor-settting/index.ts b/packages/frontend/core/src/modules/editor-settting/index.ts index a56395c75e..575fc6ecef 100644 --- a/packages/frontend/core/src/modules/editor-settting/index.ts +++ b/packages/frontend/core/src/modules/editor-settting/index.ts @@ -4,6 +4,9 @@ import { EditorSetting } from './entities/editor-setting'; import { GlobalStateEditorSettingProvider } from './impls/global-state'; import { EditorSettingProvider } from './provider/editor-setting-provider'; import { EditorSettingService } from './services/editor-setting'; +export type { FontFamily } from './schema'; +export { EditorSettingSchema, fontStyleOptions } from './schema'; +export { EditorSettingService } from './services/editor-setting'; export function configureEditorSettingModule(framework: Framework) { framework diff --git a/packages/frontend/core/src/modules/editor-settting/schema.ts b/packages/frontend/core/src/modules/editor-settting/schema.ts index a47bc4013e..35e7791bdd 100644 --- a/packages/frontend/core/src/modules/editor-settting/schema.ts +++ b/packages/frontend/core/src/modules/editor-settting/schema.ts @@ -1,5 +1,17 @@ import { z } from 'zod'; +export type FontFamily = 'Sans' | 'Serif' | 'Mono' | 'Custom'; + +export const fontStyleOptions = [ + { key: 'Sans', value: 'var(--affine-font-sans-family)' }, + { key: 'Serif', value: 'var(--affine-font-serif-family)' }, + { key: 'Mono', value: 'var(--affine-font-mono-family)' }, + { key: 'Custom', value: 'var(--affine-font-sans-family)' }, +] satisfies { + key: FontFamily; + value: string; +}[]; + const BSEditorSettingSchema = z.object({ // TODO: import from bs connector: z.object({ @@ -14,9 +26,14 @@ const BSEditorSettingSchema = z.object({ .default('#000000'), }), }); - const AffineEditorSettingSchema = z.object({ fontFamily: z.enum(['Sans', 'Serif', 'Mono', 'Custom']).default('Sans'), + customFontFamily: z.string().default(''), + newDocDefaultMode: z.enum(['edgeless', 'page']).default('page'), + spellCheck: z.boolean().default(false), + fullWidthLayout: z.boolean().default(false), + displayDocInfo: z.boolean().default(true), + displayBiDirectionalLink: z.boolean().default(true), }); type UnionToIntersection = (U extends any ? (x: U) => void : never) extends ( diff --git a/packages/frontend/core/src/modules/peek-view/view/modal-container.tsx b/packages/frontend/core/src/modules/peek-view/view/modal-container.tsx index 8e9f8b83bc..17331e5239 100644 --- a/packages/frontend/core/src/modules/peek-view/view/modal-container.tsx +++ b/packages/frontend/core/src/modules/peek-view/view/modal-container.tsx @@ -1,5 +1,5 @@ -import { useAppSettingHelper } from '@affine/core/hooks/affine/use-app-setting-helper'; import * as Dialog from '@radix-ui/react-dialog'; +import { useLiveData, useService } from '@toeverything/infra'; import anime, { type AnimeInstance, type AnimeParams } from 'animejs'; import clsx from 'clsx'; import { @@ -13,6 +13,7 @@ import { useState, } from 'react'; +import { EditorSettingService } from '../../editor-settting'; import type { PeekViewAnimation } from '../entities/peek-view'; import * as styles from './modal-container.css'; @@ -90,7 +91,8 @@ export const PeekViewModalContainer = forwardRef< const overlayRef = useRef(null); const controlsRef = useRef(null); const prevAnimeMap = useRef>({}); - const { appSettings } = useAppSettingHelper(); + const editorSettings = useService(EditorSettingService).editorSetting; + const settings = useLiveData(editorSettings.settings$); const animateControls = useCallback((animateIn = false) => { const controls = controlsRef.current; @@ -320,7 +322,7 @@ export const PeekViewModalContainer = forwardRef< >
diff --git a/tests/affine-local/e2e/settings.spec.ts b/tests/affine-local/e2e/settings.spec.ts index 0143481f0e..6abe5eb73c 100644 --- a/tests/affine-local/e2e/settings.spec.ts +++ b/tests/affine-local/e2e/settings.spec.ts @@ -5,6 +5,7 @@ import { confirmExperimentalPrompt, openAboutPanel, openAppearancePanel, + openEditorSetting, openExperimentalFeaturesPanel, openSettingModal, openShortcutsPanel, @@ -61,8 +62,7 @@ test('Change theme', async ({ page }) => { test('Change layout width', async ({ page }) => { await openHomePage(page); await waitForEditorLoad(page); - await openSettingModal(page); - await openAppearancePanel(page); + await openEditorSetting(page); await page.getByTestId('full-width-layout-trigger').click(); diff --git a/tests/kit/utils/setting.ts b/tests/kit/utils/setting.ts index 37a4eb091e..d86cf4c280 100644 --- a/tests/kit/utils/setting.ts +++ b/tests/kit/utils/setting.ts @@ -16,6 +16,11 @@ export async function openAppearancePanel(page: Page) { await page.getByTestId('appearance-panel-trigger').click(); } +export async function openEditorSetting(page: Page) { + await page.getByTestId('settings-modal-trigger').click(); + await page.getByTestId('editor-panel-trigger').click(); +} + export async function openShortcutsPanel(page: Page) { await page.getByTestId('shortcuts-panel-trigger').click(); }