# Project export: Little Gems

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: Discover and indulge in the most authentic, off-the-beaten-path restaurants with the help of passionate locals.
- Devpost: https://devpost.com/software/little-gems
- GitHub: https://github.com/0n9aldrin/littlegems
- Demo: https://www.figma.com/file/hAhyAIlMzdXIzIZkEXN8Rc/Treehacks-24-Vers?type=design&node-id=2459%3A1362&mode=design&t=onRPvqY5vhsRGsnn-1
- Video: https://www.youtube.com/embed/sv25GLAvIao?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 3 GitHub contributor(s) — Aldrin Ong (27 commits), Kush Goswami (22 commits), Austin Nicola Ardisaputra (12 commits)

## Devpost submission (written by the team)

### Inspiration

🧠 Have you ever been in a new city, sifting through thousands of highly-rated restaurants? How do you know the difference between an overhyped tourist trap over a high-quality local treasure? Little Gems provides a way to find the "iykyk" spots among locals that fit your dietary needs and preferences. Culinary tourism is a fast-growing billion-dollar industry. With over 195 million solo travellers each year, tourists are constantly searching for unique dining experiences that are not only highly rated but also authentic and immersive in local communities. By connecting locals with tourists through a time-flexible matching/pairing system, visitors can eat like locals, with locals, for a flat fee of $5. Through this convenient and transparent transaction, locals can share their knowledge of their community to provide flavourful recommendations and dish out their favourite spots alongside curious tourists.

### What it does

📱 As a tourist, customize your profile based on dietary restrictions and other information. Then set a location where you plan to be at a certain time and find a local who knows the best eats in that area! There will be a small fee given towards the local for their service. As a local, you get paid for your help in introducing your local culture to tourists. This is also a great way to meet new people and teach them about what your local area has to offer that many don't know about online. To ensure that both parties are satisfied, we implement a photo verification of either the receipt or a selfie with the other party. We would also be acting as an "escrow" to prevent tourists from taking advantage of locals.

### How we built it

⚙️ The entire frontend uses React Native and the backend is fully powered by Convex. The real-time databases and web-socket connections from Convex allowed real-time messaging and lag-free synchronization between frontend and backend.

### Challenges we ran into

🔧 Authentication: was a big pain in the beginning. Thankfully Convex's authentication features with Clerk helped make things go much smoother. Accessibility: Another big hurdle we had was making the app more accessible and available on both iOS and Android. This required extensive user testing and research as well as meticulous UI designs. Location: Since our app filters by location, we needed to safely acquire the user's location (mainly for locals) as we allow them to pick a range. This was somewhat difficult to do on both Android and iOS through Expo. For tourists, they were allowed to pick a city where they would like to make their request. This was surprisingly difficult to do so we implemented a "hacky" way of searching cities through link.

### Accomplishments we're proud of

🦾 Live chat feature, payment feature, nice UI

## README (from the GitHub repository)

![Little Gems](https://d112y698adiu2z.cloudfront.net/photos/production/software_photos/002/769/719/datas/original.png
)

## Inspiration 🧠
Have you ever been in a new city, sifting through thousands of highly-rated restaurants?
<br>
How do you know the difference between an overhyped tourist trap over a high-quality local treasure?
<br>
<br>
**Little Gems** provides a way to find the "iykyk" spots among locals that fit your dietary needs and preferences. Culinary tourism is a fast-growing billion-dollar industry. With over 195 million solo travellers each year, tourists are constantly searching for unique dining experiences that are not only highly rated but also authentic and immersive in local communities. 
<br>
<br>
By connecting locals with tourists through a time-flexible matching/pairing system, **visitors can eat like locals, with locals**, for a flat fee of $5. Through this convenient and transparent transaction, locals can share their knowledge of their community to provide flavourful recommendations and dish out their favourite spots alongside curious tourists.
<br>
## What it does 📱
**As a tourist**, customize your profile based on dietary restrictions and other information. Then set a location where you plan to be at a certain time and find a local who knows the best eats in that area! There will be a small fee given towards the local for their service.

**As a local**, you get paid for your help in introducing your local culture to tourists. This is also a great way to meet new people and teach them about what your local area has to offer that many don't know about online.

To ensure that both parties are satisfied, we implement a photo verification of either the receipt or a selfie with the other party. We would also be acting as an "escrow" to prevent tourists from taking advantage of locals.

## How we built it ⚙️
The entire frontend uses _React Native_ and the backend is fully powered by _Convex_. The **real-time databases** and **web-socket connections** from _Convex_ allowed **real-time messaging** and **lag-free synchronization** between frontend and backend. 

