Mobile app starter for Wasla, built with Expo + React Native.
This setup is intentionally minimal in app code, but opinionated in tooling so interns can focus on product features.
tamagui+@tamagui/config+@tamagui/lucide-iconsWhy: modern look, strong theming/tokens, reusable UI primitives, better visual ceiling than basic default UI.@tanstack/react-query+axiosWhy: reliable async state, caching, retries, and predictable API layer.zustandWhy: lightweight global state without boilerplate.react-hook-form+zod+@hookform/resolversWhy: fast forms with strict validation and clear type safety.react-native-reanimated+moti+@gorhom/bottom-sheetWhy: smooth, premium interactions for mobile UX patterns.socket.io-clientWhy: real-time booking updates from backend sockets.expo-camera+react-native-qrcode-svgWhy: QR scanning and ticket QR rendering.expo-web-browser+react-native-webviewWhy: payment redirect/checkout flows.expo-secure-storeWhy: secure token/session storage on device.expo-notificationsWhy: booking/ticket push notifications.dayjsWhy: simple date/time handling for ETD and schedule logic.
pnpm run start
pnpm run android
pnpm run ios
pnpm run webIf Metro cache causes stale warnings:
pnpm run start -- --clearThe app initializes a persistent Socket.IO connection to the central backend once the app bootstraps.
Environment:
EXPO_PUBLIC_CENTRAL_SERVER_URL(required; set it to your backend URL)
Connection details:
- Socket.IO namespace:
/realtime - Events in:
ping - Events out:
ready,pong
To verify locally:
- Start the backend:
pnpm -C central-server start:dev - Start the app:
pnpm -C wasla_mobile start - Confirm the app connects and receives
ready, thenpong(the app sendspingautomatically afterready).
Note for interns:
- Android emulator: use
http://10.0.2.2:4000when the backend runs on your machine. - iOS simulator: use
http://localhost:4000when the backend runs on your machine. - Physical device: replace with the dev machine LAN IP (or use an Expo tunnel), because
localhostpoints to the phone itself.
Connection state is available via useRealtimeStore (status, lastReadyAt, lastPongAt, lastPong, lastError).
pnpm run test:unit— compilestsconfig.unit.jsonhelpers and runsnode --test test/*.test.cjs.pnpm run verify:maestro-ids— ensures.maestro/smoke_login_booking.yamlstill matchestestIDs in source (CI-friendly guard).- CI: .github/workflows/mobile-ci.yml runs those on
main. - Sessions (no refresh API yet): docs/auth-sessions.md.
- iOS HTTP in dev only:
EXPO_PUBLIC_ALLOW_INSECURE_HTTP=true(see app.config.js); prefer HTTPS otherwise. - E2E smoke (Maestro device): .maestro/smoke_login_booking.yaml + docs/release-checklist.md.
- Sentry (optional): docs/observability.md.