# Project export: Eatopia

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: Cal Hacks 10.0
- Tagline: A utopia for healthy eaters
- Devpost: https://devpost.com/software/eatopia
- GitHub: https://github.com/ViralDam/CalHacks10
- Video: https://www.youtube.com/embed/k9lNYWPzFxI?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 2 GitHub contributor(s) — Viral Damaniya (16 commits), Akaash Trivedi (5 commits)

## Devpost submission (written by the team)

### Inspiration

:- The inspiration behind Eat-O-Pia comes from its name, which is to build a utopia for healthy eaters. Unhealthy dietary habits and their detrimental impact on the human body is a profound concern challenging today’s generation. According to WHO, unhealthy diets are one of the leading risk factors for diseases such as obesity, heart disease and diabetes, which collectively contribute to millions of deaths per year. In a world overrun by fast food chains, our platform aims to empower individuals to make healthier food choices, one meal at a time. Our application brings our vision to life by providing a social media platform that fosters a supportive community which serves as a beacon of encouragement to improve everyone’s food habits. Purpose:- At the heart of Eat-o-pia is a simple yet profound purpose: to build a community of fit foodies and promote healthy eating. We envision a world where every meal is a celebration of well-being, and our app is the catalyst for this transformation. We understand that eating is a social experience, and by connecting people with a shared passion for nutritious food, we can make the journey to a healthier lifestyle more enjoyable and sustainable. Features:- Recipe Exchange: Users can share photos of their delicious, healthy meals and provide the recipes, inspiring others to explore nutritious cooking. Health Rating: Our unique health rating system empowers users to rate the healthiness of shared dishes, promoting accountability and education. Progress Tracking: Users receive feedback on their food choices based on community ratings, fostering self-awareness and motivating better dietary decisions. Monthly Challenges: Engage in various healthy eating challenges within our "Foodie" community, promoting exploration and growth. Avo, Your AI Companion: Meet Avo, our AI chatbot companion, designed to teach users how to cook the dishes they aspire to try, providing guidance and answering culinary questions. Encouragement to Cook: Our platform is a wellspring of inspiration for cooking nutritious food at home, making healthy eating accessible and enjoyable. How Did we build it? The development process can be broken down in 5 steps:- 1) Ideation:- The project began with a clear conceptualization of the idea. The app's core features, were decided based on the platform's primary mission of promoting healthy eating. 2) Design and UI:- User experience (UX) and user interface (UI) design were critical components, ensuring that the app would be intuitive and appealing to users. Design decisions were made to align with the project's mission, creating an engaging and user-friendly environment for users to explore and share nutritious food experiences. 3) Technology Stack Selection:- Choosing the right technology stack was crucial for the project's success. The team selected a combination of technologies and tools that included: CockroachDB: As the database solution, CockroachDB was chosen for its distributed, resilient, and highly available architecture. It provided the foundation for data management and real-time updates. together.ai: The development team leveraged together.ai's natural language processing capabilities to create Avo, the AI chatbot companion. This technology helped in providing conversational and educational support to users. React Native: For building the mobile app, React Native, a popular framework for cross-platform development, was used. It allowed for the development of a single codebase that could run on both iOS and Android. Web Technologies: For creating the interactive web platform, standard web technologies like HTML, CSS, and JavaScript were employed. 4) Development:- Implementing and building the mobile application 5) Testing:- User testing (tested by different users including our team members) What did we learn? 1) The power of community: As the saying goes “A community is like a mosaic. The pieces alone are beautiful, but together they create a masterpiece”. Thus the best way to address the issue of unhealthy food habits is to build a vibrant community of individuals passionate about healthy eating where each individual inspires and motivates others to make better food choices thus helping everyone collective achieve their goals. 2) Integration of together.ai API: Our journey led us to harness the potent capabilities of the together.ai API, marking our first foray into this innovative technology. This novel experience equipped us with the means to create 'Avo,' our artificial intelligence chatbot. The process of working with this API and leveraging its diverse models to shape Avo's conversational and educational prowess was instrumental in achieving our mission. This new endeavor introduced us to the exciting world of AI, expanding our horizons and adding a layer of innovation to our project. 3) Impact on public health: Our journey has taught us that the pursuit of healthier living is not just a personal endeavor; it's a collective mission that thrives on the support, inspiration, and education shared within a vibrant community. We've learned that every small step towards better food choices can lead to significant improvements in our overall well-being. Challenges Faced ? : 1) Keeping users engaged:- Keeping users engaged and motivated to make healthier choices was one of the major challenges. Community challenges was one way to keep users engaged. 2) Scalability:- As the user base of Eat-O-Pia grew, ensuring that the platform could handle real-time interactions and maintain data integrity became a challenge 3) User-Generated Content Quality:- The third significant challenge we encountered in the development of Eat-O-Pia was maintaining the quality and integrity of user-generated content. User-generated content is at the core of our platform, as users share photos of their meals, rate the healthiness of other dishes, and exchange recipes. Ensuring that the content contributed positively to the community and aligned with our mission of promoting healthy eating was a critical challenge. 4)Professional and Ethical Content: We faced the challenge of ensuring that the content shared on the platform was not only accurate in terms of healthiness ratings but also related and ethical. It was vital to prevent the spread of misleading or harmful information. We addressed this challenge by implementing content guidelines and a checkbox for accepting the terms of ethical content while creating account. Languages:- JavaScript (for frontend development) Python (for AI chatbot development) Frameworks: React Native (for mobile app development) Flask (for connecting database to front-end)c Web Technologies: HTML, CSS (for web platform development) AI and Natural Language Processing:- together.ai (for AI chatbot development) Databases:- 1.CockroachDB (distributed, resilient, and highly available database) 2.Firebase (storage, authentication, and highly available database) Cloud Services:- Intel (utilized for cloud infrastructure and services) Mobile Development Tools:- Expo (for making a universal native app for iOS, Android, and the web) Version Control:- Git (for version control and collaboration) These technologies, including Intel for cloud services, were thoughtfully chosen to create a robust, user-friendly, and scalable platform that fulfills the vision of Eat-O-Pia in promoting healthy eating and building a supportive community.

