mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-15 05:37:32 +00: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:
@@ -1,99 +0,0 @@
|
||||
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';
|
||||
|
||||
export type LinkPreviewResponseData = {
|
||||
url: string;
|
||||
title?: string;
|
||||
siteName?: string;
|
||||
description?: string;
|
||||
images?: string[];
|
||||
mediaType?: string;
|
||||
contentType?: string;
|
||||
charset?: string;
|
||||
videos?: string[];
|
||||
favicons?: string[];
|
||||
};
|
||||
|
||||
export class LinkPreviewer {
|
||||
private _endpoint = DEFAULT_LINK_PREVIEW_ENDPOINT;
|
||||
|
||||
query = async (
|
||||
url: string,
|
||||
signal?: AbortSignal
|
||||
): Promise<Partial<LinkPreviewData>> => {
|
||||
if (
|
||||
(url.startsWith('https://x.com/') ||
|
||||
url.startsWith('https://www.x.com/') ||
|
||||
url.startsWith('https://www.twitter.com/') ||
|
||||
url.startsWith('https://twitter.com/')) &&
|
||||
url.includes('/status/')
|
||||
) {
|
||||
// use api.fxtwitter.com
|
||||
url =
|
||||
'https://api.fxtwitter.com/status/' + /\/status\/(.*)/.exec(url)?.[1];
|
||||
try {
|
||||
const { tweet } = await fetch(url, { signal }).then(res => res.json());
|
||||
return {
|
||||
title: tweet.author.name,
|
||||
icon: tweet.author.avatar_url,
|
||||
description: tweet.text,
|
||||
image: tweet.media?.photos?.[0].url || tweet.author.banner_url,
|
||||
};
|
||||
} catch (e) {
|
||||
console.error(`Failed to fetch tweet: ${url}`);
|
||||
console.error(e);
|
||||
return {};
|
||||
}
|
||||
} else {
|
||||
const response = await fetch(this._endpoint, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
url,
|
||||
}),
|
||||
signal,
|
||||
})
|
||||
.then(r => {
|
||||
if (!r || !r.ok) {
|
||||
throw new BlockSuiteError(
|
||||
ErrorCode.DefaultRuntimeError,
|
||||
`Failed to fetch link preview: ${url}`
|
||||
);
|
||||
}
|
||||
return r;
|
||||
})
|
||||
.catch(err => {
|
||||
if (isAbortError(err)) return null;
|
||||
console.error(`Failed to fetch link preview: ${url}`);
|
||||
console.error(err);
|
||||
return null;
|
||||
});
|
||||
|
||||
if (!response) return {};
|
||||
|
||||
const data: LinkPreviewResponseData = await response.json();
|
||||
return {
|
||||
title: data.title ? this._getStringFromHTML(data.title) : null,
|
||||
description: data.description
|
||||
? this._getStringFromHTML(data.description)
|
||||
: null,
|
||||
icon: data.favicons?.[0],
|
||||
image: data.images?.[0],
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
setEndpoint = (endpoint: string) => {
|
||||
this._endpoint = endpoint;
|
||||
};
|
||||
|
||||
private _getStringFromHTML(html: string) {
|
||||
const div = document.createElement('div');
|
||||
div.innerHTML = html;
|
||||
return div.textContent;
|
||||
}
|
||||
}
|
||||
@@ -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';
|
||||
|
||||
Reference in New Issue
Block a user