proper caching and sending of test results

This commit is contained in:
lingdocs 2021-09-21 12:45:09 -04:00
parent 82a29978a1
commit db6ba08c1a
5 changed files with 92 additions and 39 deletions

View File

@ -7,10 +7,13 @@ import "./timer.css";
import { import {
getPercentageDone, getPercentageDone,
} from "../lib/game-utils"; } from "../lib/game-utils";
import {
saveResult,
postSavedResults,
} from "../lib/game-results";
import { import {
AT, AT,
getTimestamp, getTimestamp,
postTestResults,
} from "@lingdocs/lingdocs-main"; } from "@lingdocs/lingdocs-main";
import { import {
Types as T, Types as T,
@ -27,7 +30,7 @@ function GameCore<T>({ questions, Display, timeLimit, Instructions, studyLink, i
}) { }) {
// TODO: report pass with id to user info // TODO: report pass with id to user info
const rewardRef = useRef<RewardElement | null>(null); const rewardRef = useRef<RewardElement | null>(null);
const { user, pullUser } = useUser(); const { user, pullUser, setUser } = useUser();
const [finish, setFinish] = useState<null | "pass" | "fail" | "time out">(null); const [finish, setFinish] = useState<null | "pass" | "fail" | "time out">(null);
const [current, setCurrent] = useState<Current<T> | undefined>(undefined); const [current, setCurrent] = useState<Current<T> | undefined>(undefined);
const [questionBox, setQuestionBox] = useState<QuestionGenerator<T>>(questions()); const [questionBox, setQuestionBox] = useState<QuestionGenerator<T>>(questions());
@ -47,6 +50,24 @@ function GameCore<T>({ questions, Display, timeLimit, Instructions, studyLink, i
if (next.done) handleFinish(); if (next.done) handleFinish();
else setCurrent(next.value); 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() { function handleFinish() {
setFinish("pass"); setFinish("pass");
rewardRef.current?.rewardMe(); rewardRef.current?.rewardMe();
@ -56,14 +77,7 @@ function GameCore<T>({ questions, Display, timeLimit, Instructions, studyLink, i
time: getTimestamp(), time: getTimestamp(),
id, id,
}; };
console.log("will post result", JSON.stringify(result)); handleResult(result);
// TODO: Check not showing up - should show up immediately whether online or not
postTestResults([result])
.then((res) => {
if (res.ok) {
pullUser();
}
}).catch(console.error);
} }
function handleQuit() { function handleQuit() {
setFinish(null); setFinish(null);

View File

@ -2,39 +2,34 @@ import React from "react";
import GenderGame from "./sub-cores/GenderGame"; import GenderGame from "./sub-cores/GenderGame";
import UnisexNounGame from "./sub-cores/UnisexNounGame"; import UnisexNounGame from "./sub-cores/UnisexNounGame";
const unisexNounsId = "unisex-nouns-1"; function makeGameRecord(title: string, id: string, game: (id: string) => (() => JSX.Element)): GameRecord {
const nounGender1Id = "gender-nouns-1"; return {
const nounGender2Id = "gender-nouns-2"; title,
id,
export const unisexNounGame: GameRecord = { Game: game(id),
title: "Changing genders on unisex nouns", }
id: unisexNounsId,
Game: function() {
// TODO: Why won't this.id word here??!
return <UnisexNounGame id={unisexNounsId} />;
},
} }
export const nounGenderGame1: GameRecord = { export const nounGenderGame1 = makeGameRecord(
title: "Identify Noun Genders - Level 1", "Identify Noun Genders - Level 1",
id: nounGender1Id, "gender-nouns-1",
Game: function() { (id) => () => <GenderGame id={id} level={1} />,
return <GenderGame id={nounGender1Id} level={1} />; );
}, export const nounGenderGame2 = makeGameRecord(
} "Identify Noun Genders - Level 2",
"gender-nouns-2",
export const nounGenderGame2: GameRecord = { (id) => () => <GenderGame id={id} level={2} />,
title: "Identify Noun Genders - Level 1", );
id: nounGender2Id, export const unisexNounGame = makeGameRecord(
Game: function() { "Changing genders on unisex nouns",
return <GenderGame id={nounGender2Id} level={2} />; "unisex-nouns-1",
}, (id) => () => <UnisexNounGame id={id} />,
} );
const games: GameRecord[] = [ const games: GameRecord[] = [
unisexNounGame,
nounGenderGame1, nounGenderGame1,
nounGenderGame2, nounGenderGame2,
unisexNounGame,
]; ];
export default games; export default games;

View File

@ -48,7 +48,7 @@ const exceptions: Record<string, CategorySet> = {
} }
// consonantFem: words.filter((w) => w.category === "consonant-fem"), // consonantFem: words.filter((w) => w.category === "consonant-fem"),
const amount = 40; const amount = 35;
export default function({level, id}: { level: 1 | 2, id: string}) { export default function({level, id}: { level: 1 | 2, id: string}) {
function* questions () { function* questions () {
@ -105,7 +105,7 @@ export default function({level, id}: { level: 1 | 2, id: string}) {
questions={questions} questions={questions}
id={id} id={id}
Display={Display} Display={Display}
timeLimit={level === 1 ? 65 : 85} timeLimit={level === 1 ? 50 : 70}
Instructions={Instructions} Instructions={Instructions}
/> />
}; };

40
src/lib/game-results.ts Normal file
View File

@ -0,0 +1,40 @@
import {
AT,
postTestResults,
} from "@lingdocs/lingdocs-main";
const fullKey = (uid: AT.UUID) => `test-results-${uid}`;
export async function postSavedResults(uid: AT.UUID): Promise<"sent" | "unsent" | "none"> {
const key = fullKey(uid);
const results = getSavedResults(key);
if (results.length === 0) return "none";
// if the result posting was successful, delete the result from local storage
// and pull the user
try {
const res = await postTestResults(results)
if (res.ok) {
deleteTestResults(res.tests, key);
return "sent";
}
} catch(e) {
console.error(e);
}
return "unsent";
}
export function saveResult(result: AT.TestResult, uid: AT.UUID) {
const key = fullKey(uid);
const results = getSavedResults(key);
localStorage.setItem(key, JSON.stringify([...results, result]));
}
function getSavedResults(key: string): AT.TestResult[] {
return JSON.parse(localStorage.getItem(key) || "[]") as AT.TestResult[];
}
function deleteTestResults(results: AT.TestResult[], key: string) {
const r = getSavedResults(key);
const left = r.filter((rs) => !results.some(x => x.time === rs.time));
localStorage.setItem(key, JSON.stringify(left));
}

View File

@ -6,6 +6,7 @@ import {
userObjIsEqual, userObjIsEqual,
} from "@lingdocs/lingdocs-main"; } from "@lingdocs/lingdocs-main";
import { CronJob } from "cron"; import { CronJob } from "cron";
import { postSavedResults } from "./lib/game-results";
const UserContext = createContext< const UserContext = createContext<
{ {
@ -35,6 +36,9 @@ function UserProvider({ children }: any) {
const checkUserCronJob = new CronJob("1/30 * * * * *", () => { const checkUserCronJob = new CronJob("1/30 * * * * *", () => {
pullUser(); pullUser();
if (value) {
postSavedResults(value.userId);
}
}); });
useEffect(() => { useEffect(() => {