## README (from the GitHub repository)

No README available.

## Detected evidence (automated analysis)

Indexed codebase: 22 recognized source files, 60 KB.
- Firebase (technology) — detected in the code
- JavaScript (language) — detected in the code
- React (technology) — detected in the code
- Flask (technology) — claimed on Devpost, not found in the code
- Python (language) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (26 of 26)

```
.DS_Store
.gitignore
savory-sphere/.gitignore
savory-sphere/app.config.js
savory-sphere/app/_layout.jsx
savory-sphere/app/components/EmojiSlider.jsx
savory-sphere/app/components/PostCard.jsx
savory-sphere/app/createProfile.jsx
savory-sphere/app/index.jsx
savory-sphere/app/register.jsx
savory-sphere/app/signIn.jsx
savory-sphere/app/tabs/_layout.jsx
savory-sphere/app/tabs/chat.jsx
savory-sphere/app/tabs/createPost.jsx
savory-sphere/app/tabs/index.jsx
savory-sphere/app/tabs/profile/_layout.jsx
savory-sphere/app/tabs/profile/foodies.jsx
savory-sphere/app/tabs/profile/index.jsx
savory-sphere/babel.config.js
savory-sphere/metro.config.js
savory-sphere/package.json
savory-sphere/src/firebase.js
savory-sphere/src/redux/actions.js
savory-sphere/src/redux/reducer.js
savory-sphere/src/redux/store.js
savory-sphere/src/utils/constants.js
```

### Dependencies

- savory-sphere/package.json: @babel/core@^7.20.0, @react-native-async-storage/async-storage@1.18.2, @react-native-community/datetimepicker@7.2.0, @react-native-community/slider@^4.4.3, @reduxjs/toolkit@^1.9.7, axios@^1.6.0, expo@~49.0.15, expo-checkbox@~2.4.0, expo-constants@~14.4.2, expo-image@~1.3.4, expo-image-picker@~14.3.2, expo-linking@~5.0.2, expo-router@^2.0.0, expo-status-bar@~1.6.0, firebase@10.3.1, getstream@^8.4.0, react@18.2.0, react-native@0.72.6, react-native-gesture-handler@~2.12.0, react-native-progress@^5.0.1, react-native-reanimated@~3.3.0, react-native-reanimated-carousel@^3.5.1, react-native-safe-area-context@4.6.3, react-native-screens@~3.22.0, react-native-svg@^13.14.0, react-redux@^8.1.3, redux@^4.2.1

### Recent commits (newest first)

- hfgf
- sorted foodies
- redux feed
- data out
- foodies done
- User feed
- foodies route
- profile route
- chat final
- Added AI AVo button
- chat bg
- Community rating for food
- workig chat
- postinng and saving to db
- Feed to index page
- resolved conflict
- firebase to redux
- bottom tabs
- create profile complete
- create profile

