snapshot of https://github.com/dream-num/univer.git (dev, 2026-09-16; blobs > 131072B omitted)
Some checks failed
📦 Build / build (push) Has been cancelled
🧪 CI Test / test shard (1/4) (push) Has been cancelled
🧪 CI Test / test shard (2/4) (push) Has been cancelled
🧪 CI Test / test shard (3/4) (push) Has been cancelled
🧪 CI Test / test shard (4/4) (push) Has been cancelled
🧪 CI Test / test (push) Has been cancelled
🧪 CI Test / eslint (push) Has been cancelled
🧪 CI Test / typecheck (push) Has been cancelled
🔫 Sync to Pro / dispatch-sync (push) Has been cancelled
🔮 Sync Mirror / sync (push) Has been cancelled
Some checks failed
📦 Build / build (push) Has been cancelled
🧪 CI Test / test shard (1/4) (push) Has been cancelled
🧪 CI Test / test shard (2/4) (push) Has been cancelled
🧪 CI Test / test shard (3/4) (push) Has been cancelled
🧪 CI Test / test shard (4/4) (push) Has been cancelled
🧪 CI Test / test (push) Has been cancelled
🧪 CI Test / eslint (push) Has been cancelled
🧪 CI Test / typecheck (push) Has been cancelled
🔫 Sync to Pro / dispatch-sync (push) Has been cancelled
🔮 Sync Mirror / sync (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,232 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { AddCommentMutation, DeleteCommentMutation, UpdateCommentRefMutation } from '@univerjs/thread-comment';
|
||||
import { Subject } from 'rxjs';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { SheetsThreadCommentRefRangeController } from '../sheets-thread-comment-ref-range.controller';
|
||||
|
||||
const sheetsMocks = vi.hoisted(() => ({
|
||||
handleRangeChange: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('@univerjs/sheets', async () => {
|
||||
const actual = await vi.importActual<typeof import('@univerjs/sheets')>('@univerjs/sheets');
|
||||
return {
|
||||
...actual,
|
||||
handleCommonRangeChangeWithEffectRefCommandsSkipNoInterests: sheetsMocks.handleRangeChange,
|
||||
};
|
||||
});
|
||||
|
||||
function createRoot(ref = 'A1', overrides: Record<string, unknown> = {}) {
|
||||
return {
|
||||
id: 'comment-1',
|
||||
threadId: 'thread-1',
|
||||
ref,
|
||||
dT: '2025-01-01T00:00:00.000Z',
|
||||
personId: 'user-1',
|
||||
text: { dataStream: 'hello\r\n', textRuns: [] },
|
||||
attachments: [],
|
||||
unitId: 'unit-1',
|
||||
subUnitId: 'sheet-1',
|
||||
...overrides,
|
||||
} as any;
|
||||
}
|
||||
|
||||
describe('SheetsThreadCommentRefRangeController', () => {
|
||||
it('should register initial comments and transform ref changes into undo/redo mutations', async () => {
|
||||
const registerDisposes: Array<ReturnType<typeof vi.fn>> = [];
|
||||
const watchDisposes: Array<ReturnType<typeof vi.fn>> = [];
|
||||
const registerCalls: Array<{ callback: (commandInfo: unknown) => unknown }> = [];
|
||||
const watchCalls: Array<{ callback: (before: unknown, after: any) => void }> = [];
|
||||
const executeCommand = vi.fn(async () => true);
|
||||
const root = createRoot('A1', { row: 0, column: 0 });
|
||||
const updates$ = new Subject<any>();
|
||||
|
||||
const controller = new SheetsThreadCommentRefRangeController(
|
||||
{
|
||||
registerRefRange: (_range: unknown, callback: (commandInfo: unknown) => unknown) => {
|
||||
const dispose = vi.fn();
|
||||
registerDisposes.push(dispose);
|
||||
registerCalls.push({ callback });
|
||||
return { dispose };
|
||||
},
|
||||
watchRange: (_unitId: string, _subUnitId: string, _range: unknown, callback: (before: unknown, after: any) => void) => {
|
||||
const dispose = vi.fn();
|
||||
watchDisposes.push(dispose);
|
||||
watchCalls.push({ callback });
|
||||
return { dispose };
|
||||
},
|
||||
} as any,
|
||||
{
|
||||
commentUpdate$: updates$,
|
||||
getComment: vi.fn(),
|
||||
} as any,
|
||||
{
|
||||
getAll: () => [{ threads: [{ unitId: 'unit-1', subUnitId: 'sheet-1', root }] }],
|
||||
} as any,
|
||||
{} as any,
|
||||
{ executeCommand } as any
|
||||
);
|
||||
|
||||
expect(registerCalls).toHaveLength(1);
|
||||
expect(watchCalls).toHaveLength(1);
|
||||
|
||||
sheetsMocks.handleRangeChange.mockReturnValueOnce({ startRow: 0, endRow: 0, startColumn: 0, endColumn: 0 });
|
||||
expect(registerCalls[0].callback({})).toEqual({ redos: [], undos: [] });
|
||||
|
||||
sheetsMocks.handleRangeChange.mockReturnValueOnce({ startRow: 2, endRow: 2, startColumn: 2, endColumn: 2 });
|
||||
expect(registerCalls[0].callback({})).toEqual({
|
||||
redos: [{
|
||||
id: UpdateCommentRefMutation.id,
|
||||
params: {
|
||||
unitId: 'unit-1',
|
||||
subUnitId: 'sheet-1',
|
||||
payload: { ref: 'C3', commentId: 'comment-1' },
|
||||
silent: false,
|
||||
},
|
||||
}],
|
||||
undos: [{
|
||||
id: UpdateCommentRefMutation.id,
|
||||
params: {
|
||||
unitId: 'unit-1',
|
||||
subUnitId: 'sheet-1',
|
||||
payload: { ref: 'A1', commentId: 'comment-1' },
|
||||
silent: false,
|
||||
},
|
||||
}],
|
||||
});
|
||||
|
||||
sheetsMocks.handleRangeChange.mockReturnValueOnce(null);
|
||||
expect(registerCalls[0].callback({})).toEqual({
|
||||
redos: [{
|
||||
id: DeleteCommentMutation.id,
|
||||
params: { unitId: 'unit-1', subUnitId: 'sheet-1', commentId: 'comment-1' },
|
||||
}],
|
||||
undos: [{
|
||||
id: AddCommentMutation.id,
|
||||
params: expect.objectContaining({
|
||||
unitId: 'unit-1',
|
||||
subUnitId: 'sheet-1',
|
||||
comment: expect.objectContaining(root),
|
||||
sync: true,
|
||||
}),
|
||||
}],
|
||||
});
|
||||
|
||||
await watchCalls[0].callback(null, { startRow: 4, endRow: 4, startColumn: 1, endColumn: 1 });
|
||||
expect(executeCommand).toHaveBeenCalledWith(UpdateCommentRefMutation.id, {
|
||||
unitId: 'unit-1',
|
||||
subUnitId: 'sheet-1',
|
||||
payload: { ref: 'B5', commentId: 'comment-1' },
|
||||
silent: true,
|
||||
}, { onlyLocal: true });
|
||||
|
||||
controller.dispose();
|
||||
});
|
||||
|
||||
it('should react to add, delete and updateRef comment updates by maintaining watchers', () => {
|
||||
const registerDisposes: Array<ReturnType<typeof vi.fn>> = [];
|
||||
const watchDisposes: Array<ReturnType<typeof vi.fn>> = [];
|
||||
const updates$ = new Subject<any>();
|
||||
const movedComment = createRoot('D4');
|
||||
const getComment = vi.fn(() => movedComment);
|
||||
|
||||
const controller = new SheetsThreadCommentRefRangeController(
|
||||
{
|
||||
registerRefRange: vi.fn(() => {
|
||||
const dispose = vi.fn();
|
||||
registerDisposes.push(dispose);
|
||||
return { dispose };
|
||||
}),
|
||||
watchRange: vi.fn(() => {
|
||||
const dispose = vi.fn();
|
||||
watchDisposes.push(dispose);
|
||||
return { dispose };
|
||||
}),
|
||||
} as any,
|
||||
{
|
||||
commentUpdate$: updates$,
|
||||
getComment,
|
||||
} as any,
|
||||
{
|
||||
getAll: () => [],
|
||||
} as any,
|
||||
{} as any,
|
||||
{ executeCommand: vi.fn() } as any
|
||||
);
|
||||
|
||||
updates$.next({
|
||||
type: 'add',
|
||||
unitId: 'unit-1',
|
||||
subUnitId: 'sheet-1',
|
||||
row: 0,
|
||||
column: 0,
|
||||
payload: createRoot('A1'),
|
||||
});
|
||||
expect(registerDisposes).toHaveLength(1);
|
||||
expect(watchDisposes).toHaveLength(1);
|
||||
|
||||
updates$.next({
|
||||
type: 'add',
|
||||
unitId: 'unit-1',
|
||||
subUnitId: 'sheet-1',
|
||||
row: 0,
|
||||
column: 0,
|
||||
payload: createRoot('A1', { parentId: 'root-1' }),
|
||||
});
|
||||
expect(registerDisposes).toHaveLength(1);
|
||||
|
||||
updates$.next({
|
||||
type: 'updateRef',
|
||||
unitId: 'unit-1',
|
||||
subUnitId: 'sheet-1',
|
||||
row: 3,
|
||||
column: 3,
|
||||
silent: false,
|
||||
payload: { commentId: 'comment-1' },
|
||||
});
|
||||
expect(getComment).toHaveBeenCalledWith('unit-1', 'sheet-1', 'comment-1');
|
||||
expect(registerDisposes[0]).toHaveBeenCalledTimes(1);
|
||||
expect(watchDisposes[0]).toHaveBeenCalledTimes(1);
|
||||
expect(registerDisposes).toHaveLength(2);
|
||||
expect(watchDisposes).toHaveLength(2);
|
||||
|
||||
updates$.next({
|
||||
type: 'updateRef',
|
||||
unitId: 'unit-1',
|
||||
subUnitId: 'sheet-1',
|
||||
row: 3,
|
||||
column: 3,
|
||||
silent: true,
|
||||
payload: { commentId: 'comment-1' },
|
||||
});
|
||||
expect(watchDisposes[1]).toHaveBeenCalledTimes(0);
|
||||
expect(watchDisposes).toHaveLength(2);
|
||||
expect(registerDisposes).toHaveLength(3);
|
||||
|
||||
updates$.next({
|
||||
type: 'delete',
|
||||
unitId: 'unit-1',
|
||||
subUnitId: 'sheet-1',
|
||||
payload: { commentId: 'comment-1' },
|
||||
});
|
||||
expect(registerDisposes[2]).toHaveBeenCalledTimes(1);
|
||||
expect(watchDisposes[1]).toHaveBeenCalledTimes(1);
|
||||
|
||||
controller.dispose();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,183 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { UniverInstanceType } from '@univerjs/core';
|
||||
import { CopySheetCommand, RemoveSheetCommand } from '@univerjs/sheets';
|
||||
import {
|
||||
AddCommentMutation,
|
||||
DeleteCommentMutation,
|
||||
} from '@univerjs/thread-comment';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { SHEET_UNIVER_THREAD_COMMENT_PLUGIN } from '../../types/const';
|
||||
import { SheetsThreadCommentResourceController } from '../sheets-thread-comment-resource.controller';
|
||||
|
||||
function createRootComment() {
|
||||
return {
|
||||
id: 'comment-1',
|
||||
threadId: 'thread-1',
|
||||
ref: 'A1',
|
||||
dT: '2025-01-01T00:00:00.000Z',
|
||||
personId: 'user-1',
|
||||
text: { dataStream: 'hello\r\n', textRuns: [] },
|
||||
attachments: [],
|
||||
unitId: 'unit-1',
|
||||
subUnitId: 'sheet-1',
|
||||
children: [{ id: 'reply-1', parentId: 'comment-1' }],
|
||||
} as any;
|
||||
}
|
||||
|
||||
describe('SheetsThreadCommentResourceController', () => {
|
||||
it('should create undo/redo mutations when removing a sheet', () => {
|
||||
let interceptorConfig: any;
|
||||
const rootComment = createRootComment();
|
||||
|
||||
const controller = new SheetsThreadCommentResourceController(
|
||||
{
|
||||
registerPluginResource: () => ({ dispose: vi.fn() }),
|
||||
} as any,
|
||||
{
|
||||
getCurrentUnitOfType: () => ({
|
||||
getUnitId: () => 'unit-1',
|
||||
getActiveSheet: () => ({ getSheetId: () => 'sheet-1' }),
|
||||
}),
|
||||
} as any,
|
||||
{
|
||||
interceptCommand: (config: any) => {
|
||||
interceptorConfig = config;
|
||||
return { dispose: vi.fn() };
|
||||
},
|
||||
} as any,
|
||||
{
|
||||
ensureMap: () => new Map([
|
||||
[rootComment.id, rootComment],
|
||||
['reply-1', { id: 'reply-1', parentId: rootComment.id }],
|
||||
]),
|
||||
} as any,
|
||||
{
|
||||
syncUpdateMutationToColla: true,
|
||||
} as any
|
||||
);
|
||||
|
||||
const result = interceptorConfig.getMutations({
|
||||
id: RemoveSheetCommand.id,
|
||||
params: {},
|
||||
});
|
||||
|
||||
expect(result.redos).toEqual([
|
||||
{
|
||||
id: DeleteCommentMutation.id,
|
||||
params: { unitId: 'unit-1', subUnitId: 'sheet-1', commentId: 'comment-1' },
|
||||
},
|
||||
]);
|
||||
expect(result.undos).toEqual([
|
||||
{
|
||||
id: AddCommentMutation.id,
|
||||
params: {
|
||||
unitId: 'unit-1',
|
||||
subUnitId: 'sheet-1',
|
||||
comment: rootComment,
|
||||
sync: false,
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
controller.dispose();
|
||||
});
|
||||
|
||||
it('should clone root comments onto the copied sheet and omit children when sync is disabled', () => {
|
||||
let interceptorConfig: any;
|
||||
const rootComment = createRootComment();
|
||||
|
||||
const controller = new SheetsThreadCommentResourceController(
|
||||
{
|
||||
registerPluginResource: () => ({ dispose: vi.fn() }),
|
||||
} as any,
|
||||
{} as any,
|
||||
{
|
||||
interceptCommand: (config: any) => {
|
||||
interceptorConfig = config;
|
||||
return { dispose: vi.fn() };
|
||||
},
|
||||
} as any,
|
||||
{
|
||||
ensureMap: () => new Map([
|
||||
[rootComment.id, rootComment],
|
||||
['reply-1', { id: 'reply-1', parentId: rootComment.id }],
|
||||
]),
|
||||
} as any,
|
||||
{
|
||||
syncUpdateMutationToColla: false,
|
||||
} as any
|
||||
);
|
||||
|
||||
const result = interceptorConfig.getMutations({
|
||||
id: CopySheetCommand.id,
|
||||
params: {
|
||||
unitId: 'unit-1',
|
||||
subUnitId: 'sheet-1',
|
||||
targetSubUnitId: 'sheet-2',
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.redos).toHaveLength(1);
|
||||
expect(result.redos[0]).toEqual(expect.objectContaining({
|
||||
id: AddCommentMutation.id,
|
||||
params: expect.objectContaining({
|
||||
unitId: 'unit-1',
|
||||
subUnitId: 'sheet-2',
|
||||
sync: true,
|
||||
}),
|
||||
}));
|
||||
expect(result.redos[0].params.comment).toEqual(expect.objectContaining({
|
||||
subUnitId: 'sheet-2',
|
||||
children: undefined,
|
||||
}));
|
||||
expect(result.redos[0].params.comment.id).not.toBe(rootComment.id);
|
||||
expect(result.redos[0].params.comment.threadId).not.toBe(rootComment.threadId);
|
||||
expect(result.undos).toEqual([
|
||||
{
|
||||
id: DeleteCommentMutation.id,
|
||||
params: {
|
||||
unitId: 'unit-1',
|
||||
subUnitId: 'sheet-2',
|
||||
commentId: result.redos[0].params.comment.id,
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
controller.dispose();
|
||||
});
|
||||
|
||||
it('registers sheet thread comment snapshot resources for sheet units', () => {
|
||||
let hook: any;
|
||||
const controller = new SheetsThreadCommentResourceController(
|
||||
{
|
||||
registerPluginResource: (nextHook: any) => {
|
||||
hook = nextHook;
|
||||
return { dispose: vi.fn() };
|
||||
},
|
||||
} as any,
|
||||
{} as any,
|
||||
{ interceptCommand: () => ({ dispose: vi.fn() }) } as any,
|
||||
{ getUnit: () => [] } as any,
|
||||
{ saveToSnapshot: (value: unknown) => value } as any
|
||||
);
|
||||
|
||||
expect(hook.pluginName).toBe(SHEET_UNIVER_THREAD_COMMENT_PLUGIN);
|
||||
expect(hook.businesses).toEqual([UniverInstanceType.UNIVER_SHEET]);
|
||||
controller.dispose();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,236 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { IDisposable, IRange, Nullable } from '@univerjs/core';
|
||||
import type { EffectRefRangeParams } from '@univerjs/sheets';
|
||||
import type { IAddCommentMutationParams, IUpdateCommentRefMutationParams } from '@univerjs/thread-comment';
|
||||
import type { ISheetThreadComment } from '../types/interfaces/i-sheet-thread-comment';
|
||||
import { Disposable, ICommandService, Inject, sequenceExecuteAsync, toDisposable } from '@univerjs/core';
|
||||
import { serializeRange, singleReferenceToGrid } from '@univerjs/engine-formula';
|
||||
import { handleCommonRangeChangeWithEffectRefCommandsSkipNoInterests, RefRangeService, SheetsSelectionsService } from '@univerjs/sheets';
|
||||
import { AddCommentMutation, DeleteCommentMutation, deserializeThreadCommentAnchor, ThreadCommentModel, UpdateCommentRefMutation } from '@univerjs/thread-comment';
|
||||
import { SheetsThreadCommentModel } from '../models/sheets-thread-comment.model';
|
||||
|
||||
export class SheetsThreadCommentRefRangeController extends Disposable {
|
||||
private _disposableMap = new Map<string, IDisposable>();
|
||||
private _watcherMap = new Map<string, IDisposable>();
|
||||
|
||||
constructor(
|
||||
@Inject(RefRangeService) private readonly _refRangeService: RefRangeService,
|
||||
@Inject(SheetsThreadCommentModel) private readonly _sheetsThreadCommentModel: SheetsThreadCommentModel,
|
||||
@Inject(ThreadCommentModel) private readonly _threadCommentModel: ThreadCommentModel,
|
||||
@Inject(SheetsSelectionsService) private readonly _selectionManagerService: SheetsSelectionsService,
|
||||
@ICommandService private readonly _commandService: ICommandService
|
||||
) {
|
||||
super();
|
||||
this._initData();
|
||||
this._initRefRange();
|
||||
}
|
||||
|
||||
private _getIdWithUnitId(unitId: string, subUnitId: string, id: string) {
|
||||
return `${unitId}-${subUnitId}-${id}`;
|
||||
}
|
||||
|
||||
private _handleRangeChange = (unitId: string, subUnitId: string, comment: ISheetThreadComment, resultRange: Nullable<IRange>, silent?: boolean) => {
|
||||
const commentId = comment.id;
|
||||
const oldRange: IRange = {
|
||||
startColumn: comment.column,
|
||||
endColumn: comment.column,
|
||||
startRow: comment.row,
|
||||
endRow: comment.row,
|
||||
};
|
||||
|
||||
if (!resultRange) {
|
||||
return {
|
||||
redos: [{
|
||||
id: DeleteCommentMutation.id,
|
||||
params: {
|
||||
unitId,
|
||||
subUnitId,
|
||||
commentId,
|
||||
},
|
||||
}],
|
||||
undos: [{
|
||||
id: AddCommentMutation.id,
|
||||
params: {
|
||||
unitId,
|
||||
subUnitId,
|
||||
comment,
|
||||
sync: true,
|
||||
} as IAddCommentMutationParams,
|
||||
}],
|
||||
};
|
||||
}
|
||||
return {
|
||||
redos: [{
|
||||
id: UpdateCommentRefMutation.id,
|
||||
params: {
|
||||
unitId,
|
||||
subUnitId,
|
||||
payload: {
|
||||
ref: serializeRange(resultRange),
|
||||
commentId,
|
||||
},
|
||||
silent,
|
||||
} as IUpdateCommentRefMutationParams,
|
||||
}],
|
||||
undos: [{
|
||||
id: UpdateCommentRefMutation.id,
|
||||
params: {
|
||||
unitId,
|
||||
subUnitId,
|
||||
payload: {
|
||||
ref: serializeRange(oldRange),
|
||||
commentId,
|
||||
},
|
||||
silent,
|
||||
} as IUpdateCommentRefMutationParams,
|
||||
}],
|
||||
};
|
||||
};
|
||||
|
||||
private _register(unitId: string, subUnitId: string, comment: ISheetThreadComment) {
|
||||
const commentId = comment.id;
|
||||
const oldRange: IRange = {
|
||||
startColumn: comment.column,
|
||||
endColumn: comment.column,
|
||||
startRow: comment.row,
|
||||
endRow: comment.row,
|
||||
};
|
||||
|
||||
this._disposableMap.set(
|
||||
this._getIdWithUnitId(unitId, subUnitId, commentId),
|
||||
this._refRangeService.registerRefRange(oldRange, (commandInfo: EffectRefRangeParams) => {
|
||||
const resultRanges = handleCommonRangeChangeWithEffectRefCommandsSkipNoInterests(oldRange, commandInfo, { selectionManagerService: this._selectionManagerService });
|
||||
const resultRange = Array.isArray(resultRanges) ? resultRanges[0] : resultRanges;
|
||||
if (resultRange && resultRange.startColumn === oldRange.startColumn && resultRange.startRow === oldRange.startRow) {
|
||||
return {
|
||||
undos: [],
|
||||
redos: [],
|
||||
};
|
||||
}
|
||||
const res = this._handleRangeChange(unitId, subUnitId, comment, resultRange, false);
|
||||
return res;
|
||||
}, unitId, subUnitId)
|
||||
);
|
||||
}
|
||||
|
||||
private _watch(unitId: string, subUnitId: string, comment: ISheetThreadComment) {
|
||||
const commentId = comment.id;
|
||||
const oldRange: IRange = {
|
||||
startColumn: comment.column,
|
||||
endColumn: comment.column,
|
||||
startRow: comment.row,
|
||||
endRow: comment.row,
|
||||
};
|
||||
this._watcherMap.set(
|
||||
this._getIdWithUnitId(unitId, subUnitId, commentId),
|
||||
this._refRangeService.watchRange(unitId, subUnitId, oldRange, (before, after) => {
|
||||
const { redos } = this._handleRangeChange(unitId, subUnitId, comment, after, true);
|
||||
sequenceExecuteAsync(redos, this._commandService, { onlyLocal: true });
|
||||
}, true)
|
||||
);
|
||||
}
|
||||
|
||||
private _unwatch(unitId: string, subUnitId: string, commentId: string) {
|
||||
const id = this._getIdWithUnitId(unitId, subUnitId, commentId);
|
||||
this._watcherMap.get(id)?.dispose();
|
||||
this._watcherMap.delete(id);
|
||||
}
|
||||
|
||||
private _unregister(unitId: string, subUnitId: string, commentId: string) {
|
||||
const id = this._getIdWithUnitId(unitId, subUnitId, commentId);
|
||||
this._disposableMap.get(id)?.dispose();
|
||||
this._disposableMap.delete(id);
|
||||
}
|
||||
|
||||
private _initData() {
|
||||
const datas = this._threadCommentModel.getAll();
|
||||
|
||||
for (const data of datas) {
|
||||
for (const thread of data.threads) {
|
||||
const { unitId, subUnitId, root } = thread;
|
||||
if (deserializeThreadCommentAnchor(root.ref)) {
|
||||
continue;
|
||||
}
|
||||
const pos = singleReferenceToGrid(root.ref);
|
||||
const sheetComment = {
|
||||
...root,
|
||||
...pos,
|
||||
};
|
||||
this._register(unitId, subUnitId, sheetComment);
|
||||
this._watch(unitId, subUnitId, sheetComment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _initRefRange() {
|
||||
this.disposeWithMe(
|
||||
this._sheetsThreadCommentModel.commentUpdate$.subscribe((option) => {
|
||||
const { unitId, subUnitId } = option;
|
||||
switch (option.type) {
|
||||
case 'add': {
|
||||
if (option.payload.parentId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const comment = {
|
||||
...option.payload,
|
||||
row: option.row,
|
||||
column: option.column,
|
||||
};
|
||||
|
||||
this._register(option.unitId, option.subUnitId, comment);
|
||||
this._watch(option.unitId, option.subUnitId, comment);
|
||||
break;
|
||||
}
|
||||
case 'delete': {
|
||||
this._unregister(unitId, subUnitId, option.payload.commentId);
|
||||
this._unwatch(unitId, subUnitId, option.payload.commentId);
|
||||
break;
|
||||
}
|
||||
case 'updateRef': {
|
||||
const comment = this._sheetsThreadCommentModel.getComment(unitId, subUnitId, option.payload.commentId);
|
||||
if (!comment) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._unregister(unitId, subUnitId, option.payload.commentId);
|
||||
const sheetComment = {
|
||||
...comment,
|
||||
row: option.row,
|
||||
column: option.column,
|
||||
};
|
||||
if (!option.silent) {
|
||||
this._unwatch(unitId, subUnitId, option.payload.commentId);
|
||||
this._watch(unitId, subUnitId, sheetComment);
|
||||
}
|
||||
|
||||
this._register(option.unitId, option.subUnitId, sheetComment);
|
||||
break;
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
this.disposeWithMe(toDisposable(() => {
|
||||
this._disposableMap.forEach((item) => {
|
||||
item.dispose();
|
||||
});
|
||||
this._disposableMap.clear();
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { IMutationInfo, Workbook } from '@univerjs/core';
|
||||
import type { ICopySheetCommandParams, IRemoveSheetCommandParams } from '@univerjs/sheets';
|
||||
import {
|
||||
Disposable,
|
||||
generateRandomId,
|
||||
Inject,
|
||||
IResourceManagerService,
|
||||
IUniverInstanceService,
|
||||
UniverInstanceType,
|
||||
} from '@univerjs/core';
|
||||
import { CopySheetCommand, RemoveSheetCommand, SheetInterceptorService } from '@univerjs/sheets';
|
||||
import {
|
||||
AddCommentMutation,
|
||||
createThreadCommentResourceHook,
|
||||
DeleteCommentMutation,
|
||||
IThreadCommentDataSourceService,
|
||||
ThreadCommentModel,
|
||||
} from '@univerjs/thread-comment';
|
||||
import { SHEET_UNIVER_THREAD_COMMENT_PLUGIN } from '../types/const';
|
||||
|
||||
export class SheetsThreadCommentResourceController extends Disposable {
|
||||
constructor(
|
||||
@IResourceManagerService private readonly _resourceManagerService: IResourceManagerService,
|
||||
@IUniverInstanceService private readonly _univerInstanceService: IUniverInstanceService,
|
||||
@Inject(SheetInterceptorService) private readonly _sheetInterceptorService: SheetInterceptorService,
|
||||
@Inject(ThreadCommentModel) private readonly _threadCommentModel: ThreadCommentModel,
|
||||
@IThreadCommentDataSourceService private readonly _threadCommentDataSourceService: IThreadCommentDataSourceService
|
||||
) {
|
||||
super();
|
||||
this._initSnapshot();
|
||||
this._initSheetChange();
|
||||
}
|
||||
|
||||
private _initSnapshot() {
|
||||
this.disposeWithMe(this._resourceManagerService.registerPluginResource(createThreadCommentResourceHook(
|
||||
this._threadCommentModel,
|
||||
this._threadCommentDataSourceService,
|
||||
SHEET_UNIVER_THREAD_COMMENT_PLUGIN,
|
||||
[UniverInstanceType.UNIVER_SHEET]
|
||||
)));
|
||||
}
|
||||
|
||||
// eslint-disable-next-line max-lines-per-function
|
||||
private _initSheetChange() {
|
||||
this.disposeWithMe(
|
||||
this._sheetInterceptorService.interceptCommand({
|
||||
// eslint-disable-next-line max-lines-per-function
|
||||
getMutations: (commandInfo) => {
|
||||
if (commandInfo.id === RemoveSheetCommand.id) {
|
||||
const params = commandInfo.params as IRemoveSheetCommandParams;
|
||||
const unitId = params.unitId || this._univerInstanceService.getCurrentUnitOfType<Workbook>(UniverInstanceType.UNIVER_SHEET)!.getUnitId();
|
||||
const subUnitId = params.subUnitId || this._univerInstanceService.getCurrentUnitOfType<Workbook>(UniverInstanceType.UNIVER_SHEET)!.getActiveSheet()?.getSheetId();
|
||||
|
||||
if (!unitId || !subUnitId) {
|
||||
return { redos: [], undos: [] };
|
||||
}
|
||||
|
||||
const commentMap = this._threadCommentModel.ensureMap(unitId, subUnitId);
|
||||
const comments = Array.from(commentMap.values()).filter((comment) => !comment.parentId);
|
||||
const shouldSync = this._threadCommentDataSourceService.syncUpdateMutationToColla;
|
||||
|
||||
const redos: IMutationInfo[] = [];
|
||||
const undos: IMutationInfo[] = [];
|
||||
|
||||
comments.forEach(({ children, ...comment }) => {
|
||||
redos.push({
|
||||
id: DeleteCommentMutation.id,
|
||||
params: {
|
||||
unitId,
|
||||
subUnitId,
|
||||
commentId: comment.id,
|
||||
},
|
||||
});
|
||||
undos.push({
|
||||
id: AddCommentMutation.id,
|
||||
params: {
|
||||
unitId,
|
||||
subUnitId,
|
||||
comment: {
|
||||
...comment,
|
||||
children: shouldSync ? children : undefined,
|
||||
},
|
||||
sync: !shouldSync,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return { redos, undos };
|
||||
} else if (commandInfo.id === CopySheetCommand.id) {
|
||||
const params = commandInfo.params as ICopySheetCommandParams & { targetSubUnitId: string };
|
||||
const { unitId, subUnitId, targetSubUnitId } = params;
|
||||
|
||||
if (!unitId || !subUnitId || !targetSubUnitId) {
|
||||
return { redos: [], undos: [] };
|
||||
}
|
||||
|
||||
const commentMap = this._threadCommentModel.ensureMap(unitId, subUnitId);
|
||||
const comments = Array.from(commentMap.values()).map((comment) => {
|
||||
return {
|
||||
...comment,
|
||||
subUnitId: targetSubUnitId,
|
||||
id: generateRandomId(),
|
||||
threadId: generateRandomId(),
|
||||
};
|
||||
}).filter((comment) => !comment.parentId);
|
||||
const shouldSync = this._threadCommentDataSourceService.syncUpdateMutationToColla;
|
||||
|
||||
const redos: IMutationInfo[] = [];
|
||||
const undos: IMutationInfo[] = [];
|
||||
|
||||
comments.forEach(({ children, ...comment }) => {
|
||||
redos.push({
|
||||
id: AddCommentMutation.id,
|
||||
params: {
|
||||
unitId,
|
||||
subUnitId: targetSubUnitId,
|
||||
comment: {
|
||||
...comment,
|
||||
children: shouldSync ? children : undefined,
|
||||
},
|
||||
sync: !shouldSync,
|
||||
},
|
||||
});
|
||||
undos.push({
|
||||
id: DeleteCommentMutation.id,
|
||||
params: {
|
||||
unitId,
|
||||
subUnitId: targetSubUnitId,
|
||||
commentId: comment.id,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return { redos, undos };
|
||||
}
|
||||
return { redos: [], undos: [] };
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user