Skip to content

Commit

Permalink
Merge pull request #6 from tjkuson/ref/hooks
Browse files Browse the repository at this point in the history
Miscellaneous code quality improvments
  • Loading branch information
tjkuson authored Nov 10, 2024
2 parents 3b01f1f + 70100d9 commit 5635d4a
Show file tree
Hide file tree
Showing 7 changed files with 252 additions and 194 deletions.
5 changes: 3 additions & 2 deletions biome.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
{
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"vcs": {
"enabled": false,
"enabled": true,
"clientKind": "git",
"useIgnoreFile": false
"useIgnoreFile": true,
"defaultBranch": "main"
},
"files": {
"ignoreUnknown": false,
Expand Down
172 changes: 23 additions & 149 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,155 +1,31 @@
import { useState } from "react";
import "./App.css";
import BracketDescription from "./components/BracketDescription";
import Footer from "./components/Footer";
import ScaleSelection from "./components/ScaleSelection";
import ScoreSelection from "./components/ScoreSelection";
import { scales } from "./data/scales";
import type { Bracket, JudgeBracket, ScaleType, SpeakerBracket } from "./types";
import { useBinarySearch } from "./hooks/useBinarySearch";
import { isSpeakerBracket } from "./utils";

function App() {
const [selectedScale, setSelectedScale] = useState<ScaleType>("speaker");
const [low, setLow] = useState(0);
const [high, setHigh] = useState(scales[selectedScale].length - 1);
const [mid, setMid] = useState(
Math.floor((0 + scales[selectedScale].length - 1) / 2),
);
const [phase, setPhase] = useState<"search" | "select" | "done" | "between">(
"search",
);
const [selectedBracketIndex, setSelectedBracketIndex] = useState<
number | null
>(null);
const [exactScore, setExactScore] = useState<number | null>(null);
const [showScale, setShowScale] = useState(false);
const [betweenBracketIndices, setBetweenBracketIndices] = useState<
[number, number] | null
>(null);

const currentScale = scales[selectedScale] as Bracket[];

const handleScaleChange = (newScale: ScaleType) => {
setSelectedScale(newScale);
setLow(0);
setHigh(scales[newScale].length - 1);
setMid(Math.floor((0 + scales[newScale].length - 1) / 2));
setPhase("search");
setSelectedBracketIndex(null);
setExactScore(null);
setBetweenBracketIndices(null);
setShowScale(false);
};

const handleBetter = () => {
const newHigh = mid - 1;
if (newHigh < low) {
// No more higher brackets.
if (mid === 0) {
// At the top bracket.
setSelectedBracketIndex(mid);
const bracket = currentScale[mid];
if (!isSpeakerBracket(bracket)) {
setExactScore((bracket as JudgeBracket).score);
setPhase("done");
} else {
setPhase("select");
}
} else {
// Between mid-1 and mid.
setBetweenBracketIndices([mid - 1, mid]);
setPhase("between");
}
} else {
setHigh(newHigh);
const newMid = Math.floor((low + newHigh) / 2);
setMid(newMid);
}
};

const handleWorse = () => {
const newLow = mid + 1;
if (newLow > high) {
// No more lower brackets.
if (mid === currentScale.length - 1) {
// At the lowest bracket.
setSelectedBracketIndex(mid);
const bracket = currentScale[mid];
if (!isSpeakerBracket(bracket)) {
setExactScore((bracket as JudgeBracket).score);
setPhase("done");
} else {
setPhase("select");
}
} else {
// Between mid and mid+1.
setBetweenBracketIndices([mid, mid + 1]);
setPhase("between");
}
} else {
setLow(newLow);
const newMid = Math.floor((newLow + high) / 2);
setMid(newMid);
}
};

const handleMatched = () => {
setSelectedBracketIndex(mid);
const bracket = currentScale[mid];
if (!isSpeakerBracket(bracket)) {
setExactScore((bracket as JudgeBracket).score);
setPhase("done");
} else {
setPhase("select");
}
};

const handleSelectScore = (position: string) => {
if (selectedBracketIndex !== null) {
const bracket = currentScale[selectedBracketIndex];
if (isSpeakerBracket(bracket)) {
let score: number;

if (position === "lower") {
score = bracket.minScore;
} else if (position === "higher") {
score = bracket.maxScore;
} else {
score = Math.floor((bracket.minScore + bracket.maxScore) / 2);
}

setExactScore(score);
setPhase("done");
}
}
};

const handleSelectExactScore = (score: number) => {
setExactScore(score);
setPhase("done");
};

const selectBracket = (index: number) => {
setSelectedBracketIndex(index);
const bracket = currentScale[index];
if (!isSpeakerBracket(bracket)) {
setExactScore((bracket as JudgeBracket).score);
setPhase("done");
} else {
setPhase("select");
}
};

const reset = () => {
setLow(0);
setHigh(currentScale.length - 1);
setMid(Math.floor((0 + currentScale.length - 1) / 2));
setPhase("search");
setSelectedBracketIndex(null);
setExactScore(null);
setBetweenBracketIndices(null);
setShowScale(false);
};
const {
selectedScale,
handleScaleChange,
mid,
phase,
selectedBracketIndex,
exactScore,
betweenBracketIndices,
showScale,
currentScale,
handleBetter,
handleWorse,
handleMatched,
handleSelectScore,
handleSelectExactScore,
selectBracket,
reset,
toggleShowScale,
} = useBinarySearch();

return (
<div className="App">
Expand Down Expand Up @@ -228,13 +104,11 @@ function App() {
{phase === "select" && selectedBracketIndex !== null && (
<div>
<h2>Selected Bracket:</h2>
<BracketDescription
description={currentScale[selectedBracketIndex].description}
/>
<BracketDescription description={currentScale[mid].description} />
{isSpeakerBracket(currentScale[selectedBracketIndex]) && (
<ScoreSelection
selectedBracketIndex={selectedBracketIndex}
currentScale={currentScale as SpeakerBracket[]}
scaleLength={currentScale.length}
handleSelectScore={handleSelectScore}
handleSelectExactScore={handleSelectExactScore}
/>
Expand Down Expand Up @@ -262,7 +136,7 @@ function App() {

<div>
<div className="button-container">
<button type="button" onClick={() => setShowScale(!showScale)}>
<button type="button" onClick={toggleShowScale}>
{showScale ? "Hide Scale" : "Just show me the scale"}
</button>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/components/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type React from "react";

const Footer: React.FC = () => {
return (
<footer className="footer" id="binary-search">
<footer className="footer">
<p>
Credit to the Panama WUDC 2025 team (and WUDC and EUDC teams previous)
for the scales. View the{" "}
Expand Down
48 changes: 9 additions & 39 deletions src/components/ScoreSelection.tsx
Original file line number Diff line number Diff line change
@@ -1,55 +1,25 @@
import type React from "react";
import type { PositionOption, ScoreOption } from "../data/bracketOptions";
import {
bottomBracketOptions,
defaultBracketOptions,
tenthBracketOptions,
topBracketOptions,
} from "../data/bracketOptions";
import type { SpeakerBracket } from "../types";

interface DisplayOption {
label: string;
onClick: () => void;
key: string | number;
}
import { useScoreOptions } from "../hooks/useScoreOptions";

interface ScoreSelectionProps {
selectedBracketIndex: number;
currentScale: SpeakerBracket[];
scaleLength: number;
handleSelectScore: (position: string) => void;
handleSelectExactScore: (score: number) => void;
}

const ScoreSelection: React.FC<ScoreSelectionProps> = ({
selectedBracketIndex,
currentScale,
scaleLength,
handleSelectScore,
handleSelectExactScore,
}) => {
const bracketOptionsMap: { [index: number]: ScoreOption[] } = {
0: topBracketOptions,
5: tenthBracketOptions,
[currentScale.length - 1]: bottomBracketOptions,
};

const getOptions = (): DisplayOption[] => {
const specialOptions = bracketOptionsMap[selectedBracketIndex];
if (specialOptions) {
return specialOptions.map((option: ScoreOption) => ({
label: option.label,
onClick: () => handleSelectExactScore(option.score),
key: option.score,
}));
}
return defaultBracketOptions.map((option: PositionOption) => ({
label: option.label,
onClick: () => handleSelectScore(option.position),
key: option.position,
}));
};

const options = getOptions();
const options = useScoreOptions({
selectedBracketIndex,
scaleLength,
handleSelectScore,
handleSelectExactScore,
});

return (
<div>
Expand Down
Loading

0 comments on commit 5635d4a

Please sign in to comment.