Commit Graph
9 Commits
Author SHA1 Message Date
KrikoriosandClaude Opus 5 4e0a7cca51 Fix map failing to render, dedupe expo-crypto
The home screen passed its FlatList header as `ListHeaderComponent={() =>
(...)}`. VirtualizedList renders a function-valued header prop as
`<HeaderComponent />`, so a fresh arrow function on each render is a fresh
element type: React unmounted the entire header subtree — MapView included —
and mounted a new one. Home re-renders several times on mount (useFetch
loading->data, useUserLocation pending->granted, session resolve), and
recreating the Android GoogleMap surface each time left it grey with the
Google logo and tiles that never finished loading.

Pass the header as an element instead so the type stays stable and MapView
mounts once. The same `() => (...)` pattern in ListEmptyComponent here, and in
rides.tsx and confirm-ride.tsx, cost needless remounts of list chrome and
DriverCards; fixed alongside.

Also pin expo-crypto to ~13.0.2. It was ^57.0.1 — a wrong-major native module
for SDK 51 that nothing in the app imports. npm hoisted it to the top level
and gave expo-auth-session a nested 13.0.2 to satisfy its ~13.0.0 constraint,
leaving two copies of one native module in the tree for autolinking to choose
between.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-24 00:32:34 +03:00
KrikoriosandClaude Opus 5 59e336c23d Fix home map and location, add service selector
The Android map never drew because mapType was "mutedStandard", an Apple
Maps value. Android's MapManager looks the name up in a fixed table and
unboxes the result into an int, so an unrecognised value threw a
NullPointerException in the native view manager before any tile
rendered. Android now gets "standard" plus a customMapStyle that mutes
POI and transit labels, since showsPointsOfInterest is iOS-only too.

Location hung indefinitely: getCurrentPositionAsync was called with no
accuracy and no timeout, so it waited for a GPS fix that never arrives
indoors or on an emulator with no mock location. useUserLocation now
takes a cached fix first for an immediate render, caps the precise
reading at 15 seconds, and checks device location services separately
from app permission. Reverse geocoding moved off the critical path so a
failed lookup costs the address label rather than the coordinates. The
single boolean became five states, each with its own notice and either a
retry or a settings shortcut, since retrying a hard denial does nothing.

Map also no longer deletes itself when the driver fetch fails or the
location is still pending: drivers are an overlay, and calculateRegion
already falls back to Beirut.

Adds a four-tile service selector above Recent Rides - Car, Moto,
Courier, My Car - with the choice held in useServiceStore for the
booking flow to read. English only for now; the intended Arabic names
are recorded in constants/services.ts for the language pass. Selection
styling matches the role picker on sign-up so the two read as one
control.

app.config.js layers the Google Maps key and expo-location permission
strings onto app.json. No effect under Expo Go, which ignores native
config, but required for the first EAS build.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-23 23:34:04 +03:00
KrikoriosandClaude Opus 5 bc23c94ea2 Add remember-me and OTP autofill, fix session persistence
Sign-in gains a "Keep me signed in" checkbox: checked issues a 30-day
token and prefills the address next launch, unchecked drops the session
to 12 hours and forgets the address. The TTL is chosen server-side in
the login route.

Emailed codes are now reachable without retyping. OtpField opts into the
iOS one-time-code keyboard suggestion and raises a paste chip when the
user returns from Gmail with a code on the clipboard. The mails put the
code first in the subject and body, which is what makes Gmail render its
"Copy code" notification action at all.

Fixes found along the way:

- Session was wiped on every launch. decodeJwtExp used atob, which
  neither RN 0.74 nor Expo SDK 51 defines, so it threw, returned null,
  and the caller read that as "expired" and deleted the token. Replaced
  with a dependency-free base64url decoder, and restore now only
  discards a session it can prove is expired.
- Verification and reset codes counted attempts but never enforced them,
  leaving a 6-digit code open to unlimited guessing. Both routes now
  charge the attempt before comparing so concurrent guesses can't race
  past the cap of five, and compare in constant time.
- A wrong verification code showed the "Verified" success screen:
  onModalHide fired unconditionally, so the failure state advanced the
  flow. Only an explicit "verified" state does that now.
- fetchAPI discarded the server's error body, so the UI substring-matched
  synthetic status strings and showed "Could not sign in" for everything.
  It now throws ApiError carrying status and the server's message.
- Login answered a missing account faster than a wrong password; it now
  runs the same scrypt work either way.
- Blank email or password is caught client-side instead of surfacing as
  an opaque 400, and a failed attempt only clears the password on a 401.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-23 23:33:51 +03:00
KrikoriosandClaude Fable 5 eceb6b45d5 Fix SMTP delivery, add password reset and user deletion
SMTP:
- Add connection/greeting/socket timeouts so a stalled Gmail
  connection no longer hangs sign-up
- Wrap sendMail in try/catch and fall back to logging the code
- Derive secure from port (465 implicit TLS vs 587 STARTTLS)
- Strip whitespace from the Gmail app password
- Document SMTP_HOST/SMTP_PORT in .env.example and environment.d.ts

Password reset (new):
- POST /(api)/auth/forgot-password emails a 6-digit code and does
  not reveal whether the address is registered
- POST /(api)/auth/reset-password validates the code, sets the new
  password, verifies the email, and signs the user in
- password_reset_codes table added to seed-db.mjs
- "Forgot password?" flow on the mobile sign-in screen

User deletion (new):
- DELETE /(api)/admin/users/[id], owner-only, blocks self-deletion
- Delete button with confirmation on the dashboard Users page

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-23 22:41:41 +03:00
Krikorios a0b297285a Add self-hosted auth, admin API, and owner web dashboard
- Replace Clerk with self-hosted JWT auth (register/login/verify, bcrypt
  passwords, Gmail OTP with console fallback)
- Add lib/db.ts pg pool + transaction helpers; seed script migrates legacy
  Clerk-era schema (drop clerk_id, enforce UUID ids and unique email)
- Add owner-gated admin API: stats, users, drivers CRUD, rides
- Add dashboard/ Vite React owner dashboard (login, overview, users,
  fleet, rides) with dev-server proxy to avoid Expo CORS middleware
- Add scripts/set-owner.mjs for role management
2026-08-23 16:38:41 +03:00
Krikorios fbe92c9d16 Add rider/driver role selection after sign-up
- users.role column (+ migration in seed script)
- /(api)/user: GET role by clerkId, PATCH to set role
- Role selection screen; drivers get placeholder driver-home
- Root index routes by role; hardened location handling on home
2026-08-22 16:13:59 +03:00
Krikorios fdc1664cce chore: drop upstream README-update workflow 2026-08-22 16:04:02 +03:00
Krikorios c16c335481 docs: replace Stripe setup with Areeba in README 2026-08-22 15:59:45 +03:00
Krikorios 62dc5e9d53 Rebrand to Waseel, swap Stripe for Areeba, add phone-ready auth and DB seed
- Rename app: Waseel (name, slug, scheme waseel://, com.waseel.app ids, splash)
- Payments: replace Stripe with Areeba hosted checkout (create/verify API routes,
  lib/areeba.ts, WebBrowser-based payment flow)
- Maps: migrate address autocomplete to Places API (New), drop legacy library
- Web support: map stub for web (native maps are iOS/Android only)
- Auth: keep email + Google OAuth; fix OAuth redirect for Expo Go
- Add scripts/seed-db.mjs (schema + Lebanese driver seed)
- Pin Expo SDK 51 compatible package versions
2026-08-22 15:53:41 +03:00