fix(electron): sync settings from localStorage -> atom -> electron (#5020)

- moved `appSettingAtom` to infra since we now have different packages that depends on it. There is no better place to fit in for now
- use atomEffect to sync setting changes to updater related configs to Electron side
- refactored how Electron reacts to updater config changes.
This commit is contained in:
Peng Xiao
2023-12-08 03:20:02 +00:00
parent 453d4db713
commit fcd43033fe
30 changed files with 441 additions and 343 deletions

View File

@@ -1,77 +0,0 @@
import { atom } from 'jotai';
import { atomWithStorage } from 'jotai/utils';
export type DateFormats =
| 'MM/dd/YYYY'
| 'dd/MM/YYYY'
| 'YYYY-MM-dd'
| 'YYYY.MM.dd'
| 'YYYY/MM/dd'
| 'dd-MMM-YYYY'
| 'dd MMMM YYYY';
export type AppSetting = {
clientBorder: boolean;
fullWidthLayout: boolean;
windowFrameStyle: 'frameless' | 'NativeTitleBar';
fontStyle: FontFamily;
dateFormat: DateFormats;
startWeekOnMonday: boolean;
enableBlurBackground: boolean;
enableNoisyBackground: boolean;
autoCheckUpdate: boolean;
autoDownloadUpdate: boolean;
};
export const windowFrameStyleOptions: AppSetting['windowFrameStyle'][] = [
'frameless',
'NativeTitleBar',
];
export const dateFormatOptions: DateFormats[] = [
'MM/dd/YYYY',
'dd/MM/YYYY',
'YYYY-MM-dd',
'YYYY.MM.dd',
'YYYY/MM/dd',
'dd-MMM-YYYY',
'dd MMMM YYYY',
];
export type FontFamily = 'Sans' | 'Serif' | 'Mono';
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)' },
] satisfies {
key: FontFamily;
value: string;
}[];
const appSettingBaseAtom = atomWithStorage<AppSetting>('affine-settings', {
clientBorder: environment.isDesktop && !environment.isWindows,
fullWidthLayout: false,
windowFrameStyle: 'frameless',
fontStyle: 'Sans',
dateFormat: dateFormatOptions[0],
startWeekOnMonday: false,
enableBlurBackground: true,
enableNoisyBackground: true,
autoCheckUpdate: true,
autoDownloadUpdate: true,
});
type SetStateAction<Value> = Value | ((prev: Value) => Value);
export const appSettingAtom = atom<
AppSetting,
[SetStateAction<Partial<AppSetting>>],
void
>(
get => get(appSettingBaseAtom),
(get, set, apply) => {
const prev = get(appSettingBaseAtom);
const next = typeof apply === 'function' ? apply(prev) : apply;
set(appSettingBaseAtom, { ...prev, ...next });
}
);

View File

