33import type { AnyDocumentId , DocHandle , Repo } from '@automerge/automerge-repo' ;
44import { useRepo } from '@automerge/automerge-repo-react-hooks' ;
55import { Entity , Utils } from '@graphprotocol/hypergraph' ;
6+ import { type DocumentContent , subscribeToDocumentChanges } from '@graphprotocol/hypergraph/Entity' ;
67import * as Schema from 'effect/Schema' ;
7- import { type ReactNode , createContext , useContext , useRef , useSyncExternalStore } from 'react' ;
8+ import { type ReactNode , createContext , useContext , useEffect , useRef , useState , useSyncExternalStore } from 'react' ;
89
910export type HypergraphContext = {
1011 space : string ;
1112 repo : Repo ;
1213 id : AnyDocumentId ;
1314 handle : DocHandle < Entity . DocumentContent > ;
15+ unsubscribeChangeListener : ( ) => void ;
1416} ;
1517
1618export const HypergraphReactContext = createContext < HypergraphContext | undefined > ( undefined ) ;
@@ -30,15 +32,28 @@ export function HypergraphProvider({ space, children }: { space: string; childre
3032
3133 let current = ref . current ;
3234 if ( current === undefined || space !== current . space || repo !== current . repo ) {
35+ current ?. unsubscribeChangeListener ( ) ; // unsubscribe from the previous space when switching to a new space
36+
3337 const id = Utils . idToAutomergeId ( space ) as AnyDocumentId ;
38+ const handle = repo . find < DocumentContent > ( id ) ;
39+ const unsubscribeChangeListener = subscribeToDocumentChanges ( handle ) ;
40+
3441 current = ref . current = {
3542 space,
3643 repo,
3744 id,
38- handle : repo . find ( id ) ,
45+ handle,
46+ unsubscribeChangeListener,
3947 } ;
4048 }
4149
50+ // biome-ignore lint/correctness/useExhaustiveDependencies: no need for dependencies as the unsubscribe is called from the ref
51+ useEffect ( ( ) => {
52+ return ( ) => {
53+ current ?. unsubscribeChangeListener ( ) ; // unsubscribe from the previous space when the component unmounts
54+ } ;
55+ } , [ ] ) ;
56+
4257 return < HypergraphReactContext . Provider value = { current } > { children } </ HypergraphReactContext . Provider > ;
4358}
4459
@@ -59,37 +74,17 @@ export function useDeleteEntity() {
5974
6075export function useQueryEntities < const S extends Entity . AnyNoContext > ( type : S ) {
6176 const hypergraph = useHypergraph ( ) ;
62- const equal = isEqual ( type ) ;
63-
64- // store as a map of type to array of entities of the type
65- const prevEntitiesRef = useRef < Readonly < Array < Entity . Entity < S > > > > ( [ ] ) ;
66-
67- const subscribe = ( callback : ( ) => void ) => {
68- const handleChange = ( ) => {
69- callback ( ) ;
70- } ;
71-
72- const handleDelete = ( ) => {
73- callback ( ) ;
74- } ;
75-
76- hypergraph . handle . on ( 'change' , handleChange ) ;
77- hypergraph . handle . on ( 'delete' , handleDelete ) ;
77+ const [ subscription ] = useState ( ( ) => {
78+ return Entity . subscribeToFindMany ( hypergraph . handle , type ) ;
79+ } ) ;
7880
81+ useEffect ( ( ) => {
7982 return ( ) => {
80- hypergraph . handle . off ( 'change' , handleChange ) ;
81- hypergraph . handle . off ( 'delete' , handleDelete ) ;
83+ subscription . unsubscribe ( ) ;
8284 } ;
83- } ;
85+ } , [ subscription ] ) ;
8486
85- return useSyncExternalStore < Readonly < Array < Entity . Entity < S > > > > ( subscribe , ( ) => {
86- const filtered = Entity . findMany ( hypergraph . handle , type ) ;
87- if ( ! equal ( filtered , prevEntitiesRef . current ) ) {
88- prevEntitiesRef . current = filtered ;
89- }
90-
91- return prevEntitiesRef . current ;
92- } ) ;
87+ return useSyncExternalStore ( subscription . listener , subscription . getEntities , ( ) => [ ] ) ;
9388}
9489
9590export function useQueryEntity < const S extends Entity . AnyNoContext > ( type : S , id : string ) {
@@ -135,22 +130,3 @@ export function useQueryEntity<const S extends Entity.AnyNoContext>(type: S, id:
135130 return prevEntityRef . current ;
136131 } ) ;
137132}
138-
139- /** @internal */
140- const isEqual = < A , E > ( type : Schema . Schema < A , E , never > ) => {
141- const equals = Schema . equivalence ( type ) ;
142-
143- return ( a : ReadonlyArray < A > , b : ReadonlyArray < A > ) => {
144- if ( a . length !== b . length ) {
145- return false ;
146- }
147-
148- for ( let i = 0 ; i < a . length ; i ++ ) {
149- if ( ! equals ( a [ i ] , b [ i ] ) ) {
150- return false ;
151- }
152- }
153-
154- return true ;
155- } ;
156- } ;
0 commit comments