Refactor NostrFeed module and remove marketplace components

- Deleted the MarketProduct component and associated market parsing logic, streamlining the NostrFeed module.
- Updated FeedService to exclude marketplace events from the main feed, ensuring clearer event management.
- Adjusted content filters to remove marketplace-related entries, enhancing the organization of content filtering.

These changes improve the clarity and efficiency of the NostrFeed module by separating marketplace functionality.
This commit is contained in:
padreug 2025-09-17 02:50:12 +02:00
parent 2a9915a727
commit 3c20d1c584
6 changed files with 3 additions and 389 deletions

View file

@ -154,10 +154,10 @@ const presets = computed(() => [
{ id: 'all', label: 'All Content' },
{ id: 'announcements', label: 'Announcements' },
{ id: 'community', label: 'Community' },
{ id: 'marketplace', label: 'Marketplace' },
{ id: 'social', label: 'Social' },
{ id: 'events', label: 'Events' },
{ id: 'content', label: 'Articles' }
{ id: 'content', label: 'Articles' },
{ id: 'rideshare', label: 'Rideshare' }
])
// Current active filters

View file

@ -1,171 +0,0 @@
<template>
<Card class="overflow-hidden">
<!-- Product Image -->
<div v-if="product.images && product.images.length > 0" class="relative">
<img
:src="product.images[0]"
:alt="product.name"
class="w-full h-48 object-cover"
@error="onImageError"
/>
<!-- Price Badge -->
<div class="absolute top-2 right-2 bg-black/80 text-white px-3 py-1 rounded-full text-sm font-semibold">
{{ formatPrice(product.price, product.currency) }}
</div>
<!-- Availability Badge -->
<div v-if="!product.active" class="absolute top-2 left-2 bg-red-500 text-white px-2 py-1 rounded text-xs font-medium">
Unavailable
</div>
<div v-else-if="product.quantity <= 0" class="absolute top-2 left-2 bg-orange-500 text-white px-2 py-1 rounded text-xs font-medium">
Out of Stock
</div>
<div v-else-if="product.quantity <= 5" class="absolute top-2 left-2 bg-yellow-500 text-white px-2 py-1 rounded text-xs font-medium">
Limited Stock
</div>
</div>
<CardContent class="p-4">
<!-- Product Header -->
<div class="space-y-2">
<div class="flex items-start justify-between gap-2">
<h3 class="font-semibold text-lg line-clamp-1">{{ product.name }}</h3>
<Badge variant="secondary" class="text-xs">
<ShoppingBag class="w-3 h-3 mr-1" />
Market
</Badge>
</div>
<!-- Description -->
<p class="text-muted-foreground text-sm line-clamp-2">
{{ product.description }}
</p>
</div>
<!-- Product Details -->
<div class="mt-4 space-y-3">
<!-- Price and Quantity -->
<div class="flex items-center justify-between">
<div class="text-2xl font-bold">
{{ formatPrice(product.price, product.currency) }}
</div>
<div class="text-sm text-muted-foreground">
{{ product.quantity > 0 ? `${product.quantity} available` : 'Out of stock' }}
</div>
</div>
<!-- Shipping Info -->
<div v-if="product.shipping && product.shipping.length > 0" class="flex items-center gap-2 text-sm text-muted-foreground">
<Truck class="w-4 h-4" />
<span v-if="hasFreelShipping">Free shipping available</span>
<span v-else>Shipping from {{ formatPrice(minShippingCost, product.currency) }}</span>
</div>
<!-- Stall ID (for debugging/reference) -->
<div class="text-xs text-muted-foreground">
Stall: {{ product.stall_id }}
</div>
</div>
<!-- Action Buttons -->
<div class="mt-4 flex gap-2">
<Button
class="flex-1"
:disabled="!product.active || product.quantity <= 0"
@click="onViewProduct"
>
<ShoppingCart class="w-4 h-4 mr-2" />
{{ product.active && product.quantity > 0 ? 'View Product' : 'Unavailable' }}
</Button>
<Button variant="outline" size="icon" @click="onShareProduct">
<Share2 class="w-4 h-4" />
</Button>
</div>
</CardContent>
</Card>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { Card, CardContent } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import { Badge } from '@/components/ui/badge'
import { ShoppingBag, ShoppingCart, Truck, Share2 } from 'lucide-vue-next'
export interface MarketProductData {
id: string
stall_id: string
name: string
description: string
images?: string[]
currency: string
price: number
quantity: number
active: boolean
shipping?: Array<{
id: string
cost: number
}>
}
interface Props {
product: MarketProductData
}
interface Emits {
(e: 'view-product', productId: string): void
(e: 'share-product', productId: string): void
}
const props = defineProps<Props>()
const emit = defineEmits<Emits>()
// Computed properties
const hasFreelShipping = computed(() => {
return props.product.shipping?.some(shipping => shipping.cost === 0) || false
})
const minShippingCost = computed(() => {
if (!props.product.shipping?.length) return 0
return Math.min(...props.product.shipping.map(s => s.cost))
})
// Methods
const formatPrice = (price: number, currency: string) => {
if (currency.toLowerCase() === 'sat' || currency.toLowerCase() === 'sats') {
return `${price.toLocaleString()} sats`
}
if (currency.toLowerCase() === 'btc') {
return `${(price / 100000000).toFixed(8)}`
}
return `${price} ${currency.toUpperCase()}`
}
const onImageError = (event: Event) => {
const img = event.target as HTMLImageElement
img.style.display = 'none'
}
const onViewProduct = () => {
emit('view-product', props.product.id)
}
const onShareProduct = () => {
emit('share-product', props.product.id)
}
</script>
<style scoped>
.line-clamp-1 {
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
}
.line-clamp-2 {
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}
</style>

