Merge remote-tracking branch 'origin/master' into payment-system

This commit is contained in:
Joooye_34
2023-10-27 16:04:48 +08:00
87 changed files with 1004 additions and 581 deletions

View File

@@ -1,4 +1,4 @@
import { atom, useAtom } from 'jotai';
import { atom } from 'jotai';
import { atomWithStorage } from 'jotai/utils';
export type DateFormats =
@@ -75,7 +75,3 @@ export const appSettingAtom = atom<
set(appSettingBaseAtom, { ...prev, ...next });
}
);
export const useAppSetting = () => {
return useAtom(appSettingAtom);
};

View File

@@ -20,7 +20,7 @@ export function registerAffineCreationCommands({
registerAffineCommand({
id: 'affine:new-page',
category: 'affine:creation',
label: t['com.affine.cmdk.affine.new-page'],
label: t['com.affine.cmdk.affine.new-page'](),
icon: <PlusIcon />,
keyBinding: environment.isDesktop
? {
@@ -39,7 +39,7 @@ export function registerAffineCreationCommands({
id: 'affine:new-edgeless-page',
category: 'affine:creation',
icon: <PlusIcon />,
label: t['com.affine.cmdk.affine.new-edgeless-page'],
label: t['com.affine.cmdk.affine.new-edgeless-page'](),
run() {
pageHelper.createEdgeless();
},
@@ -51,7 +51,7 @@ export function registerAffineCreationCommands({
id: 'affine:new-workspace',
category: 'affine:creation',
icon: <PlusIcon />,
label: t['com.affine.cmdk.affine.new-workspace'],
label: t['com.affine.cmdk.affine.new-workspace'](),
run() {
store.set(openCreateWorkspaceModalAtom, 'new');
},
@@ -62,7 +62,7 @@ export function registerAffineCreationCommands({
id: 'affine:import-workspace',
category: 'affine:creation',
icon: <ImportIcon />,
label: t['com.affine.cmdk.affine.import-workspace'],
label: t['com.affine.cmdk.affine.import-workspace'](),
preconditionStrategy: () => {
return environment.isDesktop;
},

View File

@@ -18,7 +18,7 @@ export function registerAffineHelpCommands({
id: 'affine:help-whats-new',
category: 'affine:help',
icon: <NewIcon />,
label: () => t['com.affine.cmdk.affine.whats-new'](),
label: t['com.affine.cmdk.affine.whats-new'](),
run() {
window.open(runtimeConfig.changelogUrl, '_blank');
},
@@ -29,7 +29,7 @@ export function registerAffineHelpCommands({
id: 'affine:help-contact-us',
category: 'affine:help',
icon: <ContactWithUsIcon />,
label: () => t['com.affine.cmdk.affine.contact-us'](),
label: t['com.affine.cmdk.affine.contact-us'](),
run() {
store.set(openSettingModalAtom, {
open: true,
@@ -44,7 +44,7 @@ export function registerAffineHelpCommands({
id: 'affine:help-getting-started',
category: 'affine:help',
icon: <UserGuideIcon />,
label: () => t['com.affine.cmdk.affine.getting-started'](),
label: t['com.affine.cmdk.affine.getting-started'](),
preconditionStrategy: () => environment.isDesktop,
run() {
store.set(openOnboardingModalAtom, true);

View File

@@ -17,14 +17,11 @@ export function registerAffineLayoutCommands({
id: 'affine:toggle-left-sidebar',
category: 'affine:layout',
icon: <SidebarIcon />,
label: () => {
const open = store.get(appSidebarOpenAtom);
return t[
open
? 'com.affine.cmdk.affine.left-sidebar.collapse'
: 'com.affine.cmdk.affine.left-sidebar.expand'
]();
},
label: () =>
store.get(appSidebarOpenAtom)
? t['com.affine.cmdk.affine.left-sidebar.collapse']()
: t['com.affine.cmdk.affine.left-sidebar.expand'](),
keyBinding: {
binding: '$mod+/',
},

View File

@@ -33,7 +33,7 @@ export function registerAffineNavigationCommands({
id: 'affine:goto-all-pages',
category: 'affine:navigation',
icon: <ArrowRightBigIcon />,
label: () => t['com.affine.cmdk.affine.navigation.goto-all-pages'](),
label: t['com.affine.cmdk.affine.navigation.goto-all-pages'](),
run() {
navigationHelper.jumpToSubPath(workspace.id, WorkspaceSubPath.ALL);
setPageListMode('all');
@@ -49,7 +49,7 @@ export function registerAffineNavigationCommands({
preconditionStrategy: () => {
return pageListMode !== 'page';
},
label: () => t['com.affine.cmdk.affine.navigation.goto-page-list'](),
label: t['com.affine.cmdk.affine.navigation.goto-page-list'](),
run() {
navigationHelper.jumpToSubPath(workspace.id, WorkspaceSubPath.ALL);
setPageListMode('page');
@@ -65,7 +65,7 @@ export function registerAffineNavigationCommands({
preconditionStrategy: () => {
return pageListMode !== 'edgeless';
},
label: () => t['com.affine.cmdk.affine.navigation.goto-edgeless-list'](),
label: t['com.affine.cmdk.affine.navigation.goto-edgeless-list'](),
run() {
navigationHelper.jumpToSubPath(workspace.id, WorkspaceSubPath.ALL);
setPageListMode('edgeless');
@@ -78,7 +78,7 @@ export function registerAffineNavigationCommands({
id: 'affine:goto-workspace',
category: 'affine:navigation',
icon: <ArrowRightBigIcon />,
label: () => t['com.affine.cmdk.affine.navigation.goto-workspace'](),
label: t['com.affine.cmdk.affine.navigation.goto-workspace'](),
run() {
store.set(openWorkspaceListModalAtom, true);
},
@@ -90,7 +90,7 @@ export function registerAffineNavigationCommands({
id: 'affine:open-settings',
category: 'affine:navigation',
icon: <ArrowRightBigIcon />,
label: () => t['com.affine.cmdk.affine.navigation.open-settings'](),
label: t['com.affine.cmdk.affine.navigation.open-settings'](),
run() {
store.set(openSettingModalAtom, {
activeTab: 'appearance',
@@ -106,7 +106,7 @@ export function registerAffineNavigationCommands({
id: 'affine:goto-trash',
category: 'affine:navigation',
icon: <ArrowRightBigIcon />,
label: () => t['com.affine.cmdk.affine.navigation.goto-trash'](),
label: t['com.affine.cmdk.affine.navigation.goto-trash'](),
run() {
navigationHelper.jumpToSubPath(workspace.id, WorkspaceSubPath.TRASH);
setPageListMode('all');

View File

@@ -1,76 +1,16 @@
import { Trans } from '@affine/i18n';
import type { useAFFiNEI18N } from '@affine/i18n/hooks';
import { SettingsIcon } from '@blocksuite/icons';
import {
PreconditionStrategy,
registerAffineCommand,
} from '@toeverything/infra/command';
import { type createStore, useAtomValue } from 'jotai';
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';
// todo - find a better way to abstract the following translations components
const ClientBorderStyleLabel = () => {
const { clientBorder } = useAtomValue(appSettingAtom);
return (
<Trans
i18nKey="com.affine.cmdk.affine.client-border-style.to"
values={{
state: clientBorder ? 'OFF' : 'ON',
}}
>
Change Client Border Style to
<strong>state</strong>
</Trans>
);
};
const FullWidthLayoutLabel = () => {
const { fullWidthLayout } = useAtomValue(appSettingAtom);
return (
<Trans
i18nKey="com.affine.cmdk.affine.full-width-layout.to"
values={{
state: fullWidthLayout ? 'OFF' : 'ON',
}}
>
Change Full Width Layout to
<strong>state</strong>
</Trans>
);
};
const NoisyBackgroundLabel = () => {
const { enableNoisyBackground } = useAtomValue(appSettingAtom);
return (
<Trans
i18nKey="com.affine.cmdk.affine.noise-background-on-the-sidebar.to"
values={{
state: enableNoisyBackground ? 'OFF' : 'ON',
}}
>
Change Noise Background On The Sidebar to <strong>state</strong>
</Trans>
);
};
const BlurBackgroundLabel = () => {
const { enableBlurBackground } = useAtomValue(appSettingAtom);
return (
<Trans
i18nKey="com.affine.cmdk.affine.translucent-ui-on-the-sidebar.to"
values={{
state: enableBlurBackground ? 'OFF' : 'ON',
}}
>
Change Translucent UI On The Sidebar to <strong>state</strong>
</Trans>
);
};
export function registerAffineSettingsCommands({
t,
store,
@@ -83,7 +23,7 @@ export function registerAffineSettingsCommands({
languageHelper: ReturnType<typeof useLanguageHelper>;
}) {
const unsubs: Array<() => void> = [];
const { onSelect, languagesList, currentLanguage } = languageHelper;
const { onLanguageChange, languagesList, currentLanguage } = languageHelper;
unsubs.push(
registerAffineCommand({
id: 'affine:show-quick-search',
@@ -92,6 +32,7 @@ export function registerAffineSettingsCommands({
keyBinding: {
binding: '$mod+K',
},
label: '',
icon: <SettingsIcon />,
run() {
const quickSearchModalState = store.get(openQuickSearchModalAtom);
@@ -104,14 +45,9 @@ export function registerAffineSettingsCommands({
unsubs.push(
registerAffineCommand({
id: 'affine:change-color-scheme-to-auto',
label: (
<Trans
i18nKey="com.affine.cmdk.affine.color-scheme.to"
values={{ colour: 'Auto' }}
>
Change Colour Scheme to <strong>colour</strong>
</Trans>
),
label: `${t['com.affine.cmdk.affine.color-scheme.to']()} ${t[
'com.affine.themeSettings.system'
]()}`,
category: 'affine:settings',
icon: <SettingsIcon />,
preconditionStrategy: () => theme.theme !== 'system',
@@ -123,14 +59,9 @@ export function registerAffineSettingsCommands({
unsubs.push(
registerAffineCommand({
id: 'affine:change-color-scheme-to-dark',
label: (
<Trans
i18nKey="com.affine.cmdk.affine.color-scheme.to"
values={{ colour: 'Dark' }}
>
Change Colour Scheme to <strong>colour</strong>
</Trans>
),
label: `${t['com.affine.cmdk.affine.color-scheme.to']()} ${t[
'com.affine.themeSettings.dark'
]()}`,
category: 'affine:settings',
icon: <SettingsIcon />,
preconditionStrategy: () => theme.theme !== 'dark',
@@ -143,14 +74,9 @@ export function registerAffineSettingsCommands({
unsubs.push(
registerAffineCommand({
id: 'affine:change-color-scheme-to-light',
label: (
<Trans
i18nKey="com.affine.cmdk.affine.color-scheme.to"
values={{ colour: 'Light' }}
>
Change Colour Scheme to <strong>colour</strong>
</Trans>
),
label: `${t['com.affine.cmdk.affine.color-scheme.to']()} ${t[
'com.affine.themeSettings.light'
]()}`,
category: 'affine:settings',
icon: <SettingsIcon />,
preconditionStrategy: () => theme.theme !== 'light',
@@ -164,16 +90,9 @@ export function registerAffineSettingsCommands({
unsubs.push(
registerAffineCommand({
id: 'affine:change-font-style-to-sans',
label: (
<Trans
i18nKey="com.affine.cmdk.affine.font-style.to"
values={{
fontFamily: t['com.affine.appearanceSettings.fontStyle.sans'](),
}}
>
Change Font Style to <strong>fontFamily</strong>
</Trans>
),
label: `${t['com.affine.cmdk.affine.font-style.to']()} ${t[
'com.affine.appearanceSettings.fontStyle.sans'
]()}`,
category: 'affine:settings',
icon: <SettingsIcon />,
preconditionStrategy: () =>
@@ -190,19 +109,9 @@ export function registerAffineSettingsCommands({
unsubs.push(
registerAffineCommand({
id: 'affine:change-font-style-to-serif',
label: (
<Trans
i18nKey="com.affine.cmdk.affine.font-style.to"
values={{
fontFamily: t['com.affine.appearanceSettings.fontStyle.serif'](),
}}
>
Change Font Style to
<strong style={{ fontFamily: 'var(--affine-font-serif-family)' }}>
fontFamily
</strong>
</Trans>
),
label: `${t['com.affine.cmdk.affine.font-style.to']()} ${t[
'com.affine.appearanceSettings.fontStyle.serif'
]()}`,
category: 'affine:settings',
icon: <SettingsIcon />,
preconditionStrategy: () =>
@@ -219,19 +128,9 @@ export function registerAffineSettingsCommands({
unsubs.push(
registerAffineCommand({
id: 'affine:change-font-style-to-mono',
label: (
<Trans
i18nKey="com.affine.cmdk.affine.font-style.to"
values={{
fontFamily: t['com.affine.appearanceSettings.fontStyle.mono'](),
}}
>
Change Font Style to
<strong style={{ fontFamily: 'var(--affine-font-mono-family)' }}>
fontFamily
</strong>
</Trans>
),
label: `${t['com.affine.cmdk.affine.font-style.to']()} ${t[
'com.affine.appearanceSettings.fontStyle.mono'
]()}`,
category: 'affine:settings',
icon: <SettingsIcon />,
preconditionStrategy: () =>
@@ -250,22 +149,14 @@ export function registerAffineSettingsCommands({
unsubs.push(
registerAffineCommand({
id: `affine:change-display-language-to-${language.name}`,
label: (
<Trans
i18nKey="com.affine.cmdk.affine.display-language.to"
values={{
language: language.originalName,
}}
>
Change Display Language to
<strong>language</strong>
</Trans>
),
label: `${t['com.affine.cmdk.affine.display-language.to']()} ${
language.originalName
}`,
category: 'affine:settings',
icon: <SettingsIcon />,
preconditionStrategy: () => currentLanguage?.tag !== language.tag,
run() {
onSelect(language.tag);
onLanguageChange(language.tag);
},
})
);
@@ -275,7 +166,12 @@ export function registerAffineSettingsCommands({
unsubs.push(
registerAffineCommand({
id: `affine:change-client-border-style`,
label: <ClientBorderStyleLabel />,
label: () => `${t['com.affine.cmdk.affine.client-border-style.to']()} ${t[
store.get(appSettingAtom).clientBorder
? 'com.affine.cmdk.affine.switch-state.off'
: 'com.affine.cmdk.affine.switch-state.on'
]()}
`,
category: 'affine:settings',
icon: <SettingsIcon />,
preconditionStrategy: () => environment.isDesktop,
@@ -291,7 +187,12 @@ export function registerAffineSettingsCommands({
unsubs.push(
registerAffineCommand({
id: `affine:change-full-width-layout`,
label: <FullWidthLayoutLabel />,
label: () =>
`${t['com.affine.cmdk.affine.full-width-layout.to']()} ${t[
store.get(appSettingAtom).fullWidthLayout
? 'com.affine.cmdk.affine.switch-state.off'
: 'com.affine.cmdk.affine.switch-state.on'
]()}`,
category: 'affine:settings',
icon: <SettingsIcon />,
run() {
@@ -306,7 +207,14 @@ export function registerAffineSettingsCommands({
unsubs.push(
registerAffineCommand({
id: `affine:change-noise-background-on-the-sidebar`,
label: <NoisyBackgroundLabel />,
label: () =>
`${t[
'com.affine.cmdk.affine.noise-background-on-the-sidebar.to'
]()} ${t[
store.get(appSettingAtom).enableNoisyBackground
? 'com.affine.cmdk.affine.switch-state.off'
: 'com.affine.cmdk.affine.switch-state.on'
]()}`,
category: 'affine:settings',
icon: <SettingsIcon />,
preconditionStrategy: () => environment.isDesktop,
@@ -322,7 +230,12 @@ export function registerAffineSettingsCommands({
unsubs.push(
registerAffineCommand({
id: `affine:change-translucent-ui-on-the-sidebar`,
label: <BlurBackgroundLabel />,
label: () =>
`${t['com.affine.cmdk.affine.translucent-ui-on-the-sidebar.to']()} ${t[
store.get(appSettingAtom).enableBlurBackground
? 'com.affine.cmdk.affine.switch-state.off'
: 'com.affine.cmdk.affine.switch-state.on'
]()}`,
category: 'affine:settings',
icon: <SettingsIcon />,
preconditionStrategy: () => environment.isDesktop,

View File

@@ -18,7 +18,7 @@ export function registerAffineUpdatesCommands({
id: 'affine:restart-to-upgrade',
category: 'affine:updates',
icon: <ResetIcon />,
label: () => t['com.affine.cmdk.affine.restart-to-upgrade'](),
label: t['com.affine.cmdk.affine.restart-to-upgrade'](),
preconditionStrategy: () => !!store.get(updateReadyAtom),
run() {
window.apis?.updater.quitAndInstall().catch(err => {

View File

@@ -3,10 +3,10 @@ import {
type WorkspaceRootProps,
} from '@affine/component/workspace';
import { useAppSetting } from '../../atoms/settings';
import { useAppSettingHelper } from '../../hooks/affine/use-app-setting-helper';
export const AppContainer = (props: WorkspaceRootProps) => {
const [appSettings] = useAppSetting();
const { appSettings } = useAppSettingHelper();
return (
<AppContainerWithoutSettings

View File

@@ -5,7 +5,8 @@ import { useLanguageHelper } from '../../../hooks/affine/use-language-helper';
// Fixme: keyboard focus should be supported by Menu component
const LanguageMenuContent = memo(function LanguageMenuContent() {
const { currentLanguage, languagesList, onSelect } = useLanguageHelper();
const { currentLanguage, languagesList, onLanguageChange } =
useLanguageHelper();
return (
<>
{languagesList.map(option => {
@@ -14,7 +15,7 @@ const LanguageMenuContent = memo(function LanguageMenuContent() {
key={option.name}
selected={currentLanguage?.originalName === option.originalName}
title={option.name}
onSelect={() => onSelect(option.tag)}
onSelect={() => onLanguageChange(option.tag)}
>
{option.originalName}
</MenuItem>

View File

@@ -66,12 +66,7 @@ const PublishPanelAffine = (props: PublishPanelAffineProps) => {
marginBottom: isPublic ? '12px' : '25px',
}}
>
<Switch
checked={isPublic}
// onChange={useCallback(value => {
// console.log('onChange', value);
// }, [])}
/>
<Switch checked={isPublic} />
</SettingRow>
{isPublic ? (
<FlexWrapper justifyContent="space-between" marginBottom={25}>

View File

@@ -15,7 +15,7 @@ export const relatedLinks = [
},
{
icon: <TwitterIcon />,
title: 'Twitter',
title: 'X',
link: 'https://twitter.com/AffineOfficial',
},
{

View File

@@ -1,3 +1,6 @@
// The icons here have been specially adjusted, theyre different from the ones in the @blocksuite/icons.
export { TwitterIcon } from '@blocksuite/icons';
export const LogoIcon = () => {
return (
<svg
@@ -33,23 +36,6 @@ export const DocIcon = () => {
);
};
export const TwitterIcon = () => {
return (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M22 5.88235C21.2639 6.21176 20.4704 6.42824 19.6482 6.53176C20.4895 6.03294 21.1396 5.24235 21.4455 4.29176C20.652 4.76235 19.7725 5.09176 18.8451 5.28C18.0899 4.47059 17.0287 4 15.8241 4C13.5774 4 11.7419 5.80706 11.7419 8.03765C11.7419 8.35765 11.7801 8.66824 11.847 8.96C8.4436 8.79059 5.413 7.18118 3.39579 4.74353C3.04207 5.33647 2.8413 6.03294 2.8413 6.76706C2.8413 8.16941 3.55832 9.41176 4.6673 10.1176C3.98853 10.1176 3.35755 9.92941 2.80306 9.64706V9.67529C2.80306 11.6329 4.21797 13.2706 6.09178 13.6376C5.49018 13.7997 4.8586 13.8223 4.24665 13.7035C4.50632 14.5059 5.01485 15.2079 5.70078 15.711C6.38671 16.2141 7.21553 16.4929 8.07075 16.5082C6.62106 17.6381 4.82409 18.2488 2.97514 18.24C2.6501 18.24 2.32505 18.2212 2 18.1835C3.81644 19.3318 5.97706 20 8.29063 20C15.8241 20 19.9637 13.8447 19.9637 8.50824C19.9637 8.32941 19.9637 8.16 19.9541 7.98118C20.7572 7.41647 21.4455 6.70118 22 5.88235Z"
fill="#1D9BF0"
/>
</svg>
);
};
export const GithubIcon = () => {
return (
<svg

View File

@@ -4,21 +4,14 @@ import { SettingRow } from '@affine/component/setting-components';
import { SettingWrapper } from '@affine/component/setting-components';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { ArrowRightSmallIcon, OpenInNewIcon } from '@blocksuite/icons';
import { useCallback } from 'react';
import { type AppSetting, useAppSetting } from '../../../../../atoms/settings';
import { useAppSettingHelper } from '../../../../../hooks/affine/use-app-setting-helper';
import { relatedLinks } from './config';
import { communityItem, communityWrapper, link } from './style.css';
export const AboutAffine = () => {
const t = useAFFiNEI18N();
const [appSettings, setAppSettings] = useAppSetting();
const changeSwitch = useCallback(
(key: keyof AppSetting, checked: boolean) => {
setAppSettings({ [key]: checked });
},
[setAppSettings]
);
const { appSettings, updateSettings } = useAppSettingHelper();
return (
<>
<SettingHeader
@@ -47,7 +40,7 @@ export const AboutAffine = () => {
>
<Switch
checked={appSettings.autoCheckUpdate}
onChange={checked => changeSwitch('autoCheckUpdate', checked)}
onChange={checked => updateSettings('autoCheckUpdate', checked)}
/>
</SettingRow>
<SettingRow
@@ -58,7 +51,7 @@ export const AboutAffine = () => {
>
<Switch
checked={appSettings.autoCheckUpdate}
onChange={checked => changeSwitch('autoCheckUpdate', checked)}
onChange={checked => updateSettings('autoCheckUpdate', checked)}
/>
</SettingRow>
<SettingRow

View File

@@ -5,8 +5,8 @@ import { useCallback } from 'react';
import {
dateFormatOptions,
type DateFormats,
useAppSetting,
} from '../../../../../atoms/settings';
import { useAppSettingHelper } from '../../../../../hooks/affine/use-app-setting-helper';
interface DateFormatMenuContentProps {
currentOption: DateFormats;
@@ -35,12 +35,12 @@ const DateFormatMenuContent = ({
};
export const DateFormatSetting = () => {
const [appearanceSettings, setAppSettings] = useAppSetting();
const { appSettings, updateSettings } = useAppSettingHelper();
const handleSelect = useCallback(
(option: DateFormats) => {
setAppSettings({ dateFormat: option });
updateSettings('dateFormat', option);
},
[setAppSettings]
[updateSettings]
);
return (
@@ -48,12 +48,12 @@ export const DateFormatSetting = () => {
items={
<DateFormatMenuContent
onSelect={handleSelect}
currentOption={appearanceSettings.dateFormat}
currentOption={appSettings.dateFormat}
/>
}
>
<MenuTrigger data-testid="date-format-menu-trigger" block>
{dayjs(new Date()).format(appearanceSettings.dateFormat)}
{dayjs(new Date()).format(appSettings.dateFormat)}
</MenuTrigger>
</Menu>
);

View File

@@ -9,9 +9,9 @@ import { useCallback } from 'react';
import {
type AppSetting,
fontStyleOptions,
useAppSetting,
windowFrameStyleOptions,
} from '../../../../../atoms/settings';
import { useAppSettingHelper } from '../../../../../hooks/affine/use-app-setting-helper';
import { LanguageMenu } from '../../../language-menu';
import { DateFormatSetting } from './date-format-setting';
import { settingWrapper } from './style.css';
@@ -47,7 +47,7 @@ export const ThemeSettings = () => {
const FontFamilySettings = () => {
const t = useAFFiNEI18N();
const [appSettings, setAppSettings] = useAppSetting();
const { appSettings, updateSettings } = useAppSettingHelper();
return (
<RadioButtonGroup
width={250}
@@ -55,9 +55,9 @@ const FontFamilySettings = () => {
value={appSettings.fontStyle}
onValueChange={useCallback(
(key: AppSetting['fontStyle']) => {
setAppSettings({ fontStyle: key });
updateSettings('fontStyle', key);
},
[setAppSettings]
[updateSettings]
)}
>
{fontStyleOptions.map(({ key, value }) => {
@@ -95,14 +95,8 @@ const FontFamilySettings = () => {
export const AppearanceSettings = () => {
const t = useAFFiNEI18N();
const [appSettings, setAppSettings] = useAppSetting();
const { appSettings, updateSettings } = useAppSettingHelper();
const changeSwitch = useCallback(
(key: keyof AppSetting, checked: boolean) => {
setAppSettings({ [key]: checked });
},
[setAppSettings]
);
return (
<>
<SettingHeader
@@ -139,7 +133,7 @@ export const AppearanceSettings = () => {
>
<Switch
checked={appSettings.clientBorder}
onChange={checked => changeSwitch('clientBorder', checked)}
onChange={checked => updateSettings('clientBorder', checked)}
/>
</SettingRow>
) : null}
@@ -151,7 +145,7 @@ export const AppearanceSettings = () => {
<Switch
data-testid="full-width-layout-trigger"
checked={appSettings.fullWidthLayout}
onChange={checked => changeSwitch('fullWidthLayout', checked)}
onChange={checked => updateSettings('fullWidthLayout', checked)}
/>
</SettingRow>
{runtimeConfig.enableNewSettingUnstableApi && environment.isDesktop ? (
@@ -164,7 +158,7 @@ export const AppearanceSettings = () => {
width={250}
defaultValue={appSettings.windowFrameStyle}
onValueChange={(value: AppSetting['windowFrameStyle']) => {
setAppSettings({ windowFrameStyle: value });
updateSettings('windowFrameStyle', value);
}}
>
{windowFrameStyleOptions.map(option => {
@@ -194,7 +188,7 @@ export const AppearanceSettings = () => {
>
<Switch
checked={appSettings.startWeekOnMonday}
onChange={checked => changeSwitch('startWeekOnMonday', checked)}
onChange={checked => updateSettings('startWeekOnMonday', checked)}
/>
</SettingRow>
</SettingWrapper>
@@ -213,7 +207,7 @@ export const AppearanceSettings = () => {
<Switch
checked={appSettings.enableNoisyBackground}
onChange={checked =>
changeSwitch('enableNoisyBackground', checked)
updateSettings('enableNoisyBackground', checked)
}
/>
</SettingRow>
@@ -227,7 +221,7 @@ export const AppearanceSettings = () => {
<Switch
checked={appSettings.enableBlurBackground}
onChange={checked =>
changeSwitch('enableBlurBackground', checked)
updateSettings('enableBlurBackground', checked)
}
/>
</SettingRow>

View File

@@ -29,7 +29,8 @@ import {
import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels';
import { pageSettingFamily } from '../atoms';
import { fontStyleOptions, useAppSetting } from '../atoms/settings';
import { fontStyleOptions } from '../atoms/settings';
import { useAppSettingHelper } from '../hooks/affine/use-app-setting-helper';
import { BlockSuiteEditor as Editor } from './blocksuite/block-suite-editor';
import { Bookmark } from './bookmark';
import * as styles from './page-detail-editor.css';
@@ -68,7 +69,7 @@ const EditorWrapper = memo(function EditorWrapper({
const currentMode = pageSetting?.mode ?? 'page';
const setBlockHub = useSetAtom(rootBlockHubAtom);
const [appSettings] = useAppSetting();
const { appSettings } = useAppSettingHelper();
assertExists(meta);
const value = useMemo(() => {

View File

@@ -1,7 +1,6 @@
import { commandScore } from '@affine/cmdk';
import { useCollectionManager } from '@affine/component/page-list';
import type { Collection } from '@affine/env/filter';
import { Trans } from '@affine/i18n';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { EdgelessIcon, PageIcon, ViewLayersIcon } from '@blocksuite/icons';
import type { Page, PageMeta } from '@blocksuite/store';
@@ -150,19 +149,28 @@ export const pageToCommand = (
page: PageMeta,
store: ReturnType<typeof getCurrentStore>,
navigationHelper: ReturnType<typeof useNavigateHelper>,
t: ReturnType<typeof useAFFiNEI18N>
t: ReturnType<typeof useAFFiNEI18N>,
label?: {
title: string;
subTitle?: string;
}
): CMDKCommand => {
const pageMode = store.get(pageSettingsAtom)?.[page.id]?.mode;
const currentWorkspaceId = store.get(currentWorkspaceIdAtom);
const label = page.title || t['Untitled']();
const title = page.title || t['Untitled']();
const commandLabel = label || {
title: title,
};
return {
id: page.id,
label: label,
label: commandLabel,
// hack: when comparing, the part between >>> and <<< will be ignored
// adding this patch so that CMDK will not complain about duplicated commands
value:
label + valueWrapperStart + page.id + '.' + category + valueWrapperEnd,
originalValue: label,
title + valueWrapperStart + page.id + '.' + category + valueWrapperEnd,
originalValue: title,
category: category,
run: () => {
if (!currentWorkspaceId) {
@@ -179,8 +187,6 @@ export const pageToCommand = (
const contentMatchedMagicString = '__$$content_matched$$__';
export const usePageCommands = () => {
// todo: considering collections for searching pages
// const { savedCollections } = useCollectionManager(currentCollectionsAtom);
const recentPages = useRecentPages();
const pages = useWorkspacePages();
const store = getCurrentStore();
@@ -203,11 +209,11 @@ export const usePageCommands = () => {
workspace.blockSuiteWorkspace.search({ query }).values()
) as unknown as { space: string; content: string }[];
const pageIds = searchResults.map(id => {
if (id.space.startsWith('space:')) {
return id.space.slice(6);
const pageIds = searchResults.map(result => {
if (result.space.startsWith('space:')) {
return result.space.slice(6);
} else {
return id.space;
return result.space;
}
});
@@ -215,12 +221,21 @@ export const usePageCommands = () => {
const pageMode = store.get(pageSettingsAtom)?.[page.id]?.mode;
const category =
pageMode === 'edgeless' ? 'affine:edgeless' : 'affine:pages';
const label = {
title: page.title || t['Untitled'](), // Used to ensure that a title exists
subTitle:
searchResults.find(result => result.space === page.id)?.content ||
'',
};
const command = pageToCommand(
category,
page,
store,
navigationHelper,
t
t,
label
);
if (pageIds.includes(page.id)) {
@@ -235,14 +250,7 @@ export const usePageCommands = () => {
if (results.every(command => command.originalValue !== query)) {
results.push({
id: 'affine:pages:create-page',
label: (
<Trans
i18nKey="com.affine.cmdk.affine.create-new-page-as"
values={{ query }}
>
Create New Page as: <strong>query</strong>
</Trans>
),
label: `${t['com.affine.cmdk.affine.create-new-page-as']()} ${query}`,
value: 'affine::create-page' + query, // hack to make the page always showing in the search result
category: 'affine:creation',
run: async () => {
@@ -255,14 +263,9 @@ export const usePageCommands = () => {
results.push({
id: 'affine:pages:create-edgeless',
label: (
<Trans
values={{ query }}
i18nKey="com.affine.cmdk.affine.create-new-edgeless-as"
>
Create New Edgeless as: <strong>query</strong>
</Trans>
),
label: `${t[
'com.affine.cmdk.affine.create-new-edgeless-as'
]()} ${query}`,
value: 'affine::create-edgeless' + query, // hack to make the page always showing in the search result
category: 'affine:creation',
run: async () => {

View File

@@ -0,0 +1,33 @@
import { style } from '@vanilla-extract/css';
export const highlightContainer = style({
display: 'flex',
flexWrap: 'nowrap',
});
export const highlightText = style({
whiteSpace: 'pre',
overflow: 'hidden',
textOverflow: 'ellipsis',
});
export const highlightKeyword = style({
color: 'var(--affine-primary-color)',
whiteSpace: 'pre',
overflow: 'visible',
flexShrink: 0,
});
export const labelTitle = style({
fontSize: 'var(--affine-font-base)',
lineHeight: '24px',
fontWeight: 400,
textAlign: 'justify',
});
export const labelContent = style({
fontSize: 'var(--affine-font-xs)',
lineHeight: '20px',
fontWeight: 400,
textAlign: 'justify',
});

View File

@@ -0,0 +1,78 @@
import { escapeRegExp } from 'lodash-es';
import { memo } from 'react';
import {
highlightContainer,
highlightKeyword,
highlightText,
labelContent,
labelTitle,
} from './highlight.css';
type SearchResultLabel = {
title: string;
subTitle?: string;
};
type HighlightProps = {
text: string;
highlight: string;
};
type HighlightLabelProps = {
label: SearchResultLabel;
highlight: string;
};
export const Highlight = memo(function Highlight({
text = '',
highlight = '',
}: HighlightProps) {
//Regex is used to ignore case
const regex = highlight.trim()
? new RegExp(`(${escapeRegExp(highlight)})`, 'ig')
: null;
if (!regex) {
return <span>{text}</span>;
}
const parts = text.split(regex);
return (
<div className={highlightContainer}>
{parts.map((part, i) => {
if (regex.test(part)) {
return (
<span key={i} className={highlightKeyword}>
{part}
</span>
);
} else {
return (
<span key={i} className={highlightText}>
{part}
</span>
);
}
})}
</div>
);
});
export const HighlightLabel = memo(function HighlightLabel({
label,
highlight,
}: HighlightLabelProps) {
return (
<div>
<div className={labelTitle}>
<Highlight text={label.title} highlight={highlight} />
</div>
{label.subTitle ? (
<div className={labelContent}>
<Highlight text={label.subTitle} highlight={highlight} />
</div>
) : null}
</div>
);
});

View File

@@ -76,6 +76,8 @@ export const timestamp = style({
display: 'flex',
fontSize: 'var(--affine-font-xs)',
color: 'var(--affine-text-secondary-color)',
minWidth: 120,
flexDirection: 'row-reverse',
});
export const keybinding = style({
@@ -153,8 +155,8 @@ globalStyle(`${root} [cmdk-list]:hover::-webkit-scrollbar-thumb:hover`, {
globalStyle(`${root} [cmdk-item]`, {
display: 'flex',
height: 44,
padding: '0 12px',
minHeight: 44,
padding: '6px 12px',
alignItems: 'center',
cursor: 'default',
borderRadius: 4,

View File

@@ -4,7 +4,7 @@ import { useAFFiNEI18N } from '@affine/i18n/hooks';
import type { PageMeta } from '@blocksuite/store';
import type { CommandCategory } from '@toeverything/infra/command';
import clsx from 'clsx';
import { useAtom, useSetAtom } from 'jotai';
import { useAtom } from 'jotai';
import { Suspense, useLayoutEffect, useMemo, useState } from 'react';
import {
@@ -13,8 +13,10 @@ import {
customCommandFilter,
useCMDKCommandGroups,
} from './data';
import { HighlightLabel } from './highlight';
import * as styles from './main.css';
import { CMDKModal, type CMDKModalProps } from './modal';
import { NotFoundGroup } from './not-found';
import type { CMDKCommand } from './types';
type NoParametersKeys<T> = {
@@ -52,10 +54,16 @@ const QuickSearchGroup = ({
}) => {
const t = useAFFiNEI18N();
const i18nkey = categoryToI18nKey[category];
const setQuery = useSetAtom(cmdkQueryAtom);
const [query, setQuery] = useAtom(cmdkQueryAtom);
return (
<Command.Group key={category} heading={t[i18nkey]()}>
{commands.map(command => {
const label =
typeof command.label === 'string'
? {
title: command.label,
}
: command.label;
return (
<Command.Item
key={command.id}
@@ -78,7 +86,7 @@ const QuickSearchGroup = ({
command.originalValue ? command.originalValue : undefined
}
>
{command.label}
<HighlightLabel highlight={query} label={label} />
</div>
{command.timestamp ? (
<div className={styles.timestamp}>
@@ -197,6 +205,7 @@ export const CMDKContainer = ({
<Command.List data-opening={opening ? true : undefined}>
{children}
</Command.List>
<NotFoundGroup />
</Command>
);
};

View File

@@ -0,0 +1,40 @@
import { style } from '@vanilla-extract/css';
export const notFoundContainer = style({
display: 'flex',
flexDirection: 'column',
padding: '0 8px',
marginBottom: 8,
});
export const notFoundItem = style({
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-start',
padding: '0 12px',
gap: 16,
});
export const notFoundIcon = style({
display: 'flex',
alignItems: 'center',
fontSize: 20,
color: 'var(--affine-icon-secondary)',
padding: '12px 0',
});
export const notFoundTitle = style({
fontSize: 'var(--affine-font-xs)',
color: 'var(--affine-text-secondary-color)',
fontWeight: '600',
lineHeight: '20px',
textAlign: 'justify',
padding: '8px',
});
export const notFoundText = style({
fontSize: 'var(--affine-font-sm)',
color: 'var(--affine-text-primary-color)',
lineHeight: '22px',
fontWeight: '400',
});

View File

@@ -0,0 +1,31 @@
import { useCommandState } from '@affine/cmdk';
import { SearchIcon } from '@blocksuite/icons';
import { useAtomValue } from 'jotai';
import { cmdkQueryAtom } from './data';
import * as styles from './not-found.css';
export const NotFoundGroup = () => {
const query = useAtomValue(cmdkQueryAtom);
// hack: we know that the filtered count is 2 when there is no result (create page & edgeless)
const renderNoResult =
useCommandState(state => state.filtered.count === 2) || false;
if (!renderNoResult) {
return null;
}
return (
<div className={styles.notFoundContainer}>
<div
className={styles.notFoundTitle}
data-testid="cmdk-search-not-found"
>{`Search for "${query}"`}</div>
<div className={styles.notFoundItem}>
<div className={styles.notFoundIcon}>
<SearchIcon />
</div>
<div className={styles.notFoundText}>No results found</div>
</div>
</div>
);
};

View File

@@ -11,7 +11,12 @@ export interface CommandContext {
// we can use a single render function to render all different commands
export interface CMDKCommand {
id: string;
label: string | React.ReactNode;
label:
| string
| {
title: string;
subTitle?: string;
};
icon?: React.ReactNode;
category: CommandCategory;
keyBinding?: string | { binding: string };

View File

@@ -10,7 +10,7 @@ import { currentPageIdAtom } from '@toeverything/infra/atom';
import { useAtomValue } from 'jotai';
import { useCallback, useEffect, useRef, useState } from 'react';
import { useAppSetting } from '../../../atoms/settings';
import { useAppSettingHelper } from '../../../hooks/affine/use-app-setting-helper';
import { useBlockSuiteMetaHelper } from '../../../hooks/affine/use-block-suite-meta-helper';
import { useCurrentWorkspace } from '../../../hooks/current/use-current-workspace';
import { useNavigateHelper } from '../../../hooks/use-navigate-helper';
@@ -29,7 +29,7 @@ export const TrashButtonGroup = () => {
);
assertExists(pageMeta);
const t = useAFFiNEI18N();
const [appSettings] = useAppSetting();
const { appSettings } = useAppSettingHelper();
const { jumpToSubPath } = useNavigateHelper();
const { restoreFromTrash } = useBlockSuiteMetaHelper(blockSuiteWorkspace);
const restoreRef = useRef(null);

View File

@@ -26,7 +26,7 @@ import { forwardRef, useCallback, useEffect, useMemo } from 'react';
import { openWorkspaceListModalAtom } from '../../atoms';
import { useHistoryAtom } from '../../atoms/history';
import { useAppSetting } from '../../atoms/settings';
import { useAppSettingHelper } from '../../hooks/affine/use-app-setting-helper';
import { useGeneralShortcuts } from '../../hooks/affine/use-shortcuts';
import { useTrashModalHelper } from '../../hooks/affine/use-trash-modal-helper';
import { useRegisterBlocksuiteEditorCommands } from '../../hooks/use-shortcut-commands';
@@ -100,7 +100,7 @@ export const RootAppSidebar = ({
onOpenSettingModal,
}: RootAppSidebarProps): ReactElement => {
const currentWorkspaceId = currentWorkspace.id;
const [appSettings] = useAppSetting();
const { appSettings } = useAppSettingHelper();
const { backToAll } = useCollectionManager(currentCollectionsAtom);
const blockSuiteWorkspace = currentWorkspace.blockSuiteWorkspace;
const t = useAFFiNEI18N();

View File

@@ -0,0 +1,23 @@
import { useAtom } from 'jotai';
import { useCallback, useMemo } from 'react';
import { type AppSetting, appSettingAtom } from '../../atoms/settings';
export function useAppSettingHelper() {
const [appSettings, setAppSettings] = useAtom(appSettingAtom);
const updateSettings = useCallback(
<K extends keyof AppSetting>(key: K, value: AppSetting[K]) => {
setAppSettings(prevSettings => ({ ...prevSettings, [key]: value }));
},
[setAppSettings]
);
return useMemo(
() => ({
appSettings,
updateSettings,
}),
[appSettings, updateSettings]
);
}

View File

@@ -16,7 +16,7 @@ export function useLanguageHelper() {
})),
[]
);
const onSelect = useCallback(
const onLanguageChange = useCallback(
(event: string) => {
i18n.changeLanguage(event);
},
@@ -27,8 +27,8 @@ export function useLanguageHelper() {
() => ({
currentLanguage,
languagesList,
onSelect,
onLanguageChange,
}),
[currentLanguage, languagesList, onSelect]
[currentLanguage, languagesList, onLanguageChange]
);
}

View File

@@ -0,0 +1,19 @@
import { appSidebarOpenAtom } from '@affine/component/app-sidebar';
import { useAtom } from 'jotai';
import { useCallback, useMemo } from 'react';
export function useSwitchSidebarStatus() {
const [isOpened, setOpened] = useAtom(appSidebarOpenAtom);
const onOpenChange = useCallback(() => {
setOpened(open => !open);
}, [setOpened]);
return useMemo(
() => ({
onOpenChange,
isOpened,
}),
[isOpened, onOpenChange]
);
}

View File

@@ -52,7 +52,7 @@ export function useRegisterWorkspaceCommands() {
languageHelper,
})
);
unsubs.push(registerAffineLayoutCommands({ store, t }));
unsubs.push(registerAffineLayoutCommands({ t, store }));
unsubs.push(
registerAffineCreationCommands({
store,

View File

@@ -38,7 +38,6 @@ import { Map as YMap } from 'yjs';
import { openQuickSearchModalAtom, openSettingModalAtom } from '../atoms';
import { mainContainerAtom } from '../atoms/element';
import { useAppSetting } from '../atoms/settings';
import { AdapterProviderWrapper } from '../components/adapter-worksapce-wrapper';
import { AppContainer } from '../components/affine/app-container';
import { usePageHelper } from '../components/blocksuite/block-suite-page-list/utils';
@@ -50,6 +49,7 @@ import {
DROPPABLE_SIDEBAR_TRASH,
RootAppSidebar,
} from '../components/root-app-sidebar';
import { useAppSettingHelper } from '../hooks/affine/use-app-setting-helper';
import { useBlockSuiteMetaHelper } from '../hooks/affine/use-block-suite-meta-helper';
import { useCurrentWorkspace } from '../hooks/current/use-current-workspace';
import { useNavigateHelper } from '../hooks/use-navigate-helper';
@@ -230,7 +230,7 @@ export const WorkspaceLayoutInner = ({
[moveToTrash, t]
);
const [appSetting] = useAppSetting();
const { appSettings } = useAppSettingHelper();
const location = useLocation();
const { pageId } = useParams();
const pageMeta = useBlockSuitePageMeta(
@@ -269,7 +269,7 @@ export const WorkspaceLayoutInner = ({
<Suspense fallback={<MainContainer ref={setMainContainer} />}>
<MainContainer
ref={setMainContainer}
padding={appSetting.clientBorder}
padding={appSettings.clientBorder}
inTrashPage={inTrashPage}
>
{incompatible ? <MigrationFallback /> : children}