Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ build-types
node_modules
packages/block-serialization-spec-parser/parser.js
packages/react-native-editor/bundle
packages/sync/src/quill-delta
vendor
!.*.js
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ build-types
packages/block-serialization-spec-parser/parser.js
packages/edit-site/lib
packages/react-native-editor/bundle
packages/sync/src/quill-delta
packages/url/src/test/fixtures
vendor
48 changes: 32 additions & 16 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion packages/core-data/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@
"fast-deep-equal": "^3.1.3",
"lib0": "^0.2.99",
"memize": "^2.1.0",
"quill-delta": "5.1.0",
"uuid": "^9.0.1"
},
"peerDependencies": {
Expand Down
29 changes: 11 additions & 18 deletions packages/core-data/src/utils/crdt-blocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,16 @@
import { v4 as uuidv4 } from 'uuid';
import * as math from 'lib0/math';
import * as fun from 'lib0/function';
import Delta from 'quill-delta';

/**
* WordPress dependencies
*/
import { RichTextData } from '@wordpress/rich-text';
import { Y } from '@wordpress/sync';
import { Y, Delta } from '@wordpress/sync';

// @ts-expect-error - This is a TypeScript file, and @wordpress/blocks doesn't have a tsconfig.json?
import { getBlockTypes } from '@wordpress/blocks';

/**
* Internal dependencies
*/
import type { WPBlockSelection } from '../types';

interface BlockAttributes {
[ key: string ]: unknown;
}
Expand Down Expand Up @@ -190,13 +184,13 @@ function createNewYBlock( block: Block ): YBlock {
*
* @param yblocks The blocks in the local Y.Doc.
* @param incomingBlocks Gutenberg blocks being synced.
* @param lastSelection
* @param cursorPosition The position of the cursor after the change occurs.
* @param _origin The origin of the sync, either 'syncProvider' or 'gutenberg'.
*/
export function mergeCrdtBlocks(
yblocks: Y.Array< YBlock >, // yblocks represent the blocks in the local Y.Doc
incomingBlocks: Block[], // incomingBlocks represent JSON blocks being synced, either from a peer or from the local editor
lastSelection: WPBlockSelection | null, // Last cursor position, used for hinting the diff algorithm
cursorPosition: number | null,
_origin: string // eslint-disable-line @typescript-eslint/no-unused-vars
): void {
// Ensure we are working with serializable block data.
Expand Down Expand Up @@ -313,7 +307,7 @@ export function mergeCrdtBlocks(
mergeRichTextUpdate(
blockYText,
attributeValue,
lastSelection
cursorPosition
);
} else {
currentAttributes.set(
Expand Down Expand Up @@ -346,7 +340,7 @@ export function mergeCrdtBlocks(
mergeCrdtBlocks(
yInnerBlocks,
value ?? [],
lastSelection,
cursorPosition,
_origin
);
break;
Expand Down Expand Up @@ -465,14 +459,14 @@ let localDoc: Y.Doc | null = null;
* Given a Y.Text object and an updated string value, diff the new value and
* apply the delta to the Y.Text.
*
* @param blockYText The Y.Text to update.
* @param updatedValue The updated value.
* @param lastSelection The last cursor position before this update, used to hint the diff algorithm.
* @param blockYText The Y.Text to update.
* @param updatedValue The updated value.
* @param cursorPosition The position of the cursor after the change occurs.
*/
function mergeRichTextUpdate(
blockYText: Y.Text,
updatedValue: string,
lastSelection: WPBlockSelection | null
cursorPosition: number | null
): void {
const doc = blockYText.doc;

Expand All @@ -492,10 +486,9 @@ function mergeRichTextUpdate(

const currentValueAsDelta = new Delta( blockYText.toDelta() );
const updatedValueAsDelta = new Delta( localYText.toDelta() );

const deltaDiff = currentValueAsDelta.diff(
const deltaDiff = currentValueAsDelta.diffWithCursor(
updatedValueAsDelta,
lastSelection?.offset
cursorPosition
);

blockYText.applyDelta( deltaDiff.ops );
Expand Down
16 changes: 7 additions & 9 deletions packages/core-data/src/utils/crdt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,13 @@ import { type CRDTDoc, CRDT_RECORD_MAP_KEY, Y } from '@wordpress/sync';
import { mergeCrdtBlocks, type Block, type YBlock } from './crdt-blocks';
import { type Post } from '../entity-types/post';
import { type Type } from '../entity-types';
import type { WPBlockSelection, WPSelection } from '../types';
import type { WPSelection } from '../types';

type PostChanges = Partial< Post > & {
blocks?: Block[];
selection?: WPSelection;
};

let lastSelection: WPBlockSelection | null = null;

/**
* Given a set of local changes to a post record, apply those changes to the
* local Y.Doc.
Expand Down Expand Up @@ -76,12 +74,17 @@ export function applyPostChangesToCRDTDoc(
// Block[] from local changes.
const newBlocks = ( newValue as PostChanges[ 'blocks' ] ) ?? [];

// Block changes from typing are bundled with a 'selection' update.
// Pass the resulting cursor position to the mergeCrdtBlocks function.
const cursorPosition =
changes.selection?.selectionStart?.offset ?? null;

// Merge blocks does not need `setValue` because it is operating on a
// Yjs type that is already in the Y.Doc.
mergeCrdtBlocks(
currentBlocks,
newBlocks,
lastSelection,
cursorPosition,
origin
);
break;
Expand Down Expand Up @@ -177,11 +180,6 @@ export function applyPostChangesToCRDTDoc(
}
}
} );

// Update the lastSelection for CRDT use
if ( 'selection' in changes ) {
lastSelection = changes.selection?.selectionStart ?? null;
}
}

/**
Expand Down
Loading
Loading