diff --git a/src/i18n/locales/en.ts b/src/i18n/locales/en.ts index 9b77759..3e1a597 100644 --- a/src/i18n/locales/en.ts +++ b/src/i18n/locales/en.ts @@ -168,6 +168,15 @@ const messages: LocaleMessages = { notAvailable: 'Income submission is not yet available. This feature is coming soon.', }, }, + market: { + auth: { + loginPrompt: 'Log in to place your order', + logIn: 'Log in', + logInToCheckout: 'Log in to checkout', + nostrKeyRequired: 'A Nostr identity is required', + nostrKeyDescription: 'Configure your Nostr public key in Profile settings to place orders.', + }, + }, dateTimeFormats: { short: { year: 'numeric', diff --git a/src/i18n/locales/es.ts b/src/i18n/locales/es.ts index 82f0816..53a7d40 100644 --- a/src/i18n/locales/es.ts +++ b/src/i18n/locales/es.ts @@ -168,6 +168,15 @@ const messages: LocaleMessages = { notAvailable: 'El registro de ingresos a\u00fan no est\u00e1 disponible. Esta funci\u00f3n llegar\u00e1 pronto.', }, }, + market: { + auth: { + loginPrompt: 'Inicia sesi\u00f3n para realizar tu pedido', + logIn: 'Iniciar sesi\u00f3n', + logInToCheckout: 'Iniciar sesi\u00f3n para finalizar compra', + nostrKeyRequired: 'Se requiere una identidad Nostr', + nostrKeyDescription: 'Configura tu clave p\u00fablica Nostr en los ajustes del perfil para realizar pedidos.', + }, + }, dateTimeFormats: { short: { year: 'numeric', diff --git a/src/i18n/locales/fr.ts b/src/i18n/locales/fr.ts index b4b4c08..5f11684 100644 --- a/src/i18n/locales/fr.ts +++ b/src/i18n/locales/fr.ts @@ -168,6 +168,15 @@ const messages: LocaleMessages = { notAvailable: 'La saisie de revenus n\u2019est pas encore disponible. Cette fonctionnalit\u00e9 arrive bient\u00f4t.', }, }, + market: { + auth: { + loginPrompt: 'Connectez-vous pour passer commande', + logIn: 'Se connecter', + logInToCheckout: 'Se connecter pour commander', + nostrKeyRequired: 'Une identit\u00e9 Nostr est requise', + nostrKeyDescription: 'Configurez votre cl\u00e9 publique Nostr dans les param\u00e8tres du profil pour passer commande.', + }, + }, dateTimeFormats: { short: { year: 'numeric', diff --git a/src/i18n/types.ts b/src/i18n/types.ts index ddd20c6..d81753a 100644 --- a/src/i18n/types.ts +++ b/src/i18n/types.ts @@ -144,6 +144,16 @@ export interface LocaleMessages { notAvailable: string } } + // Market module + market?: { + auth: { + loginPrompt: string + logIn: string + logInToCheckout: string + nostrKeyRequired: string + nostrKeyDescription: string + } + } // Add date/time formats dateTimeFormats: { short: { diff --git a/src/modules/market/composables/useMarket.ts b/src/modules/market/composables/useMarket.ts index cadb14b..98ea935 100644 --- a/src/modules/market/composables/useMarket.ts +++ b/src/modules/market/composables/useMarket.ts @@ -64,17 +64,15 @@ export function useMarket() { return 'disconnected' }) - // Load market from naddr + // Load market from naddr (or empty for public browse mode) const loadMarket = async (naddr: string) => { return await marketOperation.execute(async () => { - // Parse naddr to get market data + // Parse naddr (when given) to get market identifier + pubkey. + // Empty naddr + unauth user → public browse mode (no pubkey filter). + const parts = naddr ? naddr.split(':') : [] const marketData = { - identifier: naddr.split(':')[2] || 'default', - pubkey: naddr.split(':')[1] || authService.user.value?.pubkey || '' - } - - if (!marketData.pubkey) { - throw new Error('No pubkey available for market') + identifier: parts[2] || 'default', + pubkey: parts[1] || authService.user.value?.pubkey || '' } await loadMarketData(marketData) @@ -87,8 +85,30 @@ export function useMarket() { // Load market data from Nostr events const loadMarketData = async (marketData: any) => { try { - console.log('🛒 Loading market data for:', { identifier: marketData.identifier, pubkey: marketData.pubkey?.slice(0, 8) }) - + console.log('🛒 Loading market data for:', { identifier: marketData.identifier, pubkey: marketData.pubkey?.slice(0, 8) || '(public browse)' }) + + // Public browse mode: no curated naddr and no logged-in user. + // Skip the kind 30019 query and use a "Discover" placeholder market; + // loadStalls/loadProducts treat browseAll=true as "no authors filter". + if (!marketData.pubkey) { + const market = { + d: marketData.identifier, + pubkey: '', + relays: config.nostr.relays, + selected: true, + browseAll: true, + opts: { + name: 'Discover', + description: 'Public stalls and products from your relays', + merchants: [], + ui: {} + } + } + marketStore.addMarket(market) + marketStore.setActiveMarket(market) + return + } + // Check if we can query events (relays are connected) if (!isConnected.value) { console.log('🛒 Not connected to relays, creating default market') @@ -105,12 +125,12 @@ export function useMarket() { ui: {} } } - + marketStore.addMarket(market) marketStore.setActiveMarket(market) return } - + // Load market data from Nostr events // Fetch market configuration event const events = await relayHub.queryEvents([ @@ -179,7 +199,7 @@ export function useMarket() { } } - // Load stalls from market merchants + // Load stalls from market merchants (or all stalls in public browse mode) const loadStalls = async () => { try { // Get the active market to filter by its merchants @@ -188,19 +208,20 @@ export function useMarket() { return } - const merchants = [...(activeMarket.opts.merchants || [])] - - if (merchants.length === 0) { - return - } + const browseAll = (activeMarket as any).browseAll === true + const merchants = [...(activeMarket.opts.merchants || [])] - // Fetch stall events from market merchants only - const events = await relayHub.queryEvents([ - { - kinds: [MARKET_EVENT_KINDS.STALL], - authors: merchants - } - ]) + if (!browseAll && merchants.length === 0) { + return + } + + // Build filter: in browse-all mode no authors filter; otherwise scope to merchants. + const stallFilter: any = { kinds: [MARKET_EVENT_KINDS.STALL] } + if (!browseAll && merchants.length > 0) { + stallFilter.authors = merchants + } + + const events = await relayHub.queryEvents([stallFilter]) console.log('🛒 Found', events.length, 'stall events for', merchants.length, 'merchants') @@ -245,7 +266,7 @@ export function useMarket() { } } - // Load products from market stalls + // Load products from market stalls (or all products in public browse mode) const loadProducts = async () => { try { const activeMarket = marketStore.activeMarket @@ -253,18 +274,19 @@ export function useMarket() { return } + const browseAll = (activeMarket as any).browseAll === true const merchants = [...(activeMarket.opts.merchants || [])] - if (merchants.length === 0) { - return - } - // Fetch product events from market merchants - const events = await relayHub.queryEvents([ - { - kinds: [MARKET_EVENT_KINDS.PRODUCT], - authors: merchants - } - ]) + if (!browseAll && merchants.length === 0) { + return + } + + const productFilter: any = { kinds: [MARKET_EVENT_KINDS.PRODUCT] } + if (!browseAll && merchants.length > 0) { + productFilter.authors = merchants + } + + const events = await relayHub.queryEvents([productFilter]) console.log('🛒 Found', events.length, 'product events for', merchants.length, 'merchants') diff --git a/src/modules/market/views/CheckoutPage.vue b/src/modules/market/views/CheckoutPage.vue index 9e87ab7..997ad39 100644 --- a/src/modules/market/views/CheckoutPage.vue +++ b/src/modules/market/views/CheckoutPage.vue @@ -241,6 +241,7 @@
+ +
{{ orderValidationMessage }}
++ {{ t('market.auth.loginPrompt') }} +