Skip to content

Commit

Permalink
Simplify data passing
Browse files Browse the repository at this point in the history
  • Loading branch information
mirka committed Oct 7, 2021
1 parent bd613a1 commit e7f0bf7
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 50 deletions.
6 changes: 5 additions & 1 deletion src/components/collaborative-editing/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import CollaborativeEditingAvatars from './components/avatars';
* Transport module for real-time collaboration
*
* @typedef CollaborationTransport
* @property {(message: CollaborationTransportDocMessage|CollaborationTransportSelectionMessage) => void} sendMessage
* @property {(message: CollaborationTransportMessage) => void} sendMessage
* @property {(options: CollaborationTransportConnectOpts) => Promise<{isFirstInChannel: boolean}>} connect
* @property {() => Promise<void>} disconnect
*/
Expand Down Expand Up @@ -60,6 +60,10 @@ import CollaborativeEditingAvatars from './components/avatars';
* @property {EditorSelection} selection
*/

/**
* @typedef {CollaborationTransportDocMessage|CollaborationTransportSelectionMessage} CollaborationTransportMessage
*/

/**
* @typedef EditorSelection
* @property {Object} start
Expand Down
75 changes: 26 additions & 49 deletions src/components/collaborative-editing/use-yjs/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { postDocToObject, updatePostDoc } from './algorithms/yjs';
/**
* WordPress dependencies
*/
import { useDispatch, useSelect } from '@wordpress/data';
import { useRegistry, useSelect } from '@wordpress/data';
import { useEffect, useRef } from '@wordpress/element';
import { addFilter } from '@wordpress/hooks';

Expand All @@ -26,37 +26,17 @@ import { registerCollabFormats } from './formats';
const debug = require( 'debug' )( 'iso-editor:collab' );

/** @typedef {import('..').CollaborationSettings} CollaborationSettings */
/** @typedef {import('..').CollaborationTransport} CollaborationTransport */
/** @typedef {import('..').CollaborationTransportDocMessage} CollaborationTransportDocMessage */
/** @typedef {import('..').CollaborationTransportSelectionMessage} CollaborationTransportSelectionMessage */
/** @typedef {import('..').EditorSelection} EditorSelection */
/** @typedef {import('../../block-editor-contents').OnUpdate} OnUpdate */

export const defaultColors = [ '#4676C0', '#6F6EBE', '#9063B6', '#C3498D', '#9E6D14', '#3B4856', '#4A807A' ];

