import { useState, useRef } from "react"; import { CountdownCircleTimer } from "react-countdown-circle-timer"; import Reward, { RewardElement } from 'react-rewards'; import Link from "../components/Link"; import { useUser } from "../user-context"; import "./timer.css"; import { getPercentageDone, } from "../lib/game-utils"; import { saveResult, postSavedResults, } from "../lib/game-results"; import { AT, getTimestamp, } from "@lingdocs/lingdocs-main"; import { Types, } from "@lingdocs/pashto-inflector"; const errorVibration = 200; function GameCore({ questions, Display, timeLimit, Instructions, studyLink, id }:{ id: string, studyLink: string, Instructions: (props: { opts?: Types.TextOptions }) => JSX.Element, questions: () => QuestionGenerator, Display: (props: QuestionDisplayProps) => JSX.Element, timeLimit: number; }) { // TODO: report pass with id to user info const rewardRef = useRef(null); const { user, pullUser, setUser } = useUser(); const [finish, setFinish] = useState(null); const [current, setCurrent] = useState | undefined>(undefined); const [questionBox, setQuestionBox] = useState>(questions()); const [timerKey, setTimerKey] = useState(1); function handleCallback(correct: boolean) { if (correct) handleAdvance(); else handleFailure(); } function handleFailure() { // rewardRef.current?.punishMe(); setFinish("fail"); navigator.vibrate(errorVibration); } function handleAdvance() { const next = questionBox.next(); if (next.done) handleFinish(); else setCurrent(next.value); } function handleResult(result: AT.TestResult) { // add the test to the user object if (!user) return; setUser((u) => { // pure type safety with the prevUser if (!u) return u; return { ...u, tests: [...u.tests, result], }; }); // save the test result in local storage saveResult(result, user.userId); // try to post the result postSavedResults(user.userId).then((r) => { if (r === "sent") pullUser(); }).catch(console.error); } function handleFinish() { setFinish("pass"); rewardRef.current?.rewardMe(); if (!user) return; const result: AT.TestResult = { done: true, time: getTimestamp(), id, }; handleResult(result); } function handleQuit() { setFinish(null); setCurrent(undefined); } function handleRestart() { const newQuestionBox = questions(); const { value } = newQuestionBox.next(); // just for type safety -- the generator will have at least one question if (!value) return; setQuestionBox(newQuestionBox); setFinish(null); setCurrent(value); setTimerKey(prev => prev + 1); } function handleTimeOut() { setFinish("time out"); navigator.vibrate(errorVibration); } function getProgressWidth(): string { const num = !current ? 0 : (finish === "pass") ? 100 : getPercentageDone(current.progress); return `${num}%`; } const progressColor = finish === "pass" ? "success" : finish === "fail" ? "danger" : "primary"; return
{current &&
{!finish && }
}
{finish === null && (current ?
:
{/* TODO: ADD IN TEXT DISPLAY OPTIONS HERE TOO - WHEN WE START USING THEM*/}
) } {finish === "pass" &&

🎉 Finished!

} {(finish === "fail" || finish === "time out") &&

{failMessage(current?.progress, finish)}

}
; } function failMessage(progress: Progress | undefined, finish: "time out" | "fail"): string { const pDone = progress ? getPercentageDone(progress) : 0; const { message, face } = pDone < 20 ? { message: "No, sorry", face: "😑" } : pDone < 30 ? { message: "Oops, that's wrong", face: "😟" } : pDone < 55 ? { message: "Fail", face: "😕" } : pDone < 78 ? { message: "You almost got it!", face: "😩" } : { message: "Nooo! So close!", face: "😭" }; return finish === "fail" ? `${message} ${face}` : `⏳ Time's Up ${face}`; } export default GameCore;