added settings
This commit is contained in:
parent
0b251c566c
commit
99e7325edb
|
|
@ -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) => {
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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" />
|
||||||
|
|
|
||||||
|
|
@ -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) => {
|
||||||
|
|
|
||||||
|
|
@ -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) => {
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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 };
|
||||||
|
|
@ -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",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -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%;
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ const Endless = () => {
|
||||||
setGameId(undefined);
|
setGameId(undefined);
|
||||||
};
|
};
|
||||||
}, [setGameId]);
|
}, [setGameId]);
|
||||||
|
console.log("set", setGameId);
|
||||||
|
|
||||||
return game ? (
|
return game ? (
|
||||||
<>
|
<>
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
Loading…
Reference in New Issue