Skip to content

Commit 307b829

Browse files
feat: scaffold mobile app (Expo + React Native)
- Expo 52 with Expo Router v4 (file-based routing) - NativeWind v4 for Tailwind-style styling - Firebase Auth + JWT integration with SecureStore - i18n setup (en/ar) with RTL support - Atomic component structure (atoms, molecules, organisms) - API client with token refresh interceptor - Design tokens matching web frontend (#092601 brand green) - Navigation skeleton (auth flow + 4-tab dashboard) - Placeholder screens (home, courses, library, profile) - Base UI components (Button, Input, Badge, Avatar, etc.) - TypeScript types matching backend contracts - EAS Build config for iOS/Android - CONTRIBUTING.md for mobile contributors
1 parent 331af3b commit 307b829

61 files changed

Lines changed: 2644 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

mobile/.gitignore

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Dependencies
2+
node_modules/
3+
4+
# Expo
5+
.expo/
6+
dist/
7+
web-build/
8+
9+
# Native
10+
*.jks
11+
*.p8
12+
*.p12
13+
*.key
14+
*.mobileprovision
15+
*.orig.*
16+
17+
# Metro
18+
.metro-health-check*
19+
20+
# Environment
21+
.env
22+
.env.local
23+
.env.*.local
24+
25+
# IDE
26+
.vscode/
27+
.idea/
28+
*.swp
29+
*.swo
30+
*~
31+
32+
# OS
33+
.DS_Store
34+
Thumbs.db
35+
36+
# Build
37+
*.apk
38+
*.aab
39+
*.ipa
40+
41+
# EAS
42+
eas-build-*.json
43+
44+
# TypeScript
45+
*.tsbuildinfo
46+
47+
# Testing
48+
coverage/
49+
50+
# Debug
51+
npm-debug.*
52+
yarn-debug.*
53+
yarn-error.*

mobile/CONTRIBUTING.md

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
# Deen Bridge Mobile
2+
3+
React Native (Expo) mobile app for Deen Bridge — authentic Islamic education on iOS and Android.
4+
5+
## Prerequisites
6+
7+
- Node.js 20+
8+
- npm or yarn
9+
- Expo CLI (`npm install -g expo-cli`)
10+
- **iOS**: Xcode 15+ (Mac only) + CocoaPods
11+
- **Android**: Android Studio + Android SDK 34+
12+
13+
## Setup
14+
15+
```bash
16+
cd dnb-frontend/mobile
17+
npm install
18+
19+
# Download fonts (see assets/fonts/README.md)
20+
# Then place .ttf files in assets/fonts/
21+
22+
# Start development
23+
npx expo start
24+
```
25+
26+
Scan the QR code with Expo Go (iOS/Android) or run on a simulator:
27+
- iOS: `npx expo start --ios`
28+
- Android: `npx expo start --android`
29+
30+
## Environment Variables
31+
32+
Create a `.env` file in the `mobile/` directory:
33+
34+
```
35+
EXPO_PUBLIC_API_URL=http://localhost:5000
36+
EXPO_PUBLIC_AI_API_URL=http://localhost:8000
37+
EXPO_PUBLIC_STELLAR_NETWORK=testnet
38+
EXPO_PUBLIC_FIREBASE_API_KEY=your_key
39+
EXPO_PUBLIC_FIREBASE_AUTH_DOMAIN=your_domain
40+
EXPO_PUBLIC_FIREBASE_PROJECT_ID=your_project
41+
EXPO_PUBLIC_FIREBASE_STORAGE_BUCKET=your_bucket
42+
EXPO_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=your_sender_id
43+
EXPO_PUBLIC_FIREBASE_APP_ID=your_app_id
44+
EXPO_PUBLIC_CLOUDINARY_CLOUD_NAME=your_cloud_name
45+
```
46+
47+
## Architecture
48+
49+
```
50+
mobile/
51+
├── app/ # Expo Router (file-based routing)
52+
│ ├── _layout.tsx # Root layout — providers, fonts, splash
53+
│ ├── (auth)/ # Auth group — login, signup
54+
│ ├── (tabs)/ # Tab navigator — home, courses, library, profile
55+
│ └── +not-found.tsx # 404 screen
56+
├── components/
57+
│ ├── atoms/ # Primitives — Button, Input, Badge, Avatar
58+
│ ├── molecules/ # Composed — CourseCard, SearchBar, EmptyState
59+
│ ├── organisms/ # Complex — CourseList
60+
│ └── ui/ # Layout — ScreenWrapper, LoadingScreen
61+
├── lib/
62+
│ ├── api/ # Axios client + endpoint constants
63+
│ ├── auth/ # AuthContext (Firebase + JWT)
64+
│ ├── config/ # env.ts, theme.ts
65+
│ ├── hooks/ # useTheme, useAuth
66+
│ └── utils/ # storage, format helpers
67+
├── constants/ # Colors, spacing, typography tokens
68+
├── types/ # TypeScript interfaces
69+
├── i18n/ # English + Arabic translations
70+
└── assets/ # Fonts, images
71+
```
72+
73+
### Key Patterns
74+
75+
| Concern | Approach |
76+
|---------|----------|
77+
| Navigation | Expo Router v4 (file-based, typed routes) |
78+
| Styling | NativeWind v4 (Tailwind CSS for React Native) |
79+
| State | React Context (Auth, Theme) |
80+
| API | Axios with JWT interceptor + token refresh |
81+
| i18n | i18next + react-i18next, RTL via I18nManager |
82+
| Auth | Firebase Auth + backend JWT via SecureStore |
83+
| Types | Shared interfaces in `types/index.ts` |
84+
85+
## Adding a New Screen
86+
87+
1. Create the file in `app/` under the appropriate group:
88+
- Auth screens: `app/(auth)/your-screen.tsx`
89+
- Tab screens: `app/(tabs)/your-tab.tsx`
90+
- Detail screens: `app/your-screen.tsx`
91+
92+
2. Add navigation entry if needed in the parent `_layout.tsx`
93+
94+
3. Use existing atoms/molecules for UI:
95+
```tsx
96+
import { Button, Input, Badge } from "@/components";
97+
import { ScreenWrapper } from "@/components/ui";
98+
```
99+
100+
4. Add translations to `i18n/en.json` and `i18n/ar.json`
101+
102+
## Adding a New Component
103+
104+
Follow the atomic design hierarchy:
105+
106+
- **Atom**: Single-purpose primitive (Button, Input, Badge)
107+
- **Molecule**: Small composition of atoms (CourseCard, SearchBar)
108+
- **Organism**: Complex feature component (CourseList, DashboardHeader)
109+
110+
```tsx
111+
// components/atoms/YourComponent.tsx
112+
import { View, Text } from "react-native";
113+
114+
interface YourComponentProps {
115+
title: string;
116+
}
117+
118+
export function YourComponent({ title }: YourComponentProps) {
119+
return (
120+
<View>
121+
<Text>{title}</Text>
122+
</View>
123+
);
124+
}
125+
```
126+
127+
## Adding a New Hook
128+
129+
Create in `lib/hooks/`:
130+
131+
```tsx
132+
// lib/hooks/useYourHook.ts
133+
import { useState, useEffect } from "react";
134+
135+
export function useYourHook() {
136+
const [data, setData] = useState(null);
137+
// ...
138+
return { data };
139+
}
140+
```
141+
142+
## Coding Conventions
143+
144+
- **TypeScript** everywhere — no `.js` files
145+
- **Named exports** for components and functions
146+
- **Path aliases**: Use `@/` for imports (e.g., `@/components/atoms/Button`)
147+
- **Functional components** only — no class components
148+
- **NativeWind** for styling — prefer `className` over `style` when possible
149+
- **i18n** for all user-facing text — never hardcode strings
150+
- **RTL-aware** — use `flexDirection` logic, test in Arabic mode
151+
152+
## Building
153+
154+
### Development
155+
```bash
156+
npx expo start
157+
```
158+
159+
### Preview (internal testing)
160+
```bash
161+
npx eas build --profile preview --platform ios
162+
npx eas build --profile preview --platform android
163+
```
164+
165+
### Production
166+
```bash
167+
npx eas build --profile production --platform ios
168+
npx eas build --profile production --platform android
169+
```
170+
171+
## Testing
172+
173+
```bash
174+
npx expo start # Test on device/simulator
175+
npx expo start --ios # iOS simulator only
176+
npx expo start --android # Android emulator only
177+
```
178+
179+
Test RTL by switching device language to Arabic.
180+
181+
## Contributing
182+
183+
1. Pick an issue from the `mobile` label
184+
2. Create a branch: `feat/your-feature` or `fix/your-fix`
185+
3. Follow the coding conventions above
186+
4. Test on both iOS and Android
187+
5. Submit a PR targeting `dev` branch
188+
189+
### Issue Labels
190+
191+
- `mobile` — Mobile app issues
192+
- `mobile:screen` — New screen implementations
193+
- `mobile:component` — New or updated components
194+
- `mobile:feature` — Feature integration (Stellar, Jitsi, etc.)
195+
- `mobile:bug` — Mobile-specific bugs
196+
197+
## Useful Links
198+
199+
- [Expo Docs](https://docs.expo.dev)
200+
- [Expo Router](https://docs.expo.dev/router/introduction)
201+
- [NativeWind](https://www.nativewind.dev)
202+
- [React Navigation](https://reactnavigation.org)
203+
- [Firebase React Native](https://rnfirebase.io)

mobile/app.json

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
{
2+
"expo": {
3+
"name": "Deen Bridge",
4+
"slug": "deenbridge",
5+
"version": "0.1.0",
6+
"orientation": "portrait",
7+
"icon": "./assets/images/icon.png",
8+
"scheme": "deenbridge",
9+
"userInterfaceStyle": "automatic",
10+
"newArchEnabled": true,
11+
"splash": {
12+
"image": "./assets/images/splash.png",
13+
"resizeMode": "contain",
14+
"backgroundColor": "#092601"
15+
},
16+
"ios": {
17+
"supportsTablet": true,
18+
"bundleIdentifier": "com.deenbridge.app",
19+
"infoPlist": {
20+
"NSCameraUsageDescription": "Deen Bridge needs camera access for liveness verification and profile photos.",
21+
"NSPhotoLibraryUsageDescription": "Deen Bridge needs photo library access to upload profile photos and course media."
22+
}
23+
},
24+
"android": {
25+
"adaptiveIcon": {
26+
"foregroundImage": "./assets/images/adaptive-icon.png",
27+
"backgroundColor": "#092601"
28+
},
29+
"package": "com.deenbridge.app",
30+
"permissions": [
31+
"CAMERA",
32+
"READ_EXTERNAL_STORAGE",
33+
"WRITE_EXTERNAL_STORAGE"
34+
]
35+
},
36+
"web": {
37+
"bundler": "metro",
38+
"favicon": "./assets/images/favicon.png"
39+
},
40+
"plugins": [
41+
"expo-router",
42+
[
43+
"expo-font",
44+
{
45+
"fonts": [
46+
"./assets/fonts/GeistMono-Regular.ttf",
47+
"./assets/fonts/Geist-Regular.ttf",
48+
"./assets/fonts/Geist-Bold.ttf",
49+
"./assets/fonts/IBMPlexArabic-Regular.ttf",
50+
"./assets/fonts/IBMPlexArabic-Bold.ttf"
51+
]
52+
}
53+
]
54+
],
55+
"experiments": {
56+
"typedRoutes": true
57+
}
58+
}
59+
}

mobile/app/(auth)/_layout.tsx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { Redirect, Stack } from "expo-router";
2+
import { useAuth } from "@/lib/auth";
3+
import { View, ActivityIndicator } from "react-native";
4+
5+
export default function AuthLayout() {
6+
const { isAuthenticated, isLoading } = useAuth();
7+
8+
if (isLoading) {
9+
return (
10+
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
11+
<ActivityIndicator size="large" color="#092601" />
12+
</View>
13+
);
14+
}
15+
16+
if (isAuthenticated) {
17+
return <Redirect href="/(tabs)" />;
18+
}
19+
20+
return (
21+
<Stack screenOptions={{ headerShown: false }}>
22+
<Stack.Screen name="login" />
23+
<Stack.Screen name="signup" />
24+
</Stack>
25+
);
26+
}

0 commit comments

Comments
 (0)