mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-27 02:42:25 +08:00
feat(core): adopt editor features for journal (#5638)
This commit is contained in:
@@ -1,49 +0,0 @@
|
||||
/**
|
||||
* @vitest-environment happy-dom
|
||||
*/
|
||||
import 'fake-indexeddb/auto';
|
||||
|
||||
import { __unstableSchemas, AffineSchemas } from '@blocksuite/blocks/models';
|
||||
import { assertExists } from '@blocksuite/global/utils';
|
||||
import type { Page } from '@blocksuite/store';
|
||||
import { Schema, Workspace as BlockSuiteWorkspace } from '@blocksuite/store';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { describe, expect, test, vi } from 'vitest';
|
||||
import { beforeEach } from 'vitest';
|
||||
|
||||
import { useBlockSuiteWorkspacePageTitle } from '../use-block-suite-workspace-page-title';
|
||||
|
||||
let blockSuiteWorkspace: BlockSuiteWorkspace;
|
||||
|
||||
const schema = new Schema();
|
||||
schema.register(AffineSchemas).register(__unstableSchemas);
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.useFakeTimers({ toFake: ['requestIdleCallback'] });
|
||||
blockSuiteWorkspace = new BlockSuiteWorkspace({ id: 'test', schema });
|
||||
const initPage = async (page: Page) => {
|
||||
await page.waitForLoaded();
|
||||
expect(page).not.toBeNull();
|
||||
assertExists(page);
|
||||
const pageBlockId = page.addBlock('affine:page', {
|
||||
title: new page.Text(''),
|
||||
});
|
||||
const frameId = page.addBlock('affine:note', {}, pageBlockId);
|
||||
page.addBlock('affine:paragraph', {}, frameId);
|
||||
};
|
||||
await initPage(blockSuiteWorkspace.createPage({ id: 'page0' }));
|
||||
await initPage(blockSuiteWorkspace.createPage({ id: 'page1' }));
|
||||
await initPage(blockSuiteWorkspace.createPage({ id: 'page2' }));
|
||||
});
|
||||
|
||||
describe('useBlockSuiteWorkspacePageTitle', () => {
|
||||
test('basic', async () => {
|
||||
const pageTitleHook = renderHook(() =>
|
||||
useBlockSuiteWorkspacePageTitle(blockSuiteWorkspace, 'page0')
|
||||
);
|
||||
expect(pageTitleHook.result.current).toBe('Untitled');
|
||||
blockSuiteWorkspace.setPageMeta('page0', { title: '1' });
|
||||
pageTitleHook.rerender();
|
||||
expect(pageTitleHook.result.current).toBe('1');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* @vitest-environment happy-dom
|
||||
*/
|
||||
import 'fake-indexeddb/auto';
|
||||
|
||||
import {
|
||||
currentWorkspaceAtom,
|
||||
WorkspacePropertiesAdapter,
|
||||
} from '@affine/core/modules/workspace';
|
||||
import { WorkspaceFlavour } from '@affine/env/workspace';
|
||||
import type { Workspace } from '@affine/workspace/workspace';
|
||||
import { __unstableSchemas, AffineSchemas } from '@blocksuite/blocks/models';
|
||||
import { assertExists } from '@blocksuite/global/utils';
|
||||
import { type Page, Workspace as BlockSuiteWorkspace } from '@blocksuite/store';
|
||||
import { Schema } from '@blocksuite/store';
|
||||
import { render } from '@testing-library/react';
|
||||
import { createStore, Provider } from 'jotai';
|
||||
import { Suspense } from 'react';
|
||||
import { describe, expect, test, vi } from 'vitest';
|
||||
import { beforeEach } from 'vitest';
|
||||
|
||||
import { useBlockSuiteWorkspacePageTitle } from '../use-block-suite-workspace-page-title';
|
||||
|
||||
let blockSuiteWorkspace: BlockSuiteWorkspace;
|
||||
const store = createStore();
|
||||
|
||||
const schema = new Schema();
|
||||
schema.register(AffineSchemas).register(__unstableSchemas);
|
||||
|
||||
const Component = () => {
|
||||
const title = useBlockSuiteWorkspacePageTitle(blockSuiteWorkspace, 'page0');
|
||||
return <div>title: {title}</div>;
|
||||
};
|
||||
|
||||
// todo: this module has some side-effects that will break the tests
|
||||
vi.mock('@affine/workspace-impl', () => ({
|
||||
default: {},
|
||||
}));
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.useFakeTimers({ toFake: ['requestIdleCallback'] });
|
||||
|
||||
blockSuiteWorkspace = new BlockSuiteWorkspace({ id: 'test', schema });
|
||||
|
||||
const workspace = {
|
||||
blockSuiteWorkspace,
|
||||
flavour: WorkspaceFlavour.LOCAL,
|
||||
} as Workspace;
|
||||
|
||||
store.set(currentWorkspaceAtom, workspace);
|
||||
|
||||
blockSuiteWorkspace = workspace.blockSuiteWorkspace;
|
||||
|
||||
const initPage = async (page: Page) => {
|
||||
await page.waitForLoaded();
|
||||
expect(page).not.toBeNull();
|
||||
assertExists(page);
|
||||
const pageBlockId = page.addBlock('affine:page', {
|
||||
title: new page.Text(''),
|
||||
});
|
||||
const frameId = page.addBlock('affine:note', {}, pageBlockId);
|
||||
page.addBlock('affine:paragraph', {}, frameId);
|
||||
};
|
||||
await initPage(blockSuiteWorkspace.createPage({ id: 'page0' }));
|
||||
await initPage(blockSuiteWorkspace.createPage({ id: 'page1' }));
|
||||
await initPage(blockSuiteWorkspace.createPage({ id: 'page2' }));
|
||||
});
|
||||
|
||||
describe('useBlockSuiteWorkspacePageTitle', () => {
|
||||
test('basic', async () => {
|
||||
const { findByText, rerender } = render(
|
||||
<Provider store={store}>
|
||||
<Suspense fallback="loading">
|
||||
<Component />
|
||||
</Suspense>
|
||||
</Provider>
|
||||
);
|
||||
expect(await findByText('title: Untitled')).toBeDefined();
|
||||
blockSuiteWorkspace.setPageMeta('page0', { title: '1' });
|
||||
rerender(
|
||||
<Provider store={store}>
|
||||
<Suspense fallback="loading">
|
||||
<Component />
|
||||
</Suspense>
|
||||
</Provider>
|
||||
);
|
||||
expect(await findByText('title: 1')).toBeDefined();
|
||||
});
|
||||
|
||||
test('journal', async () => {
|
||||
const adapter = new WorkspacePropertiesAdapter(blockSuiteWorkspace);
|
||||
adapter.setJournalPageDateString('page0', '2021-01-01');
|
||||
const { findByText } = render(
|
||||
<Provider store={store}>
|
||||
<Suspense fallback="loading">
|
||||
<Component />
|
||||
</Suspense>
|
||||
</Provider>
|
||||
);
|
||||
expect(await findByText('title: Jan 1, 2021')).toBeDefined();
|
||||
});
|
||||
});
|
||||
@@ -5,7 +5,7 @@ import { useEffect, useMemo, useReducer } from 'react';
|
||||
import {
|
||||
currentWorkspacePropertiesAdapterAtom,
|
||||
WorkspacePropertiesAdapter,
|
||||
} from '../modules/workspace';
|
||||
} from '../modules/workspace/properties';
|
||||
|
||||
const useReactiveAdapter = (adapter: WorkspacePropertiesAdapter) => {
|
||||
const [, forceUpdate] = useReducer(c => c + 1, 0);
|
||||
|
||||
@@ -3,6 +3,8 @@ import type { Workspace } from '@blocksuite/store';
|
||||
import type { Atom } from 'jotai';
|
||||
import { atom, useAtomValue } from 'jotai';
|
||||
|
||||
import { useJournalInfoHelper } from './use-journal';
|
||||
|
||||
const weakMap = new WeakMap<Workspace, Map<string, Atom<string>>>();
|
||||
|
||||
function getAtom(w: Workspace, pageId: string): Atom<string> {
|
||||
@@ -35,5 +37,10 @@ export function useBlockSuiteWorkspacePageTitle(
|
||||
) {
|
||||
const titleAtom = getAtom(blockSuiteWorkspace, pageId);
|
||||
assertExists(titleAtom);
|
||||
return useAtomValue(titleAtom);
|
||||
const title = useAtomValue(titleAtom);
|
||||
const { localizedJournalDate } = useJournalInfoHelper(
|
||||
blockSuiteWorkspace,
|
||||
pageId
|
||||
);
|
||||
return localizedJournalDate || title;
|
||||
}
|
||||
|
||||
@@ -25,8 +25,6 @@ function toDayjs(j?: string | false) {
|
||||
|
||||
export const useJournalHelper = (workspace: BlockSuiteWorkspace) => {
|
||||
const bsWorkspaceHelper = useBlockSuiteWorkspaceHelper(workspace);
|
||||
const navigateHelper = useNavigateHelper();
|
||||
|
||||
const adapter = useWorkspacePropertiesAdapter(workspace);
|
||||
|
||||
/**
|
||||
@@ -82,25 +80,15 @@ export const useJournalHelper = (workspace: BlockSuiteWorkspace) => {
|
||||
[_createJournal, getJournalsByDate]
|
||||
);
|
||||
|
||||
/**
|
||||
* open journal by date, create one if not exist
|
||||
*/
|
||||
const openJournal = useCallback(
|
||||
(maybeDate: MaybeDate) => {
|
||||
const page = getJournalByDate(maybeDate);
|
||||
navigateHelper.openPage(workspace.id, page.id);
|
||||
const isPageTodayJournal = useCallback(
|
||||
(pageId: string) => {
|
||||
const date = dayjs().format(JOURNAL_DATE_FORMAT);
|
||||
const d = adapter.getJournalPageDateString(pageId);
|
||||
return isPageJournal(pageId) && d === date;
|
||||
},
|
||||
[getJournalByDate, navigateHelper, workspace.id]
|
||||
[adapter, isPageJournal]
|
||||
);
|
||||
|
||||
/**
|
||||
* open today's journal
|
||||
*/
|
||||
const openToday = useCallback(() => {
|
||||
const date = dayjs().format(JOURNAL_DATE_FORMAT);
|
||||
openJournal(date);
|
||||
}, [openJournal]);
|
||||
|
||||
const getJournalDateString = useCallback(
|
||||
(pageId: string) => {
|
||||
return adapter.getJournalPageDateString(pageId);
|
||||
@@ -123,9 +111,8 @@ export const useJournalHelper = (workspace: BlockSuiteWorkspace) => {
|
||||
getJournalByDate,
|
||||
getJournalDateString,
|
||||
getLocalizedJournalDateString,
|
||||
openJournal,
|
||||
openToday,
|
||||
isPageJournal,
|
||||
isPageTodayJournal,
|
||||
}),
|
||||
[
|
||||
getJournalByDate,
|
||||
@@ -133,9 +120,40 @@ export const useJournalHelper = (workspace: BlockSuiteWorkspace) => {
|
||||
getJournalsByDate,
|
||||
getLocalizedJournalDateString,
|
||||
isPageJournal,
|
||||
isPageTodayJournal,
|
||||
]
|
||||
);
|
||||
};
|
||||
|
||||
// split useJournalRouteHelper since it requires a <Route /> context, which may not work in lit
|
||||
export const useJournalRouteHelper = (workspace: BlockSuiteWorkspace) => {
|
||||
const navigateHelper = useNavigateHelper();
|
||||
const { getJournalByDate } = useJournalHelper(workspace);
|
||||
/**
|
||||
* open journal by date, create one if not exist
|
||||
*/
|
||||
const openJournal = useCallback(
|
||||
(maybeDate: MaybeDate) => {
|
||||
const page = getJournalByDate(maybeDate);
|
||||
navigateHelper.openPage(workspace.id, page.id);
|
||||
},
|
||||
[getJournalByDate, navigateHelper, workspace.id]
|
||||
);
|
||||
|
||||
/**
|
||||
* open today's journal
|
||||
*/
|
||||
const openToday = useCallback(() => {
|
||||
const date = dayjs().format(JOURNAL_DATE_FORMAT);
|
||||
openJournal(date);
|
||||
}, [openJournal]);
|
||||
|
||||
return useMemo(
|
||||
() => ({
|
||||
openJournal,
|
||||
openToday,
|
||||
]
|
||||
}),
|
||||
[openJournal, openToday]
|
||||
);
|
||||
};
|
||||
|
||||
@@ -143,17 +161,28 @@ export const useJournalInfoHelper = (
|
||||
workspace: BlockSuiteWorkspace,
|
||||
pageId?: string | null
|
||||
) => {
|
||||
const { isPageJournal, getJournalDateString } = useJournalHelper(workspace);
|
||||
const journalDate = useMemo(
|
||||
() => (pageId ? toDayjs(getJournalDateString(pageId)) : null),
|
||||
[getJournalDateString, pageId]
|
||||
);
|
||||
const {
|
||||
isPageJournal,
|
||||
getJournalDateString,
|
||||
getLocalizedJournalDateString,
|
||||
isPageTodayJournal,
|
||||
} = useJournalHelper(workspace);
|
||||
|
||||
return useMemo(
|
||||
() => ({
|
||||
isJournal: pageId ? isPageJournal(pageId) : false,
|
||||
journalDate,
|
||||
journalDate: pageId ? toDayjs(getJournalDateString(pageId)) : null,
|
||||
localizedJournalDate: pageId
|
||||
? getLocalizedJournalDateString(pageId)
|
||||
: null,
|
||||
isTodayJournal: pageId ? isPageTodayJournal(pageId) : false,
|
||||
}),
|
||||
[isPageJournal, journalDate, pageId]
|
||||
[
|
||||
getJournalDateString,
|
||||
getLocalizedJournalDateString,
|
||||
isPageJournal,
|
||||
isPageTodayJournal,
|
||||
pageId,
|
||||
]
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user