fundon
2024-12-19 01:02:27 +00:00
parent ef0f6e5993
commit def4dc854a
8 changed files with 132 additions and 40 deletions

View File

@@ -3,7 +3,6 @@ import { JournalService } from '@affine/core/modules/journal';
import { PeekViewService } from '@affine/core/modules/peek-view/services/peek-view'; import { PeekViewService } from '@affine/core/modules/peek-view/services/peek-view';
import { useInsidePeekView } from '@affine/core/modules/peek-view/view/modal-container'; import { useInsidePeekView } from '@affine/core/modules/peek-view/view/modal-container';
import { WorkbenchLink } from '@affine/core/modules/workbench'; import { WorkbenchLink } from '@affine/core/modules/workbench';
import { useI18n } from '@affine/i18n';
import { track } from '@affine/track'; import { track } from '@affine/track';
import type { DocMode } from '@blocksuite/affine/blocks'; import type { DocMode } from '@blocksuite/affine/blocks';
import type { DocCollection } from '@blocksuite/affine/store'; import type { DocCollection } from '@blocksuite/affine/store';
@@ -30,7 +29,7 @@ import * as styles from './styles.css';
interface AffinePageReferenceProps { interface AffinePageReferenceProps {
pageId: string; pageId: string;
params?: URLSearchParams; params?: URLSearchParams;
title?: string | null; // title alias title?: string; // title alias
className?: string; className?: string;
Icon?: ComponentType; Icon?: ComponentType;
onClick?: (e: MouseEvent) => void; onClick?: (e: MouseEvent) => void;
@@ -44,7 +43,6 @@ function AffinePageReferenceInner({
}: AffinePageReferenceProps) { }: AffinePageReferenceProps) {
const docDisplayMetaService = useService(DocDisplayMetaService); const docDisplayMetaService = useService(DocDisplayMetaService);
const docsService = useService(DocsService); const docsService = useService(DocsService);
const i18n = useI18n();
let referenceWithMode: DocMode | null = null; let referenceWithMode: DocMode | null = null;
let referenceToNode = false; let referenceToNode = false;
@@ -74,18 +72,10 @@ function AffinePageReferenceInner({
const notFound = !useLiveData(docsService.list.doc$(pageId)); const notFound = !useLiveData(docsService.list.doc$(pageId));
const docTitle = useLiveData( title = useLiveData(
docDisplayMetaService.title$(pageId, { reference: true }) docDisplayMetaService.title$(pageId, { title, reference: true })
); );
if (notFound) {
title = i18n.t('com.affine.notFoundPage.title');
}
if (!title) {
title = i18n.t(docTitle);
}
return ( return (
<span className={notFound ? styles.notFound : ''}> <span className={notFound ? styles.notFound : ''}>
<Icon className={styles.pageReferenceIcon} /> <Icon className={styles.pageReferenceIcon} />

View File

@@ -2,6 +2,7 @@ import {
AIEdgelessRootBlockSpec, AIEdgelessRootBlockSpec,
AIPageRootBlockSpec, AIPageRootBlockSpec,
} from '@affine/core/blocksuite/presets/ai'; } from '@affine/core/blocksuite/presets/ai';
import { DocDisplayMetaService } from '@affine/core/modules/doc-display-meta';
import { EditorSettingService } from '@affine/core/modules/editor-setting'; import { EditorSettingService } from '@affine/core/modules/editor-setting';
import { AppThemeService } from '@affine/core/modules/theme'; import { AppThemeService } from '@affine/core/modules/theme';
import { mixpanel } from '@affine/track'; import { mixpanel } from '@affine/track';
@@ -12,12 +13,15 @@ import {
StdIdentifier, StdIdentifier,
} from '@blocksuite/affine/block-std'; } from '@blocksuite/affine/block-std';
import type { import type {
DocDisplayMetaExtension,
DocDisplayMetaParams,
RootBlockConfig, RootBlockConfig,
TelemetryEventMap, TelemetryEventMap,
ThemeExtension, ThemeExtension,
} from '@blocksuite/affine/blocks'; } from '@blocksuite/affine/blocks';
import { import {
ColorScheme, ColorScheme,
DocDisplayMetaProvider,
EdgelessBuiltInManager, EdgelessBuiltInManager,
EdgelessRootBlockSpec, EdgelessRootBlockSpec,
EdgelessToolExtension, EdgelessToolExtension,
@@ -29,16 +33,19 @@ import {
} from '@blocksuite/affine/blocks'; } from '@blocksuite/affine/blocks';
import { import {
createSignalFromObservable, createSignalFromObservable,
referenceToNode,
type Signal, type Signal,
SpecProvider, SpecProvider,
} from '@blocksuite/affine-shared/utils'; } from '@blocksuite/affine-shared/utils';
import type { Container } from '@blocksuite/global/di'; import type { Container } from '@blocksuite/global/di';
import { LinkedPageIcon, PageIcon } from '@blocksuite/icons/lit';
import { import {
DocService, DocService,
DocsService, DocsService,
FeatureFlagService, FeatureFlagService,
type FrameworkProvider, type FrameworkProvider,
} from '@toeverything/infra'; } from '@toeverything/infra';
import type { TemplateResult } from 'lit';
import type { Observable } from 'rxjs'; import type { Observable } from 'rxjs';
import { combineLatest, map } from 'rxjs'; import { combineLatest, map } from 'rxjs';
@@ -141,6 +148,88 @@ function getThemeExtension(framework: FrameworkProvider) {
return AffineThemeExtension; return AffineThemeExtension;
} }
export function buildDocDisplayMetaExtension(framework: FrameworkProvider) {
const docDisplayMetaService = framework.get(DocDisplayMetaService);
function iconBuilder(
icon: typeof PageIcon,
size = '1.25em',
style = 'user-select:none;flex-shrink:0;vertical-align:middle;font-size:inherit;margin-bottom:0.1em;'
) {
return icon({
width: size,
height: size,
style,
});
}
class AffineDocDisplayMetaService
extends LifeCycleWatcher
implements DocDisplayMetaExtension
{
static override key = 'doc-display-meta';
readonly disposables: (() => void)[] = [];
static override setup(di: Container) {
super.setup(di);
di.override(DocDisplayMetaProvider, this, [StdIdentifier]);
}
dispose() {
while (this.disposables.length > 0) {
this.disposables.pop()?.();
}
}
icon(
docId: string,
{ params, title, referenced }: DocDisplayMetaParams = {}
): Signal<TemplateResult> {
const icon$ = docDisplayMetaService
.icon$(docId, {
type: 'lit',
reference: referenced,
hasTitleAlias: Boolean(title),
referenceToNode: referenceToNode({ pageId: docId, params }),
})
.map(iconBuilder);
const { signal: iconSignal, cleanup } = createSignalFromObservable(
icon$,
iconBuilder(referenced ? LinkedPageIcon : PageIcon)
);
this.disposables.push(cleanup);
return iconSignal;
}
title(
docId: string,
{ title = '', referenced }: DocDisplayMetaParams = {}
): Signal<string> {
const title$ = docDisplayMetaService.title$(docId, {
title,
reference: referenced,
});
const { signal: titleSignal, cleanup } =
createSignalFromObservable<string>(title$, title);
this.disposables.push(cleanup);
return titleSignal;
}
override unmounted() {
this.dispose();
}
}
return AffineDocDisplayMetaService;
}
function getEditorConfigExtension( function getEditorConfigExtension(
framework: FrameworkProvider framework: FrameworkProvider
): ExtensionType[] { ): ExtensionType[] {
@@ -184,6 +273,7 @@ export function createPageRootBlockSpec(
getFontConfigExtension(), getFontConfigExtension(),
getTelemetryExtension(), getTelemetryExtension(),
getEditorConfigExtension(framework), getEditorConfigExtension(framework),
buildDocDisplayMetaExtension(framework),
].flat(); ].flat();
} }
@@ -201,5 +291,6 @@ export function createEdgelessRootBlockSpec(
getFontConfigExtension(), getFontConfigExtension(),
getTelemetryExtension(), getTelemetryExtension(),
getEditorConfigExtension(framework), getEditorConfigExtension(framework),
buildDocDisplayMetaExtension(framework),
].flat(); ].flat();
} }

