mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-17 06:16:59 +08:00
chore(editor): reorg packages (#10702)
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
import type { ExtensionType } from '@blocksuite/store';
|
||||
|
||||
import { EmbedLoomBlockHtmlAdapterExtension } from './html.js';
|
||||
import { EmbedLoomMarkdownAdapterExtension } from './markdown.js';
|
||||
import { EmbedLoomBlockNotionHtmlAdapterExtension } from './notion-html.js';
|
||||
import { EmbedLoomBlockPlainTextAdapterExtension } from './plain-text.js';
|
||||
|
||||
export const EmbedLoomBlockAdapterExtensions: ExtensionType[] = [
|
||||
EmbedLoomBlockHtmlAdapterExtension,
|
||||
EmbedLoomMarkdownAdapterExtension,
|
||||
EmbedLoomBlockPlainTextAdapterExtension,
|
||||
EmbedLoomBlockNotionHtmlAdapterExtension,
|
||||
];
|
||||
@@ -0,0 +1,11 @@
|
||||
import { EmbedLoomBlockSchema } from '@blocksuite/affine-model';
|
||||
import { BlockHtmlAdapterExtension } from '@blocksuite/affine-shared/adapters';
|
||||
|
||||
import { createEmbedBlockHtmlAdapterMatcher } from '../../common/adapters/html.js';
|
||||
|
||||
export const embedLoomBlockHtmlAdapterMatcher =
|
||||
createEmbedBlockHtmlAdapterMatcher(EmbedLoomBlockSchema.model.flavour);
|
||||
|
||||
export const EmbedLoomBlockHtmlAdapterExtension = BlockHtmlAdapterExtension(
|
||||
embedLoomBlockHtmlAdapterMatcher
|
||||
);
|
||||
@@ -0,0 +1,4 @@
|
||||
export * from './html.js';
|
||||
export * from './markdown.js';
|
||||
export * from './notion-html.js';
|
||||
export * from './plain-text.js';
|
||||
@@ -0,0 +1,11 @@
|
||||
import { EmbedLoomBlockSchema } from '@blocksuite/affine-model';
|
||||
import { BlockMarkdownAdapterExtension } from '@blocksuite/affine-shared/adapters';
|
||||
|
||||
import { createEmbedBlockMarkdownAdapterMatcher } from '../../common/adapters/markdown.js';
|
||||
|
||||
export const embedLoomBlockMarkdownAdapterMatcher =
|
||||
createEmbedBlockMarkdownAdapterMatcher(EmbedLoomBlockSchema.model.flavour);
|
||||
|
||||
export const EmbedLoomMarkdownAdapterExtension = BlockMarkdownAdapterExtension(
|
||||
embedLoomBlockMarkdownAdapterMatcher
|
||||
);
|
||||
@@ -0,0 +1,14 @@
|
||||
import { EmbedLoomBlockSchema } from '@blocksuite/affine-model';
|
||||
import { BlockNotionHtmlAdapterExtension } from '@blocksuite/affine-shared/adapters';
|
||||
|
||||
import { createEmbedBlockNotionHtmlAdapterMatcher } from '../../common/adapters/notion-html.js';
|
||||
import { loomUrlRegex } from '../embed-loom-model.js';
|
||||
|
||||
export const embedLoomBlockNotionHtmlAdapterMatcher =
|
||||
createEmbedBlockNotionHtmlAdapterMatcher(
|
||||
EmbedLoomBlockSchema.model.flavour,
|
||||
loomUrlRegex
|
||||
);
|
||||
|
||||
export const EmbedLoomBlockNotionHtmlAdapterExtension =
|
||||
BlockNotionHtmlAdapterExtension(embedLoomBlockNotionHtmlAdapterMatcher);
|
||||
@@ -0,0 +1,10 @@
|
||||
import { EmbedLoomBlockSchema } from '@blocksuite/affine-model';
|
||||
import { BlockPlainTextAdapterExtension } from '@blocksuite/affine-shared/adapters';
|
||||
|
||||
import { createEmbedBlockPlainTextAdapterMatcher } from '../../common/adapters/plain-text.js';
|
||||
|
||||
export const embedLoomBlockPlainTextAdapterMatcher =
|
||||
createEmbedBlockPlainTextAdapterMatcher(EmbedLoomBlockSchema.model.flavour);
|
||||
|
||||
export const EmbedLoomBlockPlainTextAdapterExtension =
|
||||
BlockPlainTextAdapterExtension(embedLoomBlockPlainTextAdapterMatcher);
|
||||
@@ -0,0 +1,32 @@
|
||||
import { toggleEmbedCardCreateModal } from '@blocksuite/affine-components/embed-card-modal';
|
||||
import type { SlashMenuConfig } from '@blocksuite/affine-widget-slash-menu';
|
||||
import { LoomLogoDuotoneIcon } from '@blocksuite/icons/lit';
|
||||
|
||||
export const embedLoomSlashMenuConfig: SlashMenuConfig = {
|
||||
items: [
|
||||
{
|
||||
name: 'Loom',
|
||||
icon: LoomLogoDuotoneIcon(),
|
||||
group: '4_Content & Media@8',
|
||||
when: ({ model }) =>
|
||||
model.doc.schema.flavourSchemaMap.has('affine:embed-loom'),
|
||||
action: ({ std, model }) => {
|
||||
(async () => {
|
||||
const { host } = std;
|
||||
const parentModel = host.doc.getParent(model);
|
||||
if (!parentModel) {
|
||||
return;
|
||||
}
|
||||
const index = parentModel.children.indexOf(model) + 1;
|
||||
await toggleEmbedCardCreateModal(
|
||||
host,
|
||||
'Loom',
|
||||
'The added Loom video link will be displayed as an embed view.',
|
||||
{ mode: 'page', parentModel, index }
|
||||
);
|
||||
if (model.text?.length === 0) std.store.deleteBlock(model);
|
||||
})().catch(console.error);
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,6 @@
|
||||
import { toEdgelessEmbedBlock } from '../common/to-edgeless-embed-block.js';
|
||||
import { EmbedLoomBlockComponent } from './embed-loom-block.js';
|
||||
|
||||
export class EmbedEdgelessLoomBlockComponent extends toEdgelessEmbedBlock(
|
||||
EmbedLoomBlockComponent
|
||||
) {}
|
||||
@@ -0,0 +1,196 @@
|
||||
import { OpenIcon } from '@blocksuite/affine-components/icons';
|
||||
import type { EmbedLoomModel, EmbedLoomStyles } from '@blocksuite/affine-model';
|
||||
import { ThemeProvider } from '@blocksuite/affine-shared/services';
|
||||
import { BlockSelection } from '@blocksuite/block-std';
|
||||
import { html } from 'lit';
|
||||
import { property, state } from 'lit/decorators.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
|
||||
import { EmbedBlockComponent } from '../common/embed-block-element.js';
|
||||
import { getEmbedCardIcons } from '../common/utils.js';
|
||||
import { loomUrlRegex } from './embed-loom-model.js';
|
||||
import type { EmbedLoomBlockService } from './embed-loom-service.js';
|
||||
import { LoomIcon, styles } from './styles.js';
|
||||
import { refreshEmbedLoomUrlData } from './utils.js';
|
||||
|
||||
export class EmbedLoomBlockComponent extends EmbedBlockComponent<
|
||||
EmbedLoomModel,
|
||||
EmbedLoomBlockService
|
||||
> {
|
||||
static override styles = styles;
|
||||
|
||||
override _cardStyle: (typeof EmbedLoomStyles)[number] = 'video';
|
||||
|
||||
protected _isDragging = false;
|
||||
|
||||
protected _isResizing = false;
|
||||
|
||||
open = () => {
|
||||
let link = this.model.url;
|
||||
if (!link.match(/^[a-zA-Z]+:\/\//)) {
|
||||
link = 'https://' + link;
|
||||
}
|
||||
window.open(link, '_blank');
|
||||
};
|
||||
|
||||
refreshData = () => {
|
||||
refreshEmbedLoomUrlData(this, this.fetchAbortController.signal).catch(
|
||||
console.error
|
||||
);
|
||||
};
|
||||
|
||||
private _handleDoubleClick(event: MouseEvent) {
|
||||
event.stopPropagation();
|
||||
this.open();
|
||||
}
|
||||
|
||||
private _selectBlock() {
|
||||
const selectionManager = this.host.selection;
|
||||
const blockSelection = selectionManager.create(BlockSelection, {
|
||||
blockId: this.blockId,
|
||||
});
|
||||
selectionManager.setGroup('note', [blockSelection]);
|
||||
}
|
||||
|
||||
protected _handleClick(event: MouseEvent) {
|
||||
event.stopPropagation();
|
||||
this._selectBlock();
|
||||
}
|
||||
|
||||
override connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this._cardStyle = this.model.style;
|
||||
|
||||
if (!this.model.videoId) {
|
||||
this.doc.withoutTransact(() => {
|
||||
const url = this.model.url;
|
||||
const urlMatch = url.match(loomUrlRegex);
|
||||
if (urlMatch) {
|
||||
const [, videoId] = urlMatch;
|
||||
this.doc.updateBlock(this.model, {
|
||||
videoId,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!this.model.description && !this.model.title) {
|
||||
this.doc.withoutTransact(() => {
|
||||
this.refreshData();
|
||||
});
|
||||
}
|
||||
|
||||
this.disposables.add(
|
||||
this.model.propsUpdated.on(({ key }) => {
|
||||
this.requestUpdate();
|
||||
if (key === 'url') {
|
||||
this.refreshData();
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
// this is required to prevent iframe from capturing pointer events
|
||||
this.disposables.add(
|
||||
this.selected$.subscribe(selected => {
|
||||
this._showOverlay = this._isResizing || this._isDragging || !selected;
|
||||
})
|
||||
);
|
||||
// this is required to prevent iframe from capturing pointer events
|
||||
this.handleEvent('dragStart', () => {
|
||||
this._isDragging = true;
|
||||
this._showOverlay =
|
||||
this._isResizing || this._isDragging || !this.selected$.peek();
|
||||
});
|
||||
|
||||
this.handleEvent('dragEnd', () => {
|
||||
this._isDragging = false;
|
||||
this._showOverlay =
|
||||
this._isResizing || this._isDragging || !this.selected$.peek();
|
||||
});
|
||||
}
|
||||
|
||||
override renderBlock() {
|
||||
const { image, title = 'Loom', description, videoId } = this.model;
|
||||
|
||||
const loading = this.loading;
|
||||
const theme = this.std.get(ThemeProvider).theme;
|
||||
const { LoadingIcon, EmbedCardBannerIcon } = getEmbedCardIcons(theme);
|
||||
const titleIcon = loading ? LoadingIcon : LoomIcon;
|
||||
const titleText = loading ? 'Loading...' : title;
|
||||
const descriptionText = loading ? '' : description;
|
||||
const bannerImage =
|
||||
!loading && image
|
||||
? html`<object type="image/webp" data=${image} draggable="false">
|
||||
${EmbedCardBannerIcon}
|
||||
</object>`
|
||||
: EmbedCardBannerIcon;
|
||||
|
||||
return this.renderEmbed(
|
||||
() => html`
|
||||
<div
|
||||
class=${classMap({
|
||||
'affine-embed-loom-block': true,
|
||||
loading,
|
||||
selected: this.selected$.value,
|
||||
})}
|
||||
style=${styleMap({
|
||||
transform: `scale(${this._scale})`,
|
||||
transformOrigin: '0 0',
|
||||
})}
|
||||
@click=${this._handleClick}
|
||||
@dblclick=${this._handleDoubleClick}
|
||||
>
|
||||
<div class="affine-embed-loom-video">
|
||||
${videoId
|
||||
? html`
|
||||
<div class="affine-embed-loom-video-iframe-container">
|
||||
<iframe
|
||||
src=${`https://www.loom.com/embed/${videoId}?hide_title=true`}
|
||||
frameborder="0"
|
||||
allow="fullscreen; accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||
loading="lazy"
|
||||
></iframe>
|
||||
|
||||
<div
|
||||
class=${classMap({
|
||||
'affine-embed-loom-video-iframe-overlay': true,
|
||||
hide: !this._showOverlay,
|
||||
})}
|
||||
></div>
|
||||
</div>
|
||||
`
|
||||
: bannerImage}
|
||||
</div>
|
||||
<div class="affine-embed-loom-content">
|
||||
<div class="affine-embed-loom-content-header">
|
||||
<div class="affine-embed-loom-content-title-icon">
|
||||
${titleIcon}
|
||||
</div>
|
||||
|
||||
<div class="affine-embed-loom-content-title-text">
|
||||
${titleText}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="affine-embed-loom-content-description">
|
||||
${descriptionText}
|
||||
</div>
|
||||
|
||||
<div class="affine-embed-loom-content-url" @click=${this.open}>
|
||||
<span>loom.com</span>
|
||||
|
||||
<div class="affine-embed-loom-content-url-icon">${OpenIcon}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
);
|
||||
}
|
||||
|
||||
@state()
|
||||
protected accessor _showOverlay = true;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor loading = false;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export const loomUrlRegex: RegExp =
|
||||
/(?:https?:\/\/)??(?:www\.)?loom\.com\/share\/([a-zA-Z0-9]+)/;
|
||||
@@ -0,0 +1,25 @@
|
||||
import {
|
||||
EmbedLoomBlockSchema,
|
||||
type EmbedLoomModel,
|
||||
EmbedLoomStyles,
|
||||
} from '@blocksuite/affine-model';
|
||||
import { EmbedOptionConfig } from '@blocksuite/affine-shared/services';
|
||||
import { BlockService } from '@blocksuite/block-std';
|
||||
|
||||
import { loomUrlRegex } from './embed-loom-model.js';
|
||||
import { queryEmbedLoomData } from './utils.js';
|
||||
|
||||
export class EmbedLoomBlockService extends BlockService {
|
||||
static override readonly flavour = EmbedLoomBlockSchema.model.flavour;
|
||||
|
||||
queryUrlData = (embedLoomModel: EmbedLoomModel, signal?: AbortSignal) => {
|
||||
return queryEmbedLoomData(embedLoomModel, signal);
|
||||
};
|
||||
}
|
||||
|
||||
export const EmbedLoomBlockOptionConfig = EmbedOptionConfig({
|
||||
flavour: EmbedLoomBlockSchema.model.flavour,
|
||||
urlRegex: loomUrlRegex,
|
||||
styles: EmbedLoomStyles,
|
||||
viewType: 'embed',
|
||||
});
|
||||
@@ -0,0 +1,38 @@
|
||||
import { EmbedLoomBlockSchema } from '@blocksuite/affine-model';
|
||||
import { ToolbarModuleExtension } from '@blocksuite/affine-shared/services';
|
||||
import { SlashMenuConfigExtension } from '@blocksuite/affine-widget-slash-menu';
|
||||
import {
|
||||
BlockServiceIdentifier,
|
||||
BlockViewExtension,
|
||||
FlavourExtension,
|
||||
} from '@blocksuite/block-std';
|
||||
import type { ExtensionType } from '@blocksuite/store';
|
||||
import { literal } from 'lit/static-html.js';
|
||||
|
||||
import { createBuiltinToolbarConfigForExternal } from '../configs/toolbar';
|
||||
import { EmbedLoomBlockAdapterExtensions } from './adapters/extension';
|
||||
import { embedLoomSlashMenuConfig } from './configs/slash-menu';
|
||||
import { EmbedLoomBlockComponent } from './embed-loom-block';
|
||||
import {
|
||||
EmbedLoomBlockOptionConfig,
|
||||
EmbedLoomBlockService,
|
||||
} from './embed-loom-service';
|
||||
|
||||
const flavour = EmbedLoomBlockSchema.model.flavour;
|
||||
|
||||
export const EmbedLoomBlockSpec: ExtensionType[] = [
|
||||
FlavourExtension(flavour),
|
||||
EmbedLoomBlockService,
|
||||
BlockViewExtension(flavour, model => {
|
||||
return model.parent?.flavour === 'affine:surface'
|
||||
? literal`affine-embed-edgeless-loom-block`
|
||||
: literal`affine-embed-loom-block`;
|
||||
}),
|
||||
EmbedLoomBlockAdapterExtensions,
|
||||
EmbedLoomBlockOptionConfig,
|
||||
ToolbarModuleExtension({
|
||||
id: BlockServiceIdentifier(flavour),
|
||||
config: createBuiltinToolbarConfigForExternal(EmbedLoomBlockComponent),
|
||||
}),
|
||||
SlashMenuConfigExtension(flavour, embedLoomSlashMenuConfig),
|
||||
].flat();
|
||||
@@ -0,0 +1,6 @@
|
||||
export * from './adapters/index.js';
|
||||
export * from './embed-loom-block.js';
|
||||
export * from './embed-loom-model.js';
|
||||
export * from './embed-loom-service.js';
|
||||
export * from './embed-loom-spec.js';
|
||||
export { LoomIcon } from './styles.js';
|
||||
@@ -0,0 +1,217 @@
|
||||
import { css, html } from 'lit';
|
||||
|
||||
export const styles = css`
|
||||
.affine-embed-loom-block {
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
padding: 12px;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--affine-background-tertiary-color);
|
||||
|
||||
opacity: var(--add, 1);
|
||||
background: var(--affine-background-primary-color);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.affine-embed-loom-video {
|
||||
flex-grow: 1;
|
||||
width: 100%;
|
||||
opacity: var(--add, 1);
|
||||
}
|
||||
|
||||
.affine-embed-loom-video img,
|
||||
.affine-embed-loom-video object,
|
||||
.affine-embed-loom-video svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
border-radius: 4px 4px var(--1, 0px) var(--1, 0px);
|
||||
}
|
||||
|
||||
.affine-embed-loom-video-iframe-container {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.affine-embed-loom-video-iframe-container > iframe {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 4px 4px var(--1, 0px) var(--1, 0px);
|
||||
}
|
||||
|
||||
.affine-embed-loom-video-iframe-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.affine-embed-loom-video-iframe-overlay.hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.affine-embed-loom-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: fit-content;
|
||||
border-radius: var(--1, 0px);
|
||||
opacity: var(--add, 1);
|
||||
}
|
||||
|
||||
.affine-embed-loom-content-header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
|
||||
align-self: stretch;
|
||||
padding: var(--1, 0px);
|
||||
border-radius: var(--1, 0px);
|
||||
opacity: var(--add, 1);
|
||||
}
|
||||
|
||||
.affine-embed-loom-content-title-icon {
|
||||
display: flex;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.affine-embed-loom-content-title-icon img,
|
||||
.affine-embed-loom-content-title-icon object,
|
||||
.affine-embed-loom-content-title-icon svg {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
fill: var(--affine-background-primary-color);
|
||||
}
|
||||
|
||||
.affine-embed-loom-content-title-text {
|
||||
flex: 1 0 0;
|
||||
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 1;
|
||||
-webkit-box-orient: vertical;
|
||||
|
||||
word-break: break-word;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
color: var(--affine-text-primary-color);
|
||||
|
||||
font-family: var(--affine-font-family);
|
||||
font-size: var(--affine-font-sm);
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.affine-embed-loom-content-description {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
|
||||
flex: 1 0 0;
|
||||
align-self: stretch;
|
||||
|
||||
word-break: break-word;
|
||||
white-space: normal;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
color: var(--affine-text-primary-color);
|
||||
|
||||
font-family: var(--affine-font-family);
|
||||
font-size: var(--affine-font-xs);
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.affine-embed-loom-content-url {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: 4px;
|
||||
width: max-content;
|
||||
max-width: 100%;
|
||||
cursor: pointer;
|
||||
}
|
||||
.affine-embed-loom-content-url > span {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 1;
|
||||
-webkit-box-orient: vertical;
|
||||
|
||||
word-break: break-all;
|
||||
white-space: normal;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
color: var(--affine-text-secondary-color);
|
||||
|
||||
font-family: var(--affine-font-family);
|
||||
font-size: var(--affine-font-xs);
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
}
|
||||
.affine-embed-loom-content-url:hover > span {
|
||||
color: var(--affine-link-color);
|
||||
}
|
||||
.affine-embed-loom-content-url:hover .open-icon {
|
||||
fill: var(--affine-link-color);
|
||||
}
|
||||
|
||||
.affine-embed-loom-content-url-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
.affine-embed-loom-content-url-icon .open-icon {
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
fill: var(--affine-text-secondary-color);
|
||||
}
|
||||
|
||||
.affine-embed-loom-block.loading {
|
||||
.affine-embed-loom-content-title-text {
|
||||
color: var(--affine-placeholder-color);
|
||||
}
|
||||
}
|
||||
|
||||
.affine-embed-loom-block.selected {
|
||||
.affine-embed-loom-content-url > span {
|
||||
color: var(--affine-link-color);
|
||||
}
|
||||
.affine-embed-loom-content-url .open-icon {
|
||||
fill: var(--affine-link-color);
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const LoomIcon = html`<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g clip-path="url(#clip0_1780_25276)">
|
||||
<path
|
||||
d="M18.3333 9.07327H13.4597L17.6805 6.63642L16.7536 5.03052L12.5328 7.46736L14.9691 3.24695L13.3632 2.3195L10.9269 6.5399V1.66669H9.073V6.54037L6.63577 2.3195L5.03036 3.24648L7.46713 7.4669L3.24638 5.03052L2.31942 6.63596L6.54017 9.07281H1.66663V10.9268H6.53971L2.31942 13.3636L3.24638 14.9695L7.46667 12.5331L5.0299 16.7535L6.63577 17.6805L9.07254 13.4597V18.3334H10.9265V13.4601L13.3628 17.6805L14.9686 16.7535L12.5319 12.5327L16.7526 14.9695L17.6796 13.3636L13.4593 10.9272H18.3323V9.07327H18.3333ZM9.99996 12.5215C8.60206 12.5215 7.469 11.3884 7.469 9.99047C7.469 8.59253 8.60206 7.45943 9.99996 7.45943C11.3979 7.45943 12.5309 8.59253 12.5309 9.99047C12.5309 11.3884 11.3979 12.5215 9.99996 12.5215Z"
|
||||
fill="#625DF5"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1780_25276">
|
||||
<rect width="20" height="20" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>`;
|
||||
@@ -0,0 +1,75 @@
|
||||
import type {
|
||||
EmbedLoomBlockUrlData,
|
||||
EmbedLoomModel,
|
||||
} from '@blocksuite/affine-model';
|
||||
import { isAbortError } from '@blocksuite/affine-shared/utils';
|
||||
|
||||
import type { EmbedLoomBlockComponent } from './embed-loom-block.js';
|
||||
|
||||
const LoomOEmbedEndpoint = 'https://www.loom.com/v1/oembed';
|
||||
|
||||
export async function queryEmbedLoomData(
|
||||
embedLoomModel: EmbedLoomModel,
|
||||
signal?: AbortSignal
|
||||
): Promise<Partial<EmbedLoomBlockUrlData>> {
|
||||
const url = embedLoomModel.url;
|
||||
|
||||
const loomEmbedData: Partial<EmbedLoomBlockUrlData> =
|
||||
await queryLoomOEmbedData(url, signal);
|
||||
|
||||
return loomEmbedData;
|
||||
}
|
||||
|
||||
export async function queryLoomOEmbedData(
|
||||
url: string,
|
||||
signal?: AbortSignal
|
||||
): Promise<Partial<EmbedLoomBlockUrlData>> {
|
||||
let loomOEmbedData: Partial<EmbedLoomBlockUrlData> = {};
|
||||
|
||||
const oEmbedUrl = `${LoomOEmbedEndpoint}?url=${url}`;
|
||||
|
||||
const oEmbedResponse = await fetch(oEmbedUrl, { signal }).catch(() => null);
|
||||
if (oEmbedResponse && oEmbedResponse.ok) {
|
||||
const oEmbedJson = await oEmbedResponse.json();
|
||||
const { title, description, thumbnail_url: image } = oEmbedJson;
|
||||
|
||||
loomOEmbedData = {
|
||||
title,
|
||||
description,
|
||||
image,
|
||||
};
|
||||
}
|
||||
|
||||
return loomOEmbedData;
|
||||
}
|
||||
|
||||
export async function refreshEmbedLoomUrlData(
|
||||
embedLoomElement: EmbedLoomBlockComponent,
|
||||
signal?: AbortSignal
|
||||
): Promise<void> {
|
||||
let title = null,
|
||||
description = null,
|
||||
image = null;
|
||||
|
||||
try {
|
||||
embedLoomElement.loading = true;
|
||||
|
||||
const queryUrlData = embedLoomElement.service?.queryUrlData;
|
||||
if (!queryUrlData) return;
|
||||
|
||||
const loomUrlData = await queryUrlData(embedLoomElement.model);
|
||||
({ title = null, description = null, image = null } = loomUrlData);
|
||||
|
||||
if (signal?.aborted) return;
|
||||
|
||||
embedLoomElement.doc.updateBlock(embedLoomElement.model, {
|
||||
title,
|
||||
description,
|
||||
image,
|
||||
});
|
||||
} catch (error) {
|
||||
if (signal?.aborted || isAbortError(error)) return;
|
||||
} finally {
|
||||
embedLoomElement.loading = false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user