@@ -2,17 +2,32 @@ import React from 'react';
22import NDKSessionContext from '../../context/session' ;
33import { NDKEventWithFrom } from '../../hooks' ;
44import { useNDK } from '../../hooks/ndk' ;
5- import { NDKEvent , NDKEventId , NDKFilter , NDKKind , NDKRelay , NDKSubscription , NDKSubscriptionCacheUsage } from '@nostr-dev-kit/ndk' ;
5+ import NDK , { NDKEvent , NDKEventId , NDKFilter , NDKKind , NDKRelay , NDKSubscription , NDKSubscriptionCacheUsage , NostrEvent } from '@nostr-dev-kit/ndk' ;
66import { PropsWithChildren , useEffect } from 'react' ;
77import { useSessionStore } from '../../stores/session' ;
88import { NDKCashuWallet , NDKNutzapMonitor , NDKNWCWallet , NDKWallet , NDKWalletTypes } from '@nostr-dev-kit/ndk-wallet' ;
99import { useWalletStore } from '../../stores/wallet' ;
1010
11+ type SettingsStore = {
12+ get : ( key : string ) => Promise < string | null > ;
13+ set : ( key : string , value : string ) => Promise < void > ;
14+ delete : ( key : string ) => Promise < void > ;
15+ }
16+
17+ /**
18+ * Options for the NDKSessionProvider
19+ *
20+ * @param follows - Whether to subscribe to follow events
21+ * @param muteList - Whether to subscribe to mute list events
22+ * @param wallet - Whether to subscribe to wallet events
23+ * @param settingsStore - A store for storing and retrieving configuration values
24+ * @param kinds - A map of kinds to wrap with a custom wrapper
25+ */
1126interface NDKSessionProviderProps {
1227 follows ?: boolean ;
1328 muteList ?: boolean ;
1429 wallet ?: boolean
15- walletConfig ?: { type : NDKWalletTypes , pairingCode : string } ;
30+ settingsStore ?: SettingsStore ;
1631 kinds ?: Map < NDKKind , { wrapper ?: NDKEventWithFrom < any > } > ;
1732}
1833
@@ -63,7 +78,15 @@ const NDKSessionProvider = ({ children, ...opts }: PropsWithChildren<NDKSessionP
6378 }
6479 } ;
6580
66- const setActiveWallet = ( wallet : NDKWallet ) => {
81+ /**
82+ * Set the active wallet
83+ * @param wallet - The wallet to set
84+ * @param save - Whether to store this setting locally
85+ */
86+ const setActiveWallet = (
87+ wallet : NDKWallet ,
88+ save = true
89+ ) => {
6790 ndk . wallet = wallet ;
6891 if ( wallet instanceof NDKCashuWallet ) {
6992 setBalances ( wallet . balance ( ) ) ;
@@ -104,6 +127,10 @@ const NDKSessionProvider = ({ children, ...opts }: PropsWithChildren<NDKSessionP
104127 else {
105128 setBalances ( [ ] ) ;
106129 }
130+
131+ if ( save && opts . settingsStore ) {
132+ persistWalletConfiguration ( wallet , opts . settingsStore ) ;
133+ }
107134 }
108135
109136 useEffect ( ( ) => {
@@ -121,21 +148,16 @@ const NDKSessionProvider = ({ children, ...opts }: PropsWithChildren<NDKSessionP
121148 if ( opts . wallet ) filters [ 0 ] . kinds ! . push ( NDKKind . CashuWallet ) ;
122149 if ( opts . kinds ) filters [ 0 ] . kinds ! . push ( ...opts . kinds . keys ( ) ) ;
123150
124- if ( opts . walletConfig ) {
125- if ( opts . walletConfig . type === 'nwc' ) {
126- const wallet = new NDKNWCWallet ( ndk ) ;
127- wallet . initWithPairingCode ( opts . walletConfig . pairingCode ) . then ( ( ) => {
128- setActiveWallet ( wallet ) ;
129- } ) ;
130- }
151+ if ( opts . settingsStore ) {
152+ loadWallet ( ndk , opts . settingsStore , ( wallet ) => setActiveWallet ( wallet , false ) ) ;
131153 }
132154
133155 if ( filters [ 0 ] . kinds ! . length > 0 ) {
134156 sub = ndk . subscribe ( filters , { closeOnEose : false } , undefined , false ) ;
135157 sub . on ( 'event' , handleEvent ) ;
136158 sub . start ( ) ;
137159 }
138- } , [ ndk , opts . follows , opts . muteList , opts . walletConfig , currentUser ] ) ;
160+ } , [ ndk , opts . follows , opts . muteList , opts . settingsStore , currentUser ] ) ;
139161
140162 return (
141163 < NDKSessionContext . Provider
@@ -153,4 +175,91 @@ const NDKSessionProvider = ({ children, ...opts }: PropsWithChildren<NDKSessionP
153175 ) ;
154176} ;
155177
178+ function walletPayload ( wallet : NDKWallet ) {
179+ if ( wallet instanceof NDKNWCWallet ) {
180+ return wallet . pairingCode ;
181+ } else if ( wallet instanceof NDKCashuWallet ) {
182+ return wallet . event . rawEvent ( ) ;
183+ }
184+ }
185+
186+ /**
187+ * Persist the wallet configuration
188+ * @param wallet - The wallet to persist
189+ * @param settingsStore - The settings store to use
190+ */
191+ function persistWalletConfiguration ( wallet : NDKWallet , settingsStore : SettingsStore ) {
192+ if ( ! wallet ) {
193+ settingsStore . delete ( 'wallet' ) ;
194+ return ;
195+ }
196+
197+ const payload = walletPayload ( wallet ) ;
198+ if ( ! payload ) {
199+ alert ( 'Failed to persist wallet configuration!' ) ;
200+ return ;
201+ }
202+
203+ const type = wallet . type ;
204+ settingsStore . set ( 'wallet' , JSON . stringify ( { type, payload } ) ) ;
205+ }
206+
207+ async function loadWallet ( ndk : NDK , settingsStore : SettingsStore , setActiveWallet : ( wallet : NDKWallet | null ) => void ) {
208+ const walletConfig = await settingsStore . get ( 'wallet' ) ;
209+ if ( ! walletConfig ) return ;
210+
211+ const loadNWCWallet = ( pairingCode : string ) => {
212+ const wallet = new NDKNWCWallet ( ndk ) ;
213+ wallet . initWithPairingCode ( pairingCode ) . then ( ( ) => {
214+ setActiveWallet ( wallet ) ;
215+ } ) ;
216+ }
217+
218+ const loadNIP60Wallet = async ( payload : NostrEvent ) => {
219+ try {
220+ // Load the cached event
221+ const event = new NDKEvent ( ndk , payload ) ;
222+ const wallet = await NDKCashuWallet . from ( event ) ;
223+ setActiveWallet ( wallet ) ;
224+
225+ const relaySet = wallet . relaySet ;
226+
227+ // Load remotely
228+ const freshEvent = await ndk . fetchEvent ( event . encode ( ) , { cacheUsage : NDKSubscriptionCacheUsage . ONLY_RELAY } , relaySet ) ;
229+ if ( freshEvent . hasTag ( 'deleted' ) ) {
230+ alert ( 'This wallet has been deleted' ) ;
231+ setActiveWallet ( null ) ;
232+ return null ;
233+ } else if ( freshEvent . created_at ! > event . created_at ! ) {
234+ const wallet = await NDKCashuWallet . from ( freshEvent ) ;
235+ alert ( 'This wallet has been updated' ) ;
236+ setActiveWallet ( wallet ) ;
237+
238+ // update the cache
239+ persistWalletConfiguration ( wallet , settingsStore ) ;
240+
241+ return wallet ;
242+ }
243+
244+ return wallet ;
245+ } catch ( e ) {
246+ console . error ( 'Error activating wallet' , e ) ;
247+ }
248+ }
249+
250+ try {
251+ const { type, payload } = JSON . parse ( walletConfig ) ;
252+ if ( type === 'nwc' ) {
253+ loadNWCWallet ( payload ) ;
254+ } else if ( type === 'nip-60' ) {
255+ loadNIP60Wallet ( payload ) ;
256+ } else {
257+ alert ( 'Unknown wallet type: ' + type ) ;
258+ }
259+ } catch ( e ) {
260+ alert ( 'Failed to load wallet configuration' ) ;
261+ settingsStore . delete ( 'wallet' ) ;
262+ }
263+ }
264+
156265export { NDKSessionProvider } ;
0 commit comments