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 {
getPercentageDone,
} from "../lib/game-utils";
import {
saveResult,
postSavedResults,
} from "../lib/game-results";
import {
AT,
getTimestamp,
postTestResults,
} from "@lingdocs/lingdocs-main";
import {
Types as T,
@ -27,7 +30,7 @@ function GameCore<T>({ questions, Display, timeLimit, Instructions, studyLink, i
}) {
// TODO: report pass with id to user info
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 [current, setCurrent] = useState<Current<T> | undefined>(undefined);
const [questionBox, setQuestionBox] = useState<QuestionGenerator<T>>(questions());
@ -47,6 +50,24 @@ function GameCore<T>({ questions, Display, timeLimit, Instructions, studyLink, i
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();
@ -56,14 +77,7 @@ function GameCore<T>({ questions, Display, timeLimit, Instructions, studyLink, i
time: getTimestamp(),
id,
};
console.log("will post result", JSON.stringify(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);
handleResult(result);
}
function handleQuit() {
setFinish(null);

View File

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

View File

@ -48,7 +48,7 @@ const exceptions: Record<string, CategorySet> = {
}
// consonantFem: words.filter((w) => w.category === "consonant-fem"),
const amount = 40;
const amount = 35;
export default function({level, id}: { level: 1 | 2, id: string}) {
function* questions () {
@ -105,7 +105,7 @@ export default function({level, id}: { level: 1 | 2, id: string}) {
questions={questions}
id={id}
Display={Display}
timeLimit={level === 1 ? 65 : 85}
timeLimit={level === 1 ? 50 : 70}
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,
} from "@lingdocs/lingdocs-main";
import { CronJob } from "cron";
import { postSavedResults } from "./lib/game-results";
const UserContext = createContext<
{
@ -35,6 +36,9 @@ function UserProvider({ children }: any) {
const checkUserCronJob = new CronJob("1/30 * * * * *", () => {
pullUser();
if (value) {
postSavedResults(value.userId);
}
});
useEffect(() => {