feat: add commands (#4477)

This commit is contained in:
JimmFly 2023-09-25 18:10:53 +08:00 committed by GitHub
parent dc6b66c32f
commit 5b4ce75e13
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 181 additions and 14 deletions

View File

@ -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);

View File

@ -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());

View File

@ -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');
},
})
);

View File

@ -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) => {

View File

@ -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',

View File

@ -17,7 +17,7 @@ export const WorkspaceModeFilterTab = () => {
return (
<RadioButtonGroup
width={300}
defaultValue={value}
value={value}
onValueChange={handleValueChange}
>
<RadioButton value="all" style={{ textTransform: 'capitalize' }}>

View File

@ -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

View File

@ -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,
]);
}

View File

@ -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"
}

View File

@ -17,6 +17,8 @@ export type CommandCategory =
| 'editor:edgeless'
| 'affine:recent'
| 'affine:pages'
| 'affine:edgeless'
| 'affine:collections'
| 'affine:navigation'
| 'affine:creation'
| 'affine:settings'