mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-02-23 19:49:56 +01:00
editor: fix duplicate block-id on splitting node (#9198)
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { test, expect } from "vitest";
|
||||
import { createEditor, h } from "../../../../test-utils";
|
||||
import { Heading } from "../../heading";
|
||||
import { BlockId } from "../block-id";
|
||||
|
||||
test("splitting a node with blockId should generate new blockId for the new node", async () => {
|
||||
const el = h("div", [
|
||||
h("h1", ["A heading one"], { "data-block-id": "blockid" })
|
||||
]);
|
||||
const { editor } = createEditor({
|
||||
extensions: {
|
||||
heading: Heading.configure({ levels: [1, 2, 3, 4, 5, 6] }),
|
||||
blockId: BlockId
|
||||
},
|
||||
initialContent: el.outerHTML
|
||||
});
|
||||
|
||||
editor.commands.setTextSelection(9);
|
||||
const event = new KeyboardEvent("keydown", { key: "Enter" });
|
||||
editor.view.dom.dispatchEvent(event);
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
|
||||
const headings = editor.getJSON().content;
|
||||
expect(headings?.[0].attrs?.blockId).toBe("blockid");
|
||||
expect(headings?.[1].attrs?.blockId).not.toBeUndefined();
|
||||
expect(headings?.[1].attrs?.blockId).not.toBe("blockid");
|
||||
});
|
||||
@@ -20,6 +20,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import { Extension } from "@tiptap/core";
|
||||
import { Plugin } from "@tiptap/pm/state";
|
||||
import { nanoid } from "nanoid";
|
||||
import {
|
||||
AttributeUpdate,
|
||||
BatchAttributeStep
|
||||
} from "../../utils/batch-attribute-step.js";
|
||||
|
||||
const NESTED_BLOCK_ID_TYPES = ["callout"];
|
||||
const BLOCK_ID_TYPES = [
|
||||
@@ -74,24 +78,33 @@ export const BlockId = Extension.create({
|
||||
const isDocChanged = transactions.some((tr) => tr.docChanged);
|
||||
if (!isDocChanged) return null;
|
||||
|
||||
let updated = false;
|
||||
const blockIds = new Set<string>();
|
||||
const updates: AttributeUpdate[] = [];
|
||||
const { tr } = newState;
|
||||
|
||||
tr.doc.forEach(function addBlockId(n, offset) {
|
||||
if (!n.isBlock || !BLOCK_ID_TYPES.includes(n.type.name)) return;
|
||||
if (!n.attrs.blockId) {
|
||||
const id = nanoid(8);
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore we can't use tr.setNodeMarkup as that creates
|
||||
// a new transaction for every single node update causing
|
||||
// significant performance issues
|
||||
n.attrs.blockId = id;
|
||||
updated = true;
|
||||
|
||||
const currentId = n.attrs.blockId;
|
||||
const shouldUpdateId = !currentId || blockIds.has(currentId);
|
||||
const finalId = shouldUpdateId ? nanoid(8) : currentId;
|
||||
|
||||
if (shouldUpdateId) {
|
||||
updates.push({
|
||||
pos: offset,
|
||||
attrName: "blockId",
|
||||
value: finalId
|
||||
});
|
||||
}
|
||||
|
||||
blockIds.add(finalId);
|
||||
|
||||
if (NESTED_BLOCK_ID_TYPES.includes(n.type.name))
|
||||
n.forEach((n, pos) => addBlockId(n, offset + pos + 1));
|
||||
});
|
||||
if (updated) {
|
||||
|
||||
if (updates.length > 0) {
|
||||
tr.step(new BatchAttributeStep(updates));
|
||||
tr.setMeta("ignoreEdit", true);
|
||||
return tr;
|
||||
}
|
||||
|
||||
@@ -30,7 +30,10 @@ import {
|
||||
changedDescendants,
|
||||
findParentNodeOfTypeClosestToPos
|
||||
} from "../../utils/prosemirror.js";
|
||||
import { AttributeUpdate, BatchAttributeStep } from "./batch-attribute-step.js";
|
||||
import {
|
||||
AttributeUpdate,
|
||||
BatchAttributeStep
|
||||
} from "../../utils/batch-attribute-step.js";
|
||||
|
||||
const COLLAPSIBLE_BLOCK_TYPES = [
|
||||
"paragraph",
|
||||
|
||||
Reference in New Issue
Block a user