## Challenges we ran into 🔧
- **Authentication**: was a big pain in the beginning. Thankfully Convex's authentication features with Clerk helped make things go much smoother. 
- **Accessibility**: Another big hurdle we had was making the app more accessible and available on both iOS and Android. This required extensive user testing and research as well as meticulous UI designs. 
- **Location**: Since our app filters by location, we needed to safely acquire the user's location (mainly for locals) as we allow them to pick a range. This was somewhat difficult to do on both Android and iOS through Expo. For tourists, they were allowed to pick a city where they would like to make their request. This was surprisingly difficult to do so we implemented a "hacky" way of searching cities through [link](https://nominatim.openstreetmap.org/ui/search.html).

## Accomplishments that we're proud of 🦾
Live chat feature, payment feature, nice UI


## Detected evidence (automated analysis)

Indexed codebase: 49 recognized source files, 106 KB.
- C (language) — detected in the code
- JavaScript (language) — detected in the code
- Kotlin (language) — detected in the code
- React (technology) — detected in the code
- Swift (language) — detected in the code
- TypeScript (language) — detected in the code

## Codebase structure (from repository index)

### Files (81 of 81)

```
.gitignore
android/.gitignore
android/app/build.gradle
android/app/debug.keystore
android/app/proguard-rules.pro
android/app/src/debug/AndroidManifest.xml
android/app/src/main/AndroidManifest.xml
android/app/src/main/java/com/aardisaputra/littlegems/MainActivity.kt
android/app/src/main/java/com/aardisaputra/littlegems/MainApplication.kt
android/app/src/main/res/drawable/rn_edit_text_material.xml
android/app/src/main/res/drawable/splashscreen.xml
android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
android/app/src/main/res/values-night/colors.xml
android/app/src/main/res/values/colors.xml
android/app/src/main/res/values/strings.xml
android/app/src/main/res/values/styles.xml
android/build.gradle
android/gradle.properties
android/gradle/wrapper/gradle-wrapper.properties
android/gradlew
android/gradlew.bat
android/settings.gradle
App.js
app.json
babel.config.js
convex/_generated/api.d.ts
convex/_generated/api.js
convex/_generated/dataModel.d.ts
convex/_generated/server.d.ts
convex/_generated/server.js
convex/all_requests.js
convex/auth.config.js
convex/locals.js
convex/messaging.js
convex/tasks.js
convex/tourists.js
index.js
ios/.gitignore
ios/.xcode.env
ios/littlegems.xcodeproj/project.pbxproj
ios/littlegems.xcodeproj/xcshareddata/xcschemes/littlegems.xcscheme
ios/littlegems.xcworkspace/contents.xcworkspacedata
ios/littlegems/AppDelegate.h
ios/littlegems/AppDelegate.mm
ios/littlegems/Images.xcassets/AppIcon.appiconset/Contents.json
ios/littlegems/Images.xcassets/Contents.json
ios/littlegems/Images.xcassets/SplashScreen.imageset/Contents.json
ios/littlegems/Images.xcassets/SplashScreenBackground.imageset/Contents.json
ios/littlegems/Info.plist
ios/littlegems/littlegems.entitlements
ios/littlegems/main.m
ios/littlegems/noop-file.swift
ios/littlegems/SplashScreen.storyboard
ios/littlegems/Supporting/Expo.plist
ios/Podfile
ios/Podfile.lock
ios/Podfile.properties.json
metro.config.js
package.json
README.md
sampleData.jsonl
src/components/DietFilters.js
src/components/FilterButton.js
src/components/LoginButton.js
src/components/RadiusSelector.js
src/components/Tile.js
src/hooks/useWarmUpBrowser.js
src/navigation/AuthNavigation.js
src/navigation/LocalNavigation.js
src/navigation/TouristNavigation.js
src/screens/auth/LoginScreen.js
src/screens/auth/SignUpScreen.js
src/screens/auth/UserSelectScreen.js
src/screens/chat/Chat.js
src/screens/chat/PhotoVerification.js
src/screens/local/LocalHomeScreen.js
src/screens/tourist/PaymentScreen.js
src/screens/tourist/TouristHomeScreen.js
Tasks.js
webpack.config.js
```

### Dependencies

- package.json: @auth0/auth0-react@^2.2.4, @babel/core@^7.20.0, @clerk/clerk-expo@^0.20.5, @clerk/clerk-react@^4.30.5, @expo/metro-runtime@~3.1.3, @react-native-community/datetimepicker@^7.6.2, @react-native-community/slider@^4.5.0, @react-navigation/native@^6.1.10, @react-navigation/native-stack@^6.9.18, @rneui/base@^4.0.0-rc.7, @rneui/themed@^4.0.0-rc.8, auth0@^4.3.1, axios@^1.6.7, convex@^1.9.0, expo@~50.0.7, expo-font@^11.10.3, expo-image-picker@^14.7.1, expo-location@^16.5.3, expo-splash-screen@^0.26.4, expo-status-bar@~1.11.1, react@18.2.0, react-dom@^18.2.0, react-native@0.73.4, react-native-auth0@^3.1.0, react-native-dotenv@^3.4.10, react-native-elements@^3.4.3, react-native-get-random-values@^1.10.0, react-native-vector-icons@^10.0.3, react-native-web@~0.19.6

