UNPKG

@wordpress/core-data

Version:
185 lines (184 loc) 5.58 kB
// packages/core-data/src/awareness/post-editor-awareness.ts import { dispatch, select, subscribe } from "@wordpress/data"; import { Y } from "@wordpress/sync"; import { store as blockEditorStore } from "@wordpress/block-editor"; import { BaseAwarenessState, baseEqualityFieldChecks } from "./base-awareness.mjs"; import { AWARENESS_CURSOR_UPDATE_THROTTLE_IN_MS, LOCAL_CURSOR_UPDATE_DEBOUNCE_IN_MS } from "./config.mjs"; import { STORE_NAME as coreStore } from "../name.mjs"; import { areSelectionsStatesEqual, getSelectionState } from "../utils/crdt-user-selections.mjs"; var PostEditorAwareness = class extends BaseAwarenessState { constructor(doc, kind, name, postId) { super(doc); this.kind = kind; this.name = name; this.postId = postId; } equalityFieldChecks = { ...baseEqualityFieldChecks, editorState: this.areEditorStatesEqual }; onSetUp() { super.onSetUp(); this.subscribeToCollaboratorSelectionChanges(); } /** * Subscribe to collaborator selection changes and update the selection state. */ subscribeToCollaboratorSelectionChanges() { const { getSelectionStart, getSelectionEnd, getSelectedBlocksInitialCaretPosition } = select(blockEditorStore); let selectionStart = getSelectionStart(); let selectionEnd = getSelectionEnd(); let localCursorTimeout = null; subscribe(() => { const newSelectionStart = getSelectionStart(); const newSelectionEnd = getSelectionEnd(); if (newSelectionStart === selectionStart && newSelectionEnd === selectionEnd) { return; } selectionStart = newSelectionStart; selectionEnd = newSelectionEnd; const initialPosition = getSelectedBlocksInitialCaretPosition(); void this.updateSelectionInEntityRecord( selectionStart, selectionEnd, initialPosition ); if (localCursorTimeout) { clearTimeout(localCursorTimeout); } localCursorTimeout = setTimeout(() => { const selectionState = getSelectionState( selectionStart, selectionEnd, this.doc ); this.setThrottledLocalStateField( "editorState", { selection: selectionState }, AWARENESS_CURSOR_UPDATE_THROTTLE_IN_MS ); }, LOCAL_CURSOR_UPDATE_DEBOUNCE_IN_MS); }); } /** * Update the entity record with the current collaborator's selection. * * @param selectionStart - The start position of the selection. * @param selectionEnd - The end position of the selection. * @param initialPosition - The initial position of the selection. */ async updateSelectionInEntityRecord(selectionStart, selectionEnd, initialPosition) { const edits = { selection: { selectionStart, selectionEnd, initialPosition } }; const options = { undoIgnore: true }; dispatch(coreStore).editEntityRecord( this.kind, this.name, this.postId, edits, options ); } /** * Check if two editor states are equal. * * @param state1 - The first editor state. * @param state2 - The second editor state. * @return True if the editor states are equal, false otherwise. */ areEditorStatesEqual(state1, state2) { if (!state1 || !state2) { return state1 === state2; } return areSelectionsStatesEqual(state1.selection, state2.selection); } /** * Get the absolute position index from a selection cursor. * * @param selection - The selection cursor. * @return The absolute position index, or null if not found. */ getAbsolutePositionIndex(selection) { return Y.createAbsolutePositionFromRelativePosition( selection.cursorPosition.relativePosition, this.doc )?.index ?? null; } /** * Type guard to check if a struct is a Y.Item (not Y.GC) * @param struct - The struct to check. * @return True if the struct is a Y.Item, false otherwise. */ isYItem(struct) { return "content" in struct; } /** * Get data for debugging, using the awareness state. * * @return {YDocDebugData} The debug data. */ getDebugData() { const ydoc = this.doc; const docData = Object.fromEntries( Array.from(ydoc.share, ([key, value]) => [ key, value.toJSON() ]) ); const collaboratorMapData = new Map( Array.from(this.getSeenStates().entries()).map( ([clientId, collaboratorState]) => [ String(clientId), { name: collaboratorState.collaboratorInfo.name, wpUserId: collaboratorState.collaboratorInfo.id } ] ) ); const serializableClientItems = {}; ydoc.store.clients.forEach((structs, clientId) => { const items = structs.filter(this.isYItem); serializableClientItems[clientId] = items.map((item) => { const { left, right, ...rest } = item; return { ...rest, left: left ? { id: left.id, length: left.length, origin: left.origin, content: left.content } : null, right: right ? { id: right.id, length: right.length, origin: right.origin, content: right.content } : null }; }); }); return { doc: docData, clients: serializableClientItems, collaboratorMap: Object.fromEntries(collaboratorMapData) }; } }; export { PostEditorAwareness }; //# sourceMappingURL=post-editor-awareness.mjs.map