fix(core): fix duplicate in all page (#11229)

This commit is contained in:
EYHN
2025-03-27 06:36:57 +00:00
parent d9d5aa407a
commit 9c939da6b5
2 changed files with 91 additions and 45 deletions

View File

@@ -1,20 +1,15 @@
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
import { useDocMetaHelper } from '@affine/core/components/hooks/use-block-suite-page-meta';
import { useDocCollectionHelper } from '@affine/core/components/hooks/use-block-suite-workspace-helper';
import { DocsService } from '@affine/core/modules/doc';
import { WorkspaceService } from '@affine/core/modules/workspace';
import type { DocMode } from '@blocksuite/affine/model';
import { useService } from '@toeverything/infra';
import { useCallback } from 'react';
import { applyUpdate, encodeStateAsUpdate } from 'yjs';
import { useNavigateHelper } from '../use-navigate-helper';
export function useBlockSuiteMetaHelper() {
const workspace = useService(WorkspaceService).workspace;
const { setDocMeta, getDocMeta, setDocTitle } = useDocMetaHelper();
const { createDoc } = useDocCollectionHelper(workspace.docCollection);
const { openPage } = useNavigateHelper();
const docsService = useService(DocsService);
const docRecordList = useService(DocsService).list;
// TODO-Doma
@@ -48,47 +43,11 @@ export function useBlockSuiteMetaHelper() {
const duplicate = useAsyncCallback(
async (pageId: string, openPageAfterDuplication: boolean = true) => {
const currentPagePrimaryMode =
docRecordList.doc$(pageId).value?.primaryMode$.value;
const currentPageMeta = getDocMeta(pageId);
const newPage = createDoc();
const currentPage = workspace.docCollection.getDoc(pageId);
newPage.load();
if (!currentPageMeta || !currentPage) {
return;
}
const update = encodeStateAsUpdate(currentPage.spaceDoc);
applyUpdate(newPage.spaceDoc, update);
setDocMeta(newPage.id, {
tags: currentPageMeta.tags,
});
const lastDigitRegex = /\((\d+)\)$/;
const match = currentPageMeta?.title?.match(lastDigitRegex);
const newNumber = match ? parseInt(match[1], 10) + 1 : 1;
const newPageTitle =
currentPageMeta?.title?.replace(lastDigitRegex, '') + `(${newNumber})`;
docRecordList
.doc$(newPage.id)
.value?.setPrimaryMode(currentPagePrimaryMode || ('page' as DocMode));
setDocTitle(newPage.id, newPageTitle);
const newPageId = await docsService.duplicate(pageId);
openPageAfterDuplication &&
openPage(workspace.docCollection.id, newPage.id);
openPage(workspace.docCollection.id, newPageId);
},
[
docRecordList,
getDocMeta,
createDoc,
workspace.docCollection,
setDocMeta,
setDocTitle,
openPage,
]
[docsService, openPage, workspace.docCollection.id]
);
return {

View File

@@ -168,6 +168,93 @@ export class DocsService extends Service {
release();
}
async duplicate(sourceDocId: string, _targetDocId?: string) {
const targetDocId = _targetDocId ?? this.createDoc().id;
// check if source doc is removed
if (this.list.doc$(sourceDocId).value?.trash$.value) {
console.warn(
`Template doc(id: ${sourceDocId}) is removed, skip duplicate`
);
return targetDocId;
}
const { release: sourceRelease, doc: sourceDoc } = this.open(sourceDocId);
const { release: targetRelease, doc: targetDoc } = this.open(targetDocId);
await sourceDoc.waitForSyncReady();
// duplicate doc content
try {
const sourceBsDoc = this.store.getBlockSuiteDoc(sourceDocId);
const targetBsDoc = this.store.getBlockSuiteDoc(targetDocId);
if (!sourceBsDoc) throw new Error('Source doc not found');
if (!targetBsDoc) throw new Error('Target doc not found');
// clear the target doc (both surface and note)
targetBsDoc.root?.children.forEach(child =>
targetBsDoc.deleteBlock(child)
);
const collection = this.store.getBlocksuiteCollection();
const transformer = new Transformer({
schema: getAFFiNEWorkspaceSchema(),
blobCRUD: collection.blobSync,
docCRUD: {
create: (id: string) => collection.createDoc(id).getStore({ id }),
get: (id: string) => collection.getDoc(id)?.getStore({ id }) ?? null,
delete: (id: string) => collection.removeDoc(id),
},
middlewares: [replaceIdMiddleware(collection.idGenerator)],
});
const slice = Slice.fromModels(sourceBsDoc, [
...(sourceBsDoc.root?.children ?? []),
]);
const snapshot = transformer.sliceToSnapshot(slice);
if (!snapshot) {
throw new Error('Failed to create snapshot');
}
await transformer.snapshotToSlice(
snapshot,
targetBsDoc,
targetBsDoc.root?.id
);
} catch (e) {
logger.error('Failed to duplicate doc', {
sourceDocId,
targetDocId,
originalTargetDocId: _targetDocId,
error: e,
});
} finally {
sourceRelease();
targetRelease();
}
// duplicate doc meta
targetDoc.record.setMeta({
tags: sourceDoc.meta$.value.tags,
});
// duplicate doc title
const originalTitle = sourceDoc.title$.value;
const lastDigitRegex = /\((\d+)\)$/;
const match = originalTitle.match(lastDigitRegex);
const newNumber = match ? parseInt(match[1], 10) + 1 : 1;
const newPageTitle =
originalTitle.replace(lastDigitRegex, '') + `(${newNumber})`;
targetDoc.changeDocTitle(newPageTitle);
// duplicate doc properties
const properties = sourceDoc.getProperties();
const removedProperties = ['id', 'isTemplate', 'journal'];
removedProperties.forEach(key => {
delete properties[key];
});
targetDoc.updateProperties(properties);
return targetDocId;
}
/**
* Duplicate a doc from template
* @param sourceDocId - the id of the source doc to be duplicated