Skip to content

Commit 9691422

Browse files
committed
Initial Commit
0 parents  commit 9691422

44 files changed

Lines changed: 17749 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.

.env.example

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
EXPO_PUBLIC_API_URL=http://localhost:3000
2+
EXPO_PUBLIC_SENTRY_DSN=

.github/workflows/ci.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
8+
jobs:
9+
validate:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: actions/checkout@v4
13+
- uses: actions/setup-node@v4
14+
with:
15+
node-version: 22
16+
cache: npm
17+
- run: npm ci
18+
- run: npm run typecheck
19+
- run: npm run lint
20+
- run: npm test

.gitignore

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files
2+
3+
# dependencies
4+
node_modules/
5+
6+
# Expo
7+
.expo/
8+
dist/
9+
dist-web/
10+
web-build/
11+
expo-env.d.ts
12+
13+
# Native
14+
.kotlin/
15+
*.orig.*
16+
*.jks
17+
*.p8
18+
*.p12
19+
*.key
20+
*.mobileprovision
21+
22+
# Metro
23+
.metro-health-check*
24+
25+
# debug
26+
npm-debug.*
27+
yarn-debug.*
28+
yarn-error.*
29+
30+
# macOS
31+
.DS_Store
32+
*.pem
33+
34+
# local env files
35+
.env
36+
.env*.local
37+
38+
# typescript
39+
*.tsbuildinfo
40+
41+
app-example
42+
43+
# generated native folders
44+
/ios
45+
/android

.vscode/extensions.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{ "recommendations": ["expo.vscode-expo-tools"] }

.vscode/settings.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"editor.codeActionsOnSave": {
3+
"source.fixAll": "explicit",
4+
"source.organizeImports": "explicit",
5+
"source.sortMembers": "explicit"
6+
}
7+
}

AGENTS.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Expo HAS CHANGED
2+
3+
Read the exact versioned docs at https://docs.expo.dev/versions/v54.0.0/ before writing any code.

README.md

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# Ruffl mobile app
2+
3+
Ruffl is an Expo/React Native mobile app for iOS and Android. The codebase is TypeScript with Expo Router, which keeps navigation and business-facing screens consistent across both platforms.
4+
5+
## Why this stack
6+
7+
- One maintainable codebase for iOS and Android
8+
- Fast physical-device testing through Expo Go during early development
9+
- A clear path to production development builds when native notification, monitoring, and upload integrations are enabled
10+
- Strict TypeScript plus separately testable domain helpers
11+
- Secure device storage for the bearer token through Expo SecureStore
12+
13+
## Install packages
14+
15+
Requirements:
16+
17+
- Node.js 20.19 or newer; Node 22 LTS is recommended
18+
- npm
19+
- Expo Go on an Android or iOS phone for the quickest local test
20+
21+
```powershell
22+
npm install
23+
```
24+
25+
## Test the app on a physical phone
26+
27+
1. Start `Ruffl-Backend` with `npm run dev`.
28+
2. Find the development computer's LAN IPv4 address with `ipconfig`.
29+
3. Copy `.env.example` to `.env`.
30+
4. Replace `localhost` in `EXPO_PUBLIC_API_URL` with that LAN address, for example `http://192.168.1.20:3000`.
31+
5. Make sure the phone and computer are on the same network.
32+
6. Start Expo:
33+
34+
```powershell
35+
npm start
36+
```
37+
38+
7. Scan the QR code with Expo Go.
39+
8. Tap **Commissioner** or **Maker** on the demo sign-in panel.
40+
41+
For local Expo Go testing this project intentionally targets Expo SDK 54. The current Expo documentation identifies SDK 54 as the compatible physical-device path during the SDK 57 transition.
42+
43+
## Test with an emulator
44+
45+
Start the backend and Expo, then:
46+
47+
```powershell
48+
npm run android
49+
```
50+
51+
Android Emulator commonly reaches the host at `http://10.0.2.2:3000`; set that as `EXPO_PUBLIC_API_URL`. iOS Simulator can normally use `http://localhost:3000`:
52+
53+
```powershell
54+
npm run ios
55+
```
56+
57+
The iOS Simulator requires macOS/Xcode. Android Studio is required for the Android emulator.
58+
59+
## Automated validation
60+
61+
```powershell
62+
npm run typecheck
63+
npm run lint
64+
npm test
65+
```
66+
67+
Tests cover price/deposit/payout calculations, progress percentage, and user-facing lifecycle labels. API permission and lifecycle tests live in `Ruffl-Backend`.
68+
69+
## Available product flows
70+
71+
- Commissioner and maker signup/login with one-tap local demo accounts
72+
- Role-aware home summaries and activity
73+
- Maker search, queue status, pricing, profiles, reviews, and waitlist joining
74+
- Structured commission requests and price negotiation
75+
- Explicitly simulated deposit and milestone releases
76+
- Ordered milestone updates and approvals
77+
- Shipping, receipt confirmation, review, and dispute entry points
78+
- One inbox over commission/direct/dispute/support conversation types
79+
- Maker-local price/payout calculator
80+
- Warning and suspension-aware session handling
81+
82+
## Production development builds
83+
84+
Expo Go is appropriate for the current integration-light development phase. Before app-store testing, install the Expo development client and create development builds:
85+
86+
```powershell
87+
npx expo install expo-dev-client
88+
npx eas-cli@latest build:configure
89+
npx eas-cli@latest build --profile development --platform android
90+
npx eas-cli@latest build --profile development --platform ios
91+
```
92+
93+
EAS requires an Expo account. A macOS build machine is not required when using EAS cloud builds, but Apple Developer and Google Play accounts are required for store distribution.
94+
95+
## Known boundary
96+
97+
Media picking/upload UI, native Expo Push registration, and Sentry initialization need the real service credentials and production development builds. The backend already defines the relevant data and upload-slot boundaries. All money actions are deliberately labelled simulated because there is no payment processor.
98+
99+
As of 26 July 2026, `npm audit --omit=dev` reports high-severity advisories in transitive Expo/React Native build-tool dependencies (`brace-expansion` and `postcss`) with no compatible fix published for the SDK 54 tree. The backend and admin production dependency audits are clean. Do not run `npm audit fix --force` blindly because it can move native packages outside Expo's supported version set; reassess the advisories when moving from Expo Go to a production development build.