## Key source files (fetched from GitHub, selected and truncated for size)

### savory-sphere/package.json

```
{
  "name": "savory-sphere",
  "version": "1.0.0",
  "main": "expo-router/entry",
  "scripts": {
    "start": "expo start",
    "android": "expo start --android",
    "ios": "expo start --ios",
    "web": "expo start --web"
  },
  "dependencies": {
    "@react-native-async-storage/async-storage": "1.18.2",
    "@react-native-community/datetimepicker": "7.2.0",
    "@react-native-community/slider": "^4.4.3",
    "@reduxjs/toolkit": "^1.9.7",
    "axios": "^1.6.0",
    "expo": "~49.0.15",
    "expo-constants": "~14.4.2",
    "expo-image": "~1.3.4",
    "expo-image-picker": "~14.3.2",
    "expo-linking": "~5.0.2",
    "expo-router": "^2.0.0",
    "expo-status-bar": "~1.6.0",
    "firebase": "10.3.1",
    "getstream": "^8.4.0",
    "react": "18.2.0",
    "react-native": "0.72.6",
    "react-native-gesture-handler": "~2.12.0",
    "react-native-progress": "^5.0.1",
    "react-native-reanimated": "~3.3.0",
    "react-native-reanimated-carousel": "^3.5.1",
    "react-native-safe-area-context": "4.6.3",
    "react-native-screens": "~3.22.0",
    "react-native-svg": "^13.14.0",
    "react-redux": "^8.1.3",
    "redux": "^4.2.1",
    "expo-checkbox": "~2.4.0"
  },
  "devDependencies": {
    "@babel/core": "^7.20.0"
  },
  "private": true
}

```

### savory-sphere/app/_layout.jsx

```javascript
import { Stack } from 'expo-router';
import { Provider } from "react-redux";
import store from '../src/redux/store';

const AppLayout = () => {
    return (
        <Provider store={store}>
            <Stack
                screenOptions={{
                    headerShown: false
                }}
            >
                <Stack.Screen
                    name="signIn"
                />
                <Stack.Screen
                    name="register"
                />
            </Stack>
         </Provider>
    );
}

export default AppLayout;

```

### savory-sphere/app/index.jsx

```javascript
import { useEffect, useState } from 'react';
import { StyleSheet } from 'react-native';
import { auth, db } from '../src/firebase';
import { Redirect, router } from 'expo-router';
import { useDispatch } from 'react-redux';
import { setFoodie, setUserBio, setUserDob, setUserEmail, setUserName, setUserPhoto, setUserUid } from '../src/redux/actions';
import { doc, getDoc, Timestamp } from "firebase/firestore";

const compare = (a,b) => {
    if ( a.name < b.name ){
        return -1;
      }
      if ( a.name > b.name ){
        return 1;
      }
      return 0;
}

export default function AppLayout() {
    const [initializing, setInitializing] = useState(true);
    const [user, setUser] = useState();
    const dispatch = useDispatch();

    // Handle user state changes
    function onAuthStateChanged(user) {
        setUser(user);
        if (initializing) setInitializing(false);
    }

    useEffect(() => {
        const subscriber = auth.onAuthStateChanged(onAuthStateChanged);
        return subscriber; // unsubscribe on unmount
    }, []);

    useEffect(() => {
        if (user) {
            const docRef = doc(db, "users", user.uid);
            async function getData() {
                const docSnap = await getDoc(docRef);

                if (docSnap.exists()) {
                    const docData = docSnap.data()
                    const timestamp = new Timestamp(docData.dob.seconds, docData.dob.nanoseconds)
                    dispatch(setUserName(docData.name))
                    dispatch(setUserEmail(user.email))
                    dispatch(setUserPhoto(docData.photoUrl))
                    dispatch(setUserBio(docData.bio))
                    dispatch(setUserDob(timestamp.toDate().toString()))
                    dispatch(setUserUid(user.uid))
                    sortedFoodies = docData.foodies.sort(compare);
                    dispatch(setFoodie(sortedFoodies))
                    router.replace('tabs');
                } else {
                    console.log("No such document!");
                    router.replace('createProfile');
                }
            }
            getData()
        }
    }, [user]);

    if (initializing) return null;

    if (!user) {
        return (
            <Redirect href={'signIn'} />
        )
    }

}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        backgroundColor: '#fff',
        alignItems: 'center',
        justifyContent: 'center',
    },
});

```

