Mobile App Architecture

Doc Status: Excellent | ✓ Clear summary | ✓ Easy to read | ✓ Matches code | ✓ Good structure | ✓ Professional look | ✓ Visual components | ✓ No red herrings

Why This Matters

The mobile app is the primary consumer touchpoint for Nous. It handles:
  • Daily self-discovery (photo prompts, video reflections)
  • AI-powered conversations (Vicky wingman)
  • Discovery and matching
  • Relationship tools (Caring Moments, Official Flag)
A well-structured architecture ensures the team can iterate quickly while maintaining code quality across iOS and Android.

Directory Structure

apps/mobile — Expo React Native application app/ — Expo Router file-based routing
  • _layout.tsx — Root layout with native navigation
  • capture.tsx — Video moment capture flow
  • user-settings.tsx — User profile settings
  • app-modal.tsx — Modal presentation
app/(onboarding)/ — First-time user onboarding
  • _layout.tsx, onboarding-splash.tsx, onboarding-philosophy.tsx
  • camera-permission.tsx, notification-permission.tsx
app/(tabs)/ — Main tab navigation (Home, Discover, Messages)
  • _layout.tsx — Tab bar configuration
  • index.tsx — Home (daily content + LMS)
  • discover.tsx — Map + activities discovery
  • messages.tsx — Matches list
  • profile.tsx — User profile (standalone route, not a tab)
app/ — Standalone routes
  • design-system.tsx — Design system preview (standalone route, not a tab)
app/chat/ — Chat routes
  • [id].tsx — Individual match conversation
  • vicky.tsx — Direct Vicky AI wingman chat
app/moment/ — Moment detail
  • [id].tsx — Single moment view
components/ — Reusable UI components
  • ui/ — Primitive components
  • home/ — Home screen components
  • chat/ — Chat UI components
  • discover/ — Discovery feature components
  • messages/ — Messages list components
  • features/ — Feature-specific components
  • shared/ — Cross-feature shared components
hooks/ — Custom React hooks lib/ — Services and utilities services/ — API service layers constants/ — App-wide constants types/ — TypeScript type definitions data/ — Static data and mock data assets/ — Images, fonts, etc.
The backend (Hono/tRPC) and frontend (Next.js) services were removed from this repo in May 2026. The mobile app communicates directly with the Mastra AI runtime for all features.

Root Layout (app/_layout.tsx)

The root layout wraps the entire app with native navigation primitives and providers.

Tab Navigation (app/(tabs)/_layout.tsx)

Main navigation uses Expo Router’s native tab trigger system:
TabFileIconPurpose
Homeindex.tsxHouse iconDaily content, LMS courses
Discoverdiscover.tsxSparkles iconActivities discovery
Messagesmessages.tsxChat iconMatch conversations, Vicky AI
SearchMagnifying glassNot yet implemented
The Assistant tab was removed — Vicky AI is accessed via the Messages tab header. User settings are accessed via app/user-settings.tsx. A Search tab trigger exists in the tab layout but search.tsx has not been implemented (no corresponding route file).

Onboarding Flow (app/(onboarding)/)

First-time users experience a curated onboarding:
  1. Splash — Brand introduction
  2. Philosophy — App values and approach
  3. Camera Permission — Required for self-discovery features
  4. Notification Permission — For matches and messages

Chat Routes

RouteFilePurpose
Chatchat/[id].tsxIndividual match conversation
Vicky Chatchat/vicky.tsxDirect Vicky AI wingman chat

Custom Hooks

AI & Chat Hooks

HookPurpose
use-vicky-chat.tsVicky AI wingman chat (streams from Mastra agent)
use-chat-stream.tsNous AI chat (streams from Mastra /api/chat/stream)
use-conversation-starter.tsAI-generated conversation openers

Data & State Hooks

HookPurpose
use-color-scheme.tsSystem color scheme (light/dark)
use-color-scheme-web.tsWeb-specific color scheme
use-theme-color.tsApp theme color management
use-discover-data.tsDiscovery feed data
use-moments.tsUser moments/video timeline

