refactor(editor): refactor linkPreviewer as an extension and remove bookmark service (#9754)

[BS-2427](https://linear.app/affine-design/issue/BS-2427/移除-bookmark-block-service) [BS-2418](https://linear.app/affine-design/issue/BS-2418/linkpreviewer-重构成插件)
This commit is contained in:
donteatfriedrice
2025-01-20 04:18:00 +00:00
parent cc2958203b
commit 4bd43a698c
17 changed files with 42 additions and 105 deletions
@@ -10,15 +10,11 @@ import { property, query } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import { type StyleInfo, styleMap } from 'lit/directives/style-map.js';
import type { BookmarkBlockService } from './bookmark-service.js';
import { refreshBookmarkUrlData } from './utils.js';
export const BOOKMARK_MIN_WIDTH = 450;
export class BookmarkBlockComponent extends CaptionedBlockComponent<
BookmarkBlockModel,
BookmarkBlockService
> {
export class BookmarkBlockComponent extends CaptionedBlockComponent<BookmarkBlockModel> {
private _fetchAbortController?: AbortController;
blockDraggable = true;
@@ -1,16 +0,0 @@
import { LinkPreviewer } from '@blocksuite/affine-block-embed';
import { BookmarkBlockSchema } from '@blocksuite/affine-model';
import { BlockService } from '@blocksuite/block-std';
export class BookmarkBlockService extends BlockService {
static override readonly flavour = BookmarkBlockSchema.model.flavour;
private static readonly linkPreviewer = new LinkPreviewer();
static setLinkPreviewEndpoint =
BookmarkBlockService.linkPreviewer.setEndpoint;
queryUrlData = (url: string, signal?: AbortSignal) => {
return BookmarkBlockService.linkPreviewer.query(url, signal);
};
}
@@ -7,12 +7,10 @@ import type { ExtensionType } from '@blocksuite/store';
import { literal } from 'lit/static-html.js';
import { BookmarkBlockAdapterExtensions } from './adapters/extension.js';
import { BookmarkBlockService } from './bookmark-service.js';
import { commands } from './commands/index.js';
export const BookmarkBlockSpec: ExtensionType[] = [
FlavourExtension('affine:bookmark'),
BookmarkBlockService,
CommandExtension(commands),
BlockViewExtension('affine:bookmark', model => {
return model.parent?.flavour === 'affine:surface'
@@ -1,6 +1,5 @@
import { BookmarkBlockComponent } from './bookmark-block';
import { BookmarkEdgelessBlockComponent } from './bookmark-edgeless-block';
import type { BookmarkBlockService } from './bookmark-service';
import type { insertBookmarkCommand } from './commands/insert-bookmark';
import type { insertLinkByQuickSearchCommand } from './commands/insert-link-by-quick-search';
import { BookmarkCard } from './components/bookmark-card';
@@ -32,8 +31,5 @@ declare global {
insertBookmark: typeof insertBookmarkCommand;
insertLinkByQuickSearch: typeof insertLinkByQuickSearchCommand;
}
interface BlockServices {
'affine:bookmark': BookmarkBlockService;
}
}
}
@@ -1,5 +1,4 @@
export * from './adapters';
export * from './bookmark-block';
export * from './bookmark-service';
export * from './bookmark-spec';
export * from './components';
@@ -1,5 +1,5 @@
import { LinkPreviewerService } from '@blocksuite/affine-shared/services';
import { isAbortError } from '@blocksuite/affine-shared/utils';
import { assertExists } from '@blocksuite/global/utils';
import type { BookmarkBlockComponent } from './bookmark-block.js';
@@ -15,10 +15,8 @@ export async function refreshBookmarkUrlData(
try {
bookmarkElement.loading = true;
const queryUrlData = bookmarkElement.service?.queryUrlData;
assertExists(queryUrlData);
const bookmarkUrlData = await queryUrlData(
const linkPreviewer = bookmarkElement.doc.get(LinkPreviewerService);
const bookmarkUrlData = await linkPreviewer.query(
bookmarkElement.model.url,
signal
);
@@ -3,21 +3,18 @@ import {
type EmbedGithubModel,
EmbedGithubStyles,
} from '@blocksuite/affine-model';
import { EmbedOptionProvider } from '@blocksuite/affine-shared/services';
import {
EmbedOptionProvider,
LinkPreviewerService,
} from '@blocksuite/affine-shared/services';
import { BlockService } from '@blocksuite/block-std';
import { LinkPreviewer } from '../common/link-previewer.js';
import { githubUrlRegex } from './embed-github-model.js';
import { queryEmbedGithubApiData, queryEmbedGithubData } from './utils.js';
export class EmbedGithubBlockService extends BlockService {
static override readonly flavour = EmbedGithubBlockSchema.model.flavour;
private static readonly linkPreviewer = new LinkPreviewer();
static setLinkPreviewEndpoint =
EmbedGithubBlockService.linkPreviewer.setEndpoint;
queryApiData = (embedGithubModel: EmbedGithubModel, signal?: AbortSignal) => {
return queryEmbedGithubApiData(embedGithubModel, signal);
};
@@ -25,7 +22,7 @@ export class EmbedGithubBlockService extends BlockService {
queryUrlData = (embedGithubModel: EmbedGithubModel, signal?: AbortSignal) => {
return queryEmbedGithubData(
embedGithubModel,
EmbedGithubBlockService.linkPreviewer,
this.doc.get(LinkPreviewerService),
signal
);
};
@@ -2,11 +2,11 @@ import type {
EmbedGithubBlockUrlData,
EmbedGithubModel,
} from '@blocksuite/affine-model';
import type { LinkPreviewerService } from '@blocksuite/affine-shared/services';
import { isAbortError } from '@blocksuite/affine-shared/utils';
import { assertExists } from '@blocksuite/global/utils';
import { nothing } from 'lit';
import type { LinkPreviewer } from '../common/link-previewer.js';
import type { EmbedGithubBlockComponent } from './embed-github-block.js';
import {
GithubIssueClosedFailureIcon,
@@ -20,7 +20,7 @@ import {
export async function queryEmbedGithubData(
embedGithubModel: EmbedGithubModel,
linkPreviewer: LinkPreviewer,
linkPreviewer: LinkPreviewerService,
signal?: AbortSignal
): Promise<Partial<EmbedGithubBlockUrlData>> {
const [githubApiData, openGraphData] = await Promise.all([
@@ -6,18 +6,12 @@ import {
import { EmbedOptionProvider } from '@blocksuite/affine-shared/services';
import { BlockService } from '@blocksuite/block-std';
import { LinkPreviewer } from '../common/link-previewer.js';
import { loomUrlRegex } from './embed-loom-model.js';
import { queryEmbedLoomData } from './utils.js';
export class EmbedLoomBlockService extends BlockService {
static override readonly flavour = EmbedLoomBlockSchema.model.flavour;
private static readonly linkPreviewer = new LinkPreviewer();
static setLinkPreviewEndpoint =
EmbedLoomBlockService.linkPreviewer.setEndpoint;
queryUrlData = (embedLoomModel: EmbedLoomModel, signal?: AbortSignal) => {
return queryEmbedLoomData(embedLoomModel, signal);
};
@@ -3,28 +3,25 @@ import {
type EmbedYoutubeModel,
EmbedYoutubeStyles,
} from '@blocksuite/affine-model';
import { EmbedOptionProvider } from '@blocksuite/affine-shared/services';
import {
EmbedOptionProvider,
LinkPreviewerService,
} from '@blocksuite/affine-shared/services';
import { BlockService } from '@blocksuite/block-std';
import { LinkPreviewer } from '../common/link-previewer.js';
import { youtubeUrlRegex } from './embed-youtube-model.js';
import { queryEmbedYoutubeData } from './utils.js';
export class EmbedYoutubeBlockService extends BlockService {
static override readonly flavour = EmbedYoutubeBlockSchema.model.flavour;
private static readonly linkPreviewer = new LinkPreviewer();
static setLinkPreviewEndpoint =
EmbedYoutubeBlockService.linkPreviewer.setEndpoint;
queryUrlData = (
embedYoutubeModel: EmbedYoutubeModel,
signal?: AbortSignal
) => {
return queryEmbedYoutubeData(
embedYoutubeModel,
EmbedYoutubeBlockService.linkPreviewer,
this.doc.get(LinkPreviewerService),
signal
);
};
@@ -2,15 +2,15 @@ import type {
EmbedYoutubeBlockUrlData,
EmbedYoutubeModel,
} from '@blocksuite/affine-model';
import type { LinkPreviewerService } from '@blocksuite/affine-shared/services';
import { isAbortError } from '@blocksuite/affine-shared/utils';
import { assertExists } from '@blocksuite/global/utils';
import type { LinkPreviewer } from '../common/link-previewer.js';
import type { EmbedYoutubeBlockComponent } from './embed-youtube-block.js';
export async function queryEmbedYoutubeData(
embedYoutubeModel: EmbedYoutubeModel,
linkPreviewer: LinkPreviewer,
linkPreviewer: LinkPreviewerService,
signal?: AbortSignal
): Promise<Partial<EmbedYoutubeBlockUrlData>> {
const url = embedYoutubeModel.url;
@@ -23,10 +23,6 @@ export { createEmbedBlockMarkdownAdapterMatcher } from './common/adapters/markdo
export { createEmbedBlockPlainTextAdapterMatcher } from './common/adapters/plain-text';
export { EmbedBlockComponent } from './common/embed-block-element';
export { insertEmbedCard } from './common/insert-embed-card.js';
export {
LinkPreviewer,
type LinkPreviewResponseData,
} from './common/link-previewer.js';
export * from './common/render-linked-doc';
export { toEdgelessEmbedBlock } from './common/to-edgeless-embed-block';
export * from './common/utils';
@@ -7,6 +7,7 @@ export * from './embed-option-service';
export * from './feature-flag-service';
export * from './font-loader';
export * from './generate-url-service';
export * from './link-previewer-service';
export * from './native-clipboard-service';
export * from './notification-service';
export * from './open-doc-config';
@@ -1,7 +1,9 @@
import type { LinkPreviewData } from '@blocksuite/affine-model';
import { DEFAULT_LINK_PREVIEW_ENDPOINT } from '@blocksuite/affine-shared/consts';
import { isAbortError } from '@blocksuite/affine-shared/utils';
import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions';
import { StoreExtension } from '@blocksuite/store';
import { DEFAULT_LINK_PREVIEW_ENDPOINT } from '../consts';
import { isAbortError } from '../utils/is-abort-error';
export type LinkPreviewResponseData = {
url: string;
@@ -16,7 +18,9 @@ export type LinkPreviewResponseData = {
favicons?: string[];
};
export class LinkPreviewer {
export class LinkPreviewerService extends StoreExtension {
static override key = 'link-previewer';
private _endpoint = DEFAULT_LINK_PREVIEW_ENDPOINT;
query = async (
@@ -77,23 +81,19 @@ export class LinkPreviewer {
const data: LinkPreviewResponseData = await response.json();
return {
title: data.title ? this._getStringFromHTML(data.title) : null,
description: data.description
? this._getStringFromHTML(data.description)
: null,
title: data.title ?? null,
description: data.description ?? null,
icon: data.favicons?.[0],
image: data.images?.[0],
};
}
};
get endpoint() {
return this._endpoint;
}
setEndpoint = (endpoint: string) => {
this._endpoint = endpoint;
};
private _getStringFromHTML(html: string) {
const div = document.createElement('div');
div.innerHTML = html;
return div.textContent;
}
}
+2
View File
@@ -40,6 +40,7 @@ import {
EditPropsStore,
FeatureFlagService,
FontLoaderService,
LinkPreviewerService,
} from '@blocksuite/affine-shared/services';
import {
BlockSelectionExtension,
@@ -99,4 +100,5 @@ export const StoreExtensions: ExtensionType[] = [
HighlightSelectionExtension,
ImageSelectionExtension,
DatabaseSelectionExtension,
LinkPreviewerService,
];
@@ -1,13 +1,10 @@
import { useRefEffect } from '@affine/component';
import { EditorLoading } from '@affine/component/page-detail-skeleton';
import {
BookmarkBlockService,
customImageProxyMiddleware,
type DocMode,
EmbedGithubBlockService,
EmbedLoomBlockService,
EmbedYoutubeBlockService,
ImageBlockService,
LinkPreviewerService,
} from '@blocksuite/affine/blocks';
import { DisposableGroup } from '@blocksuite/affine/global/utils';
import type { AffineEditorContainer } from '@blocksuite/affine/presets';
@@ -71,19 +68,9 @@ const BlockSuiteEditorImpl = ({
);
ImageBlockService.setImageProxyURL(BUILD_CONFIG.imageProxyUrl);
// provide link preview endpoint to blocksuite
BookmarkBlockService.setLinkPreviewEndpoint(
BUILD_CONFIG.linkPreviewUrl
);
EmbedGithubBlockService.setLinkPreviewEndpoint(
BUILD_CONFIG.linkPreviewUrl
);
EmbedYoutubeBlockService.setLinkPreviewEndpoint(
BUILD_CONFIG.linkPreviewUrl
);
EmbedLoomBlockService.setLinkPreviewEndpoint(
BUILD_CONFIG.linkPreviewUrl
);
editor.host?.doc
.get(LinkPreviewerService)
.setEndpoint(BUILD_CONFIG.linkPreviewUrl);
return editor.host?.updateComplete;
})
@@ -22,12 +22,9 @@ import { ViewService } from '@affine/core/modules/workbench/services/view';
import { WorkspaceService } from '@affine/core/modules/workspace';
import { i18nTime } from '@affine/i18n';
import {
BookmarkBlockService,
customImageProxyMiddleware,
EmbedGithubBlockService,
EmbedLoomBlockService,
EmbedYoutubeBlockService,
ImageBlockService,
LinkPreviewerService,
RefNodeSlotsProvider,
} from '@blocksuite/affine/blocks';
import { DisposableGroup } from '@blocksuite/affine/global/utils';
@@ -159,14 +156,9 @@ const DetailPageImpl = () => {
ImageBlockService.setImageProxyURL(BUILD_CONFIG.imageProxyUrl);
// provide link preview endpoint to blocksuite
BookmarkBlockService.setLinkPreviewEndpoint(BUILD_CONFIG.linkPreviewUrl);
EmbedGithubBlockService.setLinkPreviewEndpoint(
BUILD_CONFIG.linkPreviewUrl
);
EmbedYoutubeBlockService.setLinkPreviewEndpoint(
BUILD_CONFIG.linkPreviewUrl
);
EmbedLoomBlockService.setLinkPreviewEndpoint(BUILD_CONFIG.linkPreviewUrl);
editorHost?.doc
.get(LinkPreviewerService)
.setEndpoint(BUILD_CONFIG.linkPreviewUrl);
// provide page mode and updated date to blocksuite
const refNodeService = editorHost?.std.getOptional(RefNodeSlotsProvider);