snapshot of https://github.com/toeverything/AFFiNE.git (canary, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Build & Test / Analyze (javascript, affine) (push) Failing after 4s
Build & Test / Analyze (typescript, affine) (push) Failing after 3s
Build & Test / Rust test filter (push) Failing after 4s
Build & Test / Check yarn binary (push) Failing after 3s
Build & Test / E2E BlockSuite Test (3) (push) Failing after 3s
Build & Test / Analyze (javascript, blocksuite) (push) Failing after 3s
Build & Test / Analyze (typescript, blocksuite) (push) Failing after 3s
Build & Test / Mobile native build filter (push) Failing after 3s
Build & Test / E2E BlockSuite Test (1) (push) Failing after 4s
Build & Test / E2E BlockSuite Test (2) (push) Failing after 4s
Build & Test / E2E Mobile Test (1) (push) Failing after 3s
Build & Test / Unit Test (1) (push) Has been skipped
Build & Test / Unit Test (2) (push) Has been skipped
Build & Test / Unit Test (3) (push) Has been skipped
Build & Test / Unit Test (4) (push) Has been skipped
Build & Test / Unit Test (5) (push) Has been skipped
Build & Test / Native Unit Test (push) Has been skipped
Build & Test / E2E BlockSuite Test (4) (push) Failing after 3s
Build & Test / E2E BlockSuite Test (5) (push) Failing after 3s
Build & Test / E2E BlockSuite Cross Browser Test (push) Failing after 3s
Build & Test / E2E Mobile Test (2) (push) Failing after 3s
Build & Test / Build AFFiNE native (x86_64-unknown-linux-gnu) (push) Failing after 3s
Build & Test / Build Server native (push) Failing after 3s
Build & Test / Check Git Status (push) Has been skipped
Build & Test / Server Test (0, 4) (push) Has been skipped
Build & Test / Server Test (1, 4) (push) Has been skipped
Build & Test / Server Test (2, 4) (push) Has been skipped
Build & Test / Server Test (3, 4) (push) Has been skipped
Build & Test / Server Test with Elasticsearch (push) Has been skipped
Build & Test / Build @affine/electron renderer (push) Failing after 3s
Build & Test / Copilot test filter (push) Failing after 4s
Build & Test / Lint Rust (push) Has been skipped
Build & Test / Server Test with Embedded Search (push) Has been skipped
Build & Test / Server Test with Manticore Search (push) Has been skipped
Build & Test / Server E2E Test (push) Has been skipped
Build & Test / Cloud Desktop E2E Test (push) Has been skipped
Build & Test / Cloud E2E Test 1/2 (push) Has been skipped
Build & Test / Cloud E2E Test 2/2 (push) Has been skipped
Build & Test / Build Android app (push) Has been skipped
Build & Test / Test Android app (push) Has been skipped
Build & Test / Run native tests (push) Has been skipped
Build & Test / Server Copilot Api Test (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (1, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (2, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (3, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (4, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (5, 5) (push) Has been skipped
Release / Canary Gate (push) Has been skipped
Build & Test / Build AFFiNE native (aarch64-apple-darwin) (push) Has been cancelled
Build & Test / Lint (push) Has been cancelled
Build & Test / Typecheck (push) Has been cancelled
Build & Test / Build AFFiNE native (x86_64-apple-darwin) (push) Has been cancelled
Build & Test / Desktop Test (ubuntu-latest, linux, x64, x86_64-unknown-linux-gnu, true) (push) Has been cancelled
Build & Test / Desktop Test (windows-latest, windows, x64, x86_64-pc-windows-msvc, true) (push) Has been cancelled
Build & Test / E2E Test (1) (push) Has been cancelled
Build & Test / E2E Test (2) (push) Has been cancelled
Build & Test / E2E Test (3) (push) Has been cancelled
Build & Test / E2E Test (4) (push) Has been cancelled
Build & Test / E2E Test (5) (push) Has been cancelled
Build & Test / Build AFFiNE native (aarch64-pc-windows-msvc) (push) Has been cancelled
Build & Test / Build AFFiNE native (x86_64-pc-windows-msvc) (push) Has been cancelled
Build & Test / Desktop Test (macos-latest, macos, arm64, aarch64-apple-darwin, true) (push) Has been cancelled
Build & Test / Desktop Test (macos-latest, macos, x64, x86_64-apple-darwin, false) (push) Has been cancelled
Build & Test / 3, 2, 1 Launch (push) Has been cancelled
Build & Test / Build iOS app (push) Has been cancelled
Copilot Test Automatically / Setup Test (push) Failing after 1s
Release / Prepare (push) Failing after 2s
Release / Release Cloud (push) Has been skipped
Release / Release Desktop (push) Has been skipped
Release / Release Docker Image (push) Has been skipped
Release / Release Mobile (push) Has been skipped
Some checks failed
Build & Test / Analyze (javascript, affine) (push) Failing after 4s
Build & Test / Analyze (typescript, affine) (push) Failing after 3s
Build & Test / Rust test filter (push) Failing after 4s
Build & Test / Check yarn binary (push) Failing after 3s
Build & Test / E2E BlockSuite Test (3) (push) Failing after 3s
Build & Test / Analyze (javascript, blocksuite) (push) Failing after 3s
Build & Test / Analyze (typescript, blocksuite) (push) Failing after 3s
Build & Test / Mobile native build filter (push) Failing after 3s
Build & Test / E2E BlockSuite Test (1) (push) Failing after 4s
Build & Test / E2E BlockSuite Test (2) (push) Failing after 4s
Build & Test / E2E Mobile Test (1) (push) Failing after 3s
Build & Test / Unit Test (1) (push) Has been skipped
Build & Test / Unit Test (2) (push) Has been skipped
Build & Test / Unit Test (3) (push) Has been skipped
Build & Test / Unit Test (4) (push) Has been skipped
Build & Test / Unit Test (5) (push) Has been skipped
Build & Test / Native Unit Test (push) Has been skipped
Build & Test / E2E BlockSuite Test (4) (push) Failing after 3s
Build & Test / E2E BlockSuite Test (5) (push) Failing after 3s
Build & Test / E2E BlockSuite Cross Browser Test (push) Failing after 3s
Build & Test / E2E Mobile Test (2) (push) Failing after 3s
Build & Test / Build AFFiNE native (x86_64-unknown-linux-gnu) (push) Failing after 3s
Build & Test / Build Server native (push) Failing after 3s
Build & Test / Check Git Status (push) Has been skipped
Build & Test / Server Test (0, 4) (push) Has been skipped
Build & Test / Server Test (1, 4) (push) Has been skipped
Build & Test / Server Test (2, 4) (push) Has been skipped
Build & Test / Server Test (3, 4) (push) Has been skipped
Build & Test / Server Test with Elasticsearch (push) Has been skipped
Build & Test / Build @affine/electron renderer (push) Failing after 3s
Build & Test / Copilot test filter (push) Failing after 4s
Build & Test / Lint Rust (push) Has been skipped
Build & Test / Server Test with Embedded Search (push) Has been skipped
Build & Test / Server Test with Manticore Search (push) Has been skipped
Build & Test / Server E2E Test (push) Has been skipped
Build & Test / Cloud Desktop E2E Test (push) Has been skipped
Build & Test / Cloud E2E Test 1/2 (push) Has been skipped
Build & Test / Cloud E2E Test 2/2 (push) Has been skipped
Build & Test / Build Android app (push) Has been skipped
Build & Test / Test Android app (push) Has been skipped
Build & Test / Run native tests (push) Has been skipped
Build & Test / Server Copilot Api Test (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (1, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (2, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (3, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (4, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (5, 5) (push) Has been skipped
Release / Canary Gate (push) Has been skipped
Build & Test / Build AFFiNE native (aarch64-apple-darwin) (push) Has been cancelled
Build & Test / Lint (push) Has been cancelled
Build & Test / Typecheck (push) Has been cancelled
Build & Test / Build AFFiNE native (x86_64-apple-darwin) (push) Has been cancelled
Build & Test / Desktop Test (ubuntu-latest, linux, x64, x86_64-unknown-linux-gnu, true) (push) Has been cancelled
Build & Test / Desktop Test (windows-latest, windows, x64, x86_64-pc-windows-msvc, true) (push) Has been cancelled
Build & Test / E2E Test (1) (push) Has been cancelled
Build & Test / E2E Test (2) (push) Has been cancelled
Build & Test / E2E Test (3) (push) Has been cancelled
Build & Test / E2E Test (4) (push) Has been cancelled
Build & Test / E2E Test (5) (push) Has been cancelled
Build & Test / Build AFFiNE native (aarch64-pc-windows-msvc) (push) Has been cancelled
Build & Test / Build AFFiNE native (x86_64-pc-windows-msvc) (push) Has been cancelled
Build & Test / Desktop Test (macos-latest, macos, arm64, aarch64-apple-darwin, true) (push) Has been cancelled
Build & Test / Desktop Test (macos-latest, macos, x64, x86_64-apple-darwin, false) (push) Has been cancelled
Build & Test / 3, 2, 1 Launch (push) Has been cancelled
Build & Test / Build iOS app (push) Has been cancelled
Copilot Test Automatically / Setup Test (push) Failing after 1s
Release / Prepare (push) Failing after 2s
Release / Release Cloud (push) Has been skipped
Release / Release Desktop (push) Has been skipped
Release / Release Docker Image (push) Has been skipped
Release / Release Mobile (push) Has been skipped
This commit is contained in:
230
blocksuite/affine/inlines/comment/src/inline-comment-manager.ts
Normal file
230
blocksuite/affine/inlines/comment/src/inline-comment-manager.ts
Normal file
@@ -0,0 +1,230 @@
|
||||
import { getInlineEditorByModel } from '@blocksuite/affine-rich-text';
|
||||
import { getSelectedBlocksCommand } from '@blocksuite/affine-shared/commands';
|
||||
import {
|
||||
BlockElementCommentManager,
|
||||
type CommentId,
|
||||
CommentProviderIdentifier,
|
||||
findAllCommentedBlocks,
|
||||
findAllCommentedElements,
|
||||
} from '@blocksuite/affine-shared/services';
|
||||
import type { AffineInlineEditor } from '@blocksuite/affine-shared/types';
|
||||
import { DisposableGroup } from '@blocksuite/global/disposable';
|
||||
import {
|
||||
LifeCycleWatcher,
|
||||
type TextRangePoint,
|
||||
TextSelection,
|
||||
} from '@blocksuite/std';
|
||||
import type { BaseSelection, BlockModel } from '@blocksuite/store';
|
||||
import { signal } from '@preact/signals-core';
|
||||
import difference from 'lodash-es/difference';
|
||||
|
||||
import {
|
||||
extractCommentIdFromDelta,
|
||||
findAllCommentedTexts,
|
||||
findCommentedTexts,
|
||||
} from './utils';
|
||||
|
||||
export class InlineCommentManager extends LifeCycleWatcher {
|
||||
static override key = 'inline-comment-manager';
|
||||
|
||||
private readonly _disposables = new DisposableGroup();
|
||||
|
||||
private readonly _highlightedCommentId$ = signal<CommentId | null>(null);
|
||||
|
||||
private get _provider() {
|
||||
return this.std.getOptional(CommentProviderIdentifier);
|
||||
}
|
||||
|
||||
override mounted() {
|
||||
const provider = this._provider;
|
||||
if (!provider) return;
|
||||
|
||||
this._init().catch(console.error);
|
||||
|
||||
this._disposables.add(provider.onCommentAdded(this._handleAddComment));
|
||||
this._disposables.add(
|
||||
provider.onCommentDeleted(id =>
|
||||
this._handleDeleteAndResolve(id, 'delete')
|
||||
)
|
||||
);
|
||||
this._disposables.add(
|
||||
provider.onCommentResolved(id =>
|
||||
this._handleDeleteAndResolve(id, 'resolve')
|
||||
)
|
||||
);
|
||||
this._disposables.add(
|
||||
provider.onCommentHighlighted(this._handleHighlightComment)
|
||||
);
|
||||
this._disposables.add(
|
||||
this.std.selection.slots.changed.subscribe(this._handleSelectionChanged)
|
||||
);
|
||||
}
|
||||
|
||||
override unmounted() {
|
||||
this._disposables.dispose();
|
||||
}
|
||||
|
||||
private async _init() {
|
||||
const provider = this._provider;
|
||||
if (!provider) return;
|
||||
|
||||
const commentsInProvider = await provider.getComments('all');
|
||||
|
||||
const commentsInEditor = this.getCommentsInEditor();
|
||||
|
||||
// remove comments that are in editor but not in provider
|
||||
// which means the comment may be removed or resolved in provider side
|
||||
difference(commentsInEditor, commentsInProvider).forEach(comment => {
|
||||
this.std
|
||||
.get(BlockElementCommentManager)
|
||||
.handleDeleteAndResolve(comment, 'delete');
|
||||
});
|
||||
}
|
||||
|
||||
getCommentsInEditor() {
|
||||
const inlineComments = [...findAllCommentedTexts(this.std.store).values()];
|
||||
|
||||
const blockComments = findAllCommentedBlocks(this.std.store).flatMap(
|
||||
block => Object.keys(block.props.comments)
|
||||
);
|
||||
|
||||
const surfaceComments = findAllCommentedElements(this.std.store).flatMap(
|
||||
element => Object.keys(element.comments)
|
||||
);
|
||||
|
||||
const commentsInEditor = [
|
||||
...new Set([...inlineComments, ...blockComments, ...surfaceComments]),
|
||||
];
|
||||
|
||||
return commentsInEditor;
|
||||
}
|
||||
|
||||
private readonly _handleAddComment = (
|
||||
id: CommentId,
|
||||
selections: BaseSelection[]
|
||||
) => {
|
||||
const needCommentTexts = selections.flatMap(selection => {
|
||||
if (!selection.is(TextSelection)) return [];
|
||||
const [_, { selectedBlocks }] = this.std.command
|
||||
.chain()
|
||||
.pipe(getSelectedBlocksCommand, {
|
||||
textSelection: selection,
|
||||
})
|
||||
.run();
|
||||
|
||||
if (!selectedBlocks) return [];
|
||||
|
||||
type MakeRequired<T, K extends keyof T> = T & {
|
||||
[key in K]: NonNullable<T[key]>;
|
||||
};
|
||||
|
||||
return selectedBlocks
|
||||
.map(
|
||||
({ model }) =>
|
||||
[model, getInlineEditorByModel(this.std, model)] as const
|
||||
)
|
||||
.filter(
|
||||
(
|
||||
pair
|
||||
): pair is [MakeRequired<BlockModel, 'text'>, AffineInlineEditor] =>
|
||||
!!pair[0].text && !!pair[1]
|
||||
)
|
||||
.map(([model, inlineEditor]) => {
|
||||
let from: TextRangePoint;
|
||||
let to: TextRangePoint | null;
|
||||
if (model.id === selection.from.blockId) {
|
||||
from = selection.from;
|
||||
to = null;
|
||||
} else if (model.id === selection.to?.blockId) {
|
||||
from = selection.to;
|
||||
to = null;
|
||||
} else {
|
||||
from = {
|
||||
blockId: model.id,
|
||||
index: 0,
|
||||
length: model.text.yText.length,
|
||||
};
|
||||
to = null;
|
||||
}
|
||||
return [new TextSelection({ from, to }), inlineEditor] as const;
|
||||
});
|
||||
});
|
||||
|
||||
if (needCommentTexts.length === 0) return;
|
||||
|
||||
needCommentTexts.forEach(([selection, inlineEditor]) => {
|
||||
inlineEditor.formatText(
|
||||
selection.from,
|
||||
{
|
||||
[`comment-${id}`]: true,
|
||||
},
|
||||
{
|
||||
withoutTransact: true,
|
||||
}
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
private readonly _handleDeleteAndResolve = (
|
||||
id: CommentId,
|
||||
type: 'delete' | 'resolve'
|
||||
) => {
|
||||
const commentedTexts = findCommentedTexts(this.std.store, id);
|
||||
if (commentedTexts.length === 0) return;
|
||||
|
||||
this.std.store.withoutTransact(() => {
|
||||
commentedTexts.forEach(selection => {
|
||||
const inlineEditor = getInlineEditorByModel(
|
||||
this.std,
|
||||
selection.from.blockId
|
||||
);
|
||||
|
||||
inlineEditor?.formatText(
|
||||
selection.from,
|
||||
{
|
||||
[`comment-${id}`]: type === 'delete' ? null : false,
|
||||
},
|
||||
{
|
||||
withoutTransact: true,
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
private readonly _handleHighlightComment = (id: CommentId | null) => {
|
||||
this._highlightedCommentId$.value = id;
|
||||
};
|
||||
|
||||
private readonly _handleSelectionChanged = (selections: BaseSelection[]) => {
|
||||
const currentHighlightedCommentId = this._highlightedCommentId$.peek();
|
||||
|
||||
if (selections.length === 1) {
|
||||
const selection = selections[0];
|
||||
|
||||
// InlineCommentManager only handle text selection
|
||||
if (!selection.is(TextSelection)) return;
|
||||
|
||||
if (!selection.isCollapsed() && currentHighlightedCommentId !== null) {
|
||||
this._provider?.highlightComment(null);
|
||||
return;
|
||||
}
|
||||
|
||||
const model = this.std.store.getModelById(selection.from.blockId);
|
||||
if (!model) return;
|
||||
|
||||
const inlineEditor = getInlineEditorByModel(this.std, model);
|
||||
if (!inlineEditor) return;
|
||||
|
||||
const delta = inlineEditor.getDeltaByRangeIndex(selection.from.index);
|
||||
if (!delta) return;
|
||||
|
||||
const commentIds = extractCommentIdFromDelta(delta);
|
||||
if (commentIds.length !== 0) return;
|
||||
}
|
||||
|
||||
if (currentHighlightedCommentId !== null) {
|
||||
this._provider?.highlightComment(null);
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user