Skip to content

Conversation

baiirun
Copy link
Contributor

@baiirun baiirun commented Jul 30, 2025

Currently to publish private data to a public space you need to create a hand-rolled function combining multiple pieces of functionality from Hypergraph. It would be simpler if all of this logic was wrapped in a hook which exposed the function for publishing.

Another benefit is that we can expose all the state machine states to the developer during publishing so they can handle it in their UI. This was my main motivation as publishing data in the Vite template has no feedback as to what's happening.

Current implementation

import {
  preparePublish,
  publishOps,
  useHypergraphApp,
} from '@graphprotocol/hypergraph-react';

function Example() {
  const { getSmartSessionClient } = useHypergraphApp();

 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 <button onClick={() => publishToPublicSpace(address)}>Publish</button>
}

Hook-based implementation

import { usePublishToPublicSpace } from '@graphprotocol/hypergraph-react';

function Example() {
  const { mutate, isPending, isError } = usePublishToPublicSpace()

  return <button onClick={() => mutate({ entity: address, spaceId: "5" })}>Publish</button>
}

Copy link
Collaborator

@cmwhited cmwhited left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dude this is mega. Great idea. Left a comment about passing in optional UseMutatationOptions to the hook

import { preparePublish } from '../prepare-publish.js';
import { publishOps } from '../publish-ops.js';

export function usePublishToPublicSpace(spaceId: string) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be a good idea to add an optional options: UseMutationOptions here so that the user could pass in useMutation options like onMutate, onSuccess, onError, etc. This way they could add an onSuccess listener to update state or invalidate the cache/perform optimistic mutations, etc

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call! I'll add that. I'll also wait on @nikgraf to take a look as well and make sure it aligns with how he expects people to use Hypergraph.

@baiirun
Copy link
Contributor Author

baiirun commented Jul 30, 2025

I'll also add a changeset

@nikgraf
Copy link
Collaborator

nikgraf commented Jul 31, 2025

I think this is useful, happy to merge it!

Feedback

  • onSuccess is success for the write operation, but I think the default should be that onSuccess fire when we confirmed with the indexer that the write was indexed. What do you think?
  • until now I got the feedback we don't want an API that optimizes on publishing one entity. We could solve this by the hook accepting multiple entities. This was also the idea for preparePublish. And preparePublish probably should have an option to walk through relations (maybe capped to x levels) and gather the entities that have been created and this entity has a relation to e.g. when you publish an event which has a new address (street) you also want to publish it.

My take: Let's add docs, merge it and create follow up tasks to improve it. Thoughts?

@baiirun
Copy link
Contributor Author

baiirun commented Jul 31, 2025

onSuccess is success for the write operation, but I think the default should be that onSuccess fire when we confirmed with the indexer that the write was indexed. What do you think?

We can put to code to wait for the indexer either in this function or in publishOps and that should make it so onSuccess doesn't fire until they finish.

until now I got the feedback we don't want an API that optimizes on publishing one entity. We could solve this by the hook accepting multiple entities. This was also the idea for preparePublish. And preparePublish probably should have an option to walk through relations (maybe capped to x levels) and gather the entities that have been created and this entity has a relation to e.g. when you publish an event which has a new address (street) you also want to publish it.

Yeah I think that makes sense. I think for now we should just keep it as one entity to avoid confusion since publishOps only publishes one entity. Then we can update them at the same time later.

So remaining for me is

  • Add docs
  • Create a changeset in hypergraph/react
  • Update Vite template
  • Create a changeset in template

import type { Entity } from '@graphprotocol/hypergraph';
import type * as Schema from 'effect/Schema';

export type OmitStrict<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
Copy link
Contributor Author

@baiirun baiirun Jul 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added this types utility which I add to basically every codebase. Makes it so Omit is type safe.

type Banana = {
    id: string;
    isFruit: boolean;
}

// Will error as "isVegetable" doesn't exist on type Banana
type DerivedBanana = OmitStrict<Banana, "isVegetable">

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🙏 🙏 idk why this isn't just in typescript. annoys the hell out of me


type UsePublishToSpaceOptions = OmitStrict<
UseMutationOptions<Awaited<ReturnType<typeof publishOps>>, Error, Variables<Entity.AnyNoContext>, unknown>,
'mutationFn' | 'mutationKey'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Omitted the mutation-related options so devs aren't tempted to add their own mutation function.


return useMutation({
...options,
mutationFn: async ({ entity, spaceId }) => {
Copy link
Contributor Author

@baiirun baiirun Jul 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved the spaceId parameter to the mutation function so developers don't need to make a unique hook instance or add state for every space they might deploy to.

@baiirun baiirun merged commit 3f91e84 into main Jul 31, 2025
6 checks passed
@baiirun baiirun deleted the feat/hook-for-public-publishing branch July 31, 2025 19:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants