Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
60 changes: 57 additions & 3 deletions apps/events/src/components/create-properties-and-types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,40 @@ const createPropertiesAndTypes = async ({
});
ops.push(...createAssigneesRelationTypeOps);

const { id: duePropertyId, ops: createDuePropertyOps } = Graph.createProperty({
type: 'TIME',
name: 'Due',
});
ops.push(...createDuePropertyOps);

const { id: pointPropertyId, ops: createPointPropertyOps } = Graph.createProperty({
type: 'POINT',
name: 'Point',
});
ops.push(...createPointPropertyOps);

const { id: amountPropertyId, ops: createAmountPropertyOps } = Graph.createProperty({
type: 'NUMBER',
name: 'Amount',
});
ops.push(...createAmountPropertyOps);

const { id: websitePropertyId, ops: createWebsitePropertyOps } = Graph.createProperty({
type: 'URL',
name: 'Website',
});
ops.push(...createWebsitePropertyOps);

const { id: todoTypeId, ops: createTodoTypeOps } = Graph.createType({
name: 'Todo',
properties: [checkedPropertyId, assigneesRelationTypeId],
properties: [
checkedPropertyId,
assigneesRelationTypeId,
duePropertyId,
pointPropertyId,
websitePropertyId,
amountPropertyId,
],
});
ops.push(...createTodoTypeOps);

Expand All @@ -40,7 +71,17 @@ const createPropertiesAndTypes = async ({
space,
name: 'Create properties and types',
});
return { result, todoTypeId, checkedPropertyId, userId, assigneesRelationTypeId };
return {
result,
todoTypeId,
checkedPropertyId,
userId,
assigneesRelationTypeId,
duePropertyId,
pointPropertyId,
websitePropertyId,
amountPropertyId,
};
};