app.json

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
{
2+
"expo": {
3+
"name": "Ruffl",
4+
"slug": "ruffl",
5+
"version": "1.0.0",
6+
"orientation": "portrait",
7+
"icon": "./assets/images/icon.png",
8+
"scheme": "ruffl",
9+
"userInterfaceStyle": "light",
10+
"newArchEnabled": true,
11+
"ios": {
12+
"supportsTablet": true,
13+
"bundleIdentifier": "app.ruffl.mobile",
14+
"config": {
15+
"usesNonExemptEncryption": false
16+
}
17+
},
18+
"android": {
19+
"adaptiveIcon": {
20+
"backgroundColor": "#F8F4EC",
21+
"foregroundImage": "./assets/images/android-icon-foreground.png",
22+
"backgroundImage": "./assets/images/android-icon-background.png",
23+
"monochromeImage": "./assets/images/android-icon-monochrome.png"
24+
},
25+
"edgeToEdgeEnabled": true,
26+
"predictiveBackGestureEnabled": false,
27+
"package": "app.ruffl.mobile"
28+
},
29+
"web": {
30+
"output": "static",
31+
"favicon": "./assets/images/favicon.png"
32+
},
33+
"plugins": [
34+
"expo-router",
35+
"expo-secure-store",
36+
[
37+
"expo-splash-screen",
38+
{
39+
"image": "./assets/images/splash-icon.png",
40+
"imageWidth": 200,
41+
"resizeMode": "contain",
42+
"backgroundColor": "#F8F4EC",
43+
"dark": {
44+
"backgroundColor": "#1D2A24"
45+
}
46+
}
47+
]
48+
],
49+
"experiments": {
50+
"typedRoutes": true,
51+
"reactCompiler": true
52+
}
53+
}
54+
}

app/(tabs)/_layout.tsx

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { Ionicons } from '@expo/vector-icons';
2+
import { Redirect, Tabs } from 'expo-router';
3+
4+
import { Loading, Screen } from '@/src/components/ui';
5+
import { useSession } from '@/src/context/session';
6+
import { colours } from '@/src/theme';
7+
8+
interface TabIconProps {
9+
color: string;
10+
focused: boolean;
11+
size: number;
12+
}
13+
14+
function tabIcon(
15+
active: keyof typeof Ionicons.glyphMap,
16+
inactive: keyof typeof Ionicons.glyphMap,
17+
) {
18+
return function TabBarIcon({ color, focused, size }: TabIconProps) {
19+
return <Ionicons color={color} name={focused ? active : inactive} size={size} />;
20+
};
21+
}
22+
23+
export default function TabLayout() {
24+
const { loading, restriction, user } = useSession();
25+
if (loading) {
26+
return (
27+
<Screen scroll={false}>
28+
<Loading />
29+
</Screen>
30+
);
31+
}
32+
if (restriction) return <Redirect href="/suspended" />;
33+
if (!user) return <Redirect href="/login" />;
34+
35+
return (
36+
<Tabs
37+
screenOptions={{
38+
headerShown: false,
39+
tabBarActiveTintColor: colours.moss,
40+
tabBarInactiveTintColor: colours.inkMuted,
41+
tabBarStyle: {
42+
backgroundColor: colours.surface,
43+
borderTopColor: colours.line,
44+
height: 80,
45+
paddingBottom: 18,
46+
paddingTop: 8,
47+
},
48+
tabBarLabelStyle: { fontSize: 11, fontWeight: '700' },
49+
}}>
50+
<Tabs.Screen
51+
name="index"
52+
options={{
53+
title: 'Home',
54+
tabBarIcon: tabIcon('home', 'home-outline'),
55+
}}
56+
/>
57+
<Tabs.Screen
58+
name="makers"
59+
options={{
60+
title: 'Discover',
61+
tabBarIcon: tabIcon('search', 'search-outline'),
62+
}}
63+
/>
64+
<Tabs.Screen
65+
name="commissions"
66+
options={{
67+
title: 'Projects',
68+
tabBarIcon: tabIcon('layers', 'layers-outline'),
69+
}}
70+
/>
71+
<Tabs.Screen
72+
name="inbox"
73+
options={{
74+
title: 'Messages',
75+
tabBarIcon: tabIcon('chatbubble-ellipses', 'chatbubble-ellipses-outline'),
76+
}}
77+
/>
78+
<Tabs.Screen
79+
name="profile"
80+
options={{
81+
title: 'You',
82+
tabBarIcon: tabIcon('person-circle', 'person-circle-outline'),
83+
}}
84+
/>
85+
</Tabs>
86+
);
87+
}

0 commit comments

Comments
 (0)