1
- import type { Client } from "@xmtp/browser-sdk" ;
2
- import { createContext , useMemo , useState } from "react" ;
1
+ import { Client , type ClientOptions , type Signer } from "@xmtp/browser-sdk" ;
2
+ import { ReactionCodec } from "@xmtp/content-type-reaction" ;
3
+ import { RemoteAttachmentCodec } from "@xmtp/content-type-remote-attachment" ;
4
+ import { ReplyCodec } from "@xmtp/content-type-reply" ;
5
+ import {
6
+ createContext ,
7
+ useCallback ,
8
+ useContext ,
9
+ useMemo ,
10
+ useRef ,
11
+ useState ,
12
+ } from "react" ;
13
+
14
+ export type InitializeClientOptions = {
15
+ encryptionKey : Uint8Array ;
16
+ env ?: ClientOptions [ "env" ] ;
17
+ loggingLevel ?: ClientOptions [ "loggingLevel" ] ;
18
+ signer : Signer ;
19
+ } ;
3
20
4
21
export type XMTPContextValue = {
5
22
/**
@@ -10,15 +27,18 @@ export type XMTPContextValue = {
10
27
* Set the XMTP client instance
11
28
*/
12
29
setClient : React . Dispatch < React . SetStateAction < Client | undefined > > ;
13
- /**
14
- * Set whether to show confetti
15
- */
16
- setConfetti : React . Dispatch < React . SetStateAction < boolean > > ;
30
+ initialize : ( options : InitializeClientOptions ) => Promise < Client | undefined > ;
31
+ initializing : boolean ;
32
+ error : Error | null ;
33
+ disconnect : ( ) => void ;
17
34
} ;
18
35
19
36
export const XMTPContext = createContext < XMTPContextValue > ( {
20
37
setClient : ( ) => { } ,
21
- setConfetti : ( ) => { } ,
38
+ initialize : ( ) => Promise . reject ( new Error ( "XMTPProvider not available" ) ) ,
39
+ initializing : false ,
40
+ error : null ,
41
+ disconnect : ( ) => { } ,
22
42
} ) ;
23
43
24
44
export type XMTPProviderProps = React . PropsWithChildren & {
@@ -33,18 +53,91 @@ export const XMTPProvider: React.FC<XMTPProviderProps> = ({
33
53
client : initialClient ,
34
54
} ) => {
35
55
const [ client , setClient ] = useState < Client | undefined > ( initialClient ) ;
36
- const [ confetti , setConfetti ] = useState ( false ) ;
56
+
57
+ const [ initializing , setInitializing ] = useState ( false ) ;
58
+ const [ error , setError ] = useState < Error | null > ( null ) ;
59
+ // client is initializing
60
+ const initializingRef = useRef ( false ) ;
61
+
62
+ /**
63
+ * Initialize an XMTP client
64
+ */
65
+ const initialize = useCallback (
66
+ async ( {
67
+ encryptionKey,
68
+ env,
69
+ loggingLevel,
70
+ signer,
71
+ } : InitializeClientOptions ) => {
72
+ // only initialize a client if one doesn't already exist
73
+ if ( ! client ) {
74
+ // if the client is already initializing, don't do anything
75
+ if ( initializingRef . current ) {
76
+ return undefined ;
77
+ }
78
+
79
+ // flag the client as initializing
80
+ initializingRef . current = true ;
81
+
82
+ // reset error state
83
+ setError ( null ) ;
84
+ // reset initializing state
85
+ setInitializing ( true ) ;
86
+
87
+ let xmtpClient : Client ;
88
+
89
+ try {
90
+ // create a new XMTP client
91
+ xmtpClient = await Client . create ( signer , encryptionKey , {
92
+ env,
93
+ loggingLevel,
94
+ codecs : [
95
+ new ReactionCodec ( ) ,
96
+ new ReplyCodec ( ) ,
97
+ new RemoteAttachmentCodec ( ) ,
98
+ ] ,
99
+ } ) ;
100
+ setClient ( xmtpClient ) ;
101
+ } catch ( e ) {
102
+ setClient ( undefined ) ;
103
+ setError ( e as Error ) ;
104
+ // re-throw error for upstream consumption
105
+ throw e ;
106
+ } finally {
107
+ initializingRef . current = false ;
108
+ setInitializing ( false ) ;
109
+ }
110
+
111
+ return xmtpClient ;
112
+ }
113
+ return client ;
114
+ } ,
115
+ [ client ] ,
116
+ ) ;
117
+
118
+ const disconnect = useCallback ( ( ) => {
119
+ if ( client ) {
120
+ client . close ( ) ;
121
+ setClient ( undefined ) ;
122
+ }
123
+ } , [ client , setClient ] ) ;
37
124
38
125
// memo-ize the context value to prevent unnecessary re-renders
39
126
const value = useMemo (
40
127
( ) => ( {
41
128
client,
42
129
setClient,
43
- confetti,
44
- setConfetti,
130
+ initialize,
131
+ initializing,
132
+ error,
133
+ disconnect,
45
134
} ) ,
46
- [ client , confetti ] ,
135
+ [ client , initialize , initializing , error , disconnect ] ,
47
136
) ;
48
137
49
138
return < XMTPContext . Provider value = { value } > { children } </ XMTPContext . Provider > ;
50
139
} ;
140
+
141
+ export const useXMTP = ( ) => {
142
+ return useContext ( XMTPContext ) ;
143
+ } ;
0 commit comments