This document provides guidance for AI agents working on the Discourse Mobile codebase.
Discourse Mobile is a native iOS and Android application for Discourse forums, built with React Native. It allows users to connect to multiple Discourse communities, receive push notifications, browse topics, and authenticate via OAuth2.
- Framework: React Native
- JS Engine: Hermes
- Navigation: React Navigation v6 (Stack + Bottom Tabs)
- State: React hooks, Context (ThemeContext), AsyncStorage
- Push Notifications: Firebase Cloud Messaging (FCM)
- Authentication: OAuth2 via Safari Web Auth with RSA encryption
- Testing: Detox (e2e), Jest (unit)
- Languages: JavaScript, Swift (iOS Share Extension), Kotlin (Android)
DiscourseMobile/
├── js/ # Main JavaScript source
│ ├── Discourse.js # Root app component, navigation, lifecycle
│ ├── site_manager.js # Multi-site management, auth tokens
│ ├── site.js # Site model, API interactions
│ ├── DiscourseUtils.js # Notification routing logic
│ ├── ThemeContext.js # Dark/light theme configuration
│ ├── screens/ # Screen components
│ │ ├── HomeScreen.js # Main sites list, topic viewing
│ │ ├── NotificationsScreen.js
│ │ ├── DiscoverScreen.js # Site discovery
│ │ ├── AddSiteScreen.js # Connect to new sites
│ │ ├── SettingsScreen.js
│ │ ├── WebViewScreen.js # In-app web content
│ │ └── *Components/ # Screen-specific sub-components
│ ├── platforms/ # Platform-specific implementations
│ │ ├── firebase.ios.js
│ │ ├── firebase.android.js
│ │ └── background-fetch.*.js
│ └── locale/ # 49 language translation files (JSON)
├── lib/ # Utility libraries
│ ├── fetch.js # Custom fetch wrapper
│ ├── jsencrypt.js # RSA encryption
│ └── random-bytes.js # CSPRNG utility
├── ios/ # iOS native code
│ ├── Discourse/ # Main app target
│ ├── ShareExtension/ # iOS Share Extension (Swift)
│ └── Podfile # CocoaPods dependencies
├── android/ # Android native code
│ └── app/src/main/java/com/discourse/
├── e2e/ # Detox e2e tests
└── fastlane/ # CI/CD automation
| File | Purpose |
|---|---|
js/Discourse.js |
Root component, navigation setup, deep linking, auth handling |
js/site_manager.js |
Manages connected sites, auth tokens, device registration |
js/site.js |
Site model class, API calls, basic info fetching |
js/DiscourseUtils.js |
Maps 37+ notification types to endpoints and icons |
js/ThemeContext.js |
Theme definitions (colors, fonts) for light/dark mode |
js/screens/HomeScreen.js |
Main UI with draggable site list and topic viewing |
Use file suffixes for platform divergence:
*.ios.js- iOS-specific implementation*.android.js- Android-specific implementation
The bundler automatically selects the correct file based on platform.
- Screens in
js/screens/ - Screen-specific components in
js/screens/{ScreenName}Components/ - Shared components in
js/screens/CommonComponents/
- Local state: React
useStatehooks - App-wide theme:
ThemeContext(React Context) - Site data:
SiteManagersingleton class - Persistence:
AsyncStoragefor local storage
- User initiates OAuth in
AddSiteScreen SiteManagergenerates auth URL with state/challenge- Safari Web Auth opens Discourse authorization page
- User approves, redirected to
discourse://auth_redirect - App exchanges code for token using RSA encryption
- Token stored in AsyncStorage
# Install dependencies
yarn
# iOS setup
bundle install
cd ios && pod install && cd ..
# Start Metro bundler
npx react-native start
# Run on iOS
npx react-native run-ios
# Run on Android
npx react-native run-android
# Run e2e tests
npx detox build --configuration ios.sim.debug
npx detox test --configuration ios.sim.debug
# Lint
yarn lint- Min Deployment: iOS 15.1
- Targets: Main app + Share Extension
- Capabilities: Push Notifications, Safari Web Auth, App Groups, Siri Shortcuts
- Min SDK: 26 (Android 8.0)
- Target SDK: 35 (Android 15)
- Build: Gradle with Kotlin DSL
Located in e2e/:
onboarding.test.js- Initial app flow teststopiclist.test.js- Topic list functionality
- iPhone 16 Pro simulator
- iPad (10th generation) simulator
- Android emulator
49 languages supported via JSON files in js/locale/. Uses i18n-js library.
To add translations, edit the appropriate locale file (e.g., js/locale/en.json).
- Create screen component in
js/screens/NewScreen.js - Add to navigation in
js/Discourse.js - Create sub-components in
js/screens/NewScreenComponents/if needed
- Site-specific API calls go in
js/site.js - Multi-site operations go in
js/site_manager.js - Use
lib/fetch.jswrapper for HTTP requests
- Create
*.ios.jsand*.android.jsfiles - Export same interface from both
- Import without extension:
import X from './platforms/feature'
Notification type routing is in js/DiscourseUtils.js. To add a new notification type:
- Add case to
getNotificationRoute()function - Add icon mapping to
getNotificationIcon()function
- ESLint with React Native config
- Prettier for formatting
- No TypeScript (partial adoption in
tsconfig.jsonbut not enforced)
linting.yml- ESLint/Prettier checks on PRsios-tests.yml- Detox e2e tests on macOS
- iOS/Android deployment automation
- Certificate management via Match