Key Services

LMS Service (services/lms-service.ts)

Direct fetch calls to Mastra LMS endpoints. Used in app/(tabs)/index.tsx for the home screen courses list.
const MASTRA_URL =
  process.env.EXPO_PUBLIC_MASTRA_URL ?? 'https://nous-mastra.curlynguyen95.workers.dev'

export async function getPublishedCourses(): Promise<Course[]>
export async function getCourseById(courseId: string): Promise<CourseDetail>
export async function getUserEnrollments(userId: string): Promise<unknown[]>
export async function enrollInCourse(userId: string, courseId: string): Promise<void>
export async function updateLessonProgress(
  userId: string,
  lessonId: string,
  watchedSeconds: number
): Promise<void>
lib/api-client.ts contains TanStack Query hooks for the same LMS endpoints, but these are not yet integrated into the app — services/lms-service.ts is currently used directly.

Matches Service (services/matches-service.ts)

Match and conversation management:
export async function getMatches(userId: string): Promise<Match[]>
export async function getMatchById(matchId: string): Promise<Match | null>
export async function getMessages(matchId: string): Promise<MessageData[]>
export async function sendMessage(matchId: string, content: string, userId: string): Promise<void>

Utilities (lib/utils.ts)

Standard utility functions including cn() for Tailwind class merging.

API Client (lib/api-client.ts)

TanStack Query hooks wrapping LMS API with caching and state management. Defined but not yet integrated into app components.
export function useCourses() // Fetch all published courses
export function useCourse(courseId) // Fetch single course with lessons
export function useEnroll() // Enroll in a course
export function useEnrollments(userId) // Get user's enrolled courses
export function useUpdateProgress() // Update lesson progress
export function useVideoUrl(lessonId) // Get video streaming URL

Cloudflare Stream (lib/cloudflare-stream.ts)

Video upload and streaming via Cloudflare Stream:
export class CloudflareStream {
  async listVideos(): Promise<StreamVideo[]>
  async getVideo(uid: string): Promise<StreamVideo | null>
  async createUploadUrl(videoName: string, description?: string): Promise<StreamUploadResponse>
  async uploadVideo(
    filePath: string,
    videoName: string,
    description?: string,
    onProgress?: (progress: number) => void
  ): Promise<StreamVideo>
  async deleteVideo(uid: string): Promise<boolean>
}

Mobile API (lib/mobile-api.ts)

Mobile-specific API client for match and conversation operations.

Mobile Polyfills (lib/mobile-polyfills.ts)

Platform-specific polyfills for React Native compatibility.

Memory (lib/memory/)

AI SDK + Mastra memory integration documentation and setup:
  • 2026-04-23-ai-sdk-mastra-first.md — Initial integration notes

UI Components

Primitive UI (components/ui/)

Core reusable components:
ComponentPurpose
button.tsxPrimary button with variants
card.tsxCard container
gradient-background.tsxApp-wide gradient theming
iconSymbol.tsxSF Symbols / Material Icons mapping
loading-dots.tsxLoading indicator
error-boundary.tsxError catching and display
section-title.tsxSection heading component
icon-button.tsxIcon-only button

Home Components (components/home/)

ComponentPurpose
home-header.tsxHeader with date and greeting
daily-content.tsxDaily prompt and inspirational content
capture-button.tsxVideo capture trigger
moments-timeline.tsxUser’s video moments feed
timeline.tsxTimeline view component

Chat Components (components/chat/)

