mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-15 00:56:26 +08:00
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:
@@ -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';
|
||||
|
||||
+13
-13
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
];
|
||||
|
||||
+4
-17
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user