Cross-platform location address book — React Native + Expo · iOS + Android.
PinBook is a location address book for your phone. Someone sends you an address on WhatsApp — instead of scrolling back through old chats weeks later to find it, you save it once: labeled, searchable, favorite-able. PinBook 2 is the same idea, rebuilt to work on both iPhone and Android, with sharing that reaches anyone — even someone who doesn't have the app at all.
Save — a location arrives on WhatsApp or Google Maps → share it to PinBook → it's saved with a name you choose.
Organize — search, favorites, categories, notes. Your places, your way.
Open — tap "Open With" → Google Maps navigation, or straight into Rapido as the destination.
Share — to a friend in-app (they get a notification), or to anyone by number (they get a link that opens in Maps).
Read these four stories — together they explain the whole product. No tech needed.
Ravi's cousin Meera messages him a location on WhatsApp: "Let's meet here on Saturday." Ravi taps Share → PinBook and names it "The Blue Door Cafe". Done — 5 seconds.
Two weeks later he wants to go back. He opens PinBook, types "cafe" in search — it's right there, with its address. No scrolling through old chats, no "which message was it?"
Shows: saving, labeling, search — the address-book core.
Saturday arrives. Ravi is at the cafe and books an auto. The driver asks, "Where to, sir?" Ravi opens PinBook → taps the saved place → Open With → picks Rapido from the list.
The cafe's location is now the ride's destination. No typing the address, no spelling it out, no "turn left at the big tree".
Shows: one tap from your list into any maps/ride app.
Ravi wants Priya to join them. Both have PinBook. Ravi opens the cafe → Send to Priya.
Priya's phone pings: "Ravi shared The Blue Door Cafe with you." She opens it, sees it on a map, and taps Save to my places — now it's in her list too, for next time.
Shows: in-app sharing between friends — instant, with a notification.
Ravi is at his cousin Meera's flat in Jubilee Hills and needs an auto. The driver doesn't have PinBook — and doesn't need it. Ravi taps Share by number, types the driver's number, sends.
The driver's WhatsApp pings with a link. He opens it → a simple page shows "Meera's flat — 7, Jubilee Hills, Hyderabad" with a map → he taps Open in Google Maps and drives.
No app download. No account. No friendship needed. And the link stops working after an hour.
Shows: sharing with ANYONE — even people without the app.
| Layer | Choice | Status |
|---|---|---|
| Framework | Expo (React Native) | ✓ Locked |
| Navigation | React Navigation | ✓ Locked |
| Local storage | Expo SQLite / MMKV | ✓ Locked |
| Auth (day 1) | Clerk — Gmail / email OTP now, phone OTP later | ✓ Locked |
| Backend (day 1) | Convex — reactive DB + TypeScript functions | ✓ Locked |
| Push (P2) | Expo Push Service | ✓ Locked |
| Maps (V3) | react-native-maps | ⚠ Pending (decision 3) |
| Share target | Share Menu (Android) + Swift ext (iOS) | ✓ Locked |
| Tier | Service | Job | Cost |
|---|---|---|---|
| Day 1 | EAS (Expo) | Cloud builds, store submission, OTA updates, code signing | Free |
| Day 1 | Expo Push | Push notifications, no FCM/APNs key juggling | Free |
| Day 1 | Sentry | Crash & error monitoring | Free tier |
| Day 1 | PostHog | Analytics + feature flags | Free tier |
| Launch | Vercel | Landing page + universal-link files | Free |
| Launch | Infisical | Secret keys vault (Clerk/Convex keys) | Free |
| Launch | GitHub Actions | CI — tests + auto builds | Free |
Deliberately not added: React Query (Convex does it), Firebase (replaced), second auth service (Clerk covers it).
Share from WhatsApp → PinBook in share sheet → parse → save.
Same share-target mechanism; resolves short links (maps.app.goo.gl) to coordinates.
"Open With" shares a clean geo:lat,lng URI → Rapido & maps apps appear as destination options. Proven approach, via Linking.
Sign up with phone + OTP (SMS auth = Clerk Pro, $20/mo) or email OTP / Google login (free). Profile: name, number, avatar. No passwords.
Sync contacts → match on hashed phone numbers → see friends on PinBook → send saved locations in-app with push.
react-native-maps pin preview on detail + add screens (replaces mock box).
Carried from v1, plus dark mode, a proper "no results" empty state, and user-defined categories (home/work/family are just suggestions).
After login, one card: "PinBook works from WhatsApp — try it now: open WhatsApp → share any location → pick PinBook." That's the whole onboarding. One moment, one nudge.
Client can only call your functions — never the database directly. Every function checks ctx.auth (who is calling) before touching data.
Why safer than RLS for us: there's no "query the table directly" path to accidentally leave open. Follow the docs' security checklist function-by-function.
· Clerk bot protection — enable in dashboard (blocks scripted signup abuse).
· EAS code signing — keystores/provisioning stored encrypted.
· Sentry alerts — notified on crash or suspicious spike.
· Convex schema validation — typed, validated data.
· Hashed phone matching — no raw numbers stored for contact matching.
| Service | Free tier | When you'd pay |
|---|---|---|
| Clerk | 50K users/app, unlimited apps | $20/mo — SMS/phone OTP, MFA, remove branding |
| Convex | $0 — storage, search, crons, auth | $25/dev/mo — backups, custom domain, SOC2 |
| EAS / Push / Sentry / PostHog / Vercel / Infisical / CI | $0 | At scale |
Convex document database. 6 collections. Every document gets _id + _creationTime automatically. 🔑 PK · 🔒 UNIQUE · ⚡ INDEXED · ⚠ REQUIRED
connections keeps pairs ordered (userA < userB) so each friendship is one row. ③ A share references a location, but the receiver sees a copy in their UI — the location itself stays private to its owner. ④ Push tokens pruned via lastActiveAt. ⑤ phoneHash is a salted HMAC-SHA256 (server secret) — raw hashes of phone numbers can be brute-forced; salted ones can't.· A location belongs to one user. Every saved place carries an ownerId — "whose list is this in?". A user can have many locations; a location has exactly one owner. That's the 1─N.
· Your phonebook becomes matching data. When you sync contacts, each number is hashed and stored in contacts. If a hash matches a users.phoneHash — that contact is a PinBook user! No raw numbers ever stored.
· Matches become friendships. A match doesn't auto-connect you — it creates a connection (pending → accepted). Only accepted connections can share. Two users, one row, ordered so it's unique both ways.
· Shares are the bridge. When you send a location, a share row records: which location, from whom, to whom, when, and whether it's been seen. The location itself never leaves your list — the receiver gets a copy via the share.
· Devices carry the alert. Each of your devices registers an Expo push token. When a share arrives, the app looks up the receiver's devices and pings them.
Step 1 · Ravi saves a place from WhatsApp.
Ravi taps "Share" on a WhatsApp location → PinBook opens → app parses the link → Convex creates:
Step 2 · Ravi sees his list. Home screen calls getMyLocations() — Convex checks ctx.auth = Ravi → returns ONLY rows where ownerId = Ravi. Priya's cafe appears.
Step 3 · The app knows Priya is a friend. Earlier, Ravi synced contacts → a contacts row with Priya's hashed number matched Priya's users.phoneHash → a connections row was created (accepted). That's why Priya shows up in Ravi's "Send to" list.
Step 4 · Ravi sends the location. He taps "Send to Priya" → Convex creates:
Step 5 · Priya gets pinged. Convex looks up Priya's devices → sends Expo Push → her phone buzzes: "Ravi sent you a location".
Step 6 · Priya opens it. getSharedWithMe() returns the share → she sees "The Blue Door Cafe" as a copy in her list. seenAt is stamped. Ravi's original stays his — she can't edit it, only view.
Step 7 · She opens it in Rapido. "Open With" → clean geo:lat,lng URI → Rapido sets the destination. Ride booked. 🛵
Security at every step: every function asks "who's calling?" — Ravi's queries can't see Priya's locations, and Priya can't see Ravi's other places. The share is the only bridge, and it's explicit.
Like Google Pay: share with anyone by number — no friendship needed. And like Uber's trip link: people without the app still get the location, in one tap.
Search by name → shows your contacts who have PinBook.
Search by number → if the number is saved in your contacts, shows the person's name; if it's a PinBook account, shows a badge; if neither — shows the raw number.
No friendship required. One-way share, like sending money on UPI. Perfect for the auto-driver case: type his number → location goes straight to him.
The link opens a simple web page with the location name, address and map. One tap → Google Maps. Or book a ride from there. No app, no account.
Delivery: WhatsApp (free) by default; SMS as backup — decision later. The web page is a tiny T3-stack app on Vercel, sharing the same Convex backend.
Shares from anyone — friend or stranger — land in a Received inbox: "Ravi shared The Blue Door Cafe with you". Actions: Open · Save to my places · Block sender. No connection needed to receive — consistent with the UPI model. If spam ever appears later: a "contacts only" toggle, one setting.
· One-way & single-share — the driver sees only that one location, never your list.
· Expiring links — the page dies after X hours, or when you revoke it.
· Non-spammy discovery — a small "Get PinBook" footer on the page; the sender chose to share, so it's permission-based.
· Rate limits + block — no blasting numbers, recipients can block.
| Field | Type | Why |
|---|---|---|
| token | string 🔒 | unique, unguessable — the secret inside the link |
| location | embedded | label + address + lat/lng snapshot |
| createdBy | id | → users · who made the link |
| recipientPhone | string? | "sent to" tracking |
| expiresAt | date? | temporary shares stop working |
| revokedAt | date? | sender kills the link anytime |
| openedAt / views | date / number | did they open it? tap tracking |
Ravi is at his cousin Meera's flat and books an auto. The driver asks, "Where to, sir?"
1. Ravi opens PinBook → taps Share by number → types the driver's number → taps Send.
2. The driver's WhatsApp pings with a link.
3. He opens it → a simple page shows: "Meera's flat — 7, Jubilee Hills, Hyderabad, Hyderabad" with a small map.
4. He taps Open in Google Maps → navigation starts. Done!
No app download. No account. No friendship. And in one hour, the link stops working on its own — Ravi's other places stay private.
| Android | iOS | |
|---|---|---|
| Share target (F1 / F2) | ✓ Libraries | ⚠ Native Swift ext |
| Deep links (F3) | ✓ | ✓ |
| Push notifications (V2) | ✓ via Expo Push | ⚠ APNs + $99/yr dev account |
| Testing & release | ✓ Emulator | ⚠ TestFlight + review |
Expo scaffold → Clerk login → Convex → screens → F1 Android share → F3 Rapido → polish (dark mode, empty states). Est: 2–3 weeks.
Swift share extension + App Group → TestFlight → App Store. Est: 2–4 weeks.
Contacts matching (hashed) → F5 in-app send + Expo Push → shareLinks for non-users (web page). Est: 3–5 weeks.
Real map previews, share-to-anyone via link, web companion (T3 stack candidate). Ongoing.
· Locations saved per user per week — is it useful?
· Share completion rate — sent → opened.
· Link open rate — shareLinks.
· Weekly active users — close-circle growth.
Review gate: if a phase's feature isn't used, cut it — don't polish it.
| Risk | Mitigation |
|---|---|
| iOS share extension (P1) | Thin Swift ext; early TestFlight; paste-link fallback exists |
| Rapido geo-URI parsing changes | Share-sheet fallback always available |
| WhatsApp / Maps link parsing breaks | Paste-link manual entry (in UI) + parser tests |
| Phone-OTP needed sooner | Known cost ($20/mo) — decision gate at first outside user |
| Contacts-matching privacy | Salted hashes + clear in-app explanation |
| Expo / Convex breaking changes | Pin SDK versions |
Android-first · Google Maps · name "PinBook" · phone OTP later. Any can change — but the plan proceeds on defaults.
1 · iOS timing: Android-only first, or both at once? (iOS costs $99/yr + share-extension work)
2 · Map provider: Google Maps (needs API key) vs Apple Maps on iOS.
3 · App name: PinBook? JagahBook? PinBook 2?
4 · Phone OTP now or later? If day-one must → Clerk Pro ($20/mo) or revisit Appwrite free.