refactor(editor): remove attachment and image block service (#9909)

This commit is contained in:
Saul-Mirone
2025-01-28 20:53:23 +00:00
parent 9c65c42d64
commit f3f6e8c6ac
22 changed files with 95 additions and 109 deletions
@@ -11,7 +11,10 @@ import {
type AttachmentBlockModel,
AttachmentBlockStyles,
} from '@blocksuite/affine-model';
import { ThemeProvider } from '@blocksuite/affine-shared/services';
import {
FileSizeLimitService,
ThemeProvider,
} from '@blocksuite/affine-shared/services';
import { humanFileSize } from '@blocksuite/affine-shared/utils';
import { BlockSelection, TextSelection } from '@blocksuite/block-std';
import { Slice } from '@blocksuite/store';
@@ -22,17 +25,13 @@ import { classMap } from 'lit/directives/class-map.js';
import { ref } from 'lit/directives/ref.js';
import { styleMap } from 'lit/directives/style-map.js';
import type { AttachmentBlockService } from './attachment-service.js';
import { AttachmentOptionsTemplate } from './components/options.js';
import { AttachmentEmbedProvider } from './embed.js';
import { styles } from './styles.js';
import { checkAttachmentBlob, downloadAttachmentBlob } from './utils.js';
@Peekable()
export class AttachmentBlockComponent extends CaptionedBlockComponent<
AttachmentBlockModel,
AttachmentBlockService
> {
export class AttachmentBlockComponent extends CaptionedBlockComponent<AttachmentBlockModel> {
static override styles = styles;
protected _isDragging = false;
@@ -86,10 +85,14 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<
margin: '18px 0px',
});
private get _maxFileSize() {
return this.std.store.get(FileSizeLimitService).maxFileSize;
}
convertTo = () => {
return this.std
.get(AttachmentEmbedProvider)
.convertTo(this.model, this.service.maxFileSize);
.convertTo(this.model, this._maxFileSize);
};
copy = () => {
@@ -105,7 +108,7 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<
embedded = () => {
return this.std
.get(AttachmentEmbedProvider)
.embedded(this.model, this.service.maxFileSize);
.embedded(this.model, this._maxFileSize);
};
open = () => {
@@ -122,7 +125,7 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<
protected get embedView() {
return this.std
.get(AttachmentEmbedProvider)
.render(this.model, this.blobUrl, this.service.maxFileSize);
.render(this.model, this.blobUrl, this._maxFileSize);
}
private _selectBlock() {
@@ -1,24 +1,17 @@
import { FileDropConfigExtension } from '@blocksuite/affine-components/drop-indicator';
import { AttachmentBlockSchema } from '@blocksuite/affine-model';
import { TelemetryProvider } from '@blocksuite/affine-shared/services';
import {
FileSizeLimitService,
TelemetryProvider,
} from '@blocksuite/affine-shared/services';
import {
isInsideEdgelessEditor,
matchFlavours,
} from '@blocksuite/affine-shared/utils';
import { BlockService } from '@blocksuite/block-std';
import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx';
import { addAttachments, addSiblingAttachmentBlocks } from './utils.js';
// bytes.parse('2GB')
const maxFileSize = 2147483648;
export class AttachmentBlockService extends BlockService {
static override readonly flavour = AttachmentBlockSchema.model.flavour;
maxFileSize = maxFileSize;
}
export const AttachmentDropOption = FileDropConfigExtension({
flavour: AttachmentBlockSchema.model.flavour,
onDrop: ({ files, targetModel, placement, point, std }) => {
@@ -28,11 +21,12 @@ export const AttachmentDropOption = FileDropConfigExtension({
);
if (!attachmentFiles.length) return false;
const maxFileSize = std.store.get(FileSizeLimitService).maxFileSize;
if (targetModel && !matchFlavours(targetModel, ['affine:surface'])) {
addSiblingAttachmentBlocks(
std.host,
attachmentFiles,
// TODO: use max file size from service
maxFileSize,
targetModel,
placement
@@ -3,10 +3,7 @@ import type { ExtensionType } from '@blocksuite/store';
import { literal } from 'lit/static-html.js';
import { AttachmentBlockNotionHtmlAdapterExtension } from './adapters/notion-html.js';
import {
AttachmentBlockService,
AttachmentDropOption,
} from './attachment-service.js';
import { AttachmentDropOption } from './attachment-service.js';
import {
AttachmentEmbedConfigExtension,
AttachmentEmbedService,
@@ -14,7 +11,6 @@ import {
export const AttachmentBlockSpec: ExtensionType[] = [
FlavourExtension('affine:attachment'),
AttachmentBlockService,
BlockViewExtension('affine:attachment', model => {
return model.parent?.flavour === 'affine:surface'
? literal`affine-edgeless-attachment`
@@ -1,6 +1,5 @@
import { AttachmentBlockComponent } from './attachment-block';
import { AttachmentEdgelessBlockComponent } from './attachment-edgeless-block';
import type { AttachmentBlockService } from './attachment-service';
export function effects() {
customElements.define(
@@ -9,11 +8,3 @@ export function effects() {
);
customElements.define('affine-attachment', AttachmentBlockComponent);
}
declare global {
namespace BlockSuite {
interface BlockServices {
'affine:attachment': AttachmentBlockService;
}
}
}
+13 -12
View File
@@ -2,10 +2,12 @@ import type {
AttachmentBlockModel,
ImageBlockProps,
} from '@blocksuite/affine-model';
import { FileSizeLimitService } from '@blocksuite/affine-shared/services';
import {
transformModel,
withTempBlobData,
} from '@blocksuite/affine-shared/utils';
import { type BlockStdScope, StdIdentifier } from '@blocksuite/block-std';
import type { Container } from '@blocksuite/global/di';
import { createIdentifier } from '@blocksuite/global/di';
import type { ExtensionType } from '@blocksuite/store';
@@ -57,8 +59,9 @@ export const AttachmentEmbedProvider = createIdentifier<AttachmentEmbedService>(
);
export class AttachmentEmbedService extends Extension {
// 10MB
static MAX_EMBED_SIZE = 10 * 1024 * 1024;
private get _maxFileSize() {
return this.std.store.get(FileSizeLimitService).maxFileSize;
}
get keys() {
return this.configs.keys();
@@ -68,7 +71,10 @@ export class AttachmentEmbedService extends Extension {
return this.configs.values();
}
constructor(private readonly configs: Map<string, AttachmentEmbedConfig>) {
constructor(
private readonly std: BlockStdScope,
private readonly configs: Map<string, AttachmentEmbedConfig>
) {
super();
}
@@ -77,15 +83,13 @@ export class AttachmentEmbedService extends Extension {
provider.getAll(AttachmentEmbedConfigIdentifier)
);
di.addImpl(AttachmentEmbedProvider, AttachmentEmbedService, [
StdIdentifier,
AttachmentEmbedConfigMapIdentifier,
]);
}
// Converts to embed view.
convertTo(
model: AttachmentBlockModel,
maxFileSize = AttachmentEmbedService.MAX_EMBED_SIZE
) {
convertTo(model: AttachmentBlockModel, maxFileSize = this._maxFileSize) {
const config = this.values.find(config => config.check(model, maxFileSize));
if (!config || !config.action) {
model.doc.updateBlock(model, { embed: true });
@@ -94,17 +98,14 @@ export class AttachmentEmbedService extends Extension {
config.action(model)?.catch(console.error);
}
embedded(
model: AttachmentBlockModel,
maxFileSize = AttachmentEmbedService.MAX_EMBED_SIZE
) {
embedded(model: AttachmentBlockModel, maxFileSize = this._maxFileSize) {
return this.values.some(config => config.check(model, maxFileSize));
}
render(
model: AttachmentBlockModel,
blobUrl?: string,
maxFileSize = AttachmentEmbedService.MAX_EMBED_SIZE
maxFileSize = this._maxFileSize
) {
if (!model.embed || !blobUrl) return;
@@ -8,7 +8,10 @@ import {
EMBED_CARD_HEIGHT,
EMBED_CARD_WIDTH,
} from '@blocksuite/affine-shared/consts';
import { TelemetryProvider } from '@blocksuite/affine-shared/services';
import {
FileSizeLimitService,
TelemetryProvider,
} from '@blocksuite/affine-shared/services';
import { humanFileSize } from '@blocksuite/affine-shared/utils';
import type { BlockStdScope, EditorHost } from '@blocksuite/block-std';
import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx';
@@ -272,7 +275,7 @@ export async function addAttachments(
console.error('Attachment service not found');
return [];
}
const maxFileSize = attachmentService.maxFileSize;
const maxFileSize = std.store.get(FileSizeLimitService).maxFileSize;
const isSizeExceeded = files.some(file => file.size > maxFileSize);
if (isSizeExceeded) {
toast(
@@ -1,3 +1,4 @@
import { FileSizeLimitService } from '@blocksuite/affine-shared/services';
import { getImageFilesFromLocal } from '@blocksuite/affine-shared/utils';
import type { Command } from '@blocksuite/block-std';
import type { BlockModel } from '@blocksuite/store';
@@ -28,10 +29,7 @@ export const insertImagesCommand: Command<
? selectedModels[0]
: selectedModels[selectedModels.length - 1];
const imageService = std.getService('affine:image');
if (!imageService) return [];
const maxFileSize = imageService.maxFileSize;
const maxFileSize = std.store.get(FileSizeLimitService).maxFileSize;
const result = addSiblingImageBlock(
std.host,
@@ -2,7 +2,6 @@ import { ImageBlockFallbackCard } from './components/image-block-fallback.js';
import { ImageBlockPageComponent } from './components/page-image-block.js';
import { ImageBlockComponent } from './image-block.js';
import { ImageEdgelessBlockComponent } from './image-edgeless-block.js';
import type { ImageBlockService } from './image-service.js';
export function effects() {
customElements.define('affine-image', ImageBlockComponent);
@@ -10,11 +9,3 @@ export function effects() {
customElements.define('affine-page-image', ImageBlockPageComponent);
customElements.define('affine-image-fallback-card', ImageBlockFallbackCard);
}
declare global {
namespace BlockSuite {
interface BlockServices {
'affine:image': ImageBlockService;
}
}
}
@@ -10,7 +10,6 @@ import { when } from 'lit/directives/when.js';
import type { ImageBlockFallbackCard } from './components/image-block-fallback.js';
import type { ImageBlockPageComponent } from './components/page-image-block.js';
import type { ImageBlockService } from './image-service.js';
import {
copyImageBlob,
downloadImageBlob,
@@ -21,10 +20,7 @@ import {
@Peekable({
enableOn: () => !IS_MOBILE,
})
export class ImageBlockComponent extends CaptionedBlockComponent<
ImageBlockModel,
ImageBlockService
> {
export class ImageBlockComponent extends CaptionedBlockComponent<ImageBlockModel> {
convertToCardView = () => {
turnImageIntoCardView(this).catch(console.error);
};
@@ -8,7 +8,6 @@ import { styleMap } from 'lit/directives/style-map.js';
import { when } from 'lit/directives/when.js';
import type { ImageBlockFallbackCard } from './components/image-block-fallback.js';
import type { ImageBlockService } from './image-service.js';
import {
copyImageBlob,
downloadImageBlob,
@@ -18,10 +17,7 @@ import {
} from './utils.js';
@Peekable()
export class ImageEdgelessBlockComponent extends GfxBlockComponent<
ImageBlockModel,
ImageBlockService
> {
export class ImageEdgelessBlockComponent extends GfxBlockComponent<ImageBlockModel> {
static override styles = css`
affine-edgeless-image .resizable-img,
affine-edgeless-image .resizable-img img {
@@ -0,0 +1,9 @@
import { StoreExtension } from '@blocksuite/store';
import { setImageProxyMiddlewareURL } from './adapters/middleware';
export class ImageProxyService extends StoreExtension {
static override key = 'image-proxy';
setImageProxyURL = setImageProxyMiddlewareURL;
}
@@ -1,6 +1,9 @@
import { FileDropConfigExtension } from '@blocksuite/affine-components/drop-indicator';
import { ImageBlockSchema, MAX_IMAGE_WIDTH } from '@blocksuite/affine-model';
import { TelemetryProvider } from '@blocksuite/affine-shared/services';
import {
FileSizeLimitService,
TelemetryProvider,
} from '@blocksuite/affine-shared/services';
import {
isInsideEdgelessEditor,
matchFlavours,
@@ -8,18 +11,10 @@ import {
import { BlockService } from '@blocksuite/block-std';
import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx';
import { setImageProxyMiddlewareURL } from './adapters/middleware.js';
import { addImages, addSiblingImageBlock } from './utils.js';
// bytes.parse('2GB')
const maxFileSize = 2147483648;
export class ImageBlockService extends BlockService {
static override readonly flavour = ImageBlockSchema.model.flavour;
static setImageProxyURL = setImageProxyMiddlewareURL;
maxFileSize = maxFileSize;
}
export const ImageDropOption = FileDropConfigExtension({
@@ -28,11 +23,12 @@ export const ImageDropOption = FileDropConfigExtension({
const imageFiles = files.filter(file => file.type.startsWith('image/'));
if (!imageFiles.length) return false;
const maxFileSize = std.store.get(FileSizeLimitService).maxFileSize;
if (targetModel && !matchFlavours(targetModel, ['affine:surface'])) {
addSiblingImageBlock(
std.host,
imageFiles,
// TODO: use max file size from service
maxFileSize,
targetModel,
placement
@@ -7,6 +7,7 @@ import type { ExtensionType } from '@blocksuite/store';
import { literal } from 'lit/static-html.js';
import { ImageBlockAdapterExtensions } from './adapters/extension.js';
import { ImageProxyService } from './image-proxy-service.js';
import { ImageBlockService, ImageDropOption } from './image-service.js';
export const ImageBlockSpec: ExtensionType[] = [
@@ -27,3 +28,5 @@ export const ImageBlockSpec: ExtensionType[] = [
ImageDropOption,
ImageBlockAdapterExtensions,
].flat();
export const ImageStoreSpec: ExtensionType[] = [ImageProxyService].flat();
@@ -6,6 +6,7 @@ export * from './adapters';
export * from './commands';
export * from './image-block';
export * from './image-edgeless-block';
export { ImageProxyService } from './image-proxy-service';
export * from './image-service';
export * from './image-spec';
export * from './styles';
+5 -8
View File
@@ -5,7 +5,10 @@ import type {
ImageBlockModel,
ImageBlockProps,
} from '@blocksuite/affine-model';
import { NativeClipboardProvider } from '@blocksuite/affine-shared/services';
import {
FileSizeLimitService,
NativeClipboardProvider,
} from '@blocksuite/affine-shared/services';
import {
downloadBlob,
humanFileSize,
@@ -430,15 +433,9 @@ export async function addImages(
const imageFiles = [...files].filter(file => file.type.startsWith('image/'));
if (!imageFiles.length) return [];
const imageService = std.getService('affine:image');
const gfx = std.get(GfxControllerIdentifier);
if (!imageService) {
console.error('Image service not found');
return [];
}
const maxFileSize = imageService.maxFileSize;
const maxFileSize = std.store.get(FileSizeLimitService).maxFileSize;
const isSizeExceeded = imageFiles.some(file => file.size > maxFileSize);
if (isSizeExceeded) {
toast(
@@ -0,0 +1,10 @@
import { StoreExtension } from '@blocksuite/store';
// bytes.parse('2GB')
const maxFileSize = 2147483648;
export class FileSizeLimitService extends StoreExtension {
static override key = 'file-size-limit';
maxFileSize = maxFileSize;
}
@@ -5,6 +5,7 @@ export * from './edit-props-store';
export * from './editor-setting-service';
export * from './embed-option-service';
export * from './feature-flag-service';
export * from './file-size-limit-service';
export * from './font-loader';
export * from './generate-url-service';
export * from './link-previewer-service';
+6 -2
View File
@@ -10,7 +10,7 @@ import { DividerBlockSpec } from '@blocksuite/affine-block-divider';
import { EdgelessTextBlockSpec } from '@blocksuite/affine-block-edgeless-text';
import { EmbedExtensions } from '@blocksuite/affine-block-embed';
import { FrameBlockSpec } from '@blocksuite/affine-block-frame';
import { ImageBlockSpec } from '@blocksuite/affine-block-image';
import { ImageBlockSpec, ImageStoreSpec } from '@blocksuite/affine-block-image';
import { LatexBlockSpec } from '@blocksuite/affine-block-latex';
import { ListBlockSpec } from '@blocksuite/affine-block-list';
import {
@@ -43,6 +43,7 @@ import {
DocDisplayMetaService,
EditPropsStore,
FeatureFlagService,
FileSizeLimitService,
FontLoaderService,
LinkPreviewerService,
} from '@blocksuite/affine-shared/services';
@@ -107,4 +108,7 @@ export const StoreExtensions: ExtensionType[] = [
DatabaseSelectionExtension,
TableSelectionExtension,
LinkPreviewerService,
];
FileSizeLimitService,
ImageStoreSpec,
].flat();
@@ -45,6 +45,7 @@ import {
getTextSelectionCommand,
} from '@blocksuite/affine-shared/commands';
import { REFERENCE_NODE } from '@blocksuite/affine-shared/consts';
import { FileSizeLimitService } from '@blocksuite/affine-shared/services';
import type { AffineTextAttributes } from '@blocksuite/affine-shared/types';
import {
createDefaultDoc,
@@ -436,9 +437,7 @@ const contentMediaToolGroup: KeyboardToolPanelGroup = {
const file = await openFileOrFiles();
if (!file) return;
const attachmentService = std.getService('affine:attachment');
if (!attachmentService) return;
const maxFileSize = attachmentService.maxFileSize;
const maxFileSize = std.store.get(FileSizeLimitService).maxFileSize;
await addSiblingAttachmentBlocks(std.host, [file], maxFileSize, model);
if (model.text?.length === 0) {
@@ -994,9 +993,7 @@ export const defaultKeyboardToolbarConfig: KeyboardToolbarConfig = {
const file = await openFileOrFiles();
if (!file) return;
const attachmentService = std.getService('affine:attachment');
if (!attachmentService) return;
const maxFileSize = attachmentService.maxFileSize;
const maxFileSize = std.store.get(FileSizeLimitService).maxFileSize;
await addSiblingAttachmentBlocks(std.host, [file], maxFileSize, model);
if (model.text?.length === 0) {
@@ -51,6 +51,7 @@ import {
import { REFERENCE_NODE } from '@blocksuite/affine-shared/consts';
import {
FeatureFlagService,
FileSizeLimitService,
TelemetryProvider,
} from '@blocksuite/affine-shared/services';
import {
@@ -324,10 +325,8 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
const file = await openFileOrFiles();
if (!file) return;
const attachmentService =
rootComponent.std.getService('affine:attachment');
if (!attachmentService) return;
const maxFileSize = attachmentService.maxFileSize;
const maxFileSize =
rootComponent.std.store.get(FileSizeLimitService).maxFileSize;
await addSiblingAttachmentBlocks(
rootComponent.host,
@@ -4,7 +4,7 @@ import { ServerService } from '@affine/core/modules/cloud';
import {
customImageProxyMiddleware,
type DocMode,
ImageBlockService,
ImageProxyService,
LinkPreviewerService,
} from '@blocksuite/affine/blocks';
import { DisposableGroup } from '@blocksuite/affine/global/utils';
@@ -75,14 +75,14 @@ const BlockSuiteEditorImpl = ({
BUILD_CONFIG.linkPreviewUrl,
server.baseUrl
).toString();
editor.host?.std.clipboard.use(
customImageProxyMiddleware(imageProxyUrl)
);
ImageBlockService.setImageProxyURL(imageProxyUrl);
editor.host?.doc
.get(LinkPreviewerService)
.setEndpoint(linkPreviewUrl);
page.get(LinkPreviewerService).setEndpoint(linkPreviewUrl);
page.get(ImageProxyService).setImageProxyURL(imageProxyUrl);
return editor.host?.updateComplete;
})
@@ -23,7 +23,7 @@ import { WorkspaceService } from '@affine/core/modules/workspace';
import { i18nTime } from '@affine/i18n';
import {
customImageProxyMiddleware,
ImageBlockService,
ImageProxyService,
LinkPreviewerService,
RefNodeSlotsProvider,
} from '@blocksuite/affine/blocks';
@@ -162,7 +162,7 @@ const DetailPageImpl = () => {
).toString();
editorHost?.std.clipboard.use(customImageProxyMiddleware(imageProxyUrl));
ImageBlockService.setImageProxyURL(imageProxyUrl);
editorHost?.doc.get(ImageProxyService).setImageProxyURL(imageProxyUrl);
// provide link preview endpoint to blocksuite
editorHost?.doc.get(LinkPreviewerService).setEndpoint(linkPreviewUrl);