Tane dialled its four default relays at launch, before anyone had asked
for anything — an F-Droid reviewer spotted it, and they were right. The
seed book needs no network at all, so the app should not have one until
the person joins the sharing side.
- SocialSettings gains a three-state `sharingEnabled`. `null` means
"never asked", which is what lets `migrateSharingEnabled` keep an
existing install exactly as it was: anyone past the intro was on a
build that connected at launch, so they keep messaging, device sync
and offer alerts. A fresh install starts fully offline.
- bootstrap only starts the shared connection when sharing is on. The
inbox/sync/plantaré/alert listeners are untouched: they react to a
session, and none arrives.
- SharingSwitch is the single place that moves the stored choice, the
live connection and the flag the UI listens to, so they cannot drift.
- Agreeing to the community rules is the opt-in — one consent surface,
reached from the market or from the drawer's invitation.
- SocialConnection.start is now idempotent and gains stop(), so turning
sharing off goes offline immediately instead of at the next launch.
- The social drawer entries stay visible but padlocked while sharing is
off; tapping one explains what wakes up and offers to join. Hiding
them would have kept the tool a secret. "Coming soon" is gone for
good — everything it labelled is built.
Covered by tests for the migration in both directions, start/stop
lifecycle, the gate turning sharing on, the invitation, and the drawer
in all three states (no social layer / off / on).
Remove the "this month" card from the home menu; the calendar stays
reachable from the drawer and the /calendar route is unchanged. Also
drop the now-unused calendar.homeSubtitle i18n key (en/es/pt/ast) and
regenerate slang.
Reuses the existing per-variety crop-calendar masks (sow/transplant/
flowering/fruiting/seed-harvest) — no schema change, no migration — to
answer "qué toca este mes" across the whole inventory. Since the months
are the grower's own record, there's no hemisphere assumption.
- Repo: CalendarEntry + watchCalendar() (varieties with any recorded
phase); VarietyListItem now carries sowMonths.
- Dedicated CalendarScreen (/calendar): a month strip (defaults to the
current month, intl month names) and varieties grouped by phase —
actions (sow/transplant/harvest seed) above informational (flowering/
fruiting), each tinted, tap → variety detail; empty-month state.
- Inventory filter: a 'this month' chip (shown only when some variety
has a calendar) keeps what sows in the current month; folded into the
attributes group + clear-filters.
- Entry points: a live Home card + a drawer item.
- i18n calendar block + menu.calendar (en/es/pt/ast).
- Tests: calendar_test (watchCalendar, list carries sow-months, pure
filter), calendar_screen_test (phase grouping, month switch, empty),
plus the screen added to the small-screen overflow guard.
Adds test/ui/small_screen_overflow_test.dart: pumps the full screens at a
320x568 (iPhone-SE-class) viewport in the longest-text locales (es/pt/ast);
a RenderFlex overflow fails the test, so a clipped title or save button is
caught. It found — and this fixes — a real bug: the Home menu (a fixed,
non-scrolling Center>Column) clipped ~80px on small phones in every locale.
Home is now wrapped in LayoutBuilder + SingleChildScrollView +
ConstrainedBox(minHeight: maxHeight): centered when it fits, scrolls when it
doesn't, never clipped.
Also makes the test harness faithful: wrapScreen/wrapDetail now map the
locale through materialLocaleFor (ast->es) exactly like the app, so Asturian
screens don't throw on MaterialLocalizations (AppBar tooltips) — that had
masqueraded as an overflow.
Scope notes in the test: full screens only (modal bottom sheets get an
unbounded height in widget tests -> false huge overflow; they already scroll
their body), and the variety-detail screen is skipped (its ~2px hit is on a
DISPOSED/DEFUNCT RenderFlex — a transient during the cubit rebuild, not the
stable layout).
Private messages (NIP-17) arrived in the foreground with no signal: no
unread count, no badge, no OS notification. Hook both into the single
InboxService.ingest choke point.
- UnreadService: per-peer "last read" in the keystore (timestamps+pubkey
only, no message text), live count via a changes stream, active-peer
suppression so the open chat never badges or notifies.
- UnreadBadge: reusable, RTL-aware (Flutter Badge) — on the home hamburger,
the drawer "Chat" item, and per-conversation in the messages list.
- Mark-read on chat open (ChatScreen).
- NotificationService over flutter_local_notifications: generic
"New message from <name>" (no message text, for privacy), payload = pubkey,
tap opens /chat/:pubkey. No-op on web/Windows; Android POST_NOTIFICATIONS.
- i18n notifications.newMessageFrom (en/es/pt/ast).
Background/push stays out of scope (foreground-only by design).
Tests: UnreadService, NotificationService (mock plugin), InboxService hooks
(new peer notifies/counts; duplicate, own, and open-chat do not), and a
UnreadBadge widget test. dart analyze clean; commons_core green.
The inventory screen carried its own const AppDrawer() (no marketEnabled),
so opening the menu from inventory greyed out market/chat/profile — the
recurring 'phase 1' look. It was also reached via go() (route replace),
which is why it showed a hamburger instead of a back arrow like the market.
Make it coherent with the market: reach inventory via push() (from the
home card and the drawer), and drop its drawer so the AppBar shows a back
arrow returning to the home hub. The drawer now lives only on the home
screen, which always passes marketEnabled — the wrong-drawer bug class is
gone. Update the home_screen test to assert the spoke has a back arrow and
no hamburger.
Two UX reports:
- Market/Messages (and Profile/Chat) had no back button — they were opened with
context.go (replaces the stack). Switched the drawer entries, home Market card,
offer 'Message' link and inbox taps to context.push, so the AppBar shows a back
arrow to return.
- The market's upload icon (tooltip-only) was unclear. Replaced it with a labelled
FloatingActionButton.extended 'Share my seeds' (campaign icon); the config gear
stays in the AppBar.
Analyzer clean.
Three feedback fixes:
- Servers are fully hidden now — relays are managed automatically (default
community servers; the pool skips dead ones). Sharing setup shows only 'your
area'; no jargon-y URL field. (3 default relays for redundancy.)
- Drawer 'Market' is a live destination (was stuck on 'coming soon'), gated on
the social layer being wired like the home card.
- My own listings in the market carry a 'You' badge (and no 'message yourself'
button), so it's clear which offers are mine.
Analyzer clean. Note: could not run the flutter widget suite here (10+ min
compile after the geolocator native dep in this sandbox) — run 'flutter test'
locally to confirm; the You-badge test was simplified to be robust.
Wires the 'coming soon' market card to a real discovery screen (mockups 04/05):
- MarketScreen: opens an OfferTransport lazily; local-first, so it degrades to a
human 'set up sharing' prompt when no relay/area is set or the net is down.
- Discovers offers by the saved coarse area, lists them with human reciprocity
labels (gift/swap/for sale), 'near you' (never exact location), price only for
sales. Config sheet to set area + community servers (no bundled public relays).
- Threaded via TaneApp(social, socialSettings) — null keeps the card 'coming
soon' (so Block 1 tests pass unchanged); main wires it live.
- i18n en/es/pt (market.*), reuses share.* for type labels.
3 market widget tests + home tests green; app_seeds analyzes clean.
Apply a Material 3 seed-green palette and rounded components across the
home, inventory, quick-add, quantity picker and variety-detail screens.
The full-screen photo viewer can now set any photo as the cover (via
attachment sort order) and delete after confirmation. Lot forms and
packaging surface as choice chips.
Extract About out of Settings into its own screen (app version via
package_info_plus, website link). Add es/en strings for the new lot
types, presentation, home tagline and About. Update Seedees v2 mockups.
- Bundle the real sprout logo (docs/logo.png → assets/logo.png) and show it on
the home menu instead of a Material icon.
- Settings screen: switch language (Español / English / system) via slang, plus
an About tile (logo, app name, one-line description). Drawer "Settings" is now
active and routes to /settings.
Tests: drawer Settings opens the screen and shows the language options. 54 green.
The app now opens on a main menu instead of straight into the list:
- HomeScreen (mockup 02): sprout logo over a faint seed-glyph watermark, with
big "Your inventory" (→ /inventory) and "Open market" (Block 2, "coming soon")
destinations.
- AppDrawer (mockup 03): Inventory is live; the social items (market, profile,
chat, wishlist, following, settings) are shown disabled with a "coming soon"
tag so the roadmap is visible. Drawer is available on home and inventory.
- Routing: / → home, /inventory → list, /variety/:id → detail.
Tests: home shows the menu + navigates; drawer opens + navigates. quick-add flow
updated to go home → inventory. 50 green; Linux runs.