Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
node_modules/
.expo/
dist/
build/
.expo-shared/
.env
.DS_Store
web-build/
*.log
coverage/
75 changes: 75 additions & 0 deletions App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { NavigationContainer } from '@react-navigation/native';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import { TailwindProvider } from 'nativewind';
import { StatusBar } from 'expo-status-bar';
import { LinkingOptions } from '@react-navigation/native';
import { useMemo } from 'react';

import RootNavigator from '@/navigation/RootNavigator';
import { AuthProvider } from '@/hooks/useAuth';
import { RootStackParamList } from '@/types/navigation';

const queryClient = new QueryClient();

const App = () => {
const linking = useMemo<LinkingOptions<RootStackParamList>>(
() => ({
prefixes: ['fetchapp://', 'https://fetchapp.app'],
config: {
screens: {
Auth: {
screens: {
Login: 'login',
Register: 'register'
}
},
Main: {
screens: {
HomeTab: {
screens: {
Home: '',
ScanReceiptModal: 'scan'
}
},
SquadTab: {
screens: {
Squad: 'squad/:squadId?',
SquadInvite: 'squad/join/:squadId'
}
},
LeagueTab: {
screens: {
League: 'league'
}
},
ProfileTab: {
screens: {
Profile: 'profile'
}
}
}
}
}
}
}),
[]
);

return (
<GestureHandlerRootView style={{ flex: 1 }}>
<TailwindProvider>
<QueryClientProvider client={queryClient}>
<AuthProvider>
<NavigationContainer linking={linking}>
<RootNavigator />
<StatusBar style="dark" />
</NavigationContainer>
</AuthProvider>
</QueryClientProvider>
</TailwindProvider>
</GestureHandlerRootView>
);
};

export default App;
95 changes: 94 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,94 @@
# Second
# Fetch Rewards MVP – Squads & Leagues

This repository contains a React Native (Expo) MVP for Fetch Rewards that showcases the new **Squads** and **Leagues** experiences. The app is written in TypeScript, styled with NativeWind, and backed by Firebase Authentication and Firestore. Use it to explore receipt-sharing, collaborative goals, and weekly league leaderboards on both iOS and Android via Expo Go.

## ✨ Features
- Email/password authentication backed by Firebase Auth with automatic Firestore profile creation.
- **Squads**: create or join teams, share invite links, split receipt points, manage a shared shopping list, and track progress toward group goals with a playful mascot.
- **Receipt simulator**: quickly award random points and split them across squad members.
- **Weekly challenges** and duel callouts to encourage friendly competition within squads.
- **Leagues**: weekly leaderboards, streak multipliers, promotion highlights, and seasonal countdown messaging.
- Firestore seeding script that creates demo users, squads, and leagues for richer testing data.

## 🧱 Project Structure
```
.
├── App.tsx
├── app.config.ts
├── seed.js
├── src/
│ ├── components/
│ ├── hooks/
│ ├── navigation/
│ ├── screens/
│ ├── services/
│ ├── types/
│ └── utils/
└── tailwind.config.js
```

