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
@@ -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]);
}
@@ -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;
}
}
@@ -7,16 +7,11 @@ import {
} from '@affine/component';
import { InfoModal } from '@affine/core/components/affine/page-properties';
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 type { AffineDNDData } from '@affine/core/types/dnd';
import { useI18n } from '@affine/i18n';
import { track } from '@affine/track';
import {
EdgelessIcon,
LinkedEdgelessIcon,
LinkedPageIcon,
PageIcon,
} from '@blocksuite/icons/rc';
import {
DocsService,
GlobalContextService,
@@ -46,35 +41,37 @@ export const ExplorerDocNode = ({
isLinked?: boolean;
} & GenericExplorerNode) => {
const t = useI18n();
const { docsSearchService, docsService, globalContextService } = useServices({
const {
docsSearchService,
docsService,
globalContextService,
docDisplayMetaService,
} = useServices({
DocsSearchService,
DocsService,
GlobalContextService,
DocDisplayMetaService,
});
// const pageInfoAdapter = useCurrentWorkspacePropertiesAdapter();
const active =
useLiveData(globalContextService.globalContext.docId.$) === docId;
const [collapsed, setCollapsed] = useState(true);
const docRecord = useLiveData(docsService.list.doc$(docId));
const docPrimaryMode = useLiveData(docRecord?.primaryMode$);
const docTitle = useLiveData(docRecord?.title$);
const DocIcon = useLiveData(
docDisplayMetaService.icon$(docId, {
reference: isLinked,
})
);
const docTitle = useLiveData(docDisplayMetaService.title$(docId));
const isInTrash = useLiveData(docRecord?.trash$);
const Icon = useCallback(
({ className }: { className?: string }) => {
return isLinked ? (
docPrimaryMode === 'edgeless' ? (
<LinkedEdgelessIcon className={className} />
) : (
<LinkedPageIcon className={className} />
)
) : docPrimaryMode === 'edgeless' ? (
<EdgelessIcon className={className} />
) : (
<PageIcon className={className} />
);
return <DocIcon className={className} />;
},
[docPrimaryMode, isLinked]
[DocIcon]
);
const children = useLiveData(
@@ -205,7 +202,7 @@ export const ExplorerDocNode = ({
<>
<ExplorerTreeNode
icon={Icon}
name={docTitle || t['Untitled']()}
name={typeof docTitle === 'string' ? docTitle : t[docTitle.key]()}
dndData={dndData}
onDrop={handleDropOnDoc}
renameable
@@ -4,6 +4,7 @@ import { configureInfraModules, type Framework } from '@toeverything/infra';
import { configureCloudModule } from './cloud';
import { configureCollectionModule } from './collection';
import { configureCreateWorkspaceModule } from './create-workspace';
import { configureDocDisplayMetaModule } from './doc-display-meta';
import { configureDocLinksModule } from './doc-link';
import { configureDocsSearchModule } from './docs-search';
import { configureEditorModule } from './editor';
@@ -40,6 +41,7 @@ export function configureCommonModules(framework: Framework) {
configureTelemetryModule(framework);
configureFindInPageModule(framework);
configurePeekViewModule(framework);
configureDocDisplayMetaModule(framework);
configureQuickSearchModule(framework);
configureDocsSearchModule(framework);
configureDocLinksModule(framework);
@@ -9,9 +9,9 @@ import {
import { truncate } from 'lodash-es';
import { EMPTY, map, mergeMap, of, switchMap } from 'rxjs';
import type { DocDisplayMetaService } from '../../doc-display-meta';
import type { DocsSearchService } from '../../docs-search';
import type { QuickSearchSession } from '../providers/quick-search-provider';
import type { DocDisplayMetaService } from '../services/doc-display-meta';
import type { QuickSearchItem } from '../types/item';
interface DocsPayload {
@@ -4,10 +4,10 @@ import type { DocsService, WorkspaceService } from '@toeverything/infra';
import { Entity, LiveData } from '@toeverything/infra';
import { omit, truncate } from 'lodash-es';
import type { DocDisplayMetaService } from '../../doc-display-meta';
import { resolveLinkToDoc } from '../../navigation';
import { isLink } from '../../navigation/utils';
import type { QuickSearchSession } from '../providers/quick-search-provider';
import type { DocDisplayMetaService } from '../services/doc-display-meta';
import type { QuickSearchItem } from '../types/item';
type LinkPayload = { docId: string } & ReferenceParams;
@@ -1,7 +1,7 @@
import { Entity, LiveData } from '@toeverything/infra';
import type { DocDisplayMetaService } from '../../doc-display-meta';
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 { QuickSearchGroup } from '../types/group';
import type { QuickSearchItem } from '../types/item';
@@ -8,8 +8,8 @@ import {
} from '@toeverything/infra';
import { CollectionService } from '../collection';
import { DocDisplayMetaService } from '../doc-display-meta/services/doc-display-meta';
import { DocsSearchService } from '../docs-search';
import { WorkspacePropertiesAdapter } from '../properties';
import { TagService } from '../tag';
import { WorkbenchService } from '../workbench';
import { QuickSearch } from './entities/quick-search';
@@ -22,7 +22,6 @@ import { LinksQuickSearchSession } from './impls/links';
import { RecentDocsQuickSearchSession } from './impls/recent-docs';
import { TagsQuickSearchSession } from './impls/tags';
import { CMDKQuickSearchService } from './services/cmdk';
import { DocDisplayMetaService } from './services/doc-display-meta';
import { QuickSearchService } from './services/quick-search';
import { RecentDocsService } from './services/recent-pages';
@@ -50,7 +49,6 @@ export function configureQuickSearchModule(framework: Framework) {
DocsService,
])
.service(RecentDocsService, [WorkspaceLocalState, DocsService])
.service(DocDisplayMetaService, [WorkspacePropertiesAdapter])
.entity(QuickSearch)
.entity(CommandsQuickSearchSession, [GlobalContextService])
.entity(DocsQuickSearchSession, [
@@ -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,
};
}
}