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