## 🛠️ Prerequisites
1. **Node.js** (LTS recommended). Install from [nodejs.org](https://nodejs.org/).
2. **Expo CLI** – install globally:
```bash
npm install -g expo-cli
```
3. **Firebase project** with:
- Web app configuration (API key, project ID, etc.).
- Firestore and Authentication enabled.
4. (Optional for seeding) **Firebase service account JSON** with read/write access to Firestore.

## ⚙️ Setup
1. Clone the repository and install dependencies:
```bash
git clone <repo-url>
cd fetch-rewards-mvp
npm install
```
2. Create a `.env` file in the project root and populate it with your Firebase keys:
```env
FIREBASE_API_KEY=your-api-key
FIREBASE_AUTH_DOMAIN=your-app.firebaseapp.com
FIREBASE_PROJECT_ID=your-project-id
FIREBASE_STORAGE_BUCKET=your-app.appspot.com
FIREBASE_MESSAGING_SENDER_ID=...
FIREBASE_APP_ID=...
FIREBASE_MEASUREMENT_ID=...
# Optional for seeding
FIREBASE_SERVICE_ACCOUNT_PATH=path/to/serviceAccount.json
```
> On Windows, paths can be absolute (`C:\\path\\to\\serviceAccount.json`) or relative to the project directory.

## 🧪 Seeding Firestore Data
Populate your Firestore project with 200 demo users, multiple squads, and league groups.

1. Ensure your service account file is accessible and referenced via `FIREBASE_SERVICE_ACCOUNT_PATH` (or set the `GOOGLE_APPLICATION_CREDENTIALS` environment variable).
2. Run the seeding script:
```bash
npm run seed
```
The script prints progress as it creates user documents, squads, and league groupings.

## 📱 Running the App
1. Start the Expo development server:
```bash
npm start
```
2. Press `i` or `a` to open iOS or Android simulators, or scan the QR code using **Expo Go** on your mobile device.
3. Register a new account directly in the app or sign in with seeded credentials that you manually add to Firebase Authentication.

## 🧭 Navigation Overview
- **Auth Stack**: Login and registration screens.
- **Main Tabs**:
- **Home**: receipt scanning simulator, squad goal snapshot, weekly highlights.
- **Squad**: squad management, mascot, shopping list, weekly challenges.
- **League**: weekly leaderboard, promotion summary, reward breakdowns.
- **Profile**: personal stats, streak tracker, logout.

## ✅ Notes
- All Firebase interactions rely on the configuration exposed via `app.config.ts`. Ensure your `.env` values are present **before** launching the app.
- The seed script creates Firestore documents only; to log in as a seeded user, manually create matching credentials in Firebase Authentication or register through the app and then update the Firestore profile.
- NativeWind powers the `className` styling on React Native components. Tailwind tokens can be extended via `tailwind.config.js`.
- Default Expo icon and splash assets are used so the repository stays binary-free for easier pull requests.

Enjoy exploring the Fetch Rewards Squads & Leagues MVP! 💥
39 changes: 39 additions & 0 deletions app.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import 'dotenv/config';
import type { ExpoConfig } from '@expo/config';

const config: ExpoConfig = {
name: 'Fetch Rewards MVP',
slug: 'fetch-rewards-mvp',
version: '0.1.0',
orientation: 'portrait',
scheme: 'fetchapp',
userInterfaceStyle: 'light',
// Using Expo's default icon and splash assets to keep the repository binary-free.
updates: {
fallbackToCacheTimeout: 0
},
assetBundlePatterns: ['**/*'],
ios: {
supportsTablet: true,
bundleIdentifier: 'com.fetchrewards.squadsleagues'
},
android: {
package: 'com.fetchrewards.squadsleagues'
},
extra: {
eas: {
projectId: '00000000-0000-0000-0000-000000000000'
},
firebase: {
apiKey: process.env.FIREBASE_API_KEY,
authDomain: process.env.FIREBASE_AUTH_DOMAIN,
projectId: process.env.FIREBASE_PROJECT_ID,
storageBucket: process.env.FIREBASE_STORAGE_BUCKET,
messagingSenderId: process.env.FIREBASE_MESSAGING_SENDER_ID,
appId: process.env.FIREBASE_APP_ID,
measurementId: process.env.FIREBASE_MEASUREMENT_ID
}
}
};

export default config;
18 changes: 18 additions & 0 deletions babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module.exports = function (api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
plugins: [
'nativewind/babel',
[
'module-resolver',
{
root: ['./'],
alias: {
'@': './src'
}
}
]
]
};
};
1 change: 1 addition & 0 deletions nativewind.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/// <reference types="nativewind/types" />
53 changes: 53 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"name": "fetch-rewards-squads-leagues",
"version": "0.1.0",
"private": true,
"main": "expo/AppEntry.js",
"scripts": {
"start": "expo start",
"android": "expo start --android",
"ios": "expo start --ios",
"web": "expo start --web",
"seed": "node seed.js"
},
"dependencies": {
"@react-navigation/bottom-tabs": "^6.5.11",
"@react-navigation/native": "^6.1.9",
"@react-navigation/native-stack": "^6.9.17",
"@react-navigation/stack": "^6.3.21",
"@tanstack/react-query": "^5.24.7",
"expo": "~50.0.3",
"expo-constants": "~15.4.5",
"expo-font": "~11.10.3",
"expo-linear-gradient": "~12.8.2",
"expo-linking": "~6.2.2",
"expo-secure-store": "~12.3.1",
"expo-splash-screen": "~0.26.5",
"expo-status-bar": "~1.11.1",
"@react-native-clipboard/clipboard": "^1.13.2",
"firebase": "^10.7.2",
"firebase-admin": "^11.10.1",
"nanoid": "^5.0.6",
"dotenv": "^16.3.1",
"nativewind": "^2.0.11",
"react": "18.2.0",
"react-native": "0.73.6",
"react-native-gesture-handler": "~2.14.0",
"react-native-reanimated": "~3.6.2",
"react-native-safe-area-context": "4.7.4",
"react-native-screens": "~3.29.0",
"react-native-svg": "^14.1.0",
"zustand": "^4.4.6"
},
"devDependencies": {
"@babel/core": "^7.20.0",
"@types/react": "^18.2.37",
"@types/react-native": "^0.73.0",
"@types/react-test-renderer": "18.0.0",
"autoprefixer": "^10.4.16",
"babel-preset-expo": "^10.0.1",
"postcss": "^8.4.31",
"tailwindcss": "^3.3.3",
"typescript": "^5.2.2"
}
}
6 changes: 6 additions & 0 deletions postcss.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {}
}
};
Loading