Game working alright! 💪
This commit is contained in:
parent
0fb73af58b
commit
dad93bfa14
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@lingdocs/pashto-inflector",
|
"name": "@lingdocs/pashto-inflector",
|
||||||
"version": "1.9.0",
|
"version": "1.9.1",
|
||||||
"author": "lingdocs.com",
|
"author": "lingdocs.com",
|
||||||
"description": "A Pashto inflection and verb conjugation engine, inculding React components for displaying Pashto text, inflections, and conjugations",
|
"description": "A Pashto inflection and verb conjugation engine, inculding React components for displaying Pashto text, inflections, and conjugations",
|
||||||
"homepage": "https://verbs.lingdocs.com",
|
"homepage": "https://verbs.lingdocs.com",
|
||||||
|
|
14
src/App.css
14
src/App.css
|
@ -11,6 +11,20 @@
|
||||||
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
|
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.answer-feedback {
|
||||||
|
transition: opacity 0.3s ease-in;
|
||||||
|
opacity: 0.8;
|
||||||
|
position: fixed;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
z-index: 99999999;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hide {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--secondary: #00c1fc;
|
--secondary: #00c1fc;
|
||||||
--primary: #ffda54;
|
--primary: #ffda54;
|
||||||
|
|
|
@ -68,10 +68,11 @@ export function getRandomTense(type: "basic" | "modal" | "perfect", o?: T.Perfec
|
||||||
return tns;
|
return tns;
|
||||||
}
|
}
|
||||||
|
|
||||||
function TensePicker({ onChange, vps, mode }: {
|
function TensePicker({ onChange, vps, mode, locked }: {
|
||||||
vps: T.VPSelectionState,
|
vps: T.VPSelectionState,
|
||||||
onChange: (p: T.VPSelectionState) => void,
|
onChange: (p: T.VPSelectionState) => void,
|
||||||
mode: "charts" | "phrases" | "quiz",
|
mode: "charts" | "phrases" | "quiz",
|
||||||
|
locked: boolean,
|
||||||
}) {
|
}) {
|
||||||
function onTenseSelect(o: { value: T.VerbTense | T.PerfectTense } | null) {
|
function onTenseSelect(o: { value: T.VerbTense | T.PerfectTense } | null) {
|
||||||
const value = o?.value ? o.value : undefined;
|
const value = o?.value ? o.value : undefined;
|
||||||
|
@ -179,7 +180,7 @@ function TensePicker({ onChange, vps, mode }: {
|
||||||
options={tOptions}
|
options={tOptions}
|
||||||
{...zIndexProps}
|
{...zIndexProps}
|
||||||
/>
|
/>
|
||||||
{vps.verb && <div className="d-flex flex-row justify-content-between align-items-center mt-3 mb-1" style={{ width: "100%" }}>
|
{vps.verb && !locked && <div className="d-flex flex-row justify-content-between align-items-center mt-3 mb-1" style={{ width: "100%" }}>
|
||||||
<div className="btn btn-light clickable" onClick={moveTense("back")}>
|
<div className="btn btn-light clickable" onClick={moveTense("back")}>
|
||||||
<i className="fas fa-chevron-left" />
|
<i className="fas fa-chevron-left" />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -16,10 +16,13 @@ import { randomSubjObj } from "../../library";
|
||||||
import shuffleArray from "../../lib/shuffle-array";
|
import shuffleArray from "../../lib/shuffle-array";
|
||||||
import InlinePs from "../InlinePs";
|
import InlinePs from "../InlinePs";
|
||||||
import { psStringEquals } from "../../lib/p-text-helpers";
|
import { psStringEquals } from "../../lib/p-text-helpers";
|
||||||
|
import classNames from "classnames";
|
||||||
|
import { randFromArray } from "../../lib/misc-helpers";
|
||||||
// import { useReward } from 'react-rewards';
|
// import { useReward } from 'react-rewards';
|
||||||
|
|
||||||
const kingEmoji = "👑";
|
const kingEmoji = "👑";
|
||||||
const servantEmoji = "🙇♂️";
|
const servantEmoji = "🙇♂️";
|
||||||
|
const correctEmoji = ["✅", '🤓', "✅", '😊', "🌹", "✅", "✅", "🕺", "💃", '🥳', "👏", "✅", "💯", "😎", "✅", "👍"];
|
||||||
|
|
||||||
// TODO: Drill Down text display options
|
// TODO: Drill Down text display options
|
||||||
|
|
||||||
|
@ -34,6 +37,8 @@ const servantEmoji = "🙇♂️";
|
||||||
|
|
||||||
// TODO: error handling on error with rendering etc
|
// TODO: error handling on error with rendering etc
|
||||||
|
|
||||||
|
const checkDuration = 400;
|
||||||
|
|
||||||
type QuizState = {
|
type QuizState = {
|
||||||
answer: {
|
answer: {
|
||||||
ps: T.SingleOrLengthOpts<T.PsString[]>;
|
ps: T.SingleOrLengthOpts<T.PsString[]>;
|
||||||
|
@ -62,18 +67,26 @@ export function VPExplorer(props: {
|
||||||
);
|
);
|
||||||
const [mode, setMode] = useStickyState<"charts" | "phrases" | "quiz">("phrases", "verbExplorerMode");
|
const [mode, setMode] = useStickyState<"charts" | "phrases" | "quiz">("phrases", "verbExplorerMode");
|
||||||
const [quizState, setQuizState] = useState<QuizState | undefined>(undefined);
|
const [quizState, setQuizState] = useState<QuizState | undefined>(undefined);
|
||||||
|
const [showCheck, setShowCheck] = useState<boolean>(false);
|
||||||
|
const [currentCorrectEmoji, setCurrentCorrectEmoji] = useState<string>(randFromArray(correctEmoji));
|
||||||
// const { reward } = useReward('rewardId', "emoji", {
|
// const { reward } = useReward('rewardId', "emoji", {
|
||||||
// emoji: ['🤓', '😊', '🥳', "👏", "💯", "😎", "👍"],
|
// emoji: ['🤓', '😊', '🥳', "👏", "💯", "😎", "👍"],
|
||||||
// lifetime: 50,
|
// lifetime: 50,
|
||||||
// elementCount: 10,
|
// elementCount: 10,
|
||||||
// elementSize: 30,
|
// elementSize: 30,
|
||||||
// });
|
// });
|
||||||
|
useEffect(() => {
|
||||||
|
if (mode === "quiz") {
|
||||||
|
handleResetQuiz();
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line
|
||||||
|
}, []);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setVps(o => {
|
setVps(o => {
|
||||||
if (mode === "quiz") {
|
if (mode === "quiz") {
|
||||||
return getRandomVPSelection("both")(
|
const { VPS, qs } = makeQuizState(vps);
|
||||||
makeVPSelectionState(props.verb, o)
|
setQuizState(qs);
|
||||||
);
|
return VPS;
|
||||||
}
|
}
|
||||||
return makeVPSelectionState(props.verb, o);
|
return makeVPSelectionState(props.verb, o);
|
||||||
});
|
});
|
||||||
|
@ -130,8 +143,17 @@ export function VPExplorer(props: {
|
||||||
function checkQuizAnswer(a: T.PsString) {
|
function checkQuizAnswer(a: T.PsString) {
|
||||||
if (!quizState) return;
|
if (!quizState) return;
|
||||||
if (isInAnswer(a, quizState.answer)) {
|
if (isInAnswer(a, quizState.answer)) {
|
||||||
// reward();
|
setShowCheck(true);
|
||||||
|
setTimeout(() => {
|
||||||
handleResetQuiz();
|
handleResetQuiz();
|
||||||
|
}, checkDuration / 2);
|
||||||
|
setTimeout(() => {
|
||||||
|
setShowCheck(false);
|
||||||
|
}, checkDuration);
|
||||||
|
// this sucks, have to do this so the emoji doesn't change in the middle of animation
|
||||||
|
setTimeout(() => {
|
||||||
|
setCurrentCorrectEmoji(randFromArray(correctEmoji));
|
||||||
|
}, checkDuration * 2);
|
||||||
} else {
|
} else {
|
||||||
setQuizState({
|
setQuizState({
|
||||||
...quizState,
|
...quizState,
|
||||||
|
@ -219,6 +241,7 @@ export function VPExplorer(props: {
|
||||||
vps={vps}
|
vps={vps}
|
||||||
onChange={quizLock(setVps)}
|
onChange={quizLock(setVps)}
|
||||||
mode={mode}
|
mode={mode}
|
||||||
|
locked={!!(mode === "quiz" && quizState)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -228,10 +251,15 @@ export function VPExplorer(props: {
|
||||||
{(vps.verb && (mode === "charts")) && <ChartDisplay VS={vps.verb} opts={props.opts} />}
|
{(vps.verb && (mode === "charts")) && <ChartDisplay VS={vps.verb} opts={props.opts} />}
|
||||||
<span id="rewardId" />
|
<span id="rewardId" />
|
||||||
{(mode === "quiz" && quizState) && <div className="text-center">
|
{(mode === "quiz" && quizState) && <div className="text-center">
|
||||||
|
<div style={{ fontSize: "4rem" }} className={classNames("answer-feedback", { hide: !showCheck })}>
|
||||||
|
{currentCorrectEmoji}
|
||||||
|
</div>
|
||||||
{quizState.result === "waiting" ? <>
|
{quizState.result === "waiting" ? <>
|
||||||
<div className="text-muted my-3">Choose a correct answer:</div>
|
<div className="text-muted my-3">Choose a correct answer:</div>
|
||||||
{quizState.options.map(o => <div className="pb-3">
|
{quizState.options.map(o => <div className="pb-3" key={o.f}>
|
||||||
<div className="btn btn-outline-secondary" onClick={() => checkQuizAnswer(o)}>
|
<div className="btn btn-answer btn-outline-secondary" onClick={() => {
|
||||||
|
checkQuizAnswer(o);
|
||||||
|
}}>
|
||||||
<InlinePs opts={props.opts}>{o}</InlinePs>
|
<InlinePs opts={props.opts}>{o}</InlinePs>
|
||||||
</div>
|
</div>
|
||||||
</div>)}
|
</div>)}
|
||||||
|
@ -248,9 +276,6 @@ export function VPExplorer(props: {
|
||||||
</div>
|
</div>
|
||||||
</div>}
|
</div>}
|
||||||
</div>}
|
</div>}
|
||||||
{mode === "quiz" && <div style={{ height: "300px" }}>
|
|
||||||
{/* spacer for blank space while quizzing */}
|
|
||||||
</div>}
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -398,7 +423,7 @@ function getRandomVPSelection(mix: MixType = "both") {
|
||||||
};
|
};
|
||||||
return {
|
return {
|
||||||
subject: s,
|
subject: s,
|
||||||
verb: mix === "both" ? randomizeTense(v, false) : v,
|
verb: randomizeTense(v, true),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -152,6 +152,12 @@ export function randomNumber(minInclusive: number, maxExclusive: number): number
|
||||||
return Math.floor(Math.random() * (maxExclusive - minInclusive) + minInclusive);
|
return Math.floor(Math.random() * (maxExclusive - minInclusive) + minInclusive);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function randFromArray<M>(arr: M[]): M {
|
||||||
|
return arr[
|
||||||
|
Math.floor(Math.random()*arr.length)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: deprecate this because we have it in np-tools?
|
// TODO: deprecate this because we have it in np-tools?
|
||||||
/**
|
/**
|
||||||
* Sees if a possiblePerson (for subject/object) is possible, given the other person
|
* Sees if a possiblePerson (for subject/object) is possible, given the other person
|
||||||
|
|
Loading…
Reference in New Issue