A React Native mobile application built with Expo, designed to facilitate help requests and offers within the METU community.
METU Help is a cross-platform mobile application that connects students and staff at Middle East Technical University (METU) to help each other. Users can post help requests, offer assistance, and browse available opportunities to contribute to the community.
Key Features:
- Browse help requests and offers
- Post and manage help requests
- Ask questions within the community
- User profile management
- Multi-language support (Turkish/English)
- Dark mode support
- Responsive design
Supported Platforms:
- iOS (Apple devices)
- Android (Google devices)
- Web (Browser-based)
| Category | Technology |
|---|---|
| Framework | React Native 0.81.5 / Expo 54.0.23 |
| Language | TypeScript 5.9.2 |
| Navigation | React Navigation 7.x |
| Backend | Firebase (Auth + Firestore) |
| Styling | React Native native components with theme system |
| Linting | ESLint 9.25.0 |
| Formatting | Prettier 3.6.2 |
| Build Tool | Expo CLI |
| Package Manager | npm/yarn |
-MetuAPP/
├── components/ # Reusable UI components
│ ├── Button.tsx
│ ├── Card.tsx
│ ├── ErrorBoundary.tsx
│ ├── HeaderTitle.tsx
│ ├── LanguageToggle.tsx
│ ├── ScreenFlatList.tsx
│ ├── ScreenKeyboardAwareScrollView.tsx
│ ├── ScreenScrollView.tsx
│ ├── Spacer.tsx
│ ├── ThemedText.tsx
│ └── ThemedView.tsx
│
├── screens/ # Screen components
│ ├── HomeScreen.tsx
│ ├── BrowseScreen.tsx
│ ├── AskQuestionScreen.tsx
│ ├── NeedHelpScreen.tsx
│ ├── OfferHelpScreen.tsx
│ ├── ProfileScreen.tsx
│ ├── PostNeedScreen.tsx
│ ├── QuestionDetailScreen.tsx
│ └── RequestDetailScreen.tsx
│
├── navigation/ # Navigation configuration
│ ├── MainTabNavigator.tsx
│ ├── HomeStackNavigator.tsx
│ ├── BrowseStackNavigator.tsx
│ ├── ProfileStackNavigator.tsx
│ └── screenOptions.ts
│
├── contexts/ # React Context for state management
│ └── LanguageContext.tsx
│
├── hooks/ # Custom React hooks
│ ├── useColorScheme.ts
│ ├── useColorScheme.web.ts
│ ├── useScreenInsets.ts
│ └── useTheme.ts
│
├── constants/ # Application constants
│ └── theme.ts
│
├── src/ # Source code
│ ├── firebase/ # Firebase configuration
│ │ └── firebaseConfig.js
│ ├── services/ # API services
│ │ └── helpRequestService.ts
│ ├── contexts/ # React contexts
│ │ └── AuthContext.tsx
│ └── types/ # TypeScript type definitions
│ └── helpRequest.ts
│
├── assets/ # Static assets
│ └── images/
│
├── scripts/ # Build and utility scripts
│ ├── build.js
│ └── landing-page-template.html
│
├── App.tsx # Root component
├── app.json # Expo configuration
├── package.json # Dependencies and scripts
├── tsconfig.json # TypeScript configuration
├── babel.config.js # Babel configuration
├── eslint.config.js # ESLint configuration
└── README.md # This file
This application uses Firebase for authentication and data storage (Firestore). Follow these steps to configure Firebase for development or production.
All Firebase credentials must be stored in a .env.local file in the project root. This file is never committed to version control for security.
Create a .env.local file with the following variables:
EXPO_PUBLIC_FIREBASE_API_KEY=your_api_key_here
EXPO_PUBLIC_FIREBASE_AUTH_DOMAIN=your_project.firebaseapp.com
EXPO_PUBLIC_FIREBASE_PROJECT_ID=your_project_id
EXPO_PUBLIC_FIREBASE_STORAGE_BUCKET=your_project.firebasestorage.app
EXPO_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=your_sender_id
EXPO_PUBLIC_FIREBASE_APP_ID=your_app_id-
Create a Firebase Project (if you haven't already):
- Go to Firebase Console
- Click "Add project" and follow the setup wizard
- Once created, select your project
-
Get Web App Credentials:
- In the Firebase Console, click the gear icon (⚙️) next to "Project Overview"
- Select "Project settings"
- Scroll down to "Your apps" section
- Click the web icon (
</>) to add a web app or select an existing one - Copy the configuration values to your
.env.localfile
-
Enable Firebase Services:
- Authentication: Go to "Authentication" → "Sign-in method" → Enable "Email/Password"
- Firestore: Go to "Firestore Database" → "Create database" → Choose production/test mode
The Firebase configuration is centralized in src/firebase/firebaseConfig.js. This file:
- ✅ Provides idempotent initialization (safe to call multiple times)
- ✅ Uses environment variables for configuration
- ✅ Works on both web and native platforms (iOS/Android)
- ✅ Exports helper functions for common Firestore operations
- ✅ Includes fail-fast error handling with clear messages
- ✅ Provides debug logging in development mode
Important: All Firebase operations in the codebase must use the exported functions from this config file. Never initialize Firebase directly in other files.
import { getAuthInstance } from '@/src/firebase/firebaseConfig';
import { signInWithEmailAndPassword } from 'firebase/auth';
// Get the Auth instance
const auth = getAuthInstance();
// Sign in a user
await signInWithEmailAndPassword(auth, email, password);import {
fetchHelpRequestsRealTime,
addHelpRequest
} from '@/src/firebase/firebaseConfig';
// Subscribe to real-time updates
const unsubscribe = fetchHelpRequestsRealTime((requests) => {
console.log('Help requests:', requests);
}, { category: 'academic' });
// Later, unsubscribe to stop listening
unsubscribe();
// Add a new help request
const requestId = await addHelpRequest(
{
title: 'Need help with calculus',
category: 'academic',
description: 'Need tutoring for calculus exam',
location: 'Library'
},
userId,
userEmail,
userName
);import { getFirestoreInstance } from '@/src/firebase/firebaseConfig';
import { collection, getDocs } from 'firebase/firestore';
// Get the Firestore instance
const db = getFirestoreInstance();
// Query a collection
const querySnapshot = await getDocs(collection(db, 'helpRequests'));
querySnapshot.forEach((doc) => {
console.log(doc.id, doc.data());
});The following helper functions are exported from firebaseConfig.js:
| Function | Description |
|---|---|
getAuthInstance() |
Get initialized Firebase Auth (throws if config missing) |
getAuthIfAvailable() |
Get Auth instance or undefined (non-throwing) |
getFirestoreInstance() |
Get initialized Firestore (throws if config missing) |
getFirestoreIfAvailable() |
Get Firestore instance or undefined (non-throwing) |
fetchHelpRequestsRealTime(callback, options) |
Subscribe to help requests with real-time updates |
addHelpRequest(data, userId, userEmail, userName) |
Create a new help request |
fetchQuestionsRealTime(callback, options) |
Subscribe to questions with real-time updates |
addQuestion(data, userId, userEmail, userName) |
Create a new question |
testFirebaseConnection() |
Test Firebase connection and configuration |
Before running the app, you can validate your Firebase configuration:
npm run test:firebaseThis script checks that all required environment variables are set in your .env.local file.
To verify Firebase is working correctly within your app:
import { testFirebaseConnection } from '@/src/firebase/firebaseConfig';
const status = await testFirebaseConnection();
console.log(status);
// {
// success: true,
// auth: true,
// firestore: true,
// message: "Firebase is properly configured and connected"
// }For development, you can start with test mode rules. For production, implement proper security rules:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Help Requests - Users can read all, but only create/update their own
match /helpRequests/{requestId} {
allow read: if request.auth != null;
allow create: if request.auth != null && request.auth.uid == request.resource.data.userId;
allow update, delete: if request.auth != null && request.auth.uid == resource.data.userId;
}
// Questions - Similar to help requests
match /questions/{questionId} {
allow read: if request.auth != null;
allow create: if request.auth != null && request.auth.uid == request.resource.data.userId;
allow update, delete: if request.auth != null && request.auth.uid == resource.data.userId;
}
}
}The Firebase configuration works seamlessly across:
- ✅ Web: Fully supported
- ✅ iOS: Fully supported via Expo
- ✅ Android: Fully supported via Expo
Environment variables not loading:
- Ensure
.env.localis in the project root - Restart the Expo development server:
npm start - Clear Metro cache:
npx expo start -c - Verify variable names use
EXPO_PUBLIC_prefix
"Firebase API key is missing" error:
- Check that
EXPO_PUBLIC_FIREBASE_API_KEYis set in.env.local - Verify no extra spaces or quotes around the value
- Restart the development server
Authentication errors:
- Ensure Email/Password authentication is enabled in Firebase Console
- Check that users have verified their email addresses
- Verify Firebase credentials are correct
Firestore permission errors:
- Update Firestore security rules in Firebase Console
- Ensure user is authenticated before accessing Firestore
- Check that userId matches in security rules
- ✅ Never commit
.env.local- It's in.gitignorefor security - ✅ Use environment-specific configs - Different
.env.localfor dev/staging/prod - ✅ Implement proper Firestore rules - Never use test mode in production
- ✅ Enable App Check - Protect against abuse (recommended for production)
- ✅ Monitor Firebase usage - Set up billing alerts and quotas
The project uses Prettier for code formatting and ESLint for linting.
npm run formatnpm run check:formatnpm run lintnpm run lint -- --fixAll files should use TypeScript (.tsx for React components, .ts for utilities).
Type Checking:
tsc --noEmit- Components: PascalCase (e.g.,
HomeScreen.tsx) - Hooks: camelCase prefixed with "use" (e.g.,
useTheme.ts) - Constants: UPPER_SNAKE_CASE
- Variables/Functions: camelCase
- Types/Interfaces: PascalCase
import React from 'react';
import { View, Text } from 'react-native';
interface ComponentProps {
title: string;
onPress?: () => void;
}
export const MyComponent: React.FC<ComponentProps> = ({
title,
onPress
}) => {
return (
<View>
<Text>{title}</Text>
</View>
);
};Use the useTheme hook for consistent styling:
import { useTheme } from '@/hooks/useTheme';
export const MyComponent = () => {
const { colors, spacing } = useTheme();
return (
<View style={{ backgroundColor: colors.background }}>
{/* Component */}
</View>
);
};Use the LanguageContext for internationalization:
import { useContext } from 'react';
import { LanguageContext } from '@/contexts/LanguageContext';
export const MyComponent = () => {
const { language, translate } = useContext(LanguageContext);
return <Text>{translate('key')}</Text>;
};- Create feature branch:
git checkout -b feature/feature-name- Make changes and commit:
git add .
git commit -m "feat: add new feature"- Push and create PR:
git push origin feature/feature-nameIf port 8081 is already in use:
# Kill the process using the port (Linux/macOS)
lsof -ti:8081 | xargs kill -9
# Or use a different port
npx expo start --web --port 3000If you experience build issues, clear cache:
# Clear all cache
rm -rf node_modules .expo dist
npm install
# Or use Expo's cache clearing
npx expo start --clearUpdate dependencies:
npm install
npx expo prebuild --clean# Clean iOS build
rm -rf ios
npx expo prebuild --clean
# Or reinstall pods
cd ios
rm -rf Pods Podfile.lock
pod install
cd ..# Clean Android build
rm -rf android
npx expo prebuild --clean
# Or clear Gradle cache
cd android
./gradlew clean
cd ..Ensure Metro bundler is running properly:
npx expo start --reset-cacheCheck credentials and app configuration:
npx eas credentials
npx eas build:list
npx eas submit:list- Store sensitive data (API keys, tokens) in environment variables
- Use
.envfiles (never commit to git) - Add
.envto.gitignore - Implement proper authentication mechanisms
- Validate all user inputs
- Use HTTPS for all API communications
- Keep dependencies updated regularly
- React Native Documentation
- Expo Documentation
- React Navigation Docs
- TypeScript Documentation
- Expo EAS Build
- App Store Connect
- Google Play Console
For issues, questions, or contributions:
- Check existing issues on GitHub
- Create a new issue with detailed description
- Fork the repository and create a pull request
- Follow the project's code style and conventions
This project is private and proprietary to METU Help.
Last Updated: December 2025 Version: 1.0.0