mirror of
https://github.com/toeverything/AFFiNE.git
synced 2024-12-23 00:11:33 +03:00
feat(core): show searched result with results group and add duplicate commands (#5073)
[TOV-65](https://linear.app/affine-design/issue/TOV-65/should-show-searched-result-without-categories) https://github.com/toeverything/AFFiNE/assets/102217452/50fba70b-7efa-4e47-ba8a-de21e400166c
This commit is contained in:
parent
469a18f794
commit
c78eb96507
@ -20,23 +20,15 @@ import {
|
||||
MenuItem,
|
||||
MenuSeparator,
|
||||
} from '@toeverything/components/menu';
|
||||
import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks';
|
||||
import {
|
||||
useBlockSuitePageMeta,
|
||||
usePageMetaHelper,
|
||||
} from '@toeverything/hooks/use-block-suite-page-meta';
|
||||
import { useBlockSuiteWorkspaceHelper } from '@toeverything/hooks/use-block-suite-workspace-helper';
|
||||
import { useAtomValue, useSetAtom } from 'jotai';
|
||||
import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { useCallback, useRef, useState } from 'react';
|
||||
import { applyUpdate, encodeStateAsUpdate } from 'yjs';
|
||||
|
||||
import { setPageModeAtom } from '../../../atoms';
|
||||
import { currentModeAtom } from '../../../atoms/mode';
|
||||
import { useBlockSuiteMetaHelper } from '../../../hooks/affine/use-block-suite-meta-helper';
|
||||
import { useExportPage } from '../../../hooks/affine/use-export-page';
|
||||
import { useTrashModalHelper } from '../../../hooks/affine/use-trash-modal-helper';
|
||||
import { useCurrentWorkspace } from '../../../hooks/current/use-current-workspace';
|
||||
import { useNavigateHelper } from '../../../hooks/use-navigate-helper';
|
||||
import { toast } from '../../../utils';
|
||||
import { PageHistoryModal } from '../../affine/page-history-modal/history-modal';
|
||||
import { HeaderDropDownButton } from '../../pure/header-drop-down-button';
|
||||
@ -50,7 +42,6 @@ type PageMenuProps = {
|
||||
export const PageMenu = ({ rename, pageId }: PageMenuProps) => {
|
||||
const t = useAFFiNEI18N();
|
||||
const ref = useRef(null);
|
||||
const { openPage } = useNavigateHelper();
|
||||
|
||||
// fixme(himself65): remove these hooks ASAP
|
||||
const [workspace] = useCurrentWorkspace();
|
||||
@ -64,11 +55,9 @@ export const PageMenu = ({ rename, pageId }: PageMenuProps) => {
|
||||
const currentMode = useAtomValue(currentModeAtom);
|
||||
const favorite = pageMeta.favorite ?? false;
|
||||
|
||||
const { setPageMeta, setPageTitle } = usePageMetaHelper(blockSuiteWorkspace);
|
||||
const { togglePageMode, toggleFavorite } =
|
||||
const { togglePageMode, toggleFavorite, duplicate } =
|
||||
useBlockSuiteMetaHelper(blockSuiteWorkspace);
|
||||
const { importFile } = usePageHelper(blockSuiteWorkspace);
|
||||
const { createPage } = useBlockSuiteWorkspaceHelper(blockSuiteWorkspace);
|
||||
const { setTrashModal } = useTrashModalHelper(blockSuiteWorkspace);
|
||||
|
||||
const [historyModalOpen, setHistoryModalOpen] = useState(false);
|
||||
@ -107,34 +96,11 @@ export const PageMenu = ({ rename, pageId }: PageMenuProps) => {
|
||||
};
|
||||
|
||||
const exportHandler = useExportPage(currentPage);
|
||||
const setPageMode = useSetAtom(setPageModeAtom);
|
||||
|
||||
const duplicate = useAsyncCallback(async () => {
|
||||
const currentPageMeta = currentPage.meta;
|
||||
const newPage = createPage();
|
||||
await newPage.waitForLoaded();
|
||||
const handleDuplicate = useCallback(() => {
|
||||
duplicate(pageId);
|
||||
}, [duplicate, pageId]);
|
||||
|
||||
const update = encodeStateAsUpdate(currentPage.spaceDoc);
|
||||
applyUpdate(newPage.spaceDoc, update);
|
||||
|
||||
setPageMeta(newPage.id, {
|
||||
tags: currentPageMeta.tags,
|
||||
favorite: currentPageMeta.favorite,
|
||||
});
|
||||
setPageMode(newPage.id, currentMode);
|
||||
setPageTitle(newPage.id, `${currentPageMeta.title}(1)`);
|
||||
openPage(blockSuiteWorkspace.id, newPage.id);
|
||||
}, [
|
||||
blockSuiteWorkspace.id,
|
||||
createPage,
|
||||
currentMode,
|
||||
currentPage.meta,
|
||||
currentPage.spaceDoc,
|
||||
openPage,
|
||||
setPageMeta,
|
||||
setPageMode,
|
||||
setPageTitle,
|
||||
]);
|
||||
const EditMenu = (
|
||||
<>
|
||||
<MenuItem
|
||||
@ -199,7 +165,7 @@ export const PageMenu = ({ rename, pageId }: PageMenuProps) => {
|
||||
</MenuIcon>
|
||||
}
|
||||
data-testid="editor-option-menu-duplicate"
|
||||
onSelect={duplicate}
|
||||
onSelect={handleDuplicate}
|
||||
style={menuItemStyle}
|
||||
>
|
||||
{t['com.affine.header.option.duplicate']()}
|
||||
|
@ -14,11 +14,14 @@ export const usePageHelper = (blockSuiteWorkspace: BlockSuiteWorkspace) => {
|
||||
const { openPage, jumpToSubPath } = useNavigateHelper();
|
||||
const { createPage } = useBlockSuiteWorkspaceHelper(blockSuiteWorkspace);
|
||||
const pageSettings = useAtomValue(pageSettingsAtom);
|
||||
|
||||
const isPreferredEdgeless = useCallback(
|
||||
(pageId: string) => pageSettings[pageId]?.mode === 'edgeless',
|
||||
[pageSettings]
|
||||
);
|
||||
|
||||
const setPageMode = useSetAtom(setPageModeAtom);
|
||||
|
||||
const createPageAndOpen = useCallback(
|
||||
(mode?: 'page' | 'edgeless') => {
|
||||
const page = createPage();
|
||||
@ -31,9 +34,11 @@ export const usePageHelper = (blockSuiteWorkspace: BlockSuiteWorkspace) => {
|
||||
},
|
||||
[blockSuiteWorkspace.id, createPage, openPage, setPageMode]
|
||||
);
|
||||
|
||||
const createEdgelessAndOpen = useCallback(() => {
|
||||
return createPageAndOpen('edgeless');
|
||||
}, [createPageAndOpen]);
|
||||
|
||||
const importFileAndOpen = useAsyncCallback(async () => {
|
||||
const { showImportModal } = await import('@blocksuite/blocks');
|
||||
const onSuccess = (pageIds: string[], isWorkspaceFile: boolean) => {
|
||||
@ -55,6 +60,7 @@ export const usePageHelper = (blockSuiteWorkspace: BlockSuiteWorkspace) => {
|
||||
};
|
||||
showImportModal({ workspace: blockSuiteWorkspace, onSuccess });
|
||||
}, [blockSuiteWorkspace, openPage, jumpToSubPath]);
|
||||
|
||||
return useMemo(() => {
|
||||
return {
|
||||
createPage: createPageAndOpen,
|
||||
|
@ -12,6 +12,7 @@ export const searchInput = style({
|
||||
color: 'var(--affine-text-primary-color)',
|
||||
fontSize: 'var(--affine-font-h-5)',
|
||||
padding: '21px 24px',
|
||||
marginBottom: '8px',
|
||||
width: '100%',
|
||||
borderBottom: '1px solid var(--affine-border-color)',
|
||||
flexShrink: 0,
|
||||
@ -114,13 +115,6 @@ globalStyle(`${root} [cmdk-group][hidden]`, {
|
||||
display: 'none',
|
||||
});
|
||||
|
||||
globalStyle(
|
||||
`${root} [cmdk-group]:not([hidden]):first-of-type [cmdk-group-heading]`,
|
||||
{
|
||||
paddingTop: 16,
|
||||
}
|
||||
);
|
||||
|
||||
globalStyle(`${root} [cmdk-list]`, {
|
||||
maxHeight: 400,
|
||||
minHeight: 120,
|
||||
@ -187,3 +181,11 @@ globalStyle(
|
||||
color: 'var(--affine-error-color)',
|
||||
}
|
||||
);
|
||||
|
||||
export const resultGroupHeader = style({
|
||||
padding: '8px',
|
||||
color: 'var(--affine-text-secondary-color)',
|
||||
fontSize: 'var(--affine-font-xs)',
|
||||
fontWeight: 600,
|
||||
lineHeight: '1.67',
|
||||
});
|
||||
|
@ -1,11 +1,12 @@
|
||||
import { Command } from '@affine/cmdk';
|
||||
import { useCommandState } from '@affine/cmdk';
|
||||
import { formatDate } from '@affine/component/page-list';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import type { PageMeta } from '@blocksuite/store';
|
||||
import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks';
|
||||
import type { CommandCategory } from '@toeverything/infra/command';
|
||||
import clsx from 'clsx';
|
||||
import { useAtom } from 'jotai';
|
||||
import { useAtom, useAtomValue } from 'jotai';
|
||||
import { Suspense, useLayoutEffect, useMemo, useState } from 'react';
|
||||
|
||||
import {
|
||||
@ -71,7 +72,7 @@ const QuickSearchGroup = ({
|
||||
);
|
||||
|
||||
return (
|
||||
<Command.Group key={category} heading={t[i18nkey]()}>
|
||||
<Command.Group key={category} heading={query ? '' : t[i18nkey]()}>
|
||||
{commands.map(command => {
|
||||
const label =
|
||||
typeof command.label === 'string'
|
||||
@ -129,18 +130,43 @@ const QuickSearchCommands = ({
|
||||
}: {
|
||||
onOpenChange?: (open: boolean) => void;
|
||||
}) => {
|
||||
const t = useAFFiNEI18N();
|
||||
const groups = useCMDKCommandGroups();
|
||||
|
||||
return groups.map(([category, commands]) => {
|
||||
return (
|
||||
<QuickSearchGroup
|
||||
key={category}
|
||||
onOpenChange={onOpenChange}
|
||||
category={category}
|
||||
commands={commands}
|
||||
/>
|
||||
);
|
||||
});
|
||||
const query = useAtomValue(cmdkQueryAtom);
|
||||
const resultCount = useCommandState(state => state.filtered.count);
|
||||
const resultGroupHeader = useMemo(() => {
|
||||
if (query) {
|
||||
return (
|
||||
<div className={styles.resultGroupHeader}>
|
||||
{
|
||||
// hack: use resultCount to determine if it is creation or results
|
||||
// because the creation(as 2 results) is always shown at the top when there is no result
|
||||
resultCount === 2
|
||||
? t['com.affine.cmdk.affine.category.affine.creation']()
|
||||
: t['com.affine.cmdk.affine.category.results']()
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}, [query, resultCount, t]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{resultGroupHeader}
|
||||
{groups.map(([category, commands]) => {
|
||||
return (
|
||||
<QuickSearchGroup
|
||||
key={category}
|
||||
onOpenChange={onOpenChange}
|
||||
category={category}
|
||||
commands={commands}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const CMDKContainer = ({
|
||||
|
@ -1,25 +1,31 @@
|
||||
import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks';
|
||||
import {
|
||||
useBlockSuitePageMeta,
|
||||
usePageMetaHelper,
|
||||
} from '@toeverything/hooks/use-block-suite-page-meta';
|
||||
import { useBlockSuiteWorkspaceHelper } from '@toeverything/hooks/use-block-suite-workspace-helper';
|
||||
import { useAtomValue, useSetAtom } from 'jotai';
|
||||
import { useCallback } from 'react';
|
||||
import { applyUpdate, encodeStateAsUpdate } from 'yjs';
|
||||
|
||||
import { setPageModeAtom } from '../../atoms';
|
||||
import { currentModeAtom } from '../../atoms/mode';
|
||||
import type { BlockSuiteWorkspace } from '../../shared';
|
||||
import { getWorkspaceSetting } from '../../utils/workspace-setting';
|
||||
import { useNavigateHelper } from '../use-navigate-helper';
|
||||
import { useReferenceLinkHelper } from './use-reference-link-helper';
|
||||
|
||||
export function useBlockSuiteMetaHelper(
|
||||
blockSuiteWorkspace: BlockSuiteWorkspace
|
||||
) {
|
||||
const { setPageMeta, getPageMeta, setPageReadonly } =
|
||||
const { setPageMeta, getPageMeta, setPageReadonly, setPageTitle } =
|
||||
usePageMetaHelper(blockSuiteWorkspace);
|
||||
const { addReferenceLink } = useReferenceLinkHelper(blockSuiteWorkspace);
|
||||
const metas = useBlockSuitePageMeta(blockSuiteWorkspace);
|
||||
const setPageMode = useSetAtom(setPageModeAtom);
|
||||
const currentMode = useAtomValue(currentModeAtom);
|
||||
const { createPage } = useBlockSuiteWorkspaceHelper(blockSuiteWorkspace);
|
||||
const { openPage } = useNavigateHelper();
|
||||
|
||||
const switchToPageMode = useCallback(
|
||||
(pageId: string) => {
|
||||
@ -140,6 +146,40 @@ export function useBlockSuiteMetaHelper(
|
||||
[setPageMeta]
|
||||
);
|
||||
|
||||
const duplicate = useAsyncCallback(
|
||||
async (pageId: string) => {
|
||||
const currentPageMeta = getPageMeta(pageId);
|
||||
const newPage = createPage();
|
||||
const currentPage = blockSuiteWorkspace.getPage(pageId);
|
||||
|
||||
await newPage.waitForLoaded();
|
||||
if (!currentPageMeta || !currentPage) {
|
||||
return;
|
||||
}
|
||||
|
||||
const update = encodeStateAsUpdate(currentPage.spaceDoc);
|
||||
applyUpdate(newPage.spaceDoc, update);
|
||||
|
||||
setPageMeta(newPage.id, {
|
||||
tags: currentPageMeta.tags,
|
||||
favorite: currentPageMeta.favorite,
|
||||
});
|
||||
setPageMode(newPage.id, currentMode);
|
||||
setPageTitle(newPage.id, `${currentPageMeta.title}(1)`);
|
||||
openPage(blockSuiteWorkspace.id, newPage.id);
|
||||
},
|
||||
[
|
||||
blockSuiteWorkspace,
|
||||
createPage,
|
||||
currentMode,
|
||||
getPageMeta,
|
||||
openPage,
|
||||
setPageMeta,
|
||||
setPageMode,
|
||||
setPageTitle,
|
||||
]
|
||||
);
|
||||
|
||||
return {
|
||||
switchToPageMode,
|
||||
switchToEdgelessMode,
|
||||
@ -155,5 +195,7 @@ export function useBlockSuiteMetaHelper(
|
||||
removeToTrash,
|
||||
restoreFromTrash,
|
||||
permanentlyDeletePage,
|
||||
|
||||
duplicate,
|
||||
};
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ export function useRegisterBlocksuiteEditorCommands(
|
||||
}));
|
||||
}, [pageId, setPageHistoryModalState]);
|
||||
|
||||
const { togglePageMode, toggleFavorite, restoreFromTrash } =
|
||||
const { togglePageMode, toggleFavorite, restoreFromTrash, duplicate } =
|
||||
useBlockSuiteMetaHelper(blockSuiteWorkspace);
|
||||
const exportHandler = useExportPage(currentPage);
|
||||
const { setTrashModal } = useTrashModalHelper(blockSuiteWorkspace);
|
||||
@ -125,6 +125,19 @@ export function useRegisterBlocksuiteEditorCommands(
|
||||
})
|
||||
);
|
||||
|
||||
unsubs.push(
|
||||
registerAffineCommand({
|
||||
id: `editor:${mode}-duplicate`,
|
||||
preconditionStrategy,
|
||||
category: `editor:${mode}`,
|
||||
icon: mode === 'page' ? <PageIcon /> : <EdgelessIcon />,
|
||||
label: t['com.affine.header.option.duplicate'](),
|
||||
run() {
|
||||
duplicate(pageId);
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
unsubs.push(
|
||||
registerAffineCommand({
|
||||
id: `editor:${mode}-export-to-pdf`,
|
||||
@ -234,5 +247,6 @@ export function useRegisterBlocksuiteEditorCommands(
|
||||
trash,
|
||||
isCloudWorkspace,
|
||||
openHistoryModal,
|
||||
duplicate,
|
||||
]);
|
||||
}
|
||||
|
@ -486,6 +486,7 @@
|
||||
"com.affine.cmdk.affine.category.editor.edgeless": "Edgeless Commands",
|
||||
"com.affine.cmdk.affine.category.editor.insert-object": "Insert Object",
|
||||
"com.affine.cmdk.affine.category.editor.page": "Page Commands",
|
||||
"com.affine.cmdk.affine.category.results": "Results",
|
||||
"com.affine.cmdk.affine.client-border-style.to": "Change Client Border Style to",
|
||||
"com.affine.cmdk.affine.color-mode.to": "Change Colour Mode to",
|
||||
"com.affine.cmdk.affine.color-scheme.to": "Change Colour Scheme to",
|
||||
|
Loading…
Reference in New Issue
Block a user