added settings

This commit is contained in:
MasterGordon 2024-09-29 20:35:48 +02:00
parent 0b251c566c
commit 99e7325edb
13 changed files with 126 additions and 16 deletions

View File

@ -291,6 +291,7 @@ export const game = {
if (finished) return; if (finished) return;
if (!isValid(serverGame, x, y)) return; if (!isValid(serverGame, x, y)) return;
if (isRevealed[x][y]) return; if (isRevealed[x][y]) return;
serverGame.isFlagged[x][y] = false;
serverGame.isQuestionMark[x][y] = true; serverGame.isQuestionMark[x][y] = true;
}, },
clearTile: (serverGame: ServerGame, x: number, y: number) => { clearTile: (serverGame: ServerGame, x: number, y: number) => {

BIN
bun.lockb

Binary file not shown.

View File

@ -21,6 +21,7 @@
"@radix-ui/react-dialog": "^1.1.1", "@radix-ui/react-dialog": "^1.1.1",
"@radix-ui/react-dropdown-menu": "^2.1.1", "@radix-ui/react-dropdown-menu": "^2.1.1",
"@radix-ui/react-popover": "^1.1.1", "@radix-ui/react-popover": "^1.1.1",
"@radix-ui/react-switch": "^1.1.0",
"@tailwindcss/vite": "^4.0.0-alpha.24", "@tailwindcss/vite": "^4.0.0-alpha.24",
"@tanstack/react-query": "^5.56.2", "@tanstack/react-query": "^5.56.2",
"@tanstack/react-query-devtools": "^5.0.0-alpha.91", "@tanstack/react-query-devtools": "^5.0.0-alpha.91",

View File

@ -6,6 +6,7 @@ import Hr from "./components/Hr";
import NavLink from "./components/NavLink"; import NavLink from "./components/NavLink";
import { useMediaQuery } from "@uidotdev/usehooks"; import { useMediaQuery } from "@uidotdev/usehooks";
import Header from "./components/Header"; import Header from "./components/Header";
import { Tag } from "./components/Tag";
const drawerWidth = 256; const drawerWidth = 256;
const drawerWidthWithPadding = drawerWidth; const drawerWidthWithPadding = drawerWidth;
@ -67,7 +68,7 @@ const Shell: React.FC<PropsWithChildren> = ({ children }) => {
</NavLink> </NavLink>
<NavLink href="/settings"> <NavLink href="/settings">
<Settings /> <Settings />
Settings Settings <Tag size="sm">NEW</Tag>
</NavLink> </NavLink>
<Hr /> <Hr />
<div className="grow" /> <div className="grow" />

View File

@ -13,6 +13,7 @@ import { useWSMutation } from "../../hooks";
import { useAtom } from "jotai"; import { useAtom } from "jotai";
import { loginTokenAtom } from "../../atoms"; import { loginTokenAtom } from "../../atoms";
import PasswordInput from "./PasswordInput"; import PasswordInput from "./PasswordInput";
import { wsClient } from "../../wsClient";
const LoginButton = () => { const LoginButton = () => {
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
@ -57,8 +58,11 @@ const LoginButton = () => {
onClick={() => { onClick={() => {
login login
.mutateAsync({ username, password }) .mutateAsync({ username, password })
.then((res) => { .then(async (res) => {
setToken(res.token); setToken(res.token);
await wsClient.dispatch("user.loginWithToken", {
token: JSON.parse(res.token),
});
queryClient.invalidateQueries(); queryClient.invalidateQueries();
}) })
.catch((e) => { .catch((e) => {

View File

@ -13,6 +13,7 @@ import { useAtom } from "jotai";
import { loginTokenAtom } from "../../atoms"; import { loginTokenAtom } from "../../atoms";
import { useQueryClient } from "@tanstack/react-query"; import { useQueryClient } from "@tanstack/react-query";
import PasswordInput from "./PasswordInput"; import PasswordInput from "./PasswordInput";
import { wsClient } from "../../wsClient";
const RegisterButton = () => { const RegisterButton = () => {
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
@ -57,8 +58,11 @@ const RegisterButton = () => {
onClick={() => { onClick={() => {
register register
.mutateAsync({ username, password }) .mutateAsync({ username, password })
.then((res) => { .then(async (res) => {
setToken(res.token); setToken(res.token);
await wsClient.dispatch("user.loginWithToken", {
token: JSON.parse(res.token),
});
queryClient.invalidateQueries(); queryClient.invalidateQueries();
}) })
.catch((e) => { .catch((e) => {

View File

@ -239,11 +239,12 @@ const Tile = ({
: false; : false;
const isFlagged = game.isFlagged[i][j]; const isFlagged = game.isFlagged[i][j];
const isQuestionMark = game.isQuestionMark[i][j]; const isQuestionMark = game.isQuestionMark[i][j];
const base = isRevealed ? ( const base =
<Sprite key="b" texture={theme.revealed} /> isRevealed || isMine ? (
) : ( <Sprite key="b" texture={theme.revealed} />
<Sprite key="b" texture={theme.tile} /> ) : (
); <Sprite key="b" texture={theme.tile} />
);
const extra = isLastPos ? <Sprite key="e" texture={theme.lastPos} /> : null; const extra = isLastPos ? <Sprite key="e" texture={theme.lastPos} /> : null;
const touchStart = useRef<number>(0); const touchStart = useRef<number>(0);
const isMove = useRef<boolean>(false); const isMove = useRef<boolean>(false);

28
src/components/Switch.tsx Normal file
View File

@ -0,0 +1,28 @@
"use client";
import * as React from "react";
import * as SwitchPrimitives from "@radix-ui/react-switch";
import { cn } from "../lib/utils";
const Switch = React.forwardRef<
React.ElementRef<typeof SwitchPrimitives.Root>,
React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>
>(({ className, ...props }, ref) => (
<SwitchPrimitives.Root
className={cn(
"peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
className,
)}
{...props}
ref={ref}
>
<SwitchPrimitives.Thumb
className={cn(
"pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0",
)}
/>
</SwitchPrimitives.Root>
));
Switch.displayName = SwitchPrimitives.Root.displayName;
export { Switch };

View File

@ -16,7 +16,7 @@ const tagVariants = cva("font-semibold py-2 px-4 rounded-md flex gap-2", {
}, },
size: { size: {
default: "h-10 py-2 px-4", default: "h-10 py-2 px-4",
sm: "h-9 px-3 rounded-md", sm: "h-7 py-2 px-2 rounded-md text-xs",
lg: "h-11 px-8 rounded-md", lg: "h-11 px-8 rounded-md",
}, },
}, },

View File

@ -1,7 +1,9 @@
@import "tailwindcss"; @import "tailwindcss";
@theme { @theme {
--color-primary: rgb(251, 21, 242); --color-primary: #D9AFD9;
--color-input: color-mix(in srgb, var(--color-white, #fff) 20%, transparent);
--color-background: black;
--bg-brand: -webkit-linear-gradient(225deg, rgb(251, 175, 21), rgb(251, 21, 242), --bg-brand: -webkit-linear-gradient(225deg, rgb(251, 175, 21), rgb(251, 21, 242),
rgb(21, 198, 251)) 0% 0% / 100% 300%; rgb(21, 198, 251)) 0% 0% / 100% 300%;
--bg-secondary: linear-gradient(90deg, #D9AFD9 0%, #97D9E1 100%) 0% 0% / 100% 300%; --bg-secondary: linear-gradient(90deg, #D9AFD9 0%, #97D9E1 100%) 0% 0% / 100% 300%;

View File

@ -11,6 +11,7 @@ import { Route, Switch } from "wouter";
import Endless from "./views/endless/Endless.tsx"; import Endless from "./views/endless/Endless.tsx";
import { queryClient } from "./queryClient.ts"; import { queryClient } from "./queryClient.ts";
import Home from "./views/home/Home.tsx"; import Home from "./views/home/Home.tsx";
import Settings from "./views/settings/Settings.tsx";
connectWS(); connectWS();
@ -43,12 +44,7 @@ setup().then(() => {
<h2 className="text-white/80 text-2xl">Comming Soon</h2> <h2 className="text-white/80 text-2xl">Comming Soon</h2>
)} )}
/> />
<Route <Route path="/settings" component={Settings} />
path="/settings"
component={() => (
<h2 className="text-white/80 text-2xl">Comming Soon</h2>
)}
/>
</Switch> </Switch>
{/* <App /> */} {/* <App /> */}
</Shell> </Shell>

View File

@ -22,6 +22,7 @@ const Endless = () => {
setGameId(undefined); setGameId(undefined);
}; };
}, [setGameId]); }, [setGameId]);
console.log("set", setGameId);
return game ? ( return game ? (
<> <>

View File

@ -0,0 +1,71 @@
import { ReactNode } from "react";
import { Switch } from "../../components/Switch";
import { useWSMutation, useWSQuery } from "../../hooks";
interface BoolSettingProps {
label: string;
description: ReactNode;
value: boolean;
onChange: (value: boolean) => void;
}
const BoolSetting: React.FC<BoolSettingProps> = ({
label,
description,
value,
onChange,
}) => (
<div className="flex border-white/20 border-1 text-white/80 p-4 rounded-md justify-between items-end">
<div className="flex gap-4 flex-col">
<label className="text-white/90 text-lg">{label}</label>
<p className="text-white/70 text-sm">{description}</p>
</div>
<Switch checked={value} onCheckedChange={onChange} />
</div>
);
const Settings = () => {
const { data: settings, refetch } = useWSQuery("user.getSettings", null);
const updateSettings = useWSMutation("user.updateSettings");
return (
<div className="w-full">
<div className="max-w-[650px] mx-auto flex flex-col gap-8">
<h2 className="text-white/90 text-xl">Settings</h2>
<div className="flex flex-col gap-4 ">
<BoolSetting
label="Place Question Mark"
description={
<>
You can place a question mark on a tile after placing a flag.
<br />
Just right click again on the tile.
</>
}
value={settings?.placeQuestionMark ?? false}
onChange={async (value) => {
await updateSettings.mutateAsync({ placeQuestionMark: value });
refetch();
}}
/>
<BoolSetting
label="Long Press On Desktop"
description={
<>
You can long press on a tile to reveal it. This is useful for
touch devices.
</>
}
value={settings?.longPressOnDesktop ?? false}
onChange={async (value) => {
updateSettings.mutateAsync({ longPressOnDesktop: value });
refetch();
}}
/>
</div>
</div>
</div>
);
};
export default Settings;