Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/wise-mugs-swim.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@graphprotocol/hypergraph": patch
---

improve include type for fineOne

6 changes: 6 additions & 0 deletions .changeset/yummy-beans-float.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@graphprotocol/hypergraph-react": minor
---

remove useQueryEntity and add useEntity hook

30 changes: 30 additions & 0 deletions apps/events/src/components/event.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { useEntity } from '@graphprotocol/hypergraph-react';
import { Event as EventType } from '../schema';

export const Event = ({ spaceId, entityId }: { spaceId: string; entityId: string }) => {
const { data, isPending, isError } = useEntity(EventType, {
mode: 'public',
include: {
sponsors: {
jobOffers: {},
},
},
id: entityId,
space: spaceId,
});

console.log({ component: 'Event', isPending, isError, data });

return (
<div>
{isPending && <div>Loading...</div>}
{isError && <div>Error</div>}
{data && (
<div>
<h2>Event Details</h2>
<pre className="text-xs">{JSON.stringify(data, null, 2)}</pre>
</div>
)}
</div>
);
};
2 changes: 2 additions & 0 deletions apps/events/src/routes/playground.lazy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { HypergraphSpaceProvider } from '@graphprotocol/hypergraph-react';
import { createLazyFileRoute } from '@tanstack/react-router';
import { CreateEvents } from '@/components/create-events';
import { CreatePropertiesAndTypesEvent } from '@/components/create-properties-and-types-event';
import { Event } from '@/components/event';
import { Playground } from '@/components/playground';

export const Route = createLazyFileRoute('/playground')({
Expand All @@ -12,6 +13,7 @@ function RouteComponent() {
const space = 'a393e509-ae56-4d99-987c-bed71d9db631';
return (
<>
<Event spaceId={space} entityId="cf7c620b-d724-498f-b134-8280dc8249ae" />
<Playground spaceId={space} />
<HypergraphSpaceProvider space={space}>
<div className="flex flex-col gap-4 max-w-(--breakpoint-sm) mx-auto py-8">
Expand Down
63 changes: 48 additions & 15 deletions packages/hypergraph-react/src/HypergraphSpaceContext.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client';

import { Entity, store } from '@graphprotocol/hypergraph';
import { Entity, type Id, store } from '@graphprotocol/hypergraph';
import { useSelector } from '@xstate/store/react';
import * as Schema from 'effect/Schema';
import {
Expand All @@ -14,6 +14,7 @@ import {
useSyncExternalStore,
} from 'react';
import { useHypergraphApp } from './HypergraphAppContext.js';
import { useEntityPublic } from './internal/use-entity-public.js';
import { usePublicSpace } from './internal/use-public-space.js';

// TODO space can be undefined
Expand Down Expand Up @@ -181,19 +182,28 @@ export function useQueryLocal<const S extends Entity.AnyNoContext>(type: S, para
return { entities, deletedEntities };
}

export function useQueryEntity<const S extends Entity.AnyNoContext>(
function useEntityPrivate<const S extends Entity.AnyNoContext>(
type: S,
id: string,
params?: { space?: string; include?: { [K in keyof Schema.Schema.Type<S>]?: Record<string, never> } },
params: {
id: string | Id;
enabled?: boolean;
space?: string;
include?: { [K in keyof Schema.Schema.Type<S>]?: Record<string, Record<string, never>> } | undefined;
},
) {
const { space: spaceFromContext } = useHypergraphSpaceInternal();
const { space: spaceFromParams, include } = params ?? {};
const handle = useSubscribeToSpaceAndGetHandle({ spaceId: spaceFromParams ?? spaceFromContext, enabled: true });
const prevEntityRef = useRef<Entity.Entity<S> | undefined>(undefined);
const { space: spaceFromParams, include, id, enabled = true } = params;
const handle = useSubscribeToSpaceAndGetHandle({ spaceId: spaceFromParams ?? spaceFromContext, enabled });
const prevEntityRef = useRef<{
data: Entity.Entity<S> | undefined;
invalidEntity: Record<string, string | boolean | number | Date> | undefined;
isPending: boolean;
isError: boolean;
}>({ data: undefined, invalidEntity: undefined, isPending: false, isError: false });
const equals = Schema.equivalence(type);

const subscribe = (callback: () => void) => {
if (!handle) {
if (!handle || !enabled) {
return () => {};
}
const handleChange = () => {
Expand All @@ -214,7 +224,7 @@ export function useQueryEntity<const S extends Entity.AnyNoContext>(
};

return useSyncExternalStore(subscribe, () => {
if (!handle) {
if (!handle || !enabled) {
return prevEntityRef.current;
}
const doc = handle.doc();
Expand All @@ -223,16 +233,39 @@ export function useQueryEntity<const S extends Entity.AnyNoContext>(
}

const found = Entity.findOne(handle, type, include)(id);
if (found === undefined && prevEntityRef.current !== undefined) {
if (found === undefined && prevEntityRef.current.data !== undefined) {
// entity was maybe deleted, delete from the ref
prevEntityRef.current = undefined;
} else if (found !== undefined && prevEntityRef.current === undefined) {
prevEntityRef.current = found;
} else if (found !== undefined && prevEntityRef.current !== undefined && !equals(found, prevEntityRef.current)) {
prevEntityRef.current = { data: undefined, invalidEntity: undefined, isPending: false, isError: false };
} else if (found !== undefined && prevEntityRef.current.data === undefined) {
prevEntityRef.current = { data: found, invalidEntity: undefined, isPending: false, isError: false };
} else if (
found !== undefined &&
prevEntityRef.current.data !== undefined &&
!equals(found, prevEntityRef.current.data)
) {
// found and ref have a value, compare for equality, if they are not equal, update the ref and return
prevEntityRef.current = found;
prevEntityRef.current = { data: found, invalidEntity: undefined, isPending: false, isError: false };
}

return prevEntityRef.current;
});
}

export function useEntity<const S extends Entity.AnyNoContext>(
type: S,
params: {
id: string | Id;
space?: string;
mode: 'private' | 'public';
include?: { [K in keyof Schema.Schema.Type<S>]?: Record<string, Record<string, never>> } | undefined;
},
) {
const resultPublic = useEntityPublic(type, { ...params, enabled: params.mode === 'public' });
const resultPrivate = useEntityPrivate(type, { ...params, enabled: params.mode === 'private' });

if (params.mode === 'public') {
return resultPublic;
}

return resultPrivate;
}
3 changes: 2 additions & 1 deletion packages/hypergraph-react/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ export {
HypergraphSpaceProvider,
useCreateEntity,
useDeleteEntity,
useEntity,
useHardDeleteEntity,
useQueryEntity,
useQueryLocal as _useQueryLocal,
useRemoveRelation,
useSpace,
Expand All @@ -26,6 +26,7 @@ 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';
export { useEntityPublic as _useEntityPublic } from './internal/use-entity-public.js';
export { useQueryPublic as _useQueryPublic } from './internal/use-query-public.js';
export { preparePublish } from './prepare-publish.js';
export { publishOps } from './publish-ops.js';
Expand Down
Loading
Loading