mirror of
https://github.com/toeverything/AFFiNE.git
synced 2025-01-03 21:12:22 +03:00
feat(core): add setting commands (#4568)
Co-authored-by: Peng Xiao <pengxiao@outlook.com>
This commit is contained in:
parent
b1eb926b7b
commit
1f6a105e5c
@ -63,7 +63,7 @@ const appSettingBaseAtom = atomWithStorage<AppSetting>('affine-settings', {
|
|||||||
|
|
||||||
type SetStateAction<Value> = Value | ((prev: Value) => Value);
|
type SetStateAction<Value> = Value | ((prev: Value) => Value);
|
||||||
|
|
||||||
const appSettingAtom = atom<
|
export const appSettingAtom = atom<
|
||||||
AppSetting,
|
AppSetting,
|
||||||
[SetStateAction<Partial<AppSetting>>],
|
[SetStateAction<Partial<AppSetting>>],
|
||||||
void
|
void
|
||||||
|
58
apps/core/src/commands/affine-help.tsx
Normal file
58
apps/core/src/commands/affine-help.tsx
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import type { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||||
|
import { ContactWithUsIcon, NewIcon, UserGuideIcon } from '@blocksuite/icons';
|
||||||
|
import { registerAffineCommand } from '@toeverything/infra/command';
|
||||||
|
import type { createStore } from 'jotai';
|
||||||
|
|
||||||
|
import { openOnboardingModalAtom, openSettingModalAtom } from '../atoms';
|
||||||
|
|
||||||
|
export function registerAffineHelpCommands({
|
||||||
|
t,
|
||||||
|
store,
|
||||||
|
}: {
|
||||||
|
t: ReturnType<typeof useAFFiNEI18N>;
|
||||||
|
store: ReturnType<typeof createStore>;
|
||||||
|
}) {
|
||||||
|
const unsubs: Array<() => void> = [];
|
||||||
|
unsubs.push(
|
||||||
|
registerAffineCommand({
|
||||||
|
id: 'affine:help-whats-new',
|
||||||
|
category: 'affine:help',
|
||||||
|
icon: <NewIcon />,
|
||||||
|
label: () => t['com.affine.cmdk.affine.whats-new'](),
|
||||||
|
run() {
|
||||||
|
window.open(runtimeConfig.changelogUrl, '_blank');
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
unsubs.push(
|
||||||
|
registerAffineCommand({
|
||||||
|
id: 'affine:help-contact-us',
|
||||||
|
category: 'affine:help',
|
||||||
|
icon: <ContactWithUsIcon />,
|
||||||
|
label: () => t['com.affine.cmdk.affine.contact-us'](),
|
||||||
|
run() {
|
||||||
|
store.set(openSettingModalAtom, {
|
||||||
|
open: true,
|
||||||
|
activeTab: 'about',
|
||||||
|
workspaceId: null,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
unsubs.push(
|
||||||
|
registerAffineCommand({
|
||||||
|
id: 'affine:help-getting-started',
|
||||||
|
category: 'affine:help',
|
||||||
|
icon: <UserGuideIcon />,
|
||||||
|
label: () => t['com.affine.cmdk.affine.getting-started'](),
|
||||||
|
preconditionStrategy: () => environment.isDesktop,
|
||||||
|
run() {
|
||||||
|
store.set(openOnboardingModalAtom, true);
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
unsubs.forEach(unsub => unsub());
|
||||||
|
};
|
||||||
|
}
|
@ -5,20 +5,85 @@ import {
|
|||||||
PreconditionStrategy,
|
PreconditionStrategy,
|
||||||
registerAffineCommand,
|
registerAffineCommand,
|
||||||
} from '@toeverything/infra/command';
|
} from '@toeverything/infra/command';
|
||||||
import type { createStore } from 'jotai';
|
import { type createStore, useAtomValue } from 'jotai';
|
||||||
import type { useTheme } from 'next-themes';
|
import type { useTheme } from 'next-themes';
|
||||||
|
|
||||||
import { openQuickSearchModalAtom } from '../atoms';
|
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({
|
export function registerAffineSettingsCommands({
|
||||||
|
t,
|
||||||
store,
|
store,
|
||||||
theme,
|
theme,
|
||||||
|
languageHelper,
|
||||||
}: {
|
}: {
|
||||||
t: ReturnType<typeof useAFFiNEI18N>;
|
t: ReturnType<typeof useAFFiNEI18N>;
|
||||||
store: ReturnType<typeof createStore>;
|
store: ReturnType<typeof createStore>;
|
||||||
theme: ReturnType<typeof useTheme>;
|
theme: ReturnType<typeof useTheme>;
|
||||||
|
languageHelper: ReturnType<typeof useLanguageHelper>;
|
||||||
}) {
|
}) {
|
||||||
const unsubs: Array<() => void> = [];
|
const unsubs: Array<() => void> = [];
|
||||||
|
const { onSelect, languagesList, currentLanguage } = languageHelper;
|
||||||
unsubs.push(
|
unsubs.push(
|
||||||
registerAffineCommand({
|
registerAffineCommand({
|
||||||
id: 'affine:show-quick-search',
|
id: 'affine:show-quick-search',
|
||||||
@ -29,7 +94,8 @@ export function registerAffineSettingsCommands({
|
|||||||
},
|
},
|
||||||
icon: <SettingsIcon />,
|
icon: <SettingsIcon />,
|
||||||
run() {
|
run() {
|
||||||
store.set(openQuickSearchModalAtom, true);
|
const quickSearchModalState = store.get(openQuickSearchModalAtom);
|
||||||
|
store.set(openQuickSearchModalAtom, !quickSearchModalState);
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -94,6 +160,181 @@ export function registerAffineSettingsCommands({
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
//Font styles
|
||||||
|
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>
|
||||||
|
),
|
||||||
|
category: 'affine:settings',
|
||||||
|
icon: <SettingsIcon />,
|
||||||
|
preconditionStrategy: () =>
|
||||||
|
store.get(appSettingAtom).fontStyle !== 'Sans',
|
||||||
|
run() {
|
||||||
|
store.set(appSettingAtom, prev => ({
|
||||||
|
...prev,
|
||||||
|
fontStyle: 'Sans',
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
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>
|
||||||
|
),
|
||||||
|
category: 'affine:settings',
|
||||||
|
icon: <SettingsIcon />,
|
||||||
|
preconditionStrategy: () =>
|
||||||
|
store.get(appSettingAtom).fontStyle !== 'Serif',
|
||||||
|
run() {
|
||||||
|
store.set(appSettingAtom, prev => ({
|
||||||
|
...prev,
|
||||||
|
fontStyle: 'Serif',
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
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>
|
||||||
|
),
|
||||||
|
category: 'affine:settings',
|
||||||
|
icon: <SettingsIcon />,
|
||||||
|
preconditionStrategy: () =>
|
||||||
|
store.get(appSettingAtom).fontStyle !== 'Mono',
|
||||||
|
run() {
|
||||||
|
store.set(appSettingAtom, prev => ({
|
||||||
|
...prev,
|
||||||
|
fontStyle: 'Mono',
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
//Display Language
|
||||||
|
languagesList.forEach(language => {
|
||||||
|
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>
|
||||||
|
),
|
||||||
|
category: 'affine:settings',
|
||||||
|
icon: <SettingsIcon />,
|
||||||
|
preconditionStrategy: () => currentLanguage?.tag !== language.tag,
|
||||||
|
run() {
|
||||||
|
onSelect(language.tag);
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
//Layout Style
|
||||||
|
unsubs.push(
|
||||||
|
registerAffineCommand({
|
||||||
|
id: `affine:change-client-border-style`,
|
||||||
|
label: <ClientBorderStyleLabel />,
|
||||||
|
category: 'affine:settings',
|
||||||
|
icon: <SettingsIcon />,
|
||||||
|
preconditionStrategy: () => environment.isDesktop,
|
||||||
|
run() {
|
||||||
|
store.set(appSettingAtom, prev => ({
|
||||||
|
...prev,
|
||||||
|
clientBorder: !prev.clientBorder,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
unsubs.push(
|
||||||
|
registerAffineCommand({
|
||||||
|
id: `affine:change-full-width-layout`,
|
||||||
|
label: <FullWidthLayoutLabel />,
|
||||||
|
category: 'affine:settings',
|
||||||
|
icon: <SettingsIcon />,
|
||||||
|
run() {
|
||||||
|
store.set(appSettingAtom, prev => ({
|
||||||
|
...prev,
|
||||||
|
fullWidthLayout: !prev.fullWidthLayout,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
unsubs.push(
|
||||||
|
registerAffineCommand({
|
||||||
|
id: `affine:change-noise-background-on-the-sidebar`,
|
||||||
|
label: <NoisyBackgroundLabel />,
|
||||||
|
category: 'affine:settings',
|
||||||
|
icon: <SettingsIcon />,
|
||||||
|
preconditionStrategy: () => environment.isDesktop,
|
||||||
|
run() {
|
||||||
|
store.set(appSettingAtom, prev => ({
|
||||||
|
...prev,
|
||||||
|
enableNoisyBackground: !prev.enableNoisyBackground,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
unsubs.push(
|
||||||
|
registerAffineCommand({
|
||||||
|
id: `affine:change-translucent-ui-on-the-sidebar`,
|
||||||
|
label: <BlurBackgroundLabel />,
|
||||||
|
category: 'affine:settings',
|
||||||
|
icon: <SettingsIcon />,
|
||||||
|
preconditionStrategy: () => environment.isDesktop,
|
||||||
|
run() {
|
||||||
|
store.set(appSettingAtom, prev => ({
|
||||||
|
...prev,
|
||||||
|
enableBlurBackground: !prev.enableBlurBackground,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
unsubs.forEach(unsub => unsub());
|
unsubs.forEach(unsub => unsub());
|
||||||
};
|
};
|
||||||
|
35
apps/core/src/commands/affine-updates.tsx
Normal file
35
apps/core/src/commands/affine-updates.tsx
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { updateReadyAtom } from '@affine/component/app-sidebar/app-updater-button';
|
||||||
|
import type { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||||
|
import { ResetIcon } from '@blocksuite/icons';
|
||||||
|
import { registerAffineCommand } from '@toeverything/infra/command';
|
||||||
|
import type { createStore } from 'jotai';
|
||||||
|
|
||||||
|
export function registerAffineUpdatesCommands({
|
||||||
|
t,
|
||||||
|
store,
|
||||||
|
}: {
|
||||||
|
t: ReturnType<typeof useAFFiNEI18N>;
|
||||||
|
store: ReturnType<typeof createStore>;
|
||||||
|
}) {
|
||||||
|
const unsubs: Array<() => void> = [];
|
||||||
|
|
||||||
|
unsubs.push(
|
||||||
|
registerAffineCommand({
|
||||||
|
id: 'affine:restart-to-upgrade',
|
||||||
|
category: 'affine:updates',
|
||||||
|
icon: <ResetIcon />,
|
||||||
|
label: () => t['com.affine.cmdk.affine.restart-to-upgrade'](),
|
||||||
|
preconditionStrategy: () => !!store.get(updateReadyAtom),
|
||||||
|
run() {
|
||||||
|
window.apis?.updater.quitAndInstall().catch(err => {
|
||||||
|
// TODO: add error toast here
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
unsubs.forEach(unsub => unsub());
|
||||||
|
};
|
||||||
|
}
|
@ -1,3 +1,6 @@
|
|||||||
export * from './affine-creation';
|
export * from './affine-creation';
|
||||||
|
export * from './affine-help';
|
||||||
export * from './affine-layout';
|
export * from './affine-layout';
|
||||||
|
export * from './affine-navigation';
|
||||||
export * from './affine-settings';
|
export * from './affine-settings';
|
||||||
|
export * from './affine-updates';
|
||||||
|
@ -1,24 +1,18 @@
|
|||||||
import { LOCALES } from '@affine/i18n';
|
|
||||||
import { useI18N } from '@affine/i18n';
|
|
||||||
import { Menu, MenuItem, MenuTrigger } from '@toeverything/components/menu';
|
import { Menu, MenuItem, MenuTrigger } from '@toeverything/components/menu';
|
||||||
import type { ReactElement } from 'react';
|
import { memo, type ReactElement } from 'react';
|
||||||
import { useCallback, useMemo } from 'react';
|
|
||||||
|
import { useLanguageHelper } from '../../../hooks/affine/use-language-helper';
|
||||||
|
|
||||||
// Fixme: keyboard focus should be supported by Menu component
|
// Fixme: keyboard focus should be supported by Menu component
|
||||||
const LanguageMenuContent = ({
|
const LanguageMenuContent = memo(function LanguageMenuContent() {
|
||||||
currentLanguage,
|
const { currentLanguage, languagesList, onSelect } = useLanguageHelper();
|
||||||
onSelect,
|
|
||||||
}: {
|
|
||||||
currentLanguage?: string;
|
|
||||||
onSelect: (value: string) => void;
|
|
||||||
}) => {
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{LOCALES.map(option => {
|
{languagesList.map(option => {
|
||||||
return (
|
return (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
key={option.name}
|
key={option.name}
|
||||||
selected={currentLanguage === option.originalName}
|
selected={currentLanguage?.originalName === option.originalName}
|
||||||
title={option.name}
|
title={option.name}
|
||||||
onSelect={() => onSelect(option.tag)}
|
onSelect={() => onSelect(option.tag)}
|
||||||
>
|
>
|
||||||
@ -28,30 +22,13 @@ const LanguageMenuContent = ({
|
|||||||
})}
|
})}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
export const LanguageMenu = () => {
|
export const LanguageMenu = () => {
|
||||||
const i18n = useI18N();
|
const { currentLanguage } = useLanguageHelper();
|
||||||
const currentLanguage = useMemo(
|
|
||||||
() => LOCALES.find(item => item.tag === i18n.language),
|
|
||||||
[i18n.language]
|
|
||||||
);
|
|
||||||
const onSelect = useCallback(
|
|
||||||
(event: string) => {
|
|
||||||
return i18n.changeLanguage(event);
|
|
||||||
},
|
|
||||||
[i18n]
|
|
||||||
);
|
|
||||||
return (
|
return (
|
||||||
<Menu
|
<Menu
|
||||||
items={
|
items={(<LanguageMenuContent />) as ReactElement}
|
||||||
(
|
|
||||||
<LanguageMenuContent
|
|
||||||
currentLanguage={currentLanguage?.originalName}
|
|
||||||
onSelect={onSelect}
|
|
||||||
/>
|
|
||||||
) as ReactElement
|
|
||||||
}
|
|
||||||
contentOptions={{
|
contentOptions={{
|
||||||
style: {
|
style: {
|
||||||
background: 'var(--affine-white)',
|
background: 'var(--affine-white)',
|
||||||
|
@ -24,7 +24,7 @@ export const ThemeSettings = () => {
|
|||||||
<RadioButtonGroup
|
<RadioButtonGroup
|
||||||
width={250}
|
width={250}
|
||||||
className={settingWrapper}
|
className={settingWrapper}
|
||||||
defaultValue={theme}
|
value={theme}
|
||||||
onValueChange={useCallback(
|
onValueChange={useCallback(
|
||||||
(value: string) => {
|
(value: string) => {
|
||||||
setTheme(value);
|
setTheme(value);
|
||||||
@ -52,7 +52,7 @@ const FontFamilySettings = () => {
|
|||||||
<RadioButtonGroup
|
<RadioButtonGroup
|
||||||
width={250}
|
width={250}
|
||||||
className={settingWrapper}
|
className={settingWrapper}
|
||||||
defaultValue={appSettings.fontStyle}
|
value={appSettings.fontStyle}
|
||||||
onValueChange={useCallback(
|
onValueChange={useCallback(
|
||||||
(key: AppSetting['fontStyle']) => {
|
(key: AppSetting['fontStyle']) => {
|
||||||
setAppSettings({ fontStyle: key });
|
setAppSettings({ fontStyle: key });
|
||||||
|
@ -19,6 +19,32 @@ export const searchInput = style({
|
|||||||
'::placeholder': {
|
'::placeholder': {
|
||||||
color: 'var(--affine-text-secondary-color)',
|
color: 'var(--affine-text-secondary-color)',
|
||||||
},
|
},
|
||||||
|
selectors: {
|
||||||
|
'&.inEditor': {
|
||||||
|
paddingTop: '12px',
|
||||||
|
paddingBottom: '18px',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const pageTitleWrapper = style({
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
padding: '18px 24px 0 24px',
|
||||||
|
width: '100%',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const pageTitle = style({
|
||||||
|
padding: '2px 6px',
|
||||||
|
borderRadius: 4,
|
||||||
|
fontSize: 'var(--affine-font-xs)',
|
||||||
|
lineHeight: '20px',
|
||||||
|
color: 'var(--affine-text-secondary-color)',
|
||||||
|
overflow: 'hidden',
|
||||||
|
textOverflow: 'ellipsis',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
maxWidth: '100%',
|
||||||
|
backgroundColor: 'var(--affine-background-secondary-color)',
|
||||||
});
|
});
|
||||||
|
|
||||||
export const panelContainer = style({
|
export const panelContainer = style({
|
||||||
@ -41,6 +67,9 @@ export const itemLabel = style({
|
|||||||
lineHeight: '1.5',
|
lineHeight: '1.5',
|
||||||
color: 'var(--affine-text-primary-color)',
|
color: 'var(--affine-text-primary-color)',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
|
overflow: 'hidden',
|
||||||
|
textOverflow: 'ellipsis',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
});
|
});
|
||||||
|
|
||||||
export const timestamp = style({
|
export const timestamp = style({
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { Command } from '@affine/cmdk';
|
import { Command } from '@affine/cmdk';
|
||||||
import { formatDate } from '@affine/component/page-list';
|
import { formatDate } from '@affine/component/page-list';
|
||||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||||
|
import type { PageMeta } from '@blocksuite/store';
|
||||||
import type { CommandCategory } from '@toeverything/infra/command';
|
import type { CommandCategory } from '@toeverything/infra/command';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useAtom, useSetAtom } from 'jotai';
|
import { useAtom, useSetAtom } from 'jotai';
|
||||||
@ -124,14 +125,17 @@ export const CMDKContainer = ({
|
|||||||
onQueryChange,
|
onQueryChange,
|
||||||
query,
|
query,
|
||||||
children,
|
children,
|
||||||
|
pageMeta,
|
||||||
...rest
|
...rest
|
||||||
}: React.PropsWithChildren<{
|
}: React.PropsWithChildren<{
|
||||||
className?: string;
|
className?: string;
|
||||||
query: string;
|
query: string;
|
||||||
|
pageMeta?: PageMeta;
|
||||||
onQueryChange: (query: string) => void;
|
onQueryChange: (query: string) => void;
|
||||||
}>) => {
|
}>) => {
|
||||||
const t = useAFFiNEI18N();
|
const t = useAFFiNEI18N();
|
||||||
const [value, setValue] = useAtom(cmdkValueAtom);
|
const [value, setValue] = useAtom(cmdkValueAtom);
|
||||||
|
const isInEditor = pageMeta !== undefined;
|
||||||
return (
|
return (
|
||||||
<Command
|
<Command
|
||||||
{...rest}
|
{...rest}
|
||||||
@ -153,20 +157,32 @@ export const CMDKContainer = ({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* todo: add page context here */}
|
{/* todo: add page context here */}
|
||||||
|
{isInEditor ? (
|
||||||
|
<div className={styles.pageTitleWrapper}>
|
||||||
|
<span className={styles.pageTitle}>
|
||||||
|
{pageMeta.title ? pageMeta.title : t['Untitled']()}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
<Command.Input
|
<Command.Input
|
||||||
placeholder={t['com.affine.cmdk.placeholder']()}
|
placeholder={t['com.affine.cmdk.placeholder']()}
|
||||||
autoFocus
|
autoFocus
|
||||||
{...rest}
|
{...rest}
|
||||||
value={query}
|
value={query}
|
||||||
onValueChange={onQueryChange}
|
onValueChange={onQueryChange}
|
||||||
className={clsx(className, styles.searchInput)}
|
className={clsx(className, styles.searchInput, {
|
||||||
|
inEditor: isInEditor,
|
||||||
|
})}
|
||||||
/>
|
/>
|
||||||
<Command.List>{children}</Command.List>
|
<Command.List>{children}</Command.List>
|
||||||
</Command>
|
</Command>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CMDKQuickSearchModal = (props: CMDKModalProps) => {
|
export const CMDKQuickSearchModal = ({
|
||||||
|
pageMeta,
|
||||||
|
...props
|
||||||
|
}: CMDKModalProps & { pageMeta?: PageMeta }) => {
|
||||||
const [query, setQuery] = useAtom(cmdkQueryAtom);
|
const [query, setQuery] = useAtom(cmdkQueryAtom);
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
if (props.open) {
|
if (props.open) {
|
||||||
@ -179,6 +195,7 @@ export const CMDKQuickSearchModal = (props: CMDKModalProps) => {
|
|||||||
className={styles.root}
|
className={styles.root}
|
||||||
query={query}
|
query={query}
|
||||||
onQueryChange={setQuery}
|
onQueryChange={setQuery}
|
||||||
|
pageMeta={pageMeta}
|
||||||
>
|
>
|
||||||
<Suspense fallback={<Command.Loading />}>
|
<Suspense fallback={<Command.Loading />}>
|
||||||
<QuickSearchCommands onOpenChange={props.onOpenChange} />
|
<QuickSearchCommands onOpenChange={props.onOpenChange} />
|
||||||
|
34
apps/core/src/hooks/affine/use-language-helper.ts
Normal file
34
apps/core/src/hooks/affine/use-language-helper.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { LOCALES, useI18N } from '@affine/i18n';
|
||||||
|
import { useCallback, useMemo } from 'react';
|
||||||
|
|
||||||
|
export function useLanguageHelper() {
|
||||||
|
const i18n = useI18N();
|
||||||
|
const currentLanguage = useMemo(
|
||||||
|
() => LOCALES.find(item => item.tag === i18n.language),
|
||||||
|
[i18n.language]
|
||||||
|
);
|
||||||
|
const languagesList = useMemo(
|
||||||
|
() =>
|
||||||
|
LOCALES.map(item => ({
|
||||||
|
tag: item.tag,
|
||||||
|
originalName: item.originalName,
|
||||||
|
name: item.name,
|
||||||
|
})),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
const onSelect = useCallback(
|
||||||
|
(event: string) => {
|
||||||
|
i18n.changeLanguage(event);
|
||||||
|
},
|
||||||
|
[i18n]
|
||||||
|
);
|
||||||
|
|
||||||
|
return useMemo(
|
||||||
|
() => ({
|
||||||
|
currentLanguage,
|
||||||
|
languagesList,
|
||||||
|
onSelect,
|
||||||
|
}),
|
||||||
|
[currentLanguage, languagesList, onSelect]
|
||||||
|
);
|
||||||
|
}
|
@ -52,17 +52,20 @@ export function useNavigateHelper() {
|
|||||||
},
|
},
|
||||||
[navigate]
|
[navigate]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const isPublicWorkspace = useMemo(() => {
|
||||||
|
return location.pathname.indexOf('/public-workspace') === 0;
|
||||||
|
}, [location.pathname]);
|
||||||
|
|
||||||
const openPage = useCallback(
|
const openPage = useCallback(
|
||||||
(workspaceId: string, pageId: string) => {
|
(workspaceId: string, pageId: string) => {
|
||||||
const isPublicWorkspace =
|
|
||||||
location.pathname.indexOf('/public-workspace') === 0;
|
|
||||||
if (isPublicWorkspace) {
|
if (isPublicWorkspace) {
|
||||||
return jumpToPublicWorkspacePage(workspaceId, pageId);
|
return jumpToPublicWorkspacePage(workspaceId, pageId);
|
||||||
} else {
|
} else {
|
||||||
return jumpToPage(workspaceId, pageId);
|
return jumpToPage(workspaceId, pageId);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[jumpToPage, jumpToPublicWorkspacePage, location.pathname]
|
[jumpToPage, jumpToPublicWorkspacePage, isPublicWorkspace]
|
||||||
);
|
);
|
||||||
|
|
||||||
const jumpToIndex = useCallback(
|
const jumpToIndex = useCallback(
|
||||||
|
@ -6,11 +6,14 @@ import { useEffect } from 'react';
|
|||||||
import { allPageModeSelectAtom } from '../atoms';
|
import { allPageModeSelectAtom } from '../atoms';
|
||||||
import {
|
import {
|
||||||
registerAffineCreationCommands,
|
registerAffineCreationCommands,
|
||||||
|
registerAffineHelpCommands,
|
||||||
registerAffineLayoutCommands,
|
registerAffineLayoutCommands,
|
||||||
|
registerAffineNavigationCommands,
|
||||||
registerAffineSettingsCommands,
|
registerAffineSettingsCommands,
|
||||||
|
registerAffineUpdatesCommands,
|
||||||
} from '../commands';
|
} from '../commands';
|
||||||
import { registerAffineNavigationCommands } from '../commands/affine-navigation';
|
|
||||||
import { usePageHelper } from '../components/blocksuite/block-suite-page-list/utils';
|
import { usePageHelper } from '../components/blocksuite/block-suite-page-list/utils';
|
||||||
|
import { useLanguageHelper } from './affine/use-language-helper';
|
||||||
import { useCurrentWorkspace } from './current/use-current-workspace';
|
import { useCurrentWorkspace } from './current/use-current-workspace';
|
||||||
import { useNavigateHelper } from './use-navigate-helper';
|
import { useNavigateHelper } from './use-navigate-helper';
|
||||||
|
|
||||||
@ -19,11 +22,18 @@ export function useRegisterWorkspaceCommands() {
|
|||||||
const t = useAFFiNEI18N();
|
const t = useAFFiNEI18N();
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const [currentWorkspace] = useCurrentWorkspace();
|
const [currentWorkspace] = useCurrentWorkspace();
|
||||||
|
const languageHelper = useLanguageHelper();
|
||||||
const pageHelper = usePageHelper(currentWorkspace.blockSuiteWorkspace);
|
const pageHelper = usePageHelper(currentWorkspace.blockSuiteWorkspace);
|
||||||
const navigationHelper = useNavigateHelper();
|
const navigationHelper = useNavigateHelper();
|
||||||
const [pageListMode, setPageListMode] = useAtom(allPageModeSelectAtom);
|
const [pageListMode, setPageListMode] = useAtom(allPageModeSelectAtom);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const unsubs: Array<() => void> = [];
|
const unsubs: Array<() => void> = [];
|
||||||
|
unsubs.push(
|
||||||
|
registerAffineUpdatesCommands({
|
||||||
|
store,
|
||||||
|
t,
|
||||||
|
})
|
||||||
|
);
|
||||||
unsubs.push(
|
unsubs.push(
|
||||||
registerAffineNavigationCommands({
|
registerAffineNavigationCommands({
|
||||||
store,
|
store,
|
||||||
@ -34,7 +44,14 @@ export function useRegisterWorkspaceCommands() {
|
|||||||
setPageListMode,
|
setPageListMode,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
unsubs.push(registerAffineSettingsCommands({ store, t, theme }));
|
unsubs.push(
|
||||||
|
registerAffineSettingsCommands({
|
||||||
|
store,
|
||||||
|
t,
|
||||||
|
theme,
|
||||||
|
languageHelper,
|
||||||
|
})
|
||||||
|
);
|
||||||
unsubs.push(registerAffineLayoutCommands({ store, t }));
|
unsubs.push(registerAffineLayoutCommands({ store, t }));
|
||||||
unsubs.push(
|
unsubs.push(
|
||||||
registerAffineCreationCommands({
|
registerAffineCreationCommands({
|
||||||
@ -43,6 +60,12 @@ export function useRegisterWorkspaceCommands() {
|
|||||||
t,
|
t,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
unsubs.push(
|
||||||
|
registerAffineHelpCommands({
|
||||||
|
store,
|
||||||
|
t,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
unsubs.forEach(unsub => unsub());
|
unsubs.forEach(unsub => unsub());
|
||||||
@ -56,5 +79,6 @@ export function useRegisterWorkspaceCommands() {
|
|||||||
navigationHelper,
|
navigationHelper,
|
||||||
pageListMode,
|
pageListMode,
|
||||||
setPageListMode,
|
setPageListMode,
|
||||||
|
languageHelper,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
@ -69,11 +69,16 @@ const CMDKQuickSearchModal = lazy(() =>
|
|||||||
);
|
);
|
||||||
|
|
||||||
export const QuickSearch = () => {
|
export const QuickSearch = () => {
|
||||||
const [currentWorkspace] = useCurrentWorkspace();
|
|
||||||
const [openQuickSearchModal, setOpenQuickSearchModalAtom] = useAtom(
|
const [openQuickSearchModal, setOpenQuickSearchModalAtom] = useAtom(
|
||||||
openQuickSearchModalAtom
|
openQuickSearchModalAtom
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const [currentWorkspace] = useCurrentWorkspace();
|
||||||
|
const { pageId } = useParams();
|
||||||
const blockSuiteWorkspace = currentWorkspace?.blockSuiteWorkspace;
|
const blockSuiteWorkspace = currentWorkspace?.blockSuiteWorkspace;
|
||||||
|
const pageMeta = useBlockSuitePageMeta(
|
||||||
|
currentWorkspace?.blockSuiteWorkspace
|
||||||
|
).find(meta => meta.id === pageId);
|
||||||
|
|
||||||
if (!blockSuiteWorkspace) {
|
if (!blockSuiteWorkspace) {
|
||||||
return null;
|
return null;
|
||||||
@ -83,6 +88,7 @@ export const QuickSearch = () => {
|
|||||||
<CMDKQuickSearchModal
|
<CMDKQuickSearchModal
|
||||||
open={openQuickSearchModal}
|
open={openQuickSearchModal}
|
||||||
onOpenChange={setOpenQuickSearchModalAtom}
|
onOpenChange={setOpenQuickSearchModalAtom}
|
||||||
|
pageMeta={pageMeta}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -42,6 +42,18 @@ function useRegisterCommands() {
|
|||||||
theme: 'auto',
|
theme: 'auto',
|
||||||
themes: ['auto', 'dark', 'light'],
|
themes: ['auto', 'dark', 'light'],
|
||||||
},
|
},
|
||||||
|
languageHelper: {
|
||||||
|
onSelect: () => {},
|
||||||
|
languagesList: [
|
||||||
|
{ tag: 'en', name: 'English', originalName: 'English' },
|
||||||
|
{
|
||||||
|
tag: 'zh-Hans',
|
||||||
|
name: 'Simplified Chinese',
|
||||||
|
originalName: '简体中文',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
currentLanguage: undefined,
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
registerAffineCreationCommands({
|
registerAffineCreationCommands({
|
||||||
t,
|
t,
|
||||||
|
@ -259,3 +259,5 @@ export function AppUpdaterButton({
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export * from './index.jotai';
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
// components/switch.tsx
|
// components/switch.tsx
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { type HTMLAttributes, type ReactNode, useState } from 'react';
|
import {
|
||||||
|
type HTMLAttributes,
|
||||||
|
type ReactNode,
|
||||||
|
useCallback,
|
||||||
|
useState,
|
||||||
|
} from 'react';
|
||||||
|
|
||||||
import * as styles from './index.css';
|
import * as styles from './index.css';
|
||||||
|
|
||||||
@ -10,15 +15,23 @@ type SwitchProps = Omit<HTMLAttributes<HTMLLabelElement>, 'onChange'> & {
|
|||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Switch = (props: SwitchProps) => {
|
export const Switch = ({
|
||||||
const { checked, onChange, children, ...otherProps } = props;
|
checked: checkedProp = false,
|
||||||
const [isChecked, setIsChecked] = useState(checked);
|
onChange: onChangeProp,
|
||||||
|
children,
|
||||||
|
...otherProps
|
||||||
|
}: SwitchProps) => {
|
||||||
|
const [checkedState, setCheckedState] = useState(checkedProp);
|
||||||
|
|
||||||
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
const checked = onChangeProp ? checkedProp : checkedState;
|
||||||
const newChecked = event.target.checked;
|
const onChange = useCallback(
|
||||||
setIsChecked(newChecked);
|
(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
onChange?.(newChecked);
|
const newChecked = event.target.checked;
|
||||||
};
|
if (onChangeProp) onChangeProp(newChecked);
|
||||||
|
else setCheckedState(newChecked);
|
||||||
|
},
|
||||||
|
[onChangeProp]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<label className={clsx(styles.labelStyle)} {...otherProps}>
|
<label className={clsx(styles.labelStyle)} {...otherProps}>
|
||||||
@ -26,13 +39,13 @@ export const Switch = (props: SwitchProps) => {
|
|||||||
<input
|
<input
|
||||||
className={clsx(styles.inputStyle)}
|
className={clsx(styles.inputStyle)}
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
value={isChecked ? 'on' : 'off'}
|
value={checked ? 'on' : 'off'}
|
||||||
checked={isChecked}
|
checked={checked}
|
||||||
onChange={handleChange}
|
onChange={onChange}
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
className={clsx(styles.switchStyle, {
|
className={clsx(styles.switchStyle, {
|
||||||
[styles.switchCheckedStyle]: isChecked,
|
[styles.switchCheckedStyle]: checked,
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
|
@ -618,5 +618,15 @@
|
|||||||
"com.affine.cmdk.affine.import-workspace": "Import Workspace",
|
"com.affine.cmdk.affine.import-workspace": "Import Workspace",
|
||||||
"com.affine.cmdk.affine.editor.add-to-favourites": "Add to Favourites",
|
"com.affine.cmdk.affine.editor.add-to-favourites": "Add to Favourites",
|
||||||
"com.affine.cmdk.affine.editor.remove-from-favourites": "Remove from Favourites",
|
"com.affine.cmdk.affine.editor.remove-from-favourites": "Remove from Favourites",
|
||||||
"com.affine.cmdk.affine.editor.restore-from-trash": "Restore from Trash"
|
"com.affine.cmdk.affine.editor.restore-from-trash": "Restore from Trash",
|
||||||
|
"com.affine.cmdk.affine.font-style.to": "Change Font Style to <1>{{fontFamily}}</1>",
|
||||||
|
"com.affine.cmdk.affine.display-language.to": "Change Display Language to <1>{{language}}</1>",
|
||||||
|
"com.affine.cmdk.affine.client-border-style.to": "Change Client Border Style to <1>{{state}}</1>",
|
||||||
|
"com.affine.cmdk.affine.full-width-layout.to": "Change Full Width Layout to <1>{{state}}</1>",
|
||||||
|
"com.affine.cmdk.affine.noise-background-on-the-sidebar.to": "Change Noise Background On The Sidebar to <1>{{state}}</1>",
|
||||||
|
"com.affine.cmdk.affine.translucent-ui-on-the-sidebar.to": "Change Translucent UI On The Sidebar to <1>{{state}}</1>",
|
||||||
|
"com.affine.cmdk.affine.whats-new": "What's New",
|
||||||
|
"com.affine.cmdk.affine.getting-started": "Getting Started",
|
||||||
|
"com.affine.cmdk.affine.contact-us": "Contact Us",
|
||||||
|
"com.affine.cmdk.affine.restart-to-upgrade": "Restart to Upgrade"
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user