@wordpress/core-data
Version:
Access to and manipulation of core WordPress entities.
185 lines (184 loc) • 5.58 kB
JavaScript
// 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