@@ -1,5 +1,6 @@
import type { useAFFiNEI18N } from '@affine/i18n/hooks';
import { SettingsIcon } from '@blocksuite/icons';
import { appSettingAtom } from '@toeverything/infra/atom';
import {
PreconditionStrategy,
registerAffineCommand,
@@ -8,7 +9,6 @@ import { type createStore } from 'jotai';
import type { useTheme } from 'next-themes';
import { openQuickSearchModalAtom } from '../atoms';
import { appSettingAtom } from '../atoms/settings';
import type { useLanguageHelper } from '../hooks/affine/use-language-helper';
export function registerAffineSettingsCommands({

View File

@@ -3,15 +3,8 @@ import { SettingRow } from '@affine/component/setting-components';
import { Button } from '@affine/component/ui/button';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks';
import {
downloadProgressAtom,
isCheckingForUpdatesAtom,
updateAvailableAtom,
updateReadyAtom,
useAppUpdater,
} from '@toeverything/hooks/use-app-updater';
import { useAppUpdater } from '@toeverything/hooks/use-app-updater';
import clsx from 'clsx';
import { useAtomValue } from 'jotai';
import { useCallback, useMemo, useState } from 'react';
import * as styles from './style.css';
@@ -25,18 +18,16 @@ enum CheckUpdateStatus {
const useUpdateStatusLabels = (checkUpdateStatus: CheckUpdateStatus) => {
const t = useAFFiNEI18N();
const isCheckingForUpdates = useAtomValue(isCheckingForUpdatesAtom);
const updateAvailable = useAtomValue(updateAvailableAtom);
const updateReady = useAtomValue(updateReadyAtom);
const downloadProgress = useAtomValue(downloadProgressAtom);
const { updateAvailable, downloadProgress, updateReady, checkingForUpdates } =
useAppUpdater();
const buttonLabel = useMemo(() => {
if (updateAvailable && downloadProgress === null) {
return t['com.affine.aboutAFFiNE.checkUpdate.button.download']();
}
if (updateReady) {
return t['com.affine.aboutAFFiNE.checkUpdate.button.restart']();
}
if (updateAvailable && downloadProgress === null) {
return t['com.affine.aboutAFFiNE.checkUpdate.button.download']();
}
if (
checkUpdateStatus === CheckUpdateStatus.LATEST ||
checkUpdateStatus === CheckUpdateStatus.ERROR
@@ -47,16 +38,16 @@ const useUpdateStatusLabels = (checkUpdateStatus: CheckUpdateStatus) => {
}, [checkUpdateStatus, downloadProgress, t, updateAvailable, updateReady]);
const subtitleLabel = useMemo(() => {
if (updateAvailable && downloadProgress === null) {
if (updateReady) {
return t['com.affine.aboutAFFiNE.checkUpdate.subtitle.restart']();
} else if (updateAvailable && downloadProgress === null) {
return t['com.affine.aboutAFFiNE.checkUpdate.subtitle.update-available']({
version: updateAvailable.version,
});
} else if (isCheckingForUpdates) {
} else if (checkingForUpdates) {
return t['com.affine.aboutAFFiNE.checkUpdate.subtitle.checking']();
} else if (updateAvailable && downloadProgress !== null) {
return t['com.affine.aboutAFFiNE.checkUpdate.subtitle.downloading']();
} else if (updateReady) {
return t['com.affine.aboutAFFiNE.checkUpdate.subtitle.restart']();
} else if (checkUpdateStatus === CheckUpdateStatus.ERROR) {
return t['com.affine.aboutAFFiNE.checkUpdate.subtitle.error']();
} else if (checkUpdateStatus === CheckUpdateStatus.LATEST) {
@@ -66,7 +57,7 @@ const useUpdateStatusLabels = (checkUpdateStatus: CheckUpdateStatus) => {
}, [
checkUpdateStatus,
downloadProgress,
isCheckingForUpdates,
checkingForUpdates,
t,
updateAvailable,
updateReady,
@@ -83,14 +74,14 @@ const useUpdateStatusLabels = (checkUpdateStatus: CheckUpdateStatus) => {
error: checkUpdateStatus === CheckUpdateStatus.ERROR,
})}
>
{isCheckingForUpdates ? <Loading size={14} /> : null}
{checkingForUpdates ? <Loading size={14} /> : null}
{subtitleLabel}
</span>
);
}, [
checkUpdateStatus,
downloadProgress,
isCheckingForUpdates,
checkingForUpdates,
subtitleLabel,
updateAvailable,
updateReady,
@@ -101,10 +92,14 @@ const useUpdateStatusLabels = (checkUpdateStatus: CheckUpdateStatus) => {
export const UpdateCheckSection = () => {
const t = useAFFiNEI18N();
const { checkForUpdates, downloadUpdate, quitAndInstall } = useAppUpdater();
const updateAvailable = useAtomValue(updateAvailableAtom);
const updateReady = useAtomValue(updateReadyAtom);
const downloadProgress = useAtomValue(downloadProgressAtom);
const {
checkForUpdates,
downloadUpdate,
quitAndInstall,
updateAvailable,
downloadProgress,
updateReady,
} = useAppUpdater();
const [checkUpdateStatus, setCheckUpdateStatus] = useState<CheckUpdateStatus>(
CheckUpdateStatus.UNCHECK
);

View File

@@ -1,11 +1,8 @@
import { Menu, MenuItem, MenuTrigger } from '@affine/component/ui/menu';
import { dateFormatOptions, type DateFormats } from '@toeverything/infra/atom';
import dayjs from 'dayjs';
import { useCallback } from 'react';
import {
dateFormatOptions,
type DateFormats,
} from '../../../../../atoms/settings';
import { useAppSettingHelper } from '../../../../../hooks/affine/use-app-setting-helper';
interface DateFormatMenuContentProps {

View File

@@ -3,14 +3,14 @@ import { SettingHeader } from '@affine/component/setting-components';
import { SettingRow } from '@affine/component/setting-components';
import { SettingWrapper } from '@affine/component/setting-components';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { useTheme } from 'next-themes';
import { useCallback } from 'react';
import {
type AppSetting,
fontStyleOptions,
windowFrameStyleOptions,
} from '../../../../../atoms/settings';
} from '@toeverything/infra/atom';
import { useTheme } from 'next-themes';
import { useCallback } from 'react';
import { useAppSettingHelper } from '../../../../../hooks/affine/use-app-setting-helper';
import { LanguageMenu } from '../../../language-menu';
import { DateFormatSetting } from './date-format-setting';

View File

@@ -8,6 +8,7 @@ import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-
import { useBlockSuiteWorkspacePage } from '@toeverything/hooks/use-block-suite-workspace-page';
import { pluginEditorAtom } from '@toeverything/infra/__internal__/plugin';
import { getCurrentStore } from '@toeverything/infra/atom';
import { fontStyleOptions } from '@toeverything/infra/atom';
import clsx from 'clsx';
import { useAtomValue } from 'jotai';
import type { CSSProperties } from 'react';
@@ -15,7 +16,6 @@ import { memo, Suspense, useCallback, useMemo, useState } from 'react';
import { useLocation } from 'react-router-dom';
import { type PageMode, pageSettingFamily } from '../atoms';
import { fontStyleOptions } from '../atoms/settings';
import { useAppSettingHelper } from '../hooks/affine/use-app-setting-helper';
import { useBlockSuiteMetaHelper } from '../hooks/affine/use-block-suite-meta-helper';
import { BlockSuiteEditor as Editor } from './blocksuite/block-suite-editor';

View File

@@ -20,11 +20,6 @@ import { FolderIcon, SettingsIcon } from '@blocksuite/icons';
import type { Page } from '@blocksuite/store';
import { useDroppable } from '@dnd-kit/core';
import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks';
import {
isAutoCheckUpdateAtom,
isAutoDownloadUpdateAtom,
useAppUpdater,
} from '@toeverything/hooks/use-app-updater';
import { useAtom, useAtomValue } from 'jotai';
import type { HTMLAttributes, ReactElement } from 'react';
import { forwardRef, useCallback, useEffect, useMemo } from 'react';
@@ -107,10 +102,6 @@ export const RootAppSidebar = ({
}: RootAppSidebarProps): ReactElement => {
const currentWorkspaceId = currentWorkspace.id;
const { appSettings } = useAppSettingHelper();
const { toggleAutoCheck, toggleAutoDownload } = useAppUpdater();
const { autoCheckUpdate, autoDownloadUpdate } = appSettings;
const isAutoDownload = useAtomValue(isAutoDownloadUpdateAtom);
const isAutoCheck = useAtomValue(isAutoCheckUpdateAtom);
const blockSuiteWorkspace = currentWorkspace.blockSuiteWorkspace;
const t = useAFFiNEI18N();
const [openUserWorkspaceList, setOpenUserWorkspaceList] = useAtom(
@@ -159,26 +150,6 @@ export const RootAppSidebar = ({
}
}, [sidebarOpen]);
useEffect(() => {
if (!environment.isDesktop) {
return;
}
if (isAutoCheck !== autoCheckUpdate) {
toggleAutoCheck(autoCheckUpdate);
}
if (isAutoDownload !== autoDownloadUpdate) {
toggleAutoDownload(autoDownloadUpdate);
}
}, [
autoCheckUpdate,
autoDownloadUpdate,
isAutoCheck,
isAutoDownload,
toggleAutoCheck,
toggleAutoDownload,
]);
const [history, setHistory] = useHistoryAtom();
const router = useMemo(() => {
return {

View File

@@ -1,8 +1,7 @@
import { type AppSetting, appSettingAtom } from '@toeverything/infra/atom';
import { useAtom } from 'jotai';
import { useCallback, useMemo } from 'react';
import { type AppSetting, appSettingAtom } from '../../atoms/settings';
export function useAppSettingHelper() {
const [appSettings, setAppSettings] = useAtom(appSettingAtom);

View File

@@ -11,6 +11,7 @@ import type { EditorContainer } from '@blocksuite/presets';
import type { Page, Workspace } from '@blocksuite/store';
import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta';
import {
appSettingAtom,
currentPageIdAtom,
currentWorkspaceIdAtom,
} from '@toeverything/infra/atom';
@@ -36,7 +37,6 @@ import type { Map as YMap } from 'yjs';
import { setPageModeAtom } from '../../../atoms';
import { collectionsCRUDAtom } from '../../../atoms/collections';
import { currentModeAtom } from '../../../atoms/mode';
import { appSettingAtom } from '../../../atoms/settings';
import { AffineErrorBoundary } from '../../../components/affine/affine-error-boundary';
import { HubIsland } from '../../../components/affine/hub-island';
import { GlobalPageHistoryModal } from '../../../components/affine/page-history-modal';