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.
This commit is contained in:
padreug 2025-08-02 17:35:41 +02:00
parent e6607509c5
commit 54044f165c
2 changed files with 184 additions and 26 deletions

View file

@ -26,8 +26,12 @@ export function useMarket() {
isLoading.value = true isLoading.value = true
error.value = null error.value = null
console.log('Loading market with naddr:', naddr)
// Decode naddr // Decode naddr
const { type, data } = nip19.decode(naddr) const { type, data } = nip19.decode(naddr)
console.log('Decoded naddr:', { type, data })
if (type !== 'naddr' || data.kind !== MARKET_EVENT_KINDS.MARKET) { if (type !== 'naddr' || data.kind !== MARKET_EVENT_KINDS.MARKET) {
throw new Error('Invalid market naddr') throw new Error('Invalid market naddr')
} }
@ -36,8 +40,9 @@ export function useMarket() {
await loadMarketData(data) await loadMarketData(data)
} catch (err) { } catch (err) {
console.error('Error loading market:', err)
error.value = err instanceof Error ? err.message : 'Failed to load market' error.value = err instanceof Error ? err.message : 'Failed to load market'
throw err // Don't throw error, let the UI handle it gracefully
} finally { } finally {
isLoading.value = false isLoading.value = false
} }
@ -70,30 +75,67 @@ export function useMarket() {
try { try {
const client = nostrStore.getClient() const client = nostrStore.getClient()
console.log('Loading market config for:', marketData)
// Fetch market configuration event // Fetch market configuration event
const events = await client.fetchEvents({ const events = await client.fetchEvents({
kinds: [MARKET_EVENT_KINDS.MARKET], kinds: [MARKET_EVENT_KINDS.MARKET],
authors: [marketData.pubkey], authors: [marketData.pubkey],
'#d': [marketData.d] '#d': [marketData.identifier]
}) })
console.log('Found market events:', events.length)
if (events.length > 0) { if (events.length > 0) {
const marketEvent = events[0] const marketEvent = events[0]
console.log('Market event:', marketEvent)
const market: Market = { const market: Market = {
d: marketData.d, d: marketData.identifier,
pubkey: marketData.pubkey, pubkey: marketData.pubkey,
relays: config.market.supportedRelays, relays: config.market.supportedRelays,
selected: true, selected: true,
opts: JSON.parse(marketEvent.content) 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.addMarket(market)
marketStore.setActiveMarket(market) marketStore.setActiveMarket(market)
} }
} catch (err) { } catch (err) {
console.error('Error loading market config:', 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 { try {
const client = nostrStore.getClient() const client = nostrStore.getClient()
console.log('Loading stalls for market pubkey:', marketPubkey)
// Fetch stall events for this market // Fetch stall events for this market
const events = await client.fetchEvents({ const events = await client.fetchEvents({
kinds: [MARKET_EVENT_KINDS.STALL], kinds: [MARKET_EVENT_KINDS.STALL],
authors: [marketPubkey] authors: [marketPubkey]
}) })
console.log('Found stall events:', events.length)
events.forEach(event => { events.forEach(event => {
try { try {
console.log('Processing stall event:', event)
const stallData = JSON.parse(event.content) const stallData = JSON.parse(event.content)
console.log('Parsed stall data:', stallData)
const stall: Stall = { const stall: Stall = {
id: event.id, id: event.id,
pubkey: event.pubkey, pubkey: event.pubkey,
@ -120,6 +169,7 @@ export function useMarket() {
shipping: stallData.shipping shipping: stallData.shipping
} }
console.log('Created stall:', stall)
marketStore.addStall(stall) marketStore.addStall(stall)
} catch (err) { } catch (err) {
console.warn('Failed to parse stall event:', err) console.warn('Failed to parse stall event:', err)
@ -128,7 +178,7 @@ export function useMarket() {
} catch (err) { } catch (err) {
console.error('Error loading stalls:', 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 // Get all stall pubkeys
const stallPubkeys = marketStore.stalls.map(stall => stall.pubkey) 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 // Fetch product events from all stalls
const events = await client.fetchEvents({ const events = await client.fetchEvents({
@ -147,10 +202,16 @@ export function useMarket() {
authors: stallPubkeys authors: stallPubkeys
}) })
console.log('Found product events:', events.length)
events.forEach(event => { events.forEach(event => {
try { try {
console.log('Processing product event:', event)
const productData = JSON.parse(event.content) const productData = JSON.parse(event.content)
console.log('Parsed product data:', productData)
const stall = marketStore.stalls.find(s => s.pubkey === event.pubkey) const stall = marketStore.stalls.find(s => s.pubkey === event.pubkey)
console.log('Found stall for product:', stall)
if (stall) { if (stall) {
const product: Product = { const product: Product = {
@ -168,7 +229,10 @@ export function useMarket() {
updatedAt: event.created_at updatedAt: event.created_at
} }
console.log('Created product:', product)
marketStore.addProduct(product) marketStore.addProduct(product)
} else {
console.warn('No stall found for product pubkey:', event.pubkey)
} }
} catch (err) { } catch (err) {
console.warn('Failed to parse product event:', err) console.warn('Failed to parse product event:', err)
@ -177,8 +241,66 @@ export function useMarket() {
} catch (err) { } catch (err) {
console.error('Error loading products:', 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 = () => { const subscribeToMarketUpdates = () => {

View file

@ -229,33 +229,69 @@ export class NostrClient {
'#d': dTags '#d': dTags
} = options } = options
const filters: Filter[] = [{ // Build filter object, only including defined properties
const filter: Filter = {
kinds, kinds,
limit, limit
...(authors && { authors }), }
...(since && { since }),
...(until && { until }), if (authors && authors.length > 0) {
...(dTags && { '#d': dTags }) 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 { try {
console.log('Fetching events with filters:', JSON.stringify(filters, null, 2))
const allEvents: Event[] = [] const allEvents: Event[] = []
const subscription = this.pool.subscribeMany(this.relays, filters, { // Try each relay individually to identify problematic relays
onevent(event: Event) { const relayResults = await Promise.allSettled(
allEvents.push(event) this.relays.map(async (relay) => {
}, try {
oneose() { const relayEvents: Event[] = []
// End of stored events - subscription is complete for initial fetch 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 // Deduplicate events by ID
const uniqueEvents = Array.from( const uniqueEvents = Array.from(
new Map(allEvents.map(event => [event.id, event])).values() new Map(allEvents.map(event => [event.id, event])).values()