feat(market): fix publish-duplicates, add offer detail page + filters
The vecino/market view had three gaps: - Publishing inventory duplicated the listing. discover() appended every offer the relay streamed, but relays legitimately resend an addressable NIP-99 event (stored copy + live echo on publish). Merge by (authorPubkeyHex, id) so each offer appears once; two authors reusing a lot id stay distinct. - No product detail. Tapping a card jumped straight to chat. Add a read-only "product page" (MarketOfferDetailScreen) with title, mode, category, eco badge, coarse location, price, a "Shared by" seller section (profile fetched by pubkey) and the message button. Reached via a new /market/offer route; cards are now fully tappable. Shows only what the network already carries — no photos/description added to the wire. - No filters. Add a filter bar (type, category, eco) mirroring inventory, each chip group shown only when a discovered offer can match it. To make the eco filter real, publish the organic flag on the wire: Offer.isOrganic -> NIP-99 'organic' tag -> OfferMapper -> ShareableLot. Tests: commons_core organic round-trip; app-side dedup, two-author separation, filter logic, mapper passthrough; detail-screen widget tests. i18n keys added to en/es/pt/ast and slang regenerated.
This commit is contained in:
parent
e852b569ce
commit
54d7c2d3b5
21 changed files with 982 additions and 107 deletions
|
|
@ -30,6 +30,7 @@ class Nip99Codec {
|
|||
tags.add(['g', coarse.substring(0, i)]); // NIP-52 prefix ladder, coarse only
|
||||
}
|
||||
if (offer.category != null) tags.add(['t', offer.category!]);
|
||||
if (offer.isOrganic) tags.add(['organic', '1']);
|
||||
if (offer.radiusKm != null) tags.add(['radius_km', '${offer.radiusKm}']);
|
||||
if (offer.type == OfferType.sale && offer.priceAmount != null) {
|
||||
tags.add(['price', '${offer.priceAmount}', offer.priceCurrency ?? 'EUR']);
|
||||
|
|
@ -62,6 +63,7 @@ class Nip99Codec {
|
|||
priceCurrency: price != null && price.length > 2 ? price[2] : null,
|
||||
exchangeTerms: _tagValue(event, 'exchange_terms'),
|
||||
imageUrl: _tagValue(event, 'image'),
|
||||
isOrganic: _tagValue(event, 'organic') == '1',
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ class Offer {
|
|||
this.radiusKm,
|
||||
this.imageUrl,
|
||||
this.expiresAt,
|
||||
this.isOrganic = false,
|
||||
});
|
||||
|
||||
/// Stable per-offer identifier (NIP-99 `d` tag → addressable event).
|
||||
|
|
@ -57,6 +58,10 @@ class Offer {
|
|||
|
||||
final String? imageUrl;
|
||||
final DateTime? expiresAt;
|
||||
|
||||
/// Grower-declared organic ("eco") provenance — a self-declaration the author
|
||||
/// chose to publish, so peers can filter for it. Never a certification.
|
||||
final bool isOrganic;
|
||||
}
|
||||
|
||||
/// Result of publishing to a transport.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue