diff --git a/apps/events/src/components/playground.tsx b/apps/events/src/components/playground.tsx index c324e450..084f2868 100644 --- a/apps/events/src/components/playground.tsx +++ b/apps/events/src/components/playground.tsx @@ -1,5 +1,5 @@ import { getSmartAccountWalletClient } from '@/lib/smart-account'; -import { _useDeleteEntityPublic, useQuery } from '@graphprotocol/hypergraph-react'; +import { _useCreateEntityPublic, _useDeleteEntityPublic, useQuery } from '@graphprotocol/hypergraph-react'; import { useState } from 'react'; import { Event } from '../schema'; import { Button } from './ui/button'; @@ -14,17 +14,46 @@ export const Playground = () => { }, }); const [isDeleting, setIsDeleting] = useState(false); + const [isCreating, setIsCreating] = useState(false); const deleteEntity = _useDeleteEntityPublic(Event, { space: '1c954768-7e14-4f0f-9396-0fe9dcd55fe8', }); + const createEntity = _useCreateEntityPublic(Event, { + space: '1c954768-7e14-4f0f-9396-0fe9dcd55fe8', + }); + console.log({ isLoading, isError, data }); return (
{isLoading &&
Loading...
} {isError &&
Error
} + {data?.map((event) => (

{event.name}

diff --git a/packages/hypergraph-react/src/index.ts b/packages/hypergraph-react/src/index.ts index 45767be2..a4d826ad 100644 --- a/packages/hypergraph-react/src/index.ts +++ b/packages/hypergraph-react/src/index.ts @@ -23,6 +23,7 @@ export { useUpdateEntity, } from './HypergraphSpaceContext.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'; export { useGenerateCreateOps as _useGenerateCreateOps } from './internal/use-generate-create-ops.js'; export { useQueryPublic as _useQueryPublic } from './internal/use-query-public.js'; diff --git a/packages/hypergraph-react/src/internal/use-create-entity-public.ts b/packages/hypergraph-react/src/internal/use-create-entity-public.ts new file mode 100644 index 00000000..a2ef30a5 --- /dev/null +++ b/packages/hypergraph-react/src/internal/use-create-entity-public.ts @@ -0,0 +1,100 @@ +import { type GeoSmartAccount, Graph, Id, type PropertiesParam, type RelationsParam } from '@graphprotocol/grc-20'; +import type { Entity } from '@graphprotocol/hypergraph'; +import { Type, store } from '@graphprotocol/hypergraph'; +import { useQueryClient } from '@tanstack/react-query'; +import { useSelector } from '@xstate/store/react'; +import type * as Schema from 'effect/Schema'; +import { publishOps } from '../publish-ops.js'; + +type CreateEntityPublicParams = { + space: string; +}; + +export function useCreateEntityPublic( + type: S, + { space }: CreateEntityPublicParams, +) { + const mapping = useSelector(store, (state) => state.context.mapping); + const queryClient = useQueryClient(); + + return async ( + data: Readonly>>, + { walletClient }: { walletClient: GeoSmartAccount }, + // TODO: return the entity with this type: Promise> + ) => { + try { + // @ts-expect-error TODO should use the actual type instead of the name in the mapping + const typeName = type.name; + const mappingEntry = mapping?.[typeName]; + if (!mappingEntry) { + throw new Error(`Mapping entry for ${typeName} not found`); + } + + const fields = type.fields; + const values: PropertiesParam = []; + for (const [key, value] of Object.entries(mappingEntry.properties || {})) { + let serializedValue: string = data[key]; + if (fields[key] === Type.Checkbox) { + serializedValue = Graph.serializeCheckbox(data[key]); + } else if (fields[key] === Type.Date) { + serializedValue = Graph.serializeDate(data[key]); + } else if (fields[key] === Type.Point) { + serializedValue = Graph.serializePoint(data[key]); + } else if (fields[key] === Type.Number) { + serializedValue = Graph.serializeNumber(data[key]); + } + + values.push({ + property: Id.Id(value), + value: serializedValue, + }); + } + + const relations: RelationsParam = {}; + for (const [key, relationId] of Object.entries(mappingEntry.relations || {})) { + const toIds: { toEntity: Id.Id }[] = []; + + if (data[key]) { + // @ts-expect-error - TODO: fix the types error + for (const entity of data[key]) { + if (typeof entity === 'string') { + toIds.push({ toEntity: Id.Id(entity) }); + } else { + toIds.push({ toEntity: Id.Id(entity.id) }); + } + } + relations[Id.Id(relationId)] = toIds; + } + } + + const { ops } = Graph.createEntity({ + types: mappingEntry.typeIds, + id: data.id, + values, + relations, + }); + + const { cid, txResult } = await publishOps({ + ops, + space, + name: `Create entity ${data.id}`, + walletClient, + network: 'TESTNET', + }); + // TODO: temporary fix until we get the information from the API when a transaction is confirmed + await new Promise((resolve) => setTimeout(resolve, 2000)); + queryClient.invalidateQueries({ + queryKey: [ + 'hypergraph-public-entities', + // @ts-expect-error - TODO: find a better way to access the type.name + type.name, + space, + ], + }); + return { success: true, cid, txResult }; + } catch (error) { + console.error(error); + return { success: false, error: 'Failed to create entity' }; + } + }; +}