@@ -12,82 +12,97 @@ export function useInventoryKey() {
1212 const { toast } = useToast ( ) ;
1313 const queryClient = useQueryClient ( ) ;
1414
15- // Query: Get the keychain event (either mine or one shared with me)
16- const { data : keychainEvent , isLoading : isLoadingKeychain } = useQuery ( {
17- queryKey : [ 'inventory-keychain ' , activeUser ?. pubkey ] ,
15+ // Query: Get all relevant keychain events ( mine + those shared with me)
16+ const { data : keychains = [ ] , isLoading : isLoadingKeychains } = useQuery ( {
17+ queryKey : [ 'inventory-keychains ' , activeUser ?. pubkey ] ,
1818 queryFn : async ( ) => {
19- if ( ! ndk || ! activeUser ) return null ;
19+ if ( ! ndk || ! activeUser ) return [ ] ;
2020
21- // 1. Try to fetch my own keychain first
22- const myEvent = await ndk . fetchEvent ( {
21+ // 1. Fetch own keychain
22+ const myPromise = ndk . fetchEvent ( {
2323 kinds : [ KEYCHAIN_KIND ] ,
2424 authors : [ activeUser . pubkey ] ,
2525 '#d' : [ KEYCHAIN_D_TAG ] ,
2626 } ) ;
2727
28- if ( myEvent ) return myEvent ;
28+ // 2. Fetch shared keychains
29+ const sharedPromise = ( async ( ) => {
30+ const sharedWithMeEvents = await ndk . fetchEvents ( {
31+ kinds : [ 30078 ] , // SHARING_KIND
32+ '#d' : [ 'inventory-shares' ] , // SHARING_D_TAG
33+ '#p' : [ activeUser . pubkey ] ,
34+ } ) ;
2935
30- // 2. If I don't have a keychain, check who has shared with me
31- // We can't use useSharing() here to avoid circular dependency
32- const sharedWithMeEvents = await ndk . fetchEvents ( {
33- kinds : [ 30078 ] , // SHARING_KIND
34- '#d' : [ 'inventory-shares' ] , // SHARING_D_TAG
35- '#p' : [ activeUser . pubkey ] ,
36- } ) ;
36+ const sharers = Array . from ( sharedWithMeEvents ) . map ( e => e . pubkey ) ;
37+ if ( sharers . length === 0 ) return [ ] ;
3738
38- const sharers = Array . from ( sharedWithMeEvents ) . map ( e => e . pubkey ) ;
39- if ( sharers . length === 0 ) return null ;
39+ const events = await ndk . fetchEvents ( {
40+ kinds : [ KEYCHAIN_KIND ] ,
41+ authors : sharers ,
42+ '#d' : [ KEYCHAIN_D_TAG ] ,
43+ } ) ;
44+ return Array . from ( events ) ;
45+ } ) ( ) ;
4046
41- // 3. Fetch keychains from those sharers
42- const sharedKeychains = await ndk . fetchEvents ( {
43- kinds : [ KEYCHAIN_KIND ] ,
44- authors : sharers ,
45- '#d' : [ KEYCHAIN_D_TAG ] ,
46- } ) ;
47+ const [ myEvent , sharedEvents ] = await Promise . all ( [ myPromise , sharedPromise ] ) ;
4748
48- // 4. Find one that has a key for me
49- for ( const event of sharedKeychains ) {
50- const hasKeyForMe = event . tags . some ( t => t [ 0 ] === 'p' && t [ 1 ] === activeUser . pubkey ) ;
51- if ( hasKeyForMe ) return event ;
52- }
49+ const allEvents = sharedEvents ;
50+ if ( myEvent ) allEvents . push ( myEvent ) ;
5351
54- return null ;
52+ return allEvents ;
5553 } ,
5654 enabled : ! ! ndk && ! ! activeUser
5755 } ) ;
5856
59- // Query: Extract and decrypt the symmetric key for the current user
60- const { data : sharedKey , isLoading : isLoadingKey } = useQuery ( {
61- queryKey : [ 'inventory-shared-key ' , activeUser ?. pubkey , keychainEvent ?. id ] ,
57+ // Query: Extract and decrypt keys
58+ const { data : keysData , isLoading : isLoadingKeys } = useQuery ( {
59+ queryKey : [ 'inventory-keys ' , activeUser ?. pubkey , keychains . length ] ,
6260 queryFn : async ( ) => {
63- if ( ! ndk || ! activeUser || ! keychainEvent ) return null ;
64- if ( ! ndk . signer ) return null ;
65-
66- // Find tag for me: ['p', my_pub, encrypted_key]
67- const myTag = keychainEvent . tags . find ( t => t [ 0 ] === 'p' && t [ 1 ] === activeUser . pubkey ) ;
68- if ( ! myTag || ! myTag [ 2 ] ) return null ;
69-
70- const encryptedKey = myTag [ 2 ] ;
71- // The sender is the author of the keychain event (could be self or sharer)
72- const senderUser = new NDKUser ( { pubkey : keychainEvent . pubkey } ) ;
73-
74- try {
75- // NDK decrypt: (sender, value)
76- const decryptedHex = await ndk . signer . decrypt ( senderUser , encryptedKey ) ;
77- return hexToBytes ( decryptedHex ) ;
78- } catch ( e ) {
79- console . error ( 'Decryption failed:' , e ) ;
80- return null ;
61+ if ( ! ndk || ! activeUser || ! ndk . signer || keychains . length === 0 ) {
62+ return { keys : new Map < string , Uint8Array > ( ) , myKey : null as Uint8Array | null , myKeychain : null as NDKEvent | null } ;
8163 }
64+
65+ const keys = new Map < string , Uint8Array > ( ) ;
66+ let myKey : Uint8Array | null = null ;
67+ let myKeychain : NDKEvent | null = null ;
68+
69+ await Promise . all ( keychains . map ( async ( event ) => {
70+ // Find tag for me: ['p', my_pub, encrypted_key]
71+ const myTag = event . tags . find ( t => t [ 0 ] === 'p' && t [ 1 ] === activeUser . pubkey ) ;
72+ if ( ! myTag || ! myTag [ 2 ] ) return ;
73+
74+ try {
75+ const encryptedKey = myTag [ 2 ] ;
76+ const senderUser = new NDKUser ( { pubkey : event . pubkey } ) ;
77+
78+ const decryptedHex = await ndk ! . signer ! . decrypt ( senderUser , encryptedKey ) ;
79+ const keyBytes = hexToBytes ( decryptedHex ) ;
80+
81+ keys . set ( event . pubkey , keyBytes ) ;
82+
83+ if ( event . pubkey === activeUser . pubkey ) {
84+ myKey = keyBytes ;
85+ myKeychain = event ;
86+ }
87+ } catch ( e ) {
88+ console . warn ( `Failed to decrypt key from ${ event . pubkey } ` , e ) ;
89+ }
90+ } ) ) ;
91+
92+ return { keys, myKey, myKeychain } ;
8293 } ,
83- enabled : ! ! ndk && ! ! activeUser && ! ! keychainEvent
94+ enabled : ! ! ndk && ! ! activeUser && keychains . length > 0
8495 } ) ;
8596
97+ const keys = keysData ?. keys || new Map < string , Uint8Array > ( ) ;
98+ const myKey = keysData ?. myKey || null ;
99+ const myKeychain = keysData ?. myKeychain || null ; // For mutations
100+
86101 // Mutation: Initialize standard key if missing
87102 const initializeKey = useMutation ( {
88103 mutationFn : async ( ) => {
89104 if ( ! ndk || ! activeUser || ! ndk . signer ) throw new Error ( 'Not logged in' ) ;
90- if ( sharedKey ) return sharedKey ;
105+ if ( myKey ) return myKey ;
91106
92107 const newKey = generateInventoryKey ( ) ;
93108 const newKeyHex = bytesToHex ( newKey ) ;
@@ -106,23 +121,23 @@ export function useInventoryKey() {
106121 return newKey ;
107122 } ,
108123 onSuccess : ( ) => {
109- queryClient . invalidateQueries ( { queryKey : [ 'inventory-keychain ' ] } ) ;
110- queryClient . invalidateQueries ( { queryKey : [ 'inventory-shared-key ' ] } ) ;
124+ queryClient . invalidateQueries ( { queryKey : [ 'inventory-keychains ' ] } ) ;
125+ queryClient . invalidateQueries ( { queryKey : [ 'inventory-keys ' ] } ) ;
111126 }
112127 } ) ;
113128
114129 // Mutation: Add Reader
115130 const addReader = useMutation ( {
116131 mutationFn : async ( targetPubkey : string ) => {
117- if ( ! ndk || ! activeUser || ! sharedKey || ! keychainEvent ) throw new Error ( 'Not ready' ) ;
132+ if ( ! ndk || ! activeUser || ! myKey || ! myKeychain ) throw new Error ( 'Not ready or no personal key ' ) ;
118133 if ( ! ndk . signer ) throw new Error ( 'No signer' ) ;
119134
120- const keyHex = bytesToHex ( sharedKey ) ;
135+ const keyHex = bytesToHex ( myKey ) ;
121136 const targetUser = new NDKUser ( { pubkey : targetPubkey } ) ;
122137
123138 const encryptedForTarget = await ndk . signer . encrypt ( targetUser , keyHex ) ;
124139
125- const existingTags = keychainEvent . tags . filter ( t => t [ 0 ] !== 'd' ) ;
140+ const existingTags = myKeychain . tags . filter ( t => t [ 0 ] !== 'd' ) ;
126141 const filteredTags = existingTags . filter ( t => ! ( t [ 0 ] === 'p' && t [ 1 ] === targetPubkey ) ) ;
127142
128143 const event = new NDKEvent ( ndk ) ;
@@ -135,17 +150,17 @@ export function useInventoryKey() {
135150 await event . publish ( ) ;
136151 } ,
137152 onSuccess : ( ) => {
138- queryClient . invalidateQueries ( { queryKey : [ 'inventory-keychain ' ] } ) ;
153+ queryClient . invalidateQueries ( { queryKey : [ 'inventory-keychains ' ] } ) ; // To update myKeychain ref
139154 }
140155 } ) ;
141156
142157 // Mutation: Remove Reader
143158 const removeReader = useMutation ( {
144159 mutationFn : async ( targetPubkey : string ) => {
145- if ( ! ndk || ! keychainEvent ) return ;
160+ if ( ! ndk || ! myKeychain ) return ;
146161 if ( targetPubkey === activeUser ?. pubkey ) return ;
147162
148- const newTags = keychainEvent . tags . filter ( t => ! ( t [ 0 ] === 'p' && t [ 1 ] === targetPubkey ) ) ;
163+ const newTags = myKeychain . tags . filter ( t => ! ( t [ 0 ] === 'p' && t [ 1 ] === targetPubkey ) ) ;
149164
150165 const event = new NDKEvent ( ndk ) ;
151166 event . kind = KEYCHAIN_KIND ;
@@ -154,15 +169,19 @@ export function useInventoryKey() {
154169 await event . publish ( ) ;
155170 } ,
156171 onSuccess : ( ) => {
157- queryClient . invalidateQueries ( { queryKey : [ 'inventory-keychain ' ] } ) ;
172+ queryClient . invalidateQueries ( { queryKey : [ 'inventory-keychains ' ] } ) ;
158173 }
159174 } ) ;
160175
161176 return {
162- sharedKey,
163- isLoading : isLoadingKeychain || isLoadingKey ,
177+ keys,
178+ sharedKey : myKey || keys . values ( ) . next ( ) . value || null , // Fallback for backward compatibility
179+ myKey, // Explicitly expose myKey for writing
180+ isLoading : isLoadingKeychains || isLoadingKeys ,
164181 initializeKey,
165182 addReader,
166183 removeReader
167184 } ;
168185}
186+
187+
0 commit comments