From 8c5632f6394a816e34f1e48dc51a482d15c48318 Mon Sep 17 00:00:00 2001 From: Nik Graf Date: Tue, 24 Jun 2025 14:27:49 +0200 Subject: [PATCH 1/2] remove repo and handle from HypergraphSpace context --- .../events/src/routes/space/$spaceId/chat.tsx | 15 +--- .../src/HypergraphSpaceContext.tsx | 77 +++++++++---------- 2 files changed, 39 insertions(+), 53 deletions(-) diff --git a/apps/events/src/routes/space/$spaceId/chat.tsx b/apps/events/src/routes/space/$spaceId/chat.tsx index 4121bcda..0e32f08d 100644 --- a/apps/events/src/routes/space/$spaceId/chat.tsx +++ b/apps/events/src/routes/space/$spaceId/chat.tsx @@ -1,8 +1,6 @@ import { SpaceChat } from '@/components/SpaceChat'; -import { store } from '@graphprotocol/hypergraph'; -import { HypergraphSpaceProvider, useHypergraphApp } from '@graphprotocol/hypergraph-react'; +import { useHypergraphApp } from '@graphprotocol/hypergraph-react'; import { createFileRoute } from '@tanstack/react-router'; -import { useSelector } from '@xstate/store/react'; import { useEffect } from 'react'; export const Route = createFileRoute('/space/$spaceId/chat')({ @@ -11,7 +9,6 @@ export const Route = createFileRoute('/space/$spaceId/chat')({ function RouteComponent() { const { spaceId } = Route.useParams(); - const spaces = useSelector(store, (state) => state.context.spaces); const { subscribeToSpace, isConnecting } = useHypergraphApp(); useEffect(() => { if (!isConnecting) { @@ -19,21 +16,13 @@ function RouteComponent() { } }, [isConnecting, subscribeToSpace, spaceId]); - const space = spaces.find((space) => space.id === spaceId); - if (isConnecting) { return
Loading …
; } - if (!space) { - return
Space not found
; - } - return (
- - - +
); } diff --git a/packages/hypergraph-react/src/HypergraphSpaceContext.tsx b/packages/hypergraph-react/src/HypergraphSpaceContext.tsx index 85677479..27d99957 100644 --- a/packages/hypergraph-react/src/HypergraphSpaceContext.tsx +++ b/packages/hypergraph-react/src/HypergraphSpaceContext.tsx @@ -1,17 +1,12 @@ 'use client'; -import type { AnyDocumentId, DocHandle, Repo } from '@automerge/automerge-repo'; +import type { AnyDocumentId } from '@automerge/automerge-repo'; import { useRepo } from '@automerge/automerge-repo-react-hooks'; import { Entity, Utils } from '@graphprotocol/hypergraph'; import * as Schema from 'effect/Schema'; import { type ReactNode, createContext, useContext, useMemo, useRef, useState, useSyncExternalStore } from 'react'; -export type HypergraphContext = { - space: string; - repo: Repo; - id: AnyDocumentId; - handle: DocHandle; -}; +export type HypergraphContext = { space: string }; export const HypergraphReactContext = createContext(undefined); @@ -25,48 +20,48 @@ function useHypergraphSpaceInternal() { } export function HypergraphSpaceProvider({ space, children }: { space: string; children: ReactNode }) { - const repo = useRepo(); - const ref = useRef(undefined); + return {children}; +} - let current = ref.current; - if (current === undefined || space !== current.space || repo !== current.repo) { - const id = Utils.idToAutomergeId(space) as AnyDocumentId; +function useAutomergeHandle({ spaceId }: { spaceId: string }) { + const repo = useRepo(); + const handle = useMemo(() => { + const id = Utils.idToAutomergeId(spaceId) as AnyDocumentId; const result = repo.findWithProgress(id); + return result.handle; + }, [spaceId, repo]); - current = ref.current = { - space, - repo, - id, - handle: result.handle, - }; - } - - return {children}; + return handle; } export function useCreateEntity(type: S) { - const hypergraph = useHypergraphSpaceInternal(); - return Entity.create(hypergraph.handle, type); + const { space } = useHypergraphSpaceInternal(); + const handle = useAutomergeHandle({ spaceId: space }); + return Entity.create(handle, type); } export function useUpdateEntity(type: S) { - const hypergraph = useHypergraphSpaceInternal(); - return Entity.update(hypergraph.handle, type); + const { space } = useHypergraphSpaceInternal(); + const handle = useAutomergeHandle({ spaceId: space }); + return Entity.update(handle, type); } export function useDeleteEntity() { - const hypergraph = useHypergraphSpaceInternal(); - return Entity.markAsDeleted(hypergraph.handle); + const { space } = useHypergraphSpaceInternal(); + const handle = useAutomergeHandle({ spaceId: space }); + return Entity.markAsDeleted(handle); } export function useRemoveRelation() { - const hypergraph = useHypergraphSpaceInternal(); - return Entity.removeRelation(hypergraph.handle); + const { space } = useHypergraphSpaceInternal(); + const handle = useAutomergeHandle({ spaceId: space }); + return Entity.removeRelation(handle); } export function useHardDeleteEntity() { - const hypergraph = useHypergraphSpaceInternal(); - return Entity.delete(hypergraph.handle); + const { space } = useHypergraphSpaceInternal(); + const handle = useAutomergeHandle({ spaceId: space }); + return Entity.delete(handle); } type QueryParams = { @@ -78,8 +73,9 @@ type QueryParams = { export function useQueryLocal(type: S, params?: QueryParams) { const { enabled = true, filter, include } = params ?? {}; const entitiesRef = useRef[]>([]); + const { space } = useHypergraphSpaceInternal(); + const handle = useAutomergeHandle({ spaceId: space }); - const hypergraph = useHypergraphSpaceInternal(); const [subscription] = useState(() => { if (!enabled) { return { @@ -88,7 +84,7 @@ export function useQueryLocal(type: S, para }; } - return Entity.subscribeToFindMany(hypergraph.handle, type, filter, include); + return Entity.subscribeToFindMany(handle, type, filter, include); }); // TODO: allow to change the enabled state @@ -116,7 +112,8 @@ export function useQueryEntity( id: string, include?: { [K in keyof Schema.Schema.Type]?: Record }, ) { - const hypergraph = useHypergraphSpaceInternal(); + const { space } = useHypergraphSpaceInternal(); + const handle = useAutomergeHandle({ spaceId: space }); const prevEntityRef = useRef | undefined>(undefined); const equals = Schema.equivalence(type); @@ -129,22 +126,22 @@ export function useQueryEntity( callback(); }; - hypergraph.handle.on('change', handleChange); - hypergraph.handle.on('delete', handleDelete); + handle.on('change', handleChange); + handle.on('delete', handleDelete); return () => { - hypergraph.handle.off('change', handleChange); - hypergraph.handle.off('delete', handleDelete); + handle.off('change', handleChange); + handle.off('delete', handleDelete); }; }; return useSyncExternalStore(subscribe, () => { - const doc = hypergraph.handle.doc(); + const doc = handle.doc(); if (doc === undefined) { return prevEntityRef.current; } - const found = Entity.findOne(hypergraph.handle, type, include)(id); + const found = Entity.findOne(handle, type, include)(id); if (found === undefined && prevEntityRef.current !== undefined) { // entity was maybe deleted, delete from the ref prevEntityRef.current = undefined; From a5d51ed881c22f8b86f0e0b4dec8ebaaacd90750 Mon Sep 17 00:00:00 2001 From: Nik Graf Date: Tue, 24 Jun 2025 18:18:33 +0200 Subject: [PATCH 2/2] rework space context --- apps/events/src/components/create-events.tsx | 6 +- .../create-properties-and-types-event.tsx | 5 +- .../create-properties-and-types-todos.tsx | 7 +- apps/events/src/components/dev-tool.tsx | 11 +- .../src/components/todo/todos-public.tsx | 18 +-- apps/events/src/components/todos.tsx | 6 + apps/events/src/components/todos2.tsx | 10 +- apps/events/src/components/users.tsx | 9 +- .../src/components/users/users-public.tsx | 9 +- apps/events/src/routes/playground.lazy.tsx | 4 +- .../events/src/routes/space/$spaceId/chat.tsx | 8 +- .../src/routes/space/$spaceId/index.tsx | 18 +-- .../src/routes/space/$spaceId/playground.tsx | 17 +-- .../space/$spaceId/public-integration.tsx | 19 +-- .../src/routes/space/$spaceId/users.tsx | 17 +-- .../src/HypergraphSpaceContext.tsx | 124 ++++++++++++------ packages/hypergraph-react/src/index.ts | 2 +- .../src/internal/use-query-public.tsx | 4 +- packages/hypergraph-react/src/use-query.tsx | 5 +- .../test/HypergraphSpaceContext.test.tsx | 12 +- packages/hypergraph/src/entity/findMany.ts | 10 +- 21 files changed, 154 insertions(+), 167 deletions(-) diff --git a/apps/events/src/components/create-events.tsx b/apps/events/src/components/create-events.tsx index 2677a315..20484ae9 100644 --- a/apps/events/src/components/create-events.tsx +++ b/apps/events/src/components/create-events.tsx @@ -1,6 +1,6 @@ import { getSmartAccountWalletClient } from '@/lib/smart-account'; import { type GeoSmartAccount, Graph, type Op } from '@graphprotocol/grc-20'; -import { publishOps, useHypergraphSpace } from '@graphprotocol/hypergraph-react'; +import { publishOps } from '@graphprotocol/hypergraph-react'; import { Button } from './ui/button'; const createEvents = async ({ @@ -69,9 +69,7 @@ const createEvents = async ({ } }; -export const CreateEvents = () => { - const space = useHypergraphSpace(); - +export const CreateEvents = ({ space }: { space: string }) => { return (