140 lines
4.6 KiB
TypeScript
140 lines
4.6 KiB
TypeScript
import { reverse } from 'dns';
|
|
import React, { Fragment, useEffect, useMemo, useState } from 'react';
|
|
import './App.css';
|
|
|
|
import {prompts, Categories} from './prompts';
|
|
|
|
const alphabet = "ABCDEFGHIKLMNOPQRSTUVWXYZ";
|
|
const lskey_prompts = "cs_bingo_prompts-day3";
|
|
const lskey_ticked = "cs_bingo_ticked-day3";
|
|
|
|
const shuffle = <T,>(array: T[]): T[] => {
|
|
return array
|
|
.map((item) => ({ key: Math.random(), value: item }))
|
|
.sort((a, b) => b.key - a.key)
|
|
.map((item) => item.value);
|
|
}
|
|
|
|
const App: React.FC<{}> = () => {
|
|
const [resetCounter, setResetCounter] = useState(0);
|
|
//const [lines, setLines] = useState<[Categories,string][]>([]);
|
|
const [linesP, setLinesP] = useState<number[]>([]);
|
|
const saveLines = <T,>(theLines: T[]) => {
|
|
localStorage.setItem(lskey_prompts, JSON.stringify(theLines));
|
|
};
|
|
const saveTicks = (theLines: boolean[]) => {
|
|
localStorage.setItem(lskey_ticked, JSON.stringify(theLines));
|
|
};
|
|
const [highlighted, setHighlighted] = useState<null| number>(null);
|
|
const highlight = (index: number) => {
|
|
setHighlighted(index);
|
|
setTimeout(() => setHighlighted(null), 1500);
|
|
document.getElementById(`letter-${alphabet[index]}`)?.scrollIntoView();
|
|
};
|
|
useEffect(() => {
|
|
console.log("no lines");
|
|
if (linesP.length !== 0) return;
|
|
const loadLines = localStorage.getItem(lskey_prompts);
|
|
const loadTicks = localStorage.getItem(lskey_ticked);
|
|
if (loadLines === null || loadTicks === null) {
|
|
const newPromptsP = shuffle([...(new Array(prompts.length)).fill(0).map((_, i ) => i)]);
|
|
console.log(newPromptsP);
|
|
const newTicks = new Array(25).fill(false);
|
|
setLinesP(newPromptsP);
|
|
setTicked(newTicks);
|
|
saveLines(newPromptsP);
|
|
saveTicks(newTicks);
|
|
} else {
|
|
setLinesP(JSON.parse(loadLines));
|
|
setTicked(JSON.parse(loadTicks));
|
|
}
|
|
}, [linesP])
|
|
|
|
const reverseLookup: {[x: number]: number} = useMemo(() => {
|
|
const x= Object.fromEntries(linesP.map((val, ind) => [val, ind]));
|
|
console.log("x", x);
|
|
return x;
|
|
}, [linesP]);
|
|
const reverseAlphabetLookup: {[x: string]: number} = useMemo(() => {
|
|
const x= Object.fromEntries(linesP.flatMap((_, ind) => {
|
|
const e = alphabet[reverseLookup[ind]];
|
|
if (!e) return [];
|
|
return [[e, ind]]
|
|
} ));
|
|
console.log("x", x);
|
|
return x;
|
|
}, [linesP]);
|
|
|
|
const [ticked, setTicked] = useState<boolean[]>(new Array(25).fill(false) );
|
|
const toggleField = (index: number) => {
|
|
console.log("ticking", index);
|
|
setTicked((old) => {
|
|
const newTicks = old.map((e, i) => i === index ? !e : e);
|
|
saveTicks(newTicks);
|
|
return newTicks;
|
|
});
|
|
};
|
|
const incCounter = () => {
|
|
setResetCounter((old) => {
|
|
if (old === 15) {
|
|
const newPromptsP = shuffle([...(new Array(prompts.length)).fill(0).map((_, i ) => i)]);
|
|
const newTicked = new Array(25).fill(false) ;
|
|
setLinesP(newPromptsP);
|
|
setTicked(newTicked);
|
|
saveLines(newPromptsP);
|
|
saveTicks(newTicked)
|
|
}
|
|
return old + 1;
|
|
});
|
|
}
|
|
|
|
return (
|
|
<div className="container">
|
|
<h1 className="title" onClick={incCounter}>CS Bingo</h1>
|
|
<div className="bingo-board">
|
|
{Array.from(alphabet).map((e, index) =>
|
|
<div key={index} >
|
|
<div className={ticked[index] ? "begone": undefined} onClick={() => {
|
|
// fuck IOS not sending onContextMenu events
|
|
if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) {
|
|
toggleField(index);
|
|
} else {
|
|
highlight(index);
|
|
}
|
|
}} onContextMenu={(e) => {e.preventDefault(); toggleField(index);}}>
|
|
<span>{e}</span>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
<hr/>
|
|
<div className="legend">
|
|
<div className="header">
|
|
<h2>Id</h2>
|
|
</div>
|
|
<div className="header">
|
|
<h2>Letter</h2>
|
|
</div>
|
|
<div className="header">
|
|
<h2>Goal</h2>
|
|
</div>
|
|
{linesP.map((permutation, index)=>
|
|
<Fragment key={index}>
|
|
<div className="entry-letter" id={`letter-${alphabet[reverseLookup[index]]}`}>
|
|
<span>{index}</span>
|
|
</div>
|
|
<div className="entry-letter" >
|
|
<span>{alphabet[reverseLookup[index]]}</span>
|
|
</div>
|
|
<div className={"entry-text" + (highlighted === reverseLookup[index] ? " highlighted" : "")} >
|
|
<span>{prompts[index][1]}</span>
|
|
</div>
|
|
</Fragment>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default App;
|