jazz-react-native 0.8.14 → 0.8.16

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/src/provider.tsx CHANGED
@@ -1,307 +1,298 @@
1
1
  import React, { useEffect, useState } from "react";
2
2
 
3
3
  import {
4
- Account,
5
- AccountClass,
6
- AnonymousJazzAgent,
7
- AuthMethod,
8
- CoValue,
9
- CoValueClass,
10
- DeeplyLoaded,
11
- DepthsIn,
12
- ID,
13
- subscribeToCoValue,
4
+ Account,
5
+ AccountClass,
6
+ AnonymousJazzAgent,
7
+ AuthMethod,
8
+ CoValue,
9
+ CoValueClass,
10
+ DeeplyLoaded,
11
+ DepthsIn,
12
+ ID,
13
+ subscribeToCoValue,
14
14
  } from "jazz-tools";
15
+ import { Linking } from "react-native";
15
16
  import {
16
- BrowserContext,
17
- BrowserGuestContext,
18
- createJazzRNContext,
19
- KvStoreContext,
20
- parseInviteLink,
17
+ BrowserContext,
18
+ BrowserGuestContext,
19
+ KvStoreContext,
20
+ createJazzRNContext,
21
+ parseInviteLink,
21
22
  } from "./index.js";
22
- import { Linking } from "react-native";
23
23
  import { ExpoSecureStoreAdapter } from "./storage/expo-secure-store-adapter.js";
24
24
 
25
25
  /** @category Context & Hooks */
