Commit graph

7 commits

Author SHA1 Message Date
beb29915d8 feat(market): search + pull-to-refresh in the market list
Match the inventory patterns the market was missing:
- Search: OffersState gains a `query` + `visibleOffers` getter and
  OffersCubit.search() filters the already-discovered offers locally by
  summary (case-insensitive), never re-hitting the transport. The filter
  survives a refresh. Rounded search field mirrors inventory's.
- Pull-to-refresh: RefreshIndicator re-runs discovery for the current
  area (empty state included, so a swipe re-scans). Text filter kept.
- i18n: market.searchHint / market.noMatches (en/es/pt).

Note: the shared slang output (strings*.g.dart, incl. the generated
Asturian locale) is regenerated wholesale, so it also carries the
in-progress `ast` locale keys already present in the working tree.
2026-07-10 15:40:13 +02:00
5f87ad2d56 fix(block2): sharing config UX + discovery + labels (raw feedback batch)
From on-device feedback:
- Area is no longer shown as a raw geohash 'name'. 'Use my location' is the
  primary action; a human status line says whether your area is set (coarse,
  never exact). The geohash code + the relay servers moved under 'Advanced'
  (code field labelled 'a code like sp3e9 — not a place name'), which also
  brings back node/relay selection that had been fully hidden.
- Location denial no longer yanks you into system settings — returns quietly
  with an actionable inline message.
- Discovery no longer spins forever when nobody's sharing nearby: a timeout
  resolves 'searching' to the empty state.
- Home card 'Open market' -> 'Market' + 'Discover and share seeds nearby'
  (the word 'open' confused).

i18n en/es/pt; analyzer clean; the use-my-location test asserts the status flip.
2026-07-10 13:32:11 +02:00
cf5d0243ee feat(block2): offline outbox — share now, publish when connected
Sender-side offline delivery hardening.
- OfferOutbox: a keystore-backed (no plaintext) queue of lot ids to publish
  later. Stores ids, not offers, so on flush each offer is rebuilt from the
  lot's CURRENT state — an offline edit is respected, a deleted lot drops out.
- flushOutbox(): on reconnect, republishes queued lots and clears them (no-op
  offline; drops ids whose lot is gone).
- Market screen: sharing offline parks the lots ('we'll share these when you're
  connected'); opening the market online auto-flushes the queue first.
- Wired via DI + TaneApp(outbox); i18n en/es/pt.

Tests: 4 outbox + 3 flush + market UI, 20 green. Analyzer clean for new code.
2026-07-10 10:45:53 +02:00
2a6b4b0947 feat(block2): relay pool — publish to many relays, discover deduped, tolerate failures
Relay-strategy hardening (network-trust §3): don't hang on one relay.
- NostrChannel interface: what the transports need (sign, publish, subscribe,
  reqOnce). Implemented by NostrConnection (single relay) and the new RelayPool.
- RelayPool: connects to all configured relays (skips unreachable ones, throws
  only when none are reachable), publishes to every live relay (accepted if any
  accepts), merges discovery deduped by event id, and survives a relay dropping.
- Transports now depend on NostrChannel, not a concrete connection.
- SocialService.openSession takes ALL configured relays (was relays.first);
  createOffersCubit passes the whole list.

commons_core 63 tests green (3 new pool tests over two in-process relays);
app_seeds social/market 18 green.
2026-07-10 10:35:39 +02:00
f8f73c4153 feat(block2): publish my seeds to the network (completes publish->discover)
The other half of the market: share your marked lots as offers.
- VarietyRepository.shareableLots(): non-private lots WITH their id (stable,
  updatable offer id) + the variety label. Private lots never leave the device.
- OffersCubit.publishLots(): maps each via OfferMapper and publishes, tagged
  with the coarse area + signed by the derived key; counts acceptances; no-op
  offline or with no area.
- Market screen: a 'share my seeds' action (online only) — publishes, then
  re-discovers to show them; 'mark some seeds first' when nothing's shared.
- i18n en/es/pt.

14 tests green (repo query, cubit publish, market UI); analyzer clean for new code.
2026-07-10 10:18:30 +02:00
528b499209 feat(block2): social settings + offers cubit factory (area, relays)
Persistence + wiring the offers screen needs, per the product decisions:
- SocialSettings (keystore-backed, no plaintext): coarse area geohash +
  configurable relay URLs, both empty by default (no bundled public relays —
  don't leak metadata to third parties). Registered in DI.
- createOffersCubit(): opens a SocialSession on the first configured relay when
  online, else a null-transport cubit that degrades gracefully. OffersCubit now
  closes the owning session on dispose.

4 settings tests green; offers/social suites still green.
2026-07-10 02:54:06 +02:00
7fd87e6e62 feat(block2): offers logic layer — OffersCubit + OfferMapper (no UI yet)
The testable core of the offers UI slice, transport-agnostic:
- OfferMapper: pure app-sharing-intent (OfferStatus) -> network Offer/OfferType
  mapping; keeps price only for sales, refuses to publish private lots.
- OffersCubit: discover(geohash) streams results; publish() reports the verdict;
  depends on the OfferTransport interface (fake in tests) and degrades
  gracefully offline (null transport = social layer unavailable, never throws).

8 tests green (fake in-memory transport, no relay). Screen/routing/i18n come
next — they need two product decisions (coarse-area capture, relay list).
2026-07-10 02:49:06 +02:00