mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-16 01:26:37 +08:00
refactor(editor): unify directories naming (#11516)
**Directory Structure Changes** - Renamed multiple block-related directories by removing the "block-" prefix: - `block-attachment` → `attachment` - `block-bookmark` → `bookmark` - `block-callout` → `callout` - `block-code` → `code` - `block-data-view` → `data-view` - `block-database` → `database` - `block-divider` → `divider` - `block-edgeless-text` → `edgeless-text` - `block-embed` → `embed`
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
import type { ExtensionType } from '@blocksuite/store';
|
||||
|
||||
import { EmbedGithubBlockHtmlAdapterExtension } from './html.js';
|
||||
import { EmbedGithubMarkdownAdapterExtension } from './markdown.js';
|
||||
import { EmbedGithubBlockNotionHtmlAdapterExtension } from './notion-html.js';
|
||||
import { EmbedGithubBlockPlainTextAdapterExtension } from './plain-text.js';
|
||||
|
||||
export const EmbedGithubBlockAdapterExtensions: ExtensionType[] = [
|
||||
EmbedGithubBlockHtmlAdapterExtension,
|
||||
EmbedGithubMarkdownAdapterExtension,
|
||||
EmbedGithubBlockPlainTextAdapterExtension,
|
||||
EmbedGithubBlockNotionHtmlAdapterExtension,
|
||||
];
|
||||
@@ -0,0 +1,11 @@
|
||||
import { EmbedGithubBlockSchema } from '@blocksuite/affine-model';
|
||||
import { BlockHtmlAdapterExtension } from '@blocksuite/affine-shared/adapters';
|
||||
|
||||
import { createEmbedBlockHtmlAdapterMatcher } from '../../common/adapters/html.js';
|
||||
|
||||
export const embedGithubBlockHtmlAdapterMatcher =
|
||||
createEmbedBlockHtmlAdapterMatcher(EmbedGithubBlockSchema.model.flavour);
|
||||
|
||||
export const EmbedGithubBlockHtmlAdapterExtension = BlockHtmlAdapterExtension(
|
||||
embedGithubBlockHtmlAdapterMatcher
|
||||
);
|
||||
@@ -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,10 @@
|
||||
import { EmbedGithubBlockSchema } from '@blocksuite/affine-model';
|
||||
import { BlockMarkdownAdapterExtension } from '@blocksuite/affine-shared/adapters';
|
||||
|
||||
import { createEmbedBlockMarkdownAdapterMatcher } from '../../common/adapters/markdown.js';
|
||||
|
||||
export const embedGithubBlockMarkdownAdapterMatcher =
|
||||
createEmbedBlockMarkdownAdapterMatcher(EmbedGithubBlockSchema.model.flavour);
|
||||
|
||||
export const EmbedGithubMarkdownAdapterExtension =
|
||||
BlockMarkdownAdapterExtension(embedGithubBlockMarkdownAdapterMatcher);
|
||||
@@ -0,0 +1,14 @@
|
||||
import { EmbedGithubBlockSchema } from '@blocksuite/affine-model';
|
||||
import { BlockNotionHtmlAdapterExtension } from '@blocksuite/affine-shared/adapters';
|
||||
|
||||
import { createEmbedBlockNotionHtmlAdapterMatcher } from '../../common/adapters/notion-html.js';
|
||||
import { githubUrlRegex } from '../embed-github-model.js';
|
||||
|
||||
export const embedGithubBlockNotionHtmlAdapterMatcher =
|
||||
createEmbedBlockNotionHtmlAdapterMatcher(
|
||||
EmbedGithubBlockSchema.model.flavour,
|
||||
githubUrlRegex
|
||||
);
|
||||
|
||||
export const EmbedGithubBlockNotionHtmlAdapterExtension =
|
||||
BlockNotionHtmlAdapterExtension(embedGithubBlockNotionHtmlAdapterMatcher);
|
||||
@@ -0,0 +1,10 @@
|
||||
import { EmbedGithubBlockSchema } from '@blocksuite/affine-model';
|
||||
import { BlockPlainTextAdapterExtension } from '@blocksuite/affine-shared/adapters';
|
||||
|
||||
import { createEmbedBlockPlainTextAdapterMatcher } from '../../common/adapters/plain-text.js';
|
||||
|
||||
export const embedGithubBlockPlainTextAdapterMatcher =
|
||||
createEmbedBlockPlainTextAdapterMatcher(EmbedGithubBlockSchema.model.flavour);
|
||||
|
||||
export const EmbedGithubBlockPlainTextAdapterExtension =
|
||||
BlockPlainTextAdapterExtension(embedGithubBlockPlainTextAdapterMatcher);
|
||||
@@ -0,0 +1,39 @@
|
||||
import { toggleEmbedCardCreateModal } from '@blocksuite/affine-components/embed-card-modal';
|
||||
import type { SlashMenuConfig } from '@blocksuite/affine-widget-slash-menu';
|
||||
import { GithubDuotoneIcon } from '@blocksuite/icons/lit';
|
||||
|
||||
import { GithubRepoTooltip } from './tooltips';
|
||||
|
||||
export const embedGithubSlashMenuConfig: SlashMenuConfig = {
|
||||
items: [
|
||||
{
|
||||
name: 'GitHub',
|
||||
description: 'Link to a GitHub repository.',
|
||||
icon: GithubDuotoneIcon(),
|
||||
tooltip: {
|
||||
figure: GithubRepoTooltip,
|
||||
caption: 'GitHub Repo',
|
||||
},
|
||||
group: '4_Content & Media@7',
|
||||
when: ({ model }) =>
|
||||
model.doc.schema.flavourSchemaMap.has('affine:embed-github'),
|
||||
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,
|
||||
'GitHub',
|
||||
'The added GitHub issue or pull request link will be displayed as a card view.',
|
||||
{ mode: 'page', parentModel, index }
|
||||
);
|
||||
if (model.text?.length === 0) std.store.deleteBlock(model);
|
||||
})().catch(console.error);
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
import { html } from 'lit';
|
||||
|
||||
// prettier-ignore
|
||||
export const GithubRepoTooltip = html`<svg width="170" height="68" viewBox="0 0 170 68" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<rect width="170" height="68" rx="2" fill="white"/>
|
||||
<mask id="mask0_16460_1028" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="170" height="68">
|
||||
<rect width="170" height="68" rx="2" fill="white"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_16460_1028)">
|
||||
<rect x="6.5" y="28.5" width="169" height="67" rx="3.5" fill="white" stroke="#E3E2E4"/>
|
||||
<text fill="#121212" xml:space="preserve" style="white-space: pre" font-family="Inter" font-size="9" letter-spacing="0em"><tspan x="18" y="46.7727">toeverything/</tspan></text>
|
||||
<text fill="#121212" xml:space="preserve" style="white-space: pre" font-family="Inter" font-size="9" font-weight="bold" letter-spacing="0em"><tspan x="75.041" y="46.7727">AFFiNE</tspan></text>
|
||||
<text fill="#8E8D91" xml:space="preserve" style="white-space: pre" font-family="Inter" font-size="7" letter-spacing="0em"><tspan x="18" y="57.5455">Write, Draw and Plan All at Once.</tspan></text>
|
||||
<rect x="146" y="38" width="24" height="24" fill="url(#pattern0_16460_1028)"/>
|
||||
<text fill="#8E8D91" xml:space="preserve" style="white-space: pre" font-family="Inter" font-size="10" letter-spacing="0px"><tspan x="10" y="18.6364">Link to a GitHub repository.</tspan></text>
|
||||
</g>
|
||||
<defs>
|
||||
<pattern id="pattern0_16460_1028" patternContentUnits="objectBoundingBox" width="1" height="1">
|
||||
<use xlink:href="#image0_16460_1028" transform="scale(0.0208333)"/>
|
||||
</pattern>
|
||||
<image id="image0_16460_1028" width="48" height="48" xlink:href="data:image/png;base64,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"/>
|
||||
</defs>
|
||||
</svg>
|
||||
`;
|
||||
@@ -0,0 +1,51 @@
|
||||
import { EdgelessClipboardConfig } from '@blocksuite/affine-block-surface';
|
||||
import { type BlockSnapshot } from '@blocksuite/store';
|
||||
|
||||
export class EdgelessClipboardEmbedGithubConfig extends EdgelessClipboardConfig {
|
||||
static override readonly key = 'affine:embed-github';
|
||||
|
||||
override createBlock(githubEmbed: BlockSnapshot): string | null {
|
||||
if (!this.surface) return null;
|
||||
|
||||
const {
|
||||
xywh,
|
||||
style,
|
||||
owner,
|
||||
repo,
|
||||
githubType,
|
||||
githubId,
|
||||
url,
|
||||
caption,
|
||||
image,
|
||||
status,
|
||||
statusReason,
|
||||
title,
|
||||
description,
|
||||
createdAt,
|
||||
assignees,
|
||||
} = githubEmbed.props;
|
||||
|
||||
const embedGithubId = this.crud.addBlock(
|
||||
'affine:embed-github',
|
||||
{
|
||||
xywh,
|
||||
style,
|
||||
owner,
|
||||
repo,
|
||||
githubType,
|
||||
githubId,
|
||||
url,
|
||||
caption,
|
||||
image,
|
||||
status,
|
||||
statusReason,
|
||||
title,
|
||||
description,
|
||||
createdAt,
|
||||
assignees,
|
||||
},
|
||||
this.surface.model.id
|
||||
);
|
||||
return embedGithubId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { toEdgelessEmbedBlock } from '../common/to-edgeless-embed-block.js';
|
||||
import { EmbedGithubBlockComponent } from './embed-github-block.js';
|
||||
|
||||
export class EmbedEdgelessGithubBlockComponent extends toEdgelessEmbedBlock(
|
||||
EmbedGithubBlockComponent
|
||||
) {}
|
||||
@@ -0,0 +1,270 @@
|
||||
import { OpenIcon } from '@blocksuite/affine-components/icons';
|
||||
import type {
|
||||
EmbedGithubModel,
|
||||
EmbedGithubStyles,
|
||||
} from '@blocksuite/affine-model';
|
||||
import { ThemeProvider } from '@blocksuite/affine-shared/services';
|
||||
import { BlockSelection } from '@blocksuite/std';
|
||||
import { html, nothing } from 'lit';
|
||||
import { property } from 'lit/decorators.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { repeat } from 'lit/directives/repeat.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
|
||||
import { EmbedBlockComponent } from '../common/embed-block-element.js';
|
||||
import { getEmbedCardIcons } from '../common/utils.js';
|
||||
import { githubUrlRegex } from './embed-github-model.js';
|
||||
import type { EmbedGithubBlockService } from './embed-github-service.js';
|
||||
import { GithubIcon, styles } from './styles.js';
|
||||
import {
|
||||
getGithubStatusIcon,
|
||||
refreshEmbedGithubStatus,
|
||||
refreshEmbedGithubUrlData,
|
||||
} from './utils.js';
|
||||
|
||||
export class EmbedGithubBlockComponent extends EmbedBlockComponent<
|
||||
EmbedGithubModel,
|
||||
EmbedGithubBlockService
|
||||
> {
|
||||
static override styles = styles;
|
||||
|
||||
override _cardStyle: (typeof EmbedGithubStyles)[number] = 'horizontal';
|
||||
|
||||
open = () => {
|
||||
let link = this.model.props.url;
|
||||
if (!link.match(/^[a-zA-Z]+:\/\//)) {
|
||||
link = 'https://' + link;
|
||||
}
|
||||
window.open(link, '_blank');
|
||||
};
|
||||
|
||||
refreshData = () => {
|
||||
refreshEmbedGithubUrlData(this, this.fetchAbortController.signal).catch(
|
||||
console.error
|
||||
);
|
||||
};
|
||||
|
||||
refreshStatus = () => {
|
||||
refreshEmbedGithubStatus(this, this.fetchAbortController.signal).catch(
|
||||
console.error
|
||||
);
|
||||
};
|
||||
|
||||
private _handleAssigneeClick(assignee: string) {
|
||||
const link = `https://www.github.com/${assignee}`;
|
||||
window.open(link, '_blank');
|
||||
}
|
||||
|
||||
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.props.style;
|
||||
|
||||
if (
|
||||
!this.model.props.owner ||
|
||||
!this.model.props.repo ||
|
||||
!this.model.props.githubId
|
||||
) {
|
||||
this.doc.withoutTransact(() => {
|
||||
const url = this.model.props.url;
|
||||
const urlMatch = url.match(githubUrlRegex);
|
||||
if (urlMatch) {
|
||||
const [, owner, repo, githubType, githubId] = urlMatch;
|
||||
this.doc.updateBlock(this.model, {
|
||||
owner,
|
||||
repo,
|
||||
githubType: githubType === 'issue' ? 'issue' : 'pr',
|
||||
githubId,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.doc.withoutTransact(() => {
|
||||
if (!this.model.props.description && !this.model.props.title) {
|
||||
this.refreshData();
|
||||
} else {
|
||||
this.refreshStatus();
|
||||
}
|
||||
});
|
||||
|
||||
this.disposables.add(
|
||||
this.model.propsUpdated.subscribe(({ key }) => {
|
||||
if (key === 'url') {
|
||||
this.refreshData();
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
override renderBlock() {
|
||||
const {
|
||||
title = 'GitHub',
|
||||
githubType,
|
||||
status,
|
||||
statusReason,
|
||||
owner,
|
||||
repo,
|
||||
createdAt,
|
||||
assignees,
|
||||
description,
|
||||
image,
|
||||
style,
|
||||
} = this.model.props;
|
||||
|
||||
const loading = this.loading;
|
||||
const theme = this.std.get(ThemeProvider).theme;
|
||||
const { LoadingIcon, EmbedCardBannerIcon } = getEmbedCardIcons(theme);
|
||||
const titleIcon = loading ? LoadingIcon : GithubIcon;
|
||||
const statusIcon = status
|
||||
? getGithubStatusIcon(githubType, status, statusReason)
|
||||
: nothing;
|
||||
const statusText = loading ? '' : status;
|
||||
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;
|
||||
|
||||
let dateText = '';
|
||||
if (createdAt) {
|
||||
const date = new Date(createdAt);
|
||||
dateText = date.toLocaleDateString('en-US', {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
});
|
||||
const day = date.getDate();
|
||||
const suffix =
|
||||
['th', 'st', 'nd', 'rd'][((day / 10) | 0) !== 1 ? day % 10 : 4] || 'th';
|
||||
dateText = dateText.replace(/\d+/, `${day}${suffix}`);
|
||||
}
|
||||
|
||||
return this.renderEmbed(
|
||||
() => html`
|
||||
<div
|
||||
class=${classMap({
|
||||
'affine-embed-github-block': true,
|
||||
loading,
|
||||
[style]: true,
|
||||
selected: this.selected$.value,
|
||||
})}
|
||||
style=${styleMap({
|
||||
transform: `scale(${this._scale})`,
|
||||
transformOrigin: '0 0 ',
|
||||
})}
|
||||
@click=${this._handleClick}
|
||||
@dblclick=${this._handleDoubleClick}
|
||||
>
|
||||
<div class="affine-embed-github-banner">${bannerImage}</div>
|
||||
<div class="affine-embed-github-content">
|
||||
<div class="affine-embed-github-content-title">
|
||||
<div class="affine-embed-github-content-title-icons">
|
||||
<div class="affine-embed-github-content-title-site-icon">
|
||||
${titleIcon}
|
||||
</div>
|
||||
|
||||
${status && statusText
|
||||
? html`<div
|
||||
class=${classMap({
|
||||
'affine-embed-github-content-title-status-icon': true,
|
||||
[githubType]: true,
|
||||
[status]: true,
|
||||
success: statusReason === 'completed',
|
||||
failure: statusReason === 'not_planned',
|
||||
})}
|
||||
>
|
||||
${statusIcon}
|
||||
|
||||
<span>${statusText}</span>
|
||||
</div>`
|
||||
: nothing}
|
||||
</div>
|
||||
|
||||
<div class="affine-embed-github-content-title-text">
|
||||
${titleText}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="affine-embed-github-content-description">
|
||||
${descriptionText}
|
||||
</div>
|
||||
|
||||
${githubType === 'issue' && assignees
|
||||
? html`
|
||||
<div class="affine-embed-github-content-assignees">
|
||||
<div
|
||||
class="affine-embed-github-content-assignees-text label"
|
||||
>
|
||||
Assignees
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="affine-embed-github-content-assignees-text users"
|
||||
>
|
||||
${assignees.length === 0
|
||||
? html`<span
|
||||
class="affine-embed-github-content-assignees-text-users placeholder"
|
||||
>No one</span
|
||||
>`
|
||||
: repeat(
|
||||
assignees,
|
||||
assignee => assignee,
|
||||
(assignee, index) =>
|
||||
html`<span
|
||||
class="affine-embed-github-content-assignees-text-users user"
|
||||
@click=${() =>
|
||||
this._handleAssigneeClick(assignee)}
|
||||
>${`@${assignee}`}</span
|
||||
>
|
||||
${index === assignees.length - 1 ? '' : `, `}`
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
: nothing}
|
||||
|
||||
<div class="affine-embed-github-content-url" @click=${this.open}>
|
||||
<span class="affine-embed-github-content-repo"
|
||||
>${`${owner}/${repo} |`}</span
|
||||
>
|
||||
|
||||
${createdAt
|
||||
? html`<span class="affine-embed-github-content-date"
|
||||
>${dateText} |</span
|
||||
>`
|
||||
: nothing}
|
||||
<span>github.com</span>
|
||||
|
||||
<div class="affine-embed-github-content-url-icon">
|
||||
${OpenIcon}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
);
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor loading = false;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export const githubUrlRegex: RegExp =
|
||||
/^(?:https?:\/\/)?(?:www\.)?github\.com\/([^/]+)\/([^/]+)\/(issue|pull)s?\/(\d+)$/;
|
||||
@@ -0,0 +1,36 @@
|
||||
import {
|
||||
EmbedGithubBlockSchema,
|
||||
type EmbedGithubModel,
|
||||
EmbedGithubStyles,
|
||||
} from '@blocksuite/affine-model';
|
||||
import {
|
||||
EmbedOptionConfig,
|
||||
LinkPreviewerService,
|
||||
} from '@blocksuite/affine-shared/services';
|
||||
import { BlockService } from '@blocksuite/std';
|
||||
|
||||
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;
|
||||
|
||||
queryApiData = (embedGithubModel: EmbedGithubModel, signal?: AbortSignal) => {
|
||||
return queryEmbedGithubApiData(embedGithubModel, signal);
|
||||
};
|
||||
|
||||
queryUrlData = (embedGithubModel: EmbedGithubModel, signal?: AbortSignal) => {
|
||||
return queryEmbedGithubData(
|
||||
embedGithubModel,
|
||||
this.doc.get(LinkPreviewerService),
|
||||
signal
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export const EmbedGithubBlockOptionConfig = EmbedOptionConfig({
|
||||
flavour: EmbedGithubBlockSchema.model.flavour,
|
||||
urlRegex: githubUrlRegex,
|
||||
styles: EmbedGithubStyles,
|
||||
viewType: 'card',
|
||||
});
|
||||
@@ -0,0 +1,30 @@
|
||||
import { EmbedGithubBlockSchema } from '@blocksuite/affine-model';
|
||||
import { SlashMenuConfigExtension } from '@blocksuite/affine-widget-slash-menu';
|
||||
import { BlockViewExtension, FlavourExtension } from '@blocksuite/std';
|
||||
import type { ExtensionType } from '@blocksuite/store';
|
||||
import { literal } from 'lit/static-html.js';
|
||||
|
||||
import { createBuiltinToolbarConfigExtension } from '../configs/toolbar';
|
||||
import { EmbedGithubBlockAdapterExtensions } from './adapters/extension';
|
||||
import { embedGithubSlashMenuConfig } from './configs/slash-menu';
|
||||
import { EmbedGithubBlockComponent } from './embed-github-block';
|
||||
import {
|
||||
EmbedGithubBlockOptionConfig,
|
||||
EmbedGithubBlockService,
|
||||
} from './embed-github-service';
|
||||
|
||||
const flavour = EmbedGithubBlockSchema.model.flavour;
|
||||
|
||||
export const EmbedGithubBlockSpec: ExtensionType[] = [
|
||||
FlavourExtension(flavour),
|
||||
EmbedGithubBlockService,
|
||||
BlockViewExtension(flavour, model => {
|
||||
return model.parent?.flavour === 'affine:surface'
|
||||
? literal`affine-embed-edgeless-github-block`
|
||||
: literal`affine-embed-github-block`;
|
||||
}),
|
||||
EmbedGithubBlockAdapterExtensions,
|
||||
EmbedGithubBlockOptionConfig,
|
||||
createBuiltinToolbarConfigExtension(flavour, EmbedGithubBlockComponent),
|
||||
SlashMenuConfigExtension(flavour, embedGithubSlashMenuConfig),
|
||||
].flat();
|
||||
@@ -0,0 +1,6 @@
|
||||
export * from './adapters/index.js';
|
||||
export * from './edgeless-clipboard-config';
|
||||
export * from './embed-github-block.js';
|
||||
export * from './embed-github-service.js';
|
||||
export * from './embed-github-spec.js';
|
||||
export { GithubIcon } from './styles.js';
|
||||
@@ -0,0 +1,507 @@
|
||||
import { css, html } from 'lit';
|
||||
|
||||
export const styles = css`
|
||||
.affine-embed-github-block {
|
||||
container: affine-embed-github-block / inline-size;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
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-github-content {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
flex-direction: column;
|
||||
align-self: stretch;
|
||||
gap: 4px;
|
||||
padding: 12px;
|
||||
border-radius: var(--1, 0px);
|
||||
opacity: var(--add, 1);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.affine-embed-github-content-title {
|
||||
display: flex;
|
||||
min-height: 22px;
|
||||
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-github-content-title-icons {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.affine-embed-github-content-title-icons img,
|
||||
.affine-embed-github-content-title-icons object,
|
||||
.affine-embed-github-content-title-icons svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
color: var(--affine-pure-white);
|
||||
}
|
||||
|
||||
.affine-embed-github-content-title-site-icon {
|
||||
display: flex;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.github-icon {
|
||||
fill: var(--affine-black);
|
||||
color: var(--affine-black);
|
||||
}
|
||||
}
|
||||
|
||||
.affine-embed-github-content-title-status-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 3px 6px;
|
||||
border-radius: 20px;
|
||||
|
||||
color: var(--affine-pure-white);
|
||||
leading-trim: both;
|
||||
|
||||
text-edge: cap;
|
||||
font-feature-settings:
|
||||
'clig' off,
|
||||
'liga' off;
|
||||
text-transform: capitalize;
|
||||
font-family: var(--affine-font-family);
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 16px;
|
||||
}
|
||||
.affine-embed-github-content-title-status-icon.issue.open {
|
||||
background: #238636;
|
||||
}
|
||||
.affine-embed-github-content-title-status-icon.issue.closed.success {
|
||||
background: #8957e5;
|
||||
}
|
||||
.affine-embed-github-content-title-status-icon.issue.closed.failure {
|
||||
background: #6e7681;
|
||||
}
|
||||
.affine-embed-github-content-title-status-icon.pr.open {
|
||||
background: #238636;
|
||||
}
|
||||
.affine-embed-github-content-title-status-icon.pr.draft {
|
||||
background: #6e7681;
|
||||
}
|
||||
.affine-embed-github-content-title-status-icon.pr.merged {
|
||||
background: #8957e5;
|
||||
}
|
||||
.affine-embed-github-content-title-status-icon.pr.closed {
|
||||
background: #c03737;
|
||||
}
|
||||
|
||||
.affine-embed-github-content-title-status-icon > svg {
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.affine-embed-github-content-title-status-icon > span {
|
||||
padding: 0px 1.5px;
|
||||
}
|
||||
|
||||
.affine-embed-github-content-title-text {
|
||||
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-github-content-description {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
|
||||
flex-grow: 1;
|
||||
|
||||
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-github-content-assignees {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.affine-embed-github-content-url {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: 4px;
|
||||
width: max-content;
|
||||
max-width: 100%;
|
||||
cursor: pointer;
|
||||
}
|
||||
.affine-embed-github-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-github-content-url:hover > span {
|
||||
color: var(--affine-link-color);
|
||||
}
|
||||
.affine-embed-github-content-url:hover .open-icon {
|
||||
fill: var(--affine-link-color);
|
||||
}
|
||||
|
||||
.affine-embed-github-content-url-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
.affine-embed-github-content-url-icon .open-icon {
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
fill: var(--affine-text-secondary-color);
|
||||
}
|
||||
|
||||
.affine-embed-github-banner {
|
||||
margin: 12px 0px 0px 12px;
|
||||
width: 204px;
|
||||
height: 102px;
|
||||
opacity: var(--add, 1);
|
||||
}
|
||||
|
||||
.affine-embed-github-banner img,
|
||||
.affine-embed-github-banner object,
|
||||
.affine-embed-github-banner svg {
|
||||
width: 204px;
|
||||
height: 102px;
|
||||
object-fit: cover;
|
||||
border-radius: 4px 4px var(--1, 0px) var(--1, 0px);
|
||||
}
|
||||
|
||||
.affine-embed-github-block.loading {
|
||||
.affine-embed-github-content-title-text {
|
||||
color: var(--affine-placeholder-color);
|
||||
}
|
||||
}
|
||||
|
||||
.affine-embed-github-block.selected {
|
||||
.affine-embed-github-content-url > span {
|
||||
color: var(--affine-link-color);
|
||||
}
|
||||
.affine-embed-github-content-url .open-icon {
|
||||
fill: var(--affine-link-color);
|
||||
}
|
||||
}
|
||||
|
||||
.affine-embed-github-block.list {
|
||||
.affine-embed-github-content {
|
||||
width: 100%;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.affine-embed-github-content-title {
|
||||
width: 660px;
|
||||
}
|
||||
|
||||
.affine-embed-github-content-repo {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.affine-embed-github-content-date {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.affine-embed-github-content-url {
|
||||
width: 90px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.affine-embed-github-content-description {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.affine-embed-github-banner {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.affine-embed-github-block.vertical {
|
||||
flex-direction: column;
|
||||
|
||||
.affine-embed-github-content {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.affine-embed-github-content-description {
|
||||
-webkit-line-clamp: 6;
|
||||
}
|
||||
|
||||
.affine-embed-github-content-assignees {
|
||||
display: flex;
|
||||
padding: var(--1, 0px);
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: 2px;
|
||||
align-self: stretch;
|
||||
}
|
||||
|
||||
.affine-embed-github-content-assignees-text {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 1;
|
||||
-webkit-box-orient: vertical;
|
||||
|
||||
font-family: var(--affine-font-family);
|
||||
font-size: var(--affine-font-xs);
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.affine-embed-github-content-assignees-text.label {
|
||||
width: 72px;
|
||||
color: var(--affine-text-primary-color);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.affine-embed-github-content-assignees-text.users {
|
||||
width: calc(100% - 72px);
|
||||
word-break: break-all;
|
||||
white-space: normal;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.affine-embed-github-content-assignees-text-users.user {
|
||||
color: var(--affine-link-color);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.affine-embed-github-content-assignees-text-users.placeholder {
|
||||
color: var(--affine-placeholder-color);
|
||||
}
|
||||
|
||||
.affine-embed-github-banner {
|
||||
width: 340px;
|
||||
height: 170px;
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
.affine-embed-github-banner img,
|
||||
.affine-embed-github-banner object,
|
||||
.affine-embed-github-banner svg {
|
||||
width: 340px;
|
||||
height: 170px;
|
||||
}
|
||||
}
|
||||
|
||||
.affine-embed-github-block.cube {
|
||||
.affine-embed-github-content {
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.affine-embed-github-content-title {
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.affine-embed-github-content-title-text {
|
||||
-webkit-line-clamp: 2;
|
||||
}
|
||||
|
||||
.affine-embed-github-content-description {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.affine-embed-github-banner {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.affine-embed-github-content-repo {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.affine-embed-github-content-date {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@container affine-embed-github-block (width < 375px) {
|
||||
.affine-embed-github-content {
|
||||
width: 100%;
|
||||
}
|
||||
.affine-embed-github-banner {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const GithubIcon = html`<svg
|
||||
class="github-icon"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 16 16"
|
||||
fill="currentColor"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M8.00016 1.33334C4.31683 1.33334 1.3335 4.39214 1.3335 8.16864C1.3335 11.1933 3.24183 13.7479 5.89183 14.6536C6.22516 14.7134 6.35016 14.5084 6.35016 14.3289C6.35016 14.1666 6.34183 13.6283 6.34183 13.0559C4.66683 13.372 4.2335 12.6372 4.10016 12.2527C4.02516 12.0562 3.70016 11.4496 3.41683 11.2872C3.1835 11.1591 2.85016 10.8429 3.4085 10.8344C3.9335 10.8259 4.3085 11.33 4.4335 11.535C5.0335 12.5689 5.99183 12.2784 6.37516 12.0989C6.4335 11.6546 6.6085 11.3556 6.80016 11.1847C5.31683 11.0138 3.76683 10.4243 3.76683 7.80978C3.76683 7.06644 4.02516 6.45127 4.45016 5.9728C4.3835 5.80192 4.15016 5.1013 4.51683 4.16145C4.51683 4.16145 5.07516 3.98202 6.35016 4.86206C6.8835 4.70827 7.45016 4.63137 8.01683 4.63137C8.5835 4.63137 9.15016 4.70827 9.6835 4.86206C10.9585 3.97348 11.5168 4.16145 11.5168 4.16145C11.8835 5.1013 11.6502 5.80192 11.5835 5.9728C12.0085 6.45127 12.2668 7.0579 12.2668 7.80978C12.2668 10.4328 10.7085 11.0138 9.22516 11.1847C9.46683 11.3983 9.67516 11.8084 9.67516 12.4492C9.67516 13.3635 9.66683 14.0983 9.66683 14.3289C9.66683 14.5084 9.79183 14.722 10.1252 14.6536C11.4486 14.1955 12.5986 13.3234 13.4133 12.1601C14.228 10.9968 14.6664 9.60079 14.6668 8.16864C14.6668 4.39214 11.6835 1.33334 8.00016 1.33334Z"
|
||||
/>
|
||||
</svg> `;
|
||||
|
||||
export const GithubIssueOpenIcon = html`<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="currentColor"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path d="M8 9.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z"></path>
|
||||
<path
|
||||
d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Z"
|
||||
></path>
|
||||
</svg>`;
|
||||
|
||||
export const GithubIssueClosedSuccessIcon = html`<svg
|
||||
aria-hidden="true"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
version="1.1"
|
||||
width="16"
|
||||
data-view-component="true"
|
||||
class="octicon octicon-issue-closed flex-items-center mr-1"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path
|
||||
d="M11.28 6.78a.75.75 0 0 0-1.06-1.06L7.25 8.69 5.78 7.22a.75.75 0 0 0-1.06 1.06l2 2a.75.75 0 0 0 1.06 0l3.5-3.5Z"
|
||||
></path>
|
||||
<path
|
||||
d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0Zm-1.5 0a6.5 6.5 0 1 0-13 0 6.5 6.5 0 0 0 13 0Z"
|
||||
></path>
|
||||
</svg>`;
|
||||
|
||||
export const GithubIssueClosedFailureIcon = html`<svg
|
||||
aria-hidden="true"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
version="1.1"
|
||||
width="16"
|
||||
data-view-component="true"
|
||||
class="octicon octicon-skip flex-items-center mr-1"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path
|
||||
d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Zm9.78-2.22-5.5 5.5a.749.749 0 0 1-1.275-.326.749.749 0 0 1 .215-.734l5.5-5.5a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042Z"
|
||||
></path>
|
||||
</svg>`;
|
||||
|
||||
export const GithubPROpenIcon = html`<svg
|
||||
height="16"
|
||||
class="octicon octicon-git-pull-request"
|
||||
viewBox="0 0 16 16"
|
||||
version="1.1"
|
||||
width="16"
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path
|
||||
d="M1.5 3.25a2.25 2.25 0 1 1 3 2.122v5.256a2.251 2.251 0 1 1-1.5 0V5.372A2.25 2.25 0 0 1 1.5 3.25Zm5.677-.177L9.573.677A.25.25 0 0 1 10 .854V2.5h1A2.5 2.5 0 0 1 13.5 5v5.628a2.251 2.251 0 1 1-1.5 0V5a1 1 0 0 0-1-1h-1v1.646a.25.25 0 0 1-.427.177L7.177 3.427a.25.25 0 0 1 0-.354ZM3.75 2.5a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm0 9.5a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm8.25.75a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0Z"
|
||||
></path>
|
||||
</svg>`;
|
||||
|
||||
export const GithubPRDraftIcon = html`<svg
|
||||
height="16"
|
||||
class="octicon octicon-git-pull-request-draft"
|
||||
viewBox="0 0 16 16"
|
||||
version="1.1"
|
||||
width="16"
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path
|
||||
d="M3.25 1A2.25 2.25 0 0 1 4 5.372v5.256a2.251 2.251 0 1 1-1.5 0V5.372A2.251 2.251 0 0 1 3.25 1Zm9.5 14a2.25 2.25 0 1 1 0-4.5 2.25 2.25 0 0 1 0 4.5ZM2.5 3.25a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0ZM3.25 12a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm9.5 0a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5ZM14 7.5a1.25 1.25 0 1 1-2.5 0 1.25 1.25 0 0 1 2.5 0Zm0-4.25a1.25 1.25 0 1 1-2.5 0 1.25 1.25 0 0 1 2.5 0Z"
|
||||
></path>
|
||||
</svg>`;
|
||||
|
||||
export const GithubPRMergedIcon = html`<svg
|
||||
height="16"
|
||||
class="octicon octicon-git-merge"
|
||||
viewBox="0 0 16 16"
|
||||
version="1.1"
|
||||
width="16"
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path
|
||||
d="M5.45 5.154A4.25 4.25 0 0 0 9.25 7.5h1.378a2.251 2.251 0 1 1 0 1.5H9.25A5.734 5.734 0 0 1 5 7.123v3.505a2.25 2.25 0 1 1-1.5 0V5.372a2.25 2.25 0 1 1 1.95-.218ZM4.25 13.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Zm8.5-4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5ZM5 3.25a.75.75 0 1 0 0 .005V3.25Z"
|
||||
></path>
|
||||
</svg>`;
|
||||
|
||||
export const GithubPRClosedIcon = html`<svg
|
||||
height="16"
|
||||
class="octicon octicon-git-pull-request-closed"
|
||||
viewBox="0 0 16 16"
|
||||
version="1.1"
|
||||
width="16"
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path
|
||||
d="M3.25 1A2.25 2.25 0 0 1 4 5.372v5.256a2.251 2.251 0 1 1-1.5 0V5.372A2.251 2.251 0 0 1 3.25 1Zm9.5 5.5a.75.75 0 0 1 .75.75v3.378a2.251 2.251 0 1 1-1.5 0V7.25a.75.75 0 0 1 .75-.75Zm-2.03-5.273a.75.75 0 0 1 1.06 0l.97.97.97-.97a.748.748 0 0 1 1.265.332.75.75 0 0 1-.205.729l-.97.97.97.97a.751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018l-.97-.97-.97.97a.749.749 0 0 1-1.275-.326.749.749 0 0 1 .215-.734l.97-.97-.97-.97a.75.75 0 0 1 0-1.06ZM2.5 3.25a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0ZM3.25 12a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm9.5 0a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Z"
|
||||
></path>
|
||||
</svg>`;
|
||||
@@ -0,0 +1,182 @@
|
||||
import type {
|
||||
EmbedGithubBlockUrlData,
|
||||
EmbedGithubModel,
|
||||
} from '@blocksuite/affine-model';
|
||||
import type { LinkPreviewerService } from '@blocksuite/affine-shared/services';
|
||||
import { isAbortError } from '@blocksuite/affine-shared/utils';
|
||||
import { nothing } from 'lit';
|
||||
|
||||
import type { EmbedGithubBlockComponent } from './embed-github-block.js';
|
||||
import {
|
||||
GithubIssueClosedFailureIcon,
|
||||
GithubIssueClosedSuccessIcon,
|
||||
GithubIssueOpenIcon,
|
||||
GithubPRClosedIcon,
|
||||
GithubPRDraftIcon,
|
||||
GithubPRMergedIcon,
|
||||
GithubPROpenIcon,
|
||||
} from './styles.js';
|
||||
|
||||
export async function queryEmbedGithubData(
|
||||
embedGithubModel: EmbedGithubModel,
|
||||
linkPreviewer: LinkPreviewerService,
|
||||
signal?: AbortSignal
|
||||
): Promise<Partial<EmbedGithubBlockUrlData>> {
|
||||
const [githubApiData, openGraphData] = await Promise.all([
|
||||
queryEmbedGithubApiData(embedGithubModel, signal),
|
||||
linkPreviewer.query(embedGithubModel.props.url, signal),
|
||||
]);
|
||||
return { ...githubApiData, ...openGraphData };
|
||||
}
|
||||
|
||||
export async function queryEmbedGithubApiData(
|
||||
embedGithubModel: EmbedGithubModel,
|
||||
signal?: AbortSignal
|
||||
): Promise<Partial<EmbedGithubBlockUrlData>> {
|
||||
const { owner, repo, githubType, githubId } = embedGithubModel.props;
|
||||
let githubApiData: Partial<EmbedGithubBlockUrlData> = {};
|
||||
|
||||
// github's public api has a rate limit of 60 requests per hour
|
||||
const apiUrl = `https://api.github.com/repos/${owner}/${repo}/${
|
||||
githubType === 'issue' ? 'issues' : 'pulls'
|
||||
}/${githubId}`;
|
||||
|
||||
const githubApiResponse = await fetch(apiUrl, {
|
||||
cache: 'no-cache',
|
||||
signal,
|
||||
}).catch(() => null);
|
||||
|
||||
if (githubApiResponse && githubApiResponse.ok) {
|
||||
const githubApiJson = await githubApiResponse.json();
|
||||
const { state, state_reason, draft, merged, created_at, assignees } =
|
||||
githubApiJson;
|
||||
|
||||
const assigneeLogins = assignees.map(
|
||||
(assignee: { login: string }) => assignee.login
|
||||
);
|
||||
|
||||
let status = state;
|
||||
if (merged) {
|
||||
status = 'merged';
|
||||
} else if (state === 'open' && draft) {
|
||||
status = 'draft';
|
||||
}
|
||||
|
||||
githubApiData = {
|
||||
status,
|
||||
statusReason: state_reason,
|
||||
createdAt: created_at,
|
||||
assignees: assigneeLogins,
|
||||
};
|
||||
}
|
||||
|
||||
return githubApiData;
|
||||
}
|
||||
|
||||
export async function refreshEmbedGithubUrlData(
|
||||
embedGithubElement: EmbedGithubBlockComponent,
|
||||
signal?: AbortSignal
|
||||
): Promise<void> {
|
||||
let image = null,
|
||||
status = null,
|
||||
statusReason = null,
|
||||
title = null,
|
||||
description = null,
|
||||
createdAt = null,
|
||||
assignees = null;
|
||||
|
||||
try {
|
||||
embedGithubElement.loading = true;
|
||||
|
||||
// TODO(@mirone): remove service
|
||||
const queryUrlData = embedGithubElement.service?.queryUrlData;
|
||||
if (!queryUrlData) {
|
||||
console.error(
|
||||
`Trying to refresh github url data, but the queryUrlData is not found.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const githubUrlData = await queryUrlData(embedGithubElement.model);
|
||||
({
|
||||
image = null,
|
||||
status = null,
|
||||
statusReason = null,
|
||||
title = null,
|
||||
description = null,
|
||||
createdAt = null,
|
||||
assignees = null,
|
||||
} = githubUrlData);
|
||||
|
||||
if (signal?.aborted) return;
|
||||
|
||||
embedGithubElement.doc.updateBlock(embedGithubElement.model, {
|
||||
image,
|
||||
status,
|
||||
statusReason,
|
||||
title,
|
||||
description,
|
||||
createdAt,
|
||||
assignees,
|
||||
});
|
||||
} catch (error) {
|
||||
if (signal?.aborted || isAbortError(error)) return;
|
||||
throw Error;
|
||||
} finally {
|
||||
embedGithubElement.loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
export async function refreshEmbedGithubStatus(
|
||||
embedGithubElement: EmbedGithubBlockComponent,
|
||||
signal?: AbortSignal
|
||||
) {
|
||||
// TODO(@mirone): remove service
|
||||
const queryApiData = embedGithubElement.service?.queryApiData;
|
||||
if (!queryApiData) {
|
||||
console.error(
|
||||
`Trying to refresh github status, but the queryApiData is not found.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const githubApiData = await queryApiData(embedGithubElement.model, signal);
|
||||
|
||||
if (!githubApiData.status || signal?.aborted) return;
|
||||
|
||||
embedGithubElement.doc.updateBlock(embedGithubElement.model, {
|
||||
status: githubApiData.status,
|
||||
statusReason: githubApiData.statusReason,
|
||||
createdAt: githubApiData.createdAt,
|
||||
assignees: githubApiData.assignees,
|
||||
});
|
||||
}
|
||||
|
||||
export function getGithubStatusIcon(
|
||||
type: 'issue' | 'pr',
|
||||
status: string,
|
||||
statusReason: string | null
|
||||
) {
|
||||
if (type === 'issue') {
|
||||
if (status === 'open') {
|
||||
return GithubIssueOpenIcon;
|
||||
} else if (status === 'closed' && statusReason === 'completed') {
|
||||
return GithubIssueClosedSuccessIcon;
|
||||
} else if (status === 'closed' && statusReason === 'not_planned') {
|
||||
return GithubIssueClosedFailureIcon;
|
||||
} else {
|
||||
return nothing;
|
||||
}
|
||||
} else if (type === 'pr') {
|
||||
if (status === 'open') {
|
||||
return GithubPROpenIcon;
|
||||
} else if (status === 'draft') {
|
||||
return GithubPRDraftIcon;
|
||||
} else if (status === 'merged') {
|
||||
return GithubPRMergedIcon;
|
||||
} else if (status === 'closed') {
|
||||
return GithubPRClosedIcon;
|
||||
}
|
||||
}
|
||||
return nothing;
|
||||
}
|
||||
Reference in New Issue
Block a user