mirror of
https://github.com/toeverything/AFFiNE.git
synced 2024-11-26 02:51:57 +03:00
feat: add commands (#4477)
This commit is contained in:
parent
dc6b66c32f
commit
5b4ce75e13
@ -100,3 +100,5 @@ export const setPageModeAtom = atom(
|
||||
|
||||
export type PageModeOption = 'all' | 'page' | 'edgeless';
|
||||
export const allPageModeSelectAtom = atom<PageModeOption>('all');
|
||||
|
||||
export const openWorkspaceListModalAtom = atom(false);
|
||||
|
@ -1,5 +1,5 @@
|
||||
import type { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { PlusIcon } from '@blocksuite/icons';
|
||||
import { ImportIcon, PlusIcon } from '@blocksuite/icons';
|
||||
import { registerAffineCommand } from '@toeverything/infra/command';
|
||||
import type { createStore } from 'jotai';
|
||||
|
||||
@ -57,6 +57,20 @@ export function registerAffineCreationCommands({
|
||||
},
|
||||
})
|
||||
);
|
||||
unsubs.push(
|
||||
registerAffineCommand({
|
||||
id: 'affine:import-workspace',
|
||||
category: 'affine:creation',
|
||||
icon: <ImportIcon />,
|
||||
label: t['com.affine.cmdk.affine.import-workspace'],
|
||||
preconditionStrategy: () => {
|
||||
return environment.isDesktop;
|
||||
},
|
||||
run() {
|
||||
store.set(openCreateWorkspaceModalAtom, 'add');
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
return () => {
|
||||
unsubs.forEach(unsub => unsub());
|
||||
|
@ -4,7 +4,11 @@ import type { Workspace } from '@blocksuite/store';
|
||||
import { registerAffineCommand } from '@toeverything/infra/command';
|
||||
import type { createStore } from 'jotai';
|
||||
|
||||
import { openSettingModalAtom } from '../atoms';
|
||||
import {
|
||||
openSettingModalAtom,
|
||||
openWorkspaceListModalAtom,
|
||||
type PageModeOption,
|
||||
} from '../atoms';
|
||||
import type { useNavigateHelper } from '../hooks/use-navigate-helper';
|
||||
import { WorkspaceSubPath } from '../shared';
|
||||
|
||||
@ -13,10 +17,14 @@ export function registerAffineNavigationCommands({
|
||||
store,
|
||||
workspace,
|
||||
navigationHelper,
|
||||
pageMode,
|
||||
setPageMode,
|
||||
}: {
|
||||
t: ReturnType<typeof useAFFiNEI18N>;
|
||||
store: ReturnType<typeof createStore>;
|
||||
navigationHelper: ReturnType<typeof useNavigateHelper>;
|
||||
pageMode: PageModeOption;
|
||||
setPageMode: React.Dispatch<React.SetStateAction<PageModeOption>>;
|
||||
workspace: Workspace;
|
||||
}) {
|
||||
const unsubs: Array<() => void> = [];
|
||||
@ -28,6 +36,51 @@ export function registerAffineNavigationCommands({
|
||||
label: () => t['com.affine.cmdk.affine.navigation.goto-all-pages'](),
|
||||
run() {
|
||||
navigationHelper.jumpToSubPath(workspace.id, WorkspaceSubPath.ALL);
|
||||
setPageMode('all');
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
unsubs.push(
|
||||
registerAffineCommand({
|
||||
id: 'affine:goto-page-list',
|
||||
category: 'affine:navigation',
|
||||
icon: <ArrowRightBigIcon />,
|
||||
preconditionStrategy: () => {
|
||||
return pageMode !== 'page';
|
||||
},
|
||||
label: () => t['com.affine.cmdk.affine.navigation.goto-page-list'](),
|
||||
run() {
|
||||
navigationHelper.jumpToSubPath(workspace.id, WorkspaceSubPath.ALL);
|
||||
setPageMode('page');
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
unsubs.push(
|
||||
registerAffineCommand({
|
||||
id: 'affine:goto-edgeless-list',
|
||||
category: 'affine:navigation',
|
||||
icon: <ArrowRightBigIcon />,
|
||||
preconditionStrategy: () => {
|
||||
return pageMode !== 'edgeless';
|
||||
},
|
||||
label: () => t['com.affine.cmdk.affine.navigation.goto-edgeless-list'](),
|
||||
run() {
|
||||
navigationHelper.jumpToSubPath(workspace.id, WorkspaceSubPath.ALL);
|
||||
setPageMode('edgeless');
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
unsubs.push(
|
||||
registerAffineCommand({
|
||||
id: 'affine:goto-workspace',
|
||||
category: 'affine:navigation',
|
||||
icon: <ArrowRightBigIcon />,
|
||||
label: () => t['com.affine.cmdk.affine.navigation.goto-workspace'](),
|
||||
run() {
|
||||
store.set(openWorkspaceListModalAtom, true);
|
||||
},
|
||||
})
|
||||
);
|
||||
@ -56,6 +109,7 @@ export function registerAffineNavigationCommands({
|
||||
label: () => t['com.affine.cmdk.affine.navigation.goto-trash'](),
|
||||
run() {
|
||||
navigationHelper.jumpToSubPath(workspace.id, WorkspaceSubPath.TRASH);
|
||||
setPageMode('all');
|
||||
},
|
||||
})
|
||||
);
|
||||
|
@ -1,7 +1,9 @@
|
||||
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 } from '@blocksuite/icons';
|
||||
import { EdgelessIcon, PageIcon, ViewLayersIcon } from '@blocksuite/icons';
|
||||
import type { Page, PageMeta } from '@blocksuite/store';
|
||||
import {
|
||||
useBlockSuitePageMeta,
|
||||
@ -33,6 +35,8 @@ import {
|
||||
} from '../../../atoms';
|
||||
import { useCurrentWorkspace } from '../../../hooks/current/use-current-workspace';
|
||||
import { useNavigateHelper } from '../../../hooks/use-navigate-helper';
|
||||
import { WorkspaceSubPath } from '../../../shared';
|
||||
import { currentCollectionsAtom } from '../../../utils/user-setting';
|
||||
import { usePageHelper } from '../../blocksuite/block-suite-page-list/utils';
|
||||
import type { CMDKCommand, CommandContext } from './types';
|
||||
|
||||
@ -203,8 +207,11 @@ export const usePageCommands = () => {
|
||||
});
|
||||
|
||||
results = pages.map(page => {
|
||||
const pageMode = store.get(pageSettingsAtom)?.[page.id]?.mode;
|
||||
const category =
|
||||
pageMode === 'edgeless' ? 'affine:edgeless' : 'affine:pages';
|
||||
const command = pageToCommand(
|
||||
'affine:pages',
|
||||
category,
|
||||
page,
|
||||
store,
|
||||
navigationHelper,
|
||||
@ -280,15 +287,86 @@ export const usePageCommands = () => {
|
||||
]);
|
||||
};
|
||||
|
||||
export const collectionToCommand = (
|
||||
collection: Collection,
|
||||
store: ReturnType<typeof getCurrentStore>,
|
||||
navigationHelper: ReturnType<typeof useNavigateHelper>,
|
||||
selectCollection: ReturnType<typeof useCollectionManager>['selectCollection'],
|
||||
t: ReturnType<typeof useAFFiNEI18N>
|
||||
): CMDKCommand => {
|
||||
const currentWorkspaceId = store.get(currentWorkspaceIdAtom);
|
||||
const label = collection.name || t['Untitled']();
|
||||
const category = 'affine:collections';
|
||||
return {
|
||||
id: collection.id,
|
||||
label: label,
|
||||
// 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 +
|
||||
collection.id +
|
||||
'.' +
|
||||
category +
|
||||
valueWrapperEnd,
|
||||
originalValue: label,
|
||||
category: category,
|
||||
run: () => {
|
||||
if (!currentWorkspaceId) {
|
||||
console.error('current workspace not found');
|
||||
return;
|
||||
}
|
||||
navigationHelper.jumpToSubPath(currentWorkspaceId, WorkspaceSubPath.ALL);
|
||||
selectCollection(collection.id);
|
||||
},
|
||||
icon: <ViewLayersIcon />,
|
||||
};
|
||||
};
|
||||
|
||||
export const useCollectionsCommands = () => {
|
||||
// todo: considering collections for searching pages
|
||||
const { savedCollections, selectCollection } = useCollectionManager(
|
||||
currentCollectionsAtom
|
||||
);
|
||||
const store = getCurrentStore();
|
||||
const query = useAtomValue(cmdkQueryAtom);
|
||||
const navigationHelper = useNavigateHelper();
|
||||
const t = useAFFiNEI18N();
|
||||
|
||||
return useMemo(() => {
|
||||
let results: CMDKCommand[] = [];
|
||||
if (query.trim() === '') {
|
||||
return results;
|
||||
} else {
|
||||
results = savedCollections.map(collection => {
|
||||
const command = collectionToCommand(
|
||||
collection,
|
||||
store,
|
||||
navigationHelper,
|
||||
selectCollection,
|
||||
t
|
||||
);
|
||||
return command;
|
||||
});
|
||||
return results;
|
||||
}
|
||||
}, [query, savedCollections, store, navigationHelper, selectCollection, t]);
|
||||
};
|
||||
|
||||
export const useCMDKCommandGroups = () => {
|
||||
const pageCommands = usePageCommands();
|
||||
const collectionCommands = useCollectionsCommands();
|
||||
const affineCommands = useAtomValue(filteredAffineCommands);
|
||||
|
||||
return useMemo(() => {
|
||||
const commands = [...pageCommands, ...affineCommands];
|
||||
const commands = [
|
||||
...pageCommands,
|
||||
...collectionCommands,
|
||||
...affineCommands,
|
||||
];
|
||||
const groups = groupBy(commands, command => command.category);
|
||||
return Object.entries(groups) as [CommandCategory, CMDKCommand[]][];
|
||||
}, [affineCommands, pageCommands]);
|
||||
}, [affineCommands, collectionCommands, pageCommands]);
|
||||
};
|
||||
|
||||
export const customCommandFilter = (value: string, search: string) => {
|
||||
|
@ -28,6 +28,8 @@ const categoryToI18nKey: Record<CommandCategory, i18nKey> = {
|
||||
'affine:general': 'com.affine.cmdk.affine.category.affine.general',
|
||||
'affine:layout': 'com.affine.cmdk.affine.category.affine.layout',
|
||||
'affine:pages': 'com.affine.cmdk.affine.category.affine.pages',
|
||||
'affine:edgeless': 'com.affine.cmdk.affine.category.affine.edgeless',
|
||||
'affine:collections': 'com.affine.cmdk.affine.category.affine.collections',
|
||||
'affine:settings': 'com.affine.cmdk.affine.category.affine.settings',
|
||||
'affine:updates': 'com.affine.cmdk.affine.category.affine.updates',
|
||||
'affine:help': 'com.affine.cmdk.affine.category.affine.help',
|
||||
|
@ -17,7 +17,7 @@ export const WorkspaceModeFilterTab = () => {
|
||||
return (
|
||||
<RadioButtonGroup
|
||||
width={300}
|
||||
defaultValue={value}
|
||||
value={value}
|
||||
onValueChange={handleValueChange}
|
||||
>
|
||||
<RadioButton value="all" style={{ textTransform: 'capitalize' }}>
|
||||
|
@ -20,10 +20,11 @@ import {
|
||||
import type { Page } from '@blocksuite/store';
|
||||
import { useDroppable } from '@dnd-kit/core';
|
||||
import { Menu } from '@toeverything/components/menu';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { useAtom, useAtomValue } from 'jotai';
|
||||
import type { HTMLAttributes, ReactElement } from 'react';
|
||||
import { forwardRef, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { forwardRef, useCallback, useEffect, useMemo } from 'react';
|
||||
|
||||
import { openWorkspaceListModalAtom } from '../../atoms';
|
||||
import { useHistoryAtom } from '../../atoms/history';
|
||||
import { useAppSetting } from '../../atoms/settings';
|
||||
import type { AllWorkspace } from '../../shared';
|
||||
@ -100,7 +101,9 @@ export const RootAppSidebar = ({
|
||||
const { backToAll } = useCollectionManager(currentCollectionsAtom);
|
||||
const blockSuiteWorkspace = currentWorkspace.blockSuiteWorkspace;
|
||||
const t = useAFFiNEI18N();
|
||||
const [openUserWorkspaceList, setOpenUserWorkspaceList] = useState(false);
|
||||
const [openUserWorkspaceList, setOpenUserWorkspaceList] = useAtom(
|
||||
openWorkspaceListModalAtom
|
||||
);
|
||||
const onClickNewPage = useCallback(async () => {
|
||||
const page = createPage();
|
||||
await page.waitForLoaded();
|
||||
@ -142,7 +145,7 @@ export const RootAppSidebar = ({
|
||||
});
|
||||
const closeUserWorkspaceList = useCallback(() => {
|
||||
setOpenUserWorkspaceList(false);
|
||||
}, []);
|
||||
}, [setOpenUserWorkspaceList]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -178,7 +181,7 @@ export const RootAppSidebar = ({
|
||||
currentWorkspace={currentWorkspace}
|
||||
onClick={useCallback(() => {
|
||||
setOpenUserWorkspaceList(true);
|
||||
}, [])}
|
||||
}, [setOpenUserWorkspaceList])}
|
||||
/>
|
||||
</Menu>
|
||||
<QuickSearchInput
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { useStore } from 'jotai';
|
||||
import { useAtom, useStore } from 'jotai';
|
||||
import { useTheme } from 'next-themes';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { allPageModeSelectAtom } from '../atoms';
|
||||
import {
|
||||
registerAffineCreationCommands,
|
||||
registerAffineLayoutCommands,
|
||||
@ -20,6 +21,7 @@ export function useRegisterWorkspaceCommands() {
|
||||
const [currentWorkspace] = useCurrentWorkspace();
|
||||
const pageHelper = usePageHelper(currentWorkspace.blockSuiteWorkspace);
|
||||
const navigationHelper = useNavigateHelper();
|
||||
const [pageMode, setPageMode] = useAtom(allPageModeSelectAtom);
|
||||
useEffect(() => {
|
||||
const unsubs: Array<() => void> = [];
|
||||
unsubs.push(
|
||||
@ -28,6 +30,8 @@ export function useRegisterWorkspaceCommands() {
|
||||
t,
|
||||
workspace: currentWorkspace.blockSuiteWorkspace,
|
||||
navigationHelper,
|
||||
pageMode,
|
||||
setPageMode,
|
||||
})
|
||||
);
|
||||
unsubs.push(registerAffineSettingsCommands({ store, t, theme }));
|
||||
@ -50,5 +54,7 @@ export function useRegisterWorkspaceCommands() {
|
||||
theme,
|
||||
currentWorkspace.blockSuiteWorkspace,
|
||||
navigationHelper,
|
||||
pageMode,
|
||||
setPageMode,
|
||||
]);
|
||||
}
|
||||
|
@ -609,5 +609,11 @@
|
||||
"com.affine.cmdk.affine.category.editor.insert-object": "Insert Object",
|
||||
"com.affine.cmdk.affine.category.editor.page": "Page Commands",
|
||||
"com.affine.cmdk.affine.category.editor.edgeless": "Edgeless Commands",
|
||||
"com.affine.cmdk.affine.editor.edgeless.presentation-start": "Start Presentation"
|
||||
"com.affine.cmdk.affine.editor.edgeless.presentation-start": "Start Presentation",
|
||||
"com.affine.cmdk.affine.navigation.goto-page-list": "Go to Page List",
|
||||
"com.affine.cmdk.affine.navigation.goto-edgeless-list": "Go to Edgeless List",
|
||||
"com.affine.cmdk.affine.navigation.goto-workspace": "Go to Workspace",
|
||||
"com.affine.cmdk.affine.category.affine.edgeless": "Edgeless",
|
||||
"com.affine.cmdk.affine.category.affine.collections": "Collections",
|
||||
"com.affine.cmdk.affine.import-workspace": "Import Workspace"
|
||||
}
|
||||
|
@ -17,6 +17,8 @@ export type CommandCategory =
|
||||
| 'editor:edgeless'
|
||||
| 'affine:recent'
|
||||
| 'affine:pages'
|
||||
| 'affine:edgeless'
|
||||
| 'affine:collections'
|
||||
| 'affine:navigation'
|
||||
| 'affine:creation'
|
||||
| 'affine:settings'
|
||||
|
Loading…
Reference in New Issue
Block a user