UNPKG

@wordpress/core-data

Version:
8 lines (7 loc) 8.76 kB
{ "version": 3, "sources": ["../../src/hooks/use-post-editor-awareness-state.ts"], "sourcesContent": ["/**\n * External dependencies\n */\nimport { useEffect, useState } from '@wordpress/element';\nimport type { Y } from '@wordpress/sync';\n\n/**\n * Internal dependencies\n */\nimport { getSyncManager } from '../sync';\nimport type {\n\tPostEditorAwarenessState as ActiveCollaborator,\n\tPostSaveEvent,\n\tYDocDebugData,\n} from '../awareness/types';\nimport type { SelectionState } from '../types';\nimport type { PostEditorAwareness } from '../awareness/post-editor-awareness';\n\ninterface ResolvedSelection {\n\ttextIndex: number | null;\n\tlocalClientId: string | null;\n}\n\ninterface AwarenessState {\n\tactiveCollaborators: ActiveCollaborator[];\n\tresolveSelection: ( selection: SelectionState ) => ResolvedSelection;\n\tgetDebugData: () => YDocDebugData;\n\tisCurrentCollaboratorDisconnected: boolean;\n}\n\nconst defaultResolvedSelection: ResolvedSelection = {\n\ttextIndex: null,\n\tlocalClientId: null,\n};\n\nconst defaultState: AwarenessState = {\n\tactiveCollaborators: [],\n\tresolveSelection: () => defaultResolvedSelection,\n\tgetDebugData: () => ( {\n\t\tdoc: {},\n\t\tclients: {},\n\t\tcollaboratorMap: {},\n\t} ),\n\tisCurrentCollaboratorDisconnected: false,\n};\n\nfunction getAwarenessState(\n\tawareness: PostEditorAwareness,\n\tnewState?: ActiveCollaborator[]\n): AwarenessState {\n\tconst activeCollaborators = newState ?? awareness.getCurrentState();\n\n\treturn {\n\t\tactiveCollaborators,\n\t\tresolveSelection: ( selection: SelectionState ) =>\n\t\t\tawareness.convertSelectionStateToAbsolute( selection ),\n\t\tgetDebugData: () => awareness.getDebugData(),\n\t\tisCurrentCollaboratorDisconnected:\n\t\t\tactiveCollaborators.find( ( collaborator ) => collaborator.isMe )\n\t\t\t\t?.isConnected === false,\n\t};\n}\n\nfunction usePostEditorAwarenessState(\n\tpostId: number | null,\n\tpostType: string | null\n): AwarenessState {\n\tconst [ state, setState ] = useState< AwarenessState >( defaultState );\n\n\tuseEffect( () => {\n\t\tif ( null === postId || null === postType ) {\n\t\t\tsetState( defaultState );\n\t\t\treturn;\n\t\t}\n\n\t\tconst objectType = `postType/${ postType }`;\n\t\tconst objectId = postId.toString();\n\t\tconst awareness = getSyncManager()?.getAwareness< PostEditorAwareness >(\n\t\t\tobjectType,\n\t\t\tobjectId\n\t\t);\n\n\t\tif ( ! awareness ) {\n\t\t\tsetState( defaultState );\n\t\t\treturn;\n\t\t}\n\n\t\tawareness.setUp();\n\n\t\t// Initialize with current awareness state.\n\t\tsetState( getAwarenessState( awareness ) );\n\n\t\tconst unsubscribe = awareness?.onStateChange(\n\t\t\t( newState: ActiveCollaborator[] ) => {\n\t\t\t\tsetState( getAwarenessState( awareness, newState ) );\n\t\t\t}\n\t\t);\n\n\t\treturn unsubscribe;\n\t}, [ postId, postType ] );\n\n\treturn state;\n}\n\n/**\n * Hook to get the active collaborators for a post editor.\n *\n * @param postId - The ID of the post.\n * @param postType - The type of the post.\n * @return {ActiveCollaborator[]} The active collaborators.\n */\nexport function useActiveCollaborators(\n\tpostId: number | null,\n\tpostType: string | null\n): ActiveCollaborator[] {\n\treturn usePostEditorAwarenessState( postId, postType ).activeCollaborators;\n}\n\n/**\n * Hook to resolve a selection state to a text index and block client ID.\n *\n * @param postId - The ID of the post.\n * @param postType - The type of the post.\n * @return A function that resolves a selection to its text index and block client ID.\n */\nexport function useResolvedSelection(\n\tpostId: number | null,\n\tpostType: string | null\n): ( selection: SelectionState ) => ResolvedSelection {\n\treturn usePostEditorAwarenessState( postId, postType ).resolveSelection;\n}\n\n/**\n * Hook to get data for debugging, using the awareness state.\n *\n * @param postId - The ID of the post.\n * @param postType - The type of the post.\n * @return {YDocDebugData} The debug data.\n */\nexport function useGetDebugData(\n\tpostId: number | null,\n\tpostType: string | null\n): YDocDebugData {\n\treturn usePostEditorAwarenessState( postId, postType ).getDebugData();\n}\n\n/**\n * Hook to check if the current collaborator is disconnected.\n *\n * @param postId - The ID of the post.\n * @param postType - The type of the post.\n * @return {boolean} Whether the current collaborator is disconnected.\n */\nexport function useIsDisconnected(\n\tpostId: number | null,\n\tpostType: string | null\n): boolean {\n\treturn usePostEditorAwarenessState( postId, postType )\n\t\t.isCurrentCollaboratorDisconnected;\n}\n\n/**\n * Hook that subscribes to the CRDT state map and returns the most recent\n * save event (timestamp + client ID). The state map is updated by\n * `markEntityAsSaved` in `@wordpress/sync`\n *\n * @param postId The ID of the post.\n * @param postType The type of the post.\n */\nexport function useLastPostSave(\n\tpostId: number | null,\n\tpostType: string | null\n): PostSaveEvent | null {\n\tconst [ lastSave, setLastSave ] = useState< PostSaveEvent | null >( null );\n\n\tuseEffect( () => {\n\t\tif ( null === postId || null === postType ) {\n\t\t\tsetLastSave( null );\n\t\t\treturn;\n\t\t}\n\n\t\tconst awareness = getSyncManager()?.getAwareness< PostEditorAwareness >(\n\t\t\t`postType/${ postType }`,\n\t\t\tpostId.toString()\n\t\t);\n\n\t\tif ( ! awareness ) {\n\t\t\tsetLastSave( null );\n\t\t\treturn;\n\t\t}\n\n\t\tawareness.setUp();\n\n\t\tconst stateMap = awareness.doc.getMap( 'state' );\n\t\tconst recordMap = awareness.doc.getMap( 'document' );\n\n\t\t// Only notify for saves that occur after the observer is\n\t\t// set up. This prevents false notifications when the Y.Doc\n\t\t// syncs historical state on page load or peer reconnect.\n\t\tconst setupTime = Date.now();\n\n\t\tconst observer = ( event: Y.YMapEvent< unknown > ) => {\n\t\t\tif ( event.keysChanged.has( 'savedAt' ) ) {\n\t\t\t\tconst savedAt = stateMap.get( 'savedAt' ) as number;\n\t\t\t\tconst savedByClientId = stateMap.get( 'savedBy' ) as number;\n\n\t\t\t\tif (\n\t\t\t\t\ttypeof savedAt === 'number' &&\n\t\t\t\t\ttypeof savedByClientId === 'number' &&\n\t\t\t\t\tsavedAt > setupTime\n\t\t\t\t) {\n\t\t\t\t\tconst postStatus = recordMap.get( 'status' ) as\n\t\t\t\t\t\t| string\n\t\t\t\t\t\t| undefined;\n\t\t\t\t\tsetLastSave( { savedAt, savedByClientId, postStatus } );\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\tstateMap.observe( observer );\n\n\t\treturn () => {\n\t\t\tstateMap.unobserve( observer );\n\t\t};\n\t}, [ postId, postType ] );\n\n\treturn lastSave;\n}\n"], "mappings": "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", "names": [] }