Skip to content

Commit 6e2f7f8

Browse files
committed
fix(state): add ErrorBoundary, improve logging and hydration
1 parent e1cabda commit 6e2f7f8

File tree

4 files changed

+141
-88
lines changed

4 files changed

+141
-88
lines changed

src/App.tsx

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -58,16 +58,18 @@ export function App() {
5858
// <UnheadProvider head={head}>
5959
<AppProvider storageKey="nostr:app-config" defaultConfig={defaultConfig}>
6060
<HelmetProvider>
61-
<QueryClientProvider client={queryClient}>
62-
<NDKProvider relays={relayUrls}>
63-
<TooltipProvider>
64-
<Toaster />
65-
<Suspense>
66-
<AppRouter />
67-
</Suspense>
68-
</TooltipProvider>
69-
</NDKProvider>
70-
</QueryClientProvider>
61+
<ErrorBoundary>
62+
<QueryClientProvider client={queryClient}>
63+
<NDKProvider relays={relayUrls}>
64+
<TooltipProvider>
65+
<Toaster />
66+
<Suspense>
67+
<AppRouter />
68+
</Suspense>
69+
</TooltipProvider>
70+
</NDKProvider>
71+
</QueryClientProvider>
72+
</ErrorBoundary>
7173
</HelmetProvider>
7274
</AppProvider>
7375
// </UnheadProvider>

src/components/inventory/ShareInventoryModal.tsx

Lines changed: 99 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -115,85 +115,115 @@ export function ShareInventoryModal({ open, onOpenChange }: ShareInventoryModalP
115115
return (
116116
<Dialog open={open} onOpenChange={onOpenChange}>
117117
<DialogContent className="sm:max-w-[500px]">
118-
{/* ... header ... */}
119-
{/* ... Your NPUB ... */}
120-
121-
{/* Add new share */}
122-
<div className="space-y-2">
123-
<Label htmlFor="npub">Share with someone</Label>
124-
<form
125-
className="flex gap-2"
126-
onSubmit={(e) => {
127-
e.preventDefault();
128-
handleAddUser(e as unknown as React.MouseEvent);
129-
}}
130-
>
131-
<Input
132-
id="npub"
133-
placeholder="Enter npub1... or hex pubkey"
134-
value={npubInput}
135-
onChange={(e) => setNpubInput(e.target.value)}
136-
// Removed onKeyDown, rely on form submission
137-
/>
118+
<DialogHeader>
119+
<DialogTitle className="flex items-center gap-2">
120+
<Share2 className="h-5 w-5" />
121+
Share Inventory
122+
</DialogTitle>
123+
<DialogDescription>
124+
Share your inventory with family members or roommates. They'll see your items when they log in.
125+
</DialogDescription>
126+
</DialogHeader>
127+
128+
<div className="space-y-6 py-4">
129+
{/* Your NPUB for others to use */}
130+
<div className="rounded-lg border bg-muted/50 p-4">
131+
<Label className="text-sm font-medium">Your Nostr ID (share this)</Label>
132+
<p className="text-xs text-muted-foreground mt-1 mb-2">
133+
Give this to others so they can share their inventory with you
134+
</p>
138135
<Button
139-
type="submit"
140-
disabled={isAddingUser || !npubInput.trim()}
141-
size="icon"
136+
variant="outline"
137+
onClick={handleCopyNpub}
138+
className="w-full justify-between"
142139
>
143-
<UserPlus className="h-4 w-4" />
140+
<span className="truncate text-xs font-mono">
141+
{user?.pubkey ? `npub1${user.pubkey.slice(0, 8)}...` : 'Loading...'}
142+
</span>
143+
{copied ? (
144+
<Check className="h-4 w-4 text-green-500" />
145+
) : (
146+
<Copy className="h-4 w-4" />
147+
)}
144148
</Button>
145-
</form>
146-
</div>
149+
</div>
147150

148-
{/* People you're sharing with */}
149-
{sharedUsers.length > 0 && (
151+
{/* Add new share */}
150152
<div className="space-y-2">
151-
<Label className="flex items-center gap-2">
152-
<Users className="h-4 w-4" />
153-
People who can see your inventory ({sharedUsers.length})
154-
</Label>
155-
<div className="space-y-2 max-h-40 overflow-y-auto">
156-
{sharedUsers.map((share) => (
157-
<UserShareRow
158-
key={share.pubkey}
159-
pubkey={share.pubkey}
160-
onRemove={removeSharedUser}
161-
isRemoving={isRemovingUser}
162-
canRemove
163-
/>
164-
))}
165-
</div>
153+
<Label htmlFor="npub">Share with someone</Label>
154+
<form
155+
className="flex gap-2"
156+
onSubmit={(e) => {
157+
e.preventDefault();
158+
handleAddUser(e as unknown as React.MouseEvent);
159+
}}
160+
>
161+
<Input
162+
id="npub"
163+
placeholder="Enter npub1... or hex pubkey"
164+
value={npubInput}
165+
onChange={(e) => setNpubInput(e.target.value)}
166+
// Removed onKeyDown, rely on form submission
167+
/>
168+
<Button
169+
type="submit"
170+
disabled={isAddingUser || !npubInput.trim()}
171+
size="icon"
172+
>
173+
<UserPlus className="h-4 w-4" />
174+
</Button>
175+
</form>
166176
</div>
167-
)}
168177

169-
{/* People sharing with you */}
170-
{sharedWithMe.length > 0 && (
171-
<div className="space-y-2">
172-
<Label className="flex items-center gap-2">
173-
<Share2 className="h-4 w-4" />
174-
People sharing with you ({sharedWithMe.length})
175-
</Label>
176-
<div className="space-y-2 max-h-40 overflow-y-auto">
177-
{sharedWithMe.map((share) => (
178-
<UserShareRow key={share.pubkey} pubkey={share.pubkey} />
179-
))}
178+
{/* People you're sharing with */}
179+
{sharedUsers.length > 0 && (
180+
<div className="space-y-2">
181+
<Label className="flex items-center gap-2">
182+
<Users className="h-4 w-4" />
183+
People who can see your inventory ({sharedUsers.length})
184+
</Label>
185+
<div className="space-y-2 max-h-40 overflow-y-auto">
186+
{sharedUsers.map((share) => (
187+
<UserShareRow
188+
key={share.pubkey}
189+
pubkey={share.pubkey}
190+
onRemove={removeSharedUser}
191+
isRemoving={isRemovingUser}
192+
canRemove
193+
/>
194+
))}
195+
</div>
180196
</div>
181-
</div>
182-
)}
197+
)}
183198

184-
{sharedUsers.length === 0 && sharedWithMe.length === 0 && (
185-
<p className="text-center text-sm text-muted-foreground py-4">
186-
No shares yet. Add someone's npub above to share your inventory with them.
187-
</p>
188-
)}
189-
</div>
199+
{/* People sharing with you */}
200+
{sharedWithMe.length > 0 && (
201+
<div className="space-y-2">
202+
<Label className="flex items-center gap-2">
203+
<Share2 className="h-4 w-4" />
204+
People sharing with you ({sharedWithMe.length})
205+
</Label>
206+
<div className="space-y-2 max-h-40 overflow-y-auto">
207+
{sharedWithMe.map((share) => (
208+
<UserShareRow key={share.pubkey} pubkey={share.pubkey} />
209+
))}
210+
</div>
211+
</div>
212+
)}
190213

191-
<DialogFooter>
192-
<Button variant="outline" onClick={() => onOpenChange(false)}>
193-
Close
194-
</Button>
195-
</DialogFooter>
196-
</DialogContent>
214+
{sharedUsers.length === 0 && sharedWithMe.length === 0 && (
215+
<p className="text-center text-sm text-muted-foreground py-4">
216+
No shares yet. Add someone's npub above to share your inventory with them.
217+
</p>
218+
)}
219+
</div>
220+
221+
<DialogFooter>
222+
<Button variant="outline" onClick={() => onOpenChange(false)}>
223+
Close
224+
</Button>
225+
</DialogFooter>
226+
</DialogContent>
197227
</Dialog >
198228
);
199229
}

src/contexts/NDKContext.tsx

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -129,28 +129,44 @@ export function NDKProvider({ children, relays }: NDKProviderProps) {
129129

130130
// Auto-login on mount
131131
useEffect(() => {
132-
if (activeUser) return; // Already logged in?
133-
if (!session) return;
132+
// If we have a session but no active user, we are restoring.
133+
// If no session, we aren't restoring, so we can stop loading (if not already handled by connection).
134+
if (!session && !activeUser) {
135+
if (!ndkInstance.pool.connectedRelays().length) {
136+
// wait for connection? or just let it be.
137+
}
138+
return;
139+
}
140+
141+
if (activeUser) return; // Already logged in
134142

135143
const restoreSession = async () => {
144+
// setIsLoading(true); // Should already be true initally
136145
try {
137-
if (session.type === 'extension') {
138-
// Wait a bit for extension to inject
146+
console.log('🔄 Restoring session:', session?.type);
147+
if (session?.type === 'extension') {
148+
// ... existing logic ...
139149
setTimeout(async () => {
140150
const signer = new NDKNip07Signer();
141151
await loginWithSigner(signer);
142152
}, 500);
143-
} else if (session.type === 'nsec' && session.payload) {
153+
} else if (session?.type === 'nsec' && session.payload) {
144154
const signer = new NDKPrivateKeySigner(session.payload);
145155
await loginWithSigner(signer);
146-
} else if (session.type === 'nip46' && session.payload) {
156+
} else if (session?.type === 'nip46' && session.payload) {
147157
if (!ndk) return;
148158
const signer = new NDKNip46Signer(ndk, session.payload);
149159
await loginWithSigner(signer);
150160
}
151161
} catch (e) {
152162
console.error('Failed to restore session:', e);
153-
saveSession(null); // Clear bad session
163+
saveSession(null);
164+
} finally {
165+
// Only set loading to false AFTER restoration attempt
166+
// But we have a race with the relay connection loading state.
167+
// Let's rely on the separate `isLoading` state management or unify them.
168+
// For now, let's log completion.
169+
console.log('✅ Session restore attempt complete');
154170
}
155171
};
156172

src/hooks/useInventory.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,23 @@ export function useInventory() {
2222
queryFn: async () => {
2323
if (!ndk || !activeUser) return [];
2424

25+
console.log('📦 Fetching inventory items for authors:', allAuthorPubkeys.length);
2526
const events = await ndk.fetchEvents({
2627
kinds: [INVENTORY_KIND],
2728
authors: allAuthorPubkeys,
2829
limit: 200
2930
});
3031

32+
console.log(`📦 Found ${events.size} inventory events. Decrypting...`);
33+
3134
const loadedItems: InventoryItem[] = [];
3235
for (const event of events) {
3336
try {
3437
const item = await eventToInventoryItem(event, keys);
3538
if (item) loadedItems.push(item);
39+
else console.debug('❌ Item decryption skipped/failed for:', event.id);
3640
} catch (e) {
37-
console.warn('Failed to parse item:', e);
41+
console.warn('Failed to parse item:', event.id, e);
3842
}
3943
}
4044
return loadedItems;
@@ -162,7 +166,8 @@ async function eventToInventoryItem(event: NDKEvent, keys: Map<string, Uint8Arra
162166
} catch { return null; }
163167
}
164168
return { ...parsed, id: getDTag(event) || parsed.id };
165-
} catch {
169+
} catch (e) {
170+
console.warn(`Event ${event.id} parsing error:`, e);
166171
return null;
167172
}
168173
}

0 commit comments

Comments
 (0)