export const CreatePropertiesAndTypes = () => {
Expand All @@ -62,7 +103,16 @@ export const CreatePropertiesAndTypes = () => {
if (!smartAccountWalletClient) {
throw new Error('Missing smartAccountWalletClient');
}
const { todoTypeId, checkedPropertyId, userId, assigneesRelationTypeId } = await createPropertiesAndTypes({
const {
todoTypeId,
checkedPropertyId,
userId,
assigneesRelationTypeId,
duePropertyId,
pointPropertyId,
websitePropertyId,
amountPropertyId,
} = await createPropertiesAndTypes({
smartAccountWalletClient,
space,
});
Expand All @@ -72,6 +122,10 @@ export const CreatePropertiesAndTypes = () => {
properties: {
name: Id.Id('LuBWqZAu6pz54eiJS5mLv8'),
checked: Id.Id('${checkedPropertyId}'),
due: Id.Id('${duePropertyId}'),
point: Id.Id('${pointPropertyId}'),
website: Id.Id('${websitePropertyId}'),
amount: Id.Id('${amountPropertyId}'),
},
relations: {
assignees: Id.Id('${assigneesRelationTypeId}'),
Expand Down
4 changes: 4 additions & 0 deletions apps/events/src/components/todo/todos-local.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ export const TodosLocal = () => {
<div key={todo.id} className="flex flex-row items-center gap-2">
<h2>{todo.name}</h2>
<div className="text-xs">{todo.id}</div>
<div className="text-xs">{todo.due.toLocaleDateString()}</div>
<div className="text-xs">{todo.amount}</div>
{todo.point && <div className="text-xs">{todo.point.join(', ')}</div>}
{todo.website && <div className="text-xs">{todo.website.toString()}</div>}
{todo.assignees.map((assignee) => (
<span key={assignee.id} className="border rounded-sm mr-1 p-1">
{assignee.name}
Expand Down
14 changes: 13 additions & 1 deletion apps/events/src/components/todo/todos-public-geo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ export const TodosPublicGeo = () => {
<div key={todo.id} className="flex flex-row items-center gap-2">
<h2>{todo.name}</h2>
<div className="text-xs">{todo.id}</div>
<div className="text-xs">{todo.due.toLocaleDateString()}</div>
<div className="text-xs">{todo.amount}</div>
{todo.point && <div className="text-xs">{todo.point.join(', ')}</div>}
{todo.website && <div className="text-xs">{todo.website.toString()}</div>}
<input type="checkbox" checked={todo.checked} readOnly />
{todo.assignees.map((assignee) => (
<span key={assignee.id} className="border rounded-sm mr-1 p-1">
Expand Down Expand Up @@ -71,7 +75,15 @@ export const TodosPublicGeo = () => {
throw new Error('Missing smartAccountWalletClient');
}
const userId = Id.Id('8zPJjTGLBDPtUcj6q2tghg');
const todo = createTodo({ name: 'New Todo 22', checked: false, assignees: [userId] });
const todo = createTodo({
name: 'New Todo 22',
checked: false,
assignees: [userId],
due: new Date('2025-08-20'),
amount: 200,
point: [12.34, 56.78],
website: new URL('https://example.com'),
});
console.log('todo', todo);
const { ops } = generateCreateOps(todo);
console.log('ops', ops);
Expand Down
4 changes: 4 additions & 0 deletions apps/events/src/components/todo/todos-public-kg.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ export const TodosPublicKg = () => {
<div key={todo.id} className="flex flex-row items-center gap-2">
<h2>{todo.name}</h2>
<div className="text-xs">{todo.id}</div>
<div className="text-xs">{todo.due.toLocaleDateString()}</div>
<div className="text-xs">{todo.amount}</div>
{todo.point && <div className="text-xs">{todo.point.join(', ')}</div>}
{todo.website && <div className="text-xs">{todo.website.toString()}</div>}
<input type="checkbox" checked={todo.checked} readOnly />
<Button
onClick={async () => {
Expand Down
14 changes: 13 additions & 1 deletion apps/events/src/components/todos2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ export const Todos2 = () => {
checked={todo.checked}
onChange={(e) => updateTodo(todo.id, { checked: e.target.checked })}
/>
<div className="text-xs">{todo.due.toLocaleDateString()}</div>
<div className="text-xs">{todo.amount}</div>
{todo.point && <div className="text-xs">{todo.point.join(', ')}</div>}
{todo.website && <div className="text-xs">{todo.website.toString()}</div>}
{todo.assignees.length > 0 && (
<span className="text-xs text-gray-500">
Assigned to:{' '}
Expand Down Expand Up @@ -155,7 +159,15 @@ export const Todos2 = () => {
alert('Todo text is required');
return;
}
createTodo({ name: newTodoName, checked: false, assignees: newTodoAssignees.map(({ value }) => value) });
createTodo({
name: newTodoName,
checked: false,
assignees: newTodoAssignees.map(({ value }) => value),
due: new Date('2025-08-20'),
amount: 100,
point: [12.34, 56.78],
website: new URL('https://example.com'),
});
setNewTodoName('');
}}
>
Expand Down
12 changes: 8 additions & 4 deletions apps/events/src/mapping.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,21 @@ export const mapping: Mapping = {
},
},
Todo2: {
typeIds: [Id.Id('4ewpH1mPW9f2tLhaHdKKyn')],
typeIds: [Id.Id('LJuM8ju67mCv78FhAiK9k9')],
properties: {
name: Id.Id('LuBWqZAu6pz54eiJS5mLv8'),
checked: Id.Id('7zyFtwuuf9evFNZqcLSytU'),
checked: Id.Id('Ud9kn9gAUsCr1pxvxcgDj8'),
due: Id.Id('CFisPgjjWVdnaMtSWJDBqA'),
point: Id.Id('BkcVo7JZHF5LsWw7XZJwwe'),
website: Id.Id('XZmLQ8XyaUHnNWgSSbzaHU'),
amount: Id.Id('LfzKTfgy5Qg3PxAfKB2BL7'),
},
relations: {
assignees: Id.Id('GeLe54zpz1MiMWAF8LFCCt'),
assignees: Id.Id('HCdFcTRyMyZMXScKox738i'),
},
},
User: {
typeIds: [Id.Id('KYCunro75we8KbjpsDKbm7')],
typeIds: [Id.Id('Fk5qzwdpKsD35gm5ts4SZA')],
properties: {
name: Id.Id('LuBWqZAu6pz54eiJS5mLv8'),
},
Expand Down
4 changes: 4 additions & 0 deletions apps/events/src/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ export class Todo2 extends Entity.Class<Todo2>('Todo2')({
name: Entity.Text,
checked: Entity.Checkbox,
assignees: Entity.Relation(User),
due: Entity.Date,
amount: Entity.Number,
point: Entity.Point,
website: Entity.Url,
}) {}

export class NewsStory extends Entity.Class<NewsStory>('NewsStory')({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ export const EntityCard = ({ entity, type }: EntityCardProps) => {
{key.charAt(0).toUpperCase() + key.slice(1)}
</td>
<td className="py-1.5">
{Array.isArray(value) ? (
{/* check for number to exclude Point fields */}
{Array.isArray(value) && !value.every((v) => typeof v === 'number') ? (
<ul>
{value.map(({ id, name }) => (
<li key={id}>
Expand Down
12 changes: 12 additions & 0 deletions packages/hypergraph-react/src/internal/use-generate-create-ops.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,18 @@ export function useGenerateCreateOps<const S extends Entity.AnyNoContext>(type:
if (fields[key] === Entity.Checkbox) {
valueType = 'CHECKBOX';
serializedValue = properties[key] ? '1' : '0';
} else if (fields[key] === Entity.Date) {
valueType = 'TIME';
serializedValue = properties[key].toISOString();
} else if (fields[key] === Entity.Point) {
valueType = 'POINT';
serializedValue = properties[key].join(',');
} else if (fields[key] === Entity.Url) {
valueType = 'URL';
serializedValue = properties[key].toString();
} else if (fields[key] === Entity.Number) {
valueType = 'NUMBER';
serializedValue = properties[key].toString();
}

grcProperties[value] = {
Expand Down
34 changes: 30 additions & 4 deletions packages/hypergraph-react/src/internal/use-generate-update-ops.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Id, type Op, Relation, Triple, type Value } from '@graphprotocol/grc-20';
import type { Entity } from '@graphprotocol/hypergraph';
import { Entity } from '@graphprotocol/hypergraph';
import { useHypergraph } from '../HypergraphSpaceContext.js';
import type { DiffEntry } from '../types.js';

Expand Down Expand Up @@ -31,18 +31,41 @@ export function useGenerateUpdateOps<const S extends Entity.AnyNoContext>(type:
const rawValue = propertyDiff.new;

let value: Value;
if (typeof rawValue === 'boolean') {
if (type.fields[key] === Entity.Checkbox) {
value = {
type: 'CHECKBOX',
value: rawValue ? '1' : '0',
};
} else if (type.fields[key] === Entity.Point) {
value = {
type: 'POINT',
// @ts-expect-error: must be an array of numbers
value: rawValue.join(','),
};
} else if (type.fields[key] === Entity.Url) {
value = {
type: 'URL',
// @ts-expect-error: must be a URL
value: rawValue.toString(),
};
} else if (type.fields[key] === Entity.Date) {
value = {
type: 'TIME',
// @ts-expect-error: must be a Date
value: rawValue.toISOString(),
};
} else if (type.fields[key] === Entity.Number) {
value = {
type: 'NUMBER',
// @ts-expect-error: must be a number
value: rawValue.toString(),
};
} else {
value = {
type: 'TEXT',
value: rawValue as string,
};
}

const op = Triple.make({
attributeId: propertyId,
entityId: id,
Expand All @@ -53,7 +76,10 @@ export function useGenerateUpdateOps<const S extends Entity.AnyNoContext>(type:

for (const [key, relationId] of Object.entries(mappingEntry.relations || {})) {
const relationDiff = diff[key];
if (relationDiff === undefined || relationDiff.type === 'property') {
if (!relationDiff) {
continue;
}
if (relationDiff.type === 'property') {
throw new Error(`Invalid diff or mapping for generating update Ops on the relation \`${key}\``);
}
for (const toId of relationDiff.addedIds) {
Expand Down
14 changes: 12 additions & 2 deletions packages/hypergraph-react/src/internal/use-query-public-geo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ query entities($spaceId: String!, $typeId: String!, $relationTypeIds: [String!]!
attributeId
textValue
booleanValue
numberValue
valueType
unitOption
}
}
relationsByFromVersionId(filter: {typeOfId: {in: $relationTypeIds}}) {
Expand Down Expand Up @@ -68,7 +70,9 @@ type EntityQueryResult = {
attributeId: string;
textValue: string;
booleanValue: boolean;
valueType: 'TEXT' | 'CHECKBOX';
numberValue: number;
valueType: 'TEXT' | 'CHECKBOX' | 'POINT' | 'URL' | 'TIME' | 'NUMBER';
unitOption: unknown;
}[];
};
relationsByFromVersionId: {
Expand Down Expand Up @@ -102,7 +106,7 @@ export const parseResult = <S extends Entity.AnyNoContext>(

for (const queryEntity of queryData.entities.nodes) {
const queryEntityVersion = queryEntity.currentVersion.version;
const rawEntity: Record<string, string | boolean | unknown[]> = {
const rawEntity: Record<string, string | boolean | unknown[] | URL | Date> = {
id: queryEntity.id,
};
// take the mappingEntry and assign the attributes to the rawEntity
Expand All @@ -111,6 +115,12 @@ export const parseResult = <S extends Entity.AnyNoContext>(
if (property) {
if (type.fields[key] === Entity.Checkbox) {
rawEntity[key] = property.booleanValue;
} else if (type.fields[key] === Entity.Point) {
rawEntity[key] = property.textValue.split(',').map(Number);
} else if (type.fields[key] === Entity.Url) {
rawEntity[key] = new URL(property.textValue);
} else if (type.fields[key] === Entity.Date) {
rawEntity[key] = new Date(property.textValue);
} else {
rawEntity[key] = property.textValue;
}
Expand Down
30 changes: 27 additions & 3 deletions packages/hypergraph-react/src/use-query.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { Entity } from '@graphprotocol/hypergraph';
import { Utils } from '@graphprotocol/hypergraph';
import { Entity, Utils } from '@graphprotocol/hypergraph';
import type * as Schema from 'effect/Schema';
import { useMemo } from 'react';
import { useHypergraph, useQueryLocal } from './HypergraphSpaceContext.js';
Expand Down Expand Up @@ -69,6 +68,7 @@ const getDiff = <S extends Entity.AnyNoContext>(
if (key === '__version' || key === '__deleted') {
continue;
}

if (Utils.isRelationField(field)) {
const relationIds: string[] = entity[key].map((e: Entity.Entity<S>) => e.id);
const localRelationIds: string[] = localEntity[key].map((e: Entity.Entity<S>) => e.id);
Expand All @@ -90,7 +90,31 @@ const getDiff = <S extends Entity.AnyNoContext>(
};
}
} else {
if (entity[key] !== localEntity[key]) {
if (field === Entity.Date) {
if (entity[key].getTime() !== localEntity[key].getTime()) {
diff[key] = {
type: 'property',
current: entity[key],
new: localEntity[key],
};
}
} else if (field === Entity.Url) {
if (entity[key].toString() !== localEntity[key].toString()) {
diff[key] = {
type: 'property',
current: entity[key],
new: localEntity[key],
};
}
} else if (field === Entity.Point) {
if (entity[key].join(',') !== localEntity[key].join(',')) {
diff[key] = {
type: 'property',
current: entity[key],
new: localEntity[key],
};
}
} else if (entity[key] !== localEntity[key]) {
diff[key] = {
type: 'property',
current: entity[key],
Expand Down
Loading
Loading