jazz-tools 0.19.3 → 0.19.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/.svelte-kit/__package__/jazz.class.svelte.d.ts +2 -2
  2. package/.svelte-kit/__package__/jazz.class.svelte.d.ts.map +1 -1
  3. package/.svelte-kit/__package__/jazz.class.svelte.js +15 -17
  4. package/.turbo/turbo-build.log +65 -65
  5. package/CHANGELOG.md +23 -0
  6. package/dist/{chunk-JPWM4CS2.js → chunk-DFFRRRRF.js} +137 -77
  7. package/dist/chunk-DFFRRRRF.js.map +1 -0
  8. package/dist/index.js +14 -7
  9. package/dist/index.js.map +1 -1
  10. package/dist/inspector/{custom-element-3JAYHXWQ.js → custom-element-P76EIWEV.js} +301 -142
  11. package/dist/inspector/{custom-element-3JAYHXWQ.js.map → custom-element-P76EIWEV.js.map} +1 -1
  12. package/dist/inspector/index.js +281 -122
  13. package/dist/inspector/index.js.map +1 -1
  14. package/dist/inspector/register-custom-element.js +1 -1
  15. package/dist/inspector/tests/viewer/co-plain-text-view.test.d.ts +2 -0
  16. package/dist/inspector/tests/viewer/co-plain-text-view.test.d.ts.map +1 -0
  17. package/dist/inspector/utils/history.d.ts +5 -1
  18. package/dist/inspector/utils/history.d.ts.map +1 -1
  19. package/dist/inspector/viewer/co-plain-text-view.d.ts +4 -2
  20. package/dist/inspector/viewer/co-plain-text-view.d.ts.map +1 -1
  21. package/dist/inspector/viewer/page.d.ts.map +1 -1
  22. package/dist/inspector/viewer/use-resolve-covalue.d.ts +0 -1
  23. package/dist/inspector/viewer/use-resolve-covalue.d.ts.map +1 -1
  24. package/dist/react-core/hooks.d.ts.map +1 -1
  25. package/dist/react-core/index.js +4 -17
  26. package/dist/react-core/index.js.map +1 -1
  27. package/dist/svelte/jazz.class.svelte.d.ts +2 -2
  28. package/dist/svelte/jazz.class.svelte.d.ts.map +1 -1
  29. package/dist/svelte/jazz.class.svelte.js +15 -17
  30. package/dist/testing.js +1 -1
  31. package/dist/tools/coValues/coFeed.d.ts.map +1 -1
  32. package/dist/tools/coValues/group.d.ts.map +1 -1
  33. package/dist/tools/coValues/interfaces.d.ts +7 -6
  34. package/dist/tools/coValues/interfaces.d.ts.map +1 -1
  35. package/dist/tools/coValues/request.d.ts.map +1 -1
  36. package/dist/tools/exports.d.ts +1 -1
  37. package/dist/tools/exports.d.ts.map +1 -1
  38. package/dist/tools/implementation/refs.d.ts +1 -1
  39. package/dist/tools/implementation/refs.d.ts.map +1 -1
  40. package/dist/tools/implementation/zodSchema/runtimeConverters/schemaFieldToCoFieldDef.d.ts +3 -1
  41. package/dist/tools/implementation/zodSchema/runtimeConverters/schemaFieldToCoFieldDef.d.ts.map +1 -1
  42. package/dist/tools/subscribe/SubscriptionScope.d.ts +5 -2
  43. package/dist/tools/subscribe/SubscriptionScope.d.ts.map +1 -1
  44. package/dist/tools/subscribe/index.d.ts +1 -1
  45. package/dist/tools/subscribe/index.d.ts.map +1 -1
  46. package/dist/tools/subscribe/types.d.ts +2 -1
  47. package/dist/tools/subscribe/types.d.ts.map +1 -1
  48. package/dist/tools/tests/SubscriptionScope.test.d.ts +2 -0
  49. package/dist/tools/tests/SubscriptionScope.test.d.ts.map +1 -0
  50. package/package.json +4 -4
  51. package/src/inspector/tests/utils/history.test.ts +233 -2
  52. package/src/inspector/tests/viewer/co-plain-text-view.test.tsx +125 -0
  53. package/src/inspector/tests/viewer/history-view.test.tsx +134 -2
  54. package/src/inspector/utils/history.ts +168 -1
  55. package/src/inspector/viewer/co-plain-text-view.tsx +102 -3
  56. package/src/inspector/viewer/history-view.tsx +5 -25
  57. package/src/inspector/viewer/page.tsx +8 -1
  58. package/src/inspector/viewer/use-resolve-covalue.ts +2 -6
  59. package/src/react-core/hooks.ts +5 -29
  60. package/src/svelte/jazz.class.svelte.ts +16 -34
  61. package/src/tools/coValues/coFeed.ts +10 -7
  62. package/src/tools/coValues/coMap.ts +10 -7
  63. package/src/tools/coValues/group.ts +6 -2
  64. package/src/tools/coValues/interfaces.ts +48 -28
  65. package/src/tools/coValues/request.ts +12 -8
  66. package/src/tools/exports.ts +1 -0
  67. package/src/tools/implementation/refs.ts +9 -17
  68. package/src/tools/implementation/zodSchema/runtimeConverters/schemaFieldToCoFieldDef.ts +62 -30
  69. package/src/tools/subscribe/SubscriptionScope.ts +38 -2
  70. package/src/tools/subscribe/index.ts +28 -13
  71. package/src/tools/subscribe/types.ts +5 -2
  72. package/src/tools/tests/SubscriptionScope.test.ts +397 -0
  73. package/src/tools/tests/deepLoading.test.ts +22 -0
  74. package/src/tools/tests/subscribe.test.ts +69 -0
  75. package/dist/chunk-JPWM4CS2.js.map +0 -1
