Skip to content

Commit 4706031

Browse files
committed
improve relation creation
1 parent 32b70e3 commit 4706031

File tree

7 files changed

+73
-66
lines changed

7 files changed

+73
-66
lines changed

apps/events/src/components/todo/todos-public-geo.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ export const TodosPublicGeo = () => {
5151
<Button
5252
onClick={async () => {
5353
const userId = Id.Id('8zPJjTGLBDPtUcj6q2tghg');
54-
const todo = createTodo({ name: 'New Todo', checked: false, assignees: [userId] });
54+
const todo = createTodo({ name: 'New Todo 22', checked: false, assignees: [userId] });
5555
console.log('todo', todo);
5656
const { ops } = generateCreateOps(todo);
5757
console.log('ops', ops);

apps/events/src/components/todos2.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ export const Todos2 = () => {
152152
alert('Todo text is required');
153153
return;
154154
}
155-
createTodo({ name: newTodoName, checked: false });
155+
createTodo({ name: newTodoName, checked: false, assignees: newTodoAssignees.map(({ value }) => value) });
156156
setNewTodoName('');
157157
}}
158158
>

packages/hypergraph-react/src/internal/use-generate-create-ops.tsx

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@ import { useHypergraph } from '../HypergraphSpaceContext.js';
44

55
export function useGenerateCreateOps<const S extends Entity.AnyNoContext>(type: S, enabled = true) {
66
const { mapping } = useHypergraph();
7-
// @ts-expect-error TODO should use the actual type instead of the name in the mapping
8-
const typeName = type.name;
9-
const mappingEntry = mapping?.[typeName];
10-
if (!mappingEntry && enabled) {
11-
throw new Error(`Mapping entry for ${typeName} not found`);
12-
}
137

148
return (properties: Entity.Entity<S>) => {
9+
// @ts-expect-error TODO should use the actual type instead of the name in the mapping
10+
const typeName = type.name;
11+
const mappingEntry = mapping?.[typeName];
12+
if (!mappingEntry && enabled) {
13+
throw new Error(`Mapping entry for ${typeName} not found`);
14+
}
15+
1516
if (!enabled || !mappingEntry) {
1617
return { ops: [] };
1718
}
@@ -33,8 +34,8 @@ export function useGenerateCreateOps<const S extends Entity.AnyNoContext>(type:
3334

3435
for (const [key, value] of Object.entries(mappingEntry.relations || {})) {
3536
const toIds: { to: Id.Id }[] = [];
36-
for (const toId of properties[key]) {
37-
toIds.push({ to: Id.Id(toId) });
37+
for (const entity of properties[key]) {
38+
toIds.push({ to: Id.Id(entity.id) });
3839
}
3940
grcProperties[value] = toIds;
4041
}

packages/hypergraph-react/src/internal/use-generate-update-ops.tsx

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,20 @@ import type { DiffEntry } from '../types.js';
55

66
export function useGenerateUpdateOps<const S extends Entity.AnyNoContext>(type: S, enabled = true) {
77
const { mapping } = useHypergraph();
8-
// @ts-expect-error TODO should use the actual type instead of the name in the mapping
9-
const typeName = type.name;
10-
const mappingEntry = mapping?.[typeName];
11-
if (!mappingEntry && enabled) {
12-
throw new Error(`Mapping entry for ${typeName} not found`);
13-
}
148

159
return ({ id, __deleted, __version, ...properties }: DiffEntry<S>) => {
16-
if (!enabled || !mappingEntry) {
10+
// @ts-expect-error TODO should use the actual type instead of the name in the mapping
11+
const typeName = type.name;
12+
const mappingEntry = mapping?.[typeName];
13+
if (!mappingEntry && enabled) {
14+
throw new Error(`Mapping entry for ${typeName} not found`);
15+
}
16+
17+
if (!enabled || !mappingEntry || !mappingEntry.properties) {
1718
return { ops: [] };
1819
}
1920
const ops: Op[] = [];
21+
2022
for (const [key, rawValue] of Object.entries(properties)) {
2123
const attributeId = mappingEntry.properties[key];
2224
if (attributeId) {

packages/hypergraph-react/src/use-query.tsx

Lines changed: 39 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -120,48 +120,48 @@ export function useQuery<const S extends Entity.AnyNoContext>(type: S, params?:
120120
};
121121
}
122122

123+
const preparePublish = async (): Promise<PublishDiffInfo> => {
124+
// @ts-expect-error TODO should use the actual type instead of the name in the mapping
125+
const typeName = type.name;
126+
const mappingEntry = mapping?.[typeName];
127+
if (!mappingEntry) {
128+
throw new Error(`Mapping entry for ${typeName} not found`);
129+
}
130+
131+
const result = await publicResult.refetch();
132+
if (!result.data) {
133+
throw new Error('No data found');
134+
}
135+
const diff = getDiff(
136+
parseResult(result.data, type, mappingEntry, mapping).data,
137+
localResult.entities,
138+
localResult.deletedEntities,
139+
);
140+
141+
const newEntities = diff.newEntities.map((entity) => {
142+
const { ops: createOps } = generateCreateOps(entity);
143+
return { id: entity.id, entity, ops: createOps };
144+
});
145+
146+
const updatedEntities = diff.updatedEntities.map((updatedEntityInfo) => {
147+
const { ops: updateOps } = generateUpdateOps({ ...updatedEntityInfo.diff, id: updatedEntityInfo.id });
148+
return { ...updatedEntityInfo, ops: updateOps };
149+
});
150+
151+
const deletedEntities = await Promise.all(
152+
diff.deletedEntities.map(async (entity) => {
153+
const deleteOps = await generateDeleteOps(entity);
154+
return { id: entity.id, entity, ops: deleteOps };
155+
}),
156+
);
157+
158+
return { newEntities, updatedEntities, deletedEntities };
159+
};
160+
123161
return {
124162
...publicResult,
125163
data: mergedData,
126164
deleted: localResult.deletedEntities,
127-
preparePublish: !publicResult.isLoading
128-
? async (): Promise<PublishDiffInfo> => {
129-
// @ts-expect-error TODO should use the actual type instead of the name in the mapping
130-
const typeName = type.name;
131-
const mappingEntry = mapping?.[typeName];
132-
if (!mappingEntry) {
133-
throw new Error(`Mapping entry for ${typeName} not found`);
134-
}
135-
136-
const result = await publicResult.refetch();
137-
if (!result.data) {
138-
throw new Error('No data found');
139-
}
140-
const diff = getDiff(
141-
parseResult(result.data, type, mappingEntry, mapping).data,
142-
localResult.entities,
143-
localResult.deletedEntities,
144-
);
145-
146-
const newEntities = diff.newEntities.map((entity) => {
147-
const { ops: createOps } = generateCreateOps(entity);
148-
return { id: entity.id, entity, ops: createOps };
149-
});
150-
151-
const updatedEntities = diff.updatedEntities.map((updatedEntityInfo) => {
152-
const { ops: updateOps } = generateUpdateOps({ ...updatedEntityInfo.diff, id: updatedEntityInfo.id });
153-
return { ...updatedEntityInfo, ops: updateOps };
154-
});
155-
156-
const deletedEntities = await Promise.all(
157-
diff.deletedEntities.map(async (entity) => {
158-
const deleteOps = await generateDeleteOps(entity);
159-
return { id: entity.id, entity, ops: deleteOps };
160-
}),
161-
);
162-
163-
return { newEntities, updatedEntities, deletedEntities };
164-
}
165-
: preparePublishDummy,
165+
preparePublish: !publicResult.isLoading ? preparePublish : preparePublishDummy,
166166
};
167167
}

packages/hypergraph/src/entity/create.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { DocHandle } from '@automerge/automerge-repo';
22
import * as Schema from 'effect/Schema';
33
import { generateId } from '../utils/generateId.js';
4+
import { findOne } from './findOne.js';
45
import type { AnyNoContext, DocumentContent, Entity, Insert } from './types.js';
56

67
/**
@@ -26,6 +27,6 @@ export const create = <const S extends AnyNoContext>(handle: DocHandle<DocumentC
2627
};
2728
});
2829

29-
return { id: entityId, ...encoded, type: typeName };
30+
return findOne(handle, type)(entityId) as Entity<S>;
3031
};
3132
};
Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,29 @@
11
import type { DocHandle } from '@automerge/automerge-repo';
22
import * as Schema from 'effect/Schema';
3+
import { getEntityRelations } from './getEntityRelations.js';
34
import { hasValidTypesProperty } from './hasValidTypesProperty.js';
45
import type { AnyNoContext, DocumentContent, Entity } from './types.js';
56

67
/**
78
* Find the entity of the given type, with the given id, from the repo.
89
*/
9-
export const findOne =
10-
<const S extends AnyNoContext>(handle: DocHandle<DocumentContent>, type: S) =>
11-
(id: string): Entity<S> | undefined => {
12-
const decode = Schema.decodeUnknownSync(type);
10+
export const findOne = <const S extends AnyNoContext>(handle: DocHandle<DocumentContent>, type: S) => {
11+
const decode = Schema.decodeUnknownSync(type);
1312

14-
// TODO: what's the right way to get the name of the type?
15-
// @ts-expect-error name is defined
16-
const typeName = type.name;
13+
// TODO: what's the right way to get the name of the type?
14+
// @ts-expect-error name is defined
15+
const typeName = type.name;
1716

17+
return (id: string): Entity<S> | undefined => {
1818
// TODO: Instead of this insane filtering logic, we should be keeping track of the entities in
1919
// an index and store the decoded values instead of re-decoding over and over again.
20-
const entity = handle.docSync()?.entities?.[id];
20+
const doc = handle.docSync();
21+
const entity = doc?.entities?.[id];
22+
const relations = doc ? getEntityRelations(entity, type, doc) : {};
2123
if (hasValidTypesProperty(entity) && entity['@@types@@'].includes(typeName)) {
22-
return { ...decode({ ...entity, id }), type: typeName };
24+
return { ...decode({ ...entity, id, ...relations }), type: typeName };
2325
}
2426

2527
return undefined;
2628
};
29+
};

0 commit comments

Comments
 (0)