jazz-tools 0.18.23 → 0.18.25
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.
- package/.svelte-kit/__package__/Provider.svelte +2 -0
- package/.svelte-kit/__package__/Provider.svelte.d.ts.map +1 -1
- package/.turbo/turbo-build.log +65 -65
- package/CHANGELOG.md +25 -0
- package/dist/browser/index.js +9 -9
- package/dist/browser/index.js.map +1 -1
- package/dist/{chunk-D5L6ES2M.js → chunk-DOCEAUVD.js} +72 -35
- package/dist/chunk-DOCEAUVD.js.map +1 -0
- package/dist/{chunk-BOMSRY5H.js → chunk-M2HGBOXS.js} +2 -2
- package/dist/chunk-M2HGBOXS.js.map +1 -0
- package/dist/index.js +2 -2
- package/dist/inspector/{custom-element-XDJT5T57.js → custom-element-A7UAELEG.js} +3 -1
- package/dist/inspector/{custom-element-XDJT5T57.js.map → custom-element-A7UAELEG.js.map} +1 -1
- package/dist/inspector/index.js +2 -0
- package/dist/inspector/index.js.map +1 -1
- package/dist/inspector/register-custom-element.js +1 -1
- package/dist/inspector/viewer/use-open-inspector.d.ts.map +1 -1
- package/dist/inspector/viewer/use-page-path.d.ts.map +1 -1
- package/dist/react/hooks.d.ts +1 -1
- package/dist/react/hooks.d.ts.map +1 -1
- package/dist/react/index.d.ts +2 -1
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +17 -3
- package/dist/react/index.js.map +1 -1
- package/dist/react/provider.d.ts +2 -1
- package/dist/react/provider.d.ts.map +1 -1
- package/dist/react/ssr.js +1 -1
- package/dist/react/ssr.js.map +1 -1
- package/dist/react-core/hooks.d.ts +13 -0
- package/dist/react-core/hooks.d.ts.map +1 -1
- package/dist/react-core/index.d.ts +2 -0
- package/dist/react-core/index.d.ts.map +1 -1
- package/dist/react-core/index.js +104 -0
- package/dist/react-core/index.js.map +1 -1
- package/dist/react-core/subscription-provider.d.ts +27 -0
- package/dist/react-core/subscription-provider.d.ts.map +1 -0
- package/dist/react-core/tests/subscription.bench.d.ts +2 -0
- package/dist/react-core/tests/subscription.bench.d.ts.map +1 -0
- package/dist/react-core/tests/useSubscriptionSelector.test.d.ts +2 -0
- package/dist/react-core/tests/useSubscriptionSelector.test.d.ts.map +1 -0
- package/dist/react-core/types.d.ts +10 -0
- package/dist/react-core/types.d.ts.map +1 -0
- package/dist/react-native-core/hooks.d.ts +1 -1
- package/dist/react-native-core/hooks.d.ts.map +1 -1
- package/dist/react-native-core/index.d.ts +1 -0
- package/dist/react-native-core/index.d.ts.map +1 -1
- package/dist/react-native-core/index.js +25 -12
- package/dist/react-native-core/index.js.map +1 -1
- package/dist/react-native-core/provider.d.ts +2 -1
- package/dist/react-native-core/provider.d.ts.map +1 -1
- package/dist/svelte/Provider.svelte +2 -0
- package/dist/svelte/Provider.svelte.d.ts.map +1 -1
- package/dist/testing.js +4 -4
- package/dist/testing.js.map +1 -1
- package/dist/tools/auth/AuthSecretStorage.d.ts +3 -1
- package/dist/tools/auth/AuthSecretStorage.d.ts.map +1 -1
- package/dist/tools/coValues/account.d.ts +3 -3
- package/dist/tools/coValues/account.d.ts.map +1 -1
- package/dist/tools/coValues/group.d.ts +20 -1
- package/dist/tools/coValues/group.d.ts.map +1 -1
- package/dist/tools/coValues/interfaces.d.ts.map +1 -1
- package/dist/tools/implementation/ContextManager.d.ts +1 -0
- package/dist/tools/implementation/ContextManager.d.ts.map +1 -1
- package/dist/tools/implementation/createContext.d.ts +7 -7
- package/dist/tools/implementation/createContext.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/schemaTypes/AccountSchema.d.ts +3 -8
- package/dist/tools/implementation/zodSchema/schemaTypes/AccountSchema.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/schemaTypes/GroupSchema.d.ts +5 -0
- package/dist/tools/implementation/zodSchema/schemaTypes/GroupSchema.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/zodCo.d.ts.map +1 -1
- package/dist/tools/ssr.js +1 -1
- package/dist/tools/testing.d.ts.map +1 -1
- package/dist/worker/index.js +3 -3
- package/dist/worker/index.js.map +1 -1
- package/package.json +5 -4
- package/src/better-auth/database-adapter/tests/sync-utils.ts +1 -1
- package/src/browser/createBrowserContext.ts +9 -9
- package/src/browser/tests/utils.ts +2 -2
- package/src/inspector/viewer/use-open-inspector.ts +1 -0
- package/src/inspector/viewer/use-page-path.ts +1 -0
- package/src/react/hooks.tsx +3 -0
- package/src/react/index.ts +9 -0
- package/src/react/provider.tsx +3 -0
- package/src/react-core/hooks.ts +37 -5
- package/src/react-core/index.ts +2 -0
- package/src/react-core/subscription-provider.tsx +144 -0
- package/src/react-core/tests/subscription.bench.tsx +319 -0
- package/src/react-core/tests/useSubscriptionSelector.test.ts +250 -0
- package/src/react-core/types.ts +19 -0
- package/src/react-native-core/hooks.tsx +3 -0
- package/src/react-native-core/index.ts +6 -0
- package/src/react-native-core/platform.ts +9 -9
- package/src/react-native-core/provider.tsx +3 -1
- package/src/svelte/Provider.svelte +2 -0
- package/src/tools/auth/AuthSecretStorage.ts +16 -9
- package/src/tools/coValues/account.ts +5 -5
- package/src/tools/coValues/group.ts +33 -0
- package/src/tools/coValues/interfaces.ts +4 -1
- package/src/tools/implementation/ContextManager.ts +7 -3
- package/src/tools/implementation/createContext.ts +12 -12
- package/src/tools/implementation/zodSchema/schemaTypes/AccountSchema.ts +5 -5
- package/src/tools/implementation/zodSchema/schemaTypes/GroupSchema.ts +7 -1
- package/src/tools/ssr/ssr.ts +1 -1
- package/src/tools/testing.ts +3 -3
- package/src/tools/tests/AuthSecretStorage.test.ts +59 -1
- package/src/tools/tests/ContextManager.test.ts +11 -1
- package/src/tools/tests/account.test.ts +16 -0
- package/src/tools/tests/coPlainText.test.ts +2 -2
- package/src/tools/tests/createContext.test.ts +19 -19
- package/src/tools/tests/deepLoading.test.ts +2 -2
- package/src/tools/tests/group.test.ts +59 -0
- package/src/tools/tests/inbox.test.ts +2 -2
- package/src/tools/tests/utils.ts +3 -3
- package/src/worker/index.ts +3 -3
- package/dist/chunk-BOMSRY5H.js.map +0 -1
- package/dist/chunk-D5L6ES2M.js.map +0 -1
@@ -0,0 +1,319 @@
|
|
1
|
+
// @vitest-environment happy-dom
|
2
|
+
|
3
|
+
import { cojsonInternals } from "cojson";
|
4
|
+
import { co, z } from "jazz-tools";
|
5
|
+
import { describe, bench } from "vitest";
|
6
|
+
import {
|
7
|
+
useAccountSubscription,
|
8
|
+
useSubscriptionSelector,
|
9
|
+
useAccountWithSelector,
|
10
|
+
CoValueSubscription,
|
11
|
+
} from "../index.js";
|
12
|
+
import { createJazzTestAccount, setupJazzTestSync } from "../testing.js";
|
13
|
+
import { render } from "./testUtils.js";
|
14
|
+
|
15
|
+
cojsonInternals.setCoValueLoadingRetryDelay(300);
|
16
|
+
|
17
|
+
await setupJazzTestSync();
|
18
|
+
await createJazzTestAccount({
|
19
|
+
isCurrentActiveAccount: true,
|
20
|
+
creationProps: { name: "Hermes Puggington" },
|
21
|
+
});
|
22
|
+
|
23
|
+
const AccountSchema = co.account();
|
24
|
+
|
25
|
+
const AccountName = () => {
|
26
|
+
const name = useAccountWithSelector(AccountSchema, {
|
27
|
+
resolve: {
|
28
|
+
profile: true,
|
29
|
+
},
|
30
|
+
select: (account) => account?.profile?.name,
|
31
|
+
});
|
32
|
+
|
33
|
+
if (!name) return null;
|
34
|
+
|
35
|
+
return <span>{name}</span>;
|
36
|
+
};
|
37
|
+
|
38
|
+
const AccountNameFromSubscription = ({
|
39
|
+
subscription,
|
40
|
+
}: {
|
41
|
+
subscription: CoValueSubscription<typeof AccountSchema, { profile: true }>;
|
42
|
+
}) => {
|
43
|
+
const name = useSubscriptionSelector(subscription, {
|
44
|
+
select: (account) => account?.profile?.name,
|
45
|
+
});
|
46
|
+
|
47
|
+
if (!name) return null;
|
48
|
+
|
49
|
+
return <span>{name}</span>;
|
50
|
+
};
|
51
|
+
|
52
|
+
const UseAccountWithSelector = () => {
|
53
|
+
return (
|
54
|
+
<div>
|
55
|
+
{Array.from({ length: 1000 }).map((_, i) => (
|
56
|
+
<AccountName key={i} />
|
57
|
+
))}
|
58
|
+
</div>
|
59
|
+
);
|
60
|
+
};
|
61
|
+
|
62
|
+
const UseAccountSubscription = () => {
|
63
|
+
const subscription = useAccountSubscription(AccountSchema, {
|
64
|
+
resolve: {
|
65
|
+
profile: true,
|
66
|
+
},
|
67
|
+
});
|
68
|
+
|
69
|
+
return (
|
70
|
+
<div>
|
71
|
+
{Array.from({ length: 1000 }).map((_, i) => (
|
72
|
+
<AccountNameFromSubscription key={i} subscription={subscription} />
|
73
|
+
))}
|
74
|
+
</div>
|
75
|
+
);
|
76
|
+
};
|
77
|
+
|
78
|
+
describe("1000 value loads", () => {
|
79
|
+
bench(
|
80
|
+
"useAccountSubscription + useSubscriptionSelector",
|
81
|
+
() => {
|
82
|
+
render(<UseAccountSubscription />);
|
83
|
+
},
|
84
|
+
{ iterations: 50 },
|
85
|
+
);
|
86
|
+
|
87
|
+
bench(
|
88
|
+
"useAccountWithSelector",
|
89
|
+
() => {
|
90
|
+
render(<UseAccountWithSelector />);
|
91
|
+
},
|
92
|
+
{ iterations: 50 },
|
93
|
+
);
|
94
|
+
});
|
95
|
+
|
96
|
+
describe("deeply resolved coMaps", async () => {
|
97
|
+
const Task = co.map({
|
98
|
+
title: z.string(),
|
99
|
+
get project() {
|
100
|
+
return Project;
|
101
|
+
},
|
102
|
+
});
|
103
|
+
|
104
|
+
const TaskList = co.list(Task);
|
105
|
+
|
106
|
+
const Project = co.map({
|
107
|
+
name: z.string(),
|
108
|
+
tasks: TaskList,
|
109
|
+
draftTasks: TaskList,
|
110
|
+
deletedTasks: TaskList,
|
111
|
+
});
|
112
|
+
|
113
|
+
const Organization = co.map({
|
114
|
+
name: z.string(),
|
115
|
+
projects: co.list(Project),
|
116
|
+
});
|
117
|
+
|
118
|
+
const AccountRoot = co.map({
|
119
|
+
organizations: co.list(Organization),
|
120
|
+
});
|
121
|
+
|
122
|
+
const AccountSchema = co
|
123
|
+
.account({
|
124
|
+
root: AccountRoot,
|
125
|
+
profile: co.profile(),
|
126
|
+
})
|
127
|
+
.withMigration(async (account) => {
|
128
|
+
if (!account.$jazz.has("root")) {
|
129
|
+
account.$jazz.set("root", {
|
130
|
+
organizations: [
|
131
|
+
{
|
132
|
+
name: "My organization",
|
133
|
+
projects: [
|
134
|
+
{
|
135
|
+
name: "My project",
|
136
|
+
tasks: [],
|
137
|
+
draftTasks: [],
|
138
|
+
deletedTasks: [],
|
139
|
+
},
|
140
|
+
],
|
141
|
+
},
|
142
|
+
],
|
143
|
+
});
|
144
|
+
}
|
145
|
+
});
|
146
|
+
|
147
|
+
const account = await createJazzTestAccount({
|
148
|
+
AccountSchema,
|
149
|
+
isCurrentActiveAccount: true,
|
150
|
+
creationProps: { name: "Hermes Puggington" },
|
151
|
+
});
|
152
|
+
|
153
|
+
const root = await account.root.$jazz.ensureLoaded({
|
154
|
+
resolve: {
|
155
|
+
organizations: {
|
156
|
+
$each: {
|
157
|
+
projects: {
|
158
|
+
$each: true,
|
159
|
+
},
|
160
|
+
},
|
161
|
+
},
|
162
|
+
},
|
163
|
+
});
|
164
|
+
|
165
|
+
const project = root.organizations[0]?.projects[0]!;
|
166
|
+
|
167
|
+
for (let i = 0; i < 250; i++) {
|
168
|
+
const taskList = (["tasks", "draftTasks", "deletedTasks"] as const)[i % 3]!;
|
169
|
+
|
170
|
+
project[taskList].$jazz.push({
|
171
|
+
title: `Task ${i}`,
|
172
|
+
project,
|
173
|
+
});
|
174
|
+
}
|
175
|
+
|
176
|
+
const SingleSubscriptionTasks = ({
|
177
|
+
subscription,
|
178
|
+
taskListType,
|
179
|
+
}: {
|
180
|
+
subscription: CoValueSubscription<
|
181
|
+
typeof AccountSchema,
|
182
|
+
{
|
183
|
+
root: {
|
184
|
+
organizations: {
|
185
|
+
$each: {
|
186
|
+
projects: {
|
187
|
+
$each: {
|
188
|
+
tasks: { $each: { project: true } };
|
189
|
+
draftTasks: { $each: { project: true } };
|
190
|
+
deletedTasks: { $each: { project: true } };
|
191
|
+
};
|
192
|
+
};
|
193
|
+
};
|
194
|
+
};
|
195
|
+
};
|
196
|
+
}
|
197
|
+
>;
|
198
|
+
taskListType: "tasks" | "draftTasks" | "deletedTasks";
|
199
|
+
}) => {
|
200
|
+
const allProjectsTasks = useSubscriptionSelector(subscription, {
|
201
|
+
select: (account) => {
|
202
|
+
return account?.root.organizations.flatMap((org) =>
|
203
|
+
org?.projects.flatMap((project) =>
|
204
|
+
project?.[taskListType]?.flatMap((task) => task),
|
205
|
+
),
|
206
|
+
);
|
207
|
+
},
|
208
|
+
});
|
209
|
+
|
210
|
+
return (
|
211
|
+
<div>
|
212
|
+
{allProjectsTasks?.map((task) => (
|
213
|
+
<div key={task?.$jazz.id}>{task?.title}</div>
|
214
|
+
))}
|
215
|
+
</div>
|
216
|
+
);
|
217
|
+
};
|
218
|
+
|
219
|
+
const SingleSubscription = () => {
|
220
|
+
const subscription = useAccountSubscription(AccountSchema, {
|
221
|
+
resolve: {
|
222
|
+
root: {
|
223
|
+
organizations: {
|
224
|
+
$each: {
|
225
|
+
projects: {
|
226
|
+
$each: {
|
227
|
+
tasks: { $each: { project: true } },
|
228
|
+
draftTasks: { $each: { project: true } },
|
229
|
+
deletedTasks: { $each: { project: true } },
|
230
|
+
},
|
231
|
+
},
|
232
|
+
},
|
233
|
+
},
|
234
|
+
},
|
235
|
+
},
|
236
|
+
});
|
237
|
+
|
238
|
+
return (
|
239
|
+
<div>
|
240
|
+
<SingleSubscriptionTasks
|
241
|
+
subscription={subscription}
|
242
|
+
taskListType="tasks"
|
243
|
+
/>
|
244
|
+
<SingleSubscriptionTasks
|
245
|
+
subscription={subscription}
|
246
|
+
taskListType="draftTasks"
|
247
|
+
/>
|
248
|
+
<SingleSubscriptionTasks
|
249
|
+
subscription={subscription}
|
250
|
+
taskListType="deletedTasks"
|
251
|
+
/>
|
252
|
+
</div>
|
253
|
+
);
|
254
|
+
};
|
255
|
+
|
256
|
+
bench(
|
257
|
+
"useAccountSubscription + useSubscriptionSelector",
|
258
|
+
() => {
|
259
|
+
render(<SingleSubscription />);
|
260
|
+
},
|
261
|
+
{ iterations: 200 },
|
262
|
+
);
|
263
|
+
|
264
|
+
const MultipleSubscriptionTasks = ({
|
265
|
+
taskListType,
|
266
|
+
}: {
|
267
|
+
taskListType: "tasks" | "draftTasks" | "deletedTasks";
|
268
|
+
}) => {
|
269
|
+
const subscription = useAccountWithSelector(AccountSchema, {
|
270
|
+
resolve: {
|
271
|
+
root: {
|
272
|
+
organizations: {
|
273
|
+
$each: {
|
274
|
+
projects: {
|
275
|
+
$each: {
|
276
|
+
tasks: { $each: { project: true } },
|
277
|
+
draftTasks: { $each: { project: true } },
|
278
|
+
deletedTasks: { $each: { project: true } },
|
279
|
+
},
|
280
|
+
},
|
281
|
+
},
|
282
|
+
},
|
283
|
+
},
|
284
|
+
},
|
285
|
+
select: (account) =>
|
286
|
+
account?.root.organizations.flatMap((org) =>
|
287
|
+
org?.projects.flatMap((project) =>
|
288
|
+
project?.[taskListType]?.flatMap((task) => task),
|
289
|
+
),
|
290
|
+
),
|
291
|
+
});
|
292
|
+
|
293
|
+
return (
|
294
|
+
<div>
|
295
|
+
{subscription?.map((task) => (
|
296
|
+
<div key={task?.$jazz.id}>{task?.title}</div>
|
297
|
+
))}
|
298
|
+
</div>
|
299
|
+
);
|
300
|
+
};
|
301
|
+
|
302
|
+
const MultipleSubscription = () => {
|
303
|
+
return (
|
304
|
+
<div>
|
305
|
+
<MultipleSubscriptionTasks taskListType="tasks" />
|
306
|
+
<MultipleSubscriptionTasks taskListType="draftTasks" />
|
307
|
+
<MultipleSubscriptionTasks taskListType="deletedTasks" />
|
308
|
+
</div>
|
309
|
+
);
|
310
|
+
};
|
311
|
+
|
312
|
+
bench(
|
313
|
+
"useAccountWithSelector",
|
314
|
+
() => {
|
315
|
+
render(<MultipleSubscription />);
|
316
|
+
},
|
317
|
+
{ iterations: 200 },
|
318
|
+
);
|
319
|
+
});
|
@@ -0,0 +1,250 @@
|
|
1
|
+
// @vitest-environment happy-dom
|
2
|
+
|
3
|
+
import { cojsonInternals } from "cojson";
|
4
|
+
import { co, z } from "jazz-tools";
|
5
|
+
import { beforeEach, describe, expect, expectTypeOf, it } from "vitest";
|
6
|
+
import {
|
7
|
+
useCoValueSubscription,
|
8
|
+
useAccountSubscription,
|
9
|
+
useSubscriptionSelector,
|
10
|
+
} from "../index.js";
|
11
|
+
import { createJazzTestAccount, setupJazzTestSync } from "../testing.js";
|
12
|
+
import { act, renderHook, waitFor } from "./testUtils.js";
|
13
|
+
import { useRef } from "react";
|
14
|
+
|
15
|
+
beforeEach(async () => {
|
16
|
+
await setupJazzTestSync();
|
17
|
+
|
18
|
+
await createJazzTestAccount({
|
19
|
+
isCurrentActiveAccount: true,
|
20
|
+
});
|
21
|
+
});
|
22
|
+
|
23
|
+
cojsonInternals.setCoValueLoadingRetryDelay(300);
|
24
|
+
|
25
|
+
const useRenderCount = <T>(hook: () => T) => {
|
26
|
+
const renderCountRef = useRef(0);
|
27
|
+
const result = hook();
|
28
|
+
renderCountRef.current = renderCountRef.current + 1;
|
29
|
+
return {
|
30
|
+
renderCount: renderCountRef.current,
|
31
|
+
result,
|
32
|
+
};
|
33
|
+
};
|
34
|
+
|
35
|
+
describe("useSubscriptionSelector", () => {
|
36
|
+
it("should return coValue", () => {
|
37
|
+
const TestMap = co.map({
|
38
|
+
value: z.string(),
|
39
|
+
});
|
40
|
+
|
41
|
+
const map = TestMap.create({
|
42
|
+
value: "123",
|
43
|
+
});
|
44
|
+
|
45
|
+
const { result } = renderHook(() => {
|
46
|
+
const subscription = useCoValueSubscription(TestMap, map.$jazz.id);
|
47
|
+
return useSubscriptionSelector(subscription);
|
48
|
+
});
|
49
|
+
|
50
|
+
expect(result.current?.value).toBe("123");
|
51
|
+
});
|
52
|
+
|
53
|
+
it("should resolve nested coValues", () => {
|
54
|
+
const TestNestedMap = co.map({
|
55
|
+
content: z.string(),
|
56
|
+
});
|
57
|
+
|
58
|
+
const TestMap = co.map({
|
59
|
+
content: z.string(),
|
60
|
+
nested: TestNestedMap,
|
61
|
+
});
|
62
|
+
|
63
|
+
const map = TestMap.create({
|
64
|
+
content: "123",
|
65
|
+
nested: {
|
66
|
+
content: "456",
|
67
|
+
},
|
68
|
+
});
|
69
|
+
|
70
|
+
const { result } = renderHook(() => {
|
71
|
+
const subscription = useCoValueSubscription(TestMap, map.$jazz.id);
|
72
|
+
return useSubscriptionSelector(subscription);
|
73
|
+
});
|
74
|
+
|
75
|
+
expect(result.current?.nested?.content).toBe("456");
|
76
|
+
});
|
77
|
+
|
78
|
+
it("should return null on invalid coValue id", async () => {
|
79
|
+
const TestMap = co.map({
|
80
|
+
value: z.string(),
|
81
|
+
});
|
82
|
+
|
83
|
+
const { result } = renderHook(() => {
|
84
|
+
const subscription = useCoValueSubscription(TestMap, "123");
|
85
|
+
return useSubscriptionSelector(subscription);
|
86
|
+
});
|
87
|
+
|
88
|
+
expect(result.current).toBeUndefined();
|
89
|
+
|
90
|
+
await waitFor(() => {
|
91
|
+
expect(result.current).toBeNull();
|
92
|
+
});
|
93
|
+
});
|
94
|
+
|
95
|
+
it("should return coAccount", async () => {
|
96
|
+
const account = await createJazzTestAccount();
|
97
|
+
|
98
|
+
const { result } = renderHook(
|
99
|
+
() => {
|
100
|
+
const subscription = useAccountSubscription(co.account());
|
101
|
+
return useSubscriptionSelector(subscription);
|
102
|
+
},
|
103
|
+
{
|
104
|
+
account,
|
105
|
+
},
|
106
|
+
);
|
107
|
+
|
108
|
+
expect(result.current?.$jazz.id).toBe(account.$jazz.id);
|
109
|
+
});
|
110
|
+
|
111
|
+
it("should return value from coValue with selector", () => {
|
112
|
+
const TestMap = co.map({
|
113
|
+
value: z.string(),
|
114
|
+
});
|
115
|
+
|
116
|
+
const map = TestMap.create({
|
117
|
+
value: "123",
|
118
|
+
});
|
119
|
+
|
120
|
+
const { result } = renderHook(() => {
|
121
|
+
const subscription = useCoValueSubscription(TestMap, map.$jazz.id);
|
122
|
+
return useSubscriptionSelector(subscription, {
|
123
|
+
select: (v) => v?.value,
|
124
|
+
});
|
125
|
+
});
|
126
|
+
|
127
|
+
expect(result.current).toBe("123");
|
128
|
+
});
|
129
|
+
|
130
|
+
it("should return value from coAccount with selector", async () => {
|
131
|
+
const account = await createJazzTestAccount({
|
132
|
+
creationProps: { name: "test" },
|
133
|
+
});
|
134
|
+
|
135
|
+
const { result } = renderHook(
|
136
|
+
() => {
|
137
|
+
const subscription = useAccountSubscription(co.account());
|
138
|
+
return useSubscriptionSelector(subscription, {
|
139
|
+
select: (v) => v?.profile?.name,
|
140
|
+
});
|
141
|
+
},
|
142
|
+
{
|
143
|
+
account,
|
144
|
+
},
|
145
|
+
);
|
146
|
+
|
147
|
+
expect(result.current).toBe("test");
|
148
|
+
});
|
149
|
+
|
150
|
+
it("should update value from coValue with selected value changes", () => {
|
151
|
+
const TestMap = co.map({
|
152
|
+
value: z.string(),
|
153
|
+
});
|
154
|
+
|
155
|
+
const map = TestMap.create({
|
156
|
+
value: "123",
|
157
|
+
});
|
158
|
+
|
159
|
+
const { result } = renderHook(() => {
|
160
|
+
const subscription = useCoValueSubscription(TestMap, map.$jazz.id);
|
161
|
+
return useSubscriptionSelector(subscription, {
|
162
|
+
select: (v) => v?.value,
|
163
|
+
});
|
164
|
+
});
|
165
|
+
|
166
|
+
expect(result.current).toBe("123");
|
167
|
+
|
168
|
+
act(() => {
|
169
|
+
map.$jazz.set("value", "456");
|
170
|
+
});
|
171
|
+
|
172
|
+
expect(result.current).toBe("456");
|
173
|
+
});
|
174
|
+
|
175
|
+
it("should not re-render when a non-selected field is updated and not selected", () => {
|
176
|
+
const TestMap = co.map({
|
177
|
+
value: z.string(),
|
178
|
+
other: z.string(),
|
179
|
+
});
|
180
|
+
|
181
|
+
const map = TestMap.create({
|
182
|
+
value: "1",
|
183
|
+
other: "1",
|
184
|
+
});
|
185
|
+
|
186
|
+
const { result } = renderHook(() =>
|
187
|
+
useRenderCount(() => {
|
188
|
+
const subscription = useCoValueSubscription(TestMap, map.$jazz.id);
|
189
|
+
return useSubscriptionSelector(subscription, {
|
190
|
+
select: (v) => v?.value,
|
191
|
+
});
|
192
|
+
}),
|
193
|
+
);
|
194
|
+
|
195
|
+
expect(result.current.result).toBe("1");
|
196
|
+
expect(result.current.renderCount).toBe(1);
|
197
|
+
|
198
|
+
act(() => {
|
199
|
+
map.$jazz.set("other", "2");
|
200
|
+
});
|
201
|
+
|
202
|
+
expect(result.current.result).toBe("1");
|
203
|
+
expect(result.current.renderCount).toBe(1);
|
204
|
+
});
|
205
|
+
|
206
|
+
it("should only re-render or load new value when equalityFn returns true", () => {
|
207
|
+
const TestMap = co.map({
|
208
|
+
value: z.number(),
|
209
|
+
other: z.number(),
|
210
|
+
});
|
211
|
+
|
212
|
+
const map = TestMap.create({
|
213
|
+
value: 1,
|
214
|
+
other: 2,
|
215
|
+
});
|
216
|
+
|
217
|
+
const { result } = renderHook(() =>
|
218
|
+
useRenderCount(() => {
|
219
|
+
const subscription = useCoValueSubscription(TestMap, map.$jazz.id);
|
220
|
+
return useSubscriptionSelector(subscription, {
|
221
|
+
select: (v) =>
|
222
|
+
v
|
223
|
+
? [Math.floor(v.value / 5), Math.floor(v.other / 5)].toSorted()
|
224
|
+
: [],
|
225
|
+
equalityFn: (a, b) => a.every((v, i) => v === b[i]),
|
226
|
+
});
|
227
|
+
}),
|
228
|
+
);
|
229
|
+
|
230
|
+
expect(result.current.result).toEqual([0, 0]);
|
231
|
+
expect(result.current.renderCount).toBe(1);
|
232
|
+
|
233
|
+
act(() => {
|
234
|
+
map.$jazz.applyDiff({
|
235
|
+
value: 3,
|
236
|
+
other: 4,
|
237
|
+
});
|
238
|
+
});
|
239
|
+
|
240
|
+
expect(result.current.result).toEqual([0, 0]);
|
241
|
+
expect(result.current.renderCount).toBe(1);
|
242
|
+
|
243
|
+
act(() => {
|
244
|
+
map.$jazz.set("value", 5);
|
245
|
+
});
|
246
|
+
|
247
|
+
expect(result.current.result).toEqual([0, 1]);
|
248
|
+
expect(result.current.renderCount).toBe(2);
|
249
|
+
});
|
250
|
+
});
|
@@ -0,0 +1,19 @@
|
|
1
|
+
import {
|
2
|
+
CoValueClassOrSchema,
|
3
|
+
ResolveQuery,
|
4
|
+
SubscriptionScope,
|
5
|
+
} from "jazz-tools";
|
6
|
+
|
7
|
+
declare const subscriptionTag: unique symbol;
|
8
|
+
|
9
|
+
export type CoValueSubscription<
|
10
|
+
S extends CoValueClassOrSchema,
|
11
|
+
R extends ResolveQuery<S>,
|
12
|
+
> =
|
13
|
+
| (SubscriptionScope<any> & {
|
14
|
+
[subscriptionTag]: {
|
15
|
+
schema: S;
|
16
|
+
resolve: R;
|
17
|
+
};
|
18
|
+
})
|
19
|
+
| null;
|
@@ -16,6 +16,9 @@ export {
|
|
16
16
|
useCoStateWithSelector,
|
17
17
|
useAccountWithSelector,
|
18
18
|
useSyncConnectionStatus,
|
19
|
+
useCoValueSubscription,
|
20
|
+
useAccountSubscription,
|
21
|
+
useSubscriptionSelector,
|
19
22
|
} from "jazz-tools/react-core";
|
20
23
|
|
21
24
|
export function useAcceptInviteNative<S extends CoValueClassOrSchema>({
|
@@ -4,6 +4,12 @@ export * from "./provider.js";
|
|
4
4
|
export * from "./storage/kv-store-context.js";
|
5
5
|
export * from "./media/image.js";
|
6
6
|
|
7
|
+
export {
|
8
|
+
createCoValueSubscriptionContext,
|
9
|
+
createAccountSubscriptionContext,
|
10
|
+
type CoValueSubscription,
|
11
|
+
} from "jazz-tools/react-core";
|
12
|
+
|
7
13
|
export { SQLiteDatabaseDriverAsync } from "cojson";
|
8
14
|
export { parseInviteLink } from "jazz-tools";
|
9
15
|
export { createInviteLink, setupKvStore } from "./platform.js";
|
@@ -46,7 +46,7 @@ async function setupPeers(options: BaseReactNativeContextOptions) {
|
|
46
46
|
const crypto = await CryptoProvider.create();
|
47
47
|
let node: LocalNode | undefined = undefined;
|
48
48
|
|
49
|
-
const
|
49
|
+
const peers: Peer[] = [];
|
50
50
|
|
51
51
|
const storage =
|
52
52
|
options.storage && options.storage !== "disabled"
|
@@ -58,7 +58,7 @@ async function setupPeers(options: BaseReactNativeContextOptions) {
|
|
58
58
|
toggleNetwork: () => {},
|
59
59
|
addConnectionListener: () => () => {},
|
60
60
|
connected: () => false,
|
61
|
-
|
61
|
+
peers,
|
62
62
|
setNode: () => {},
|
63
63
|
crypto,
|
64
64
|
storage,
|
@@ -72,11 +72,11 @@ async function setupPeers(options: BaseReactNativeContextOptions) {
|
|
72
72
|
if (node) {
|
73
73
|
node.syncManager.addPeer(peer);
|
74
74
|
} else {
|
75
|
-
|
75
|
+
peers.push(peer);
|
76
76
|
}
|
77
77
|
},
|
78
78
|
removePeer: (peer) => {
|
79
|
-
|
79
|
+
peers.splice(peers.indexOf(peer), 1);
|
80
80
|
},
|
81
81
|
});
|
82
82
|
|
@@ -106,7 +106,7 @@ async function setupPeers(options: BaseReactNativeContextOptions) {
|
|
106
106
|
};
|
107
107
|
},
|
108
108
|
connected: () => wsPeer.connected,
|
109
|
-
|
109
|
+
peers,
|
110
110
|
setNode,
|
111
111
|
crypto,
|
112
112
|
storage,
|
@@ -118,7 +118,7 @@ export async function createJazzReactNativeGuestContext(
|
|
118
118
|
) {
|
119
119
|
const {
|
120
120
|
toggleNetwork,
|
121
|
-
|
121
|
+
peers,
|
122
122
|
setNode,
|
123
123
|
crypto,
|
124
124
|
storage,
|
@@ -128,7 +128,7 @@ export async function createJazzReactNativeGuestContext(
|
|
128
128
|
|
129
129
|
const context = createAnonymousJazzContext({
|
130
130
|
crypto,
|
131
|
-
|
131
|
+
peers,
|
132
132
|
storage,
|
133
133
|
});
|
134
134
|
|
@@ -170,7 +170,7 @@ export async function createJazzReactNativeContext<
|
|
170
170
|
>(options: ReactNativeContextOptions<S>) {
|
171
171
|
const {
|
172
172
|
toggleNetwork,
|
173
|
-
|
173
|
+
peers,
|
174
174
|
setNode,
|
175
175
|
crypto,
|
176
176
|
storage,
|
@@ -202,7 +202,7 @@ export async function createJazzReactNativeContext<
|
|
202
202
|
const context = await createJazzContext({
|
203
203
|
credentials: options.credentials,
|
204
204
|
newAccountProps: options.newAccountProps,
|
205
|
-
|
205
|
+
peers,
|
206
206
|
crypto,
|
207
207
|
defaultProfileName: options.defaultProfileName,
|
208
208
|
AccountSchema: options.AccountSchema,
|
@@ -20,6 +20,7 @@ export type JazzProviderProps<
|
|
20
20
|
> = {
|
21
21
|
children: React.ReactNode;
|
22
22
|
kvStore?: KvStore;
|
23
|
+
authSecretStorageKey?: string;
|
23
24
|
} & JazzContextManagerProps<S>;
|
24
25
|
|
25
26
|
/** @category Context & Hooks */
|
@@ -39,11 +40,12 @@ export function JazzProviderCore<
|
|
39
40
|
onAnonymousAccountDiscarded,
|
40
41
|
kvStore,
|
41
42
|
CryptoProvider,
|
43
|
+
authSecretStorageKey,
|
42
44
|
}: JazzProviderProps<S>) {
|
43
45
|
setupKvStore(kvStore);
|
44
46
|
|
45
47
|
const [contextManager] = React.useState(
|
46
|
-
() => new ReactNativeContextManager<S>(),
|
48
|
+
() => new ReactNativeContextManager<S>({ authSecretStorageKey }),
|
47
49
|
);
|
48
50
|
|
49
51
|
const onLogOutRefCallback = useRefCallback(onLogOut);
|