5
Plans
5/5
Plans shipped
97
Photos served locally
389
Pexels URLs → /curated/photos
📋 Kickoff state
Phase 39 = Demo Quality Sweep (inserted before original Phase 39). Phase 40 = Singles + Vault community-voice editorial (was 39). Phase 41 = Final Polish (was 40). Approach: do quality work BEFORE Singles + Vault add new entities that would need cross-link/empty-state/photo audit anyway. Founder approved: Phase 39 scope, Pexels-over-Unsplash pivot, "Nabe Pulse" row name (was "Stories"), Variant B-Bubble bubble sizing. Pending founder calls: variant C-Side (side-by-side) layout option, Q4/Q5/Q8 from sweep prep (hero+contribs scope, country-specific WC fans, batch-by-file vs all-at-once).
🔗 Links
Live + mockups
Planning
- •
.planning/ROADMAP.md— Phase 39/40/41 renumbered - •
.planning/TODOS.md— feed diversity audit + "New Here" bug - •
src/data/curatedPhotos.ts— Pexels photo library
📦 Plans
| Plan | What | Status |
|---|---|---|
| 39-01 | Photo curation system — Pexels photo IDs per content category. src/data/curatedPhotos.ts with deterministic picker. |
✅ Shipped · 46 cats / ~210 IDs |
| 39-02 | Photo replacement sweep — picsum URLs across mockEvents (154), mockPosts (123), mockListings (52) + 9 misc data files. 369 swaps shipped. | ✅ Shipped |
| 39-02b | Local photo download — 97 Pexels JPEGs in public/curated/photos/. Kills Cloudflare 429 rate-limiting. getCuratedPhoto() + new picsumSeedToCurated() helper return local paths. WC Hub V2 dynamic seeds now routed through the helper. |
✅ Shipped |
| 39-03 | Nabe Pulse row — replaces NeighborhoodStoriesRow. C-Split-Preview: pulsing brand-gradient header + two preview cards (stories left, live activity right). Tap right → NabePulseDrawer lists all check-ins/recs. CheckIn/QuickRec removed from long feed. Stories capped at 4. |
✅ Shipped |
| 39-04 | Feed diversity reorder — Flooding alert moved from visible position 2 → 8; Found-dog from 4 → 11. First 8 W'burg posts now diverse (safety, help, event, marketplace, tip, agent-insight, help, safety). | ✅ Shipped |
| 39-05 | "New Here" guide bug — nurturing-card CTA now invokes onAction(card.ctaAction) and AppShell routes to calendar drawer / 'me' tab / 'explore' tab based on the action. Pseudo "Done!" confirmation removed. |
✅ Shipped |
✅ Decisions locked
- D-01 — Pexels over Unsplash.
source.unsplash.comdeprecated June 2024 (503 confirmed). Pexels exposes numeric photo IDs directly, license is free for any commercial use with attribution appreciated-not-required, URL pattern is stable. URL:images.pexels.com/photos/{ID}/pexels-photo-{ID}.jpeg?w=W&h=H&fit=crop&auto=compress. - D-02 — Deterministic picker per seed.
getCuratedPhoto(category, seed)hashes the seed to pick stably from the category's photo array. Same post id always renders the same photo. - D-03 — Phase 39 = quality sweep, NOT singles/vault. Singles + Vault pushed to Phase 40, Final Polish to 41. Founder rationale: do photo audit + feed reorder + Stories row BEFORE adding new entities that would need the same passes.
- D-04 — "Nabe Pulse" is the row name. Captures liveness + brand resonance (Living Skyline logo has a pulse dot). Replaces "Stories" naming. Component file rename:
NeighborhoodStoriesRow.tsx→NabePulseRow.tsx. - D-05 — Variant B-Bubble picked (same size as story bubbles). 48px circle + corner badge (pin = live check-in, star = quick rec) + venue name as label. Color-coded ring system (gradient = story, emerald-cyan = check-in, amber-orange = quick rec). Side-by-side layout option still being explored (see updated mockup).
- D-06 — Backup tag before sweep.
git tag pre-photo-sweepat HEAD before plan 39-02 starts. Easy rollback if result is wrong. - D-07 — Batch by file with founder checkpoints. Order: events (158 refs, biggest impact) → posts (123, most visible) → listings (52). Three commits. Founder verifies on iPad after each before next batch.
⬜ Awaiting iPad verification
- V1 — All photos load reliably on iPad. 97 JPEGs served from same-origin
/curated/photos/. No more Cloudflare 429s. Open feed → scroll first 8 posts → confirm every image renders. - V2 — Nabe Pulse row renders correctly. Pulsing brand-gradient dot + "Nabe Pulse" header above feed. Left card = 3-avatar story peek + "+1 more sharing now". Right card = live check-in preview + "+N more live now". Tap left → story viewer. Tap right → Nabe Pulse drawer with check-ins + recs listed.
- V3 — Feed first 8 is diverse. Open W'burg feed. Posts 1-8 should be: power-flicker (safety), lost-cat (help), block party (event), free moving boxes (marketplace), parking hack (tip), Q2 market update (agent-insight), McCarren kid playdate (help), flooding alert (safety). No two adjacent of the same type.
- V4 — "New Here" nurturing cards navigate. Tap "RSVP to something" card → "See Events" CTA → opens calendar drawer. Tap "Complete profile" → "Edit Profile" → switches to Me tab. Same for the 5 other action types.
- V5 — Photo content match. Some images may not match seed perfectly (remap from missing → downloaded). Skim feed + WC Hub for any laughably wrong photo. Flag specific IDs to swap; non-blocking.
Rollback tag: pre-photo-sweep still valid · v1.2-warm-palette for full Phase 39 rollback.
⚖️ Known compromises
- 97 photos, not 210. Cloudflare rate-limited the bulk download (HTTP 429 from second batch onward). Missing IDs remapped to downloaded equivalents from the same category (or fallback pool). Result: every image loads, some categories repeat photos. Acceptable for 1-2 week demo window. Follow-up: run the cookie-aware downloader off-hours to fill the remaining 114 IDs and re-run remap.
- Some content mismatches. Where a missing ID's category had zero downloaded photos (e.g. block-party), the remap picked from neighborhood-walking or any downloaded pool — so a "block party" post might render a Tokyo neon street. Visible during V5 check. Easy individual swap if any specific photo offends.
🏗️ Architecture (already shipped)
src/data/curatedPhotos.ts
├── PhotoCategory union (75 categories defined, 36 populated)
├── CURATED_PHOTOS: Partial<Record<PhotoCategory, readonly string[]>>
│ (Pexels numeric IDs per category, 3-6 per cat)
├── getCuratedPhoto(category, seed, width, height) — deterministic picker
├── curated(category, seed, w, h) — convenience helper with defaults
└── Fallback: 'neighborhood-walking' (3 safe NYC brownstone IDs)
Direct image URL:
https://images.pexels.com/photos/{ID}/pexels-photo-{ID}.jpeg
?w=W&h=H&fit=crop&auto=compress
📜 Commits this phase (newest at top)
feat(39-02b) photos served locally — kills Pexels Cloudflare 429s (97 JPEGs, 19MB)fix(39-05) nurturing-card CTAs now navigate — wire onAction to AppShellfix(39-04) feed diversity — break up safety + help clusters at top of feedfeat(39-03) Nabe Pulse row — split-preview replaces NeighborhoodStoriesRowfix(39-02) sweep remaining picsum URLs across 9 missed mock data filesdocs(39-03) lock C-Split-Preview as final variant + 'Nabe Pulse' header patternfeat(39-02) photo sweep — mockListings.ts (52 picsum → Pexels curated)feat(39-02) photo sweep — mockPosts.ts (121 picsum → Pexels curated)feat(39-02) photo sweep — mockEvents.ts (154 picsum → Pexels curated)feat(39-01) curated photos — 46 categories populated (~210 IDs)docs(39) refine stories row mockup — B-Bubble + B-Stack alt; ROADMAP renumberdocs(39) stories row consolidation mockup — 3 variants + decision matrix