# Project export: MinuteCamp

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: Think of an intersection between knowledge, convenience, and entertainment, tailored for quick comprehension, that allows people of all ages to learn about any, and many, topics. Think MinuteCamp.
- Devpost: https://devpost.com/software/minutecamp
- GitHub: https://github.com/marcreniel/Minutecamp
- Video: https://player.vimeo.com/video/914131546?byline=0&portrait=0&title=0#t=
- Team: 4 GitHub contributor(s) — imedra (27 commits), hannah (10 commits), Brian Ni (3 commits), Shani Su (3 commits)

## Devpost submission (written by the team)

### Inspiration

We were inspired by our own interests in countless subjects, and the lack of time to pursue such interests. With our busy preoccupied schedules as well as our own (unfortunate) addiction to "doom scrolling" on Tiktok, we searched for a way to find fun in fun, efficient learning. Essentially, we wanted to create a platform that allows for an intersection of education, entertainment, and convenience. Taking inspiration from booming platforms, including TikTok, Coursera, Duolingo, and other entertainment platforms, we created MinuteCamp.

### What it does

it do MinuteCamp is a short-form content platform that introduces its users to new intellectual pursuits, as well as educational and professional development opportunities. This begins through the Discovery Feed, a feed for users to find topics they may want to learn about from the intro level that is also filterable by subject. There are also feeds of just specific topics that you can find by clicking on a video of the subject you want to dive into on your Discovery Feed. Every course that you "subscribe" to will show up on your personal profile, and after watching a certain number of videos and taking quizzes, you will have completed that course. With our Freemium business model, we aim to make quality, quick to learn material accessible to people of all groups. In addition, we strive for a knowledge sharing digital environment where people can reignite their curiosity. We created a platform that utilizes the trends of mobile usage and tailors to the rapid pace learning style people today prefer.

### How we built it

We used React Native to build out the frontend interface of the application (including the Discovery Feed, Profile Section, and Quizzes). In addition, we used Convex and Clerk to build the authentication process (providing login and sign up services, including OAuth integration with Google, etc.). We also used Convex database to store relevant user information that is needed for our services.

### Challenges we ran into

We ran into some difficulty with handling the backend part of our code. In addition, some team members were being exposed to certain technologies and languages that we would use for the first time, including React Native and Expo. However, with lots of patience, we were all able to collectively learn and proudly create MinuteCamp.

### Accomplishments we're proud of

Some of us had to learn all of the languages we implemented from scratch, and through this process, we were able to create something that we all feel is going to make an impact. We are proud of pushing through needing to learn how to code, or contribute to the team, fast. We are proud of the skills we have all gained from this experience.

### What we learned

A lot of our beginning coders learned how to use React Native for the first time and build screens, while our more seasoned developers learned how to build more refined UI components/screens and work with a new backend.

### What's next

Technically, we plan to make the backend more useful, as it would track each user's completed courses and completed course count. We plan to populate with more courses, and then implement a recommendation algorithm, so each user has tailored videos on things they want to learn more about. The courses will eventually build out to demonstrate a level of proficiency in a broad subject (similar to Duolingo). We plan on making more drawn-out courses and having periodic progress quizzes rather than one quiz at the end. While we did not have time, we wanted to implement an RAG model that generated unique quiz questions relevant to the course.

## README (from the GitHub repository)

No README available.

## Detected evidence (automated analysis)

Indexed codebase: 40 recognized source files, 85 KB.
- JavaScript (language) — detected in the code
- React (technology) — detected in the code
- TypeScript (language) — detected in the code

## Codebase structure (from repository index)

### Files (47 of 47)

```
.env
.gitignore
app.json
App.tsx
babel.config.js
cache.ts
components/discoverFeed.tsx
components/discoverPost.tsx
components/excel2Feed.tsx
components/excelFeed.tsx
components/hackathonFeed.tsx
components/OAuth.tsx
components/popupSheet.tsx
components/pythonFeed.tsx
components/Styles.ts
components/taxesFeed.tsx
components/VideoPost.tsx
convex/_generated/api.d.ts
convex/_generated/api.js
convex/_generated/dataModel.d.ts
convex/_generated/server.d.ts
convex/_generated/server.js
convex/auth.config.js
convex/README.md
convex/schema.ts
convex/tsconfig.json
convex/users.ts
data/QuizData.ts
env.d.ts
hooks/useCachedResources.ts
hooks/useStoreUserEffect.ts
hooks/useWarmUpBrowser.ts
logger.ts
navigation/index.tsx
navigation/LinkingConfiguration.ts
package.json
sampleData.jsonl
screens/HomeScreen.tsx
screens/MyProfileScreen.tsx
screens/Profile.tsx
screens/Quiz.tsx
screens/SignInScreen.tsx
screens/SignUpScreen.tsx
screens/VerifyCodeScreen.tsx
tasks.js
tsconfig.json
types.tsx
```

### Dependencies

