study links on browser and formatting fix on phonetics viewer

This commit is contained in:
lingdocs 2021-09-30 19:21:08 -04:00
parent a0b1c04d1d
commit 32dc0518a2
11 changed files with 132 additions and 80 deletions

View File

@ -23,6 +23,7 @@
"react-countdown-circle-timer": "^2.5.4", "react-countdown-circle-timer": "^2.5.4",
"react-dom": "^17.0.1", "react-dom": "^17.0.1",
"react-ga": "^3.3.0", "react-ga": "^3.3.0",
"react-media": "^1.10.0",
"react-rewards": "^1.1.2", "react-rewards": "^1.1.2",
"react-router-dom": "^5.2.0", "react-router-dom": "^5.2.0",
"react-router-hash-link": "^2.3.1", "react-router-hash-link": "^2.3.1",

View File

@ -37,8 +37,6 @@ if (prod) {
function App(props: RouteComponentProps) { function App(props: RouteComponentProps) {
const [navOpen, setNavOpen] = useState(false); const [navOpen, setNavOpen] = useState(false);
const { user } = useUser(); const { user } = useUser();
// TODO: seperate function for getUserInfo with useUser and fetch
// then set cronjob to call that - also do signin flox
useEffect(() => { useEffect(() => {
ReactGA.pageview(window.location.pathname); ReactGA.pageview(window.location.pathname);
}, []); }, []);

View File

@ -12,7 +12,7 @@ const isObject = x => (
function Table({ headRow, children, opts, wide }) { function Table({ headRow, children, opts, wide }) {
return ( return (
<div style={{ overflowX: "auto", marginBottom: "1em" }}> <div style={{ overflowX: "auto", marginBottom: "1em" }}>
<table class="table" style={wide ? { minWidth: "635px" } : {}}> <table className="table" style={wide ? { minWidth: "635px" } : {}}>
{headRow && <thead> {headRow && <thead>
<tr> <tr>
{headRow.map((h, i) => ( {headRow.map((h, i) => (

View File

@ -8,6 +8,7 @@ import {
} from "./phonemes"; } from "./phonemes";
import playAudio from "../../lib/play-audio"; import playAudio from "../../lib/play-audio";
import views from "./views"; import views from "./views";
import Media from "react-media";
export type ViewOptions = "all" | "shortVowel" | "longVowel" | "fiveYs" | "specialConsonant"; export type ViewOptions = "all" | "shortVowel" | "longVowel" | "fiveYs" | "specialConsonant";
@ -42,74 +43,80 @@ class PhoneticsViewer extends React.Component<any, IAppState> {
} }
return <> return <>
<div className="text-center mt-4"> <div className="text-center mt-4">
<div className="btn-group mb-3"> <Media queries={{ small: "(max-width: 599px)" }}>
{views.map(({ label, value }) => ( {matches => (
<button <div className={`btn-group${matches.small ? "-vertical" : ""} mb-3`}>
key={value} {views.map(({ label, value }) => (
type="button" <button
className={classNames("btn", "btn-outline-secondary", { key={value}
active: this.state.view === value, type="button"
})} className={classNames("btn", "btn-outline-secondary", {
onClick={() => this.setState({ view: value })} active: this.state.view === value,
> })}
{label} onClick={() => this.setState({ view: value })}
</button> >
))} {label}
</div> </button>
))}
</div>
)}
</Media>
<div className="small mb-2"><i className="fas fa-volume-down"></i> click the phonetic letter or examples to hear - not all sounds are available</div> <div className="small mb-2"><i className="fas fa-volume-down"></i> click the phonetic letter or examples to hear - not all sounds are available</div>
</div> </div>
<table className="table table-striped"> <div style={{ overflowX: "auto", marginBottom: "1em" }}>
<thead> <table className="table table-striped">
<tr> <thead>
<th>Phonetic Letter</th> <tr>
{/* <th>IPA Letter</th> */} <th>Phonetic Letter</th>
<th>Short Explanation</th> {/* <th>IPA Letter</th> */}
<th>Example</th> <th>Short Explanation</th>
<th>Pashto Letter(s)</th> <th>Example</th>
</tr> <th>Pashto Letter(s)</th>
</thead>
<tbody>
{phonemesShowing.map((phoneme) => (
<tr key={phoneme.phoneme}>
<td onClick={generatePlayerFunction(phoneme)}>
{phoneme.phoneme}
</td>
{/* <td>{phoneme.ipa.letter} </td> */}
<td>
{phoneme.quickExplanation}{" "}
{phoneme.ipa.video && (
<a href={phoneme.ipa.video} target="_blank" rel="noopener noreferrer">
<i className="fa fa-video" />
</a>
)}
</td>
<td onClick={generatePlayerFunction(phoneme.examples[0])}>
{highlightExample(
phoneme.examples[0].f,
phoneme.examples[0].fHighlight
)}
{` - `}
{highlightExample(
phoneme.examples[0].p,
phoneme.examples[0].pHighlight
)}
</td>
<td>
{phoneme.possibleLetters
? phoneme.possibleLetters.reduce(
(s, l) =>
`${s}${l.letter} ${
l.alternate ? ` (${l.alternate}) ` : ""
}`,
""
)
: ""}
{/* phoneme.diacritic && `(diacritic ◌${phoneme.diacritic})` */}
</td>
</tr> </tr>
))} </thead>
</tbody> <tbody>
</table> {phonemesShowing.map((phoneme) => (
<tr key={phoneme.phoneme}>
<td onClick={generatePlayerFunction(phoneme)}>
{phoneme.phoneme}
</td>
{/* <td>{phoneme.ipa.letter} </td> */}
<td>
{phoneme.quickExplanation}{" "}
{phoneme.ipa.video && (
<a href={phoneme.ipa.video} target="_blank" rel="noopener noreferrer">
<i className="fa fa-video" />
</a>
)}
</td>
<td onClick={generatePlayerFunction(phoneme.examples[0])}>
{highlightExample(
phoneme.examples[0].f,
phoneme.examples[0].fHighlight
)}
{` - `}
{highlightExample(
phoneme.examples[0].p,
phoneme.examples[0].pHighlight
)}
</td>
<td>
{phoneme.possibleLetters
? phoneme.possibleLetters.reduce(
(s, l) =>
`${s}${l.letter} ${
l.alternate ? ` (${l.alternate}) ` : ""
}`,
""
)
: ""}
{/* phoneme.diacritic && `(diacritic ◌${phoneme.diacritic})` */}
</td>
</tr>
))}
</tbody>
</table>
</div>
{selectedOption?.notes && <div> {selectedOption?.notes && <div>
<p><strong>Notes about {selectedOption.label.toLowerCase()}:</strong></p> <p><strong>Notes about {selectedOption.label.toLowerCase()}:</strong></p>
{selectedOption.notes} {selectedOption.notes}

View File

@ -4,7 +4,7 @@ title: Games 🎮
import Link from "../components/Link"; import Link from "../components/Link";
There are little games/quizzes scattered throughout this book. Here you can see them all listed below. If you are <Link to="/account">logged in</Link>, when you successfully complete a game you will see a ✅ beside it. Try to master them all! 🤓🏆 There are little games/quizzes scattered throughout this book. Here you can see them all listed below. If you are <Link to="/account">logged in</Link>, when you successfully complete a game you will see a ✅ beside it. To study for a game, click on the 📚 on the right. Try to master them all! 🤓🏆
There currently just a few games available. Keep checking back as over time there will be lots of games covering all the aspects of grammar. There currently just a few games available. Keep checking back as over time there will be lots of games covering all the aspects of grammar.

View File

@ -1,6 +1,7 @@
import React, { useState } from "react"; import React, { useState } from "react";
import games from "./games"; import games from "./games";
import { useUser } from "../user-context"; import { useUser } from "../user-context";
import Link from "../components/Link";
import SmoothCollapse from "react-smooth-collapse"; import SmoothCollapse from "react-smooth-collapse";
function GamesBrowser() { function GamesBrowser() {
@ -15,7 +16,7 @@ function GamesBrowser() {
{games.map((chapter) => ( {games.map((chapter) => (
<> <>
<h3 key={chapter.chapter}>{chapter.chapter}</h3> <h3 key={chapter.chapter}>{chapter.chapter}</h3>
{chapter.items.map(({ id, title, Game }) => { {chapter.items.map(({ id, title, Game, studyLink }) => {
const done = user && user.tests.some(t => t.id === id); const done = user && user.tests.some(t => t.id === id);
const open = opened === id; const open = opened === id;
return <div key={id}> return <div key={id}>
@ -23,10 +24,16 @@ function GamesBrowser() {
<div> <div>
<h4 className="my-4 clickable" onClick={() => handleTitleClick(id)}> <h4 className="my-4 clickable" onClick={() => handleTitleClick(id)}>
<i className={`fas fa-caret-${open ? "down" : "right"}`}></i> {title} <i className={`fas fa-caret-${open ? "down" : "right"}`}></i> {title}
{` `}
</h4> </h4>
</div> </div>
<div> <div>
<h4>{done ? "✅" : ""}</h4> <h4>
{done ? "✅"
:
<Link to={studyLink}>{"📚"}</Link>
}
</h4>
</div> </div>
</div> </div>
<SmoothCollapse expanded={open}> <SmoothCollapse expanded={open}>

View File

@ -2,28 +2,37 @@ 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";
function makeGameRecord(title: string, id: string, game: (id: string) => (() => JSX.Element)): GameRecord { function makeGameRecord(
title: string,
id: string,
studyLink: string,
game: (id: string, link: string) => (() => JSX.Element),
): GameRecord {
return { return {
title, title,
studyLink,
id, id,
Game: game(id), Game: game(id, studyLink),
} }
} }
export const nounGenderGame1 = makeGameRecord( export const nounGenderGame1 = makeGameRecord(
"Identify Noun Genders - Level 1", "Identify Noun Genders - Level 1",
"gender-nouns-1", "gender-nouns-1",
(id) => () => <GenderGame id={id} level={1} />, "/nouns/nouns-gender#gender-by-ending",
(id, link) => () => <GenderGame id={id} level={1} link={link} />,
); );
export const nounGenderGame2 = makeGameRecord( export const nounGenderGame2 = makeGameRecord(
"Identify Noun Genders - Level 2", "Identify Noun Genders - Level 2",
"gender-nouns-2", "gender-nouns-2",
(id) => () => <GenderGame id={id} level={2} />, "/nouns/nouns-gender#exceptions",
(id, link) => () => <GenderGame id={id} level={2} link={link} />,
); );
export const unisexNounGame = makeGameRecord( export const unisexNounGame = makeGameRecord(
"Changing genders on unisex nouns", "Changing genders on unisex nouns",
"unisex-nouns-1", "unisex-nouns-1",
(id) => () => <UnisexNounGame id={id} />, "/nouns/nouns-unisex/",
(id, link) => () => <UnisexNounGame id={id} link={link} />,
); );
const games: { chapter: string, items: GameRecord[] }[] = [ const games: { chapter: string, items: GameRecord[] }[] = [

View File

@ -50,7 +50,7 @@ const exceptions: Record<string, CategorySet> = {
const amount = 35; const amount = 35;
export default function({level, id}: { level: 1 | 2, id: string}) { export default function({level, id, link}: { level: 1 | 2, id: string, link: string }) {
function* questions () { function* questions () {
const wordPool = {...types}; const wordPool = {...types};
const exceptionsPool = {...exceptions}; const exceptionsPool = {...exceptions};
@ -101,7 +101,7 @@ export default function({level, id}: { level: 1 | 2, id: string}) {
} }
return <GameCore return <GameCore
studyLink={level === 1 ? "/nouns/nouns-gender#gender-by-ending" : "/nouns/nouns-gender#exceptions"} studyLink={link}
questions={questions} questions={questions}
id={id} id={id}
Display={Display} Display={Display}

View File

@ -34,7 +34,7 @@ const amount = 20;
type Question = { entry: T.DictionaryEntry, gender: T.Gender }; type Question = { entry: T.DictionaryEntry, gender: T.Gender };
export default function({ id }: { id: string }) { export default function({ id, link }: { id: string, link: string }) {
function* questions (): Generator<Current<Question>> { function* questions (): Generator<Current<Question>> {
let pool = [...nouns]; let pool = [...nouns];
for (let i = 0; i < amount; i++) { for (let i = 0; i < amount; i++) {
@ -127,7 +127,7 @@ export default function({ id }: { id: string }) {
} }
return <GameCore return <GameCore
studyLink="/nouns/nouns-unisex#" studyLink={link}
questions={questions} questions={questions}
id={id} id={id}
Display={Display} Display={Display}

1
src/types.d.ts vendored
View File

@ -18,5 +18,6 @@ type QuestionDisplayProps<T> = {
type GameRecord = { type GameRecord = {
title: string, title: string,
id: string, id: string,
studyLink: string,
Game: () => JSX.Element, Game: () => JSX.Element,
}; };

View File

@ -1223,6 +1223,13 @@
dependencies: dependencies:
regenerator-runtime "^0.13.4" regenerator-runtime "^0.13.4"
"@babel/runtime@^7.2.0":
version "7.15.4"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.15.4.tgz#fd17d16bfdf878e6dd02d19753a39fa8a8d9c84a"
integrity sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw==
dependencies:
regenerator-runtime "^0.13.4"
"@babel/template@^7.10.4": "@babel/template@^7.10.4":
version "7.10.4" version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278"
@ -7225,6 +7232,13 @@ json-stringify-safe@~5.0.1:
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
json2mq@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/json2mq/-/json2mq-0.2.0.tgz#b637bd3ba9eabe122c83e9720483aeb10d2c904a"
integrity sha1-tje9O6nqvhIsg+lyBIOusQ0skEo=
dependencies:
string-convert "^0.2.0"
json3@^3.3.2: json3@^3.3.2:
version "3.3.3" version "3.3.3"
resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.3.tgz#7fc10e375fc5ae42c4705a5cc0aa6f62be305b81" resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.3.tgz#7fc10e375fc5ae42c4705a5cc0aa6f62be305b81"
@ -10031,6 +10045,16 @@ react-lifecycles-compat@^3.0.4:
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
react-media@^1.10.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/react-media/-/react-media-1.10.0.tgz#7b0c5fe8ac55a53ce31b5249db3aaf8a22ff7703"
integrity sha512-FjgYmFoaPTImST06jqotuu0Mk8LOXiGYS/fIyiXuLnf20l3DPniBwtrxi604/HxxjqvmHS3oz5rAwnqdvosV4A==
dependencies:
"@babel/runtime" "^7.2.0"
invariant "^2.2.2"
json2mq "^0.2.0"
prop-types "^15.5.10"
react-overlays@^5.0.0: react-overlays@^5.0.0:
version "5.0.0" version "5.0.0"
resolved "https://registry.yarnpkg.com/react-overlays/-/react-overlays-5.0.0.tgz#b50351de194dda0706b40f9632d261c9f0011c4c" resolved "https://registry.yarnpkg.com/react-overlays/-/react-overlays-5.0.0.tgz#b50351de194dda0706b40f9632d261c9f0011c4c"
@ -11387,6 +11411,11 @@ strict-uri-encode@^1.0.0:
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM= integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=
string-convert@^0.2.0:
version "0.2.1"
resolved "https://registry.yarnpkg.com/string-convert/-/string-convert-0.2.1.tgz#6982cc3049fbb4cd85f8b24568b9d9bf39eeff97"
integrity sha1-aYLMMEn7tM2F+LJFaLnZvznu/5c=
string-length@^2.0.0: string-length@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/string-length/-/string-length-2.0.0.tgz#d40dbb686a3ace960c1cffca562bf2c45f8363ed" resolved "https://registry.yarnpkg.com/string-length/-/string-length-2.0.0.tgz#d40dbb686a3ace960c1cffca562bf2c45f8363ed"