### Recent commits (newest first)

- Update README.md
- Update README.md
- Create README.md
- Small UI changes
- ui fixes
- ui changes
- Make changes
- Merge branch 'main' of github.com:0n9aldrin/littlegems
- Fix some bugs and add payment
- fast location retrieval
- Merge branch 'main' of github.com:0n9aldrin/littlegems
- minor changes
- minor changes
- done?
- Made minor changes
- actually almost done
- done??
- idek
- selection more clear
- button selection is clear

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

### package.json

```
{
  "name": "littlegems",
  "version": "1.0.0",
  "scripts": {
    "start": "expo start --dev-client",
    "android": "expo run:android",
    "ios": "expo run:ios",
    "web": "expo start --web"
  },
  "dependencies": {
    "@auth0/auth0-react": "^2.2.4",
    "@clerk/clerk-expo": "^0.20.5",
    "@clerk/clerk-react": "^4.30.5",
    "@expo/metro-runtime": "~3.1.3",
    "@react-native-community/datetimepicker": "^7.6.2",
    "@react-native-community/slider": "^4.5.0",
    "@react-navigation/native": "^6.1.10",
    "@react-navigation/native-stack": "^6.9.18",
    "@rneui/base": "^4.0.0-rc.7",
    "@rneui/themed": "^4.0.0-rc.8",
    "auth0": "^4.3.1",
    "axios": "^1.6.7",
    "convex": "^1.9.0",
    "expo": "~50.0.7",
    "expo-font": "^11.10.3",
    "expo-image-picker": "^14.7.1",
    "expo-location": "^16.5.3",
    "expo-splash-screen": "^0.26.4",
    "expo-status-bar": "~1.11.1",
    "react": "18.2.0",
    "react-dom": "^18.2.0",
    "react-native": "0.73.4",
    "react-native-auth0": "^3.1.0",
    "react-native-dotenv": "^3.4.10",
    "react-native-elements": "^3.4.3",
    "react-native-get-random-values": "^1.10.0",
    "react-native-vector-icons": "^10.0.3",
    "react-native-web": "~0.19.6"
  },
  "devDependencies": {
    "@babel/core": "^7.20.0"
  },
  "private": true
}
```

### index.js

```javascript
import { registerRootComponent } from 'expo';

import App from './App';

// registerRootComponent calls AppRegistry.registerComponent('main', () => App);
// It also ensures that whether you load the app in Expo Go or in a native build,
// the environment is set up appropriately
registerRootComponent(App);

```

### App.js

```javascript
import React, { useEffect, useState } from "react";
import { ConvexProvider, ConvexReactClient } from "convex/react";
import "react-native-get-random-values";
import { CONVEX_URL } from "@env";
import * as Font from "expo-font";
import * as SplashScreen from "expo-splash-screen";
import { NavigationContainer } from "@react-navigation/native";
import AuthNavigator from "./src/navigation/AuthNavigation";
import { ClerkProvider, useAuth } from "@clerk/clerk-expo";
import { ConvexProviderWithClerk } from "convex/react-clerk";
import Constants from "expo-constants";
import LocalNavigator from "./src/navigation/LocalNavigation";
import TouristNavigator from "./src/navigation/TouristNavigation";
import { LogBox } from 'react-native';

LogBox.ignoreAllLogs();
SplashScreen.preventAutoHideAsync();

const fetchFonts = () => {
  return Font.loadAsync({
    "Poppins-Bold": require("./assets/fonts/Poppins-Bold.ttf"),
    "Poppins-Medium": require("./assets/fonts/Poppins-Medium.ttf"),
    "Poppins-BoldItalic": require("./assets/fonts/Poppins-BoldItalic.ttf"),
    "Poppins-MediumItalic": require("./assets/fonts/Poppins-MediumItalic.ttf"),
    "Poppins-Regular": require("./assets/fonts/Poppins-Regular.ttf"),
    "Poppins-SemiBold": require("./assets/fonts/Poppins-SemiBold.ttf"),
    "Poppins-SemiBoldItalic": require("./assets/fonts/Poppins-SemiBoldItalic.ttf"),
    "Poppins-Italic": require("./assets/fonts/Poppins-Italic.ttf"),
  });
};

const convex = new ConvexReactClient(CONVEX_URL, {
  unsavedChangesWarning: false,
});

export default function App() {
  const [fontsLoaded, setFontsLoaded] = useState(false);

  useEffect(() => {
    async function loadFonts() {
      await fetchFonts();
      setFontsLoaded(true);
      await SplashScreen.hideAsync();
    }

    loadFonts();
  }, []);

  if (!fontsLoaded) {
    return null;
  }

  return (
    <ClerkProvider
      publishableKey={
        "pk_test_Y3VycmVudC1oYWxpYnV0LTE5LmNsZXJrLmFjY291bnRzLmRldiQ"
      }
    >
      <ConvexProviderWithClerk client={convex} useAuth={useAuth}>
        <NavigationContainer>
          <AuthNavigator />
        </NavigationContainer>
      </ConvexProviderWithClerk>
    </ClerkProvider>
  );
}

```