- package.json: @babel/core@^7.20.0, @clerk/clerk-expo@^0.20.5, @expo-google-fonts/inter@^0.2.3, @gorhom/bottom-sheet@^4.6.0, @react-navigation/native@^6.1.10, @react-navigation/native-stack@^6.9.18, @react-navigation/stack@^6.3.21, @types/react@~18.2.45, @types/react-native@^0.73.0, @types/react-native-vector-icons@^6.4.18, convex@^1.9.0, expo@~50.0.7, expo-app-loading@^2.1.1, expo-av@~13.10.5, expo-font@~11.10.3, expo-linear-gradient@^12.7.2, expo-router@^3.4.7, expo-secure-store@^12.8.1, expo-status-bar@~1.11.1, react@18.2.0, react-dom@^18.2.0, react-native@0.73.4, react-native-dotenv@^3.4.10, react-native-gesture-handler@~2.14.0, react-native-get-random-values@~1.8.0, react-native-reanimated@~3.6.2, react-native-safe-area-context@4.8.2, react-native-screens@~3.29.0, react-native-vector-icons@^10.0.3, react-router-dom@^6.22.1, typescript@^5.1.3

### Recent commits (newest first)

- final touches
- Merge branch 'main' of https://github.com/marcreniel/Minutecamp
- erge to main
- added more ui
- made sign out button work on profile
- Merge pull request #8 from marcreniel/discovery-and-course-flow
- Merge branch 'discovery-and-course-flow' of https://github.com/marcreniel/Minutecamp into discovery-and-course-flow
- resolve errors with merge
- Merge branch 'discovery-and-course-flow' of github.com:marcreniel/Minutecamp into discovery-and-course-flow
- feeds for each course
- add coursesCompleted table schema
- added profile to feed
- Merge pull request #7 from marcreniel/discovery-and-course-flow
- Merge branch 'main' of https://github.com/marcreniel/Minutecamp into discovery-and-course-flow
- discover page UI
- Merge branch 'main' of https://github.com/marcreniel/Minutecamp
- linked users to convex db
- use correct relative filepath
- added integration with convex
- added category bar

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

### package.json

```
{
  "name": "minutecamp",
  "version": "1.0.0",
  "main": "node_modules/expo/AppEntry.js",
  "scripts": {
    "start": "expo start",
    "android": "expo start --android",
    "ios": "expo start --ios",
    "web": "expo start --web",
    "ts:check": "tsc"
  },
  "dependencies": {
    "@clerk/clerk-expo": "^0.20.5",
    "@expo-google-fonts/inter": "^0.2.3",
    "@gorhom/bottom-sheet": "^4.6.0",
    "@react-navigation/native": "^6.1.10",
    "@react-navigation/native-stack": "^6.9.18",
    "@react-navigation/stack": "^6.3.21",
    "convex": "^1.9.0",
    "expo": "~50.0.7",
    "expo-app-loading": "^2.1.1",
    "expo-av": "~13.10.5",
    "expo-font": "~11.10.3",
    "expo-linear-gradient": "^12.7.2",
    "expo-router": "^3.4.7",
    "expo-secure-store": "^12.8.1",
    "expo-status-bar": "~1.11.1",
    "react": "18.2.0",
    "react-dom": "^18.2.0",
    "react-native": "0.73.4",
    "react-native-dotenv": "^3.4.10",
    "react-native-gesture-handler": "~2.14.0",
    "react-native-get-random-values": "~1.8.0",
    "react-native-reanimated": "~3.6.2",
    "react-native-safe-area-context": "4.8.2",
    "react-native-screens": "~3.29.0",
    "react-native-vector-icons": "^10.0.3",
    "react-router-dom": "^6.22.1"
  },
  "devDependencies": {
    "@babel/core": "^7.20.0",
    "@types/react": "~18.2.45",
    "@types/react-native": "^0.73.0",
    "@types/react-native-vector-icons": "^6.4.18",
    "typescript": "^5.1.3"
  },
  "private": true
}

```

### App.tsx

```typescript
// App.js
import React from "react";
import AppStack from "./navigation";
import useCachedResources from "./hooks/useCachedResources";
import { tokenCache } from "./cache";
import { CONVEX_URL } from "@env";
import { ClerkProvider, useAuth} from "@clerk/clerk-expo";
import { ConvexProvider, ConvexReactClient } from "convex/react";
import { ConvexProviderWithClerk } from "convex/react-clerk";
import "react-native-get-random-values";
import Tasks from "./tasks";

// Your publishable Key goes here
const publishableKey = "pk_test_bWFnbmV0aWMtdG9ydG9pc2UtOTMuY2xlcmsuYWNjb3VudHMuZGV2JA";

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

const App = () => {
  const isLoadingComplete = useCachedResources();

  if (!isLoadingComplete) {
    return null;
  } else {
    return (
      <ClerkProvider publishableKey={publishableKey} tokenCache={tokenCache}>
        <ConvexProviderWithClerk client={convex} useAuth={useAuth}>
          <AppStack/>
        </ConvexProviderWithClerk>
      </ClerkProvider>
    );
  };
}

export default App;

```

### navigation/index.tsx

```typescript
import * as React from "react";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
import { NavigationContainer } from "@react-navigation/native";
import { GestureHandlerRootView } from "react-native-gesture-handler";
import HomeScreen from "../screens/HomeScreen";
import QuizScreen from "../screens/Quiz";
import SignUpScreen from "../screens/SignUpScreen";
import SignInScreen from "../screens/SignInScreen";
import ProfileScreen from "../screens/Profile";
import VerifyCodeScreen from "../screens/VerifyCodeScreen";
import MyProfileScreen from "../screens/MyProfileScreen";
import { ClerkLoaded } from "@clerk/clerk-expo";
import { useConvexAuth } from "convex/react";

import FeedScreen from '../components/hackathonFeed';
import TaxesFeed from '../components/taxesFeed';
import ExcelFeed from '../components/excelFeed';
import Excel2Feed from '../components/excel2Feed';
import PythonFeed from '../components/pythonFeed';

const Stack = createNativeStackNavigator();

function AppStack() {
  const { isAuthenticated } = useConvexAuth();
  return (
    <GestureHandlerRootView style={{ flex: 1 }}>
      <NavigationContainer>
        <ClerkLoaded>
          <Stack.Navigator>
            {isAuthenticated ? (
              <>
                {/* <Stack.Screen
                  name="MyProfile"
                  component={MyProfileScreen}
                  options={{ title: "MyProfile" }}
                /> */}
                <Stack.Screen
                  name="Home"
                  component={HomeScreen}
                  options={{ headerShown: false }}
                />
                <Stack.Screen
                  name="Profile"
                  /*@ts-ignore*/
                  component={ProfileScreen}
                  options={{ headerShown: false }}
                />
                <Stack.Screen
                  name="Feed"
                  component={FeedScreen}
                  options={{ headerShown: false }}
                />
                <Stack.Screen
                  name="Quiz"
                  component={QuizScreen}
                  options={{ headerShown: false }}
                />
                <Stack.Screen
                  name="TaxesFeed"
                  component={TaxesFeed}
                  options={{ headerShown: false }}
                />
                <Stack.Screen
                  name="ExcelFeed"
                  component={ExcelFeed}
                  options={{ headerShown: false }}
                />
                <Stack.Screen
                  name="Excel2Feed"
                  component={Excel2Feed}
                  options={{ headerShown: false }}
                />
                <Stack.Screen
                  name="PythonFeed"
                  component={PythonFeed}
                  options={{ headerShown: false }}
                />
              </>
            ) : (
              <>
                <Stack.Screen
                  name="SignUp"
                  /*@ts-ignore*/
                  component={SignUpScreen}
                  options={{ title: "Sign Up" }}
                />
                <Stack.Screen
                  name="SignIn"
                  /*@ts-ignore*/
                  component={SignInScreen}
                  options={{ title: "Sign In" }}
                />
                <Stack.Screen
                  name="VerifyCode"
                  /*@ts-ignore*/
                  component={VerifyCodeScreen}
                  options={{ title: "Sign Up" }}
                />
              </>
            )}
          </Stack.Navigator>
        </ClerkLoaded>
      </NavigationContainer>
    </GestureHandlerRootView>
  );
}

export default AppStack;

```

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

```

### logger.ts

```typescript
export function log(msg = "") {
  return console.log(new Date() + ":> " + msg);
}

```

### env.d.ts

```typescript
declare module '@env' {
    export const CONVEX_URL: string;
    export const CONVEX_DEPLOYMENT: string;
  }
```

### babel.config.js

```javascript
module.exports = function (api) {
  api.cache(true);
  return {
    presets: ["babel-preset-expo"],
    plugins: [
      ["module:react-native-dotenv", {
        "moduleName": "@env",
        "path": ".env",
        "safe": false,
        "allowUndefined": true,        
        "blocklist": null,
        "allowlist": null,
        "verbose": false
      }],
      ['react-native-reanimated/plugin']
    ],
   };
};
```

### types.tsx

```typescript
import { NativeStackScreenProps } from "@react-navigation/native-stack";

declare global {
  namespace ReactNavigation {
    interface RootParamList extends RootStackParamList {}
  }
}

export type RootStackParamList = {
  Root: undefined;
  SignUp: undefined;
  SignIn: undefined;
  MyProfile: undefined;
  VerifyCode: undefined;
};

export type RootStackScreenProps<Screen extends keyof RootStackParamList> =
  NativeStackScreenProps<RootStackParamList, Screen>;

```

### 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",
  },
});
```

### cache.ts

```typescript
import * as SecureStore from "expo-secure-store";
import { Platform } from "react-native";
import { TokenCache } from "@clerk/clerk-expo/dist/cache";

const createTokenCache = (): TokenCache => {
    return {
        getToken: (key) => {
            return SecureStore.getItemAsync(key);
        },
        saveToken: (key, token) => {
            return SecureStore.setItemAsync(key, token);
        },
    };
};

// SecureStore is not supported on the web
// https://github.com/expo/expo/issues/7744#issuecomment-611093485
export const tokenCache = Platform.OS !== "web" ? createTokenCache() : undefined;

```

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