mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-12 04:18:54 +00:00
refactor(core): minimize comment editor (#12995)
#### PR Dependency Tree * **PR #12995** 👈 This tree was auto-generated by [Charcoal](https://github.com/danerwilliams/charcoal) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Introduced a new clipboard module, making clipboard-related functionality available for external use. * Added a comprehensive extension system for the comment editor, supporting rich text features, widgets, and configurable options. * **Bug Fixes** * Improved stability by ensuring comment highlighting features and toolbar event subscriptions handle missing dependencies gracefully, preventing potential runtime errors. * **Refactor** * Simplified comment editor view manager setup for easier configuration and maintenance. * **Chores** * Updated package exports to expose new clipboard modules and configurations. * Removed confirm modal and portal-related logic from the comment editor component. * Adjusted temporary store creation to omit adding an extra surface block under the root page. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -285,6 +285,7 @@
|
||||
"./sync": "./src/sync/index.ts",
|
||||
"./extensions/store": "./src/extensions/store.ts",
|
||||
"./extensions/view": "./src/extensions/view.ts",
|
||||
"./foundation/clipboard": "./src/foundation/clipboard.ts",
|
||||
"./foundation/store": "./src/foundation/store.ts",
|
||||
"./foundation/view": "./src/foundation/view.ts"
|
||||
},
|
||||
|
||||
1
blocksuite/affine/all/src/foundation/clipboard.ts
Normal file
1
blocksuite/affine/all/src/foundation/clipboard.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from '@blocksuite/affine-foundation/clipboard';
|
||||
@@ -94,9 +94,11 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
|
||||
}
|
||||
|
||||
get isCommentHighlighted() {
|
||||
return this.std
|
||||
.get(BlockCommentManager)
|
||||
.isBlockCommentHighlighted(this.model);
|
||||
return (
|
||||
this.std
|
||||
.getOptional(BlockCommentManager)
|
||||
?.isBlockCommentHighlighted(this.model) ?? false
|
||||
);
|
||||
}
|
||||
|
||||
convertTo = () => {
|
||||
|
||||
@@ -130,9 +130,11 @@ export class BookmarkBlockComponent extends CaptionedBlockComponent<BookmarkBloc
|
||||
}
|
||||
|
||||
get isCommentHighlighted() {
|
||||
return this.std
|
||||
.get(BlockCommentManager)
|
||||
.isBlockCommentHighlighted(this.model);
|
||||
return (
|
||||
this.std
|
||||
.getOptional(BlockCommentManager)
|
||||
?.isBlockCommentHighlighted(this.model) ?? false
|
||||
);
|
||||
}
|
||||
|
||||
handleClick = (event: MouseEvent) => {
|
||||
|
||||
@@ -392,9 +392,11 @@ export class CodeBlockComponent extends CaptionedBlockComponent<CodeBlockModel>
|
||||
}
|
||||
|
||||
get isCommentHighlighted() {
|
||||
return this.std
|
||||
.get(BlockCommentManager)
|
||||
.isBlockCommentHighlighted(this.model);
|
||||
return (
|
||||
this.std
|
||||
.getOptional(BlockCommentManager)
|
||||
?.isBlockCommentHighlighted(this.model) ?? false
|
||||
);
|
||||
}
|
||||
|
||||
override async getUpdateComplete() {
|
||||
|
||||
@@ -313,9 +313,11 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent<DatabaseBloc
|
||||
}
|
||||
|
||||
get isCommentHighlighted() {
|
||||
return this.std
|
||||
.get(BlockCommentManager)
|
||||
.isBlockCommentHighlighted(this.model);
|
||||
return (
|
||||
this.std
|
||||
.getOptional(BlockCommentManager)
|
||||
?.isBlockCommentHighlighted(this.model) ?? false
|
||||
);
|
||||
}
|
||||
|
||||
override get topContenteditableElement() {
|
||||
|
||||
@@ -63,9 +63,11 @@ export class EmbedBlockComponent<
|
||||
protected embedContainerStyle: StyleInfo = {};
|
||||
|
||||
get isCommentHighlighted() {
|
||||
return this.std
|
||||
.get(BlockCommentManager)
|
||||
.isBlockCommentHighlighted(this.model);
|
||||
return (
|
||||
this.std
|
||||
.getOptional(BlockCommentManager)
|
||||
?.isBlockCommentHighlighted(this.model) ?? false
|
||||
);
|
||||
}
|
||||
|
||||
renderEmbed = (content: () => TemplateResult) => {
|
||||
|
||||
@@ -69,9 +69,11 @@ export class ImageBlockComponent extends CaptionedBlockComponent<ImageBlockModel
|
||||
}
|
||||
|
||||
get isCommentHighlighted() {
|
||||
return this.std
|
||||
.get(BlockCommentManager)
|
||||
.isBlockCommentHighlighted(this.model);
|
||||
return (
|
||||
this.std
|
||||
.getOptional(BlockCommentManager)
|
||||
?.isBlockCommentHighlighted(this.model) ?? false
|
||||
);
|
||||
}
|
||||
|
||||
private _handleClick(event: MouseEvent) {
|
||||
|
||||
@@ -109,9 +109,11 @@ export class ParagraphBlockComponent extends CaptionedBlockComponent<ParagraphBl
|
||||
}
|
||||
|
||||
get isCommentHighlighted() {
|
||||
return this.std
|
||||
.get(BlockCommentManager)
|
||||
.isBlockCommentHighlighted(this.model);
|
||||
return (
|
||||
this.std
|
||||
.getOptional(BlockCommentManager)
|
||||
?.isBlockCommentHighlighted(this.model) ?? false
|
||||
);
|
||||
}
|
||||
|
||||
override get topContenteditableElement() {
|
||||
|
||||
@@ -20,6 +20,10 @@ import {
|
||||
isFormatSupported,
|
||||
textFormatConfigs,
|
||||
} from '@blocksuite/affine-inline-preset';
|
||||
import {
|
||||
EmbedLinkedDocBlockSchema,
|
||||
EmbedSyncedDocBlockSchema,
|
||||
} from '@blocksuite/affine-model';
|
||||
import { textConversionConfigs } from '@blocksuite/affine-rich-text';
|
||||
import {
|
||||
copySelectedModelsCommand,
|
||||
@@ -50,7 +54,11 @@ import {
|
||||
DuplicateIcon,
|
||||
LinkedPageIcon,
|
||||
} from '@blocksuite/icons/lit';
|
||||
import { type BlockComponent, BlockSelection } from '@blocksuite/std';
|
||||
import {
|
||||
type BlockComponent,
|
||||
BlockSelection,
|
||||
BlockViewIdentifier,
|
||||
} from '@blocksuite/std';
|
||||
import { toDraftModel } from '@blocksuite/store';
|
||||
import { html } from 'lit';
|
||||
import { repeat } from 'lit/directives/repeat.js';
|
||||
@@ -214,7 +222,18 @@ const turnIntoLinkedDoc = {
|
||||
id: 'f.convert-to-linked-doc',
|
||||
tooltip: 'Create Linked Doc',
|
||||
icon: LinkedPageIcon(),
|
||||
when({ chain }) {
|
||||
when({ chain, std }) {
|
||||
const supportFlavours = [
|
||||
EmbedLinkedDocBlockSchema,
|
||||
EmbedSyncedDocBlockSchema,
|
||||
].map(schema => schema.model.flavour);
|
||||
if (
|
||||
supportFlavours.some(
|
||||
flavour => !std.getOptional(BlockViewIdentifier(flavour))
|
||||
)
|
||||
)
|
||||
return false;
|
||||
|
||||
const [ok, { selectedModels }] = chain
|
||||
.pipe(getSelectedModelsCommand, {
|
||||
types: ['block', 'text'],
|
||||
|
||||
@@ -143,9 +143,11 @@ export class SurfaceRefBlockComponent extends BlockComponent<SurfaceRefBlockMode
|
||||
}
|
||||
|
||||
get isCommentHighlighted() {
|
||||
return this.std
|
||||
.get(BlockCommentManager)
|
||||
.isBlockCommentHighlighted(this.model);
|
||||
return (
|
||||
this.std
|
||||
.getOptional(BlockCommentManager)
|
||||
?.isBlockCommentHighlighted(this.model) ?? false
|
||||
);
|
||||
}
|
||||
|
||||
private readonly _handleClick = () => {
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
},
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./clipboard": "./src/clipboard.ts",
|
||||
"./store": "./src/store.ts",
|
||||
"./view": "./src/view.ts"
|
||||
},
|
||||
|
||||
@@ -43,7 +43,7 @@ const imageClipboardConfigs = [
|
||||
});
|
||||
});
|
||||
|
||||
const PlainTextClipboardConfig = ClipboardAdapterConfigExtension({
|
||||
export const PlainTextClipboardConfig = ClipboardAdapterConfigExtension({
|
||||
mimeType: 'text/plain',
|
||||
adapter: MixTextAdapter,
|
||||
priority: 70,
|
||||
|
||||
@@ -579,9 +579,11 @@ export class AffineToolbarWidget extends WidgetComponent {
|
||||
);
|
||||
|
||||
// Handles elements when resizing
|
||||
const edgelessSlots = std.get(EdgelessLegacySlotIdentifier);
|
||||
disposables.add(edgelessSlots.elementResizeStart.subscribe(dragStart));
|
||||
disposables.add(edgelessSlots.elementResizeEnd.subscribe(dragEnd));
|
||||
const edgelessSlots = std.getOptional(EdgelessLegacySlotIdentifier);
|
||||
if (edgelessSlots) {
|
||||
disposables.add(edgelessSlots.elementResizeStart.subscribe(dragStart));
|
||||
disposables.add(edgelessSlots.elementResizeEnd.subscribe(dragEnd));
|
||||
}
|
||||
|
||||
// Handles elements when hovering
|
||||
disposables.add(
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { useConfirmModal, useLitPortalFactory } from '@affine/component';
|
||||
import { LitDocEditor, type PageEditor } from '@affine/core/blocksuite/editors';
|
||||
import { getViewManager } from '@affine/core/blocksuite/manager/view';
|
||||
import { SnapshotHelper } from '@affine/core/modules/comment/services/snapshot-helper';
|
||||
import type { RichText } from '@blocksuite/affine/rich-text';
|
||||
import { ViewportElementExtension } from '@blocksuite/affine/shared/services';
|
||||
@@ -10,7 +8,6 @@ import { useFramework, useService } from '@toeverything/infra';
|
||||
import clsx from 'clsx';
|
||||
import {
|
||||
forwardRef,
|
||||
Fragment,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useImperativeHandle,
|
||||
@@ -19,45 +16,21 @@ import {
|
||||
useState,
|
||||
} from 'react';
|
||||
|
||||
import { getCommentEditorViewManager } from './specs';
|
||||
import * as styles from './style.css';
|
||||
|
||||
const usePatchSpecs = (readonly: boolean) => {
|
||||
const [reactToLit, portals] = useLitPortalFactory();
|
||||
const framework = useFramework();
|
||||
const confirmModal = useConfirmModal();
|
||||
// const confirmModal = useConfirmModal();
|
||||
|
||||
const patchedSpecs = useMemo(() => {
|
||||
const manager = getViewManager()
|
||||
.config.init()
|
||||
.foundation(framework)
|
||||
.theme(framework)
|
||||
.editorConfig(framework)
|
||||
.editorView({
|
||||
framework,
|
||||
reactToLit,
|
||||
confirmModal,
|
||||
})
|
||||
.linkedDoc(framework)
|
||||
.paragraph(false)
|
||||
.codeBlockHtmlPreview(framework).value;
|
||||
const manager = getCommentEditorViewManager(framework);
|
||||
return manager
|
||||
.get(readonly ? 'preview-page' : 'page')
|
||||
.concat([ViewportElementExtension('.comment-editor-viewport')]);
|
||||
}, [confirmModal, framework, reactToLit, readonly]);
|
||||
}, [framework, readonly]);
|
||||
|
||||
return [
|
||||
patchedSpecs,
|
||||
useMemo(
|
||||
() => (
|
||||
<>
|
||||
{portals.map(p => (
|
||||
<Fragment key={p.id}>{p.portal}</Fragment>
|
||||
))}
|
||||
</>
|
||||
),
|
||||
[portals]
|
||||
),
|
||||
] as const;
|
||||
return patchedSpecs;
|
||||
};
|
||||
|
||||
interface CommentEditorProps {
|
||||
@@ -115,7 +88,7 @@ export const CommentEditor = forwardRef<CommentEditorRef, CommentEditorProps>(
|
||||
if (!defaultSnapshotOrDoc) {
|
||||
throw new Error('Either defaultSnapshot or doc must be provided');
|
||||
}
|
||||
const [specs, portals] = usePatchSpecs(!!readonly);
|
||||
const specs = usePatchSpecs(!!readonly);
|
||||
const doc = useSnapshotDoc(defaultSnapshotOrDoc, readonly);
|
||||
const snapshotHelper = useService(SnapshotHelper);
|
||||
const editorRef = useRef<PageEditor>(null);
|
||||
@@ -196,7 +169,6 @@ export const CommentEditor = forwardRef<CommentEditorRef, CommentEditorProps>(
|
||||
className={clsx(styles.container, 'comment-editor-viewport')}
|
||||
>
|
||||
{doc && <LitDocEditor ref={editorRef} specs={specs} doc={doc} />}
|
||||
{portals}
|
||||
{!readonly && (
|
||||
<div className={styles.footer}>
|
||||
<button onClick={onCommit} className={styles.commitButton}>
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
import { createLinkedWidgetConfig } from '@affine/core/blocksuite/view-extensions/editor-config/linked';
|
||||
import { AffineEditorViewExtension } from '@affine/core/blocksuite/view-extensions/editor-view/editor-view';
|
||||
import { AffineThemeViewExtension } from '@affine/core/blocksuite/view-extensions/theme';
|
||||
import { CodeBlockViewExtension } from '@blocksuite/affine/blocks/code/view';
|
||||
import { DividerViewExtension } from '@blocksuite/affine/blocks/divider/view';
|
||||
import { LatexViewExtension as LatexBlockViewExtension } from '@blocksuite/affine/blocks/latex/view';
|
||||
import { ListViewExtension } from '@blocksuite/affine/blocks/list/view';
|
||||
import { NoteViewExtension } from '@blocksuite/affine/blocks/note/view';
|
||||
import { ParagraphViewExtension } from '@blocksuite/affine/blocks/paragraph/view';
|
||||
import { RootViewExtension } from '@blocksuite/affine/blocks/root/view';
|
||||
import {
|
||||
PeekViewExtension,
|
||||
type PeekViewService,
|
||||
} from '@blocksuite/affine/components/peek';
|
||||
import {
|
||||
type ViewExtensionContext,
|
||||
ViewExtensionManager,
|
||||
ViewExtensionProvider,
|
||||
} from '@blocksuite/affine/ext-loader';
|
||||
import { PlainTextClipboardConfig } from '@blocksuite/affine/foundation/clipboard';
|
||||
import { LatexInlineSpecExtension } from '@blocksuite/affine/inlines/latex';
|
||||
import { LatexViewExtension as LatexInlineViewExtension } from '@blocksuite/affine/inlines/latex/view';
|
||||
import { LinkInlineSpecExtension } from '@blocksuite/affine/inlines/link';
|
||||
import { LinkViewExtension } from '@blocksuite/affine/inlines/link/view';
|
||||
import { MentionInlineSpecExtension } from '@blocksuite/affine/inlines/mention';
|
||||
import { MentionViewExtension } from '@blocksuite/affine/inlines/mention/view';
|
||||
import {
|
||||
BackgroundInlineSpecExtension,
|
||||
BoldInlineSpecExtension,
|
||||
CodeInlineSpecExtension,
|
||||
ColorInlineSpecExtension,
|
||||
InlineSpecExtensions,
|
||||
ItalicInlineSpecExtension,
|
||||
StrikeInlineSpecExtension,
|
||||
UnderlineInlineSpecExtension,
|
||||
} from '@blocksuite/affine/inlines/preset';
|
||||
import { ReferenceInlineSpecExtension } from '@blocksuite/affine/inlines/reference';
|
||||
import { ReferenceViewExtension } from '@blocksuite/affine/inlines/reference/view';
|
||||
import {
|
||||
DefaultOpenDocExtension,
|
||||
DocDisplayMetaService,
|
||||
DocModeService,
|
||||
FileSizeLimitService,
|
||||
FontConfigExtension,
|
||||
fontConfigSchema,
|
||||
FontLoaderService,
|
||||
PageViewportServiceExtension,
|
||||
ThemeService,
|
||||
ToolbarRegistryExtension,
|
||||
} from '@blocksuite/affine/shared/services';
|
||||
import type { AffineTextAttributes } from '@blocksuite/affine/shared/types';
|
||||
import { InlineManagerExtension } from '@blocksuite/affine/std/inline';
|
||||
import { LinkedDocViewExtension } from '@blocksuite/affine/widgets/linked-doc/view';
|
||||
import { ToolbarViewExtension } from '@blocksuite/affine/widgets/toolbar/view';
|
||||
import { ViewportOverlayViewExtension } from '@blocksuite/affine/widgets/viewport-overlay/view';
|
||||
import type { FrameworkProvider } from '@toeverything/infra';
|
||||
import { z } from 'zod';
|
||||
|
||||
const commentEditorViewExtensionOptionsSchema = z.object({
|
||||
peekView: z.optional(z.custom<PeekViewService>()),
|
||||
fontConfig: z.optional(z.array(fontConfigSchema)),
|
||||
});
|
||||
|
||||
export type CommentEditorViewExtensionOptions = z.infer<
|
||||
typeof commentEditorViewExtensionOptionsSchema
|
||||
>;
|
||||
|
||||
class CommentEditorViewExtensionProvider extends ViewExtensionProvider<CommentEditorViewExtensionOptions> {
|
||||
override name = 'comment-editor';
|
||||
|
||||
override schema = commentEditorViewExtensionOptionsSchema;
|
||||
|
||||
override setup(
|
||||
context: ViewExtensionContext,
|
||||
options?: CommentEditorViewExtensionOptions
|
||||
) {
|
||||
super.setup(context, options);
|
||||
context.register([
|
||||
ThemeService,
|
||||
DocModeService,
|
||||
DocDisplayMetaService,
|
||||
DefaultOpenDocExtension,
|
||||
FontLoaderService,
|
||||
ToolbarRegistryExtension,
|
||||
PageViewportServiceExtension,
|
||||
FileSizeLimitService,
|
||||
|
||||
...InlineSpecExtensions,
|
||||
InlineManagerExtension<AffineTextAttributes>({
|
||||
id: 'DefaultInlineManager',
|
||||
specs: [
|
||||
BoldInlineSpecExtension.identifier,
|
||||
ItalicInlineSpecExtension.identifier,
|
||||
UnderlineInlineSpecExtension.identifier,
|
||||
StrikeInlineSpecExtension.identifier,
|
||||
CodeInlineSpecExtension.identifier,
|
||||
BackgroundInlineSpecExtension.identifier,
|
||||
ColorInlineSpecExtension.identifier,
|
||||
LatexInlineSpecExtension.identifier,
|
||||
ReferenceInlineSpecExtension.identifier,
|
||||
LinkInlineSpecExtension.identifier,
|
||||
MentionInlineSpecExtension.identifier,
|
||||
],
|
||||
}),
|
||||
|
||||
PlainTextClipboardConfig,
|
||||
]);
|
||||
|
||||
if (options?.fontConfig) {
|
||||
context.register(FontConfigExtension(options.fontConfig));
|
||||
}
|
||||
if (options?.peekView) {
|
||||
context.register(PeekViewExtension(options.peekView));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let manager: ViewExtensionManager | null = null;
|
||||
export function getCommentEditorViewManager(framework: FrameworkProvider) {
|
||||
if (!manager) {
|
||||
manager = new ViewExtensionManager([
|
||||
CommentEditorViewExtensionProvider,
|
||||
|
||||
// Blocks
|
||||
CodeBlockViewExtension,
|
||||
DividerViewExtension,
|
||||
LatexBlockViewExtension,
|
||||
ListViewExtension,
|
||||
|
||||
NoteViewExtension,
|
||||
ParagraphViewExtension,
|
||||
RootViewExtension,
|
||||
|
||||
// Inline
|
||||
LinkViewExtension,
|
||||
ReferenceViewExtension,
|
||||
MentionViewExtension,
|
||||
LatexInlineViewExtension,
|
||||
|
||||
// Widget
|
||||
ToolbarViewExtension,
|
||||
ViewportOverlayViewExtension,
|
||||
LinkedDocViewExtension,
|
||||
|
||||
// Affine side
|
||||
AffineThemeViewExtension,
|
||||
AffineEditorViewExtension,
|
||||
]);
|
||||
|
||||
manager.configure(ParagraphViewExtension, {
|
||||
getPlaceholder: () => {
|
||||
return '';
|
||||
},
|
||||
});
|
||||
|
||||
manager.configure(
|
||||
LinkedDocViewExtension,
|
||||
createLinkedWidgetConfig(framework)
|
||||
);
|
||||
}
|
||||
return manager;
|
||||
}
|
||||
@@ -130,9 +130,6 @@ export class SnapshotHelper extends Service {
|
||||
title: new Text(''),
|
||||
});
|
||||
|
||||
// Add surface block
|
||||
store.addBlock('affine:surface', {}, rootId);
|
||||
|
||||
// Add note block
|
||||
const noteId = store.addBlock('affine:note', {}, rootId);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user