BACK_TO_BLOG
·3 min read·Building / Engineering

Community feed, live nav, and getting Kickstand into the App Store

46 commits on kickstand-web this week: ride posts hit the community feed, Live Nav got a heading-up chase cam and auto-reroute, and the App Store submission pile got worked down.

Kickstand week. 46 commits across the monorepo, no AI-product work worth reporting. The push was App Store submission prep and closing the gap between what the mobile app records and what the web feed shows.

Ride posts finally show up on the web

The biggest bug was structural. Mobile riders could tap "Post to community" on a completed ride (#218) — a new ride_posts table with denormalized stats and a downsampled path — and those posts appeared on the mobile feed. On the web Community page, they were invisible. The web feed never queried the table.

#233 adds ride_posts as a fourth feed source on the web, mirroring apps/mobile/lib/feed.ts, and fixes the same self-exclusion bug mobile had in #228: the owner set is now [viewer, ...followed] for every source, with no zero-follows early return. If you post a ride, you see your own ride. Should not have taken two PRs to figure that out.

Related: #222 found that free rides and plan-tagged runs were being dropped from the profile entirely — fetchMyRuns filtered null-poi rows and fetchRideStats only aggregated route_runs. Rides looked lost. They weren't; the queries were just wrong.

Live Nav is actually usable now

The nav map shipped flat, north-up, with follow-me off by default. Riders reported it as "the side view." Fixed in three passes:

  • #201: heading-up 3D chase cam driven by animateCamera — GPS course, forward pitch, centered ahead of the puck.
  • #202: off-route detection using true perpendicular point-to-polyline distance (not nearest vertex), with an 8s reroute cooldown.
  • #226: the same chase cam applied to free rides and Track-a-ride, not just Live Nav.

Also #220: expo-audio was ducking the rider's music on the first voice prompt and never un-ducking, because setIsAudioActiveAsync(false) was never called. Held one duck across back-to-back prompts, released after 200ms of quiet. And #223 raised the crash-detection impact threshold from 4g to 6g plus a corroboration window — potholes were tripping the SOS countdown.

App Store submission grind

Guideline 1.2 (UGC) needed report-content + block-user before submission. #207 added the moderation foundation: polymorphic content_reports, user_blocks, a report_content RPC, and feed filtering on both platforms. #232 closed the last gap — riders couldn't delete their own ride posts.

5.1.1(v): #191 shipped in-app account deletion. Web server action's core extracted into lib/profile/delete-account.ts, exposed as a Bearer-authed POST /api/account/delete the mobile Settings screen calls.

Twilio toll-free verification needed opt-in proof for the emergency SMS program. #204, #186, #215, and #216 built the double opt-in flow, the /legal/sms page, an inbound keyword classifier, and — after the first submission — the corrected screenshot showing the double opt-in UI. Carriers want to see the consent screen, not read a description of it.

#185 fixed the actual "app closes on launch" crash: EXPO_PUBLIC_* values only lived in the local .env, so preview/production builds bundled a Supabase client with no URL. eas.json embeds them at build time now.

The through-line

None of this is glamorous. Half the PRs are one-line fixes for assumptions that were wrong. But the work of shipping consumer software is exactly this — the query that dropped null rows, the audio session that never released, the crash threshold that was too sensitive, the screenshot that was out of date.

Next week: the Live Activity widget scaffolding from #230 turns into a real lock-screen turn-by-turn card.