# Project export: Proxima

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: TreeHacks 2024
- Tagline: Creating wholesome, spontaneous, and serendipitous interactions between pairs of stars
- Devpost: https://devpost.com/software/proxima-yl9zib
- GitHub: https://github.com/antoniocye/proxima
- Video: https://www.youtube.com/embed/ZExBb_txR14?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 4 GitHub contributor(s) — Asanshay Gupta (24 commits), Ravdrew (10 commits), Antonio Kambiré (8 commits), Joshua Hsieh (1 commits)

## Devpost submission (written by the team)

### Inspiration

It happens to us all. You see a cute person at the dining hall, in class, or studying at the library. You want to say something, but you don't know if they are open to be approached, or even just single. Many people, especially college students, wish they could know if another person is interested in meeting them. The predicament of not knowing if an attractive person is open to being approached has dissuaded billions from shooting their shot. On the flip side, having the power to know who you are mutually attracted with would be a game changer. Love Alarm, a popular Korean drama, is centered around a fictional app that tells people whether someone in their vicinity has romantic feelings towards them. Proxima turns this app from fiction to reality. Proxima was also inspired by a desire to fix the toxic hookup culture that is pervasive in dating apps. Apps like Tinder encourage users to search for artificial and fleeting connections. How can you truly meet someone through a text messaging channel? We believe that each user is a star who deserves to collide with other stars in raw, authentic moments and places. Proxima meetings occur when two compatible people happen to be in the same place, at the same time. In an age dominated by social media, we want to bring back spontaneous romance.

### What it does

Basically, Proxima notifies users when they cross paths with someone they are mutually attracted with. Like in other popular dating apps, Proxima users swipe through potential partners on the app in their free time. In this way users will build up a group of people they'd like to meet. However, the similarities end there. Beyond swiping, the users spend very little time on the app. Even swiping is limited—you can only see three people a day. Instead, users are encouraged to go about their daily lives and interact with their communities. By living their fullest lives, users increase their chances of meeting a match. When two potential matches enter each others' proximity, they get a 'ping'. In that moment, they have no idea who their match is, only that they were mutually attracted with them. Both users then have the option to meet their match, or to decline the match with no stakes, since no information is shared. If they're both down to meet, they go through a camera-based verification process, then are given just enough information to meet up.

### How we built it

After weeks of ideation, we decided on the concept for Proxima based on its relevance, scope, and challenge level, as it would expose us to things we had never done before. We started with a quite extensive planning process, which included user interviews (of fellow students), hand-drawn UI designs, and research into other similar products. We then combined the best of our hand-drawn ideas into a high-fidelity Sketch prototype, which let us see our own page flows and interactions in a realistic way. Once the hackathon started, we used React Native along with Expo to build the product itself. We had a few key needs - authentication, user profiles, location data, and notifications, each of which presented unique challenges. Based on our previous experience, we decided on Firebase as a framework to support the key authentication and profile features, as well as the server off of which we'd trigger the pings. We also used expo's libraries for location and notifications, which took us quite a bit of time and effort to figure out!

### Challenges we ran into

None of our members had done a hackathon before, so we had to figure a lot of logistics out for the first time. Using GitHub to manage the project was pretty new to us, especially working in such a small team with such tight turnarounds, so we all learnt the merits of committing often and the perils of large merge conflicts. It was also three of our members' first time developing an app using React Native, so there was quite a learning curve there. We also had a lot of trouble with writing locations to the database, since we needed a system that would update often enough not to miss potential pings but not too much that it would break down the database. Dealing with system features like notifications was also a big challenge, because of the vast differences between iOS and Android when developing a cross platform app that uses many system APIs like camera, location, notifications, and more.

### Accomplishments we're proud of

We were able to overcome most of the challenges that we encountered. Since we had never used React Native or Expo, we learned a ton about full stack app development throughout the hackathon. We also learned about how to generate pings based on data from a database. We also think we did a good job identifying the subproblems that we needed to solve and delegating tasks among ourselves. The entire event was an amazing learning experience for all of us.

