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

This commit is contained in:
upstream-mirror
2026-09-16 05:09:45 +05:00
commit bcf6ef63ad
7118 changed files with 1236854 additions and 0 deletions

View File

@@ -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();
});
});

View File

@@ -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();
});
});

View File

@@ -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();
}));
}
}

View File

@@ -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: [] };
},
})
);
}
}