
Locked In — Social Fitness App
A squad-based workout tracker my friends actually installed.

Watch it run
02 TAKESEvery feature, step by step
05 FEATURES · 11 SCREENS · OPEN ONEThe week, and who won it
02 STEPSThe whole point of the app is the table at the bottom of the feed. Training alone is easy to skip; being seventh is not.
Stories, and who watched them
02 STEPSA session posts as a story that expires. It is the part people actually open, so it carries the likes, the comments and — if it is yours — the list of who has seen it.
Two ways to log a session
03 STEPSManual entry is where a fitness app dies. The first path is a screenshot and nothing else; the second is for the sessions no watch recorded.
The squad
02 STEPSA closed group rather than a network: you see the people you follow, and nobody else sees you.
Talking about it, and being told off
02 STEPSThe two things that bring people back: the group chat, and the app noticing when you have done nothing.
Every person in these captures is invented. Locked In is a social app, so its database is mostly other people — my friends’ names, their photographs and their actual sessions — and none of them agreed to appear on a public portfolio. So the squad here is made up, and so is everything attached to them: the workouts, durations, calories, points, leaderboard positions, comments, chat messages and notifications. The avatars are generated from each invented name’s initials rather than sourced from anywhere, and the images on the sessions are drawn from that session’s own numbers rather than being photographs of anyone. The uploaded watch screenshot is generated too, and says so on its face. Nothing came out of the real project: the web build under capture is compiled with synthetic Supabase credentials, the harness replaces fetch before any application code runs so the app issues exactly the queries it always does and gets invented rows back, and every request to a real Supabase host is intercepted and counted — a run that let one through would say so. Gemini is not called either; the extraction you see running is the app’s real code path against a stubbed reply. My own name is the one real thing left, and only because it is already the title of this site. The repository is not modified at all, and beyond the invented data nothing is retouched — every screen, control and state is the app exactly as it runs.
Training alone is easy to skip, so I built the accountability in. Locked In is part workout log, part social feed: you post a session, your squad sees it, and the leaderboard keeps everyone honest. Shipped as a real Android APK through EAS with a web build on Vercel.
How it’s put together
Built with
- PERIOD
- April — May 2026
- ROLE
- Solo — design, build, ship
- SOURCE
- View on GitHub ↗
The hard parts
08 NOTESAround 5,000 lines of TypeScript across twelve modules — feed, profile, posting, search, inbox, chat, notifications, auth — on Supabase Auth, Storage, Realtime subscriptions and row-level security.
Gemini vision reads a workout screenshot straight from your smartwatch app and auto-fills activity, distance, duration, pace and calories. No manual entry, because manual entry is where a fitness app dies.
Push notifications are sent by the database, not the app: a Postgres trigger calls the Expo Push Service through pg_net, so a like or a comment notifies whether or not anyone has the app open.
Web push is deliberately not implemented. It is fragile enough on iOS Safari that shipping it would have meant supporting something that quietly fails for half the squad.
Schema changes are additive on purpose. Old APKs are already on people’s phones and will ignore a new column, but a rename or a drop breaks them — so nothing gets renamed once it has shipped.
Realtime is opt-in per table through the supabase_realtime publication, so a new table has to be added deliberately rather than silently broadcasting.
Distributed as an Android APK via EAS Build, with the web version auto-deploying to Vercel on push to main.
Written up with developer onboarding docs covering environment setup, release builds and schema-migration practice, so friends could contribute without me in the room.
Want the parts that aren’t on this page — the architecture arguments, the things that broke, a live walkthrough?