docs: B1 (photo experience v2) contract frozen — UI-SPEC §11 (galleries, photo flip, fullscreen viewer, repeat rotation), DATA-MODEL B1 additions; PLAN/BACKLOG: B1 pulled forward before Phase 4
This commit is contained in:
@@ -4,7 +4,7 @@ Glenn's "later versions" idea queue. He drops ideas in conversation; they land h
|
||||
|
||||
## B1 · Photo experience v2 — multi-image galleries, photo swipe, fullscreen viewer
|
||||
|
||||
**Queued 2026-11-10** (during the P3 build; for the version after it).
|
||||
**Queued 2026-11-10** (during the P3 build; for the version after it). **PULLED FORWARD 2026-11-10 — building now, before Phase 4 (Glenn's call).** Spec frozen in `UI-SPEC.md` §11 + `DATA-MODEL.md` B1 additions; live tree untouched during the build.
|
||||
|
||||
Glenn's words: *"more pictures! As many pictures as we can get honestly. I'd like to swipe through them left and right like tinder does. One repeats, the image should change as to not tip me off."* · *"I'd like to tap the image to 'full screen' it so I can see it better."*
|
||||
|
||||
@@ -19,6 +19,8 @@ Scope sketch:
|
||||
Both are cheap once galleries exist; ask which was meant at spec time.
|
||||
5. **Gesture-map tension to resolve at spec:** today a tap on the card opens the dossier, and Glenn now wants tap = fullscreen; Tinder's model taps to advance photos. The exact zone map (tap-left/right = photo, center/ⓘ = dossier vs fullscreen) is a spec-time design session with Glenn. Dossier remains reachable from ⓘ and list rows either way.
|
||||
|
||||
> **Settled 2026-11-10 (Glenn, in chat):** gesture map = tap sides flip photos · tap center fullscreen · ⓘ details. Repeat handling = lead with a *different* photo each time (rotation). Frozen in `UI-SPEC.md` §11.
|
||||
|
||||
Not for P3 — nothing here blocks or alters the current phase. Forward-compatible: gallery segment indicators and the P3 card bands (§10.3) must share the card's top area; the stack/drag engine is untouched.
|
||||
|
||||
---
|
||||
|
||||
@@ -166,3 +166,13 @@ Semantics:
|
||||
- **`points` is computed, not stored** (deterministic from events + profile; a cache is allowed only if it never changes semantics).
|
||||
- **prefs:** `forge_mode` ('ondemand' default | 'strict'). Existing keys unchanged.
|
||||
- **Legacy deck semantics:** `/api/deck` (cursor paging; 7-day maybe re-admit) stays as a read-only fallback for debugging; sections are the production flow.
|
||||
|
||||
## B1 additions — frozen 2026-11-10 (photo experience v2)
|
||||
|
||||
- **New columns** (guarded, idempotent migrations in `db.init_db`):
|
||||
- `listings.imgs TEXT` — JSON array of gallery image URLs (page order, deduped by image id; entry 0 = the established primary). Absent/empty → single-image fallback to `img_url`.
|
||||
- `swipes.last_photo_idx INTEGER` — the photo index on screen when the card was struck (set by `POST /api/swipe`'s optional `photo_idx`; NULL when unknown). Drives UI-SPEC §11.5 rotation: a re-shown listing leads at `(idx+1) % n`.
|
||||
- **Importer:** carries `imgs` from hunt state (state gains `imgs[]` beside `img`; the hunt image tool fills both in the ONE page fetch it already makes — `img` = established primary, `imgs[0]` = that primary, remainder = gallery in page order).
|
||||
- **Media mirror:** `data/img/{id}_{n}.webp` per `imgs[n]` (polite ~1 req/s, skip-existing, resumable — same as P2); `GET /img/{id}?i=n` → mirrored file → CDN 302 → 404. Legacy `/img/{id}` unchanged.
|
||||
- **API payload additions:** deck/section items and list rows carry `n_photos` (int) and `last_photo_idx` (int|null); `POST /api/swipe` accepts optional `photo_idx` (int) and persists it.
|
||||
- **Rotation is deck-only** (list thumbs + dossier hero = photo 0).
|
||||
|
||||
@@ -59,6 +59,8 @@ Glenn's approved design (2026-11-10):
|
||||
|
||||
**Carried forward:** service-side OpenRouter key wiring at P4 — until then each forge's agent pass runs in mock mode and supersedes the live profile · wildcard pool hygiene (rare non-bike listings can surface) · early-ramp texture: dense nopes punish broad cells (year 2020+, 12k+, nb) — the Scouts read mid until the agent carves family-level exceptions · minor reload edge: an already-restruck second look can re-cycle on mid-section reload.
|
||||
|
||||
**Glenn's call (2026-11-10, late): B1 "Photo experience v2" pulled forward** — built next, *before* Phase 4 ("get that feature baked in before we proceed to phase 4"). Spec frozen in `UI-SPEC.md` §11 (galleries · tap-side photo flip · center-tap fullscreen viewer · fresh lead photo on repeats) + `DATA-MODEL.md` B1 additions. Rail side: hunt gallery capture + full-catalog backfill (Alex). App side: Torres on a fresh clone (`~/dev/bike-tinder-b1-build`) — the live tree and live DB stay untouched during the build; tests on port 9454 + DB copies.
|
||||
|
||||
## Phase 4 — Deploy + automate
|
||||
|
||||
- Podman compose stack at `/var/mnt/DATA/podman-stacks/bike-tinder/` + systemd unit (`Delegate=yes`) + in-stack Tailscale sidecar serving `https://biketinder.tailf35a34.ts.net`; PWA install on Glenn's phone
|
||||
|
||||
@@ -172,3 +172,54 @@ Top→bottom:
|
||||
|
||||
- Deck buffer source becomes `GET /api/section` (pinned order). All P2 wiring (optimistic swipe + retry queue, badge, SW) unchanged; bump the `sw.js` cache version.
|
||||
- Card payload additions the FE reads: `slot` ('new'|'return'|'wildcard'), `points`, `tank` {value, band}, `round_next` (1|2, for the SECOND LOOK band copy when needed).
|
||||
|
||||
## 11. Photo experience v2 (B1) — galleries, photo flip, fullscreen viewer
|
||||
|
||||
**Frozen 2026-11-10 (Glenn approved: gesture map + repeat rotation picked by him in chat).** Additive; §1–§10 remain in force except where explicitly superseded here (§2's "Tap … → dossier" and §3's "(or tap)" are retired — §11.4).
|
||||
|
||||
### 11.1 Galleries
|
||||
|
||||
- Every listing carries its full photo gallery: `imgs[]` (page order, deduped by image id; entry 0 = the established primary = today's `img`/`img_url`).
|
||||
- Every gallery image is mirrored locally (`data/img/{id}_{n}.webp`) and served by the app (`GET /img/{id}?i={n}`, `i` default 0; falls back into the §7.6 chain). The client never hotlinks directly.
|
||||
- Zero photos → §2 placeholder; one photo → single photo, no segments, no flip.
|
||||
|
||||
### 11.2 Photo flip (deck card — top card only)
|
||||
|
||||
- Tap LEFT third → previous photo; tap RIGHT third → next photo. **Clamp at the ends** (no wrap). Tap test = §2's (≤6px movement on pointerup); a tap inside the ⓘ button hits ⓘ, not the zones.
|
||||
- Segment indicator bars: one segment per photo along the top edge of the photo, centered, ~3px tall; current = solid white, others `rgba(255,255,255,.35)`; Tinder-style. (§10.3 amendment: when segments are present, band chips render at `top:22px`; no other change to §10.3.)
|
||||
- Flip is instant, commits nothing, locks nothing (§2 THE law). A drag that starts anywhere still drags for LIKE/NOPE/MAYBE — the zones apply to taps only.
|
||||
- Photo position is per-card-session state; a card re-shown fresh returns to §11.5's lead photo.
|
||||
|
||||
### 11.3 Fullscreen viewer
|
||||
|
||||
- Tap the CENTER third → fullscreen viewer opens on the photo currently shown.
|
||||
- Viewer: horizontal swipe = prev/next (clamp); pinch-zoom + double-tap = zoom (1×–~4×); segment bars stay visible; close via ✕ (36px, top-left) or pull-down (grabber or photo, `dy > 110` — the §3 idiom). Closing returns to the same card, same photo position; deck state untouched.
|
||||
- Also opens from the dossier hero (starts at photo 0). Same sheet geometry as the dossier (z-band 69/70), photo-first: no body, no actions row.
|
||||
|
||||
### 11.4 Doors (supersedes the old tap→dossier)
|
||||
|
||||
- ⓘ (top-right of card) → dossier (unchanged). The card face now owns: **sides = flip, center = fullscreen**; the "tap anywhere → dossier" gesture is RETIRED.
|
||||
- The dossier stays reachable from ⓘ and from list rows (§3/§4 unchanged).
|
||||
|
||||
### 11.5 Repeat rotation
|
||||
|
||||
- A listing presented again (second look / re-show) opens on a different photo than the last one viewed: lead = `(last_photo_idx + 1) % n`; no stored index → photo 0.
|
||||
- The index is persisted server-side at swipe-commit time (survives sessions and devices; works with §7.2's optimistic retry queue).
|
||||
- List thumbnails and the dossier hero keep photo 0 (the canonical shot) — rotation is deck-only.
|
||||
|
||||
### 11.6 Performance & invariants
|
||||
|
||||
- Preload the top card's next photo and the next card's lead photo; decode off the critical path; **no network on the swipe path** (all mirrored locally).
|
||||
- Stack metrics, `.topcard`, z-order law, stamps, undo, no-lock — all §2 laws untouched. Bands and segments coexist per §11.2.
|
||||
|
||||
### 11.7 Acceptance (B1)
|
||||
|
||||
Machine-checkable:
|
||||
- Gallery capture ≥1 photo for every listing with a page image; multi-photo listings show N segments = N photos; the mirror holds all gallery files; `/img/{id}?i=n` answers for every n.
|
||||
- Flip: left/right taps advance and clamp; a tap never commits (no stamp flash, no flight); drag-commit regression passes (fast-chain, no-lock, one `.topcard`).
|
||||
- Viewer: opens from center tap and dossier hero; swipe/zoom/close work; close restores the card and photo position.
|
||||
- Rotation: a re-shown card leads at `last+1`; persisted across page reload (server-side).
|
||||
- Fallbacks: 0-photo placeholder, 1-photo (no segments), broken file → §7.6 chain.
|
||||
- All §9/§10 acceptance re-run green.
|
||||
|
||||
Human: Glenn flips photos on his phone, opens fullscreen, and sees a fresh photo on a repeat — the B1 exit criterion.
|
||||
|
||||
Reference in New Issue
Block a user