refactor(editor): add cache extension for link preview service (#12196)

Closes: [BS-2578](https://linear.app/affine-design/issue/BS-2578/优化-footnote-预览的逻辑:支持缓存结果,避免重复-loading)

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
  - Introduced a link preview caching mechanism, enabling faster and more efficient reuse of link preview data across the app.
  - Added a feature flag for enabling or disabling link preview cache, configurable through workspace experimental settings.
  - Enhanced localization with new entries describing the link preview cache feature.

- **Improvements**
  - Updated link preview service architecture for better extensibility and maintainability.
  - Improved integration of feature flags throughout chat and rendering components.

- **Bug Fixes**
  - Fixed tooltip formatting for footnote URLs.

- **Chores**
  - Updated dependencies and localization completeness tracking.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
donteatfriedrice
2025-05-13 05:11:33 +00:00
parent cfe7b7cf29
commit 0d518adc5b
42 changed files with 830 additions and 169 deletions

View File

@@ -150,6 +150,20 @@ export class AffineFootnoteNode extends WithDisposable(ShadowlessElement) {
window.open(url, '_blank');
};
private readonly _updateFootnoteAttributes = (footnote: FootNote) => {
if (!this.footnote || this.readonly) {
return;
}
if (!this.inlineEditor || !this.selfInlineRange) {
return;
}
this.inlineEditor.formatText(this.selfInlineRange, {
footnote: footnote,
});
};
private readonly _FootNoteDefaultContent = (footnote: FootNote) => {
return html`<span
class="footnote-content-default"
@@ -169,6 +183,7 @@ export class AffineFootnoteNode extends WithDisposable(ShadowlessElement) {
.std=${this.std}
.abortController=${abortController}
.onPopupClick=${this.onPopupClick ?? this.onFootnoteClick}
.updateFootnoteAttributes=${this._updateFootnoteAttributes}
></footnote-popup>`;
};

View File

@@ -7,7 +7,7 @@ import type { FootNote } from '@blocksuite/affine-model';
import { ImageProxyService } from '@blocksuite/affine-shared/adapters';
import {
DocDisplayMetaProvider,
LinkPreviewerService,
LinkPreviewServiceIdentifier,
ThemeProvider,
} from '@blocksuite/affine-shared/services';
import { unsafeCSSVar, unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme';
@@ -121,7 +121,7 @@ export class FootNotePopup extends SignalWatcher(WithDisposable(LitElement)) {
if (referenceType === 'url') {
const title = this._linkPreview$.value?.title;
const url = this.footnote.reference.url;
return [title, url].filter(Boolean).join(' ') || '';
return [title, url].filter(Boolean).join('\n') || '';
}
return this._popupLabel$.value;
});
@@ -160,15 +160,29 @@ export class FootNotePopup extends SignalWatcher(WithDisposable(LitElement)) {
isTitleAndDescriptionEmpty
) {
this._isLoading$.value = true;
this.std.store
.get(LinkPreviewerService)
this.std
.get(LinkPreviewServiceIdentifier)
.query(this.footnote.reference.url)
.then(data => {
// update the local link preview data
this._linkPreview$.value = {
favicon: data.icon ?? undefined,
title: data.title ?? undefined,
description: data.description ?? undefined,
};
// update the footnote attributes in the node with the link preview data
// to avoid fetching the same data multiple times
const footnote: FootNote = {
...this.footnote,
reference: {
...this.footnote.reference,
...(data.icon && { favicon: data.icon }),
...(data.title && { title: data.title }),
...(data.description && { description: data.description }),
},
};
this.updateFootnoteAttributes(footnote);
})
.catch(console.error)
.finally(() => {
@@ -209,4 +223,7 @@ export class FootNotePopup extends SignalWatcher(WithDisposable(LitElement)) {
@property({ attribute: false })
accessor onPopupClick: FootNotePopupClickHandler | (() => void) = () => {};
@property({ attribute: false })
accessor updateFootnoteAttributes: (footnote: FootNote) => void = () => {};
}