View File

@@ -312,7 +312,6 @@ export function patchDocModeService(
return (mode || DEFAULT_MODE) as DocMode; return (mode || DEFAULT_MODE) as DocMode;
}; };
onPrimaryModeChange = (handler: (mode: DocMode) => void, id?: string) => { onPrimaryModeChange = (handler: (mode: DocMode) => void, id?: string) => {
// eslint-disable-next-line rxjs/finnish
const mode$ = id const mode$ = id
? docsService.list.primaryMode$(id) ? docsService.list.primaryMode$(id)
: docService.doc.primaryMode$; : docService.doc.primaryMode$;

View File

@@ -8,7 +8,6 @@ import {
WorkbenchLink, WorkbenchLink,
type WorkbenchLinkProps, type WorkbenchLinkProps,
} from '@affine/core/modules/workbench'; } from '@affine/core/modules/workbench';
import { useI18n } from '@affine/i18n';
import type { DocMeta } from '@blocksuite/affine/store'; import type { DocMeta } from '@blocksuite/affine/store';
import { useLiveData, useService } from '@toeverything/infra'; import { useLiveData, useService } from '@toeverything/infra';
import clsx from 'clsx'; import clsx from 'clsx';
@@ -48,13 +47,9 @@ export const DocCard = forwardRef<HTMLAnchorElement, DocCardProps>(
outerRef outerRef
) { ) {
const containerRef = useRef<HTMLAnchorElement | null>(null); const containerRef = useRef<HTMLAnchorElement | null>(null);
const t = useI18n();
const favAdapter = useService(CompatibleFavoriteItemsAdapter); const favAdapter = useService(CompatibleFavoriteItemsAdapter);
const docDisplayService = useService(DocDisplayMetaService); const docDisplayService = useService(DocDisplayMetaService);
const titleInfo = useLiveData(docDisplayService.title$(meta.id)); const title = useLiveData(docDisplayService.title$(meta.id));
const title =
typeof titleInfo === 'string' ? titleInfo : t[titleInfo.i18nKey]();
const favorited = useLiveData(favAdapter.isFavorite$(meta.id, 'doc')); const favorited = useLiveData(favAdapter.isFavorite$(meta.id, 'doc'));
const toggleFavorite = useCatchEventCallback( const toggleFavorite = useCatchEventCallback(

View File

@@ -19,11 +19,9 @@ const DocIcon = ({ docId }: { docId: string }) => {
}; };
const DocLabel = ({ docId }: { docId: string }) => { const DocLabel = ({ docId }: { docId: string }) => {
const t = useI18n();
const docDisplayMetaService = useService(DocDisplayMetaService); const docDisplayMetaService = useService(DocDisplayMetaService);
const label = useLiveData(docDisplayMetaService.title$(docId)); const label = useLiveData(docDisplayMetaService.title$(docId));
return label;
return typeof label === 'string' ? label : t[label.i18nKey]();
}; };
export const DocSelectorDialog = ({ export const DocSelectorDialog = ({

View File

@@ -15,7 +15,7 @@ import { EditorService } from '@affine/core/modules/editor';
import { JournalService } from '@affine/core/modules/journal'; import { JournalService } from '@affine/core/modules/journal';
import { WorkbenchService } from '@affine/core/modules/workbench'; import { WorkbenchService } from '@affine/core/modules/workbench';
import { ViewService } from '@affine/core/modules/workbench/services/view'; import { ViewService } from '@affine/core/modules/workbench/services/view';
import { i18nTime, useI18n } from '@affine/i18n'; import { i18nTime } from '@affine/i18n';
import { import {
BookmarkBlockService, BookmarkBlockService,
customImageProxyMiddleware, customImageProxyMiddleware,
@@ -240,14 +240,11 @@ const MobileDetailPage = ({
pageId: string; pageId: string;
date?: string; date?: string;
}) => { }) => {
const t = useI18n();
const docDisplayMetaService = useService(DocDisplayMetaService); const docDisplayMetaService = useService(DocDisplayMetaService);
const journalService = useService(JournalService); const journalService = useService(JournalService);
const workbench = useService(WorkbenchService).workbench; const workbench = useService(WorkbenchService).workbench;
const [showTitle, setShowTitle] = useState(checkShowTitle); const [showTitle, setShowTitle] = useState(checkShowTitle);
const titleInfo = useLiveData(docDisplayMetaService.title$(pageId)); const title = useLiveData(docDisplayMetaService.title$(pageId));
const title =
typeof titleInfo === 'string' ? titleInfo : t[titleInfo.i18nKey]();
const allJournalDates = useLiveData(journalService.allJournalDates$); const allJournalDates = useLiveData(journalService.allJournalDates$);

View File

@@ -5,6 +5,7 @@ import {
WorkspaceScope, WorkspaceScope,
} from '@toeverything/infra'; } from '@toeverything/infra';
import { I18nService } from '../i18n';
import { JournalService } from '../journal'; import { JournalService } from '../journal';
import { DocDisplayMetaService } from './services/doc-display-meta'; import { DocDisplayMetaService } from './services/doc-display-meta';
@@ -17,5 +18,6 @@ export function configureDocDisplayMetaModule(framework: Framework) {
JournalService, JournalService,
DocsService, DocsService,
FeatureFlagService, FeatureFlagService,
I18nService,
]); ]);
} }

View File

@@ -31,6 +31,7 @@ import { LiveData, Service } from '@toeverything/infra';
import type { Dayjs } from 'dayjs'; import type { Dayjs } from 'dayjs';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import type { I18nService } from '../../i18n';
import type { JournalService } from '../../journal'; import type { JournalService } from '../../journal';
type IconType = 'rc' | 'lit'; type IconType = 'rc' | 'lit';
@@ -52,6 +53,7 @@ interface DocDisplayIconOptions<T extends IconType> {
} }
interface DocDisplayTitleOptions { interface DocDisplayTitleOptions {
originalTitle?: string; originalTitle?: string;
title?: string; // title alias
reference?: boolean; reference?: boolean;
/** /**
* @default true * @default true
@@ -90,7 +92,8 @@ export class DocDisplayMetaService extends Service {
constructor( constructor(
private readonly journalService: JournalService, private readonly journalService: JournalService,
private readonly docsService: DocsService, private readonly docsService: DocsService,
private readonly featureFlagService: FeatureFlagService private readonly featureFlagService: FeatureFlagService,
private readonly i18nService: I18nService
) { ) {
super(); super();
} }
@@ -149,7 +152,7 @@ export class DocDisplayMetaService extends Service {
// journal icon // journal icon
const journalDate = this._toDayjs( const journalDate = this._toDayjs(
this.journalService.journalDate$(docId).value get(this.journalService.journalDate$(docId))
); );
if (journalDate) { if (journalDate) {
return this.getJournalIcon(journalDate, options); return this.getJournalIcon(journalDate, options);
@@ -178,31 +181,48 @@ export class DocDisplayMetaService extends Service {
title$(docId: string, options?: DocDisplayTitleOptions) { title$(docId: string, options?: DocDisplayTitleOptions) {
return LiveData.computed(get => { return LiveData.computed(get => {
const enableEmojiIcon =
get(this.featureFlagService.flags.enable_emoji_doc_icon.$) &&
options?.enableEmojiIcon !== false;
const lng = get(this.i18nService.i18n.currentLanguageKey$);
const doc = get(this.docsService.list.doc$(docId)); const doc = get(this.docsService.list.doc$(docId));
const docTitle = doc ? get(doc.title$) : undefined;
// title alias
if (options?.title) {
return enableEmojiIcon
? extractEmojiIcon(options.title).rest
: options.title;
}
if (!doc) {
return this.i18nService.i18n.i18next.t(
'com.affine.notFoundPage.title',
{ lng }
);
}
// journal title
const journalDateString = get(this.journalService.journalDate$(docId)); const journalDateString = get(this.journalService.journalDate$(docId));
// journal
if (journalDateString) { if (journalDateString) {
return i18nTime(journalDateString, { absolute: { accuracy: 'day' } }); return i18nTime(journalDateString, { absolute: { accuracy: 'day' } });
} }
// original title
if (options?.originalTitle) return options.originalTitle; if (options?.originalTitle) return options.originalTitle;
const docTitle = get(doc.title$);
// empty title // empty title
if (!docTitle) return { i18nKey: 'Untitled' } as const; if (!docTitle) {
return this.i18nService.i18n.i18next.t('Untitled', { lng });
}
// reference // reference
if (options?.reference) return docTitle; if (options?.reference) return docTitle;
// check emoji // emoji icon
const enableEmojiIcon =
get(this.featureFlagService.flags.enable_emoji_doc_icon.$) &&
options?.enableEmojiIcon !== false;
if (enableEmojiIcon) { if (enableEmojiIcon) {
const { rest } = extractEmojiIcon(docTitle); return extractEmojiIcon(docTitle).rest;
return rest;
} }
// default // default