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:
parent
e6607509c5
commit
54044f165c
2 changed files with 184 additions and 26 deletions
|
|
@ -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 = () => {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
const relayResults = await Promise.allSettled(
|
||||||
|
this.relays.map(async (relay) => {
|
||||||
|
try {
|
||||||
|
const relayEvents: Event[] = []
|
||||||
|
const subscription = this.pool.subscribeMany([relay], filters, {
|
||||||
onevent(event: Event) {
|
onevent(event: Event) {
|
||||||
allEvents.push(event)
|
relayEvents.push(event)
|
||||||
},
|
},
|
||||||
oneose() {
|
oneose() {
|
||||||
// End of stored events - subscription is complete for initial fetch
|
// End of stored events
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Wait for events to be collected
|
// Wait for events to be collected
|
||||||
await new Promise(resolve => setTimeout(resolve, 2000))
|
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||||
|
|
||||||
// Close the subscription
|
|
||||||
subscription.close()
|
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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// 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()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue