From 7719f3095652a23db245c987b52149e40f5ad9d0 Mon Sep 17 00:00:00 2001 From: Byron Guina Date: Wed, 30 Jul 2025 15:53:37 -0700 Subject: [PATCH 1/9] feat: hook for publishing public data --- .../src/hooks/usePublishToSpace.ts | 28 +++++++ packages/hypergraph-react/src/index.ts | 1 + pnpm-lock.yaml | 83 ------------------- 3 files changed, 29 insertions(+), 83 deletions(-) create mode 100644 packages/hypergraph-react/src/hooks/usePublishToSpace.ts diff --git a/packages/hypergraph-react/src/hooks/usePublishToSpace.ts b/packages/hypergraph-react/src/hooks/usePublishToSpace.ts new file mode 100644 index 00000000..dec48c63 --- /dev/null +++ b/packages/hypergraph-react/src/hooks/usePublishToSpace.ts @@ -0,0 +1,28 @@ +import type { Entity } from '@graphprotocol/hypergraph'; +import { useMutation } from '@tanstack/react-query'; +import { useHypergraphApp } from '../HypergraphAppContext.js'; +import { preparePublish } from '../prepare-publish.js'; +import { publishOps } from '../publish-ops.js'; + +export function usePublishToPublicSpace(spaceId: string) { + const { getSmartSessionClient } = useHypergraphApp(); + + return useMutation({ + mutationFn: async (entity: Entity.Entity) => { + const { ops } = await preparePublish({ + entity, + publicSpace: spaceId, + }); + const smartSessionClient = await getSmartSessionClient(); + if (!smartSessionClient) { + throw new Error('Missing smartSessionClient'); + } + return await publishOps({ + ops, + space: spaceId, + name: 'Published entity', + walletClient: smartSessionClient, + }); + }, + }); +} diff --git a/packages/hypergraph-react/src/index.ts b/packages/hypergraph-react/src/index.ts index e8485cd4..f2f80723 100644 --- a/packages/hypergraph-react/src/index.ts +++ b/packages/hypergraph-react/src/index.ts @@ -22,6 +22,7 @@ export { useExternalSpaceInbox } from './hooks/useExternalSpaceInbox.js'; export { useOwnAccountInbox } from './hooks/useOwnAccountInbox.js'; export { useOwnSpaceInbox } from './hooks/useOwnSpaceInbox.js'; export { usePublicAccountInboxes } from './hooks/usePublicAccountInboxes.js'; +export { usePublishToPublicSpace } from './hooks/usePublishToSpace.js'; export { generateDeleteOps as _generateDeleteOps } from './internal/generate-delete-ops.js'; export { useCreateEntityPublic as _useCreateEntityPublic } from './internal/use-create-entity-public.js'; export { useDeleteEntityPublic as _useDeleteEntityPublic } from './internal/use-delete-entity-public.js'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a359822b..1e71f769 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -720,81 +720,6 @@ importers: version: 7.0.4(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.39.1)(tsx@4.20.3)(yaml@2.7.0) publishDirectory: dist - apps/typesync/dist: - dependencies: - '@graphprotocol/grc-20': - specifier: ^0.21.6 - version: 0.21.6(bufferutil@4.0.9)(graphql@16.11.0)(ox@0.6.7(typescript@5.8.3)(zod@3.25.51))(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.51) - '@graphprotocol/typesync': - specifier: ^0.0.3 - version: 0.0.3(bufferutil@4.0.9)(graphql@16.11.0)(ox@0.6.7(typescript@5.8.3)(zod@3.25.51))(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.51) - '@graphql-typed-document-node/core': - specifier: ^3.2.0 - version: 3.2.0(graphql@16.11.0) - '@headlessui/react': - specifier: ^2.2.4 - version: 2.2.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@heroicons/react': - specifier: ^2.2.0 - version: 2.2.0(react@19.1.0) - '@phosphor-icons/react': - specifier: ^2.1.10 - version: 2.1.10(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-tabs': - specifier: ^1.1.12 - version: 1.1.12(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@tailwindcss/vite': - specifier: ^4.1.11 - version: 4.1.11(vite@7.0.5(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.39.1)(tsx@4.20.3)(yaml@2.7.0)) - '@tanstack/react-form': - specifier: ^1.14.1 - version: 1.14.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@tanstack/react-query': - specifier: ^5.83.0 - version: 5.83.0(react@19.1.0) - '@tanstack/react-query-devtools': - specifier: ^5.83.0 - version: 5.83.0(@tanstack/react-query@5.83.0(react@19.1.0))(react@19.1.0) - '@tanstack/react-router': - specifier: ^1.129.5 - version: 1.129.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@tanstack/react-router-devtools': - specifier: ^1.129.5 - version: 1.129.5(@tanstack/react-router@1.129.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@tanstack/router-core@1.129.6)(csstype@3.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(solid-js@1.9.5)(tiny-invariant@1.3.3) - better-sqlite3: - specifier: ^12.2.0 - version: 12.2.0 - date-fns: - specifier: ^4.1.0 - version: 4.1.0 - effect: - specifier: ^3.17.3 - version: 3.17.3 - graphql: - specifier: ^16.11.0 - version: 16.11.0 - graphql-request: - specifier: ^7.2.0 - version: 7.2.0(graphql@16.11.0) - jotai: - specifier: ^2.12.5 - version: 2.12.5(@types/react@19.1.8)(react@19.1.0) - open: - specifier: ^10.2.0 - version: 10.2.0 - react: - specifier: ^19.1.0 - version: 19.1.0 - react-dom: - specifier: ^19.1.0 - version: 19.1.0(react@19.1.0) - shiki: - specifier: ^3.8.0 - version: 3.8.0 - tailwindcss: - specifier: ^4.1.11 - version: 4.1.11 - docs: dependencies: '@docusaurus/core': @@ -7942,9 +7867,6 @@ packages: effect@3.17.1: resolution: {integrity: sha512-t917ks10FGNf7MpwOxHUg6vo42p0XsdMHuBMVpy4NttPu5gIv8/ah5MgbHLVQJ2kmDvZfQUT1/xyCa1IR09u2Q==} - effect@3.17.3: - resolution: {integrity: sha512-FbFMr6xBXPII5Od8QJnkHz+2GTmQgq+8NPQev6C2k9cf1lcUjQ4vpw1kjbMc2X0UkjIkIWe0EtNK2RV6bl34UQ==} - electron-to-chromium@1.5.152: resolution: {integrity: sha512-xBOfg/EBaIlVsHipHl2VdTPJRSvErNUaqW8ejTq5OlOlIYx1wOllCHsAvAIrr55jD1IYEfdR86miUEt8H5IeJg==} @@ -23171,11 +23093,6 @@ snapshots: '@standard-schema/spec': 1.0.0 fast-check: 3.23.2 - effect@3.17.3: - dependencies: - '@standard-schema/spec': 1.0.0 - fast-check: 3.23.2 - electron-to-chromium@1.5.152: {} electron-to-chromium@1.5.52: {} From d1feaa414479c2710c0bf7fbee641d6304244335 Mon Sep 17 00:00:00 2001 From: Byron Guina Date: Wed, 30 Jul 2025 16:04:02 -0700 Subject: [PATCH 2/9] feat: move generic to mutationFn --- packages/hypergraph-react/src/hooks/usePublishToSpace.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/hypergraph-react/src/hooks/usePublishToSpace.ts b/packages/hypergraph-react/src/hooks/usePublishToSpace.ts index dec48c63..628f5fe4 100644 --- a/packages/hypergraph-react/src/hooks/usePublishToSpace.ts +++ b/packages/hypergraph-react/src/hooks/usePublishToSpace.ts @@ -4,11 +4,11 @@ import { useHypergraphApp } from '../HypergraphAppContext.js'; import { preparePublish } from '../prepare-publish.js'; import { publishOps } from '../publish-ops.js'; -export function usePublishToPublicSpace(spaceId: string) { +export function usePublishToPublicSpace(spaceId: string) { const { getSmartSessionClient } = useHypergraphApp(); return useMutation({ - mutationFn: async (entity: Entity.Entity) => { + mutationFn: async (entity: Entity.Entity) => { const { ops } = await preparePublish({ entity, publicSpace: spaceId, From 28694297cba63694971e6bffe3f419672b61c172 Mon Sep 17 00:00:00 2001 From: Byron Guina Date: Wed, 30 Jul 2025 16:18:05 -0700 Subject: [PATCH 3/9] feat: allow caller to pass in useMutation options --- packages/hypergraph-react/src/hooks/usePublishToSpace.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/hypergraph-react/src/hooks/usePublishToSpace.ts b/packages/hypergraph-react/src/hooks/usePublishToSpace.ts index 628f5fe4..69dd8b99 100644 --- a/packages/hypergraph-react/src/hooks/usePublishToSpace.ts +++ b/packages/hypergraph-react/src/hooks/usePublishToSpace.ts @@ -1,13 +1,16 @@ import type { Entity } from '@graphprotocol/hypergraph'; -import { useMutation } from '@tanstack/react-query'; +import { type UseMutationOptions, useMutation } from '@tanstack/react-query'; import { useHypergraphApp } from '../HypergraphAppContext.js'; import { preparePublish } from '../prepare-publish.js'; import { publishOps } from '../publish-ops.js'; -export function usePublishToPublicSpace(spaceId: string) { +type UsePublishToSpaceOptions = UseMutationOptions>, Error, string, unknown>; + +export function usePublishToPublicSpace(spaceId: string, options: UsePublishToSpaceOptions) { const { getSmartSessionClient } = useHypergraphApp(); return useMutation({ + ...options, mutationFn: async (entity: Entity.Entity) => { const { ops } = await preparePublish({ entity, @@ -17,6 +20,7 @@ export function usePublishToPublicSpace(spaceId: string) { if (!smartSessionClient) { throw new Error('Missing smartSessionClient'); } + return await publishOps({ ops, space: spaceId, From bdeda9e06db162a895636d1d4aabc6e4e39b0cfe Mon Sep 17 00:00:00 2001 From: Byron Guina Date: Thu, 31 Jul 2025 09:24:43 -0700 Subject: [PATCH 4/9] feat: update publishing docs to use usePublishToPublicSpace --- docs/docs/publishing-public-data.md | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/docs/docs/publishing-public-data.md b/docs/docs/publishing-public-data.md index 4175baa0..1d6748c7 100644 --- a/docs/docs/publishing-public-data.md +++ b/docs/docs/publishing-public-data.md @@ -39,35 +39,24 @@ const { result } = publishOps({ }); ``` -Here is a full example flow: +Additionally, we export a `usePublishToPublishSpace` hook which abstracts the above functionality into a single function call. This function internally uses React Query's useMutate hook, so you have access to all of the same state machine and callback functions. ```tsx -import { publishOps, useHypergraphApp } from "@graphprotocol/hypergraph-react"; +import { usePublishToPublicSpace, useHypergraphApp } from "@graphprotocol/hypergraph-react"; const MyComponent = ({ publicSpaceId }: { publicSpaceId: string }) => { const { getSmartSessionClient } = useHypergraphApp(); const { data: events } = useQuery(Event, { mode: "private" }); + const { mutate, isPending } = usePublishToPublicSpace(); - const publishEvent = async (entity) => { - const smartSessionClient = await getSmartSessionClient(); - - const { ops } = preparePublish({ - entity: entity, - publicSpace: publicSpaceId, - }); - - const result = await publishOps({ - ops, - walletClient: smartSessionClient, - space: publicSpaceId, - name: "Create Event", - }); - }; + if (isPending) { + return
Publishing...
+ } return (
{events.map((event) => ( - ))} From 56a0aed5914f57412f7920fe88c1fedc1383a80a Mon Sep 17 00:00:00 2001 From: Byron Guina Date: Thu, 31 Jul 2025 09:31:53 -0700 Subject: [PATCH 5/9] fix: update variables types --- docs/docs/publishing-public-data.md | 2 +- .../src/hooks/usePublishToSpace.ts | 15 ++++++++++++--- packages/hypergraph-react/src/types.ts | 2 ++ 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/docs/docs/publishing-public-data.md b/docs/docs/publishing-public-data.md index 1d6748c7..9413e4d7 100644 --- a/docs/docs/publishing-public-data.md +++ b/docs/docs/publishing-public-data.md @@ -56,7 +56,7 @@ const MyComponent = ({ publicSpaceId }: { publicSpaceId: string }) => { return (
{events.map((event) => ( - ))} diff --git a/packages/hypergraph-react/src/hooks/usePublishToSpace.ts b/packages/hypergraph-react/src/hooks/usePublishToSpace.ts index 69dd8b99..3f763a19 100644 --- a/packages/hypergraph-react/src/hooks/usePublishToSpace.ts +++ b/packages/hypergraph-react/src/hooks/usePublishToSpace.ts @@ -3,15 +3,24 @@ import { type UseMutationOptions, useMutation } from '@tanstack/react-query'; import { useHypergraphApp } from '../HypergraphAppContext.js'; import { preparePublish } from '../prepare-publish.js'; import { publishOps } from '../publish-ops.js'; +import type { OmitStrict } from '../types.js'; -type UsePublishToSpaceOptions = UseMutationOptions>, Error, string, unknown>; +type Variables = { + entity: S; + spaceId: string; +}; -export function usePublishToPublicSpace(spaceId: string, options: UsePublishToSpaceOptions) { +type UsePublishToSpaceOptions = OmitStrict< + UseMutationOptions>, Error, Variables, unknown>, + 'mutationFn' | 'mutationKey' +>; + +export function usePublishToPublicSpace(options: UsePublishToSpaceOptions = {}) { const { getSmartSessionClient } = useHypergraphApp(); return useMutation({ ...options, - mutationFn: async (entity: Entity.Entity) => { + mutationFn: async ({ entity, spaceId }) => { const { ops } = await preparePublish({ entity, publicSpace: spaceId, diff --git a/packages/hypergraph-react/src/types.ts b/packages/hypergraph-react/src/types.ts index 9be00c01..df31f3be 100644 --- a/packages/hypergraph-react/src/types.ts +++ b/packages/hypergraph-react/src/types.ts @@ -2,6 +2,8 @@ import type { Op } from '@graphprotocol/grc-20'; import type { Entity } from '@graphprotocol/hypergraph'; import type * as Schema from 'effect/Schema'; +export type OmitStrict = Pick>; + export type EntityLike = { id: string; [key: string]: unknown; From f56bfe769526db11ccaa5c9295763d9f7e233f90 Mon Sep 17 00:00:00 2001 From: Byron Guina Date: Thu, 31 Jul 2025 10:06:02 -0700 Subject: [PATCH 6/9] feat: update vite template with hook --- .tool-versions | 1 + .../src/routes/private-space/$space-id.tsx | 38 ++++--------------- 2 files changed, 8 insertions(+), 31 deletions(-) create mode 100644 .tool-versions diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 00000000..1d7ff813 --- /dev/null +++ b/.tool-versions @@ -0,0 +1 @@ +pnpm 10.14.0 diff --git a/apps/create-hypergraph/template-vite-react/src/routes/private-space/$space-id.tsx b/apps/create-hypergraph/template-vite-react/src/routes/private-space/$space-id.tsx index 9fa19500..f1c26d4f 100644 --- a/apps/create-hypergraph/template-vite-react/src/routes/private-space/$space-id.tsx +++ b/apps/create-hypergraph/template-vite-react/src/routes/private-space/$space-id.tsx @@ -2,13 +2,11 @@ import { Button } from '@/components/ui/button'; import { Address } from '@/schema'; import { HypergraphSpaceProvider, - preparePublish, - publishOps, useCreateEntity, - useHypergraphApp, useQuery, useSpace, useSpaces, + usePublishToPublicSpace } from '@graphprotocol/hypergraph-react'; import { createFileRoute } from '@tanstack/react-router'; import { useState } from 'react'; @@ -34,7 +32,10 @@ function PrivateSpace() { const [selectedSpace, setSelectedSpace] = useState(''); const createAddress = useCreateEntity(Address); const [addressName, setAddressName] = useState(''); - const { getSmartSessionClient } = useHypergraphApp(); + const { mutate: publishToPublicSpace, isPending } = usePublishToPublicSpace({ + onSuccess: () => alert('Address published to public space'), + onError: () => alert('Error publishing address to public space') + }); if (!ready) { return ( @@ -53,31 +54,6 @@ function PrivateSpace() { setAddressName(''); }; - const publishToPublicSpace = async (address: Address) => { - if (!selectedSpace) { - alert('No space selected'); - return; - } - try { - const { ops } = await preparePublish({ entity: address, publicSpace: selectedSpace }); - const smartSessionClient = await getSmartSessionClient(); - if (!smartSessionClient) { - throw new Error('Missing smartSessionClient'); - } - const publishResult = await publishOps({ - ops, - space: selectedSpace, - name: 'Publish Address', - walletClient: smartSessionClient, - }); - console.log(publishResult, ops); - alert('Address published to public space'); - } catch (error) { - console.error(error); - alert('Error publishing address to public space'); - } - }; - return (
@@ -153,8 +129,8 @@ function PrivateSpace() {