mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-15 00:56:26 +08:00
feat(core): extract DocDisplayMetaService to resolve doc icon/title (#8226)
AF-1315
This commit is contained in:
@@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user