Mobile App
The Omega Shop mobile app is an Expo (SDK 54) React Native client that talks to the Laravel REST API at /api/v1.
Overview
The app lives in the mobile/ directory of the Omega Shop repository. It uses expo-router for file-based navigation, NativeWind (Tailwind) for styling, TanStack Query for server state, and Zustand for client state.
- Catalog — Featured products on the home tab, product search with category and sort filters, category listings, product details, ratings, and seller pages.
- Cart & Wishlist — Server-side cart with a live badge on the Cart tab, and a wishlist with move-to-cart.
- Checkout — Select a billing detail (and a delivery address when needed), then pay with the Stripe payment sheet.
- Orders — Order list with status filter, order details, and invoice receipt links.
- Account — Registration, login, password reset, profile and password updates, billing details, and delivery addresses.
- Session Persistence — The Sanctum token is kept in the device secure store and restored on launch.
The app is API-only: it has no database or business logic of its own. All data comes from the endpoints described in the Architecture guide. The app declares no push-notification code; @pusher/pusher-websocket-react-native is installed but not used anywhere in app/ or src/.
Prerequisites
- Node.js and npm
- A running Omega Shop backend (see Getting Started) reachable from your device or simulator
- Xcode (iOS simulator) or Android Studio (Android emulator), or the Expo Go app on a physical device
- A Stripe publishable key, if you want to test payments
Installation
cd mobile
npm install
A dry run of npm install against the current package-lock.json completes without peer-dependency errors, so --legacy-peer-deps is not required.
Environment Variables
Copy .env.example to .env inside mobile/ and fill in the values. Variables prefixed with EXPO_PUBLIC_ are inlined into the app bundle, so never put secrets in them.
- Name
EXPO_PUBLIC_API_URL- Type
- string
- Description
Base URL of the Laravel API including the
/api/v1prefix. Read insrc/api/client.ts; falls back tohttp://localhost:8000/api/v1when empty.
- Name
EXPO_PUBLIC_STRIPE_PUBLISHABLE_KEY- Type
- string
- Description
Stripe publishable key passed to
StripeProviderinapp/_layout.tsx. Without it, the provider is not mounted.
Running the App
Against a local backend
Start the backend, then point the app at it:
# In the repository root
php artisan serve
# In mobile/
npm start
localhost only works from a simulator or emulator on the same machine. On a physical device, set EXPO_PUBLIC_API_URL to an address the device can reach (for example your computer's LAN IP, with php artisan serve --host=0.0.0.0). Restart Expo after changing .env.
app.json allows plain HTTP for development: iOS has NSAllowsLocalNetworking plus an insecure-HTTP exception for omega-shop.test (and subdomains), and Android sets usesCleartextTraffic.
Expo Go vs development build
npm start
Expo Go is enough for browsing, cart, and account features. Payments are not available there, because the Stripe native module is not bundled in Expo Go. The app detects this with Constants.appOwnership === "expo", skips loading @stripe/stripe-react-native, and checkout shows an "Unavailable" alert asking for a development build. npm run ios and npm run android run expo run:ios / expo run:android, which compile a native development build (an ios/ project is present in the repository).
Authentication
- On launch,
AuthProvidercallsloadStoredAuth, which reads theauth_tokenentry fromexpo-secure-storeand, if present, fetchesGET /auth/user. - The Axios client in
src/api/client.tsaddsAuthorization: Bearer <token>to every request. - The
(tabs)layout redirects unauthenticated users to/(auth)/loginonce loading finishes. - Login and registration post to
/auth/loginand/auth/registerwith adevice_name; the returned token is stored in the secure store and the user in the Zustand auth store. - Any
401response deletes the stored token. A failed restore on launch clears the session. - Logout calls
/auth/logout, ignoring API errors, and clears the local session.
The API module also defines password reset (/auth/forgot-password, /auth/reset-password) and a social-login call (/auth/social-login/{provider}), but no screen currently uses social login.
Payments
Checkout (app/checkout/index.tsx) posts the selected billing_detail_id and optional delivery_address_id to POST /checkout. The response carries a client_secret, which is used to initialise and present the Stripe payment sheet. After a successful payment the app calls GET /checkout/{purchaseId}/confirm to verify the order. Google Pay is enabled through the @stripe/stripe-react-native config plugin in app.json.
Building and Updates
app.json configures expo-updates with "runtimeVersion": "exposdk:54.0.0", so over-the-air updates are matched to builds on Expo SDK 54. The app uses bundle identifier and Android package com.omega.shop.
The repository contains no eas.json, so EAS build profiles and the update channel and URL are not defined in code yet. Run eas build:configure (and eas update:configure) to generate them before using eas build or eas update.
Testing
The mobile project has no test script and no test files; package.json only defines start, android, ios, and web. Type checking is available through TypeScript (npx tsc --noEmit).
Project Structure
mobile/
├── app/ # expo-router routes
│ ├── _layout.tsx # Providers (Stripe, Query, Auth) and root stack
│ ├── (auth)/ # login, register, forgot-password
│ ├── (tabs)/ # index, search, cart, purchases, profile
│ ├── product/[slug].tsx
│ ├── category/[slug].tsx
│ ├── seller/[id].tsx
│ ├── checkout/index.tsx
│ ├── purchase/[id].tsx
│ ├── wishlist.tsx
│ ├── billing-details/index.tsx
│ └── delivery-addresses/index.tsx
├── src/
│ ├── api/ # Axios client and per-resource modules
│ ├── components/ # ProductCard, EmptyState
│ ├── hooks/ # useRefresh
│ ├── providers/ # QueryProvider, AuthProvider
│ ├── store/ # Zustand: auth, cart badge
│ ├── types/
│ └── utils/ # format helpers
├── app.json # Expo config
├── global.css # NativeWind entry
└── ios/ # Native iOS project
TanStack Query is configured with a 5-minute staleTime and 2 retries. The auth user and the cart badge count live in Zustand stores.