View file

@ -9,8 +9,6 @@ import { useProfiles } from '../composables/useProfiles'
import { useReactions } from '../composables/useReactions'
import appConfig from '@/app.config'
import type { ContentFilter } from '../services/FeedService'
import MarketProduct from './MarketProduct.vue'
import { parseMarketProduct } from '../utils/marketParser'
interface Emits {
(e: 'reply-to-note', note: { id: string; content: string; pubkey: string }): void
@ -130,34 +128,6 @@ function getRideshareType(note: any): string | null {
return 'Rideshare'
}
// Get market product data for market events
function getMarketProductData(note: any) {
if (note.kind === 30018) {
// Create a mock NostrEvent from our FeedPost
const mockEvent = {
id: note.id,
pubkey: note.pubkey,
content: note.content,
created_at: note.created_at,
kind: note.kind,
tags: note.tags,
sig: '' // Required by Event interface
}
return parseMarketProduct(mockEvent)
}
return null
}
// Handle market product actions
function onViewProduct(productId: string) {
console.log('View product:', productId)
// TODO: Navigate to product detail page or open modal
}
function onShareProduct(productId: string) {
console.log('Share product:', productId)
// TODO: Implement sharing functionality
}
// Handle reply to note
function onReplyToNote(note: any) {
@ -247,53 +217,8 @@ async function onToggleLike(note: any) {
<div v-else class="h-full overflow-y-auto scrollbar-thin scrollbar-thumb-border scrollbar-track-transparent">
<div>
<div v-for="(note, index) in notes" :key="note.id" :class="{ 'bg-muted/20': index % 2 === 1 }">
<!-- Market Product Component (kind 30018) -->
<template v-if="note.kind === 30018">
<div class="p-3 border-b border-border/40">
<div class="mb-2 flex items-center justify-between">
<div class="flex items-center gap-2">
<Badge
v-if="isAdminPost(note.pubkey)"
variant="default"
class="text-xs"
>
Admin
</Badge>
<span class="text-sm font-medium">{{ getDisplayName(note.pubkey) }}</span>
</div>
<span class="text-xs text-muted-foreground">
{{ formatDistanceToNow(note.created_at * 1000, { addSuffix: true }) }}
</span>
</div>
<MarketProduct
v-if="getMarketProductData(note)"
:product="getMarketProductData(note)!"
@view-product="onViewProduct"
@share-product="onShareProduct"
/>
<!-- Fallback for invalid market data -->
<div
v-else
class="p-3 border rounded-lg border-destructive/20"
>
<div class="flex items-center gap-2 mb-2">
<Badge variant="destructive" class="text-xs">
Invalid Market Product
</Badge>
<span class="text-xs text-muted-foreground">
{{ formatDistanceToNow(note.created_at * 1000, { addSuffix: true }) }}
</span>
</div>
<div class="text-sm text-muted-foreground">
Unable to parse market product data
</div>
</div>
</div>
</template>
<!-- Regular Text Posts and Other Event Types -->
<!-- Text Posts and Other Event Types -->
<div
v-else
class="p-3 hover:bg-accent/50 transition-colors border-b border-border/40"
>
<!-- Note Header -->
@ -313,13 +238,6 @@ async function onToggleLike(note: any) {
>
Reply
</Badge>
<Badge
v-if="note.kind === 30018"
variant="outline"
class="text-xs px-1.5 py-0.5"
>
Market Product
</Badge>
<Badge
v-if="isRidesharePost(note)"
variant="secondary"