From c656dd1bff680a9c334e19c46a10e38fc855a140 Mon Sep 17 00:00:00 2001 From: Penar Musaraj Date: Fri, 23 Jan 2026 17:24:49 -0500 Subject: [PATCH 1/6] DEV: ADd AGENTS.md --- AGENTS.md | 206 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..c8daefde --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,206 @@ +# AGENTS.md - Discourse Mobile Codebase Guide + +This document provides guidance for AI agents working on the Discourse Mobile codebase. + +## Project Overview + +**Discourse Mobile** is a native iOS and Android application for [Discourse](https://discourse.org) forums, built with React Native. It allows users to connect to multiple Discourse communities, receive push notifications, browse topics, and authenticate via OAuth2. + +### Technology Stack + +- **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) + +## Project Structure + +``` +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 +``` + +## Key Files + +| 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 | + +## Architecture Patterns + +### Platform-Specific Code + +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. + +### Component Organization + +- Screens in `js/screens/` +- Screen-specific components in `js/screens/{ScreenName}Components/` +- Shared components in `js/screens/CommonComponents/` + +### State Management + +- **Local state**: React `useState` hooks +- **App-wide theme**: `ThemeContext` (React Context) +- **Site data**: `SiteManager` singleton class +- **Persistence**: `AsyncStorage` for local storage + +### Authentication Flow + +1. User initiates OAuth in `AddSiteScreen` +2. `SiteManager` generates auth URL with state/challenge +3. Safari Web Auth opens Discourse authorization page +4. User approves, redirected to `discourse://auth_redirect` +5. App exchanges code for token using RSA encryption +6. Token stored in AsyncStorage + +## Development Commands + +```bash +# 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 +``` + +## Build Configuration + +### iOS + +- **Min Deployment**: iOS 15.1 +- **Targets**: Main app + Share Extension +- **Capabilities**: Push Notifications, Safari Web Auth, App Groups, Siri Shortcuts + +### Android + +- **Min SDK**: 26 (Android 8.0) +- **Target SDK**: 35 (Android 15) +- **Build**: Gradle with Kotlin DSL + +## Testing + +### E2E Tests (Detox) + +Located in `e2e/`: + +- `onboarding.test.js` - Initial app flow tests +- `topiclist.test.js` - Topic list functionality + +### Test Configurations + +- iPhone 16 Pro simulator +- iPad (10th generation) simulator +- Android emulator + +## Internationalization + +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`). + +## Common Tasks + +### Adding a New Screen + +1. Create screen component in `js/screens/NewScreen.js` +2. Add to navigation in `js/Discourse.js` +3. Create sub-components in `js/screens/NewScreenComponents/` if needed + +### Modifying API Calls + +- Site-specific API calls go in `js/site.js` +- Multi-site operations go in `js/site_manager.js` +- Use `lib/fetch.js` wrapper for HTTP requests + +### Adding Platform-Specific Features + +1. Create `*.ios.js` and `*.android.js` files +2. Export same interface from both +3. Import without extension: `import X from './platforms/feature'` + +### Handling Notifications + +Notification type routing is in `js/DiscourseUtils.js`. To add a new notification type: + +1. Add case to `getNotificationRoute()` function +2. Add icon mapping to `getNotificationIcon()` function + +## Code Style + +- ESLint with React Native config +- Prettier for formatting +- No TypeScript (partial adoption in `tsconfig.json` but not enforced) + +## CI/CD + +### GitHub Actions + +- `linting.yml` - ESLint/Prettier checks on PRs +- `ios-tests.yml` - Detox e2e tests on macOS + +### Fastlane + +- iOS/Android deployment automation +- Certificate management via Match From 3e752994942b0991608867dc9fb50d6462fd42b8 Mon Sep 17 00:00:00 2001 From: Penar Musaraj Date: Wed, 4 Feb 2026 11:22:38 -0500 Subject: [PATCH 2/6] Add AI tag --- js/locale/en.json | 1 + js/screens/DiscoverScreen.js | 2 ++ 2 files changed, 3 insertions(+) diff --git a/js/locale/en.json b/js/locale/en.json index 8424493d..6c554516 100644 --- a/js/locale/en.json +++ b/js/locale/en.json @@ -74,6 +74,7 @@ "discover_open_source": "open-source", "discover_international": "international", "discover_recent": "recent", + "discover_ai": "ai", "active_counts": "%{active_users} active users", "ok": "OK", "cancel": "Cancel", diff --git a/js/screens/DiscoverScreen.js b/js/screens/DiscoverScreen.js index fa993530..5806f95e 100644 --- a/js/screens/DiscoverScreen.js +++ b/js/screens/DiscoverScreen.js @@ -319,6 +319,7 @@ class DiscoverScreen extends React.Component { '#media', '#gaming', '#open-source', + '#ai', '#locale-intl', 'order:latest_topic', ]; @@ -340,6 +341,7 @@ class DiscoverScreen extends React.Component { {this._renderTag(i18n.t('discover_media'), '#media')} {this._renderTag(i18n.t('discover_gaming'), '#gaming')} {this._renderTag(i18n.t('discover_open_source'), '#open-source')} + {this._renderTag(i18n.t('discover_ai'), '#ai')} {this._renderTag(i18n.t('discover_international'), '#locale-intl')} {this._renderTag(i18n.t('discover_recent'), 'order:latest_topic')} From df6bff90588c55f7840b5b2d32fd11ec0600b9e6 Mon Sep 17 00:00:00 2001 From: Penar Musaraj Date: Wed, 4 Feb 2026 14:33:00 -0500 Subject: [PATCH 3/6] Split iPhone and iPad tests into separate jobs --- .github/workflows/ios-tests.yml | 54 ++++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ios-tests.yml b/.github/workflows/ios-tests.yml index b573d308..c2907992 100644 --- a/.github/workflows/ios-tests.yml +++ b/.github/workflows/ios-tests.yml @@ -7,8 +7,8 @@ on: pull_request: jobs: - ios-tests: - runs-on: macos-latest + ios-iphone-tests: + runs-on: macos-15 steps: - name: Checkout repository uses: actions/checkout@v3 @@ -44,12 +44,58 @@ jobs: - name: Detox iPhone tests run: yarn detox test --configuration ios.sim.release --cleanup --record-logs all + - name: Upload artifacts + if: failure() + uses: actions/upload-artifact@v4 + with: + name: detox-artifacts-iphone + path: artifacts + + ios-ipad-tests: + runs-on: macos-15 + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up Node.js + uses: actions/setup-node@v3 + with: + node-version: 24 + cache: yarn + + - name: Install Yarn dependencies + run: yarn + + - name: Install macOS dependencies + run: | + brew tap wix/brew + brew install applesimutils + env: + HOMEBREW_NO_AUTO_UPDATE: 1 + HOMEBREW_NO_INSTALL_CLEANUP: 1 + + - name: Setup Ruby + uses: ruby/setup-ruby@v1 + with: + bundler-cache: true + + - name: Install CocoaPods + run: cd ios ; pod install ; cd - + + - name: Detox build + run: yarn detox build --configuration ios.sim.release + + - name: Boot iPad simulator + run: | + xcrun simctl boot 'iPad (10th generation)' || true + xcrun simctl bootstatus 'iPad (10th generation)' -b + - name: Detox iPad tests - run: yarn detox test --configuration ios.sim.release --cleanup --record-logs all -n 'iPad (10th generation)' + run: yarn detox test --configuration ios.sim.release --cleanup --record-logs all -n 'iPad (10th generation)' --retries 2 - name: Upload artifacts if: failure() uses: actions/upload-artifact@v4 with: - name: detox-artifacts + name: detox-artifacts-ipad path: artifacts From 24576f8a77f243e45663c180ab4299f432293093 Mon Sep 17 00:00:00 2001 From: Penar Musaraj Date: Wed, 4 Feb 2026 15:26:32 -0500 Subject: [PATCH 4/6] xcpretty let's try --- .github/workflows/ios-tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ios-tests.yml b/.github/workflows/ios-tests.yml index c2907992..caf48f5e 100644 --- a/.github/workflows/ios-tests.yml +++ b/.github/workflows/ios-tests.yml @@ -39,7 +39,7 @@ jobs: run: cd ios ; pod install ; cd - - name: Detox build - run: yarn detox build --configuration ios.sim.release + run: set -o pipefail && yarn detox build --configuration ios.sim.release 2>&1 | xcpretty - name: Detox iPhone tests run: yarn detox test --configuration ios.sim.release --cleanup --record-logs all @@ -83,7 +83,7 @@ jobs: run: cd ios ; pod install ; cd - - name: Detox build - run: yarn detox build --configuration ios.sim.release + run: set -o pipefail && yarn detox build --configuration ios.sim.release 2>&1 | xcpretty - name: Boot iPad simulator run: | From f03582b71d7a7e7761f52cd22c0986a960805421 Mon Sep 17 00:00:00 2001 From: Penar Musaraj Date: Wed, 4 Feb 2026 16:10:53 -0500 Subject: [PATCH 5/6] Revert "xcpretty let's try" This reverts commit 24576f8a77f243e45663c180ab4299f432293093. --- .github/workflows/ios-tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ios-tests.yml b/.github/workflows/ios-tests.yml index caf48f5e..c2907992 100644 --- a/.github/workflows/ios-tests.yml +++ b/.github/workflows/ios-tests.yml @@ -39,7 +39,7 @@ jobs: run: cd ios ; pod install ; cd - - name: Detox build - run: set -o pipefail && yarn detox build --configuration ios.sim.release 2>&1 | xcpretty + run: yarn detox build --configuration ios.sim.release - name: Detox iPhone tests run: yarn detox test --configuration ios.sim.release --cleanup --record-logs all @@ -83,7 +83,7 @@ jobs: run: cd ios ; pod install ; cd - - name: Detox build - run: set -o pipefail && yarn detox build --configuration ios.sim.release 2>&1 | xcpretty + run: yarn detox build --configuration ios.sim.release - name: Boot iPad simulator run: | From 6c9ef546c8bf22a05fc39850e6082eec4c62cb09 Mon Sep 17 00:00:00 2001 From: Penar Musaraj Date: Wed, 4 Feb 2026 16:17:38 -0500 Subject: [PATCH 6/6] use debug config --- .github/workflows/ios-tests.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ios-tests.yml b/.github/workflows/ios-tests.yml index c2907992..f7acf2bb 100644 --- a/.github/workflows/ios-tests.yml +++ b/.github/workflows/ios-tests.yml @@ -39,10 +39,10 @@ jobs: run: cd ios ; pod install ; cd - - name: Detox build - run: yarn detox build --configuration ios.sim.release + run: yarn detox build --configuration ios.sim.debug - name: Detox iPhone tests - run: yarn detox test --configuration ios.sim.release --cleanup --record-logs all + run: yarn detox test --configuration ios.sim.debug --cleanup --record-logs all - name: Upload artifacts if: failure() @@ -83,7 +83,7 @@ jobs: run: cd ios ; pod install ; cd - - name: Detox build - run: yarn detox build --configuration ios.sim.release + run: yarn detox build --configuration ios.sim.debug - name: Boot iPad simulator run: | @@ -91,7 +91,7 @@ jobs: xcrun simctl bootstatus 'iPad (10th generation)' -b - name: Detox iPad tests - run: yarn detox test --configuration ios.sim.release --cleanup --record-logs all -n 'iPad (10th generation)' --retries 2 + run: yarn detox test --configuration ios.sim.debug --cleanup --record-logs all -n 'iPad (10th generation)' --retries 2 - name: Upload artifacts if: failure()