mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-25 02:13:00 +08:00
fix(core): polling to search in cmdk (#5274)
This is a temporary solution until https://github.com/toeverything/blocksuite/issues/5668 be solved.
This commit is contained in:
@@ -25,7 +25,7 @@ import {
|
|||||||
} from '@toeverything/infra/command';
|
} from '@toeverything/infra/command';
|
||||||
import { atom, useAtomValue } from 'jotai';
|
import { atom, useAtomValue } from 'jotai';
|
||||||
import { groupBy } from 'lodash-es';
|
import { groupBy } from 'lodash-es';
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
openQuickSearchModalAtom,
|
openQuickSearchModalAtom,
|
||||||
@@ -173,13 +173,21 @@ export const pageToCommand = (
|
|||||||
title: title,
|
title: title,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// hack: when comparing, the part between >>> and <<< will be ignored
|
||||||
|
// adding this patch so that CMDK will not complain about duplicated commands
|
||||||
|
const id =
|
||||||
|
title +
|
||||||
|
(label?.subTitle || '') +
|
||||||
|
valueWrapperStart +
|
||||||
|
page.id +
|
||||||
|
'.' +
|
||||||
|
category +
|
||||||
|
valueWrapperEnd;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: page.id,
|
id,
|
||||||
label: commandLabel,
|
label: commandLabel,
|
||||||
// hack: when comparing, the part between >>> and <<< will be ignored
|
value: id,
|
||||||
// adding this patch so that CMDK will not complain about duplicated commands
|
|
||||||
value:
|
|
||||||
title + valueWrapperStart + page.id + '.' + category + valueWrapperEnd,
|
|
||||||
originalValue: title,
|
originalValue: title,
|
||||||
category: category,
|
category: category,
|
||||||
run: () => {
|
run: () => {
|
||||||
@@ -216,7 +224,25 @@ export const usePageCommands = () => {
|
|||||||
const navigationHelper = useNavigateHelper();
|
const navigationHelper = useNavigateHelper();
|
||||||
const t = useAFFiNEI18N();
|
const t = useAFFiNEI18N();
|
||||||
|
|
||||||
|
const [searchTime, setSearchTime] = useState<number>(0);
|
||||||
|
|
||||||
|
// HACK: blocksuite indexer is async,
|
||||||
|
// so we need to re-search after it has been updated
|
||||||
|
useEffect(() => {
|
||||||
|
let timer: NodeJS.Timeout | null = null;
|
||||||
|
const dosearch = () => {
|
||||||
|
setSearchTime(Date.now());
|
||||||
|
timer = setTimeout(dosearch, 500);
|
||||||
|
};
|
||||||
|
timer = setTimeout(dosearch, 500);
|
||||||
|
return () => {
|
||||||
|
if (timer) clearTimeout(timer);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
|
searchTime; // hack to make the searchTime as a dependency
|
||||||
|
|
||||||
let results: CMDKCommand[] = [];
|
let results: CMDKCommand[] = [];
|
||||||
if (query.trim() === '') {
|
if (query.trim() === '') {
|
||||||
results = recentPages.map(page => {
|
results = recentPages.map(page => {
|
||||||
@@ -322,6 +348,7 @@ export const usePageCommands = () => {
|
|||||||
store,
|
store,
|
||||||
t,
|
t,
|
||||||
workspace.blockSuiteWorkspace,
|
workspace.blockSuiteWorkspace,
|
||||||
|
searchTime,
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,13 @@ import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks';
|
|||||||
import type { CommandCategory } from '@toeverything/infra/command';
|
import type { CommandCategory } from '@toeverything/infra/command';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useAtom, useAtomValue } from 'jotai';
|
import { useAtom, useAtomValue } from 'jotai';
|
||||||
import { Suspense, useLayoutEffect, useMemo, useState } from 'react';
|
import {
|
||||||
|
Suspense,
|
||||||
|
useCallback,
|
||||||
|
useLayoutEffect,
|
||||||
|
useMemo,
|
||||||
|
useState,
|
||||||
|
} from 'react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
cmdkQueryAtom,
|
cmdkQueryAtom,
|
||||||
@@ -189,6 +195,12 @@ export const CMDKContainer = ({
|
|||||||
const isInEditor = pageMeta !== undefined;
|
const isInEditor = pageMeta !== undefined;
|
||||||
const [opening, setOpening] = useState(open);
|
const [opening, setOpening] = useState(open);
|
||||||
|
|
||||||
|
const handleFocus = useCallback((ref: HTMLInputElement | null) => {
|
||||||
|
if (ref) {
|
||||||
|
window.setTimeout(() => ref.focus(), 0);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
// fix list height animation on openning
|
// fix list height animation on openning
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
if (open) {
|
if (open) {
|
||||||
@@ -235,7 +247,7 @@ export const CMDKContainer = ({
|
|||||||
) : null}
|
) : null}
|
||||||
<Command.Input
|
<Command.Input
|
||||||
placeholder={t['com.affine.cmdk.placeholder']()}
|
placeholder={t['com.affine.cmdk.placeholder']()}
|
||||||
autoFocus
|
ref={handleFocus}
|
||||||
{...rest}
|
{...rest}
|
||||||
value={query}
|
value={query}
|
||||||
onValueChange={onQueryChange}
|
onValueChange={onQueryChange}
|
||||||
|
|||||||
@@ -16,6 +16,12 @@ const openQuickSearchByShortcut = async (page: Page, checkVisible = true) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const insertInputText = async (page: Page, text: string) => {
|
||||||
|
await page.locator('[cmdk-input]').fill(text);
|
||||||
|
const actual = await page.locator('[cmdk-input]').inputValue();
|
||||||
|
expect(actual).toBe(text);
|
||||||
|
};
|
||||||
|
|
||||||
const keyboardDownAndSelect = async (page: Page, label: string) => {
|
const keyboardDownAndSelect = async (page: Page, label: string) => {
|
||||||
await page.keyboard.press('ArrowDown');
|
await page.keyboard.press('ArrowDown');
|
||||||
if (
|
if (
|
||||||
@@ -77,14 +83,16 @@ async function waitForScrollToFinish(page: Page) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function assertResultList(page: Page, texts: string[]) {
|
async function assertResultList(page: Page, texts: string[]) {
|
||||||
const actual = await page
|
await expect(async () => {
|
||||||
.locator('[cmdk-item] [data-testid=cmdk-label]')
|
const actual = await page
|
||||||
.allInnerTexts();
|
.locator('[cmdk-item] [data-testid=cmdk-label]')
|
||||||
const actualSplit = actual[0].split('\n');
|
.allInnerTexts();
|
||||||
expect(actualSplit[0]).toEqual(texts[0]);
|
const actualSplit = actual[0].split('\n');
|
||||||
if (actualSplit[1]) {
|
expect(actualSplit[0]).toEqual(texts[0]);
|
||||||
expect(actualSplit[1]).toEqual(texts[1]);
|
if (actualSplit[1]) {
|
||||||
}
|
expect(actualSplit[1]).toEqual(texts[1]);
|
||||||
|
}
|
||||||
|
}).toPass();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function titleIsFocused(page: Page) {
|
async function titleIsFocused(page: Page) {
|
||||||
@@ -137,7 +145,7 @@ test('Create a new page with keyword', async ({ page }) => {
|
|||||||
await waitForEditorLoad(page);
|
await waitForEditorLoad(page);
|
||||||
await clickNewPageButton(page);
|
await clickNewPageButton(page);
|
||||||
await openQuickSearchByShortcut(page);
|
await openQuickSearchByShortcut(page);
|
||||||
await page.keyboard.insertText('"test123456"');
|
await insertInputText(page, '"test123456"');
|
||||||
const addNewPage = page.locator(
|
const addNewPage = page.locator(
|
||||||
'[cmdk-item] >> text=New ""test123456"" Page'
|
'[cmdk-item] >> text=New ""test123456"" Page'
|
||||||
);
|
);
|
||||||
@@ -151,9 +159,7 @@ test('Enter a keyword to search for', async ({ page }) => {
|
|||||||
await waitForEditorLoad(page);
|
await waitForEditorLoad(page);
|
||||||
await clickNewPageButton(page);
|
await clickNewPageButton(page);
|
||||||
await openQuickSearchByShortcut(page);
|
await openQuickSearchByShortcut(page);
|
||||||
await page.keyboard.insertText('test123456');
|
await insertInputText(page, 'test123456');
|
||||||
const actual = await page.locator('[cmdk-input]').inputValue();
|
|
||||||
expect(actual).toBe('test123456');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Create a new page and search this page', async ({ page }) => {
|
test('Create a new page and search this page', async ({ page }) => {
|
||||||
@@ -162,7 +168,7 @@ test('Create a new page and search this page', async ({ page }) => {
|
|||||||
await clickNewPageButton(page);
|
await clickNewPageButton(page);
|
||||||
await openQuickSearchByShortcut(page);
|
await openQuickSearchByShortcut(page);
|
||||||
// input title and create new page
|
// input title and create new page
|
||||||
await page.keyboard.insertText('test123456');
|
await insertInputText(page, 'test123456');
|
||||||
await page.waitForTimeout(300);
|
await page.waitForTimeout(300);
|
||||||
const addNewPage = page.locator('[cmdk-item] >> text=New "test123456" Page');
|
const addNewPage = page.locator('[cmdk-item] >> text=New "test123456" Page');
|
||||||
await addNewPage.click();
|
await addNewPage.click();
|
||||||
@@ -170,7 +176,7 @@ test('Create a new page and search this page', async ({ page }) => {
|
|||||||
await page.waitForTimeout(300);
|
await page.waitForTimeout(300);
|
||||||
await assertTitle(page, 'test123456');
|
await assertTitle(page, 'test123456');
|
||||||
await openQuickSearchByShortcut(page);
|
await openQuickSearchByShortcut(page);
|
||||||
await page.keyboard.insertText('test123456');
|
await insertInputText(page, 'test123456');
|
||||||
await page.waitForTimeout(300);
|
await page.waitForTimeout(300);
|
||||||
await assertResultList(page, ['test123456', 'test123456']);
|
await assertResultList(page, ['test123456', 'test123456']);
|
||||||
await page.keyboard.press('Enter');
|
await page.keyboard.press('Enter');
|
||||||
@@ -180,7 +186,7 @@ test('Create a new page and search this page', async ({ page }) => {
|
|||||||
await page.reload();
|
await page.reload();
|
||||||
await waitForEditorLoad(page);
|
await waitForEditorLoad(page);
|
||||||
await openQuickSearchByShortcut(page);
|
await openQuickSearchByShortcut(page);
|
||||||
await page.keyboard.insertText('test123456');
|
await insertInputText(page, 'test123456');
|
||||||
await page.waitForTimeout(300);
|
await page.waitForTimeout(300);
|
||||||
await assertResultList(page, ['test123456', 'test123456']);
|
await assertResultList(page, ['test123456', 'test123456']);
|
||||||
await page.keyboard.press('Enter');
|
await page.keyboard.press('Enter');
|
||||||
@@ -375,7 +381,7 @@ test('show not found item', async ({ page }) => {
|
|||||||
await clickNewPageButton(page);
|
await clickNewPageButton(page);
|
||||||
await openQuickSearchByShortcut(page);
|
await openQuickSearchByShortcut(page);
|
||||||
// input title and create new page
|
// input title and create new page
|
||||||
await page.keyboard.insertText('test123456');
|
await insertInputText(page, 'test123456');
|
||||||
const notFoundItem = page.getByTestId('cmdk-search-not-found');
|
const notFoundItem = page.getByTestId('cmdk-search-not-found');
|
||||||
await expect(notFoundItem).toBeVisible();
|
await expect(notFoundItem).toBeVisible();
|
||||||
await expect(notFoundItem).toHaveText('Search for "test123456"');
|
await expect(notFoundItem).toHaveText('Search for "test123456"');
|
||||||
@@ -395,15 +401,17 @@ test('can use cmdk to search page content and scroll to it, then the block will
|
|||||||
await page.keyboard.press('Enter', { delay: 10 });
|
await page.keyboard.press('Enter', { delay: 10 });
|
||||||
}
|
}
|
||||||
await page.keyboard.insertText('123456');
|
await page.keyboard.insertText('123456');
|
||||||
|
const textBlock = page.getByText('123456');
|
||||||
|
await expect(textBlock).toBeVisible();
|
||||||
await clickSideBarAllPageButton(page);
|
await clickSideBarAllPageButton(page);
|
||||||
await openQuickSearchByShortcut(page);
|
await openQuickSearchByShortcut(page);
|
||||||
await page.keyboard.insertText('123456');
|
await insertInputText(page, '123456');
|
||||||
await page.waitForTimeout(300);
|
await page.waitForTimeout(300);
|
||||||
await assertResultList(page, [
|
await assertResultList(page, [
|
||||||
'this is a new page to search for content',
|
'this is a new page to search for content',
|
||||||
'123456',
|
'123456',
|
||||||
]);
|
]);
|
||||||
await page.keyboard.press('Enter');
|
await page.locator('[cmdk-item] [data-testid=cmdk-label]').first().click();
|
||||||
await waitForScrollToFinish(page);
|
await waitForScrollToFinish(page);
|
||||||
const isVisitable = await checkElementIsInView(page, '123456');
|
const isVisitable = await checkElementIsInView(page, '123456');
|
||||||
expect(isVisitable).toBe(true);
|
expect(isVisitable).toBe(true);
|
||||||
@@ -424,7 +432,7 @@ test('Create a new page with special characters in the title and search for this
|
|||||||
await getBlockSuiteEditorTitle(page).fill(specialTitle);
|
await getBlockSuiteEditorTitle(page).fill(specialTitle);
|
||||||
await openQuickSearchByShortcut(page);
|
await openQuickSearchByShortcut(page);
|
||||||
|
|
||||||
await page.keyboard.insertText(specialTitle);
|
await insertInputText(page, specialTitle);
|
||||||
await page.waitForTimeout(300);
|
await page.waitForTimeout(300);
|
||||||
|
|
||||||
await assertResultList(page, [specialTitle, specialTitle]);
|
await assertResultList(page, [specialTitle, specialTitle]);
|
||||||
|
|||||||
Reference in New Issue
Block a user