### convex/_generated/server.js

```javascript
/* eslint-disable */
/**
 * Generated utilities for implementing server-side Convex query and mutation functions.
 *
 * THIS CODE IS AUTOMATICALLY GENERATED.
 *
 * Generated by convex@1.9.0.
 * To regenerate, run `npx convex dev`.
 * @module
 */

import {
  actionGeneric,
  httpActionGeneric,
  queryGeneric,
  mutationGeneric,
  internalActionGeneric,
  internalMutationGeneric,
  internalQueryGeneric,
} from "convex/server";

/**
 * Define a query in this Convex app's public API.
 *
 * This function will be allowed to read your Convex database and will be accessible from the client.
 *
 * @param func - The query function. It receives a {@link QueryCtx} as its first argument.
 * @returns The wrapped query. Include this as an `export` to name it and make it accessible.
 */
export const query = queryGeneric;

/**
 * Define a query that is only accessible from other Convex functions (but not from the client).
 *
 * This function will be allowed to read from your Convex database. It will not be accessible from the client.
 *
 * @param func - The query function. It receives a {@link QueryCtx} as its first argument.
 * @returns The wrapped query. Include this as an `export` to name it and make it accessible.
 */
export const internalQuery = internalQueryGeneric;

/**
 * Define a mutation in this Convex app's public API.
 *
 * This function will be allowed to modify your Convex database and will be accessible from the client.
 *
 * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.
 * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.
 */
export const mutation = mutationGeneric;

/**
 * Define a mutation that is only accessible from other Convex functions (but not from the client).
 *
 * This function will be allowed to modify your Convex database. It will not be accessible from the client.
 *
 * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.
 * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.
 */
export const internalMutation = internalMutationGeneric;

/**
 * Define an action in this Convex app's public API.
 *
 * An action is a function which can execute any JavaScript code, including non-deterministic
 * code and code with side-effects, like calling third-party services.
 * They can be run in Convex's JavaScript environment or in Node.js using the "use node" directive.
 * They can interact with the database indirectly by calling queries and mutations using the {@link ActionCtx}.
 *
 * @param func - The action. It receives an {@link ActionCtx} as its first argument.
 * @returns The wrapped action. Include this as an `export` to name it and make it accessible.
 */
export const action = actionGeneric;

/**
 * Define an action that is only accessible from other Convex functions (but not from the client).
 *
 * @param func - The function. It receives an {@link ActionCtx} as its first argument.
 * @returns The wrapped function. Include this as an `export` to name it and make it accessible.
 */
export const internalAction = internalActionGeneric;

/**
 * Define a Convex HTTP action.
 *
 * @param func - The function. It receives an {@link ActionCtx} as its first argument, and a `Request` object
 * as its second.
 * @returns The wrapped endpoint function. Route a URL path to this function in `convex/http.js`.
 */
export const httpAction = httpActionGeneric;

```

### webpack.config.js

```javascript
resolve: {
    alias: {
      react: path.resolve('./node_modules/react'),
  }}
```

### babel.config.js

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

```

### 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);

module.exports = config;

```

### Tasks.js

```javascript
import { StyleSheet, Text, View } from "react-native";
import { useQuery } from "convex/react";
import { api } from "./convex/_generated/api";

export default function Tasks() {
  const tasks = useQuery(api.tasks.get);
  return (
    <View style={styles.container}>
      {tasks?.map(({ _id, text }) => (
        <Text key={_id}>{text}</Text>
      ))}
    </View>
  );
}

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

### convex/auth.config.js

```javascript
export default {
  providers: [
    {
      domain: "https://current-halibut-19.clerk.accounts.dev",
      applicationID: "convex",
    },
  ],
};

```

### convex/tasks.js

```javascript
import { query } from "./_generated/server";

export const get = query({
  args: {},
  handler: async (ctx) => {
    return await ctx.db.query("tasks").collect();
  },
});
```

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