Build the essential features of MealSnap app with AI assistance
In this lesson, we'll implement the essential features of our MealSnap app using Firebase and Cursor. We'll focus on authentication, database integration, and the core image-based calorie calculation.
Key Takeaway: Cursor's AI capabilities dramatically accelerate the implementation of Firebase features, allowing rapid development of sophisticated functionality.
Let's break down the implementation of our core features step by step.
We'll start by creating an authentication service and login/signup screens:
"Successfully signed up a user. User appeared in Firebase console. Sign-in and sign-out working."
Let's create an authentication service using AI assistance:
// authService.js
import { auth } from './firebase-config';
import {
createUserWithEmailAndPassword,
signInWithEmailAndPassword,
signOut,
onAuthStateChanged
} from 'firebase/auth';
// Register a new user
export const registerUser = async (email, password) => {
try {
const userCredential = await createUserWithEmailAndPassword(auth, email, password);
return { user: userCredential.user, error: null };
} catch (error) {
return { user: null, error: error.message };
}
};
// Sign in existing user
export const signInUser = async (email, password) => {
try {
const userCredential = await signInWithEmailAndPassword(auth, email, password);
return { user: userCredential.user, error: null };
} catch (error) {
return { user: null, error: error.message };
}
};
// Sign out current user
export const signOutUser = async () => {
try {
await signOut(auth);
return { error: null };
} catch (error) {
return { error: error.message };
}
};
// Listen to auth state changes
export const subscribeToAuthChanges = (callback) => {
return onAuthStateChanged(auth, (user) => {
callback(user);
});
};
Next, we'll implement the login and registration screens:
// LoginScreen.js
import React, { useState } from 'react';
import {
View, Text, TextInput, TouchableOpacity,
StyleSheet, Alert, ActivityIndicator
} from 'react-native';
import { signInUser } from '../services/authService';
const LoginScreen = ({ navigation }) => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [loading, setLoading] = useState(false);
const handleLogin = async () => {
if (!email || !password) {
Alert.alert('Error', 'Please fill in all fields');
return;
}
setLoading(true);
const { user, error } = await signInUser(email, password);
setLoading(false);
if (error) {
Alert.alert('Error', error);
}
};
return (
<View style={styles.container}>
<Text style={styles.title}>Welcome to MealSnap</Text>
<Text style={styles.subtitle}>Sign in to continue</Text>
<TextInput
style={styles.input}
placeholder="Email"
value={email}
onChangeText={setEmail}
keyboardType="email-address"
autoCapitalize="none"
/>
<TextInput
style={styles.input}
placeholder="Password"
value={password}
onChangeText={setPassword}
secureTextEntry
/>
<TouchableOpacity
style={styles.button}
onPress={handleLogin}
disabled={loading}
>
{loading ? (
<ActivityIndicator color="#fff" />
) : (
<Text style={styles.buttonText}>Login</Text>
)}
</TouchableOpacity>
<TouchableOpacity
style={styles.button}
onPress={() => navigation.navigate('Register')}
>
<Text style={styles.link}>
Don't have an account? Sign Up
</Text>
</TouchableOpacity>
</View>
);
};
const styles = StyleSheet.create({
// Styles created by AI
});
Now we'll implement the main meal tracking screen with AI assistance:
// MealsScreen.js
import React, { useState, useEffect } from 'react';
import {
View, Text, FlatList, TouchableOpacity,
StyleSheet, Image
} from 'react-native';
import { db, auth } from '../services/firebase-config';
import {
collection, query, where, orderBy,
getDocs, Timestamp
} from 'firebase/firestore';
const MealsScreen = ({ navigation }) => {
const [meals, setMeals] = useState([]);
const [loading, setLoading] = useState(true);
const [viewMode, setViewMode] = useState('weekly'); // 'weekly' or 'monthly'
useEffect(() => {
fetchMeals();
}, [viewMode]);
const fetchMeals = async () => {
try {
setLoading(true);
const userId = auth.currentUser.uid;
// Calculate date range based on view mode
const today = new Date();
let startDate = new Date();
if (viewMode === 'weekly') {
startDate.setDate(today.getDate() - 7);
} else {
startDate.setMonth(today.getMonth() - 1);
}
const mealsRef = collection(db, 'meals');
const q = query(
mealsRef,
where('userId', '==', userId),
where('date', '>=', Timestamp.fromDate(startDate)),
orderBy('date', 'desc')
);
const querySnapshot = await getDocs(q);
const mealsList = [];
querySnapshot.forEach((doc) => {
mealsList.push({
id: doc.id,
...doc.data(),
date: doc.data().date.toDate()
});
});
setMeals(mealsList);
} catch (error) {
console.error('Error fetching meals:', error);
} finally {
setLoading(false);
}
};
const renderMealItem = ({ item }) => (
<TouchableOpacity
style={styles.mealCard}
onPress={() => navigation.navigate('MealDetail', { mealId: item.id })}
>
<Image
source={{ uri: item.imageUrl }}
style={styles.mealImage}
/>
<View style={styles.mealInfo}>
<Text style={styles.mealName}>{item.name}</Text>
<Text style={styles.mealCalories}>{item.calories} calories</Text>
<Text style={styles.mealDate}>
{item.date.toLocaleDateString()}
</Text>
</View>
</TouchableOpacity>
);
return (
<View style={styles.container}>
<View style={styles.header}>
<Text style={styles.title}>Your Meals</Text>
<View style={styles.viewToggle}>
<TouchableOpacity
style={[
styles.toggleButton,
viewMode === 'weekly' && styles.activeToggle
]}
onPress={() => setViewMode('weekly')}
>
<Text style={styles.toggleText}>Weekly</Text>
</TouchableOpacity>
<TouchableOpacity
style={[
styles.toggleButton,
viewMode === 'monthly' && styles.activeToggle
]}
onPress={() => setViewMode('monthly')}
>
<Text style={styles.toggleText}>Monthly</Text>
</TouchableOpacity>
</View>
</View>
<FlatList
data={meals}
renderItem={renderMealItem}
keyExtractor={item => item.id}
contentContainerStyle={styles.mealsList}
ListEmptyComponent={
<Text style={styles.emptyText}>
No meals recorded in this period
</Text>
}
/>
<TouchableOpacity
style={styles.addButton}
onPress={() => navigation.navigate('AddMeal')}
>
<Text style={styles.addButtonText}>+</Text>
</TouchableOpacity>
</View>
);
};
const styles = StyleSheet.create({
// Styles generated by AI
});
When implementing these features, we encountered some common issues and resolved them using AI:
Common Error:
Conflicting routes and layout components
Solution:
Use Cursor's terminal to check error logs and fix routing configuration
Common Error:
TypeScript files generated instead of JavaScript
Solution:
Update Cursor rules and explicitly instruct AI to use JavaScript
Using Cursor's checkpoint feature, we could easily roll back changes when needed and try again with clearer instructions.
Using what you've learned, implement a user profile screen for the MealSnap app that allows users to:
Use Cursor and AI to generate the necessary components and Firebase integration code.
Share your implementation in our community