OutWork is a mobile app for discovering and reviewing remote-work-friendly venues β cafes, co-working spaces, libraries, and more. Users can explore spots on a map, check real-time crowd levels, write reviews, and earn XP and badges through a gamification system.
This is an Expo project built with React Native, targeting iOS and Android.
| Layer | Technology |
|---|---|
| Framework | Expo (React Native) with Expo Router (file-based routing) |
| Language | TypeScript (strict) |
| Backend | Supabase (Postgres + Auth + Storage + Edge Functions) |
| Maps | react-native-maps with Apple Maps (default provider) |
| Animations | react-native-reanimated + react-native-gesture-handler |
| Images | expo-image (optimized caching) |
| Auth storage | expo-secure-store (native) |
/
βββ app/ # Expo Router screens (file-based routing)
β βββ _layout.tsx # Root layout β wraps everything in ThemeProvider, AuthProvider, FavoritesProvider
β βββ index.tsx # Entry redirect
β βββ (tabs)/ # Main tab navigator (auth-gated)
β β βββ _layout.tsx # Tab bar config, auth guard (redirects to signin if no session)
β β βββ index.tsx # Map/Explore β fullscreen map with bottom sheet and cluster markers
β β βββ home.tsx # Home β ranked card feed of places
β β βββ favorites.tsx # Saved/favorited places list
β β βββ explore.tsx # Post β form to submit a new workspace
β β βββ passport.tsx # Gamification hub: XP, rank, badges, passport stamps, leaderboard
β β βββ settings.tsx # Profile & account settings
β βββ auth/ # Auth screens (unauthenticated)
β β βββ signin.tsx
β β βββ signup.tsx
β β βββ reset-password.tsx
β β βββ callback.tsx # OAuth redirect handler
β βββ place/
β β βββ [id].tsx # Dynamic place detail screen
β βββ components/ # Screen-level components
β β βββ AirbnbBottomSheet.tsx # Draggable 3-state sheet (collapsed/half/full)
β β βββ ClusterMarker.tsx # Map cluster bubble
β β βββ CrowdLevelModal.tsx # Crowd report submission modal
β β βββ FilterModal.tsx # Category/filter picker
β β βββ FloatingCard.tsx # Preview card that slides up on map pin tap
β β βββ MapHeader.tsx # Search bar + category chips pinned at top of map
β β βββ MapMarker.tsx # Custom map pin showing rating
β β βββ PlaceDetailed.tsx # Full place detail view (images, reviews, crowd stats)
β β βββ ReviewForm.tsx # Star-rating + sub-rating review form
β β βββ ReviewsList.tsx # Paginated reviews list
β β βββ ListingCard.tsx # Compact place card
β β βββ ListingCardDetailed.tsx
β β βββ ListingRow.tsx # Horizontal scrollable place row
β β βββ ImageCarousel.tsx
β β βββ gamification/ # Gamification UI overlays
β β βββ BadgeAwardedSheet.tsx
β β βββ BadgeDetailSheet.tsx
β β βββ RankUpModal.tsx
β β βββ XpToast.tsx
β βββ feedback.tsx
β βββ help.tsx
β βββ modal.tsx
β βββ report-nsfw.tsx
β
βββ components/ # Shared primitive UI components
β βββ ui/ # Auth container, inputs, gallery, gradient button, icon symbols
β
βββ context/ # Global React context providers
β βββ AuthContext.tsx # Session state + signOut; listens to supabase.auth.onAuthStateChange
β βββ FavoritesContext.tsx # Favorites list with local + remote sync
β βββ ThemeContext.tsx # Light/dark mode
β
βββ hooks/
β βββ useClusters.ts # Geo-clustering logic for map markers (supercluster)
β βββ use-color-scheme.ts # Respects system preference + ThemeContext override
β
βββ services/ # All Supabase data access (no business logic in screens)
β βββ places.ts # CRUD for places; supports category filter + text search
β βββ reviews.ts # Review CRUD
β βββ crowd.ts # Crowd reports: insert + aggregate by Β±2-hour window
β βββ favorites.ts # User favorites persistence
β βββ gamification.ts # XP grants, badge checks, passport stamps, leaderboard
β βββ images.ts # Image upload to Supabase Storage
β βββ categories.ts # Workspace category lookup
β βββ places_categories.ts # Many-to-many placeβcategory
β βββ profiles.ts # User profile read/update
β βββ users.ts # Auth user helpers
β
βββ lib/
β βββ supabase.ts # Supabase client (SecureStore adapter on native, cookie on web)
β βββ auth/social.ts # Google/Apple OAuth helpers
β βββ date.ts # Date formatting utilities
β βββ imagePicker.ts # Expo ImagePicker wrapper
β
βββ utils/
β βββ workingHours.ts # Open/closed status calculation from working_hours JSON
β βββ location.ts # Geolocation helpers
β βββ transitions.ts # Shared animation presets
β
βββ constants/
β βββ theme.ts # Brand colours, category list, shared style tokens
β
βββ supabase/
β βββ functions/
β βββ delete-account/ # Deno Edge Function β deletes auth user via service-role key
β
βββ .eas/workflows/ # EAS CI/CD workflow definitions (preview, build, deploy)
Screen / Component
β
βΌ
services/* β thin async functions, call supabase directly
β
βΌ
lib/supabase.ts β single shared Supabase client
β
βΌ
Supabase (Postgres + Auth + Storage)
Context providers (AuthContext, FavoritesContext) sit between the root layout and all screens, providing global state without prop-drilling. Individual screens call services/* directly for data fetching β there is no intermediate state management library (no Redux/Zustand).
Map screen ((tabs)/index.tsx) β The primary discovery surface. Renders a fullscreen MapView with custom MapMarker pins (show rating) and ClusterMarker bubbles for dense areas. A MapHeader floats at the top with a search input and horizontally-scrollable category chips. An Airbnb-style draggable bottom sheet (collapsed / half / full) contains a carousel of place cards. Tapping a pin hides the sheet and shows a FloatingCard preview; tapping the card navigates to the place detail screen.
Passport screen ((tabs)/passport.tsx) β Gamification hub. Displays the user's current rank (with animated rank badge), XP progress bar, earned badges grid, passport stamp map, and a weekly leaderboard.
Place detail (place/[id].tsx) β Full detail view with image carousel, open/closed status from working hours, crowd level indicator (with hourly bar chart), star rating breakdown, reviews list, and a crowd report submission modal.
Defined in services/gamification.ts:
| Action | XP |
|---|---|
| Write a review | +50 |
| Review with all sub-ratings filled | +20 |
| First ever review of a place | +30 |
| Submit a crowd report | +15 |
| Add a new place | +100 |
Ranks (6 levels): Desk Lurker β Coffee Scout β WiFi Wanderer β Office Nomad β WorkNomad Pro β Workspace Legend.
Badges are checked after every XP-granting action and inserted into user_badges when conditions are met (e.g. 10 crowd reports = "Crowd Whisperer", first review = "First Steps"). Neighborhood-completion badges are generated dynamically based on stamping every approved place in a city neighborhood.
Passport stamps are issued when a user submits a review for a place they haven't reviewed before, creating a geographic "visited" log displayed on the Passport screen.
- Email/password sign-up and sign-in via Supabase Auth
- Google, Apple and Github OAuth (
lib/auth/social.ts) - Session token persisted in
expo-secure-storeon native, browser cookies on web AuthContextsubscribes toonAuthStateChangeand drives the tab navigator's auth guard- Account deletion handled by a Supabase Deno Edge Function (
delete-account) using the service-role key so the user's auth record is fully removed
Users submit reports via services/crowd.ts with { place_id, day_of_week, time_bucket, crowd_level }. The current crowd level for a place is computed as the mode of all reports within a Β±2-hour window for the current day and hour. Confidence is "high" when β₯ 10 reports exist, "low" otherwise. A separate hourly bar chart query shows typical busy times for the current day of the week.
To start the app, in your terminal run:
npm run startIn the output, you'll find options to open the app in:
- a development build
- an Android emulator
- an iOS simulator
- Expo Go, a limited sandbox for trying out app development with Expo
You can start developing by editing the files inside the app directory. This project uses file-based routing.
This project is configured to use EAS Workflows to automate some development and release processes. These commands are set up in package.json and can be run using NPM scripts in your terminal.
Run npm run draft to publish a preview update of your project, which can be viewed in Expo Go or in a development build.
Run npm run development-builds to create a development build. Note - you'll need to follow the Prerequisites to ensure you have the correct emulator setup on your machine.
Run npm run deploy to deploy to production. Note - you'll need to follow the Prerequisites to ensure you're set up to submit to the Apple and Google stores.
Expo offers hosting for websites and API functions via EAS Hosting. See the Getting Started guide to learn more.
When you're ready, run:
npm run reset-projectThis command will move the starter code to the app-example directory and create a blank app directory where you can start developing.
To learn more about developing your project with Expo, look at the following resources:
- Expo documentation: Learn fundamentals, or go into advanced topics with our guides.
- Learn Expo tutorial: Follow a step-by-step tutorial where you'll create a project that runs on Android, iOS, and the web.
Join our community of developers creating universal apps.
- Expo on GitHub: View our open source platform and contribute.
- Discord community: Chat with Expo users and ask questions.