Skip to content

Commit

Permalink
Add shortcuts to change settings (#128)
Browse files Browse the repository at this point in the history
And document them on the Help page.
  • Loading branch information
eltoder authored Feb 16, 2025
1 parent 7754a7a commit c726628
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 50 deletions.
36 changes: 15 additions & 21 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,16 @@ import "./styles.css";
import { darkTheme, lightTheme, withCardColors } from "./themes";
import { generateColor, generateName, standardLayouts } from "./util";

function makeThemes(customColors) {
function makeThemes(rawCustomCardColors) {
let parsed;
try {
parsed = JSON.parse(customColors);
parsed = JSON.parse(rawCustomCardColors) || {};
} catch (error) {
console.log("failed to parse custom colors", error);
parsed = {};
}
return {
parsedCustomColors: parsed,
customCardColors: parsed,
customLightTheme: withCardColors(lightTheme, parsed.light),
customDarkTheme: withCardColors(darkTheme, parsed.dark),
};
Expand All @@ -59,10 +59,13 @@ function App() {
const [authUser, setAuthUser] = useState(null);
const [user, setUser] = useState(null);
const [themeType, setThemeType] = useStorage("theme", "light");
const [customColors, setCustomColors] = useStorage("customColors", "{}");
const { parsedCustomColors, customLightTheme, customDarkTheme } = useMemo(
() => makeThemes(customColors),
[customColors]
const [rawCustomCardColors, setCustomCardColors] = useStorage(
"customColors",
"{}"
);
const { customCardColors, customLightTheme, customDarkTheme } = useMemo(
() => makeThemes(rawCustomCardColors),
[rawCustomCardColors]
);
const [keyboardLayoutName, setKeyboardLayoutName] = useStorage(
"keyboardLayout",
Expand Down Expand Up @@ -131,14 +134,6 @@ function App() {
};
}, [authUser]);

const handleChangeTheme = () => {
setThemeType(themeType === "light" ? "dark" : "light");
};

const handleCustomColors = (custom) => {
setCustomColors(JSON.stringify(custom));
};

return (
<ThemeProvider
theme={themeType === "light" ? customLightTheme : customDarkTheme}
Expand All @@ -158,6 +153,10 @@ function App() {
setKeyboardLayoutName,
customKeyboardLayout,
setCustomKeyboardLayout,
themeType,
setThemeType,
customCardColors,
setCustomCardColors,
volume,
setVolume,
layoutOrientation,
Expand All @@ -168,12 +167,7 @@ function App() {
>
<ConnectionsTracker />
<WelcomeDialog />
<Navbar
themeType={themeType}
handleChangeTheme={handleChangeTheme}
customColors={parsedCustomColors}
handleCustomColors={handleCustomColors}
/>
<Navbar />
<Switch>
<Route exact path="/help" component={HelpPage} />
<Route exact path="/about" component={AboutPage} />
Expand Down
43 changes: 36 additions & 7 deletions src/components/Navbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import SettingsIcon from "@material-ui/icons/Settings";
import { version } from "../config";
import { SettingsContext, UserContext } from "../context";
import firebase from "../firebase";
import useKeydown, { getModifierState } from "../hooks/useKeydown";
import AccountOptionsDialog from "./AccountOptionsDialog";
import BoardLayoutDialog from "./BoardLayoutDialog";
import ColorChoiceDialog from "./ColorChoiceDialog";
Expand All @@ -23,12 +24,7 @@ import PromptDialog from "./PromptDialog";
import User from "./User";
import UserColorDialog from "./UserColorDialog";

function Navbar({
themeType,
handleChangeTheme,
customColors,
handleCustomColors,
}) {
function Navbar() {
const user = useContext(UserContext);
const settings = useContext(SettingsContext);
const [anchorEl, setAnchorEl] = useState(null);
Expand Down Expand Up @@ -58,14 +54,47 @@ function Navbar({
function handleChangeCardColors(colorMap) {
setChangeCardColors(false);
if (colorMap) {
handleCustomColors({ ...customColors, [themeType]: colorMap });
settings.setCustomCardColors(
JSON.stringify({
...settings.customCardColors,
[settings.themeType]: colorMap,
})
);
}
}

function handleChangeVolume() {
settings.setVolume((volume) => (volume === "on" ? "off" : "on"));
}

function handleChangeTheme() {
settings.setThemeType((themeType) =>
themeType === "light" ? "dark" : "light"
);
}

useKeydown((event) => {
if (getModifierState(event) === "Control") {
if (event.key === "s") {
event.preventDefault();
handleChangeVolume();
} else if (event.key === "e") {
event.preventDefault();
handleChangeTheme();
} else if (event.key === "o") {
event.preventDefault();
settings.setLayoutOrientation((layoutOrientation) =>
layoutOrientation === "portrait" ? "landscape" : "portrait"
);
} else if (event.key === "i") {
event.preventDefault();
settings.setCardOrientation((cardOrientation) =>
cardOrientation === "vertical" ? "horizontal" : "vertical"
);
}
}
});

return (
<AppBar position="relative" color="transparent" elevation={0}>
<Toolbar variant="dense">
Expand Down
30 changes: 18 additions & 12 deletions src/pages/GamePage.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,21 +120,18 @@ function GamePage({ match }) {
}, [finished.gameId, gameId]);

useKeydown((event) => {
const mod = getModifierState(event);
if (event.key === "Enter" && mod === "Control") {
event.preventDefault();
if (game?.status === "done" && !(spectating || waiting)) {
if (getModifierState(event) === "Control") {
if (event.key === "Enter") {
event.preventDefault();
handlePlayAgain();
} else if (event.key === "q") {
event.preventDefault();
setRedirect("/");
} else if (event.key === "p") {
event.preventDefault();
togglePause();
}
}
if (event.key === "q" && mod === "Control") {
event.preventDefault();
setRedirect("/");
}
if (event.key === "p" && mod === "Control") {
event.preventDefault();
togglePause();
}
});

const gameMode = game?.mode || "normal";
Expand Down Expand Up @@ -274,13 +271,19 @@ function GamePage({ match }) {
}

function handleAddHint() {
if (!game.enableHint || gameEnded || paused) {
return;
}
firebase
.database()
.ref(`gameData/${gameId}/hints`)
.set(numHints + 1);
}

function togglePause() {
if (!game.enableHint || gameEnded) {
return;
}
firebase
.database()
.ref(`gameData/${gameId}/pause`)
Expand All @@ -297,6 +300,9 @@ function GamePage({ match }) {
}

async function handlePlayAgain() {
if (game?.status !== "done" || spectating || waiting) {
return;
}
const idx = gameId.lastIndexOf("-");
let id = gameId,
num = 0;
Expand Down
73 changes: 72 additions & 1 deletion src/pages/HelpPage.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useContext } from "react";

import Container from "@material-ui/core/Container";
import Grid from "@material-ui/core/Grid";
import Link from "@material-ui/core/Link";
import Paper from "@material-ui/core/Paper";
import Typography from "@material-ui/core/Typography";
Expand All @@ -13,6 +14,21 @@ import { BASE_RATING, SCALING_FACTOR } from "../game";
function HelpPage() {
const { keyboardLayout, keyboardLayoutName } = useContext(SettingsContext);

const shortcutsTable = (shortcuts) => (
<Grid container style={{ maxWidth: 500, margin: "8px 0 16px 0" }}>
{shortcuts.flatMap(([command, key]) => [
<Grid item key={command} xs={8}>
<Typography variant="body1">{command}</Typography>
</Grid>,
<Grid item key={`${command}-key`} xs={4}>
<Typography variant="body1">
<strong>{key}</strong>
</Typography>
</Grid>,
])}
</Grid>
);

return (
<Container>
<Typography variant="h4" align="center" style={{ marginTop: 24 }}>
Expand Down Expand Up @@ -150,7 +166,7 @@ function HelpPage() {
</Typography>
<Typography variant="body1" gutterBottom>
You can choose the board layout (between portrait and landscape) and
the card orientation (between horizontal and vertical) in the
the cards orientation (between horizontal and vertical) in the
settings. The former also changes the keyboard shortcuts, which may be
more convenient to use.
</Typography>
Expand All @@ -164,6 +180,25 @@ function HelpPage() {
reflected in the list above.
</Typography>
)}
<Typography variant="h6" gutterBottom>
Keyboard shortcuts
</Typography>
<Typography variant="body1" gutterBottom>
Additionally, the following keyboard shortcuts are available:
</Typography>
{shortcutsTable([
["New game", "Ctrl+Enter"],
["New private game", "Shift+Enter"],
["Start game", "Ctrl+Enter"],
["Play again", "Ctrl+Enter"],
["Mute or unmute sound", "Ctrl+S"],
["Change theme", "Ctrl+E"],
["Change board orientation", "Ctrl+O"],
["Change cards orientation", "Ctrl+I"],
["Pause or resume game", "Ctrl+P"],
["Quit game", "Ctrl+Q"],
["Unselect cards", "Space or Escape"],
])}

<hr />
<Typography variant="h5" gutterBottom>
Expand Down Expand Up @@ -260,6 +295,33 @@ function HelpPage() {
Note that you do not have to select the four cards in any particular
order while playing.
</Typography>
<Typography variant="h6" gutterBottom>
UltraSet-Chain
</Typography>
<Typography variant="body1" gutterBottom>
Set-Chain meets UltraSet. After you pick the first UltraSet, every new
UltraSet should have{" "}
<strong>exactly two cards from the previous UltraSet</strong>. For
example, the first three UltraSets of a game might look as follows:
</Typography>
<Typography component="div" align="center" gutterBottom>
<SetCard value="1202" />
<SetCard value="1122" />
<SetCard value="0212" />
<SetCard value="2112" />
</Typography>
<Typography component="div" align="center" gutterBottom>
<SetCard value="1122" />
<SetCard value="0212" />
<SetCard value="0112" />
<SetCard value="1222" />
</Typography>
<Typography component="div" align="center" gutterBottom>
<SetCard value="1122" />
<SetCard value="0112" />
<SetCard value="2220" />
<SetCard value="2011" />
</Typography>
<Typography variant="h6" gutterBottom>
Ultra9
</Typography>
Expand Down Expand Up @@ -332,6 +394,15 @@ function HelpPage() {
UltraSet (that is, 3 pairs of cards that form sets with the same
additional card).
</Typography>
<Typography variant="h6" gutterBottom>
Puzzle
</Typography>
<Typography variant="body1" gutterBottom>
This mode is inspired by The Daily SET Puzzle website. The rules are
the same as in the standard Set, except that you have to find{" "}
<strong>all sets</strong> on each board before you move to the next
board.
</Typography>
<Typography variant="h6" gutterBottom>
Memory
</Typography>
Expand Down
11 changes: 5 additions & 6 deletions src/pages/LobbyPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -154,14 +154,10 @@ function LobbyPage() {
const mod = getModifierState(event);
if (mod === "Control") {
event.preventDefault();
if (!waiting) {
newRoom("public");
}
newRoom("public");
} else if (mod === "Shift") {
event.preventDefault();
if (!waiting) {
newRoom("private");
}
newRoom("private");
}
}
});
Expand All @@ -170,6 +166,9 @@ function LobbyPage() {

async function newRoom(access) {
// Make several attempts to create a game with an unused ID
if (waiting) {
return;
}
setWaiting(true);
let attempts = 0;
while (attempts < 5) {
Expand Down
7 changes: 4 additions & 3 deletions src/pages/RoomPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,7 @@ function RoomPage({ match, location }) {
const mod = getModifierState(event);
if (event.key === "Enter" && mod === "Control") {
event.preventDefault();
if (!leaving && user.id === game?.host) {
startGame();
}
startGame();
}
});

Expand All @@ -111,6 +109,9 @@ function RoomPage({ match, location }) {
}

function startGame() {
if (leaving || user.id !== game?.host) {
return;
}
firebase.database().ref(`games/${gameId}`).update({
status: "ingame",
startedAt: firebase.database.ServerValue.TIMESTAMP,
Expand Down

0 comments on commit c726628

Please sign in to comment.