feat(core): page info adapter for journal (#5561)

Page info adapter + schema.
Adapted for journal features.

![image](https://github.com/toeverything/AFFiNE/assets/584378/2731ed2b-a125-4d62-b658-f2aff49d0e17)
This commit is contained in:
Peng Xiao
2024-01-22 08:25:27 +00:00
parent 8b92cc0cae
commit 735e1cb117
18 changed files with 412 additions and 68 deletions

View File

@@ -0,0 +1,39 @@
import type { Workspace } from '@blocksuite/store';
import { useAtomValue } from 'jotai';
import { useEffect, useMemo, useReducer } from 'react';
import {
currentWorkspacePropertiesAdapterAtom,
WorkspacePropertiesAdapter,
} from '../modules/workspace';
const useReactiveAdapter = (adapter: WorkspacePropertiesAdapter) => {
const [, forceUpdate] = useReducer(c => c + 1, 0);
useEffect(() => {
// todo: track which properties are used and then filter by property path change
// using Y.YEvent.path
function observe() {
forceUpdate();
}
adapter.properties.observeDeep(observe);
return () => {
adapter.properties.unobserveDeep(observe);
};
}, [adapter]);
return adapter;
};
export function useCurrentWorkspacePropertiesAdapter() {
const adapter = useAtomValue(currentWorkspacePropertiesAdapterAtom);
return useReactiveAdapter(adapter);
}
export function useWorkspacePropertiesAdapter(workspace: Workspace) {
const adapter = useMemo(
() => new WorkspacePropertiesAdapter(workspace),
[workspace]
);
return useReactiveAdapter(adapter);
}

View File

@@ -1,29 +0,0 @@
import { useBlockSuitePageMeta } from '@affine/core/hooks/use-block-suite-page-meta';
import type { GetPageInfoById } from '@affine/env/page-info';
import type { Workspace } from '@blocksuite/store';
import { useAtomValue } from 'jotai';
import { useCallback, useMemo } from 'react';
import { pageSettingsAtom } from '../atoms';
export const useGetPageInfoById = (workspace: Workspace): GetPageInfoById => {
const pageMetas = useBlockSuitePageMeta(workspace);
const pageMap = useMemo(
() => Object.fromEntries(pageMetas.map(page => [page.id, page])),
[pageMetas]
);
const pageSettings = useAtomValue(pageSettingsAtom);
return useCallback(
(id: string) => {
const page = pageMap[id];
if (!page) {
return;
}
return {
...page,
isEdgeless: pageSettings[id]?.mode === 'edgeless',
};
},
[pageMap, pageSettings]
);
};

View File

@@ -1,31 +1,34 @@
import type { PageMeta } from '@blocksuite/store';
import { initEmptyPage } from '@toeverything/infra/blocksuite';
import dayjs from 'dayjs';
import { useCallback, useMemo } from 'react';
import type { BlockSuiteWorkspace } from '../shared';
import { timestampToLocalDate } from '../utils';
import { useWorkspacePropertiesAdapter } from './use-affine-adapter';
import { useBlockSuiteWorkspaceHelper } from './use-block-suite-workspace-helper';
import { useNavigateHelper } from './use-navigate-helper';
type MaybeDate = Date | string | number;
export const JOURNAL_DATE_FORMAT = 'YYYY-MM-DD';
function isPageJournal(pageMeta?: PageMeta) {
function isJournalString(j?: string | false) {
if (!runtimeConfig.enableJournal) return false;
return !!(pageMeta && pageMeta.title.match(/^\d{4}-\d{2}-\d{2}$/));
return j ? !!j?.match(/^\d{4}-\d{2}-\d{2}$/) : false;
}
function getJournalDate(pageMeta?: PageMeta) {
if (!isPageJournal(pageMeta)) return null;
if (!pageMeta?.title) return null;
if (!dayjs(pageMeta.title).isValid()) return null;
return dayjs(pageMeta.title);
function toDayjs(j?: string | false) {
if (!j || !isJournalString(j)) return null;
const day = dayjs(j);
if (!day.isValid()) return null;
return day;
}
export const useJournalHelper = (workspace: BlockSuiteWorkspace) => {
const bsWorkspaceHelper = useBlockSuiteWorkspaceHelper(workspace);
const navigateHelper = useNavigateHelper();
const adapter = useWorkspacePropertiesAdapter(workspace);
/**
* @internal
*/
@@ -36,9 +39,17 @@ export const useJournalHelper = (workspace: BlockSuiteWorkspace) => {
initEmptyPage(page, title).catch(err =>
console.error('Failed to load journal page', err)
);
adapter.setJournalPageDateString(page.id, title);
return page;
},
[bsWorkspaceHelper]
[adapter, bsWorkspaceHelper]
);
const isPageJournal = useCallback(
(pageId: string) => {
return !!adapter.getJournalPageDateString(pageId);
},
[adapter]
);
/**
@@ -48,14 +59,15 @@ export const useJournalHelper = (workspace: BlockSuiteWorkspace) => {
(maybeDate: MaybeDate) => {
const day = dayjs(maybeDate);
return Array.from(workspace.pages.values()).filter(page => {
if (!isPageJournal(page.meta)) return false;
const pageId = page.id;
if (!isPageJournal(pageId)) return false;
if (page.meta.trash) return false;
const journalDate = getJournalDate(page.meta);
const journalDate = adapter.getJournalPageDateString(page.id);
if (!journalDate) return false;
return day.isSame(journalDate, 'day');
});
},
[workspace.pages]
[adapter, isPageJournal, workspace.pages]
);
/**
@@ -89,26 +101,59 @@ export const useJournalHelper = (workspace: BlockSuiteWorkspace) => {
openJournal(date);
}, [openJournal]);
const getJournalDateString = useCallback(
(pageId: string) => {
return adapter.getJournalPageDateString(pageId);
},
[adapter]
);
const getLocalizedJournalDateString = useCallback(
(pageId: string) => {
const journalDateString = getJournalDateString(pageId);
if (!journalDateString) return null;
return timestampToLocalDate(journalDateString);
},
[getJournalDateString]
);
return useMemo(
() => ({
getJournalsByDate,
getJournalByDate,
getJournalDateString,
getLocalizedJournalDateString,
openJournal,
openToday,
isPageJournal,
}),
[getJournalByDate, getJournalsByDate, openJournal, openToday]
[
getJournalByDate,
getJournalDateString,
getJournalsByDate,
getLocalizedJournalDateString,
isPageJournal,
openJournal,
openToday,
]
);
};
export const useJournalInfoHelper = (pageMeta?: PageMeta) => {
const isJournal = isPageJournal(pageMeta);
const journalDate = useMemo(() => getJournalDate(pageMeta), [pageMeta]);
export const useJournalInfoHelper = (
workspace: BlockSuiteWorkspace,
pageId?: string | null
) => {
const { isPageJournal, getJournalDateString } = useJournalHelper(workspace);
const journalDate = useMemo(
() => (pageId ? toDayjs(getJournalDateString(pageId)) : null),
[getJournalDateString, pageId]
);
return useMemo(
() => ({
isJournal,
isJournal: pageId ? isPageJournal(pageId) : false,
journalDate,
}),
[isJournal, journalDate]
[isPageJournal, journalDate, pageId]
);
};