Skip to content

Commit

Permalink
Connect logic to Quiz and Result pages
Browse files Browse the repository at this point in the history
  • Loading branch information
SeoulSKY committed Apr 30, 2024
1 parent f64de8e commit 9fe3749
Show file tree
Hide file tree
Showing 13 changed files with 247 additions and 346 deletions.
2 changes: 1 addition & 1 deletion App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export default function App() {
options={{ headerShown: false }}
/>
<Stack.Screen
name="Results"
name="Result"
component={Result}
options={{ headerShown: false }}
/>
Expand Down
25 changes: 0 additions & 25 deletions __tests__/index.test.ts

This file was deleted.

6 changes: 5 additions & 1 deletion __tests__/utils/quiz.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ describe("MultipleChoiceQuestion", () => {
difficulty: Difficulty.EASY,
correctAnswer: 1,
choices: ["1", "2", "3", "4"],
answer: 1,
isCorrect: true,
});
});
Expand Down Expand Up @@ -160,7 +161,10 @@ describe("Quiz", () => {
it("should get the previous quiz", async () => {
await storage.set(path, JSON.stringify(mockQuiz.map(q => q.toJSON())));

expect(await quiz.getSavedQuiz()).toEqual(mockQuiz.map(q => q.toJSON()));
const questions = await quiz.getSavedQuiz();
questions.forEach(q => expect(q).toBeInstanceOf(MultipleChoiceQuestion));
expect(questions.map(q => q.toJSON()))
.toEqual(mockQuiz.map(q => q.toJSON()));
});

it("should throw InvalidStateError if there is no previous quiz", async () => {
Expand Down
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
]
},
"dependencies": {
"@google/generative-ai": "0.5.0",
"@google/generative-ai": "0.9.0",
"@react-navigation/native": "6.1.17",
"@react-navigation/native-stack": "6.9.26",
"@react-navigation/stack": "6.3.29",
Expand Down
142 changes: 76 additions & 66 deletions src/screens/Chat.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import React, {useEffect, useRef, useState} from "react";
import {
Bubble,
GiftedChat,
IMessage,
} from "react-native-gifted-chat";
import {Bubble, GiftedChat, IMessage,} from "react-native-gifted-chat";
import {SafeAreaView} from "react-native-safe-area-context";
import {
FlatList,
Keyboard,
KeyboardAvoidingView,
StyleSheet,
Text,
TextInput,
TouchableOpacity,
View,
TextInput,
Text, KeyboardAvoidingView, FlatList,
} from "react-native";
import {BorderRadius, Colour, FontFamily, FontSize} from "../constants";
import {Ionicons, MaterialCommunityIcons} from "@expo/vector-icons";
Expand All @@ -21,18 +20,18 @@ import {ImageData, MimeType} from "../utils/image";
import Avatar from "../components/Avatar";
import Chat from "../utils/chat";
import {rootLogger} from "../index";
import { StackNavigationProp } from "@react-navigation/stack";
import { useNavigation, ParamListBase } from "@react-navigation/native";
import {StackNavigationProp} from "@react-navigation/stack";
import {ParamListBase, useNavigation} from "@react-navigation/native";
import {HttpError} from "../utils/errors";
import Quiz from "../utils/quiz";
import {sleep} from "../utils";
import {HttpStatusCode, sleep} from "../utils";

const logger = rootLogger.extend("Chat");

const userId = 1;
const botId = 2;

const numUserMessagesForQuiz = 10;
const numUserMessagesForQuiz = 1;

const quizLoadingMessages = [
"Let's engage our minds with a simple quiz!",
Expand Down Expand Up @@ -63,42 +62,47 @@ export default function () {
setHistory(previousMessages => [...previousMessages, message]);
}

/**
* Set the bot profile and the initial message
*/
async function loadData() {
if (!await BotProfile.getInstance().has() || !await UserProfile.getInstance().has()) {
navigation.navigate("SignUp");
return;
}

const bot = await BotProfile.getInstance().get();

setBotProfile(bot);
const chat = await Chat.getInstance();
setChat(chat);

setHistory((await chat.getHistory()).map((message, i) => {
return {
_id: i,
text: message.text,
createdAt: message.timestamp,
user: {
_id: message.author === Participant.BOT ? botId : userId,
name: message.author === Participant.BOT ? bot.name : undefined,
avatar: message.author === Participant.BOT ? bot.image!.path : undefined
},
image: message.images.length > 0 ? message.images[0].path as string : undefined,
};
}));
}

useEffect(() => {
(async function() {
if (!await BotProfile.getInstance().has() || !await UserProfile.getInstance().has()) {
navigation.navigate("SignUp");
return;
}

const bot = await BotProfile.getInstance().get();

setBotProfile(bot);
const chat = await Chat.getInstance();
setChat(chat);

setHistory((await chat.getHistory()).map((message, i) => {
return {
_id: i,
text: message.text,
createdAt: message.timestamp,
user: {
_id: message.author === Participant.BOT ? botId : userId,
name: message.author === Participant.BOT ? bot.name : undefined,
avatar: message.author === Participant.BOT ? bot.image!.path : undefined
},
image: message.images.length > 0 ? message.images[0].path as string : undefined,
};
}));
})().catch(logger.error);
loadData().catch(logger.error);
}, []);

useEffect(() => {
chatContainer.current?.scrollToEnd({animated: true});
}, [history]);

useEffect(() => {
return navigation.addListener("focus",() => {
loadData().catch(logger.error);
});
}, [navigation]);

/**
* Pick an image from the image library
*/
Expand Down Expand Up @@ -127,7 +131,10 @@ export default function () {
* Render the chat page
*/
return (
<SafeAreaView style={styles.container}>
<SafeAreaView style={styles.container} onStartShouldSetResponder={() => {
Keyboard.dismiss();
return false;
}}>
<View style={styles.header}>
<Ionicons name="arrow-back-sharp" size={30} color="black" onPress={async () => {

Expand Down Expand Up @@ -156,7 +163,7 @@ export default function () {
_id: userId,
}}
listViewProps={{
style: styles.listBox
style: styles.listBox,
}}
lightboxProps={{
style: styles.lightBox,
Expand Down Expand Up @@ -253,10 +260,15 @@ export default function () {
} catch (e) {
let message;
if (e instanceof HttpError) {
message = "Sorry, I'm having trouble connecting to the server. Please try again later.";
if (e.status === HttpStatusCode.TOO_MANY_REQUESTS) {
message = "I'm sorry, I'm currently busy. Let's chat later.";
} else {
logger.error(e);
message = "I'm sorry, something happened on my end. Let's chat later.";
}
} else {
logger.error(e);
message = "Sorry, something went wrong. Please try again later.";
message = "I'm sorry, something happened on my end. Let's chat later.";
}

imageToSend && setImage(imageToSend);
Expand All @@ -277,39 +289,37 @@ export default function () {
return;
}

appendMessage({
_id: Date.now(),
text: reply.text,
createdAt: reply.timestamp,
user: {
_id: botId,
name: botProfile!.name,
avatar: botProfile!.image?.path,
},
});
let isQuiz = (await chat!.getHistory())
.filter(m => m.author === Participant.USER).length % numUserMessagesForQuiz === 0;

if ((await chat!.getHistory())
.filter(m => m.author === Participant.USER).length % numUserMessagesForQuiz !== 0) {
setIsTyping(false);
return;
if (isQuiz) {
try {
await Quiz.getInstance().create();
} catch (e) {
logger.error(e);

// continue chatting if failed to create quiz
isQuiz = false;
}
}

await sleep(1000);
setIsTyping(false);

appendMessage({
_id: Date.now(),
text: quizLoadingMessages[Math.floor(Math.random() * quizLoadingMessages.length)],
createdAt: Date.now(),
text: isQuiz ? quizLoadingMessages[Math.floor(Math.random() * quizLoadingMessages.length)] : reply.text,
createdAt: reply.timestamp,
user: {
_id: botId,
name: botProfile!.name,
avatar: botProfile!.image?.path,
},
});

await Quiz.getInstance().create();

navigation.navigate("Quiz");
if (isQuiz) {
await sleep(3000);
navigation.navigate("Quiz");
}
}}>
<MaterialCommunityIcons name="send-circle-outline" style={styles.inputButtonText} />
</TouchableOpacity>}
Expand All @@ -332,6 +342,8 @@ const styles = StyleSheet.create({
marginTop: "auto",
marginBottom: "2%",
zIndex: 1,
borderTopColor: Colour.lightGray,
borderTopWidth: 1,
},
input: {
flex: 1,
Expand Down Expand Up @@ -394,7 +406,5 @@ const styles = StyleSheet.create({
marginBottom: "-10%",
borderTopColor: Colour.lightGray,
borderTopWidth: 1,
borderBottomColor: Colour.lightGray,
borderBottomWidth: 1,
},
});
23 changes: 17 additions & 6 deletions src/screens/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,31 @@ import { UserProfile } from "../utils/profile";
import {useEffect, useState} from "react";
import Animated from "react-native-reanimated";
import {usePulseAnimation} from "../hooks/animations/pulseAnimation";
import {rootLogger} from "../index";

const logger = rootLogger.extend("Home");

export default function Home() {
const navigation = useNavigation<StackNavigationProp<ParamListBase>>();
const [buttonText, setButtonText] = useState("Let's get started!");
const {pulseStyle} = usePulseAnimation();

async function loadData() {
if (await UserProfile.getInstance().has()) {
setButtonText("Let's chat!");
}
}

useEffect(() => {
UserProfile.getInstance().has().then((hasProfile) => {
if (hasProfile) {
setButtonText("Continue");
}
});
loadData().catch(logger.error);
},[]);

useEffect(() => {
navigation.addListener("focus", async () =>
loadData().catch(logger.error)
);
}, [navigation]);

return (
<View style={styles.container}>
<ImageBackground
Expand Down Expand Up @@ -56,7 +67,7 @@ Play brain games to keep your mind sharp.`}</Text>
style={styles.button}
onPress={async () => {
if (await UserProfile.getInstance().has()) {
navigation.navigate("ChatPage");
navigation.navigate("Chat");
} else {
navigation.navigate("SignUp");
}
Expand Down
Loading

0 comments on commit 9fe3749

Please sign in to comment.