### savory-sphere/app/tabs/_layout.jsx

```javascript
import { Tabs } from "expo-router/tabs";
import Ionicons from '@expo/vector-icons/Ionicons';
import { COLORS } from "../../src/utils/constants";

const TabLayout = () => {

    const getIconName = (routeName, isFocused) => {
        switch (routeName) {
            case 'index':
                return isFocused
                    ? 'ios-home'
                    : 'ios-home-outline';
            case 'createPost':
                return isFocused
                    ? 'ios-add-circle'
                    : 'ios-add-circle-outline';
            case 'chat':
                return isFocused
                    ? 'ios-chatbox'
                    : 'ios-chatbox-outline';
            case 'profile':
                return isFocused
                    ? 'ios-person'
                    : 'ios-person-outline';
        }
        return 'alert-circle';
    }

    return (
        <Tabs
            screenOptions={({ route }) => ({
                headerShown: false,
                tabBarIcon: ({ focused, color, size }) => {
                    const { name } = route;
                    return <Ionicons name={getIconName(name, focused)} size={size} color={color} />;
                },
                tabBarActiveTintColor: COLORS.BACKGROUND,
                tabBarInactiveTintColor: COLORS.BACKGROUND,
                tabBarStyle: {
                    backgroundColor: COLORS.PRIMARY,
                },
            })}>
            <Tabs.Screen
                name='index'
                options={{
                    title: "Explore"
                }}
            />
            <Tabs.Screen
                name='createPost'
                options={{
                    title: "Post"
                }}
            />
            <Tabs.Screen
                name='chat'
                options={{
                    title: "Ask Avo"
                }}
            />
            <Tabs.Screen
                name='profile'
                options={{
                    title: "Profile"
                }}
            />
        </Tabs>
    );
}

export default TabLayout;
```

### savory-sphere/app/tabs/index.jsx

```javascript
import { router, Link } from 'expo-router'
import React, { useState, useEffect } from 'react';
import { Text, View, StyleSheet, FlatList, SafeAreaView, TouchableOpacity, Dimensions } from "react-native";
import { COLORS } from '../../src/utils/constants';
import Slider from '@react-native-community/slider';
import { FontAwesome } from '@expo/vector-icons';
import Constants from 'expo-constants';
import { Image } from 'expo-image';
import { getFeed } from '../../getData';
import Carousel from 'react-native-reanimated-carousel';

const logo = require('../../assets/images/logo-white.png');

const Tabs = () => {

    const updateSliderValue = (itemId, newValue) => {
        setFeed(feed.map(item =>
            item.id === itemId ? { ...item, sliderValue: newValue } : item
        ));
    };

    const challenges = ['https://static.wixstatic.com/media/78f01e_4cad7cf5e4df4bb4bac80f005e3f43c7~mv2.jpg/v1/fit/w_1000%2Ch_1000%2Cal_c%2Cq_80,enc_auto/file.jpg', 'https://www.blogilates.com/wp-content/uploads/2018/08/WaterChallenge-1.jpg', 'https://detoxchallenge.weebly.com/uploads/1/7/1/9/17191398/9413750.jpg'];

    const width = Dimensions.get('window').width;
    const [sliderCompleted, setSliderCompleted] = useState(false);

    const handleSliderComplete = (itemId, value) => {
        setTimeout(() => {
            const percentage = Math.round(value * 100);

            setFeed(feed.map(item =>
                item.id === itemId ? { ...item, rating: percentage } : item
            ));
            setSliderCompleted(true);
        }, 1000);
    };

    const [feed, setFeed] = useState(getFeed());

    const renderItem = ({ item }) => {
        if (item.type == 'recipe') {
            return <View style={styles.item}>
                <View style={styles.userInfo}>
                    <Image source={{ uri: item.profileImageUrl }} style={styles.profileImage} />
                    <Text style={styles.username}>{`${item.user} shared a secret recipe.`}</Text>

                </View>
                <View style={{ padding: 10 }}>
                    <View style={{ flexDirection: 'row' }}>
                        <Text style={{ fontWeight: 'bold', marginRight: 10 }}>Dish:</Text>
                        <Text>{item.dishName}</Text>
                    </View>
                    <Text style={{ textAlign: 'justify', padding: 10 }}>{item.summary}</Text>
                    <View style={{ flexDirection: 'row' }}>
                        <Text style={{ fontWeight: 'bold', marginRight: 10 }}>{`According to Avo, recipe is ${item.userRatings}% healthy`}</Text>
                    </View>
                    <View style={{ width: '100%', height: 50 }}>
                        <View style={styles.buttonContainer}>
                            <Link
                                style={styles.bakeButton2}
                                href={{ pathname: 'tabs/chat', params: { recipename: item.dishName } }}
                            >
                                <Text style={styles.buttonText}>Get Full Recipe</Text>
                                <Text style={styles.avoemoji}>🥑</Text>
                            </Link>
                        </View>
                    </View>
                </View>
            </View>
        }
        return (
            <View style={styles.item}>
                <View style={styles.userSection}>
                    <View style={styles.userInfo}>
                        <Image source={{ uri: item.profileImageUrl }} style={styles.profileImage} />
                        <Text style={styles.username}>{item.user}</Text>
                        <View style={styles.buttonContainer}>
                            <Link
                                style={styles.bakeButton}
                                href={{ pathname: 'tabs/chat', params: { caption: item.dishName } }}
                            >
                                <Text style={styles.buttonText}>Ask Avo</Text>
                                <Text style={styles.avoemoji}>🥑</Text>
                            </Link>
                        </View>
                    </View>
                </View>
                <Image source={{ uri: item.imageUrl }} style={styles.image} />
                <View style={styles.sliderContainer}>
                    <Text style={styles.emoji}>🍔</Text>
                    <Slider
                        style={styles.slider}
                        value={item.sliderValue}
                        // onValueChange={(newValue) => updateSliderValue(item.id, newValue)}
                        minimumValue={0}
                        maximumValue={10}
                        onSlidingComplete={(value) => handleSliderComplete(item.id, value)}
                        minimumTrackTintColor={COLORS.PRIMARY}
                        maximumTrackTintColor="#000000"
                    />
                    <Text style={styles.emoji}>🥦</Text>
                    <Text style={styles.percentageText}>
                        {item.userRatings && item.rating && (
                            <>
                                <FontAwesome name="users" size={24} color="black" />
                                <Text style={styles.percentageText}>
                                    {`${item.userRatings}%`}
                                </Text>
                            </>
                        )}
                    </Text>
                </View>
                <Text style={styles.caption}>
                    <Text style={styles.username}>{item.user}</Text>{" "}
                    {item.caption}
                </Text>

            </View>
        )
    };

    return (
        <SafeAreaView style={styles.safeArea}>
            <View style={{ paddingHorizontal: 80 }}>
                <Image source={logo} style={{ width: '100%', aspectRatio: 470 / 141 }} />
            </View>
            <FlatList
                data={feed}
                renderItem={renderItem}
         
[truncated — 4734 more characters]
```

