/// The four (well, five) reciprocity modes from sharing-model.md §3, plus the /// generic `lend` the core reserves for the future "library of things". enum OfferType { gift, exchange, sale, wanted, lend } enum OfferStatus { active, reserved, closed } /// A publishable offer — the "shop window", deliberately DECOUPLED from Lot / /// inventory (sharing-model.md §2, core-domain-boundary.md §4.3). /// /// It carries only a chosen, denormalised *summary* of what's offered — never a /// foreign key into the seed tables, never the full inventory, never an exact /// address. This is the privacy seam: the core can publish and sync offers /// WITHOUT understanding seeds. Everything here is what the person elected to /// reveal. class Offer { const Offer({ required this.id, required this.authorPubkeyHex, required this.summary, required this.type, required this.approxGeohash, this.status = OfferStatus.active, this.category, this.priceAmount, this.priceCurrency, this.exchangeTerms, this.radiusKm, this.imageUrl, this.expiresAt, }); /// Stable per-offer identifier (NIP-99 `d` tag → addressable event). final String id; /// The publishing identity (BIP340 x-only pubkey hex). Derived, per Q1. final String authorPubkeyHex; /// Human summary the author chose to publish (e.g. "Tomate rosa de Barbastro"). final String summary; final OfferType type; final OfferStatus status; /// Free-text category, prefilled in-app from Species.family but opaque here. final String? category; /// LOW-PRECISION geohash only (sharing-model.md §2: "10 km near you", never /// exact coordinates). The spike enforces a max length in the transport. final String approxGeohash; final int? radiusKm; final num? priceAmount; // only for sale final String? priceCurrency; // ISO 4217 or a community currency (e.g. "G1") final String? exchangeTerms; // only for exchange final String? imageUrl; final DateTime? expiresAt; } /// Result of publishing to a transport. class PublishResult { const PublishResult({ required this.accepted, required this.transportRef, this.message = '', }); /// Whether the relay/instance accepted the event. final bool accepted; /// The transport-side id (Nostr event id / ActivityPub object id). final String transportRef; final String message; } /// A discovery query: coarse location + optional facets. Note there is NO way to /// ask for "everything from person X's inventory" — you can only browse the /// public shop window by area and kind. class DiscoveryQuery { const DiscoveryQuery({ required this.geohashPrefix, this.types = const {}, this.limit = 100, }); /// Coarse geohash prefix to search near (e.g. "u09" ≈ tens of km). final String geohashPrefix; final Set types; final int limit; }