feat(core): extract DocDisplayMetaService to resolve doc icon/title (#8226)

AF-1315
This commit is contained in:
CatsJuice
2024-09-19 01:25:02 +00:00
parent f397815ad1
commit f4a19921c4
26 changed files with 361 additions and 383 deletions

View File

@@ -32,5 +32,6 @@ globalStyle(`${wrapper} span`, {
whiteSpace: 'nowrap', whiteSpace: 'nowrap',
overflow: 'hidden', overflow: 'hidden',
textOverflow: 'ellipsis', textOverflow: 'ellipsis',
borderBottom: 'none', // don't modify border width to avoid layout shift
borderBottomColor: 'transparent',
}); });

View File

@@ -1,8 +1,6 @@
import type { Backlink, Link } from '@affine/core/modules/doc-link'; import type { Backlink, Link } from '@affine/core/modules/doc-link';
import { useContext } from 'react';
import { AffinePageReference } from '../../reference-link'; import { AffinePageReference } from '../../reference-link';
import { managerContext } from '../common';
import * as styles from './links-row.css'; import * as styles from './links-row.css';
export const LinksRow = ({ export const LinksRow = ({
@@ -16,20 +14,18 @@ export const LinksRow = ({
className?: string; className?: string;
onClick?: () => void; onClick?: () => void;
}) => { }) => {
const manager = useContext(managerContext);
return ( return (
<div className={className}> <div className={className}>
<div className={styles.title}> <div className={styles.title}>
{label} · {references.length} {label} · {references.length}
</div> </div>
{references.map(link => ( {references.map((link, index) => (
<AffinePageReference <AffinePageReference
key={link.docId} key={index}
pageId={link.docId} pageId={link.docId}
wrapper={props => ( wrapper={props => (
<div className={styles.wrapper} onClick={onClick} {...props} /> <div className={styles.wrapper} onClick={onClick} {...props} />
)} )}
docCollection={manager.workspace.docCollection}
/> />
))} ))}
</div> </div>

View File

@@ -379,8 +379,6 @@ export const PageBacklinksPopup = ({
backlinks, backlinks,
children, children,
}: PageBacklinksPopupProps) => { }: PageBacklinksPopupProps) => {
const manager = useContext(managerContext);
return ( return (
<Menu <Menu
contentOptions={{ contentOptions={{
@@ -395,7 +393,6 @@ export const PageBacklinksPopup = ({
key={link.docId + ':' + link.blockId} key={link.docId + ':' + link.blockId}
wrapper={MenuItem} wrapper={MenuItem}
pageId={link.docId} pageId={link.docId}
docCollection={manager.workspace.docCollection}
/> />
))} ))}
</div> </div>

View File

@@ -1,5 +1,5 @@
import { useDocMetaHelper } from '@affine/core/components/hooks/use-block-suite-page-meta'; import { useJournalInfoHelper } from '@affine/core/components/hooks/use-journal';
import { useJournalHelper } from '@affine/core/components/hooks/use-journal'; import { DocDisplayMetaService } from '@affine/core/modules/doc-display-meta';
import { import {
PeekViewService, PeekViewService,
useInsidePeekView, useInsidePeekView,
@@ -8,15 +8,8 @@ import { WorkbenchLink } from '@affine/core/modules/workbench';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
import { track } from '@affine/track'; import { track } from '@affine/track';
import type { DocMode } from '@blocksuite/blocks'; import type { DocMode } from '@blocksuite/blocks';
import {
BlockLinkIcon,
DeleteIcon,
LinkedEdgelessIcon,
LinkedPageIcon,
TodayIcon,
} from '@blocksuite/icons/rc';
import type { DocCollection } from '@blocksuite/store'; import type { DocCollection } from '@blocksuite/store';
import { DocsService, useLiveData, useService } from '@toeverything/infra'; import { useLiveData, useService } from '@toeverything/infra';
import { nanoid } from 'nanoid'; import { nanoid } from 'nanoid';
import { import {
type PropsWithChildren, type PropsWithChildren,
@@ -29,77 +22,18 @@ import { Link } from 'react-router-dom';
import * as styles from './styles.css'; import * as styles from './styles.css';
export interface PageReferenceRendererOptions {
pageId: string;
docCollection: DocCollection;
pageMetaHelper: ReturnType<typeof useDocMetaHelper>;
journalHelper: ReturnType<typeof useJournalHelper>;
t: ReturnType<typeof useI18n>;
docMode?: DocMode;
// Link to block or element
linkToNode?: boolean;
}
// use a function to be rendered in the lit renderer
export function pageReferenceRenderer({
pageId,
pageMetaHelper,
journalHelper,
t,
docMode,
linkToNode = false,
}: PageReferenceRendererOptions) {
const { isPageJournal, getLocalizedJournalDateString } = journalHelper;
const referencedPage = pageMetaHelper.getDocMeta(pageId);
let title =
referencedPage?.title ?? t['com.affine.editor.reference-not-found']();
let Icon = DeleteIcon;
if (referencedPage) {
if (docMode === 'edgeless') {
Icon = LinkedEdgelessIcon;
} else {
Icon = LinkedPageIcon;
}
if (linkToNode) {
Icon = BlockLinkIcon;
}
}
const isJournal = isPageJournal(pageId);
const localizedJournalDate = getLocalizedJournalDateString(pageId);
if (isJournal && localizedJournalDate) {
title = localizedJournalDate;
Icon = TodayIcon;
}
return (
<>
<Icon className={styles.pageReferenceIcon} />
<span className="affine-reference-title">
{title ? title : t['Untitled']()}
</span>
</>
);
}
export function AffinePageReference({ export function AffinePageReference({
pageId, pageId,
docCollection,
wrapper: Wrapper, wrapper: Wrapper,
params, params,
}: { }: {
pageId: string; pageId: string;
docCollection: DocCollection;
wrapper?: React.ComponentType<PropsWithChildren>; wrapper?: React.ComponentType<PropsWithChildren>;
params?: URLSearchParams; params?: URLSearchParams;
}) { }) {
const docDisplayMetaService = useService(DocDisplayMetaService);
const journalHelper = useJournalInfoHelper();
const t = useI18n(); const t = useI18n();
const pageMetaHelper = useDocMetaHelper();
const journalHelper = useJournalHelper(docCollection);
const docsService = useService(DocsService);
const mode = useLiveData(docsService.list.primaryMode$(pageId));
let linkWithMode: DocMode | null = null; let linkWithMode: DocMode | null = null;
let linkToNode = false; let linkToNode = false;
@@ -111,15 +45,23 @@ export function AffinePageReference({
linkToNode = params.has('blockIds') || params.has('elementIds'); linkToNode = params.has('blockIds') || params.has('elementIds');
} }
const el = pageReferenceRenderer({ const Icon = useLiveData(
docMode: linkWithMode ?? mode ?? 'page', docDisplayMetaService.icon$(pageId, {
pageId, mode: linkWithMode ?? undefined,
pageMetaHelper, reference: true,
journalHelper, referenceToNode: linkToNode,
docCollection, })
t, );
linkToNode, const title = useLiveData(docDisplayMetaService.title$(pageId));
});
const el = (
<>
<Icon className={styles.pageReferenceIcon} />
<span className="affine-reference-title">
{typeof title === 'string' ? title : t[title.key]()}
</span>
</>
);
const ref = useRef<HTMLAnchorElement>(null); const ref = useRef<HTMLAnchorElement>(null);
@@ -186,11 +128,9 @@ export function AffineSharedPageReference({
wrapper?: React.ComponentType<PropsWithChildren>; wrapper?: React.ComponentType<PropsWithChildren>;
params?: URLSearchParams; params?: URLSearchParams;
}) { }) {
const docDisplayMetaService = useService(DocDisplayMetaService);
const journalHelper = useJournalInfoHelper();
const t = useI18n(); const t = useI18n();
const pageMetaHelper = useDocMetaHelper();
const journalHelper = useJournalHelper(docCollection);
const docsService = useService(DocsService);
const mode = useLiveData(docsService.list.primaryMode$(pageId));
let linkWithMode: DocMode | null = null; let linkWithMode: DocMode | null = null;
let linkToNode = false; let linkToNode = false;
@@ -202,15 +142,22 @@ export function AffineSharedPageReference({
linkToNode = params.has('blockIds') || params.has('elementIds'); linkToNode = params.has('blockIds') || params.has('elementIds');
} }
const el = pageReferenceRenderer({ const Icon = useLiveData(
docMode: linkWithMode ?? mode ?? 'page', docDisplayMetaService.icon$(pageId, {
pageId, mode: linkWithMode ?? undefined,
pageMetaHelper, reference: true,
journalHelper, referenceToNode: linkToNode,
docCollection, })
t, );
linkToNode, const title = useLiveData(docDisplayMetaService.title$(pageId));
}); const el = (
<>
<Icon className={styles.pageReferenceIcon} />
<span className="affine-reference-title">
{typeof title === 'string' ? title : t[title.key]()}
</span>
</>
);
const ref = useRef<HTMLAnchorElement>(null); const ref = useRef<HTMLAnchorElement>(null);

View File

@@ -1,10 +1,6 @@
import { DocLinksService } from '@affine/core/modules/doc-link'; import { DocLinksService } from '@affine/core/modules/doc-link';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
import { import { useLiveData, useServices } from '@toeverything/infra';
useLiveData,
useServices,
WorkspaceService,
} from '@toeverything/infra';
import { useCallback, useState } from 'react'; import { useCallback, useState } from 'react';
import { AffinePageReference } from '../../affine/reference-link'; import { AffinePageReference } from '../../affine/reference-link';
@@ -12,9 +8,8 @@ import * as styles from './bi-directional-link-panel.css';
export const BiDirectionalLinkPanel = () => { export const BiDirectionalLinkPanel = () => {
const [show, setShow] = useState(false); const [show, setShow] = useState(false);
const { docLinksService, workspaceService } = useServices({ const { docLinksService } = useServices({
DocLinksService, DocLinksService,
WorkspaceService,
}); });
const t = useI18n(); const t = useI18n();
@@ -50,11 +45,7 @@ export const BiDirectionalLinkPanel = () => {
</div> </div>
{backlinks.map(link => ( {backlinks.map(link => (
<div key={link.docId} className={styles.link}> <div key={link.docId} className={styles.link}>
<AffinePageReference <AffinePageReference key={link.docId} pageId={link.docId} />
key={link.docId}
pageId={link.docId}
docCollection={workspaceService.workspace.docCollection}
/>
</div> </div>
))} ))}
</div> </div>
@@ -68,11 +59,7 @@ export const BiDirectionalLinkPanel = () => {
key={`${link.docId}-${link.params?.toString()}-${i}`} key={`${link.docId}-${link.params?.toString()}-${i}`}
className={styles.link} className={styles.link}
> >
<AffinePageReference <AffinePageReference pageId={link.docId} params={link.params} />
pageId={link.docId}
params={link.params}
docCollection={workspaceService.workspace.docCollection}
/>
</div> </div>
))} ))}
</div> </div>

View File

@@ -6,7 +6,7 @@ import * as styles from './styles.css';
export const BlocksuiteEditorJournalDocTitle = ({ page }: { page: Doc }) => { export const BlocksuiteEditorJournalDocTitle = ({ page }: { page: Doc }) => {
const { localizedJournalDate, isTodayJournal, journalDate } = const { localizedJournalDate, isTodayJournal, journalDate } =
useJournalInfoHelper(page.collection, page.id); useJournalInfoHelper(page.id);
const t = useI18n(); const t = useI18n();
// TODO(catsjuice): i18n // TODO(catsjuice): i18n

View File

@@ -72,7 +72,7 @@ interface BlocksuiteEditorProps {
shared?: boolean; shared?: boolean;
} }
const usePatchSpecs = (page: Doc, shared: boolean, mode: DocMode) => { const usePatchSpecs = (shared: boolean, mode: DocMode) => {
const [reactToLit, portals] = useLitPortalFactory(); const [reactToLit, portals] = useLitPortalFactory();
const { const {
peekViewService, peekViewService,
@@ -110,15 +110,9 @@ const usePatchSpecs = (page: Doc, shared: boolean, mode: DocMode) => {
); );
} }
return ( return <AffinePageReference pageId={pageId} params={params} />;
<AffinePageReference
docCollection={page.collection}
pageId={pageId}
params={params}
/>
);
}; };
}, [page.collection, workspaceService]); }, [workspaceService]);
const specs = useMemo(() => { const specs = useMemo(() => {
const enableAI = featureFlagService.flags.enable_ai.value; const enableAI = featureFlagService.flags.enable_ai.value;
@@ -184,7 +178,7 @@ export const BlocksuiteDocEditor = forwardRef<
) { ) {
const titleRef = useRef<DocTitle | null>(null); const titleRef = useRef<DocTitle | null>(null);
const docRef = useRef<PageEditor | null>(null); const docRef = useRef<PageEditor | null>(null);
const { isJournal } = useJournalInfoHelper(page.collection, page.id); const { isJournal } = useJournalInfoHelper(page.id);
const editorSettingService = useService(EditorSettingService); const editorSettingService = useService(EditorSettingService);
@@ -216,7 +210,7 @@ export const BlocksuiteDocEditor = forwardRef<
[externalTitleRef] [externalTitleRef]
); );
const [specs, portals] = usePatchSpecs(page, !!shared, 'page'); const [specs, portals] = usePatchSpecs(!!shared, 'page');
const displayBiDirectionalLink = useLiveData( const displayBiDirectionalLink = useLiveData(
editorSettingService.editorSetting.settings$.selector( editorSettingService.editorSetting.settings$.selector(
@@ -257,7 +251,7 @@ export const BlocksuiteEdgelessEditor = forwardRef<
EdgelessEditor, EdgelessEditor,
BlocksuiteEditorProps BlocksuiteEditorProps
>(function BlocksuiteEdgelessEditor({ page, shared }, ref) { >(function BlocksuiteEdgelessEditor({ page, shared }, ref) {
const [specs, portals] = usePatchSpecs(page, !!shared, 'edgeless'); const [specs, portals] = usePatchSpecs(!!shared, 'edgeless');
const editorRef = useRef<EdgelessEditor | null>(null); const editorRef = useRef<EdgelessEditor | null>(null);
const onDocRef = useCallback( const onDocRef = useCallback(

View File

@@ -1,20 +1,12 @@
import { DocDisplayMetaService } from '@affine/core/modules/doc-display-meta';
import { WorkspacePropertiesAdapter } from '@affine/core/modules/properties'; import { WorkspacePropertiesAdapter } from '@affine/core/modules/properties';
import { I18n, i18nTime } from '@affine/i18n'; import { I18n } from '@affine/i18n';
import { track } from '@affine/track'; import { track } from '@affine/track';
import type { EditorHost } from '@blocksuite/block-std'; import type { EditorHost } from '@blocksuite/block-std';
import type { AffineInlineEditor } from '@blocksuite/blocks'; import type { AffineInlineEditor } from '@blocksuite/blocks';
import { LinkedWidgetUtils } from '@blocksuite/blocks'; import { LinkedWidgetUtils } from '@blocksuite/blocks';
import {
LinkedEdgelessIcon,
LinkedPageIcon,
TodayIcon,
} from '@blocksuite/icons/lit';
import type { DocMeta } from '@blocksuite/store'; import type { DocMeta } from '@blocksuite/store';
import { import { type FrameworkProvider, WorkspaceService } from '@toeverything/infra';
DocsService,
type FrameworkProvider,
WorkspaceService,
} from '@toeverything/infra';
// TODO: fix the type // TODO: fix the type
export function createLinkedWidgetConfig( export function createLinkedWidgetConfig(
@@ -33,6 +25,7 @@ export function createLinkedWidgetConfig(
const isJournal = (d: DocMeta) => const isJournal = (d: DocMeta) =>
!!adapter.getJournalPageDateString(d.id); !!adapter.getJournalPageDateString(d.id);
const docDisplayMetaService = framework.get(DocDisplayMetaService);
const docMetas = rawMetas const docMetas = rawMetas
.filter(meta => { .filter(meta => {
if (isJournal(meta) && !meta.updatedDate) { if (isJournal(meta) && !meta.updatedDate) {
@@ -41,37 +34,28 @@ export function createLinkedWidgetConfig(
return !meta.trash; return !meta.trash;
}) })
.map(meta => { .map(meta => {
if (isJournal(meta)) { const title = docDisplayMetaService.title$(meta.id).value;
const date = adapter.getJournalPageDateString(meta.id); return {
if (date) { ...meta,
const title = i18nTime(date, { absolute: { accuracy: 'day' } }); title: typeof title === 'string' ? title : I18n[title.key](),
return { ...meta, title }; };
}
}
if (!meta.title) {
const title = I18n['Untitled']();
return { ...meta, title };
}
return meta;
}) })
.filter(({ title }) => isFuzzyMatch(title, query)); .filter(({ title }) => isFuzzyMatch(title, query));
// TODO need i18n if BlockSuite supported // TODO need i18n if BlockSuite supported
const MAX_DOCS = 6; const MAX_DOCS = 6;
const docsService = framework.get(DocsService);
const isEdgeless = (d: DocMeta) =>
docsService.list.getPrimaryMode(d.id) === 'edgeless';
return Promise.resolve([ return Promise.resolve([
{ {
name: 'Link to Doc', name: 'Link to Doc',
items: docMetas.map(doc => ({ items: docMetas.map(doc => ({
key: doc.id, key: doc.id,
name: doc.title, name: doc.title,
icon: isJournal(doc) icon: docDisplayMetaService
? TodayIcon() .icon$(doc.id, {
: isEdgeless(doc) type: 'lit',
? LinkedEdgelessIcon() reference: true,
: LinkedPageIcon(), })
.value(),
action: () => { action: () => {
abort(); abort();
LinkedWidgetUtils.insertLinkedNode({ LinkedWidgetUtils.insertLinkedNode({

View File

@@ -19,7 +19,7 @@ export const JournalWeekDatePicker = ({
page, page,
}: JournalWeekDatePickerProps) => { }: JournalWeekDatePickerProps) => {
const handleRef = useRef<WeekDatePickerHandle>(null); const handleRef = useRef<WeekDatePickerHandle>(null);
const { journalDate } = useJournalInfoHelper(docCollection, page.id); const { journalDate } = useJournalInfoHelper(page.id);
const { openJournal } = useJournalRouteHelper(docCollection); const { openJournal } = useJournalRouteHelper(docCollection);
const [date, setDate] = useState( const [date, setDate] = useState(
(journalDate ?? dayjs()).format('YYYY-MM-DD') (journalDate ?? dayjs()).format('YYYY-MM-DD')

View File

@@ -4,7 +4,7 @@ import { useCallback, useMemo } from 'react';
import { useAsyncCallback } from './affine-async-hooks'; import { useAsyncCallback } from './affine-async-hooks';
import { useAllBlockSuiteDocMeta } from './use-all-block-suite-page-meta'; import { useAllBlockSuiteDocMeta } from './use-all-block-suite-page-meta';
import { useJournalHelper } from './use-journal'; import { useJournalInfoHelper } from './use-journal';
/** /**
* Get pageMetas excluding journal pages without updatedDate * Get pageMetas excluding journal pages without updatedDate
@@ -13,7 +13,7 @@ import { useJournalHelper } from './use-journal';
*/ */
export function useBlockSuiteDocMeta(docCollection: DocCollection) { export function useBlockSuiteDocMeta(docCollection: DocCollection) {
const pageMetas = useAllBlockSuiteDocMeta(docCollection); const pageMetas = useAllBlockSuiteDocMeta(docCollection);
const { isPageJournal } = useJournalHelper(docCollection); const { isPageJournal } = useJournalInfoHelper();
return useMemo( return useMemo(
() => () =>
pageMetas.filter( pageMetas.filter(

View File

@@ -4,7 +4,7 @@ import type { Atom } from 'jotai';
import { atom, useAtomValue } from 'jotai'; import { atom, useAtomValue } from 'jotai';
import { useCallback } from 'react'; import { useCallback } from 'react';
import { useJournalHelper, useJournalInfoHelper } from './use-journal'; import { useJournalInfoHelper } from './use-journal';
const weakMap = new WeakMap<DocCollection, Map<string, Atom<string>>>(); const weakMap = new WeakMap<DocCollection, Map<string, Atom<string>>>();
@@ -32,6 +32,9 @@ function getAtom(w: DocCollection, pageId: string): Atom<string> {
} }
} }
/**
* @deprecated use `useDocTitle(docId: string)` instead
*/
export function useDocCollectionPageTitle( export function useDocCollectionPageTitle(
docCollection: DocCollection, docCollection: DocCollection,
pageId: string pageId: string
@@ -39,13 +42,13 @@ export function useDocCollectionPageTitle(
const titleAtom = getAtom(docCollection, pageId); const titleAtom = getAtom(docCollection, pageId);
assertExists(titleAtom); assertExists(titleAtom);
const title = useAtomValue(titleAtom); const title = useAtomValue(titleAtom);
const { localizedJournalDate } = useJournalInfoHelper(docCollection, pageId); const { localizedJournalDate } = useJournalInfoHelper(pageId);
return localizedJournalDate || title; return localizedJournalDate || title;
} }
// This hook is NOT reactive to the page title change // This hook is NOT reactive to the page title change
export function useGetDocCollectionPageTitle(docCollection: DocCollection) { export function useGetDocCollectionPageTitle(docCollection: DocCollection) {
const { getLocalizedJournalDateString } = useJournalHelper(docCollection); const { getLocalizedJournalDateString } = useJournalInfoHelper();
return useCallback( return useCallback(
(pageId: string) => { (pageId: string) => {
return ( return (

View File

@@ -37,6 +37,7 @@ export const useJournalHelper = (docCollection: DocCollection) => {
EditorSettingService, EditorSettingService,
}); });
const adapter = useCurrentWorkspacePropertiesAdapter(); const adapter = useCurrentWorkspacePropertiesAdapter();
const { isPageJournal } = useJournalInfoHelper();
/** /**
* @internal * @internal
@@ -67,13 +68,6 @@ export const useJournalHelper = (docCollection: DocCollection) => {
[adapter, bsWorkspaceHelper, docsService.list, editorSettingService] [adapter, bsWorkspaceHelper, docsService.list, editorSettingService]
); );
const isPageJournal = useCallback(
(pageId: string) => {
return !!adapter.getJournalPageDateString(pageId);
},
[adapter]
);
/** /**
* query all journals by date * query all journals by date
*/ */
@@ -104,31 +98,6 @@ export const useJournalHelper = (docCollection: DocCollection) => {
[_createJournal, getJournalsByDate] [_createJournal, getJournalsByDate]
); );
const isPageTodayJournal = useCallback(
(pageId: string) => {
const date = dayjs().format(JOURNAL_DATE_FORMAT);
const d = adapter.getJournalPageDateString(pageId);
return isPageJournal(pageId) && d === date;
},
[adapter, isPageJournal]
);
const getJournalDateString = useCallback(
(pageId: string) => {
return adapter.getJournalPageDateString(pageId);
},
[adapter]
);
const getLocalizedJournalDateString = useCallback(
(pageId: string) => {
const journalDateString = getJournalDateString(pageId);
if (!journalDateString) return null;
return i18nTime(journalDateString, { absolute: { accuracy: 'day' } });
},
[getJournalDateString]
);
const appendContentToToday = useCallback( const appendContentToToday = useCallback(
async (content: string) => { async (content: string) => {
if (!content) return; if (!content) return;
@@ -148,21 +117,9 @@ export const useJournalHelper = (docCollection: DocCollection) => {
() => ({ () => ({
getJournalsByDate, getJournalsByDate,
getJournalByDate, getJournalByDate,
getJournalDateString,
getLocalizedJournalDateString,
isPageJournal,
isPageTodayJournal,
appendContentToToday, appendContentToToday,
}), }),
[ [getJournalsByDate, getJournalByDate, appendContentToToday]
getJournalsByDate,
getJournalByDate,
getJournalDateString,
getLocalizedJournalDateString,
isPageJournal,
isPageTodayJournal,
appendContentToToday,
]
); );
}; };
@@ -207,16 +164,41 @@ export const useJournalRouteHelper = (docCollection: DocCollection) => {
); );
}; };
export const useJournalInfoHelper = ( // get journal info that don't rely on `docCollection`
docCollection: DocCollection, export const useJournalInfoHelper = (pageId?: string | null) => {
pageId?: string | null const adapter = useCurrentWorkspacePropertiesAdapter();
) => {
const { const isPageJournal = useCallback(
isPageJournal, (pageId: string) => {
getJournalDateString, return !!adapter.getJournalPageDateString(pageId);
getLocalizedJournalDateString, },
isPageTodayJournal, [adapter]
} = useJournalHelper(docCollection); );
const isPageTodayJournal = useCallback(
(pageId: string) => {
const date = dayjs().format(JOURNAL_DATE_FORMAT);
const d = adapter.getJournalPageDateString(pageId);
return isPageJournal(pageId) && d === date;
},
[adapter, isPageJournal]
);
const getJournalDateString = useCallback(
(pageId: string) => {
return adapter.getJournalPageDateString(pageId);
},
[adapter]
);
const getLocalizedJournalDateString = useCallback(
(pageId: string) => {
const journalDateString = getJournalDateString(pageId);
if (!journalDateString) return null;
return i18nTime(journalDateString, { absolute: { accuracy: 'day' } });
},
[getJournalDateString]
);
return useMemo( return useMemo(
() => ({ () => ({
@@ -226,6 +208,10 @@ export const useJournalInfoHelper = (
? getLocalizedJournalDateString(pageId) ? getLocalizedJournalDateString(pageId)
: null, : null,
isTodayJournal: pageId ? isPageTodayJournal(pageId) : false, isTodayJournal: pageId ? isPageTodayJournal(pageId) : false,
isPageJournal,
isPageTodayJournal,
getJournalDateString,
getLocalizedJournalDateString,
}), }),
[ [
getJournalDateString, getJournalDateString,

View File

@@ -1,17 +1,11 @@
import { useJournalInfoHelper } from '@affine/core/components/hooks/use-journal'; import { DocDisplayMetaService } from '@affine/core/modules/doc-display-meta';
import type { Tag } from '@affine/env/filter'; import type { Tag } from '@affine/env/filter';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
import { assertExists } from '@blocksuite/global/utils'; import { assertExists } from '@blocksuite/global/utils';
import { import { ToggleCollapseIcon, ViewLayersIcon } from '@blocksuite/icons/rc';
EdgelessIcon,
PageIcon,
TodayIcon,
ToggleCollapseIcon,
ViewLayersIcon,
} from '@blocksuite/icons/rc';
import type { DocCollection, DocMeta } from '@blocksuite/store'; import type { DocCollection, DocMeta } from '@blocksuite/store';
import * as Collapsible from '@radix-ui/react-collapsible'; import * as Collapsible from '@radix-ui/react-collapsible';
import { DocsService, useLiveData, useService } from '@toeverything/infra'; import { useLiveData, useService } from '@toeverything/infra';
import clsx from 'clsx'; import clsx from 'clsx';
import { selectAtom } from 'jotai/utils'; import { selectAtom } from 'jotai/utils';
import type { MouseEventHandler } from 'react'; import type { MouseEventHandler } from 'react';
@@ -284,26 +278,16 @@ function tagIdToTagOption(
} }
const PageTitle = ({ id }: { id: string }) => { const PageTitle = ({ id }: { id: string }) => {
const doc = useLiveData(useService(DocsService).list.doc$(id));
const title = useLiveData(doc?.title$);
const t = useI18n(); const t = useI18n();
return title || t['Untitled'](); const docDisplayMetaService = useService(DocDisplayMetaService);
const title = useLiveData(docDisplayMetaService.title$(id));
return typeof title === 'string' ? title : t[title.key]();
}; };
const UnifiedPageIcon = ({ const UnifiedPageIcon = ({ id }: { id: string }) => {
id, const docDisplayMetaService = useService(DocDisplayMetaService);
docCollection, const Icon = useLiveData(docDisplayMetaService.icon$(id));
}: { return <Icon />;
id: string;
docCollection: DocCollection;
}) => {
const list = useService(DocsService).list;
const isEdgeless = useLiveData(list.primaryMode$(id)) === 'edgeless';
const { isJournal } = useJournalInfoHelper(docCollection, id);
if (isJournal) {
return <TodayIcon />;
}
return isEdgeless ? <EdgelessIcon /> : <PageIcon />;
}; };
function pageMetaToListItemProp( function pageMetaToListItemProp(
@@ -338,7 +322,7 @@ function pageMetaToListItemProp(
updatedDate: item.updatedDate ? new Date(item.updatedDate) : undefined, updatedDate: item.updatedDate ? new Date(item.updatedDate) : undefined,
to: props.rowAsLink && !props.selectable ? `/${item.id}` : undefined, to: props.rowAsLink && !props.selectable ? `/${item.id}` : undefined,
onClick: toggleSelection, onClick: toggleSelection,
icon: <UnifiedPageIcon id={item.id} docCollection={props.docCollection} />, icon: <UnifiedPageIcon id={item.id} />,
tags: tags:
item.tags item.tags
?.map(id => tagIdToTagOption(id, props.docCollection)) ?.map(id => tagIdToTagOption(id, props.docCollection))

View File

@@ -3,10 +3,11 @@ import {
useJournalInfoHelper, useJournalInfoHelper,
useJournalRouteHelper, useJournalRouteHelper,
} from '@affine/core/components/hooks/use-journal'; } from '@affine/core/components/hooks/use-journal';
import { DocDisplayMetaService } from '@affine/core/modules/doc-display-meta';
import { WorkbenchService } from '@affine/core/modules/workbench'; import { WorkbenchService } from '@affine/core/modules/workbench';
import { isNewTabTrigger } from '@affine/core/utils'; import { isNewTabTrigger } from '@affine/core/utils';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
import { TodayIcon, TomorrowIcon, YesterdayIcon } from '@blocksuite/icons/rc'; import { TodayIcon } from '@blocksuite/icons/rc';
import type { DocCollection } from '@blocksuite/store'; import type { DocCollection } from '@blocksuite/store';
import { useLiveData, useService } from '@toeverything/infra'; import { useLiveData, useService } from '@toeverything/infra';
import { type MouseEvent } from 'react'; import { type MouseEvent } from 'react';
@@ -21,13 +22,11 @@ export const AppSidebarJournalButton = ({
docCollection, docCollection,
}: AppSidebarJournalButtonProps) => { }: AppSidebarJournalButtonProps) => {
const t = useI18n(); const t = useI18n();
const docDisplayMetaService = useService(DocDisplayMetaService);
const workbench = useService(WorkbenchService).workbench; const workbench = useService(WorkbenchService).workbench;
const location = useLiveData(workbench.location$); const location = useLiveData(workbench.location$);
const { openToday } = useJournalRouteHelper(docCollection); const { openToday } = useJournalRouteHelper(docCollection);
const { journalDate, isJournal } = useJournalInfoHelper( const { isJournal } = useJournalInfoHelper(location.pathname.split('/')[1]);
docCollection,
location.pathname.split('/')[1]
);
const handleOpenToday = useCatchEventCallback( const handleOpenToday = useCatchEventCallback(
(e: MouseEvent) => { (e: MouseEvent) => {
@@ -36,14 +35,12 @@ export const AppSidebarJournalButton = ({
[openToday] [openToday]
); );
const Icon = const JournalIcon = useLiveData(
isJournal && journalDate docDisplayMetaService.icon$(docCollection.id, {
? journalDate.isBefore(new Date(), 'day') compareDate: new Date(),
? YesterdayIcon })
: journalDate.isAfter(new Date(), 'day') );
? TomorrowIcon const Icon = isJournal ? JournalIcon : TodayIcon;
: TodayIcon
: TodayIcon;
return ( return (
<MenuItem <MenuItem

View File

@@ -166,7 +166,7 @@ export function NormalPageHeader({ page, workspace }: PageHeaderProps) {
export function DetailPageHeader(props: PageHeaderProps) { export function DetailPageHeader(props: PageHeaderProps) {
const { page, workspace } = props; const { page, workspace } = props;
const { isJournal } = useJournalInfoHelper(page.collection, page.id); const { isJournal } = useJournalInfoHelper(page.id);
const isInTrash = page.meta?.trash; const isInTrash = page.meta?.trash;
const [openInfoModal, setOpenInfoModal] = useAtom(openInfoModalAtom); const [openInfoModal, setOpenInfoModal] = useAtom(openInfoModalAtom);

View File

@@ -1,21 +1,16 @@
import type { DateCell } from '@affine/component'; import type { DateCell } from '@affine/component';
import { DatePicker, IconButton, Menu, Scrollable } from '@affine/component'; import { DatePicker, IconButton, Menu, Scrollable } from '@affine/component';
import { useTrashModalHelper } from '@affine/core/components/hooks/affine/use-trash-modal-helper'; import { useTrashModalHelper } from '@affine/core/components/hooks/affine/use-trash-modal-helper';
import { useDocCollectionPageTitle } from '@affine/core/components/hooks/use-block-suite-workspace-page-title';
import { import {
useJournalHelper, useJournalHelper,
useJournalInfoHelper, useJournalInfoHelper,
useJournalRouteHelper, useJournalRouteHelper,
} from '@affine/core/components/hooks/use-journal'; } from '@affine/core/components/hooks/use-journal';
import { MoveToTrash } from '@affine/core/components/page-list'; import { MoveToTrash } from '@affine/core/components/page-list';
import { DocDisplayMetaService } from '@affine/core/modules/doc-display-meta';
import { WorkbenchLink } from '@affine/core/modules/workbench'; import { WorkbenchLink } from '@affine/core/modules/workbench';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
import { import { MoreHorizontalIcon } from '@blocksuite/icons/rc';
EdgelessIcon,
MoreHorizontalIcon,
PageIcon,
TodayIcon,
} from '@blocksuite/icons/rc';
import type { DocRecord } from '@toeverything/infra'; import type { DocRecord } from '@toeverything/infra';
import { import {
DocService, DocService,
@@ -28,7 +23,7 @@ import { assignInlineVars } from '@vanilla-extract/dynamic';
import clsx from 'clsx'; import clsx from 'clsx';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import type { HTMLAttributes, PropsWithChildren, ReactNode } from 'react'; import type { HTMLAttributes, PropsWithChildren, ReactNode } from 'react';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useCallback, useMemo, useRef, useState } from 'react';
import * as styles from './journal.css'; import * as styles from './journal.css';
@@ -44,30 +39,22 @@ const CountDisplay = ({
}; };
interface PageItemProps interface PageItemProps
extends Omit<HTMLAttributes<HTMLAnchorElement>, 'onClick'> { extends Omit<HTMLAttributes<HTMLAnchorElement>, 'onClick'> {
docRecord: DocRecord; docId: string;
right?: ReactNode; right?: ReactNode;
} }
const PageItem = ({ docRecord, right, className, ...attrs }: PageItemProps) => { const PageItem = ({ docId, right, className, ...attrs }: PageItemProps) => {
const mode = useLiveData(docRecord.primaryMode$); const t = useI18n();
const workspace = useService(WorkspaceService).workspace; const docDisplayMetaService = useService(DocDisplayMetaService);
const title = useDocCollectionPageTitle( const Icon = useLiveData(
workspace.docCollection, docDisplayMetaService.icon$(docId, { compareDate: new Date() })
docRecord.id
);
const { isJournal } = useJournalInfoHelper(
workspace.docCollection,
docRecord.id
); );
const titleMeta = useLiveData(docDisplayMetaService.title$(docId));
const title = typeof titleMeta === 'string' ? titleMeta : t[titleMeta.key]();
const Icon = isJournal
? TodayIcon
: mode === 'edgeless'
? EdgelessIcon
: PageIcon;
return ( return (
<WorkbenchLink <WorkbenchLink
aria-label={title} aria-label={title}
to={`/${docRecord.id}`} to={`/${docId}`}
className={clsx(className, styles.pageItem)} className={clsx(className, styles.pageItem)}
{...attrs} {...attrs}
> >
@@ -95,16 +82,8 @@ export const EditorJournalPanel = () => {
const t = useI18n(); const t = useI18n();
const doc = useService(DocService).doc; const doc = useService(DocService).doc;
const workspace = useService(WorkspaceService).workspace; const workspace = useService(WorkspaceService).workspace;
const { journalDate, isJournal } = useJournalInfoHelper( const { journalDate, isJournal } = useJournalInfoHelper(doc.id);
workspace.docCollection,
doc.id
);
const { openJournal } = useJournalRouteHelper(workspace.docCollection); const { openJournal } = useJournalRouteHelper(workspace.docCollection);
const [date, setDate] = useState(dayjs().format('YYYY-MM-DD'));
useEffect(() => {
journalDate && setDate(journalDate.format('YYYY-MM-DD'));
}, [journalDate]);
const onDateSelect = useCallback( const onDateSelect = useCallback(
(date: string) => { (date: string) => {
@@ -150,14 +129,18 @@ export const EditorJournalPanel = () => {
monthNames={t['com.affine.calendar-date-picker.month-names']()} monthNames={t['com.affine.calendar-date-picker.month-names']()}
todayLabel={t['com.affine.calendar-date-picker.today']()} todayLabel={t['com.affine.calendar-date-picker.today']()}
customDayRenderer={customDayRenderer} customDayRenderer={customDayRenderer}
value={date} value={journalDate?.format('YYYY-MM-DD')}
onChange={onDateSelect} onChange={onDateSelect}
monthHeaderCellClassName={styles.journalDateCellWrapper} monthHeaderCellClassName={styles.journalDateCellWrapper}
monthBodyCellClassName={styles.journalDateCellWrapper} monthBodyCellClassName={styles.journalDateCellWrapper}
/> />
</div> </div>
<JournalConflictBlock date={dayjs(date)} /> {journalDate ? (
<JournalDailyCountBlock date={dayjs(date)} /> <>
<JournalConflictBlock date={journalDate} />
<JournalDailyCountBlock date={journalDate} />
</>
) : null}
</div> </div>
); );
}; };
@@ -276,7 +259,7 @@ const JournalDailyCountBlock = ({ date }: JournalBlockProps) => {
<PageItem <PageItem
tabIndex={name === activeItem ? 0 : -1} tabIndex={name === activeItem ? 0 : -1}
key={index} key={index}
docRecord={pageRecord} docId={pageRecord.id}
/> />
))} ))}
</div> </div>
@@ -322,7 +305,7 @@ const ConflictList = ({
return ( return (
<PageItem <PageItem
aria-selected={isCurrent} aria-selected={isCurrent}
docRecord={docRecord} docId={docRecord.id}
key={docRecord.id} key={docRecord.id}
right={ right={
<Menu <Menu

View File

@@ -1,8 +1,8 @@
import { IconButton, MobileMenu } from '@affine/component'; import { IconButton, MobileMenu } from '@affine/component';
import { useJournalInfoHelper } from '@affine/core/components/hooks/use-journal'; import { useJournalInfoHelper } from '@affine/core/components/hooks/use-journal';
import { EditorJournalPanel } from '@affine/core/desktop/pages/workspace/detail-page/tabs/journal'; import { EditorJournalPanel } from '@affine/core/desktop/pages/workspace/detail-page/tabs/journal';
import { TodayIcon, TomorrowIcon, YesterdayIcon } from '@blocksuite/icons/rc'; import { DocDisplayMetaService } from '@affine/core/modules/doc-display-meta';
import { useService, WorkspaceService } from '@toeverything/infra'; import { useLiveData, useService } from '@toeverything/infra';
export const JournalIconButton = ({ export const JournalIconButton = ({
docId, docId,
@@ -11,18 +11,14 @@ export const JournalIconButton = ({
docId: string; docId: string;
className?: string; className?: string;
}) => { }) => {
const workspace = useService(WorkspaceService).workspace; const { isJournal } = useJournalInfoHelper(docId);
const { journalDate, isJournal } = useJournalInfoHelper(
workspace.docCollection, const docDisplayMetaService = useService(DocDisplayMetaService);
docId const Icon = useLiveData(
docDisplayMetaService.icon$(docId, {
compareDate: new Date(),
})
); );
const Icon = journalDate
? journalDate.isBefore(new Date(), 'day')
? YesterdayIcon
: journalDate.isAfter(new Date(), 'day')
? TomorrowIcon
: TodayIcon
: TodayIcon;
if (!isJournal) { if (!isJournal) {
return null; return null;

View File

@@ -0,0 +1,16 @@
import {
DocsService,
type Framework,
WorkspaceScope,
} from '@toeverything/infra';
import { WorkspacePropertiesAdapter } from '../properties';
import { DocDisplayMetaService } from './services/doc-display-meta';
export { DocDisplayMetaService };
export function configureDocDisplayMetaModule(framework: Framework) {
framework
.scope(WorkspaceScope)
.service(DocDisplayMetaService, [WorkspacePropertiesAdapter, DocsService]);
}

View File

@@ -0,0 +1,147 @@
import { i18nTime } from '@affine/i18n';
import {
BlockLinkIcon as LitBlockLinkIcon,
EdgelessIcon as LitEdgelessIcon,
LinkedEdgelessIcon as LitLinkedEdgelessIcon,
LinkedPageIcon as LitLinkedPageIcon,
PageIcon as LitPageIcon,
TodayIcon as LitTodayIcon,
TomorrowIcon as LitTomorrowIcon,
YesterdayIcon as LitYesterdayIcon,
} from '@blocksuite/icons/lit';
import {
BlockLinkIcon,
EdgelessIcon,
LinkedEdgelessIcon,
LinkedPageIcon,
PageIcon,
TodayIcon,
TomorrowIcon,
YesterdayIcon,
} from '@blocksuite/icons/rc';
import type { DocRecord, DocsService } from '@toeverything/infra';
import { LiveData, Service } from '@toeverything/infra';
import type { Dayjs } from 'dayjs';
import dayjs from 'dayjs';
import type { WorkspacePropertiesAdapter } from '../../properties';
type IconType = 'rc' | 'lit';
interface DocDisplayIconOptions<T extends IconType> {
type?: T;
compareDate?: Date | Dayjs;
/**
* Override the mode detected inside the hook:
* by default, it will use the `primaryMode$` of the doc.
*/
mode?: 'edgeless' | 'page';
reference?: boolean;
referenceToNode?: boolean;
}
const rcIcons = {
BlockLinkIcon,
EdgelessIcon,
LinkedEdgelessIcon,
LinkedPageIcon,
PageIcon,
TodayIcon,
TomorrowIcon,
YesterdayIcon,
};
const litIcons = {
BlockLinkIcon: LitBlockLinkIcon,
EdgelessIcon: LitEdgelessIcon,
LinkedEdgelessIcon: LitLinkedEdgelessIcon,
LinkedPageIcon: LitLinkedPageIcon,
PageIcon: LitPageIcon,
TodayIcon: LitTodayIcon,
TomorrowIcon: LitTomorrowIcon,
YesterdayIcon: LitYesterdayIcon,
};
const icons = { rc: rcIcons, lit: litIcons } as {
rc: Record<keyof typeof rcIcons, any>;
lit: Record<keyof typeof litIcons, any>;
};
export class DocDisplayMetaService extends Service {
constructor(
private readonly propertiesAdapter: WorkspacePropertiesAdapter,
private readonly docsService: DocsService
) {
super();
}
icon$<T extends IconType = 'rc'>(
docId: string,
options?: DocDisplayIconOptions<T>
): LiveData<T extends 'lit' ? typeof LitTodayIcon : typeof TodayIcon> {
const iconSet = icons[options?.type ?? 'rc'];
return LiveData.computed(get => {
const doc = get(this.docsService.list.doc$(docId));
const mode = doc ? get(doc.primaryMode$) : undefined;
const finalMode = options?.mode ?? mode ?? 'page';
const journalDate = this._toDayjs(
this.propertiesAdapter.getJournalPageDateString(docId)
);
if (journalDate) {
if (!options?.compareDate) return iconSet.TodayIcon;
const compareDate = dayjs(options?.compareDate);
return journalDate.isBefore(compareDate, 'day')
? iconSet.YesterdayIcon
: journalDate.isAfter(compareDate, 'day')
? iconSet.TomorrowIcon
: iconSet.TodayIcon;
}
return options?.reference
? options?.referenceToNode
? iconSet.BlockLinkIcon
: finalMode === 'edgeless'
? iconSet.LinkedEdgelessIcon
: iconSet.LinkedPageIcon
: finalMode === 'edgeless'
? iconSet.EdgelessIcon
: iconSet.PageIcon;
});
}
title$(docId: string, originalTitle?: string) {
return LiveData.computed(get => {
const doc = get(this.docsService.list.doc$(docId));
const docTitle = doc ? get(doc.title$) : undefined;
const journalDateString =
this.propertiesAdapter.getJournalPageDateString(docId);
return journalDateString
? i18nTime(journalDateString, { absolute: { accuracy: 'day' } })
: originalTitle ||
docTitle ||
({
key: 'Untitled',
} as const);
});
}
getDocDisplayMeta(docRecord: DocRecord, originalTitle?: string) {
return {
title: this.title$(docRecord.id, originalTitle).value,
icon: this.icon$(docRecord.id).value,
updatedDate: docRecord.meta$.value.updatedDate,
};
}
private _isJournalString(j?: string | false) {
return j ? !!j?.match(/^\d{4}-\d{2}-\d{2}$/) : false;
}
private _toDayjs(j?: string | false) {
if (!j || !this._isJournalString(j)) return null;
const day = dayjs(j);
if (!day.isValid()) return null;
return day;
}
}

View File

@@ -7,16 +7,11 @@ import {
} from '@affine/component'; } from '@affine/component';
import { InfoModal } from '@affine/core/components/affine/page-properties'; import { InfoModal } from '@affine/core/components/affine/page-properties';
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks'; import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
import { DocDisplayMetaService } from '@affine/core/modules/doc-display-meta';
import { DocsSearchService } from '@affine/core/modules/docs-search'; import { DocsSearchService } from '@affine/core/modules/docs-search';
import type { AffineDNDData } from '@affine/core/types/dnd'; import type { AffineDNDData } from '@affine/core/types/dnd';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
import { track } from '@affine/track'; import { track } from '@affine/track';
import {
EdgelessIcon,
LinkedEdgelessIcon,
LinkedPageIcon,
PageIcon,
} from '@blocksuite/icons/rc';
import { import {
DocsService, DocsService,
GlobalContextService, GlobalContextService,
@@ -46,35 +41,37 @@ export const ExplorerDocNode = ({
isLinked?: boolean; isLinked?: boolean;
} & GenericExplorerNode) => { } & GenericExplorerNode) => {
const t = useI18n(); const t = useI18n();
const { docsSearchService, docsService, globalContextService } = useServices({ const {
docsSearchService,
docsService,
globalContextService,
docDisplayMetaService,
} = useServices({
DocsSearchService, DocsSearchService,
DocsService, DocsService,
GlobalContextService, GlobalContextService,
DocDisplayMetaService,
}); });
// const pageInfoAdapter = useCurrentWorkspacePropertiesAdapter();
const active = const active =
useLiveData(globalContextService.globalContext.docId.$) === docId; useLiveData(globalContextService.globalContext.docId.$) === docId;
const [collapsed, setCollapsed] = useState(true); const [collapsed, setCollapsed] = useState(true);
const docRecord = useLiveData(docsService.list.doc$(docId)); const docRecord = useLiveData(docsService.list.doc$(docId));
const docPrimaryMode = useLiveData(docRecord?.primaryMode$); const DocIcon = useLiveData(
const docTitle = useLiveData(docRecord?.title$); docDisplayMetaService.icon$(docId, {
reference: isLinked,
})
);
const docTitle = useLiveData(docDisplayMetaService.title$(docId));
const isInTrash = useLiveData(docRecord?.trash$); const isInTrash = useLiveData(docRecord?.trash$);
const Icon = useCallback( const Icon = useCallback(
({ className }: { className?: string }) => { ({ className }: { className?: string }) => {
return isLinked ? ( return <DocIcon className={className} />;
docPrimaryMode === 'edgeless' ? (
<LinkedEdgelessIcon className={className} />
) : (
<LinkedPageIcon className={className} />
)
) : docPrimaryMode === 'edgeless' ? (
<EdgelessIcon className={className} />
) : (
<PageIcon className={className} />
);
}, },
[docPrimaryMode, isLinked] [DocIcon]
); );
const children = useLiveData( const children = useLiveData(
@@ -205,7 +202,7 @@ export const ExplorerDocNode = ({
<> <>
<ExplorerTreeNode <ExplorerTreeNode
icon={Icon} icon={Icon}
name={docTitle || t['Untitled']()} name={typeof docTitle === 'string' ? docTitle : t[docTitle.key]()}
dndData={dndData} dndData={dndData}
onDrop={handleDropOnDoc} onDrop={handleDropOnDoc}
renameable renameable

View File

@@ -4,6 +4,7 @@ import { configureInfraModules, type Framework } from '@toeverything/infra';
import { configureCloudModule } from './cloud'; import { configureCloudModule } from './cloud';
import { configureCollectionModule } from './collection'; import { configureCollectionModule } from './collection';
import { configureCreateWorkspaceModule } from './create-workspace'; import { configureCreateWorkspaceModule } from './create-workspace';
import { configureDocDisplayMetaModule } from './doc-display-meta';
import { configureDocLinksModule } from './doc-link'; import { configureDocLinksModule } from './doc-link';
import { configureDocsSearchModule } from './docs-search'; import { configureDocsSearchModule } from './docs-search';
import { configureEditorModule } from './editor'; import { configureEditorModule } from './editor';
@@ -40,6 +41,7 @@ export function configureCommonModules(framework: Framework) {
configureTelemetryModule(framework); configureTelemetryModule(framework);
configureFindInPageModule(framework); configureFindInPageModule(framework);
configurePeekViewModule(framework); configurePeekViewModule(framework);
configureDocDisplayMetaModule(framework);
configureQuickSearchModule(framework); configureQuickSearchModule(framework);
configureDocsSearchModule(framework); configureDocsSearchModule(framework);
configureDocLinksModule(framework); configureDocLinksModule(framework);

View File

@@ -9,9 +9,9 @@ import {
import { truncate } from 'lodash-es'; import { truncate } from 'lodash-es';
import { EMPTY, map, mergeMap, of, switchMap } from 'rxjs'; import { EMPTY, map, mergeMap, of, switchMap } from 'rxjs';
import type { DocDisplayMetaService } from '../../doc-display-meta';
import type { DocsSearchService } from '../../docs-search'; import type { DocsSearchService } from '../../docs-search';
import type { QuickSearchSession } from '../providers/quick-search-provider'; import type { QuickSearchSession } from '../providers/quick-search-provider';
import type { DocDisplayMetaService } from '../services/doc-display-meta';
import type { QuickSearchItem } from '../types/item'; import type { QuickSearchItem } from '../types/item';
interface DocsPayload { interface DocsPayload {

View File

@@ -4,10 +4,10 @@ import type { DocsService, WorkspaceService } from '@toeverything/infra';
import { Entity, LiveData } from '@toeverything/infra'; import { Entity, LiveData } from '@toeverything/infra';
import { omit, truncate } from 'lodash-es'; import { omit, truncate } from 'lodash-es';
import type { DocDisplayMetaService } from '../../doc-display-meta';
import { resolveLinkToDoc } from '../../navigation'; import { resolveLinkToDoc } from '../../navigation';
import { isLink } from '../../navigation/utils'; import { isLink } from '../../navigation/utils';
import type { QuickSearchSession } from '../providers/quick-search-provider'; import type { QuickSearchSession } from '../providers/quick-search-provider';
import type { DocDisplayMetaService } from '../services/doc-display-meta';
import type { QuickSearchItem } from '../types/item'; import type { QuickSearchItem } from '../types/item';
type LinkPayload = { docId: string } & ReferenceParams; type LinkPayload = { docId: string } & ReferenceParams;

View File

@@ -1,7 +1,7 @@
import { Entity, LiveData } from '@toeverything/infra'; import { Entity, LiveData } from '@toeverything/infra';
import type { DocDisplayMetaService } from '../../doc-display-meta';
import type { QuickSearchSession } from '../providers/quick-search-provider'; import type { QuickSearchSession } from '../providers/quick-search-provider';
import type { DocDisplayMetaService } from '../services/doc-display-meta';
import type { RecentDocsService } from '../services/recent-pages'; import type { RecentDocsService } from '../services/recent-pages';
import type { QuickSearchGroup } from '../types/group'; import type { QuickSearchGroup } from '../types/group';
import type { QuickSearchItem } from '../types/item'; import type { QuickSearchItem } from '../types/item';

View File

@@ -8,8 +8,8 @@ import {
} from '@toeverything/infra'; } from '@toeverything/infra';
import { CollectionService } from '../collection'; import { CollectionService } from '../collection';
import { DocDisplayMetaService } from '../doc-display-meta/services/doc-display-meta';
import { DocsSearchService } from '../docs-search'; import { DocsSearchService } from '../docs-search';
import { WorkspacePropertiesAdapter } from '../properties';
import { TagService } from '../tag'; import { TagService } from '../tag';
import { WorkbenchService } from '../workbench'; import { WorkbenchService } from '../workbench';
import { QuickSearch } from './entities/quick-search'; import { QuickSearch } from './entities/quick-search';
@@ -22,7 +22,6 @@ import { LinksQuickSearchSession } from './impls/links';
import { RecentDocsQuickSearchSession } from './impls/recent-docs'; import { RecentDocsQuickSearchSession } from './impls/recent-docs';
import { TagsQuickSearchSession } from './impls/tags'; import { TagsQuickSearchSession } from './impls/tags';
import { CMDKQuickSearchService } from './services/cmdk'; import { CMDKQuickSearchService } from './services/cmdk';
import { DocDisplayMetaService } from './services/doc-display-meta';
import { QuickSearchService } from './services/quick-search'; import { QuickSearchService } from './services/quick-search';
import { RecentDocsService } from './services/recent-pages'; import { RecentDocsService } from './services/recent-pages';
@@ -50,7 +49,6 @@ export function configureQuickSearchModule(framework: Framework) {
DocsService, DocsService,
]) ])
.service(RecentDocsService, [WorkspaceLocalState, DocsService]) .service(RecentDocsService, [WorkspaceLocalState, DocsService])
.service(DocDisplayMetaService, [WorkspacePropertiesAdapter])
.entity(QuickSearch) .entity(QuickSearch)
.entity(CommandsQuickSearchSession, [GlobalContextService]) .entity(CommandsQuickSearchSession, [GlobalContextService])
.entity(DocsQuickSearchSession, [ .entity(DocsQuickSearchSession, [

View File

@@ -1,37 +0,0 @@
import { i18nTime } from '@affine/i18n';
import { EdgelessIcon, PageIcon, TodayIcon } from '@blocksuite/icons/rc';
import type { DocRecord } from '@toeverything/infra';
import { Service } from '@toeverything/infra';
import type { WorkspacePropertiesAdapter } from '../../properties';
export class DocDisplayMetaService extends Service {
constructor(private readonly propertiesAdapter: WorkspacePropertiesAdapter) {
super();
}
getDocDisplayMeta(docRecord: DocRecord, originalTitle?: string) {
const journalDateString = this.propertiesAdapter.getJournalPageDateString(
docRecord.id
);
const icon = journalDateString
? TodayIcon
: docRecord.primaryMode$.value === 'edgeless'
? EdgelessIcon
: PageIcon;
const title = journalDateString
? i18nTime(journalDateString, { absolute: { accuracy: 'day' } })
: originalTitle ||
docRecord.meta$.value.title ||
({
key: 'Untitled',
} as const);
return {
title: title,
icon: icon,
updatedDate: docRecord.meta$.value.updatedDate,
};
}
}