### What we learned

Building a fully functional app is really hard and really rewarding. There are so many different moving parts in a full stack mobile app, and so many APIs and challenges to deal with. We wish we had more time to really explore all the features of every library. Also, clean code is really important! We prioritized commenting and keeping legible code, which really came in clutch when we were low on time and hacking away. We learned that this a project that we're passionate about and committed to developing in the long term, so our hacking on this project won't end here!

### What's next

We think a college campus is the perfect place for Proxima to get started, because of the level of familiarity and close proximity of the students, so we hope to launch it at Stanford later this spring. In the future, we hope to expand beyond just Stanford to some of our peer institutions and see where it grows from there, maybe even towards a release for the general public!

## README (from the GitHub repository)

# proxima
Bringing back spontaneous romance.

Click [here](https://devpost.com/software/proxima-yl9zib?ref_content=my-projects-tab&ref_feature=my_projects) for the Devpost.


## Detected evidence (automated analysis)

Indexed codebase: 49 recognized source files, 72 KB.
- Firebase (technology) — detected in the code
- JavaScript (language) — detected in the code
- React (technology) — detected in the code

## Codebase structure (from repository index)

### Files (71 of 71)

```
.expo/README.md
.expo/settings.json
app/.gitignore
app/App.js
app/app.json
app/assets/fonts/GeneralSans-Bold.otf
app/assets/fonts/GeneralSans-BoldItalic.otf
app/assets/fonts/GeneralSans-Extralight.otf
app/assets/fonts/GeneralSans-ExtralightItalic.otf
app/assets/fonts/GeneralSans-Italic.otf
app/assets/fonts/GeneralSans-Light.otf
app/assets/fonts/GeneralSans-LightItalic.otf
app/assets/fonts/GeneralSans-Medium.otf
app/assets/fonts/GeneralSans-MediumItalic.otf
app/assets/fonts/GeneralSans-Regular.otf
app/assets/fonts/GeneralSans-Semibold.otf
app/assets/fonts/GeneralSans-SemiboldItalic.otf
app/babel.config.js
app/database/authUtil.js
app/database/Init.js
app/database/Location.js
app/database/Profile.js
app/eas.json
app/google-services.json
app/GoogleService-Info.plist
app/package.json
app/src/components/button.js
app/src/components/camera.js
app/src/components/captionImage.js
app/src/components/chip.js
app/src/components/passwordfield.js
app/src/components/photopicker.js
app/src/components/progress.js
app/src/components/textfield.js
app/src/screens/addPhotos.js
app/src/screens/awaitingVerification.js
app/src/screens/details.js
app/src/screens/home.js
app/src/screens/login.js
app/src/screens/main.js
app/src/screens/onboarding/onboardingAge.js
app/src/screens/onboarding/onboardingDone.js
app/src/screens/onboarding/onboardingFirstPrompt.js
app/src/screens/onboarding/onboardingName.js
app/src/screens/onboarding/onboardingPassions.js
app/src/screens/onboarding/onboardingPreferredGender.js
app/src/screens/onboarding/onboardingSchool.js
app/src/screens/onboarding/onboardingSecondPrompt.js
app/src/screens/onboarding/onboardingThirdPrompt.js
app/src/screens/ping/pingDecision.js
app/src/screens/ping/pingDeclined.js
app/src/screens/ping/pingResult.js
app/src/screens/ping/pingStart.js
app/src/screens/ping/pingVerification.js
app/src/screens/ping/pingWaiting.js
app/src/screens/selfie.js
app/src/screens/signup.js
app/src/styles/cameraStyles.js
app/src/styles/captionImageStyles.js
app/src/styles/globalStyles.js
app/src/styles/photoPickerStyles.js
app/src/styles/profileStyles.js
app/utils/android-manifest-launch-mode-plugin.js
app/utils/notifs.js
app/utils/RootNavigation.js
firebase.json
functions/.eslintrc.js
functions/.gitignore
functions/index.js
functions/package.json
README.md
```

### Dependencies

- app/package.json: @babel/core@^7.20.0, @babel/plugin-proposal-export-namespace-from@^7.18.9, @babel/plugin-transform-export-namespace-from@^7.23.4, @expo/config-plugins@^7.8.4, @expo/metro-runtime@~3.1.3, @expo/vector-icons@^14.0.0, @react-native-firebase/app@^18.8.0, @react-native-firebase/messaging@^18.8.0, @react-navigation/native@^6.1.10, @react-navigation/native-stack@^6.9.18, expo@~50.0.7, expo-build-properties@^0.11.1, expo-camera@~14.0.5, expo-dev-client@~3.3.8, expo-font@~11.10.3, expo-image-picker@~14.7.1, expo-linear-gradient@~12.7.2, expo-notifications@^0.27.6, expo-splash-screen@^0.26.4, expo-status-bar@~1.11.1, firebase@^10.8.0, react@18.2.0, react-dom@18.2.0, react-native@0.73.4, react-native-gesture-handler@~2.14.0, react-native-linear-gradient@^2.8.3, react-native-progress@^5.0.1, react-native-reanimated@^3.7.0, react-native-safe-area-context@4.8.2, react-native-screens@~3.29.0, react-native-svg@^14.1.0, react-native-web@~0.19.6, reanimated@^0.0.1-alpha
- functions/package.json: eslint@^8.15.0, eslint-config-google@^0.14.0, firebase-admin@^11.8.0, firebase-functions@^4.3.1, firebase-functions-test@^3.1.0

### Recent commits (newest first)

- Update README.md
- Update README.md
- Update README.md
- Update README.md
- Merge pull request #11 from antoniocye/asanshay-4
- Notification ping relays user id
- Main working?
- trying to fix main
- Merge branch 'andres'
- adding server side
- Merge pull request #9 from antoniocye/asanshay-2 - profiles
- Merge branch 'main' into asanshay-2
- Merge branch 'onboarding' into main
- Finished swiping interaction
- Profiles and basic swiping implemented
- Merge pull request #7 from antoniocye/andres
- incorporated notif to ping page
- Merge pull request #6 from antoniocye/andres
- Merge branch 'main' into andres
- attempted to fix android bug (not fixed)

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

### functions/package.json

```
{
  "name": "functions",
  "description": "Cloud Functions for Firebase",
  "scripts": {
    "lint": "eslint .",
    "serve": "firebase emulators:start --only functions",
    "shell": "firebase functions:shell",
    "start": "npm run shell",
    "deploy": "firebase deploy --only functions",
    "logs": "firebase functions:log"
  },
  "engines": {
    "node": "18"
  },
  "main": "index.js",
  "dependencies": {
    "firebase-admin": "^11.8.0",
    "firebase-functions": "^4.3.1"
  },
  "devDependencies": {
    "eslint": "^8.15.0",
    "eslint-config-google": "^0.14.0",
    "firebase-functions-test": "^3.1.0"
  },
  "private": true
}

```

### app/package.json

```
{
  "name": "app",
  "version": "1.0.0",
  "main": "node_modules/expo/AppEntry.js",
  "scripts": {
    "start": "expo start",
    "android": "expo run:android",
    "ios": "expo run:ios",
    "web": "expo start --web"
  },
  "dependencies": {
    "@babel/plugin-proposal-export-namespace-from": "^7.18.9",
    "@expo/config-plugins": "^7.8.4",
    "@expo/metro-runtime": "~3.1.3",
    "@expo/vector-icons": "^14.0.0",
    "@react-native-firebase/app": "^18.8.0",
    "@react-native-firebase/messaging": "^18.8.0",
    "@react-navigation/native": "^6.1.10",
    "@react-navigation/native-stack": "^6.9.18",
    "expo": "~50.0.7",
    "expo-build-properties": "^0.11.1",
    "expo-camera": "~14.0.5",
    "expo-dev-client": "~3.3.8",
    "expo-font": "~11.10.3",
    "expo-image-picker": "~14.7.1",
    "expo-linear-gradient": "~12.7.2",
    "expo-notifications": "^0.27.6",
    "expo-splash-screen": "^0.26.4",
    "expo-status-bar": "~1.11.1",
    "firebase": "^10.8.0",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "react-native": "0.73.4",
    "react-native-gesture-handler": "~2.14.0",
    "react-native-linear-gradient": "^2.8.3",
    "react-native-progress": "^5.0.1",
    "react-native-reanimated": "^3.7.0",
    "react-native-safe-area-context": "4.8.2",
    "react-native-screens": "~3.29.0",
    "react-native-svg": "^14.1.0",
    "react-native-web": "~0.19.6",
    "reanimated": "^0.0.1-alpha"
  },
  "devDependencies": {
    "@babel/core": "^7.20.0",
    "@babel/plugin-transform-export-namespace-from": "^7.23.4"
  },
  "private": true
}

```

### functions/index.js

```javascript
/**
 * Import function triggers from their respective submodules:
 *
 * const {onCall} = require("firebase-functions/v2/https");
 * const {onDocumentWritten} = require("firebase-functions/v2/firestore");
 *
 * See a full list of supported triggers at https://firebase.google.com/docs/functions
 */

const {onRequest} = require("firebase-functions/v2/https");
const logger = require("firebase-functions/logger");
var admin = require('firebase-admin');

async function onLocationChanged(_notifId, _location) {
    // Get the owners details
    const owner = admin.firestore().collection('users').doc(ownerId).get();
  
    // Get the users details
    const user = admin.firestore().collection('users').doc(userId).get();
  
    await admin.messaging().sendEachForMulticast({
        tokens: owner.tokens, // ['token_1', 'token_2', ...]
        data: {
            owner: JSON.stringify(owner),
            user: JSON.stringify(user),
            
        },
        apns: {
            payload: {
                aps: {
            // Required for background/quit data-only messages on iOS
            // Note: iOS frequently will receive the message but decline to deliver it to your app.
            //           This is an Apple design choice to favor user battery life over data-only delivery
            //           reliability. It is not under app control, though you may see the behavior in device logs.
                    'content-available': true,
            // Required for background/quit data-only messages on Android
                    priority: 'high',
                },
            },
        },
    });
}

// Create and deploy your first functions
// https://firebase.google.com/docs/functions/get-started

// exports.helloWorld = onRequest((request, response) => {
//   logger.info("Hello logs!", {structuredData: true});
//   response.send("Hello from Firebase!");
// });

```

### app/App.js

```javascript
import { StatusBar } from "expo-status-bar";
import React, { useState, useEffect, createRef,  useCallback, createContext  } from "react";
import { NavigationContainer } from '@react-navigation/native';
import { Button, StyleSheet, Text, View } from 'react-native';
import {createNativeStackNavigator} from '@react-navigation/native-stack';
import { GestureHandlerRootView } from "react-native-gesture-handler";

// expo
import { useFonts } from 'expo-font';
import * as SplashScreen from 'expo-splash-screen'; 

// db
import Profile from './database/Profile.js';
import { init, auth, db } from './database/Init.js'

// screens
import HomeScreen from './src/screens/home';
import DetailsScreen from './src/screens/details'; 
import LoginScreen from './src/screens/login';
import SignupScreen from './src/screens/signup';
import AddPhotosScreen from './src/screens/addPhotos';
import SelfieScreen from './src/screens/selfie';
import PingStartScreen from './src/screens/ping/pingStart';
import PingDecisionScreen from './src/screens/ping/pingDecision';
import PingDeclinedScreen from './src/screens/ping/pingDeclined';
import PingVerificationScreen from './src/screens/ping/pingVerification';
import PingResultScreen from './src/screens/ping/pingResult';
import PingWaitingScreen from './src/screens/ping/pingWaiting';
import AwaitingVerificationScreen from './src/screens/awaitingVerification';
import MainScreen from './src/screens/main.js';
import OnboardingNameScreen from './src/screens/onboarding/onboardingName.js';
import OnboardingAgeScreen from './src/screens/onboarding/onboardingAge.js';
import OnboardingSchoolScreen from './src/screens/onboarding/onboardingSchool.js';
import OnboardingPreferredGenderScreen from './src/screens/onboarding/onboardingPreferredGender.js';
import OnboardingPassionsScreen from './src/screens/onboarding/onboardingPassions.js';
import OnboardingFirstPromptScreen from './src/screens/onboarding/onboardingFirstPrompt.js';
import OnboardingSecondPromptScreen from './src/screens/onboarding/onboardingSecondPrompt.js';
import OnboardingThirdPromptScreen from './src/screens/onboarding/onboardingThirdPrompt.js';
import OnboardingDoneScreen from './src/screens/onboarding/onboardingDone.js';

import notifs from "./utils/notifs";
import { navigationRef } from './utils/RootNavigation';

// styles
import globalStyles from './src/styles/globalStyles.js';



const Stack = createNativeStackNavigator();

SplashScreen.preventAutoHideAsync(); // prevent the splash screen from auto-hiding


/*
 * Only add navigation points to this file, handle navigation in the files themselves
 */

export const GlobalUser = createContext();


function App() {

  notifs();
  
  const [myUser, setMyUser] = useState();

  const [fontsLoaded, fontError] = useFonts({
    'GeneralSans-Medium': require('./assets/fonts/GeneralSans-Medium.otf'),
    'GeneralSans-Semibold': require('./assets/fonts/GeneralSans-Semibold.otf'),
  });

  const onLayoutRootView = useCallback(async () => {
    if (fontsLoaded || fontError) {
      await SplashScreen.hideAsync();
    }
  }, [fontsLoaded, fontError]);

  if (!fontsLoaded && !fontError) {
    return null;
  }

  for(i = 0; i < 5; i++){
    console.log("----------------------------------------");
  }
  
  result = init();

  return (

    <GlobalUser.Provider value={[ myUser, setMyUser ] }>
    {
      <NavigationContainer ref={navigationRef}r>
        <Stack.Navigator initialRouteName="Home" screenOptions={{headerShown: false}}>
          <Stack.Screen name="Home" component={HomeScreen} options={onLayoutRootView}/>
          <Stack.Screen name="Details" component={DetailsScreen} />
          <Stack.Screen name="Login" component={LoginScreen} />
          <Stack.Screen name="Sign Up" component={SignupScreen} />
          <Stack.Screen name="Add Photos" component={AddPhotosScreen} />
          <Stack.Screen name="Camera" component={SelfieScreen} />
          <Stack.Screen name="Ping Start" component={PingStartScreen} />
          <Stack.Screen name="Ping Decision" component={PingDecisionScreen} />
          <Stack.Screen name="Ping Declined" component={PingDeclinedScreen} />
          <Stack.Screen name="Ping Verification" component={PingVerificationScreen} />
          <Stack.Screen name="Ping Waiting" component={PingWaitingScreen} />
          <Stack.Screen name="Ping Result" component={PingResultScreen} />
          <Stack.Screen name="Awaiting Verification" component={AwaitingVerificationScreen} />
          <Stack.Screen name="Main" component={MainScreen} />

          <Stack.Screen name="Onboarding Name" component={OnboardingNameScreen} />
          <Stack.Screen name="Onboarding Age" component={OnboardingAgeScreen} />
          <Stack.Screen name="Onboarding School" component={OnboardingSchoolScreen} />
          <Stack.Screen name="Onboarding Preferred Gender" component={OnboardingPreferredGenderScreen} />
          <Stack.Screen name="Onboarding Passions" component={OnboardingPassionsScreen} />
          <Stack.Screen name="Onboarding First Prompt" component={OnboardingFirstPromptScreen} />
          <Stack.Screen name="Onboarding Second Prompt" component={OnboardingSecondPromptScreen} />
          <Stack.Screen name="Onboarding Third Prompt" component={OnboardingThirdPromptScreen} />
          <Stack.Screen name="Onboarding Done" component={OnboardingDoneScreen} />
        </Stack.Navigator>
      </NavigationContainer>
    }
  </GlobalUser.Provider>
    
  );
}

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

export default App;

```

### app/src/screens/main.js

```javascript
import React, {useState, useEffect} from 'react';
import { SafeAreaView, Text, Image, ImageBackground, View, Platform, ScrollView, ActivityIndicator} from 'react-native';
import globalStyles from '../styles/globalStyles';
import AnimatedButton from '../components/button';
import profileStyles from '../styles/profileStyles';
import Chip from '../components/chip';
import CaptionImage from '../components/captionImage';

import { GestureHandlerRootView, Gesture, GestureDetector, Directions } from 'react-native-gesture-handler';
import { runOnJS } from 'react-native-reanimated';

var profiles = [
  {
    name: "Tanvir", 
    age: 18, 
    pronouns: "he/him", 
    photos: [
      {uri: require("../../assets/example-profile-1/image-1.jpeg"), caption: "brown munde"}, 
      {uri: require("../../assets/example-profile-1/image-2.jpeg"), caption: "big pregame guy"},
      {uri: require("../../assets/example-profile-1/image-3.jpeg"), caption: "am i a good dancer?"},
    ], 
    quotes: ["i only date punjabi girls.  i’m the real brown munde", "i don’t live in la hoya. i’m just from the poor part of san diego"],
  },
  {
    name: "Josh", 
    age: 13, 
    pronouns: "him", 
    photos: [
      {uri: require("../../assets/example-profile-2/image-1.jpeg"), caption: "brown munde"}, 
      {uri: require("../../assets/example-profile-2/image-2.jpg"), caption: "i'm basically jeremy lin"},
      {uri: require("../../assets/example-profile-2/image-3.jpeg"), caption: "didn't ask for this birthday party"},
    ], 
    quotes: ["i talk to females every day", "math 61 was the easiest class i’ve ever taken"],
  },
  {
    name: "Andres", 
    age: 18, 
    pronouns: "he/him", 
    photos: [
      {uri: require("../../assets/example-profile-3/image-1.jpg"), caption: "brown munde"}, 
      {uri: require("../../assets/example-profile-3/image-2.jpeg"), caption: "point five"},
      {uri: require("../../assets/example-profile-3/image-3.jpeg"), caption: "hacker mode initiated"},
    ], 
    quotes: ["i do cool guy things like play league of legends", "you wouldn't believe it, but i have a girlfriend"],
  }
];

export default function MainScreen({ navigation }) {
  

  const [i, setI] = useState(0);
  const [isLoading, setIsLoading] = useState(true);
  const [profile, setProfile] = useState('');

  // placeholder function
  async function waitForThreeSeconds() {
    return new Promise(resolve => {
      setTimeout(() => {
        resolve(profiles[i]);
      }, 10); // 3000 milliseconds = 3 seconds
    });
  }

  const getProfile = async () => {
    setIsLoading(true);
    const result = await waitForThreeSeconds();
    setProfile(result);
    setIsLoading(false);
  };

  function updateI () {
    // Use a functional update to ensure we always have the current state
    setI(currentI => {
      const nextI = (currentI + 1) % profiles.length;
      return nextI;
    });

    getProfile();
  }

const updateProfilesArray = (updatedProfile) => {
  // Update the profiles array with the new profile data
  profiles[(i - 1) % profiles.length] = updatedProfile; // Directly updating the array for demonstration; consider state management practices
  console.log(profiles);
  console.log(i);
};

  const swipeLeft = Gesture.Fling()
    .direction(Directions.LEFT)
    .onStart((e) => {
      console.log("swiped left");
      const updatedProfile = { ...profile };
      runOnJS(setProfile)(updatedProfile);
      runOnJS(updateProfilesArray)(updatedProfile); // Update profiles array
      runOnJS(updateI)();
  });

  const swipeRight = Gesture.Fling()
    .direction(Directions.RIGHT)
    .onStart((e) => {
      console.log("swiped right");
      const updatedProfile = { ...profile}
      runOnJS(setProfile)(updatedProfile);
      runOnJS(updateProfilesArray)(updatedProfile); // Update profiles array
      runOnJS(updateI)();
  });

  const composed = Gesture.Exclusive(swipeRight, swipeLeft);

  useEffect(() => {
    updateI();
  }, []);

  return (
    <GestureHandlerRootView style={[globalStyles.backgroundImage]}>
      <GestureDetector gesture={swipeLeft}>
        <ImageBackground source={require('../../assets/img/background.png')} style={globalStyles.backgroundImage}>
          <SafeAreaView/>
            {!isLoading ? <ScrollView>
              <View style={profileStyles.container}>
                <Text style={[globalStyles.text, profileStyles.label]}>meet</Text>
                <Text style={[globalStyles.heading, {marginBottom: 20}]}>{profile.name.toLowerCase()}</Text>
                
                <Image source={profile.photos[0].uri} style={profileStyles.mainImage}/>
                <View style={profileStyles.row}>
                  <Chip label={profile.age}/> 
                  <Chip label={profile.pronouns}/>
                </View>
                <View style={profileStyles.quote}>
                  <Text style={profileStyles.quoteText}>{profile.quotes[0].toLowerCase()}</Text>
                </View>
                <CaptionImage imageSource={profile.photos[1].uri} caption={profile.photos[1].caption}/>
                <View style={profileStyles.quote}>
                  <Text style={profileStyles.quoteText}>{profile.quotes[1].toLowerCase()}</Text>
                </View>
                <CaptionImage imageSource={profile.photos[2].uri} caption={profile.photos[2].caption}/>
              </View>
              <View style={{height: 50}}/>
            </ScrollView> : <ActivityIndicator size="large" color={globalStyles.primaryColor}/>}
        </ImageBackground>
      </GestureDetector>
    </GestureHandlerRootView>
  );
}
```

### app/babel.config.js

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

```

### functions/.eslintrc.js

```javascript
module.exports = {
  env: {
    es6: true,
    node: true,
  },
  parserOptions: {
    "ecmaVersion": 2018,
  },
  extends: [
    "eslint:recommended",
    "google",
  ],
  rules: {
    "no-restricted-globals": ["error", "name", "length"],
    "prefer-arrow-callback": "error",
    "quotes": ["error", "double", {"allowTemplateLiterals": true}],
  },
  overrides: [
    {
      files: ["**/*.spec.*"],
      env: {
        mocha: true,
      },
      rules: {},
    },
  ],
  globals: {},
};

```

### app/utils/RootNavigation.js

```javascript
import { createNavigationContainerRef } from '@react-navigation/native';

export const navigationRef = createNavigationContainerRef()

export function navigate(name, params) {
    console.log("READY CHECK");
    if (navigationRef.isReady()) {
        console.log("NAVIGATING TO", name);
        navigationRef.navigate(name, params);
    }
}
```

### app/utils/android-manifest-launch-mode-plugin.js

```javascript
const { withAndroidManifest } = require('@expo/config-plugins');

module.exports = function withAndroidStrategiesPlugin(config) {
  return withAndroidManifest(config, config => {
    config.modResults.manifest.application[0].activity[0].$[
      'android:launchMode'
    ] = 'singleTask'; // SingleTask/SingleMode
    return config;
  });
};
```

### app/database/authUtil.js

```javascript
import { Alert } from 'react-native';

// validate email and password
export function isValidEmail(email) {
    const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    return regex.test(email);
}
export function isStanfordEmail(email) {
    const domain = 'stanford.edu';
    const regex = new RegExp(`@${domain}$`, 'i'); // Case insensitive match for domain
    return regex.test(email);
}
export function notEmpty(email, password) {
    // Check if both email and password are not null and not empty strings
    // Also, check if password length is greater than 5
    return email && password && email.trim() !== "" && password.trim() !== "" && password.length > 5;
}
  

// create alert error for login and sign up errors
export const createAlert = (errorTitle, errorInfo, callback) =>
Alert.alert(errorTitle, errorInfo, [
  {text: 'Try again', onPress: callback } ,
]);
```

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