@@ -17,6 +17,8 @@ import { HistoryView } from "../../viewer/history-view";
17
17
  import { setup } from "goober";
18
18
  import React from "react";
19
19
 
20
+ const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
21
+
20
22
  function extractAction(row: HTMLElement | null | undefined) {
21
23
  if (!row) return "";
22
24
  // index 0: author, index 1: action, index 2: timestamp
@@ -30,7 +32,6 @@ function extractActions(): string[] {
30
32
 
31
33
  describe("HistoryView", async () => {
32
34
  const account = await setupJazzTestSync();
33
- const account2 = await createJazzTestAccount();
34
35
 
35
36
  beforeAll(() => {
36
37
  // setup goober
@@ -270,7 +271,138 @@ describe("HistoryView", async () => {
270
271
  });
271
272
  });
272
273
 
273
- describe("co.group", () => {
274
+ describe("co.plaintext", () => {
275
+ it("should render co.plaintext initial append in a single row", async () => {
276
+ const value = co.plainText().create("hello", account);
277
+ render(
278
+ <HistoryView coValue={value.$jazz.raw} node={value.$jazz.localNode} />,
279
+ );
280
+
281
+ expect(extractActions()).toEqual(['"hello" has been appended']);
282
+ });
283
+
284
+ it("should render co.plaintext appends in a single row", async () => {
285
+ const value = co.plainText().create("hello", account);
286
+ value.$jazz.applyDiff("hello world");
287
+ value.$jazz.applyDiff("hello world!");
288
+
289
+ expect(value.$jazz.raw.toString()).toEqual("hello world!");
290
+
291
+ render(
292
+ <HistoryView coValue={value.$jazz.raw} node={value.$jazz.localNode} />,
293
+ );
294
+
295
+ const history = [
296
+ '"hello" has been appended',
297
+ '" world" has been inserted after "o"',
298
+ '"!" has been inserted after " "', // it is after " " because previous action is reversed
299
+ ].toReversed(); // Default sort is descending
300
+
301
+ expect(extractActions()).toEqual(history);
302
+ });
303
+
304
+ it("should render co.plaintext delete in tail", async () => {
305
+ const value = co.plainText().create("hello", account);
306
+ value.$jazz.applyDiff("hell");
307
+
308
+ expect(value.$jazz.raw.toString()).toEqual("hell");
309
+
310
+ render(
311
+ <HistoryView coValue={value.$jazz.raw} node={value.$jazz.localNode} />,
312
+ );
313
+
314
+ const history = [
315
+ '"hello" has been appended',
316
+ '"o" has been deleted',
317
+ ].toReversed(); // Default sort is descending
318
+
319
+ expect(extractActions()).toEqual(history);
320
+ });
321
+
322
+ it("should render co.plaintext delete in head", async () => {
323
+ const value = co.plainText().create("hello", account);
324
+ value.$jazz.applyDiff("ello");
325
+
326
+ expect(value.$jazz.raw.toString()).toEqual("ello");
327
+
328
+ render(
329
+ <HistoryView coValue={value.$jazz.raw} node={value.$jazz.localNode} />,
330
+ );
331
+
332
+ const history = [
333
+ '"hello" has been appended',
334
+ '"h" has been deleted',
335
+ ].toReversed(); // Default sort is descending
336
+
337
+ expect(extractActions()).toEqual(history);
338
+ });
339
+
340
+ it("should render co.plaintext delete history of multiple old insertions in a single row", async () => {
341
+ const value = co.plainText().create("hello", account);
342
+ await sleep(2);
343
+ value.$jazz.applyDiff("hello world");
344
+ await sleep(2);
345
+ value.$jazz.applyDiff("hed");
346
+
347
+ expect(value.$jazz.raw.toString()).toEqual("hed");
348
+
349
+ render(
350
+ <HistoryView coValue={value.$jazz.raw} node={value.$jazz.localNode} />,
351
+ );
352
+
353
+ const history = [
354
+ '"hello" has been appended',
355
+ '" world" has been inserted after "o"',
356
+ '"lod" has been deleted',
357
+ '" worl" has been deleted',
358
+ ].toReversed(); // Default sort is descending
359
+
360
+ expect(extractActions()).toEqual(history);
361
+ });
362
+
363
+ it("should render co.plaintext insertBefore in history", async () => {
364
+ const value = co.plainText().create("world", account);
365
+ await sleep(2);
366
+ value.insertBefore(0, "Hello, ");
367
+
368
+ expect(value.$jazz.raw.toString()).toEqual("Hello, world");
369
+
370
+ render(
371
+ <HistoryView coValue={value.$jazz.raw} node={value.$jazz.localNode} />,
372
+ );
373
+
374
+ const history = [
375
+ '"world" has been appended',
376
+ '"H" has been inserted before "w"',
377
+ '"ello, " has been inserted after "H"',
378
+ ].toReversed(); // Default sort is descending
379
+
380
+ expect(extractActions()).toEqual(history);
381
+ });
382
+
383
+ it("should render co.plaintext insertAfter in history", async () => {
384
+ const value = co.plainText().create("world", account);
385
+ await sleep(2);
386
+ value.insertAfter(0, "Hello, ");
387
+
388
+ expect(value.$jazz.raw.toString()).toEqual("wHello, orld");
389
+
390
+ render(
391
+ <HistoryView coValue={value.$jazz.raw} node={value.$jazz.localNode} />,
392
+ );
393
+
394
+ const history = [
395
+ '"world" has been appended',
396
+ '"Hello, " has been inserted after "w"',
397
+ ].toReversed(); // Default sort is descending
398
+
399
+ expect(extractActions()).toEqual(history);
400
+ });
401
+ });
402
+
403
+ describe("co.group", async () => {
404
+ const account2 = await createJazzTestAccount();
405
+
274
406
  it("should render co.group changes", async () => {
275
407
  const group = co.group().create(account);
276
408
 
@@ -1,5 +1,172 @@
1
- import type { JsonObject, JsonValue, RawCoMap, Role } from "cojson";
1
+ import type {
2
+ JsonObject,
3
+ JsonValue,
4
+ OpID,
5
+ RawCoMap,
6
+ RawCoPlainText,
7
+ RawCoValue,
8
+ Role,
9
+ } from "cojson";
10
+ import { stringifyOpID } from "cojson";
11
+ import type { VerifiedTransaction } from "cojson/dist/coValueCore/coValueCore.js";
2
12
  import type { MapOpPayload } from "cojson/dist/coValues/coMap.js";
13
+ import * as TransactionChanges from "./transactions-changes";
14
+ import type {
15
+ DeletionOpPayload,
16
+ InsertionOpPayload,
17
+ } from "cojson/dist/coValues/coList.js";
18
+
19
+ export function areSameOpIds(
20
+ opId1: OpID | string,
21
+ opId2: OpID | string,
22
+ ): boolean {
23
+ if (typeof opId1 === "string" || typeof opId2 === "string") {
24
+ return opId1 === opId2;
25
+ }
26
+
27
+ return (
28
+ opId1.sessionID === opId2.sessionID &&
29
+ opId1.txIndex === opId2.txIndex &&
30
+ opId1.changeIdx === opId2.changeIdx
31
+ );
32
+ }
33
+
34
+ export function isCoPlainText(coValue: RawCoValue): coValue is RawCoPlainText {
35
+ return coValue.type === "coplaintext";
36
+ }
37
+
38
+ export function getTransactionChanges(
39
+ tx: VerifiedTransaction,
40
+ coValue: RawCoValue,
41
+ ): JsonValue[] {
42
+ if (tx.isValid === false && tx.tx.privacy === "private") {
43
+ const readKey = coValue.core.getReadKey(tx.tx.keyUsed);
44
+ if (!readKey) {
45
+ return [
46
+ `Unable to decrypt transaction: read key ${tx.tx.keyUsed} not found.`,
47
+ ];
48
+ }
49
+
50
+ return (
51
+ coValue.core.verified.decryptTransaction(
52
+ tx.txID.sessionID,
53
+ tx.txID.txIndex,
54
+ readKey,
55
+ ) ?? []
56
+ );
57
+ }
58
+
59
+ // Trying to collapse multiple changes into a single action in the history
60
+ if (isCoPlainText(coValue)) {
61
+ if (tx.changes === undefined || tx.changes.length === 0) return [];
62
+ const firstChange = tx.changes[0]!;
63
+
64
+ if (
65
+ TransactionChanges.isItemAppend(firstChange) &&
66
+ tx.changes.every(
67
+ (c) =>
68
+ TransactionChanges.isItemAppend(c) &&
69
+ areSameOpIds(c.after, firstChange.after),
70
+ )
71
+ ) {
72
+ const changes = tx.changes as InsertionOpPayload<string>[];
73
+ if (firstChange.after !== "start") {
74
+ changes.reverse();
75
+ }
76
+
77
+ return [
78
+ {
79
+ op: "app",
80
+ value: changes.map((c) => c.value).join(""),
81
+ after: firstChange.after,
82
+ },
83
+ ];
84
+ }
85
+
86
+ if (
87
+ TransactionChanges.isItemPrepend(firstChange) &&
88
+ tx.changes.every(
89
+ (c) =>
90
+ TransactionChanges.isItemPrepend(c) &&
91
+ areSameOpIds(c.before, firstChange.before),
92
+ )
93
+ ) {
94
+ const changes = tx.changes as InsertionOpPayload<string>[];
95
+ if (firstChange.before !== "end") {
96
+ changes.reverse();
97
+ }
98
+
99
+ return [
100
+ {
101
+ op: "pre",
102
+ value: changes.map((c) => c.value).join(""),
103
+ before: firstChange.before,
104
+ },
105
+ ];
106
+ }
107
+
108
+ if (
109
+ TransactionChanges.isItemDeletion(firstChange) &&
110
+ tx.changes.every((c) => TransactionChanges.isItemDeletion(c))
111
+ ) {
112
+ const coValueBeforeDeletions = coValue.atTime(tx.madeAt - 1);
113
+
114
+ // Verify if the deleted chars are consecutive
115
+ function changesAreConsecutive(changes: DeletionOpPayload[]): boolean {
116
+ if (changes.length < 2) return false;
117
+ const mapping = coValueBeforeDeletions.mapping.idxAfterOpID;
118
+
119
+ for (let i = 1; i < changes.length; ++i) {
120
+ const prevIdx = mapping[stringifyOpID(changes[i - 1]!.insertion)];
121
+ const currIdx = mapping[stringifyOpID(changes[i]!.insertion)];
122
+ if (currIdx !== prevIdx && currIdx !== (prevIdx ?? -2) + 1) {
123
+ return false;
124
+ }
125
+ }
126
+ return true;
127
+ }
128
+
129
+ if (changesAreConsecutive(tx.changes)) {
130
+ // Group the deletions by insertion.sessionID-txIndex
131
+ // This is to help the readability of deletions that act on different previous transactions
132
+ const groupedBySession: Map<string, DeletionOpPayload[]> = new Map();
133
+ for (const change of tx.changes) {
134
+ const group = `${change.insertion.sessionID}-${change.insertion.txIndex}`;
135
+ if (!groupedBySession.has(group)) groupedBySession.set(group, []);
136
+ groupedBySession.get(group)!.push(change);
137
+ }
138
+
139
+ return Array.from(groupedBySession.values()).map((changes) => {
140
+ const stringDeleted = changes
141
+ // order by txIndex and changeIdx
142
+ .toSorted((a, b) => {
143
+ if (a.insertion.txIndex === b.insertion.txIndex) {
144
+ return a.insertion.changeIdx - b.insertion.changeIdx;
145
+ }
146
+
147
+ return a.insertion.txIndex - b.insertion.txIndex;
148
+ })
149
+ // extract the single char from the insertions
150
+ .map((c) =>
151
+ coValueBeforeDeletions.get(
152
+ coValueBeforeDeletions.mapping.idxAfterOpID[
153
+ stringifyOpID(c.insertion)
154
+ ]!,
155
+ ),
156
+ )
157
+ .join("");
158
+
159
+ return {
160
+ op: "custom",
161
+ action: `"${stringDeleted}" has been deleted`,
162
+ };
163
+ });
164
+ }
165
+ }
166
+ }
167
+
168
+ return tx.changes ?? (tx.tx as any).changes ?? [];
169
+ }
3
170
 
4
171
  export function restoreCoMapToTimestamp(
5
172
  coValue: RawCoMap,
@@ -1,13 +1,112 @@
1
- import { JsonObject } from "cojson";
1
+ import { JsonObject, LocalNode, RawCoPlainText } from "cojson";
2
+ import { useState } from "react";
3
+ import { styled } from "goober";
4
+ import { CoPlainText } from "jazz-tools";
5
+ import { isWriter } from "../utils/permissions.js";
6
+ import { Button } from "../ui/button.js";
2
7
  import { RawDataCard } from "./raw-data-card.js";
8
+ import { Icon } from "../ui/icon.js";
9
+
10
+ export function CoPlainTextView({
11
+ data,
12
+ coValue,
13
+ }: {
14
+ data: JsonObject;
15
+ coValue: RawCoPlainText;
16
+ node: LocalNode;
17
+ }) {
18
+ const currentText = Object.values(data).join("");
19
+ const [isEditing, setIsEditing] = useState(false);
20
+ const [editValue, setEditValue] = useState("");
21
+ const canEdit = isWriter(coValue.group.myRole());
22
+
23
+ const handleEditClick = () => {
24
+ setIsEditing(true);
25
+ setEditValue(currentText);
26
+ };
27
+
28
+ const handleCancel = () => {
29
+ setIsEditing(false);
30
+ setEditValue(currentText);
31
+ };
32
+
33
+ const handleSave = (e: React.FormEvent) => {
34
+ e.preventDefault();
35
+ e.stopPropagation();
36
+
37
+ const coPlainText = CoPlainText.fromRaw(coValue);
38
+ coPlainText.$jazz.applyDiff(editValue);
39
+
40
+ setIsEditing(false);
41
+ };
3
42
 
4
- export function CoPlainTextView({ data }: { data: JsonObject }) {
5
43
  if (!data) return;
6
44
 
45
+ if (isEditing) {
46
+ return (
47
+ <>
48
+ <EditForm onSubmit={handleSave}>
49
+ <StyledTextarea
50
+ value={editValue}
51
+ onChange={(e) => setEditValue(e.target.value)}
52
+ onClick={(e) => e.stopPropagation()}
53
+ />
54
+ <FormActions>
55
+ <Button type="button" variant="secondary" onClick={handleCancel}>
56
+ Cancel
57
+ </Button>
58
+ <Button type="submit" variant="primary">
59
+ Save
60
+ </Button>
61
+ </FormActions>
62
+ </EditForm>
63
+ <RawDataCard data={data} />
64
+ </>
65
+ );
66
+ }
67
+
7
68
  return (
8
69
  <>
9
- <p>{Object.values(data).join("")}</p>
70
+ <p>{currentText}</p>
71
+ <div>
72
+ {canEdit && (
73
+ <Button variant="secondary" onClick={handleEditClick} title="Edit">
74
+ <Icon name="edit" />
75
+ </Button>
76
+ )}
77
+ </div>
10
78
  <RawDataCard data={data} />
11
79
  </>
12
80
  );
13
81
  }
82
+
83
+ const EditForm = styled("form")`
84
+ display: flex;
85
+ flex-direction: column;
86
+ gap: 0.75rem;
87
+ margin-bottom: 1rem;
88
+ `;
89
+
90
+ const StyledTextarea = styled("textarea")`
91
+ width: 100%;
92
+ min-height: 120px;
93
+ border-radius: var(--j-radius-md);
94
+ border: 1px solid var(--j-border-color);
95
+ padding: 0.5rem 0.875rem;
96
+ box-shadow: var(--j-shadow-sm);
97
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
98
+ font-size: 0.875rem;
99
+ background-color: white;
100
+ color: var(--j-text-color-strong);
101
+ resize: vertical;
102
+
103
+ @media (prefers-color-scheme: dark) {
104
+ background-color: var(--j-foreground);
105
+ }
106
+ `;
107
+
108
+ const FormActions = styled("div")`
109
+ display: flex;
110
+ gap: 0.5rem;
111
+ justify-content: flex-end;
112
+ `;
@@ -3,9 +3,9 @@ import { useMemo } from "react";
3
3
  import { styled } from "goober";
4
4
  import { AccountOrGroupText } from "./account-or-group-text";
5
5
  import { DataTable, ColumnDef } from "../ui/data-table";
6
- import type { VerifiedTransaction } from "cojson/dist/coValueCore/coValueCore.js";
7
6
  import { Icon, Accordion } from "../ui";
8
7
  import * as TransactionChanges from "../utils/transactions-changes";
8
+ import { getTransactionChanges } from "../utils/history";
9
9
 
10
10
  type HistoryEntry = {
11
11
  id: string;
@@ -108,30 +108,6 @@ export function HistoryView({
108
108
  );
109
109
  }
110
110
 
111
- function getTransactionChanges(
112
- tx: VerifiedTransaction,
113
- coValue: RawCoValue,
114
- ): JsonValue[] {
115
- if (tx.isValid === false && tx.tx.privacy === "private") {
116
- const readKey = coValue.core.getReadKey(tx.tx.keyUsed);
117
- if (!readKey) {
118
- return [
119
- `Unable to decrypt transaction: read key ${tx.tx.keyUsed} not found.`,
120
- ];
121
- }
122
-
123
- return (
124
- coValue.core.verified.decryptTransaction(
125
- tx.txID.sessionID,
126
- tx.txID.txIndex,
127
- readKey,
128
- ) ?? []
129
- );
130
- }
131
-
132
- return tx.changes ?? (tx.tx as any).changes ?? [];
133
- }
134
-
135
111
  function getHistory(coValue: RawCoValue): HistoryEntry[] {
136
112
  return coValue.core.verifiedTransactions.flatMap((tx, index) => {
137
113
  const changes = getTransactionChanges(tx, coValue);
@@ -240,6 +216,10 @@ function mapTransactionToAction(
240
216
  return `Property "${change.key}" has been deleted`;
241
217
  }
242
218
 
219
+ if ((change as any).op === "custom") {
220
+ return (change as any).action;
221
+ }
222
+
243
223
  return "Unknown action: " + JSON.stringify(change);
244
224
  }
245
225
 
@@ -3,6 +3,7 @@ import {
3
3
  LocalNode,
4
4
  RawCoList,
5
5
  RawCoMap,
6
+ RawCoPlainText,
6
7
  RawCoStream,
7
8
  RawCoValue,
8
9
  RawGroup,
@@ -142,7 +143,13 @@ function View(
142
143
  }
143
144
 
144
145
  if (type === "coplaintext") {
145
- return <CoPlainTextView data={snapshot} />;
146
+ return (
147
+ <CoPlainTextView
148
+ data={snapshot}
149
+ coValue={value as RawCoPlainText}
150
+ node={node}
151
+ />
152
+ );
146
153
  }
147
154
 
148
155
  if (type === "colist") {
@@ -48,10 +48,6 @@ export type ResolvedAccount = {
48
48
  [key: string]: JSON;
49
49
  };
50
50
 
51
- export const isAccount = (coValue: JSONObject): coValue is ResolvedAccount => {
52
- return isGroup(coValue) && "profile" in coValue;
53
- };
54
-
55
51
  export async function resolveCoValue(
56
52
  coValueId: CoID<RawCoValue>,
57
53
  node: LocalNode,
@@ -89,7 +85,7 @@ export async function resolveCoValue(
89
85
  if (type === "comap") {
90
86
  if (isBrowserImage(snapshot)) {
91
87
  extendedType = "image";
92
- } else if (isAccount(snapshot)) {
88
+ } else if (value.headerMeta?.type === "account") {
93
89
  extendedType = "account";
94
90
  } else if (value.core.isGroup()) {
95
91
  extendedType = "group";
@@ -125,7 +121,7 @@ function subscribeToCoValue(
125
121
  if (type === "comap") {
126
122
  if (isBrowserImage(snapshot)) {
127
123
  extendedType = "image";
128
- } else if (isAccount(snapshot)) {
124
+ } else if (value.headerMeta?.type === "account") {
129
125
  extendedType = "account";
130
126
  } else if (value.core.isGroup()) {
131
127
  extendedType = "group";
@@ -26,7 +26,7 @@ import {
26
26
  SchemaResolveQuery,
27
27
  SubscriptionScope,
28
28
  coValueClassFromCoValueClassOrSchema,
29
- createUnloadedCoValue,
29
+ getUnloadedCoValueWithoutId,
30
30
  type BranchDefinition,
31
31
  } from "jazz-tools";
32
32
  import { JazzContext, JazzContextManagerContext } from "./provider.js";
@@ -172,39 +172,15 @@ export function useCoValueSubscription<
172
172
  return subscription.subscription as CoValueSubscription<S, R>;
173
173
  }
174
174
 
175
- function getSubscriptionValue<C extends CoValue>(
176
- subscription: SubscriptionScope<C> | null,
177
- ): MaybeLoaded<C> {
178
- if (!subscription) {
179
- return createUnloadedCoValue("", CoValueLoadingState.UNAVAILABLE);
180
- }
181
- const value = subscription.getCurrentValue();
182
- if (typeof value === "string") {
183
- return createUnloadedCoValue(subscription.id, value);
184
- }
185
- return value;
186
- }
187
-
188
175
  function useGetCurrentValue<C extends CoValue>(
189
176
  subscription: SubscriptionScope<C> | null,
190
177
  ) {
191
- const previousValue = useRef<MaybeLoaded<CoValue> | undefined>(undefined);
192
-
193
178
  return useCallback(() => {
194
- const currentValue = getSubscriptionValue(subscription);
195
- // Avoid re-renders if the value is not loaded and didn't change
196
- if (
197
- previousValue.current !== undefined &&
198
- previousValue.current.$jazz.id === currentValue.$jazz.id &&
199
- !previousValue.current.$isLoaded &&
200
- !currentValue.$isLoaded &&
201
- previousValue.current.$jazz.loadingState ===
202
- currentValue.$jazz.loadingState
203
- ) {
204
- return previousValue.current as MaybeLoaded<C>;
179
+ if (!subscription) {
180
+ return getUnloadedCoValueWithoutId(CoValueLoadingState.UNAVAILABLE);
205
181
  }
206
- previousValue.current = currentValue;
207
- return currentValue;
182
+
183
+ return subscription.getCurrentValue();
208
184
  }, [subscription]);
209
185
  }
210
186