Skip to content

How to correctly intercept and mutate block copies/changes? #1797

Open
@vitallygolovanov

Description

@vitallygolovanov

Hi! I'm having an issue with BlockNote in Next.js/React.

What I'm trying to achieve:

  • If a custom block is being copied via drag-n-drop (ctrl + drag), or a duplicate of the block is pasted from the clipboard, I want to reset some properties of a copy.

What I tried:

  • Doing editor.updateBlock() in an onChange() listener. The problem here is that the blocks returned by getChanges() still have the same block.id as the originals, so editor.updateBlock(changedBlock) updates at random, either the source of the drag, or a duplicate (probably, whichever comes higher in the document). Also, at the time when onChange fires, editor.forEachBlock() already has the new copy (with the old id), so somehow discerning the original block from that is impossible. This is a simplified example of the logic:
    editor.onChange(async (_ed, { getChanges }) => {
      const changes = getChanges();

      // First checking if there even are changes to process.
      // This is important because the `onChange` event is called on every change,
      // and we don't want to do any work if there are no changes that match our criteria.
      if (!changes.some(c => c.type === "insert" && c.block.type === TARGET_BLOCK_TYPE)) {
        return; // no changes to process
      }

      // Some logic to separate the used ids from the new ones
      const existingIds =buildExistingIdMap(_ed, changes);

      for (const change of changes) {
        if (
          change.type === "insert" &&
          change.block.type === TARGET_BLOCK_TYPE &&
          (change.source.type === "drop" || change.source.type === "paste")
        ) {
          const oldId = change.block.props.permanentId;

          // If the insert did not remove the “source” block, it’s a copy
          // (moving produces a matching delete-change for the same blockId).
          const isMove =
            changes.some(
              c =>
                c.type === "delete" &&
                c.block.id === change.block.id /* same Yjs id */
            );

          const isDuplicate = existingIds.has(oldId) && !isMove;
          if (isDuplicate) {
            const newId = uuid();

            // Patch the block inside the editor
            _ed.updateBlock(change.block, { // <- Problem here, change.block is EXACTLY the same as the source block that was being copied, down to change.block.id.
              type: TARGET_BLOCK_TYPE,
              props: { 
                permanentId: newId,
                isArchived: false,
              }
            });
          }
        }
      }
    });

So as a first step I want to determine, if I'm even attempting this in the right spot. If not, then where should I look. If it is the right spot, then what's the procedure here?

Also, this is just the first step. The second step is achieving the same behavior when dragging/copying between editors in a multiple editor setup.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions