# Project export: MindMint: Empowering Education with AI & NFTs

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: NFTs, Verifiable Credentials, AI Generated Feedback, Spaced Repetition all in an Educational App: Improves Learning Experience for Children with incentives, Allows Teachers to Manage Classes with ease
- Devpost: https://devpost.com/software/learnboost-empowering-education-with-ai-nfts
- GitHub: https://github.com/shray3420/treehacks2024
- Video: https://www.youtube.com/embed/F02jUUxgL6k?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Result: winner (Crossmint: Best Identity Credential Application Hack (2x rabbit r1 + $1k credit [1st] & 4x Ledger Nano S Plus + $400 credit [2nd]))
- Team: 4 GitHub contributor(s) — Socrates Osorio Diaz (28 commits), Shray Srivastava (20 commits), Arnav Rastogi (13 commits), Aiden Tung (8 commits)

## Devpost submission (written by the team)

### Inspiration

Inspired by the learning incentives offered by Duolingo, and an idea from a real customer (Shray's 9 year old cousin), we wanted to elevate the learning experience by integrating modern technologies, incentivizing students to learn better and teaching them about different school subjects, AI, and NFTs simultaneously.

### What it does

It is an educational app, offering two views, Student and Teacher. On Student view, compete with others in your class through a leaderboard by solving questions correctly and earning points. If you get questions wrong, you have the chance to get feedback from Together.ai's Mistral model. Use your points to redeem cool NFT characters and show them off to your peers/classmates in your profile collection! For Teachers, manage students and classes and see how each student is doing.

### How we built it

Built using TypeScript, React Native and Expo, it is a quickly deployable mobile app. We also used Together.ai for our AI generated hints and feedback, and CrossMint for verifiable credentials and managing transactions with Stable Diffusion generated NFTs

### Challenges we ran into

We had some trouble deciding which AI models to use, but settled on Together.ai's API calls for its ease of use and flexibility. Initially, we wanted to do AI generated questions but understandably, these had some errors so we decided to use AI to provide hints and feedback when a student gets a question wrong. Using CrossMint and creating our stable diffusion NFT marketplace was also challenging, but we are proud of how we successfully incorporated it and allowed each student to manage their wallets and collections in a fun and engaging way.

### Accomplishments we're proud of

Using Together.ai and CrossMint for the first time, and implementing numerous features, such as a robust AI helper to help with any missed questions, and allowing users to buy and collect NFTs directly on the app.

### What we learned

Learned a lot about NFTs, stable diffusion, how to efficiently prompt AIs, and how to incorporate all of this into an Expo React Native app. Also met a lot of cool people and sponsors at this event and loved our time at TreeHacks!

### What's next

Our priority is to incorporate a spaced repetition-styled learning algorithm, similar to what Anki does, to tailor the learning curves of various students and help them understand difficult and challenging concepts efficiently. In the future, we would want to have more subjects and grade levels, and allow the teachers to input questions for the student to solve. Another interesting idea we had was to create a mini real-time interactive game for students to play among themselves, so they can encourage each other to play between themselves.

## README (from the GitHub repository)

No README available.

## Detected evidence (automated analysis)

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

## Codebase structure (from repository index)

### Files (46 of 46)

```
.gitignore
package.json
treehacks/.gitignore
treehacks/app.json
treehacks/App.tsx
treehacks/babel.config.js
treehacks/components/Auth/Auth.tsx
treehacks/components/Auth/SignIn.tsx
treehacks/components/Auth/SignUp.tsx
treehacks/components/Skip.tsx
treehacks/components/Student/Home/StudentHome.tsx
treehacks/components/Student/Home/StudentHomeStack.tsx
treehacks/components/Student/Home/Subjects/History.tsx
treehacks/components/Student/Home/Subjects/Math.tsx
treehacks/components/Student/Home/Subjects/Mathematics.tsx
treehacks/components/Student/Home/Subjects/Physics.tsx
treehacks/components/Student/Home/Subjects/Science.tsx
treehacks/components/Student/Home/Subjects/Subject.tsx
treehacks/components/Student/LeaderBoard.tsx
treehacks/components/Student/Profile/ProfileHome.tsx
treehacks/components/Student/Profile/ProfileHomeStack.tsx
treehacks/components/Student/RewardStore/.gitignore
treehacks/components/Student/RewardStore/Popup.tsx
treehacks/components/Student/RewardStore/RewardStore.tsx
treehacks/components/Student/RewardStore/RewardStoreStack.tsx
treehacks/components/Student/StudentNav.tsx
treehacks/components/Teacher/Home/CreateClass.tsx
treehacks/components/Teacher/Home/CreateCourse.tsx
treehacks/components/Teacher/Home/ShowClasses.tsx
treehacks/components/Teacher/Home/ShowStudents.tsx
treehacks/components/Teacher/Home/StudentProgressModals.tsx
treehacks/components/Teacher/Home/TeacherHome.tsx
treehacks/components/Teacher/Home/TeacherHomeStack.tsx
treehacks/components/Teacher/Profile/ProfileHome.tsx
treehacks/components/Teacher/Profile/ProfileHomeStack.tsx
treehacks/components/Teacher/TeacherNav.tsx
treehacks/constants/Colors.ts
treehacks/constants/ProgressBar.tsx
treehacks/constants/Toast.tsx
treehacks/constants/types.ts
treehacks/crossmint/crossmint.ts
treehacks/firebase/auth.ts
treehacks/firebase/firebase.ts
treehacks/firebase/firestore.ts
treehacks/package.json
treehacks/tsconfig.json
```

### Dependencies

- treehacks/package.json: @babel/core@^7.20.0, @firebase/auth@^1.6.0, @react-native-async-storage/async-storage@^1.22.0, @react-native-firebase/app@^18.8.0, @react-native-firebase/auth@^18.8.0, @react-navigation/bottom-tabs@^6.5.12, @react-navigation/native@^6.1.10, @react-navigation/stack@^6.3.21, @types/base-64@^1.0.2, @types/react@~18.2.45, @types/react-native@^0.73.0, @types/react-navigation@^3.4.0, axios@^1.6.7, babel-plugin-module-resolver@^5.0.0, base-64@^1.0.0, expo@~50.0.7, expo-file-system@^16.0.6, expo-status-bar@~1.11.1, firebase@^10.8.0, query-string@^8.2.0, react@18.2.0, react-native@0.73.4, react-native-dropdown-picker@^5.4.6, react-native-safe-area-context@^4.9.0, react-native-screens@^3.29.0, react-native-vector-icons@^10.0.3, together-ai-sdk@^0.0.7, typescript@^5.1.3

### Recent commits (newest first)

- random changes
- package change
- ignore warnings
- tweaks
- UI
- last submission
- Merge branch 'main' of https://github.com/shray3420/treehacks2024
- clean-up
- Merge branch 'main' of https://github.com/shray3420/treehacks2024
- Fix icon and welcome label
- add AI hint on NFTs
- auth changes
- Merge pull request #19 from shray3420/smallChanges
- small changes
- added page
- styling changes
- Fix merge with nft
- Implement NFT Crossmint
- remove merge conflicts
- Merge pull request #18 from shray3420/smallChanges

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

### package.json

```
{}

```

### treehacks/package.json

```
{
  "name": "treehacks",
  "version": "1.0.0",
  "main": "node_modules/expo/AppEntry.js",
  "scripts": {
    "start": "expo start --tunnel",
    "android": "expo start --android",
    "ios": "expo start --ios",
    "web": "expo start --web"
  },
  "dependencies": {
    "@firebase/auth": "^1.6.0",
    "@react-native-async-storage/async-storage": "^1.22.0",
    "@react-native-firebase/app": "^18.8.0",
    "@react-native-firebase/auth": "^18.8.0",
    "@react-navigation/bottom-tabs": "^6.5.12",
    "@react-navigation/native": "^6.1.10",
    "@react-navigation/stack": "^6.3.21",
    "axios": "^1.6.7",
    "base-64": "^1.0.0",
    "expo": "~50.0.7",
    "expo-file-system": "^16.0.6",
    "expo-status-bar": "~1.11.1",
    "firebase": "^10.8.0",
    "query-string": "^8.2.0",
    "react": "18.2.0",
    "react-native": "0.73.4",
    "react-native-dropdown-picker": "^5.4.6",
    "react-native-safe-area-context": "^4.9.0",
    "react-native-screens": "^3.29.0",
    "react-native-vector-icons": "^10.0.3",
    "together-ai-sdk": "^0.0.7"
  },
  "devDependencies": {
    "@babel/core": "^7.20.0",
    "@types/base-64": "^1.0.2",
    "@types/react": "~18.2.45",
    "@types/react-native": "^0.73.0",
    "@types/react-navigation": "^3.4.0",
    "babel-plugin-module-resolver": "^5.0.0",
    "typescript": "^5.1.3"
  },
  "private": true
}

```

### treehacks/App.tsx

```typescript
import { StatusBar } from 'expo-status-bar';
import { Button, StyleSheet, Text, View } from 'react-native';
import { StudentNav } from './components/Student/StudentNav';
import { useEffect, useState } from 'react';
import { User, onAuthStateChanged } from "firebase/auth";
import { DocumentData } from 'firebase/firestore';
import { auth } from './firebase/firebase';
import { Auth } from './components/Auth/Auth';
import { fetchUserData } from './firebase/firestore';
import { TeacherNav } from './components/Teacher/TeacherNav';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { LogBox } from "react-native"

LogBox.ignoreAllLogs(true)
// import { encode, decode } from 'js-base64';

/* if(typeof atob === 'undefined') {
  global.atob = decode;
} */
//  if (!global.btoa) { global.btoa = encode; } if (!global.atob) { global.atob = decode; }
import { decode } from 'base-64';
import { signOutUser } from './firebase/auth';

if(typeof atob === 'undefined') {
  global.atob = decode;
}

export default function App() {
  const [user, setUser] = useState<User>();
  const [userData, setUserData] = useState<DocumentData>();
  const [error, setError] = useState<string>("");

  useEffect(() => {
    const checkUser = async () => {
      const storedUser = await AsyncStorage.getItem('user');
      if (storedUser) {
        setUser(JSON.parse(storedUser));
        // Optionally, fetch user data here if needed
      }
    };
  
    checkUser();
  }, []);
  
  onAuthStateChanged(auth, async (user) => {
    if (user) {
      await AsyncStorage.setItem('user', JSON.stringify(user));
      setUser(user);
    } else {
      await AsyncStorage.removeItem('user');
      setUser(undefined);
    }
  });
  if (user === undefined) {
    return (
      <View style={styles.container}>
        <Auth />
      </View>
    );
  } else {
    if (userData === undefined) {
      fetchUserData(user, setUserData);
      // console.log(userData);
      return (
        <View style={styles.container}>
          <Text>Loading</Text>
          <Button title="logout" onPress={() => signOutUser(setError)}></Button>

        </View>
      )
    } else if (userData.accountType == undefined) {
      return (
        <View style={styles.container}>
          <Text>User has no type.</Text>
        </View>
      );
      
    } 
    else if (userData.accountType == "Teacher") {
      return <TeacherNav/>
    }else if (userData.accountType == "Student") {
      return <StudentNav />
    } 
    else {
      return (
        <View style={styles.container}>
          <Text>Navigation not setup yet for {userData.type}</Text>
        </View>
      );
    }
}
}


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

```

### treehacks/babel.config.js

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

```

### treehacks/constants/Colors.ts

```typescript
export const Colors = {
  background: "white", 
  primary: "#007aff", // blueish
  secondary: "#030003", // white
  accent1: "#00c04a", // greenish
  accent2: "#ff003a", //redish#030003
  blue: "blue",
  red: "red",
  green: "green",
  toastSuccess: "green",
  toastError: "red",
  lightgray: "lightgray",
  textPrimary: "black",
  textSecondary: "white",
};

```

### treehacks/constants/types.ts

```typescript
export interface StudentData {
    accountType: string;
    classes: Array<string>;
    email: string;
    gradeLevel: string;
    name: string;
    points: number;
    leaderBoardPoints: number;
    subjectPoints: {
        [subject: string]: number;
    };
    username: string;
    id: string;
  }
  
  export interface Classes {
    [className: string]: Array<string>;
  }
  
export interface TeacherData {
    accountType: string;
    classes: Classes;
    email: string;
    name: string;
    username: string;
  }
  
```

### treehacks/components/Skip.tsx

```typescript
import {
  View,
  Text,
  TouchableOpacity,
  StyleSheet,
  TextStyle,
  ViewStyle,
} from "react-native";
import React from "react";
import { Colors } from "../constants/Colors";

interface SkipProps {
  onPress: () => void;
}

const Skip: React.FC<SkipProps> = ({ onPress }) => {
  return (
    <TouchableOpacity onPress={onPress}>
      <Text style={styles.text}>Skip Question</Text>
    </TouchableOpacity>
  );
};

const styles = StyleSheet.create({
  text: {
    marginRight: 5,
    fontSize: 18,
    marginTop: 40,
    color: Colors.accent2, 
    textDecorationLine: 'underline', 
  },
});


export default Skip;

```

### treehacks/firebase/firebase.ts

```typescript
// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";
import { firebaseConfig } from "./api";
import AsyncStorage from '@react-native-async-storage/async-storage';
import {initializeAuth} from 'firebase/auth';
import {getReactNativePersistence} from 'firebase/auth';
import { getFirestore } from "firebase/firestore";

// , { persistence: getReactNativePersistence(AsyncStorage)
const app = initializeApp(firebaseConfig);
export const auth = initializeAuth(app, { persistence: getReactNativePersistence(AsyncStorage)});
export const db = getFirestore(app);

export { getFirestore };

```

### treehacks/firebase/firestore.ts

```typescript
import { User } from "firebase/auth";
import { doc, getDoc, DocumentData} from "firebase/firestore";
import { db } from "./firebase";
import { FirebaseError } from "firebase/app";
import { SetStateAction } from "react";

/**
 * Gets the data associated with the user
 * @param user user to get data from
 * @returns user's document data (or null if doesn't exist)
 */
export const fetchUserData = (user: User, setUserData: {(value: SetStateAction<DocumentData | undefined>): void}) : void => {
    const userRef = doc(db, 'users', user.uid);
    getDoc(userRef).then((userData: DocumentData) => {
        setUserData(userData.data());
        console.log(userData.data());
    }).catch((error: FirebaseError) => {
        handleError(error);
    });
};

const handleError = (error: FirebaseError) => {
  const errorCode: string = error.code;
  const errorMessage = error.message;
  console.log(errorCode, errorMessage);
}
```

### treehacks/constants/ProgressBar.tsx

```typescript
import React from "react";
import { View, Text, StyleSheet } from "react-native";
import {Colors} from "../constants/Colors";

interface ProgressBarProps {
  count: number;
  capacity: number;
}

export const ProgressBar: React.FC<ProgressBarProps> = ({ count, capacity }) => {
  const clampedProgress = Math.max(0, Math.min(count / capacity, 1));
  const percentage = Math.round(clampedProgress * 100);
  let progressBarColor = Colors.accent1;

  return (
    <View style={styles.container}>
      <View style={styles.progressBar}>
        <View style={[styles.filledProgress, { width: `${percentage}%`, backgroundColor: progressBarColor }]}>
          {/* <CustomText style={styles.progressText}>
            {`${percentage}%`}
          </CustomText> */}
        </View>
        
          <Text style={[styles.progressText, styles.unfilledProgressText]}>
            {`${percentage}%`}
          </Text>
        
      </View>
      <Text style={styles.countCapacityText}>
        {count}/{capacity}
      </Text>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flexDirection: "row",
    alignItems: "center",
    width: "50%",
    marginLeft: 5,
    position: "relative",
    left: -30,
    marginTop: 5,
  },
  progressBar: {
    width: "100%",
    height: 20,
    flexDirection: "row",
    backgroundColor: Colors.lightgray,
    borderRadius: 10,
    overflow: "hidden",
    
  },
  filledProgress: {
    justifyContent: "center", // Center text vertically
    alignItems: "flex-end", // Align text to the right
  },
  progressText: {
    fontWeight: 'bold',
    color: 'black', // Ensuring text is visible on colored background
  },
  unfilledProgressText: {
    position: 'absolute',
    left: 0,
    right: 0,
    textAlign: 'center', // Center text horizontally
    color: 'black', // Text color for unfilled section
  },
  countCapacityText: {
    marginLeft: 5,
    color: 'gray',
    fontSize: 13,
  },
});

```

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