@wordpress/core-data
Version:
Access to and manipulation of core WordPress entities.
197 lines (196 loc) • 6.2 kB
JavaScript
// packages/core-data/src/hooks/use-post-editor-awareness-state.ts
import { usePrevious } from "@wordpress/compose";
import { useEffect, useState, useCallback } from "@wordpress/element";
import { getSyncManager } from "../sync.mjs";
import { usePostContentBlocks } from "../awareness/block-lookup.mjs";
var defaultResolvedSelection = {
richTextOffset: null,
localClientId: null,
attributeKey: null
};
var defaultState = {
activeCollaborators: [],
resolveSelection: () => defaultResolvedSelection,
getDebugData: () => ({
doc: {},
clients: {},
collaboratorMap: {}
}),
isCurrentCollaboratorDisconnected: false
};
function getAwarenessState(awareness, newState) {
const activeCollaborators = newState ?? awareness.getCurrentState();
return {
activeCollaborators,
resolveSelection: (selection, blocks) => awareness.convertSelectionStateToAbsolute(selection, blocks),
getDebugData: () => awareness.getDebugData(),
isCurrentCollaboratorDisconnected: activeCollaborators.find((collaborator) => collaborator.isMe)?.isConnected === false
};
}
function usePostEditorAwarenessState(postId, postType) {
const [state, setState] = useState(defaultState);
useEffect(() => {
if (null === postId || null === postType) {
setState(defaultState);
return;
}
const objectType = `postType/${postType}`;
const objectId = postId.toString();
const awareness = getSyncManager()?.getAwareness(
objectType,
objectId
);
if (!awareness) {
setState(defaultState);
return;
}
awareness.setUp();
setState(getAwarenessState(awareness));
const unsubscribe = awareness?.onStateChange(
(newState) => {
setState(getAwarenessState(awareness, newState));
}
);
return unsubscribe;
}, [postId, postType]);
return state;
}
function useActiveCollaborators(postId, postType) {
return usePostEditorAwarenessState(postId, postType).activeCollaborators;
}
function useResolvedSelection(postId, postType) {
const blocks = usePostContentBlocks();
const awarenessState = usePostEditorAwarenessState(postId, postType);
return useCallback(
(selection) => awarenessState.resolveSelection(selection, blocks),
[blocks, awarenessState]
);
}
function useGetDebugData(postId, postType) {
return usePostEditorAwarenessState(postId, postType).getDebugData();
}
function useIsDisconnected(postId, postType) {
return usePostEditorAwarenessState(postId, postType).isCurrentCollaboratorDisconnected;
}
function useLastPostSave(postId, postType) {
const [lastSave, setLastSave] = useState(null);
useEffect(() => {
if (null === postId || null === postType) {
setLastSave(null);
return;
}
const awareness = getSyncManager()?.getAwareness(
`postType/${postType}`,
postId.toString()
);
if (!awareness) {
setLastSave(null);
return;
}
awareness.setUp();
const stateMap = awareness.doc.getMap("state");
const recordMap = awareness.doc.getMap("document");
const setupTime = Date.now();
const observer = (event) => {
if (event.keysChanged.has("savedAt")) {
const savedAt = stateMap.get("savedAt");
const savedByClientId = stateMap.get("savedBy");
if (typeof savedAt === "number" && typeof savedByClientId === "number" && savedAt > setupTime) {
const postStatus = recordMap.get("status");
setLastSave({ savedAt, savedByClientId, postStatus });
}
}
};
stateMap.observe(observer);
return () => {
stateMap.unobserve(observer);
};
}, [postId, postType]);
return lastSave;
}
function useOnCollaboratorJoin(postId, postType, callback) {
const { activeCollaborators } = usePostEditorAwarenessState(
postId,
postType
);
const prevCollaborators = usePrevious(activeCollaborators);
useEffect(() => {
if (!prevCollaborators || prevCollaborators.length === 0) {
return;
}
const prevMap = new Map(
prevCollaborators.map((collaborator) => [
collaborator.clientId,
collaborator
])
);
const me = activeCollaborators.find(
(collaborator) => collaborator.isMe
);
for (const collaborator of activeCollaborators) {
if (!prevMap.has(collaborator.clientId) && !collaborator.isMe) {
callback(collaborator, me);
}
}
}, [activeCollaborators, prevCollaborators, callback]);
}
function useOnCollaboratorLeave(postId, postType, callback) {
const { activeCollaborators } = usePostEditorAwarenessState(
postId,
postType
);
const prevCollaborators = usePrevious(activeCollaborators);
useEffect(() => {
if (!prevCollaborators || prevCollaborators.length === 0) {
return;
}
const newMap = new Map(
activeCollaborators.map((collaborator) => [
collaborator.clientId,
collaborator
])
);
for (const prevCollab of prevCollaborators) {
if (prevCollab.isMe || !prevCollab.isConnected) {
continue;
}
const newCollab = newMap.get(prevCollab.clientId);
if (!newCollab?.isConnected) {
callback(prevCollab);
}
}
}, [activeCollaborators, prevCollaborators, callback]);
}
function useOnPostSave(postId, postType, callback) {
const { activeCollaborators } = usePostEditorAwarenessState(
postId,
postType
);
const lastPostSave = useLastPostSave(postId, postType);
const prevPostSave = usePrevious(lastPostSave);
useEffect(() => {
if (!lastPostSave) {
return;
}
if (prevPostSave && lastPostSave.savedAt === prevPostSave.savedAt) {
return;
}
const saver = activeCollaborators.find(
(collaborator) => collaborator.clientId === lastPostSave.savedByClientId && !collaborator.isMe
);
if (!saver) {
return;
}
callback(lastPostSave, saver, prevPostSave ?? null);
}, [lastPostSave, prevPostSave, activeCollaborators, callback]);
}
export {
useActiveCollaborators,
useGetDebugData,
useIsDisconnected,
useOnCollaboratorJoin,
useOnCollaboratorLeave,
useOnPostSave,
useResolvedSelection
};
//# sourceMappingURL=use-post-editor-awareness-state.mjs.map