/**
* @param {Object} opts - Hook options
* @param {() => object[]} opts.getBlocks - Content to initialize the Yjs doc with.
* @param {Function} opts.getSelection
* @param {Function} opts.setSelection
* @param {OnUpdate} opts.onRemoteDataChange - Function to update editor blocks in redux state.
* @param {CollaborationSettings} opts.settings
* @param {import('../../../store/peers/actions').setAvailablePeers} opts.setAvailablePeers
* @param {import('../../../store/peers/actions').setPeerSelection} opts.setPeerSelection
* @typedef IsoEditorSelection
* @property {Object} selectionStart
* @property {Object} selectionEnd
* @param {Object} opts
* @param {import('..').CollaborationSettings} opts.settings
* @param {Object} opts.registry - Redux data registry for this context.
*/
async function initYDoc( {
getBlocks,
getSelection,
setSelection,
onRemoteDataChange,
settings,
setPeerSelection,
setAvailablePeers,
} ) {
async function initYDoc( { settings, registry } ) {
const { channelId, transport } = settings;
const { dispatch, select } = registry;

/** @type {string} */
const identity = uuidv4();
Expand All @@ -67,16 +47,25 @@ async function initYDoc( {
identity,
applyDataChanges: updatePostDoc,
getData: postDocToObject,
getSelection,
setSelection,
getSelection: () => ( {
start: select( 'core/block-editor' ).getSelectionStart(),
end: select( 'core/block-editor' ).getSelectionEnd(),
} ),
setSelection: ( { start, end } ) =>
dispatch( 'core/block-editor' ).selectionChange(
start?.clientId,
start?.attributeKey,
start?.offset,
end?.offset
),
/** @param {Object} message */
sendMessage: ( message ) => {
debug( 'sendDocMessage', message );
transport.sendMessage( { type: 'doc', identity, message } );
},
} );

/** @param {CollaborationTransportDocMessage|CollaborationTransportSelectionMessage} data */
/** @param {import('..').CollaborationTransportMessage} data */
const onReceiveMessage = ( data ) => {
debug( 'remote change received by transport', data );

Expand All @@ -86,15 +75,15 @@ async function initYDoc( {
break;
}
case 'selection': {
setPeerSelection( data.identity, data.selection );
dispatch( 'isolated/editor' ).setPeerSelection( data.identity, data.selection );
break;
}
}
};

doc.onRemoteDataChange( ( changes ) => {
debug( 'remote change received by ydoc', changes );
onRemoteDataChange( changes.blocks );
dispatch( 'isolated/editor' ).updateBlocksWithUndo( changes.blocks );
} );

return transport
Expand All @@ -108,7 +97,7 @@ async function initYDoc( {
onReceiveMessage,
setAvailablePeers: ( peers ) => {
debug( 'setAvailablePeers', peers );
setAvailablePeers( peers );
dispatch( 'isolated/editor' ).setAvailablePeers( peers );
},
channelId,
} )
Expand All @@ -121,7 +110,7 @@ async function initYDoc( {
// Fetching the blocks from redux now, after the transport has successfully connected,
// ensures that we don't initialize the Yjs doc with stale blocks.
// (This can happen if <IsolatedBlockEditor> is given an onLoad handler.)
doc.startSharing( { title: '', blocks: getBlocks() } );
doc.startSharing( { title: '', blocks: select( 'core/block-editor' ).getBlocks() } );
} else {
doc.connect();
}
Expand Down Expand Up @@ -164,22 +153,15 @@ async function initYDoc( {
export default function useYjs( { settings } ) {
const onBlocksChange = useRef( noop );
const onSelectionChange = useRef( noop );
const registry = useRegistry();

const { getBlocks, getSelection, selectionStart, selectionEnd } = useSelect( ( select ) => {
const { selectionStart, selectionEnd } = useSelect( ( select ) => {
return {
getBlocks: select( 'isolated/editor' ).getBlocks,
getSelection: () => ( {
start: select( 'core/block-editor' ).getSelectionStart(),
end: select( 'core/block-editor' ).getSelectionEnd(),
} ),
selectionStart: select( 'core/block-editor' ).getSelectionStart(),
selectionEnd: select( 'core/block-editor' ).getSelectionEnd(),
};
}, [] );

const { setAvailablePeers, setPeerSelection, updateBlocksWithUndo } = useDispatch( 'isolated/editor' );
const { selectionChange } = useDispatch( 'core/block-editor' );

useEffect( () => {
if ( ! settings?.enabled ) {
return;
Expand All @@ -200,14 +182,8 @@ export default function useYjs( { settings } ) {
let onUnmount = noop;

initYDoc( {
onRemoteDataChange: updateBlocksWithUndo,
settings,
getBlocks,
getSelection,
setSelection: ( { start, end } ) =>
selectionChange( start?.clientId, start?.attributeKey, start?.offset, end?.offset ),
setPeerSelection,
setAvailablePeers,
registry,
} ).then( ( { applyChangesToYjs, sendSelection, undoManager, disconnect } ) => {
onUnmount = () => {
debug( 'unmount' );
Expand All @@ -216,6 +192,7 @@ export default function useYjs( { settings } ) {

onBlocksChange.current = applyChangesToYjs;
onSelectionChange.current = sendSelection;

addFilter( 'isoEditor.blockEditorProvider.onInput', 'isolated-block-editor/collab', ( onInput ) =>
over( [ onInput, applyChangesToYjs, () => debug( 'BlockEditorProvider onInput' ) ] )
);
Expand Down
14 changes: 14 additions & 0 deletions src/components/collaborative-editing/use-yjs/yjs-doc.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,23 @@ import * as yjs from 'yjs';

const debugUndo = require( 'debug' )( 'iso-editor:collab:undo' );

/** @typedef {import('./algorithms/yjs').PostObject} PostObject */
/** @typedef {import('..').EditorSelection} EditorSelection */

const encodeArray = ( array ) => array.toString();
const decodeArray = ( string ) => new Uint8Array( string.split( ',' ) );

/**
* Create a Yjs document.
*
* @param {Object} opts
* @param {string} opts.identity - Client identifier.
* @param {function(yjs.Doc, PostObject): void} opts.applyDataChanges - Function to apply changes to the Yjs doc.
* @param {function(yjs.Doc): PostObject} opts.getData - Function to get post object data from the Yjs doc.
* @param {function(): EditorSelection} opts.getSelection
* @param {function(EditorSelection): void} opts.setSelection
* @param {function(any): void} opts.sendMessage
*/
export function createDocument( { identity, applyDataChanges, getData, getSelection, setSelection, sendMessage } ) {
const doc = new yjs.Doc();
let state = 'off';
Expand Down

0 comments on commit e7f0bf7

Please sign in to comment.