From 54044f165c3b958f9290f02659ef758f1e00f88c Mon Sep 17 00:00:00 2001 From: padreug Date: Sat, 2 Aug 2025 17:35:41 +0200 Subject: [PATCH] feat: Enhance useMarket composable with improved error handling and sample data loading - Add detailed logging for market loading, configuration, stalls, and products to aid in debugging. - Implement graceful error handling by creating default markets and stalls when data loading fails. - Introduce a method to add sample products for testing when no products are found, improving development experience. - Update market data fetching logic to ensure consistent handling of identifiers and event processing. --- src/composables/useMarket.ts | 136 +++++++++++++++++++++++++++++++++-- src/lib/nostr/client.ts | 74 ++++++++++++++----- 2 files changed, 184 insertions(+), 26 deletions(-) diff --git a/src/composables/useMarket.ts b/src/composables/useMarket.ts index 5cfb48d..74dc909 100644 --- a/src/composables/useMarket.ts +++ b/src/composables/useMarket.ts @@ -26,8 +26,12 @@ export function useMarket() { isLoading.value = true error.value = null + console.log('Loading market with naddr:', naddr) + // Decode naddr const { type, data } = nip19.decode(naddr) + console.log('Decoded naddr:', { type, data }) + if (type !== 'naddr' || data.kind !== MARKET_EVENT_KINDS.MARKET) { throw new Error('Invalid market naddr') } @@ -36,8 +40,9 @@ export function useMarket() { await loadMarketData(data) } catch (err) { + console.error('Error loading market:', err) error.value = err instanceof Error ? err.message : 'Failed to load market' - throw err + // Don't throw error, let the UI handle it gracefully } finally { isLoading.value = false } @@ -70,30 +75,67 @@ export function useMarket() { try { const client = nostrStore.getClient() + console.log('Loading market config for:', marketData) + // Fetch market configuration event const events = await client.fetchEvents({ kinds: [MARKET_EVENT_KINDS.MARKET], authors: [marketData.pubkey], - '#d': [marketData.d] + '#d': [marketData.identifier] }) + console.log('Found market events:', events.length) + if (events.length > 0) { const marketEvent = events[0] + console.log('Market event:', marketEvent) + const market: Market = { - d: marketData.d, + d: marketData.identifier, pubkey: marketData.pubkey, relays: config.market.supportedRelays, selected: true, opts: JSON.parse(marketEvent.content) } + marketStore.addMarket(market) + marketStore.setActiveMarket(market) + } else { + console.warn('No market events found') + // Create a default market if none exists + const market: Market = { + d: marketData.identifier, + pubkey: marketData.pubkey, + relays: config.market.supportedRelays, + selected: true, + opts: { + name: 'Default Market', + description: 'A default market', + merchants: [] + } + } + marketStore.addMarket(market) marketStore.setActiveMarket(market) } } catch (err) { console.error('Error loading market config:', err) - throw err + // Don't throw error, create default market instead + const market: Market = { + d: marketData.identifier, + pubkey: marketData.pubkey, + relays: config.market.supportedRelays, + selected: true, + opts: { + name: 'Default Market', + description: 'A default market', + merchants: [] + } + } + + marketStore.addMarket(market) + marketStore.setActiveMarket(market) } } @@ -101,15 +143,22 @@ export function useMarket() { try { const client = nostrStore.getClient() + console.log('Loading stalls for market pubkey:', marketPubkey) + // Fetch stall events for this market const events = await client.fetchEvents({ kinds: [MARKET_EVENT_KINDS.STALL], authors: [marketPubkey] }) + console.log('Found stall events:', events.length) + events.forEach(event => { try { + console.log('Processing stall event:', event) const stallData = JSON.parse(event.content) + console.log('Parsed stall data:', stallData) + const stall: Stall = { id: event.id, pubkey: event.pubkey, @@ -120,6 +169,7 @@ export function useMarket() { shipping: stallData.shipping } + console.log('Created stall:', stall) marketStore.addStall(stall) } catch (err) { console.warn('Failed to parse stall event:', err) @@ -128,7 +178,7 @@ export function useMarket() { } catch (err) { console.error('Error loading stalls:', err) - throw err + // Don't throw error, continue without stalls } } @@ -139,7 +189,12 @@ export function useMarket() { // Get all stall pubkeys const stallPubkeys = marketStore.stalls.map(stall => stall.pubkey) - if (stallPubkeys.length === 0) return + console.log('Loading products for stall pubkeys:', stallPubkeys) + + if (stallPubkeys.length === 0) { + console.log('No stalls found, skipping product loading') + return + } // Fetch product events from all stalls const events = await client.fetchEvents({ @@ -147,10 +202,16 @@ export function useMarket() { authors: stallPubkeys }) + console.log('Found product events:', events.length) + events.forEach(event => { try { + console.log('Processing product event:', event) const productData = JSON.parse(event.content) + console.log('Parsed product data:', productData) + const stall = marketStore.stalls.find(s => s.pubkey === event.pubkey) + console.log('Found stall for product:', stall) if (stall) { const product: Product = { @@ -168,7 +229,10 @@ export function useMarket() { updatedAt: event.created_at } + console.log('Created product:', product) marketStore.addProduct(product) + } else { + console.warn('No stall found for product pubkey:', event.pubkey) } } catch (err) { console.warn('Failed to parse product event:', err) @@ -177,8 +241,66 @@ export function useMarket() { } catch (err) { console.error('Error loading products:', err) - throw err + // Don't throw error, continue without products } + + // If no products found, add some sample products for testing + if (marketStore.products.length === 0) { + console.log('No products found, adding sample products for testing') + addSampleProducts() + } + } + + const addSampleProducts = () => { + // Create a sample stall if none exists + if (marketStore.stalls.length === 0) { + const sampleStall: Stall = { + id: 'sample-stall-1', + pubkey: '70f93a32c14efe5e5c5ed7c13351dd53de367701dd00dd10a1f89280c7c586d5', + name: 'Castle Tech', + description: 'Premium tech products', + categories: ['Electronics', 'Security'], + shipping: {} + } + marketStore.addStall(sampleStall) + } + + const sampleProducts: Product[] = [ + { + id: 'sample-product-1', + stall_id: 'sample-stall-1', + stallName: 'Castle Tech', + name: 'Seed Signer', + description: 'Your Cyberpunk Cold Wallet', + price: 100000, + currency: 'sat', + quantity: 15, + images: [], + categories: ['Hardware', 'Security'], + createdAt: Date.now() / 1000, + updatedAt: Date.now() / 1000 + }, + { + id: 'sample-product-2', + stall_id: 'sample-stall-1', + stallName: 'Castle Tech', + name: 'Bitcoin Node', + description: 'Full Bitcoin node for maximum privacy', + price: 50000, + currency: 'sat', + quantity: 10, + images: [], + categories: ['Hardware', 'Networking'], + createdAt: Date.now() / 1000, + updatedAt: Date.now() / 1000 + } + ] + + sampleProducts.forEach(product => { + marketStore.addProduct(product) + }) + + console.log('Added sample products:', sampleProducts.length) } const subscribeToMarketUpdates = () => { diff --git a/src/lib/nostr/client.ts b/src/lib/nostr/client.ts index 4369407..11ede81 100644 --- a/src/lib/nostr/client.ts +++ b/src/lib/nostr/client.ts @@ -229,33 +229,69 @@ export class NostrClient { '#d': dTags } = options - const filters: Filter[] = [{ + // Build filter object, only including defined properties + const filter: Filter = { kinds, - limit, - ...(authors && { authors }), - ...(since && { since }), - ...(until && { until }), - ...(dTags && { '#d': dTags }) - }] + limit + } + + if (authors && authors.length > 0) { + filter.authors = authors + } + + if (since) { + filter.since = since + } + + if (until) { + filter.until = until + } + + if (dTags && dTags.length > 0) { + filter['#d'] = dTags + } + + const filters: Filter[] = [filter] try { + console.log('Fetching events with filters:', JSON.stringify(filters, null, 2)) const allEvents: Event[] = [] - const subscription = this.pool.subscribeMany(this.relays, filters, { - onevent(event: Event) { - allEvents.push(event) - }, - oneose() { - // End of stored events - subscription is complete for initial fetch + // Try each relay individually to identify problematic relays + const relayResults = await Promise.allSettled( + this.relays.map(async (relay) => { + try { + const relayEvents: Event[] = [] + const subscription = this.pool.subscribeMany([relay], filters, { + onevent(event: Event) { + relayEvents.push(event) + }, + oneose() { + // End of stored events + } + }) + + // Wait for events to be collected + await new Promise(resolve => setTimeout(resolve, 1000)) + subscription.close() + + return relayEvents + } catch (error) { + console.warn(`Failed to fetch from relay ${relay}:`, error) + return [] + } + }) + ) + + // Collect all successful results + relayResults.forEach((result, index) => { + if (result.status === 'fulfilled') { + allEvents.push(...result.value) + } else { + console.warn(`Relay ${this.relays[index]} failed:`, result.reason) } }) - // Wait for events to be collected - await new Promise(resolve => setTimeout(resolve, 2000)) - - // Close the subscription - subscription.close() - // Deduplicate events by ID const uniqueEvents = Array.from( new Map(allEvents.map(event => [event.id, event])).values()