26
26
  export function createJazzRNApp<Acc extends Account>({
27
- kvStore = new ExpoSecureStoreAdapter(),
28
- AccountSchema = Account as unknown as AccountClass<Acc>,
27
+ kvStore = new ExpoSecureStoreAdapter(),
28
+ AccountSchema = Account as unknown as AccountClass<Acc>,
29
29
  } = {}): JazzReactApp<Acc> {
30
- const JazzContext = React.createContext<
31
- BrowserContext<Acc> | BrowserGuestContext | undefined
32
- >(undefined);
33
-
34
- if (!kvStore) {
35
- throw new Error("kvStore is required");
30
+ const JazzContext = React.createContext<
31
+ BrowserContext<Acc> | BrowserGuestContext | undefined
32
+ >(undefined);
33
+
34
+ if (!kvStore) {
35
+ throw new Error("kvStore is required");
36
+ }
37
+
38
+ KvStoreContext.getInstance().initialize(kvStore);
39
+
40
+ function Provider({
41
+ children,
42
+ auth,
43
+ peer,
44
+ storage,
45
+ }: {
46
+ children: React.ReactNode;
47
+ auth: AuthMethod | "guest";
48
+ peer: `wss://${string}` | `ws://${string}`;
49
+ storage?: "indexedDB" | "singleTabOPFS";
50
+ }) {
51
+ const [ctx, setCtx] = useState<
52
+ BrowserContext<Acc> | BrowserGuestContext | undefined
53
+ >();
54
+
55
+ const [sessionCount, setSessionCount] = useState(0);
56
+
57
+ useEffect(() => {
58
+ const promiseWithDoneCallback = createJazzRNContext<Acc>(
59
+ auth === "guest"
60
+ ? {
61
+ peer,
62
+ storage,
63
+ }
64
+ : {
65
+ AccountSchema,
66
+ auth: auth,
67
+ peer,
68
+ storage,
69
+ },
70
+ ).then((context) => {
71
+ setCtx({
72
+ ...context,
73
+ logOut: () => {
74
+ context.logOut();
75
+ setCtx(undefined);
76
+ setSessionCount(sessionCount + 1);
77
+ },
78
+ });
79
+ return context.done;
80
+ });
81
+
82
+ return () => {
83
+ void promiseWithDoneCallback.then((done) => done());
84
+ };
85
+ }, [AccountSchema, auth, peer, storage, sessionCount]);
86
+
87
+ return (
88
+ <JazzContext.Provider value={ctx}>{ctx && children}</JazzContext.Provider>
89
+ );
90
+ }
91
+
92
+ function useAccount(): { me: Acc; logOut: () => void };
93
+ function useAccount<D extends DepthsIn<Acc>>(
94
+ depth: D,
95
+ ): { me: DeeplyLoaded<Acc, D> | undefined; logOut: () => void };
96
+ function useAccount<D extends DepthsIn<Acc>>(
97
+ depth?: D,
98
+ ): { me: Acc | DeeplyLoaded<Acc, D> | undefined; logOut: () => void } {
99
+ const context = React.useContext(JazzContext);
100
+
101
+ if (!context) {
102
+ throw new Error("useAccount must be used within a JazzProvider");
36
103
  }
37
104
 
38
- KvStoreContext.getInstance().initialize(kvStore);
39
-
40
- function Provider({
41
- children,
42
- auth,
43
- peer,
44
- storage,
45
- }: {
46
- children: React.ReactNode;
47
- auth: AuthMethod | "guest";
48
- peer: `wss://${string}` | `ws://${string}`;
49
- storage?: "indexedDB" | "singleTabOPFS";
50
- }) {
51
- const [ctx, setCtx] = useState<
52
- BrowserContext<Acc> | BrowserGuestContext | undefined
53
- >();
54
-
55
- const [sessionCount, setSessionCount] = useState(0);
56
-
57
- useEffect(() => {
58
- const promiseWithDoneCallback = createJazzRNContext<Acc>(
59
- auth === "guest"
60
- ? {
61
- peer,
62
- storage,
63
- }
64
- : {
65
- AccountSchema,
66
- auth: auth,
67
- peer,
68
- storage,
69
- },
70
- ).then((context) => {
71
- setCtx({
72
- ...context,
73
- logOut: () => {
74
- context.logOut();
75
- setCtx(undefined);
76
- setSessionCount(sessionCount + 1);
77
- },
78
- });
79
- return context.done;
80
- });
81
-
82
- return () => {
83
- void promiseWithDoneCallback.then((done) => done());
84
- };
85
- }, [AccountSchema, auth, peer, storage, sessionCount]);
86
-
87
- return (
88
- <JazzContext.Provider value={ctx}>
89
- {ctx && children}
90
- </JazzContext.Provider>
91
- );
105
+ if (!("me" in context)) {
106
+ throw new Error(
107
+ "useAccount can't be used in a JazzProvider with auth === 'guest' - consider using useAccountOrGuest()",
108
+ );
92
109
  }
93
110
 
94
- function useAccount(): { me: Acc; logOut: () => void };
95
- function useAccount<D extends DepthsIn<Acc>>(
96
- depth: D,
97
- ): { me: DeeplyLoaded<Acc, D> | undefined; logOut: () => void };
98
- function useAccount<D extends DepthsIn<Acc>>(
99
- depth?: D,
100
- ): { me: Acc | DeeplyLoaded<Acc, D> | undefined; logOut: () => void } {
101
- const context = React.useContext(JazzContext);
102
-
103
- if (!context) {
104
- throw new Error("useAccount must be used within a JazzProvider");
105
- }
106
-
107
- if (!("me" in context)) {
108
- throw new Error(
109
- "useAccount can't be used in a JazzProvider with auth === 'guest' - consider using useAccountOrGuest()",
110
- );
111
- }
111
+ const me = useCoState<Acc, D>(
112
+ context?.me.constructor as CoValueClass<Acc>,
113
+ context?.me.id,
114
+ depth,
115
+ );
112
116
 
113
- const me = useCoState<Acc, D>(
114
- context?.me.constructor as CoValueClass<Acc>,
115
- context?.me.id,
116
- depth,
117
- );
118
-
119
- return {
120
- me: depth === undefined ? me || context.me : me,
121
- logOut: context.logOut,
122
- };
117
+ return {
118
+ me: depth === undefined ? me || context.me : me,
119
+ logOut: context.logOut,
120
+ };
121
+ }
122
+
123
+ function useAccountOrGuest(): { me: Acc | AnonymousJazzAgent };
124
+ function useAccountOrGuest<D extends DepthsIn<Acc>>(
125
+ depth: D,
126
+ ): { me: DeeplyLoaded<Acc, D> | undefined | AnonymousJazzAgent };
127
+ function useAccountOrGuest<D extends DepthsIn<Acc>>(
128
+ depth?: D,
129
+ ): { me: Acc | DeeplyLoaded<Acc, D> | undefined | AnonymousJazzAgent } {
130
+ const context = React.useContext(JazzContext);
131
+
132
+ if (!context) {
133
+ throw new Error("useAccountOrGuest must be used within a JazzProvider");
123
134
  }
124
135
 
125
- function useAccountOrGuest(): { me: Acc | AnonymousJazzAgent };
126
- function useAccountOrGuest<D extends DepthsIn<Acc>>(
127
- depth: D,
128
- ): { me: DeeplyLoaded<Acc, D> | undefined | AnonymousJazzAgent };
129
- function useAccountOrGuest<D extends DepthsIn<Acc>>(
130
- depth?: D,
131
- ): { me: Acc | DeeplyLoaded<Acc, D> | undefined | AnonymousJazzAgent } {
132
- const context = React.useContext(JazzContext);
133
-
134
- if (!context) {
135
- throw new Error(
136
- "useAccountOrGuest must be used within a JazzProvider",
137
- );
138
- }
139
-
140
- const contextMe = "me" in context ? context.me : undefined;
136
+ const contextMe = "me" in context ? context.me : undefined;
141
137
 
142
- const me = useCoState<Acc, D>(
143
- contextMe?.constructor as CoValueClass<Acc>,
144
- contextMe?.id,
145
- depth,
146
- );
138
+ const me = useCoState<Acc, D>(
139
+ contextMe?.constructor as CoValueClass<Acc>,
140
+ contextMe?.id,
141
+ depth,
142
+ );
147
143
 
148
- if ("me" in context) {
149
- return {
150
- me: depth === undefined ? me || context.me : me,
151
- };
152
- } else {
153
- return { me: context.guest };
154
- }
144
+ if ("me" in context) {
145
+ return {
146
+ me: depth === undefined ? me || context.me : me,
147
+ };
148
+ } else {
149
+ return { me: context.guest };
155
150
  }
156
-
157
- function useCoState<V extends CoValue, D>(
158
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
159
- Schema: CoValueClass<V>,
160
- id: ID<V> | undefined,
161
- depth: D & DepthsIn<V> = [] as D & DepthsIn<V>,
162
- ): DeeplyLoaded<V, D> | undefined {
163
- const [state, setState] = useState<{
164
- value: DeeplyLoaded<V, D> | undefined;
165
- }>({ value: undefined });
166
- const context = React.useContext(JazzContext);
167
-
168
- if (!context) {
169
- throw new Error("useCoState must be used within a JazzProvider");
170
- }
171
-
172
- useEffect(() => {
173
- if (!id) return;
174
-
175
- return subscribeToCoValue(
176
- Schema,
177
- id,
178
- "me" in context ? context.me : context.guest,
179
- depth,
180
- (value) => {
181
- setState({ value });
182
- },
183
- );
184
- }, [Schema, id, context]);
185
-
186
- return state.value;
151
+ }
152
+
153
+ function useCoState<V extends CoValue, D>(
154
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
155
+ Schema: CoValueClass<V>,
156
+ id: ID<V> | undefined,
157
+ depth: D & DepthsIn<V> = [] as D & DepthsIn<V>,
158
+ ): DeeplyLoaded<V, D> | undefined {
159
+ const [state, setState] = useState<{
160
+ value: DeeplyLoaded<V, D> | undefined;
161
+ }>({ value: undefined });
162
+ const context = React.useContext(JazzContext);
163
+
164
+ if (!context) {
165
+ throw new Error("useCoState must be used within a JazzProvider");
187
166
  }
188
167
 
189
- function useAcceptInvite<V extends CoValue>({
190
- invitedObjectSchema,
191
- onAccept,
192
- forValueHint,
193
- }: {
194
- invitedObjectSchema: CoValueClass<V>;
195
- onAccept: (projectID: ID<V>) => void;
196
- forValueHint?: string;
197
- }): void {
198
- const context = React.useContext(JazzContext);
199
-
200
- if (!context) {
201
- throw new Error(
202
- "useAcceptInvite must be used within a JazzProvider",
203
- );
204
- }
205
-
206
- if (!("me" in context)) {
207
- throw new Error(
208
- "useAcceptInvite can't be used in a JazzProvider with auth === 'guest'.",
209
- );
210
- }
211
-
212
- useEffect(() => {
213
- const handleDeepLink = ({ url }: { url: string }) => {
214
- const result = parseInviteLink<V>(url);
215
- if (result && result.valueHint === forValueHint) {
216
- context.me
217
- .acceptInvite(
218
- result.valueID,
219
- result.inviteSecret,
220
- invitedObjectSchema,
221
- )
222
- .then(() => {
223
- onAccept(result.valueID);
224
- })
225
- .catch((e) => {
226
- console.error("Failed to accept invite", e);
227
- });
228
- }
229
- };
230
-
231
- const linkingListener = Linking.addEventListener(
232
- "url",
233
- handleDeepLink,
234
- );
235
-
236
- void Linking.getInitialURL().then((url) => {
237
- if (url) handleDeepLink({ url });
238
- });
168
+ useEffect(() => {
169
+ if (!id) return;
170
+
171
+ return subscribeToCoValue(
172
+ Schema,
173
+ id,
174
+ "me" in context ? context.me : context.guest,
175
+ depth,
176
+ (value) => {
177
+ setState({ value });
178
+ },
179
+ );
180
+ }, [Schema, id, context]);
181
+
182
+ return state.value;
183
+ }
184
+
185
+ function useAcceptInvite<V extends CoValue>({
186
+ invitedObjectSchema,
187
+ onAccept,
188
+ forValueHint,
189
+ }: {
190
+ invitedObjectSchema: CoValueClass<V>;
191
+ onAccept: (projectID: ID<V>) => void;
192
+ forValueHint?: string;
193
+ }): void {
194
+ const context = React.useContext(JazzContext);
195
+
196
+ if (!context) {
197
+ throw new Error("useAcceptInvite must be used within a JazzProvider");
198
+ }
239
199
 
240
- return () => {
241
- linkingListener.remove();
242
- };
243
- }, [context, onAccept, invitedObjectSchema, forValueHint]);
200
+ if (!("me" in context)) {
201
+ throw new Error(
202
+ "useAcceptInvite can't be used in a JazzProvider with auth === 'guest'.",
203
+ );
244
204
  }
245
205
 
246
- return {
247
- Provider,
248
- useAccount,
249
- useAccountOrGuest,
250
- useCoState,
251
- useAcceptInvite,
252
- };
206
+ useEffect(() => {
207
+ const handleDeepLink = ({ url }: { url: string }) => {
208
+ const result = parseInviteLink<V>(url);
209
+ if (result && result.valueHint === forValueHint) {
210
+ context.me
211
+ .acceptInvite(
212
+ result.valueID,
213
+ result.inviteSecret,
214
+ invitedObjectSchema,
215
+ )
216
+ .then(() => {
217
+ onAccept(result.valueID);
218
+ })
219
+ .catch((e) => {
220
+ console.error("Failed to accept invite", e);
221
+ });
222
+ }
223
+ };
224
+
225
+ const linkingListener = Linking.addEventListener("url", handleDeepLink);
226
+
227
+ void Linking.getInitialURL().then((url) => {
228
+ if (url) handleDeepLink({ url });
229
+ });
230
+
231
+ return () => {
232
+ linkingListener.remove();
233
+ };
234
+ }, [context, onAccept, invitedObjectSchema, forValueHint]);
235
+ }
236
+
237
+ return {
238
+ Provider,
239
+ useAccount,
240
+ useAccountOrGuest,
241
+ useCoState,
242
+ useAcceptInvite,
243
+ };
253
244
  }
254
245
 
255
246
  /** @category Context & Hooks */
256
247
  export interface JazzReactApp<Acc extends Account> {
257
- /** @category Provider Component */
258
- Provider: React.FC<{
259
- children: React.ReactNode;
260
- auth: AuthMethod | "guest";
261
- peer: `wss://${string}` | `ws://${string}`;
262
- storage?: "indexedDB" | "singleTabOPFS";
263
- }>;
264
-
265
- /** @category Hooks */
266
- useAccount(): {
267
- me: Acc;
268
- logOut: () => void;
269
- };
270
- /** @category Hooks */
271
- useAccount<D extends DepthsIn<Acc>>(
272
- depth: D,
273
- ): {
274
- me: DeeplyLoaded<Acc, D> | undefined;
275
- logOut: () => void;
276
- };
277
-
278
- /** @category Hooks */
279
- useAccountOrGuest(): {
280
- me: Acc | AnonymousJazzAgent;
281
- };
282
- useAccountOrGuest<D extends DepthsIn<Acc>>(
283
- depth: D,
284
- ): {
285
- me: DeeplyLoaded<Acc, D> | undefined | AnonymousJazzAgent;
286
- };
287
- /** @category Hooks */
288
- useCoState<V extends CoValue, D>(
289
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
290
- Schema: { new (...args: any[]): V } & CoValueClass,
291
- id: ID<V> | undefined,
292
- depth?: D & DepthsIn<V>,
293
- ): DeeplyLoaded<V, D> | undefined;
294
-
295
- /** @category Hooks */
296
- useAcceptInvite<V extends CoValue>({
297
- invitedObjectSchema,
298
- onAccept,
299
- forValueHint,
300
- }: {
301
- invitedObjectSchema: CoValueClass<V>;
302
- onAccept: (projectID: ID<V>) => void;
303
- forValueHint?: string;
304
- }): void;
248
+ /** @category Provider Component */
249
+ Provider: React.FC<{
250
+ children: React.ReactNode;
251
+ auth: AuthMethod | "guest";
252
+ peer: `wss://${string}` | `ws://${string}`;
253
+ storage?: "indexedDB" | "singleTabOPFS";
254
+ }>;
255
+
256
+ /** @category Hooks */
257
+ useAccount(): {
258
+ me: Acc;
259
+ logOut: () => void;
260
+ };
261
+ /** @category Hooks */
262
+ useAccount<D extends DepthsIn<Acc>>(
263
+ depth: D,
264
+ ): {
265
+ me: DeeplyLoaded<Acc, D> | undefined;
266
+ logOut: () => void;
267
+ };
268
+
269
+ /** @category Hooks */
270
+ useAccountOrGuest(): {
271
+ me: Acc | AnonymousJazzAgent;
272
+ };
273
+ useAccountOrGuest<D extends DepthsIn<Acc>>(
274
+ depth: D,
275
+ ): {
276
+ me: DeeplyLoaded<Acc, D> | undefined | AnonymousJazzAgent;
277
+ };
278
+ /** @category Hooks */
279
+ useCoState<V extends CoValue, D>(
280
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
281
+ Schema: { new (...args: any[]): V } & CoValueClass,
282
+ id: ID<V> | undefined,
283
+ depth?: D & DepthsIn<V>,
284
+ ): DeeplyLoaded<V, D> | undefined;
285
+
286
+ /** @category Hooks */
287
+ useAcceptInvite<V extends CoValue>({
288
+ invitedObjectSchema,
289
+ onAccept,
290
+ forValueHint,
291
+ }: {
292
+ invitedObjectSchema: CoValueClass<V>;
293
+ onAccept: (projectID: ID<V>) => void;
294
+ forValueHint?: string;
295
+ }): void;
305
296
  }
306
297
 
307
298
  export * from "./media.js";
@@ -2,28 +2,28 @@ import * as SecureStore from "expo-secure-store";
2
2
  import type { KvStore } from "./kv-store-context.js";
3
3
 
4
4
  export class ExpoSecureStoreAdapter implements KvStore {
5
- get(key: string): Promise<string | null> {
6
- return SecureStore.getItemAsync(key, {
7
- requireAuthentication: SecureStore.canUseBiometricAuthentication(),
8
- keychainAccessible: SecureStore.AFTER_FIRST_UNLOCK,
9
- });
10
- }
5
+ get(key: string): Promise<string | null> {
6
+ return SecureStore.getItemAsync(key, {
7
+ requireAuthentication: SecureStore.canUseBiometricAuthentication(),
8
+ keychainAccessible: SecureStore.AFTER_FIRST_UNLOCK,
9
+ });
10
+ }
11
11
 
12
- async set(key: string, value: string): Promise<void> {
13
- return SecureStore.setItemAsync(key, value, {
14
- requireAuthentication: SecureStore.canUseBiometricAuthentication(),
15
- keychainAccessible: SecureStore.AFTER_FIRST_UNLOCK,
16
- });
17
- }
12
+ async set(key: string, value: string): Promise<void> {
13
+ return SecureStore.setItemAsync(key, value, {
14
+ requireAuthentication: SecureStore.canUseBiometricAuthentication(),
15
+ keychainAccessible: SecureStore.AFTER_FIRST_UNLOCK,
16
+ });
17
+ }
18
18
 
19
- async delete(key: string): Promise<void> {
20
- return SecureStore.deleteItemAsync(key, {
21
- requireAuthentication: SecureStore.canUseBiometricAuthentication(),
22
- keychainAccessible: SecureStore.AFTER_FIRST_UNLOCK,
23
- });
24
- }
19
+ async delete(key: string): Promise<void> {
20
+ return SecureStore.deleteItemAsync(key, {
21
+ requireAuthentication: SecureStore.canUseBiometricAuthentication(),
22
+ keychainAccessible: SecureStore.AFTER_FIRST_UNLOCK,
23
+ });
24
+ }
25
25
 
26
- async clearAll(): Promise<void> {
27
- throw new Error("Not implemented");
28
- }
26
+ async clearAll(): Promise<void> {
27
+ throw new Error("Not implemented");
28
+ }
29
29
  }
@@ -1,35 +1,35 @@
1
1
  export interface KvStore {
2
- get(key: string): Promise<string | null>;
3
- set(key: string, value: string): Promise<void>;
4
- delete(key: string): Promise<void>;
5
- clearAll(): Promise<void>;
2
+ get(key: string): Promise<string | null>;
3
+ set(key: string, value: string): Promise<void>;
4
+ delete(key: string): Promise<void>;
5
+ clearAll(): Promise<void>;
6
6
  }
7
7
 
8
8
  export class KvStoreContext {
9
- private static instance: KvStoreContext;
10
- private storageInstance: KvStore | null = null;
9
+ private static instance: KvStoreContext;
10
+ private storageInstance: KvStore | null = null;
11
11
 
12
- private constructor() {}
12
+ private constructor() {}
13
13
 
14
- public static getInstance(): KvStoreContext {
15
- if (!KvStoreContext.instance) {
16
- KvStoreContext.instance = new KvStoreContext();
17
- }
18
- return KvStoreContext.instance;
14
+ public static getInstance(): KvStoreContext {
15
+ if (!KvStoreContext.instance) {
16
+ KvStoreContext.instance = new KvStoreContext();
19
17
  }
18
+ return KvStoreContext.instance;
19
+ }
20
20
 
21
- public initialize(store: KvStore): void {
22
- if (!this.storageInstance) {
23
- this.storageInstance = store;
24
- }
21
+ public initialize(store: KvStore): void {
22
+ if (!this.storageInstance) {
23
+ this.storageInstance = store;
25
24
  }
25
+ }
26
26
 
27
- public getStorage(): KvStore {
28
- if (!this.storageInstance) {
29
- throw new Error("Storage instance is not initialized.");
30
- }
31
- return this.storageInstance;
27
+ public getStorage(): KvStore {
28
+ if (!this.storageInstance) {
29
+ throw new Error("Storage instance is not initialized.");
32
30
  }
31
+ return this.storageInstance;
32
+ }
33
33
  }
34
34
 
35
35
  export default KvStoreContext;