ComponentPurpose
chat-header.tsxChat screen header with back button
message-item.tsxIndividual message bubble
message-input.tsxText input with send button
suggestion-chips.tsxAI-suggested responses
schedule-message.tsxDate/event scheduling card
text-bubble.tsxText message bubble
image-message.tsxImage attachment message
ai-toggle-pill.tsxAI enable/disable toggle
ai-button.tsxAI action button
avatar.tsxUser/match avatar
bento-grid.tsxBento-style layout grid
bento-image-wrapper.tsxImage wrapper for bento grid
chat-back-button.tsxBack navigation
gift-message.tsxGift/caring moment message
gradient-message.tsxGradient-styled message
grain-overlay.tsxGrain texture overlay
poke-message.tsxPoke notification message
schedule-date-row.tsxDate row in schedule
schedule-location-row.tsxLocation row in schedule
send-button.tsxMessage send button
suggestion-chip.tsxIndividual suggestion chip
suggestion-error.tsxSuggestion error state
suggestion-header.tsxSuggestion section header
suggestion-loading.tsxSuggestion loading state
chat-input-accessory.tsxInput accessory view

Messages Components (components/messages/)

ComponentPurpose
nous-button.tsxNous action button
connection-avatar.tsxConnection avatar
connection-content.tsxConnection content
connection-divider.tsxConnection list divider
connection-online-dot.tsxOnline status indicator
connection-row.tsxConnection list item
messages-header.tsxMessages list header
connection-actions.tsxConnection action buttons

Discover Components (components/discover/ and components/features/discover/)

components/discover/ — Top-level discover components:
ComponentPurpose
discover-header.tsxDiscovery screen header
components/features/discover/ — Feature-specific discover components:
ComponentPurpose
category-pill.tsxCategory filter pill
category-pills-row.tsxHorizontal category pills scroll
detail-sheet.tsxEvent detail bottom sheet
event-card-badge.tsxEvent card badge
event-card-host.tsxEvent host info
event-card-meta.tsxEvent metadata (date, time, price)
event-card-price.tsxEvent price display
event-card.tsxEvent card component
events-section.tsxEvents list section
host-card.tsxHost information card
invitation-card.tsxInvitation card
invitations-section.tsxInvitations list section
map-marker.tsxMap marker component
map-section.tsxMap view section
meta-item.tsxMetadata item

Profile Components (components/features/profile/)

ComponentPurpose
premium-badge.tsxPremium tier badge
profile-avatar.tsxProfile avatar with status
profile-header.tsxProfile screen header
profile-hero-overlay.tsxHero image overlay
profile-hero.tsxProfile hero section
profile-stats-row.tsxStats row (matches, etc.)
settings-row.tsxSettings list item
settings-section.tsxSettings group section
stat-card.tsxStat display card

Home Feature Components (components/features/home/)

ComponentPurpose
empty-day.tsxEmpty state for no content
day-header.tsxDay/section header
fullscreen-viewer.tsxFullscreen media viewer
moment-bento-grid.tsxBento grid for moments
video-card.tsxVideo content card

Settings Components (components/settings/)

ComponentPurpose
settings-section.tsxSettings group section
settings-header.tsxSettings page header
settings-row.tsxSettings list item

Shared Components (components/shared/)

ComponentPurpose
haptic-tab.tsxTab with haptic feedback
themed-text.tsxTheme-aware text
themed-view.tsxTheme-aware view

Design System Components (components/design-system/)

ComponentPurpose
button-demo.tsxButton component demo
color-row.tsxColor palette row
swatch.tsxColor swatch display
typography-demo.tsxTypography showcase

State Management

TanStack Query

Data fetching and caching uses TanStack Query:
  • Courses and enrollments cached automatically
  • Optimistic updates for enrollments
  • Background refetch on focus

React Context

Navigation and global state via React Context:
  • Theme colors and styling constants
  • Safe area insets for notch handling
  • Auth state via Clerk

Key Dependencies

expo-router

File-based routing

@tanstack/react-query

Server state management

ai

AI SDK for streaming chat

expo-image-picker

Camera and gallery access

expo-sensors

Accelerometer for shake-to-capture

expo-haptics

Tactile feedback

expo-image

Optimized image loading

react-native-safe-area-context

Safe area handling

Last Updated: 2026-05-13