Skip to content

Commit ffcb648

Browse files
authored
Add agents file + AI tag in Discover tab (#273)
1 parent b03bad4 commit ffcb648

4 files changed

Lines changed: 276 additions & 5 deletions

File tree

.github/workflows/ios-tests.yml

Lines changed: 67 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ on:
77
pull_request:
88

99
jobs:
10-
ios-tests:
11-
runs-on: macos-latest
10+
ios-iphone-tests:
11+
runs-on: macos-15
1212
steps:
1313
- name: Checkout repository
1414
uses: actions/checkout@v3
@@ -39,17 +39,79 @@ jobs:
3939
run: cd ios ; pod install ; cd -
4040

4141
- name: Detox build
42-
run: yarn detox build --configuration ios.sim.release
42+
run: |
43+
xcodebuild -workspace ios/Discourse.xcworkspace \
44+
-scheme Discourse \
45+
-configuration Release \
46+
-sdk iphonesimulator \
47+
-destination 'generic/platform=iOS Simulator' \
48+
-derivedDataPath ios/build \
49+
-quiet \
50+
build
4351
4452
- name: Detox iPhone tests
4553
run: yarn detox test --configuration ios.sim.release --cleanup --record-logs all
4654

55+
- name: Upload artifacts
56+
if: failure()
57+
uses: actions/upload-artifact@v4
58+
with:
59+
name: detox-artifacts-iphone
60+
path: artifacts
61+
62+
ios-ipad-tests:
63+
runs-on: macos-15
64+
steps:
65+
- name: Checkout repository
66+
uses: actions/checkout@v3
67+
68+
- name: Set up Node.js
69+
uses: actions/setup-node@v3
70+
with:
71+
node-version: 24
72+
cache: yarn
73+
74+
- name: Install Yarn dependencies
75+
run: yarn
76+
77+
- name: Install macOS dependencies
78+
run: |
79+
brew tap wix/brew
80+
brew install applesimutils
81+
env:
82+
HOMEBREW_NO_AUTO_UPDATE: 1
83+
HOMEBREW_NO_INSTALL_CLEANUP: 1
84+
85+
- name: Setup Ruby
86+
uses: ruby/setup-ruby@v1
87+
with:
88+
bundler-cache: true
89+
90+
- name: Install CocoaPods
91+
run: cd ios ; pod install ; cd -
92+
93+
- name: Detox build
94+
run: |
95+
xcodebuild -workspace ios/Discourse.xcworkspace \
96+
-scheme Discourse \
97+
-configuration Release \
98+
-sdk iphonesimulator \
99+
-destination 'generic/platform=iOS Simulator' \
100+
-derivedDataPath ios/build \
101+
-quiet \
102+
build
103+
104+
- name: Boot iPad simulator
105+
run: |
106+
xcrun simctl boot 'iPad (10th generation)' || true
107+
xcrun simctl bootstatus 'iPad (10th generation)' -b
108+
47109
- name: Detox iPad tests
48-
run: yarn detox test --configuration ios.sim.release --cleanup --record-logs all -n 'iPad (10th generation)'
110+
run: yarn detox test --configuration ios.sim.release --cleanup --record-logs all -n 'iPad (10th generation)' --retries 2
49111

50112
- name: Upload artifacts
51113
if: failure()
52114
uses: actions/upload-artifact@v4
53115
with:
54-
name: detox-artifacts
116+
name: detox-artifacts-ipad
55117
path: artifacts

AGENTS.md

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
# AGENTS.md - Discourse Mobile Codebase Guide
2+
3+
This document provides guidance for AI agents working on the Discourse Mobile codebase.
4+
5+
## Project Overview
6+
7+
**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.
8+
9+
### Technology Stack
10+
11+
- **Framework**: React Native
12+
- **JS Engine**: Hermes
13+
- **Navigation**: React Navigation v6 (Stack + Bottom Tabs)
14+
- **State**: React hooks, Context (ThemeContext), AsyncStorage
15+
- **Push Notifications**: Firebase Cloud Messaging (FCM)
16+
- **Authentication**: OAuth2 via Safari Web Auth with RSA encryption
17+
- **Testing**: Detox (e2e), Jest (unit)
18+
- **Languages**: JavaScript, Swift (iOS Share Extension), Kotlin (Android)
19+
20+
## Project Structure
21+
22+
```
23+
DiscourseMobile/
24+
├── js/ # Main JavaScript source
25+
│ ├── Discourse.js # Root app component, navigation, lifecycle
26+
│ ├── site_manager.js # Multi-site management, auth tokens
27+
│ ├── site.js # Site model, API interactions
28+
│ ├── DiscourseUtils.js # Notification routing logic
29+
│ ├── ThemeContext.js # Dark/light theme configuration
30+
│ ├── screens/ # Screen components
31+
│ │ ├── HomeScreen.js # Main sites list, topic viewing
32+
│ │ ├── NotificationsScreen.js
33+
│ │ ├── DiscoverScreen.js # Site discovery
34+
│ │ ├── AddSiteScreen.js # Connect to new sites
35+
│ │ ├── SettingsScreen.js
36+
│ │ ├── WebViewScreen.js # In-app web content
37+
│ │ └── *Components/ # Screen-specific sub-components
38+
│ ├── platforms/ # Platform-specific implementations
39+
│ │ ├── firebase.ios.js
40+
│ │ ├── firebase.android.js
41+
│ │ └── background-fetch.*.js
42+
│ └── locale/ # 49 language translation files (JSON)
43+
├── lib/ # Utility libraries
44+
│ ├── fetch.js # Custom fetch wrapper
45+
│ ├── jsencrypt.js # RSA encryption
46+
│ └── random-bytes.js # CSPRNG utility
47+
├── ios/ # iOS native code
48+
│ ├── Discourse/ # Main app target
49+
│ ├── ShareExtension/ # iOS Share Extension (Swift)
50+
│ └── Podfile # CocoaPods dependencies
51+
├── android/ # Android native code
52+
│ └── app/src/main/java/com/discourse/
53+
├── e2e/ # Detox e2e tests
54+
└── fastlane/ # CI/CD automation
55+
```
56+
57+
## Key Files
58+
59+
| File | Purpose |
60+
| -------------------------- | ------------------------------------------------------------- |
61+
| `js/Discourse.js` | Root component, navigation setup, deep linking, auth handling |
62+
| `js/site_manager.js` | Manages connected sites, auth tokens, device registration |
63+
| `js/site.js` | Site model class, API calls, basic info fetching |
64+
| `js/DiscourseUtils.js` | Maps 37+ notification types to endpoints and icons |
65+
| `js/ThemeContext.js` | Theme definitions (colors, fonts) for light/dark mode |
66+
| `js/screens/HomeScreen.js` | Main UI with draggable site list and topic viewing |
67+
68+
## Architecture Patterns
69+
70+
### Platform-Specific Code
71+
72+
Use file suffixes for platform divergence:
73+
74+
- `*.ios.js` - iOS-specific implementation
75+
- `*.android.js` - Android-specific implementation
76+
77+
The bundler automatically selects the correct file based on platform.
78+
79+
### Component Organization
80+
81+
- Screens in `js/screens/`
82+
- Screen-specific components in `js/screens/{ScreenName}Components/`
83+
- Shared components in `js/screens/CommonComponents/`
84+
85+
### State Management
86+
87+
- **Local state**: React `useState` hooks
88+
- **App-wide theme**: `ThemeContext` (React Context)
89+
- **Site data**: `SiteManager` singleton class
90+
- **Persistence**: `AsyncStorage` for local storage
91+
92+
### Authentication Flow
93+
94+
1. User initiates OAuth in `AddSiteScreen`
95+
2. `SiteManager` generates auth URL with state/challenge
96+
3. Safari Web Auth opens Discourse authorization page
97+
4. User approves, redirected to `discourse://auth_redirect`
98+
5. App exchanges code for token using RSA encryption
99+
6. Token stored in AsyncStorage
100+
101+
## Development Commands
102+
103+
```bash
104+
# Install dependencies
105+
yarn
106+
107+
# iOS setup
108+
bundle install
109+
cd ios && pod install && cd ..
110+
111+
# Start Metro bundler
112+
npx react-native start
113+
114+
# Run on iOS
115+
npx react-native run-ios
116+
117+
# Run on Android
118+
npx react-native run-android
119+
120+
# Run e2e tests
121+
npx detox build --configuration ios.sim.debug
122+
npx detox test --configuration ios.sim.debug
123+
124+
# Lint
125+
yarn lint
126+
```
127+
128+
## Build Configuration
129+
130+
### iOS
131+
132+
- **Min Deployment**: iOS 15.1
133+
- **Targets**: Main app + Share Extension
134+
- **Capabilities**: Push Notifications, Safari Web Auth, App Groups, Siri Shortcuts
135+
136+
### Android
137+
138+
- **Min SDK**: 26 (Android 8.0)
139+
- **Target SDK**: 35 (Android 15)
140+
- **Build**: Gradle with Kotlin DSL
141+
142+
## Testing
143+
144+
### E2E Tests (Detox)
145+
146+
Located in `e2e/`:
147+
148+
- `onboarding.test.js` - Initial app flow tests
149+
- `topiclist.test.js` - Topic list functionality
150+
151+
### Test Configurations
152+
153+
- iPhone 16 Pro simulator
154+
- iPad (10th generation) simulator
155+
- Android emulator
156+
157+
## Internationalization
158+
159+
49 languages supported via JSON files in `js/locale/`. Uses `i18n-js` library.
160+
161+
To add translations, edit the appropriate locale file (e.g., `js/locale/en.json`).
162+
163+
## Common Tasks
164+
165+
### Adding a New Screen
166+
167+
1. Create screen component in `js/screens/NewScreen.js`
168+
2. Add to navigation in `js/Discourse.js`
169+
3. Create sub-components in `js/screens/NewScreenComponents/` if needed
170+
171+
### Modifying API Calls
172+
173+
- Site-specific API calls go in `js/site.js`
174+
- Multi-site operations go in `js/site_manager.js`
175+
- Use `lib/fetch.js` wrapper for HTTP requests
176+
177+
### Adding Platform-Specific Features
178+
179+
1. Create `*.ios.js` and `*.android.js` files
180+
2. Export same interface from both
181+
3. Import without extension: `import X from './platforms/feature'`
182+
183+
### Handling Notifications
184+
185+
Notification type routing is in `js/DiscourseUtils.js`. To add a new notification type:
186+
187+
1. Add case to `getNotificationRoute()` function
188+
2. Add icon mapping to `getNotificationIcon()` function
189+
190+
## Code Style
191+
192+
- ESLint with React Native config
193+
- Prettier for formatting
194+
- No TypeScript (partial adoption in `tsconfig.json` but not enforced)
195+
196+
## CI/CD
197+
198+
### GitHub Actions
199+
200+
- `linting.yml` - ESLint/Prettier checks on PRs
201+
- `ios-tests.yml` - Detox e2e tests on macOS
202+
203+
### Fastlane
204+
205+
- iOS/Android deployment automation
206+
- Certificate management via Match

js/locale/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
"discover_open_source": "open-source",
7575
"discover_international": "international",
7676
"discover_recent": "recent",
77+
"discover_ai": "ai",
7778
"active_counts": "%{active_users} active users",
7879
"ok": "OK",
7980
"cancel": "Cancel",

js/screens/DiscoverScreen.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,7 @@ class DiscoverScreen extends React.Component {
319319
'#media',
320320
'#gaming',
321321
'#open-source',
322+
'#ai',
322323
'#locale-intl',
323324
'order:latest_topic',
324325
];
@@ -340,6 +341,7 @@ class DiscoverScreen extends React.Component {
340341
{this._renderTag(i18n.t('discover_media'), '#media')}
341342
{this._renderTag(i18n.t('discover_gaming'), '#gaming')}
342343
{this._renderTag(i18n.t('discover_open_source'), '#open-source')}
344+
{this._renderTag(i18n.t('discover_ai'), '#ai')}
343345
{this._renderTag(i18n.t('discover_international'), '#locale-intl')}
344346
{this._renderTag(i18n.t('discover_recent'), 'order:latest_topic')}
345347
<View style={{ width: 24 }} />

0 commit comments

Comments
 (0)