### savory-sphere/app/tabs/profile/_layout.jsx

```javascript
import { Stack } from 'expo-router';

const ProfileLayout = () => {
    return (
        <Stack
            screenOptions={{
                headerShown: false
            }}
        >
            <Stack.Screen
                name="index"
            />
            <Stack.Screen
                name="foodies"
            />
        </Stack>
    );
}

export default ProfileLayout;

```

### savory-sphere/app/tabs/profile/index.jsx

```javascript
import { router } from 'expo-router'
import { signOut } from "firebase/auth";
import React, { useState } from 'react';
import { Text, View, StyleSheet, Button, Touchable, TouchableOpacity, FlatList } from "react-native";
import { auth } from "../../../src/firebase";
import { Image } from "expo-image";
import { useSelector } from "react-redux";
import { COLORS } from '../../../src/utils/constants';
import Constants from 'expo-constants';
import PostCard from '../../components/PostCard'

const dummyImage = require('../../../assets/images/blank_profile.png');

const Profile = () => {
    const user = useSelector((state) => state.user);
    const {photoUrl, displayName, bio, foodies } = user
    const foodieCount = foodies.length;
    // const profilePic = useSelector((state) => state.user.photoUrl);
    // const userName = useSelector((state) => state.user.displayName);
    // const bio = useSelector((state) => state.user.bio);
    const handleSignOut = () => {
        signOut(auth).then(() => {
            router.replace('/');
        })
    }

    const post = useSelector((state) => state.pFeed);
    // const [post, setPost] = useState([
    //     {
    //         id: '1',
    //         imageUrl: 'https://cdn.loveandlemons.com/wp-content/uploads/2021/04/green-salad.jpg',
    //         caption: 'Love this healthy salad! #HealthyEating',
    //         ratings: 80,
    //       },
    //       {
    //         id: '2',
    //         imageUrl: 'https://i2.wp.com/www.downshiftology.com/wp-content/uploads/2018/03/How-to-Boil-Eggs-main-1-2.jpg',
    //         caption: 'Ande ande ande!',
    //         ratings: 95,
    //       },
    //       {
    //         id: '3',
    //         imageUrl: 'https://images.freeimages.com/images/large-previews/ab2/burger-and-fries-1328407.jpg',
    //         caption: 'Burger',
    //         ratings: 25,
    //       },
    // ]);

    const renderPost = ({ item, index }) => {
        return <PostCard key={index} post={item} />;
    };
    return (
        <View style={styles.container}>
            <View style={styles.header}>
                <Image source={{ uri: photoUrl }} style={{ width: 120, height: 120, borderRadius: 60, borderWidth: 2, borderColor: COLORS.PRIMARY }} placeholder={dummyImage} />
                <Text style={styles.nameText}>{displayName}</Text>
                <Text style={styles.bioText}>{bio}</Text>
                <View style={{flexDirection:'row', marginVertical: 24}}>
                    <TouchableOpacity onPress={() => router.push('tabs/profile/foodies')}>
                        <Text style={styles.statsNum}>{foodieCount}</Text>
                        <Text >Foodies</Text>
                    </TouchableOpacity>
                    <View style={{width: 16}}></View>
                    <View>
                        <Text style={styles.statsNum}>5</Text>
                        <Text>Commuities</Text>
                    </View>
                    <View style={{width: 16}}></View>
                    <View>
                        <Text style={styles.statsNum}>80%</Text>
                        <Text>Progress</Text>
                    </View>
                </View>
            </View>
            <FlatList
                    data={post}
                    renderItem={renderPost}
                    // keyExtractor={item => item.id}
                    style={styles.flatList}
            />
            <Button title="LogOut" onPress={handleSignOut} />
        </View>
    );
}

export default Profile;

const styles = StyleSheet.create({
    container: {
        flex: 1,
        marginTop: Constants.statusBarHeight,
    },
    nameText: {
        fontWeight: '600',
        fontSize: 18,
        marginTop: 20,
    },
    header: {
        alignItems: 'center',
        width: '100%',
        paddingVertical: 30,
    },
    bioText: {
        width: '70%',
        textAlign: 'center',
        marginTop: 10,
    },
    statsNum: {
        textAlign: 'center',
        fontSize: 24,
        fontWeight: 'bold'
    },
    statsTitle: {

    }
});

```

### savory-sphere/babel.config.js

```javascript
module.exports = function (api) {
  api.cache(true);
  return {
    presets: ['babel-preset-expo'],
    plugins: ['expo-router/babel',
      'react-native-reanimated/plugin',
    ],
  };
};

```

### savory-sphere/metro.config.js

```javascript
// Learn more https://docs.expo.io/guides/customizing-metro
const { getDefaultConfig } = require('expo/metro-config');

/** @type {import('expo/metro-config').MetroConfig} */
const config = getDefaultConfig(__dirname);
// config.resolver.sourceExts.push('cjs');

module.exports = config;

```

### savory-sphere/app.config.js

```javascript
import 'dotenv/config'

export default {
  "expo": {
    "name": "Eat-O-Pia",
    "slug": "eat-o-pia",
    "version": "1.0.0",
    "orientation": "portrait",
    "icon": "./assets/icon.png",
    "userInterfaceStyle": "light",
    "splash": {
      "image": "./assets/splash.png",
      "resizeMode": "contain",
      "backgroundColor": "#ffffff"
    },
    "assetBundlePatterns": [
      "**/*"
    ],
    "ios": {
      "supportsTablet": true
    },
    "android": {
      "adaptiveIcon": {
        "foregroundImage": "./assets/adaptive-icon.png",
        "backgroundColor": "#ffffff"
      }
    },
    "scheme": "your-app-scheme",
    "plugins": [
      "expo-router"
    ],
    "extra": {
      firebaseApiKey: process.env.FIREBASE_API_KEY,
      firebaseAuthDomain: process.env.FIREBASE_AUTH_DOMAIN,
      firebaseProjectId: process.env.FIREBASE_PROJECT_ID,
      firebaseStorageBucket: process.env.FIREBASE_STORAGE_BUCKET,
      firebaseMessagingSenderId: process.env.FIREBASE_MESSAGING_SENDER_ID,
      firebaseAppId: process.env.FIREBASE_APP_ID,
      togetherApiKey: process.env.TOGETHER_API_KEY
    }
  }
}
```

[13 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]