Fixed all eslint errors
This commit is contained in:
parent
fc7bf96b04
commit
9d8bb8e20b
39 changed files with 709 additions and 758 deletions
2
leaky-ships/global.d.ts
vendored
2
leaky-ships/global.d.ts
vendored
|
@ -2,5 +2,5 @@ import { PrismaClient } from "@prisma/client"
|
|||
import "@total-typescript/ts-reset"
|
||||
|
||||
declare global {
|
||||
var prismaClient: PrismaClient
|
||||
let prismaClient: PrismaClient
|
||||
}
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
"classnames": "^2.3.2",
|
||||
"colors": "^1.4.0",
|
||||
"http-status": "^1.6.2",
|
||||
"immer": "^10.0.2",
|
||||
"nodemailer": "6.9.4",
|
||||
"prisma": "^4.16.2",
|
||||
"socket.io": "^4.7.2",
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import { defineConfig, devices } from "@playwright/test"
|
||||
import dotenv from "dotenv"
|
||||
|
||||
/**
|
||||
* Read environment variables from file.
|
||||
* https://github.com/motdotla/dotenv
|
||||
*/
|
||||
require("dotenv").config()
|
||||
dotenv.config()
|
||||
|
||||
/**
|
||||
* See https://playwright.dev/docs/test-configuration.
|
||||
|
|
12
leaky-ships/pnpm-lock.yaml
generated
12
leaky-ships/pnpm-lock.yaml
generated
|
@ -56,9 +56,6 @@ dependencies:
|
|||
http-status:
|
||||
specifier: ^1.6.2
|
||||
version: 1.6.2
|
||||
immer:
|
||||
specifier: ^10.0.2
|
||||
version: 10.0.2
|
||||
nodemailer:
|
||||
specifier: 6.9.4
|
||||
version: 6.9.4
|
||||
|
@ -3521,10 +3518,6 @@ packages:
|
|||
engines: {node: '>= 4'}
|
||||
dev: true
|
||||
|
||||
/immer@10.0.2:
|
||||
resolution: {integrity: sha512-Rx3CqeqQ19sxUtYV9CU911Vhy8/721wRFnJv3REVGWUmoAcIwzifTsdmJte/MV+0/XpM35LZdQMBGkRIoLPwQA==}
|
||||
dev: false
|
||||
|
||||
/immutable@4.3.2:
|
||||
resolution: {integrity: sha512-oGXzbEDem9OOpDWZu88jGiYCvIsLHMvGw+8OXlpsvTFvIQplQbjg1B1cvKg8f7Hoch6+NGjpPsH1Fr+Mc2D1aA==}
|
||||
|
||||
|
@ -4789,7 +4782,7 @@ packages:
|
|||
zustand: ^4.3.0
|
||||
dependencies:
|
||||
solid-js: 1.7.11
|
||||
zustand: 4.4.1(immer@10.0.2)(react@18.2.0)
|
||||
zustand: 4.4.1(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/source-map-js@1.0.2:
|
||||
|
@ -5375,7 +5368,7 @@ packages:
|
|||
resolution: {integrity: sha512-+dTu2m6gmCbO9Ahm4ZBDapx2O6ZY9QSPXst2WXjcznPMwf2YNpn3RevLx4KkZp1OPW/ouFcoBtBzFz/LeY69oA==}
|
||||
dev: false
|
||||
|
||||
/zustand@4.4.1(immer@10.0.2)(react@18.2.0):
|
||||
/zustand@4.4.1(react@18.2.0):
|
||||
resolution: {integrity: sha512-QCPfstAS4EBiTQzlaGP1gmorkh/UL1Leaj2tdj+zZCZ/9bm0WS7sI2wnfD5lpOszFqWJ1DcPnGoY8RDL61uokw==}
|
||||
engines: {node: '>=12.7.0'}
|
||||
peerDependencies:
|
||||
|
@ -5390,7 +5383,6 @@ packages:
|
|||
react:
|
||||
optional: true
|
||||
dependencies:
|
||||
immer: 10.0.2
|
||||
react: 18.2.0
|
||||
use-sync-external-store: 1.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
|
|
@ -48,7 +48,7 @@ function Bluetooth() {
|
|||
if (!isWebBluetoothEnabled()) return
|
||||
return getDeviceInfo()
|
||||
.then(connectGatt)
|
||||
.then((_) => {
|
||||
.then(() => {
|
||||
console.log("Reading UV Index...")
|
||||
return gattCharacteristic.readValue()
|
||||
})
|
||||
|
@ -108,7 +108,7 @@ function Bluetooth() {
|
|||
if (!isWebBluetoothEnabled()) return
|
||||
gattCharacteristic
|
||||
.startNotifications()
|
||||
.then((_) => {
|
||||
.then(() => {
|
||||
console.log("Start reading...")
|
||||
setStartDisabled(true)
|
||||
setStopDisabled(false)
|
||||
|
@ -119,7 +119,7 @@ function Bluetooth() {
|
|||
if (!isWebBluetoothEnabled()) return
|
||||
gattCharacteristic
|
||||
.stopNotifications()
|
||||
.then((_) => {
|
||||
.then(() => {
|
||||
console.log("Stop reading...")
|
||||
setStartDisabled(false)
|
||||
setStopDisabled(true)
|
||||
|
@ -141,7 +141,7 @@ function Bluetooth() {
|
|||
<button
|
||||
id="start"
|
||||
class="bluetooth"
|
||||
disabled={startDisabled}
|
||||
disabled={startDisabled()}
|
||||
onClick={start}
|
||||
>
|
||||
Start
|
||||
|
@ -149,7 +149,7 @@ function Bluetooth() {
|
|||
<button
|
||||
id="stop"
|
||||
class="bluetooth"
|
||||
disabled={stopDisabled}
|
||||
disabled={stopDisabled()}
|
||||
onClick={stop}
|
||||
>
|
||||
Stop
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import classNames from "classnames"
|
||||
|
||||
function BurgerMenu({
|
||||
onClick,
|
||||
blur,
|
||||
}: {
|
||||
function BurgerMenu(props: {
|
||||
onClick?: () => void
|
||||
blur?: boolean
|
||||
}) {
|
||||
|
@ -12,9 +9,9 @@ function BurgerMenu({
|
|||
id="menu"
|
||||
class={classNames(
|
||||
"absolute left-4 top-4 flex h-16 w-16 items-center justify-center rounded-lg border-b-2 border-shield-gray bg-grayish shadow-lg duration-100 active:border-b-0 active:border-t-2 md:left-6 md:top-6 md:h-20 md:w-20 md:rounded-xl md:border-b-4 md:active:border-t-4 lg:left-8 lg:top-8 xl:left-12 xl:top-12 xl:h-24 xl:w-24",
|
||||
{ "blur-sm": blur },
|
||||
{ "blur-sm": props.blur },
|
||||
)}
|
||||
onClick={() => onClick && setTimeout(onClick, 200)}
|
||||
onClick={() => props.onClick && setTimeout(props.onClick, 200)}
|
||||
>
|
||||
<img
|
||||
class="pixelart h-12 w-12 md:h-16 md:w-16 xl:h-20 xl:w-20"
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { For } from "solid-js"
|
||||
import { useGameProps } from "~/hooks/useGameProps"
|
||||
import useIndex from "~/hooks/useIndex"
|
||||
import useShips from "~/hooks/useShips"
|
||||
|
@ -59,7 +60,7 @@ function BorderTiles() {
|
|||
}
|
||||
}
|
||||
|
||||
let tilesProperties: TilesType[] = []
|
||||
const tilesProperties: TilesType[] = []
|
||||
|
||||
for (let y = 0; y < count + 2; y++) {
|
||||
for (let x = 0; x < count + 2; x++) {
|
||||
|
@ -82,55 +83,53 @@ function BorderTiles() {
|
|||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{tilesProperties.map(({ key, className, isGameTile, x, y }) => {
|
||||
return (
|
||||
<div
|
||||
class={className}
|
||||
style={{ "--x": x, "--y": y }}
|
||||
onClick={() => {
|
||||
if (payload?.game?.state === "running") {
|
||||
settingTarget(isGameTile, x, y)
|
||||
} else if (payload?.game?.state === "starting") {
|
||||
const { index } = intersectingShip(ships(), {
|
||||
...mouseCursor,
|
||||
size: 1,
|
||||
variant: 0,
|
||||
orientation: "h",
|
||||
})
|
||||
if (typeof index === "undefined")
|
||||
settingTarget(isGameTile, x, y)
|
||||
else {
|
||||
const ship = ships()[index]
|
||||
useGameProps.setState({ mode: ship.size - 2 })
|
||||
removeShip(ship)
|
||||
setMouseCursor((e) => ({ ...e, shouldShow: true }))
|
||||
}
|
||||
}
|
||||
}}
|
||||
onMouseEnter={() =>
|
||||
setMouseCursor({
|
||||
x,
|
||||
y,
|
||||
shouldShow:
|
||||
isGameTile &&
|
||||
(payload?.game?.state === "starting"
|
||||
? intersectingShip(
|
||||
ships(),
|
||||
shipProps(ships(), mode, {
|
||||
x,
|
||||
y,
|
||||
orientation: targetPreview.orientation,
|
||||
}),
|
||||
true,
|
||||
).score < 2
|
||||
: true),
|
||||
<For each={tilesProperties}>
|
||||
{(props) => (
|
||||
<div
|
||||
class={props.className}
|
||||
style={{ "--x": props.x, "--y": props.y }}
|
||||
onClick={() => {
|
||||
if (payload?.game?.state === "running") {
|
||||
settingTarget(props.isGameTile, props.x, props.y)
|
||||
} else if (payload?.game?.state === "starting") {
|
||||
const { index } = intersectingShip(ships(), {
|
||||
...mouseCursor,
|
||||
size: 1,
|
||||
variant: 0,
|
||||
orientation: "h",
|
||||
})
|
||||
if (typeof index === "undefined")
|
||||
settingTarget(props.isGameTile, props.x, props.y)
|
||||
else {
|
||||
const ship = ships()[index]
|
||||
useGameProps.setState({ mode: ship.size - 2 })
|
||||
removeShip(ship)
|
||||
setMouseCursor((e) => ({ ...e, shouldShow: true }))
|
||||
}
|
||||
}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</>
|
||||
}}
|
||||
onMouseEnter={() =>
|
||||
setMouseCursor({
|
||||
x: props.x,
|
||||
y: props.y,
|
||||
shouldShow:
|
||||
props.isGameTile &&
|
||||
(payload?.game?.state === "starting"
|
||||
? intersectingShip(
|
||||
ships(),
|
||||
shipProps(ships(), mode, {
|
||||
x: props.x,
|
||||
y: props.y,
|
||||
orientation: targetPreview.orientation,
|
||||
}),
|
||||
true,
|
||||
).score < 2
|
||||
: true),
|
||||
})
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</For>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ import { socket } from "~/lib/socket"
|
|||
import { modes } from "~/lib/utils/helpers"
|
||||
import { GamePropsSchema } from "~/lib/zodSchemas"
|
||||
// import { Icons, toast } from "react-toastify"
|
||||
import { createEffect, createMemo } from "solid-js"
|
||||
import { For, Show, createEffect } from "solid-js"
|
||||
import { useNavigate } from "solid-start"
|
||||
import { useDrawProps } from "~/hooks/useDrawProps"
|
||||
import { useGameProps } from "~/hooks/useGameProps"
|
||||
|
@ -49,7 +49,7 @@ export function setGameSetting(
|
|||
}
|
||||
}
|
||||
|
||||
function EventBar({ clear }: { clear: () => void }) {
|
||||
function EventBar(props: { clear: () => void }) {
|
||||
const { shouldHide, color } = useDrawProps()
|
||||
const { selfIndex, isActiveIndex, selfUser } = useIndex()
|
||||
const { ships } = useShips()
|
||||
|
@ -70,7 +70,7 @@ function EventBar({ clear }: { clear: () => void }) {
|
|||
const gameSetting = (payload: GameSettings) =>
|
||||
setGameSetting(payload, setSetting, full)
|
||||
|
||||
const items = createMemo<EventBarModes>(() => ({
|
||||
const items = (): EventBarModes => ({
|
||||
main: [
|
||||
{
|
||||
icon: "burger-menu",
|
||||
|
@ -199,7 +199,7 @@ function EventBar({ clear }: { clear: () => void }) {
|
|||
},
|
||||
],
|
||||
draw: [
|
||||
{ icon: faBroomWide, text: "Clear", callback: clear },
|
||||
{ icon: faBroomWide, text: "Clear", callback: props.clear },
|
||||
{ icon: faPalette, text: "Color", iconColor: color },
|
||||
{
|
||||
icon: shouldHide ? faEye : faEyeSlash,
|
||||
|
@ -261,7 +261,7 @@ function EventBar({ clear }: { clear: () => void }) {
|
|||
},
|
||||
},
|
||||
],
|
||||
}))
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
if (
|
||||
|
@ -277,39 +277,39 @@ function EventBar({ clear }: { clear: () => void }) {
|
|||
|
||||
createEffect(() => {
|
||||
useDrawProps.setState({ enable: menu === "draw" })
|
||||
}, [menu])
|
||||
|
||||
createEffect(() => {
|
||||
if (payload?.game?.state !== "running") return
|
||||
|
||||
let toastId = "otherPlayer"
|
||||
// if (isActiveIndex) toast.dismiss(toastId)
|
||||
// else
|
||||
// toast.info("Waiting for other player...", {
|
||||
// toastId,
|
||||
// position: "top-right",
|
||||
// icon: Icons.spinner(),
|
||||
// autoClose: false,
|
||||
// hideProgressBar: true,
|
||||
// closeButton: false,
|
||||
// })
|
||||
|
||||
// toastId = "connect_error"
|
||||
// const isActive = toast.isActive(toastId)
|
||||
// console.log(toastId, isActive)
|
||||
// if (isActive)
|
||||
// toast.update(toastId, {
|
||||
// autoClose: 5000,
|
||||
// })
|
||||
// else
|
||||
// toast.warn("Spie", { toastId })
|
||||
})
|
||||
|
||||
// createEffect(() => {
|
||||
// if (payload?.game?.state !== "running") return
|
||||
|
||||
// const toastId = "otherPlayer"
|
||||
// if (isActiveIndex) toast.dismiss(toastId)
|
||||
// else
|
||||
// toast.info("Waiting for other player...", {
|
||||
// toastId,
|
||||
// position: "top-right",
|
||||
// icon: Icons.spinner(),
|
||||
// autoClose: false,
|
||||
// hideProgressBar: true,
|
||||
// closeButton: false,
|
||||
// })
|
||||
|
||||
// toastId = "connect_error"
|
||||
// const isActive = toast.isActive(toastId)
|
||||
// console.log(toastId, isActive)
|
||||
// if (isActive)
|
||||
// toast.update(toastId, {
|
||||
// autoClose: 5000,
|
||||
// })
|
||||
// else
|
||||
// toast.warn("Spie", { toastId })
|
||||
// })
|
||||
|
||||
return (
|
||||
<div class="event-bar">
|
||||
{menu !== "main" ? (
|
||||
<Show when={menu !== "main"}>
|
||||
<Item
|
||||
props={{
|
||||
{...{
|
||||
icon: faReply,
|
||||
text: "Return",
|
||||
iconColor: "#555",
|
||||
|
@ -318,20 +318,23 @@ function EventBar({ clear }: { clear: () => void }) {
|
|||
},
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
{items()[menu].map((e, i) => {
|
||||
if (!isActiveIndex && menu === "main" && i === 1) return
|
||||
return <Item props={e} />
|
||||
})}
|
||||
{menu === "moves" ? (
|
||||
</Show>
|
||||
<For each={items()[menu]}>
|
||||
{(e, i) => (
|
||||
<Show when={isActiveIndex && menu !== "main" && i() !== 1}>
|
||||
<Item {...e} />
|
||||
</Show>
|
||||
)}
|
||||
</For>
|
||||
<Show when={menu === "moves"}>
|
||||
<Item
|
||||
props={{
|
||||
{...{
|
||||
icon:
|
||||
selfIndex >= 0 && userStates[selfIndex].isReady
|
||||
selfIndex >= 0 && userStates[selfIndex].isReady()
|
||||
? faLock
|
||||
: faCheck,
|
||||
text:
|
||||
selfIndex >= 0 && userStates[selfIndex].isReady
|
||||
selfIndex >= 0 && userStates[selfIndex].isReady()
|
||||
? "unready"
|
||||
: "Done",
|
||||
disabled:
|
||||
|
@ -365,7 +368,7 @@ function EventBar({ clear }: { clear: () => void }) {
|
|||
},
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
</Show>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ function Gamefield() {
|
|||
} = useGameProps()
|
||||
const { isConnected } = useSocket()
|
||||
|
||||
const { canvasRef, onMouseDown, clear } = useDraw()
|
||||
const usingDraw = useDraw()
|
||||
const { enable, color, shouldHide } = useDrawProps()
|
||||
|
||||
createEffect(() => {
|
||||
|
@ -46,12 +46,12 @@ function Gamefield() {
|
|||
return
|
||||
socket.emit("ships", selfUser?.ships ?? [])
|
||||
socket.emit("gameState", "running")
|
||||
}, [payload?.game?.state, selfUser?.ships, userStates])
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
if (payload?.game?.id || !isConnected) return
|
||||
socket.emit("update", full)
|
||||
}, [full, payload?.game?.id, isConnected])
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
if (mode < 0) return
|
||||
|
@ -85,7 +85,7 @@ function Gamefield() {
|
|||
document.removeEventListener("keydown", handleKeyPress)
|
||||
}
|
||||
}
|
||||
}, [mode, mouseCursor, payload?.game?.state, setTargetPreview, target])
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
if (payload?.game?.state !== "aborted") return
|
||||
|
@ -129,13 +129,13 @@ function Gamefield() {
|
|||
"box-shadow": enable ? "inset 0 0 0 2px " + color : "none",
|
||||
"pointer-events": enable && !shouldHide ? "auto" : "none",
|
||||
}}
|
||||
ref={canvasRef}
|
||||
onMouseDown={onMouseDown}
|
||||
ref={usingDraw.canvasRef}
|
||||
onMouseDown={usingDraw.onMouseDown}
|
||||
width="648"
|
||||
height="648"
|
||||
/>
|
||||
</div>
|
||||
<EventBar clear={clear} />
|
||||
<EventBar clear={usingDraw.clear} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -5,25 +5,29 @@ import {} from "solid-js"
|
|||
import { FontAwesomeIcon } from "~/components/FontAwesomeIcon"
|
||||
import { PointerProps } from "../../interfaces/frontend"
|
||||
|
||||
function GamefieldPointer({
|
||||
props: { x, y, show, type, edges, imply },
|
||||
preview,
|
||||
}: {
|
||||
props: PointerProps
|
||||
preview?: boolean
|
||||
}) {
|
||||
const isRadar = type === "radar"
|
||||
const style = !(isRadar && !edges.filter((s) => s).length)
|
||||
? { "--x": x, "--y": y }
|
||||
: { "--x1": x - 1, "--x2": x + 2, "--y1": y - 1, "--y2": y + 2 }
|
||||
function GamefieldPointer(
|
||||
props: PointerProps & {
|
||||
preview?: boolean
|
||||
},
|
||||
) {
|
||||
const isRadar = () => props.type === "radar"
|
||||
const style = () =>
|
||||
!(isRadar() && !props.edges.filter((s) => s).length)
|
||||
? { "--x": props.x, "--y": props.y }
|
||||
: {
|
||||
"--x1": props.x - 1,
|
||||
"--x2": props.x + 2,
|
||||
"--y1": props.y - 1,
|
||||
"--y2": props.y + 2,
|
||||
}
|
||||
return (
|
||||
<div
|
||||
class={classNames("hit-svg", "target", type, ...edges, {
|
||||
preview: preview,
|
||||
show: show,
|
||||
imply: imply,
|
||||
class={classNames("hit-svg", "target", props.type, ...props.edges, {
|
||||
preview: props.preview,
|
||||
show: props.show,
|
||||
imply: props.imply,
|
||||
})}
|
||||
style={style}
|
||||
style={style()}
|
||||
>
|
||||
<FontAwesomeIcon icon={!isRadar ? faCrosshairs : faRadar} />
|
||||
</div>
|
||||
|
|
|
@ -1,28 +1,27 @@
|
|||
import { faBurst, faXmark } from "@fortawesome/pro-solid-svg-icons"
|
||||
import { For } from "solid-js"
|
||||
import { FontAwesomeIcon } from "~/components/FontAwesomeIcon"
|
||||
import useIndex from "~/hooks/useIndex"
|
||||
import { Hit } from "../../interfaces/frontend"
|
||||
|
||||
function HitElems({
|
||||
props,
|
||||
}: {
|
||||
props?: { hits: Hit[]; colorOverride?: string }
|
||||
}) {
|
||||
function HitElems(props: { hits?: Hit[]; colorOverride?: string }) {
|
||||
const { activeUser } = useIndex()
|
||||
const hits = () => props?.hits
|
||||
const colorOverride = () => props?.colorOverride
|
||||
|
||||
return (
|
||||
<>
|
||||
{(props?.hits ?? activeUser?.hits ?? []).map(({ hit, x, y }, i) => (
|
||||
<div class="hit-svg" style={{ "--x": x, "--y": y }}>
|
||||
<For each={hits() ?? activeUser?.hits ?? []}>
|
||||
{(props) => (
|
||||
<div class="hit-svg" style={{ "--x": props.x, "--y": props.y }}>
|
||||
<FontAwesomeIcon
|
||||
icon={hit ? faBurst : faXmark}
|
||||
icon={props.hit ? faBurst : faXmark}
|
||||
style={{
|
||||
color: props?.colorOverride || (hit ? "red" : undefined),
|
||||
color: colorOverride() || (props.hit ? "red" : undefined),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</For>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,17 +1,13 @@
|
|||
import { createEffect, createSignal } from "solid-js"
|
||||
import { FontAwesomeIcon } from "~/components/FontAwesomeIcon"
|
||||
import { useDrawProps } from "~/hooks/useDrawProps"
|
||||
// import { useDrawProps } from "~/hooks/useDrawProps"
|
||||
// import { HexColorPicker } from "react-colorful"
|
||||
import classNames from "classnames"
|
||||
import { ItemProps } from "../../interfaces/frontend"
|
||||
|
||||
function Item({
|
||||
props: { icon, text, amount, iconColor, disabled, enabled, callback },
|
||||
}: {
|
||||
props: ItemProps
|
||||
}) {
|
||||
const isColor = text === "Color"
|
||||
const { color, setColor } = useDrawProps()
|
||||
function Item(props: ItemProps) {
|
||||
const isColor = () => props.text === "Color"
|
||||
// const { color, setColor } = useDrawProps()
|
||||
const [active, setActive] = createSignal(false)
|
||||
let cpRef: HTMLDivElement
|
||||
|
||||
|
@ -21,18 +17,23 @@ function Item({
|
|||
}
|
||||
|
||||
// Add event listeners
|
||||
if (!isColor) return
|
||||
if (!isColor()) return
|
||||
setTimeout(() => window.addEventListener("click", inActive), 200)
|
||||
|
||||
// Remove event listeners
|
||||
return () => {
|
||||
window.removeEventListener("click", inActive)
|
||||
}
|
||||
}, [active, isColor])
|
||||
})
|
||||
|
||||
return (
|
||||
<div class="item" onClick={isColor ? () => setActive(true) : callback}>
|
||||
{isColor ? (
|
||||
<div
|
||||
class="item"
|
||||
onClick={() =>
|
||||
isColor() ? setActive(true) : props.callback && props.callback()
|
||||
}
|
||||
>
|
||||
{isColor() ? (
|
||||
<div
|
||||
ref={cpRef!}
|
||||
class={classNames("react-colorful-wrapper", { active: active })}
|
||||
|
@ -42,29 +43,32 @@ function Item({
|
|||
) : null}
|
||||
<div
|
||||
class={classNames("container", {
|
||||
amount: typeof amount !== "undefined",
|
||||
disabled: disabled || amount === 0,
|
||||
enabled: disabled === false || enabled,
|
||||
amount: typeof props.amount !== "undefined",
|
||||
disabled: props.disabled || props.amount === 0,
|
||||
enabled: props.disabled === false || props.enabled,
|
||||
})}
|
||||
style={
|
||||
typeof amount !== "undefined"
|
||||
typeof props.amount !== "undefined"
|
||||
? {
|
||||
"--amount": JSON.stringify(amount.toString()),
|
||||
"--amount": JSON.stringify(props.amount.toString()),
|
||||
}
|
||||
: {}
|
||||
}
|
||||
>
|
||||
{typeof icon === "string" ? (
|
||||
{typeof props.icon === "string" ? (
|
||||
<img
|
||||
src={`/assets/${icon}.png`}
|
||||
alt={`${icon}.png`}
|
||||
src={`/assets/${props.icon}.png`}
|
||||
alt={`${props.icon}.png`}
|
||||
class="pixelart"
|
||||
/>
|
||||
) : (
|
||||
<FontAwesomeIcon icon={icon} color={iconColor ?? "#444"} />
|
||||
<FontAwesomeIcon
|
||||
icon={props.icon}
|
||||
color={props.iconColor ?? "#444"}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<span>{text}</span>
|
||||
<span>{props.text}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import classNames from "classnames"
|
||||
import {} from "solid-js"
|
||||
import { For } from "solid-js"
|
||||
import { fieldIndex } from "~/lib/utils/helpers"
|
||||
import { Field } from "../../interfaces/frontend"
|
||||
import { count } from "./Gamefield"
|
||||
|
@ -34,16 +34,16 @@ function Labeling() {
|
|||
(a, b) => fieldIndex(count, a.x, a.y) - fieldIndex(count, b.x, b.y),
|
||||
)
|
||||
return (
|
||||
<>
|
||||
{elems.map(({ field, x, y, orientation }, i) => (
|
||||
<For each={elems}>
|
||||
{(props) => (
|
||||
<span
|
||||
class={classNames("label", orientation, field)}
|
||||
style={{ "--x": x, "--y": y }}
|
||||
class={classNames("label", props.orientation, props.field)}
|
||||
style={{ "--x": props.x, "--y": props.y }}
|
||||
>
|
||||
{field}
|
||||
{props.field}
|
||||
</span>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</For>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -8,18 +8,14 @@ const sizes: { [n: number]: number } = {
|
|||
4: 196,
|
||||
}
|
||||
|
||||
function Ship({
|
||||
props: { size, variant, x, y, orientation },
|
||||
preview,
|
||||
warn,
|
||||
color,
|
||||
}: {
|
||||
props: ShipProps
|
||||
preview?: boolean
|
||||
warn?: boolean
|
||||
color?: string
|
||||
}) {
|
||||
const filename = `ship_blue_${size}x_${variant}.gif`
|
||||
function Ship(
|
||||
props: ShipProps & {
|
||||
preview?: boolean
|
||||
warn?: boolean
|
||||
color?: string
|
||||
},
|
||||
) {
|
||||
const filename = () => `ship_blue_${props.size}x_${props.variant}.gif`
|
||||
let canvasRef: HTMLCanvasElement
|
||||
|
||||
createEffect(() => {
|
||||
|
@ -27,30 +23,40 @@ function Ship({
|
|||
const ctx = canvas?.getContext("2d")
|
||||
if (!canvas || !ctx) return
|
||||
const gif = new Image()
|
||||
gif.src = "/assets/" + filename
|
||||
gif.src = "/assets/" + filename()
|
||||
|
||||
// Load the GIF and start rendering
|
||||
gif.onload = function () {
|
||||
// Set the canvas size to match the GIF dimensions
|
||||
canvas.width = orientation === "h" ? sizes[size] : 48
|
||||
canvas.height = orientation === "v" ? sizes[size] : 48
|
||||
canvas.width = props.orientation === "h" ? sizes[props.size] : 48
|
||||
canvas.height = props.orientation === "v" ? sizes[props.size] : 48
|
||||
|
||||
if (orientation === "v")
|
||||
if (props.orientation === "v")
|
||||
// Rotate the canvas by 90 degrees
|
||||
ctx.rotate((90 * Math.PI) / 180)
|
||||
|
||||
// Draw the rotated GIF
|
||||
ctx.drawImage(gif, 0, orientation === "h" ? 0 : -48, sizes[size], 48)
|
||||
ctx.drawImage(
|
||||
gif,
|
||||
0,
|
||||
props.orientation === "h" ? 0 : -48,
|
||||
sizes[props.size],
|
||||
48,
|
||||
)
|
||||
}
|
||||
}, [filename, orientation, size, x, y])
|
||||
})
|
||||
|
||||
return (
|
||||
<div
|
||||
class={classNames("ship", "s" + size, orientation, {
|
||||
preview: preview,
|
||||
warn: warn,
|
||||
class={classNames("ship", "s" + props.size, props.orientation, {
|
||||
preview: props.preview,
|
||||
warn: props.warn,
|
||||
})}
|
||||
style={{ "--x": x, "--y": y, "--color": color ?? "limegreen" }}
|
||||
style={{
|
||||
"--x": props.x,
|
||||
"--y": props.y,
|
||||
"--color": props.color ?? "limegreen",
|
||||
}}
|
||||
>
|
||||
<canvas ref={canvasRef!} />
|
||||
</div>
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { For, Show } from "solid-js"
|
||||
import { useGameProps } from "~/hooks/useGameProps"
|
||||
import useIndex from "~/hooks/useIndex"
|
||||
import Ship from "./Ship"
|
||||
|
@ -6,9 +7,11 @@ function Ships() {
|
|||
const { payload } = useGameProps()
|
||||
const { isActiveIndex, selfUser } = useIndex()
|
||||
|
||||
if (payload?.game?.state === "running" && isActiveIndex) return null
|
||||
|
||||
return <>{selfUser?.ships.map((props, i) => <Ship props={props} />)}</>
|
||||
return (
|
||||
<Show when={payload?.game?.state === "running" && isActiveIndex}>
|
||||
<For each={selfUser?.ships}>{(props) => <Ship {...props} />}</For>
|
||||
</Show>
|
||||
)
|
||||
}
|
||||
|
||||
export default Ships
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { For, Match, Switch } from "solid-js"
|
||||
import { useGameProps } from "~/hooks/useGameProps"
|
||||
import useIndex from "~/hooks/useIndex"
|
||||
import useShips from "~/hooks/useShips"
|
||||
|
@ -15,54 +16,40 @@ function Targets() {
|
|||
const { payload, target, targetPreview, mode } = useGameProps()
|
||||
const { ships } = useShips()
|
||||
|
||||
switch (payload?.game?.state) {
|
||||
case "running":
|
||||
return (
|
||||
<>
|
||||
{[
|
||||
...composeTargetTiles(target, mode, activeUser?.hits ?? []).map(
|
||||
(props, i) => <GamefieldPointer props={props} />,
|
||||
),
|
||||
...composeTargetTiles(
|
||||
targetPreview,
|
||||
mode,
|
||||
activeUser?.hits ?? [],
|
||||
).map((props, i) => <GamefieldPointer props={props} preview />),
|
||||
]}
|
||||
</>
|
||||
)
|
||||
const ship = shipProps(ships(), mode, targetPreview)
|
||||
const { fields, borders, score } = intersectingShip(ships(), ship)
|
||||
|
||||
case "starting":
|
||||
if (mode < 0 && !targetPreview.show) return null
|
||||
const ship = shipProps(ships(), mode, targetPreview)
|
||||
const { fields, borders, score } = intersectingShip(ships(), ship)
|
||||
return (
|
||||
<>
|
||||
<Ship
|
||||
preview
|
||||
warn={score > 0}
|
||||
color={
|
||||
fields.length ? "red" : borders.length ? "orange" : undefined
|
||||
}
|
||||
props={ship}
|
||||
/>
|
||||
<HitElems
|
||||
props={{
|
||||
hits: fields.map((e, i) => ({ ...e, i, hit: true })),
|
||||
}}
|
||||
/>
|
||||
<HitElems
|
||||
props={{
|
||||
hits: borders.map((e, i) => ({ ...e, i, hit: true })),
|
||||
colorOverride: "orange",
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
|
||||
default:
|
||||
return null
|
||||
}
|
||||
return (
|
||||
<Switch>
|
||||
<Match when={payload?.game?.state === "running"}>
|
||||
<For each={composeTargetTiles(target, mode, activeUser?.hits ?? [])}>
|
||||
{(props) => <GamefieldPointer {...props} />}
|
||||
</For>
|
||||
<For
|
||||
each={composeTargetTiles(targetPreview, mode, activeUser?.hits ?? [])}
|
||||
>
|
||||
{(props) => <GamefieldPointer {...props} preview />}
|
||||
</For>
|
||||
</Match>
|
||||
<Match
|
||||
when={
|
||||
payload?.game?.state === "starting" && mode >= 0 && targetPreview.show
|
||||
}
|
||||
>
|
||||
<Ship
|
||||
{...ship}
|
||||
preview
|
||||
warn={score > 0}
|
||||
color={fields.length ? "red" : borders.length ? "orange" : undefined}
|
||||
/>
|
||||
<HitElems hits={fields.map((e, i) => ({ ...e, i, hit: true }))} />
|
||||
<HitElems
|
||||
hits={borders.map((e, i) => ({ ...e, i, hit: true }))}
|
||||
colorOverride={"orange"}
|
||||
/>
|
||||
</Match>
|
||||
</Switch>
|
||||
)
|
||||
}
|
||||
|
||||
export default Targets
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import classNames from "classnames"
|
||||
import { createEffect, createMemo, createSignal } from "solid-js"
|
||||
import { For, createEffect, createSignal } from "solid-js"
|
||||
|
||||
function Grid() {
|
||||
function floorClient(number: number) {
|
||||
|
@ -24,7 +24,7 @@ function Grid() {
|
|||
}
|
||||
handleResize()
|
||||
window.addEventListener("resize", handleResize)
|
||||
}, [])
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
const timeout = setTimeout(() => {
|
||||
|
@ -37,80 +37,77 @@ function Grid() {
|
|||
return () => clearTimeout(timeout)
|
||||
})
|
||||
|
||||
const createTiles = createMemo(() => {
|
||||
const colors = [
|
||||
"rgb(229, 57, 53)",
|
||||
"rgb(253, 216, 53)",
|
||||
"rgb(244, 81, 30)",
|
||||
"rgb(76, 175, 80)",
|
||||
"rgb(33, 150, 243)",
|
||||
"rgb(156, 39, 176)",
|
||||
]
|
||||
function Tile(props: { index: number }) {
|
||||
const x = () => props.index % params().columns
|
||||
const y = () => Math.floor(props.index / params().columns)
|
||||
const xDiff = () => (x() - position()[0]) / 20
|
||||
const yDiff = () => (y() - position()[1]) / 20
|
||||
const pos = () =>
|
||||
Math.sqrt(xDiff() * xDiff() + yDiff() * yDiff()).toFixed(2)
|
||||
|
||||
function createTile(index: number) {
|
||||
const x = index % params().columns
|
||||
const y = Math.floor(index / params().columns)
|
||||
const xDiff = (x - position()[0]) / 20
|
||||
const yDiff = (y - position()[1]) / 20
|
||||
const pos = Math.sqrt(xDiff * xDiff + yDiff * yDiff).toFixed(2)
|
||||
function doEffect(posX: number, posY: number) {
|
||||
if (active()) return
|
||||
setPosition([posX, posY])
|
||||
setActve(true)
|
||||
|
||||
function doEffect(posX: number, posY: number) {
|
||||
if (active()) return
|
||||
setPosition([posX, posY])
|
||||
setActve(true)
|
||||
|
||||
function xDiff(x: number) {
|
||||
return (x - posX) / 20
|
||||
}
|
||||
function yDiff(y: number) {
|
||||
return (y - posY) / 20
|
||||
}
|
||||
function pos(x: number, y: number) {
|
||||
return Math.sqrt(xDiff(x) * xDiff(x) + yDiff(y) * yDiff(y))
|
||||
}
|
||||
const diagonals = [
|
||||
pos(0, 0),
|
||||
pos(params().columns, 0),
|
||||
pos(0, params().rows),
|
||||
pos(params().columns, params().rows),
|
||||
]
|
||||
|
||||
setTimeout(
|
||||
() => {
|
||||
setActve(false)
|
||||
setCount((e) => e + 1)
|
||||
},
|
||||
Math.max(...diagonals) * 1000 + 300,
|
||||
)
|
||||
function xDiff(x: number) {
|
||||
return (x - posX) / 20
|
||||
}
|
||||
function yDiff(y: number) {
|
||||
return (y - posY) / 20
|
||||
}
|
||||
function pos(x: number, y: number) {
|
||||
return Math.sqrt(xDiff(x) * xDiff(x) + yDiff(y) * yDiff(y))
|
||||
}
|
||||
const diagonals = [
|
||||
pos(0, 0),
|
||||
pos(params().columns, 0),
|
||||
pos(0, params().rows),
|
||||
pos(params().columns, params().rows),
|
||||
]
|
||||
|
||||
return (
|
||||
<div
|
||||
class={classNames({ tile: true, active: active() })}
|
||||
style={{ "--delay": pos + "s" }}
|
||||
onClick={() => doEffect(x, y)}
|
||||
/>
|
||||
setTimeout(
|
||||
() => {
|
||||
setActve(false)
|
||||
setCount((e) => e + 1)
|
||||
},
|
||||
Math.max(...diagonals) * 1000 + 300,
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
id="tiles"
|
||||
style={{
|
||||
"--columns": params().columns,
|
||||
"--rows": params().rows,
|
||||
"--bg-color-1": colors[count() % colors.length],
|
||||
"--bg-color-2": colors[(count() + 1) % colors.length],
|
||||
}}
|
||||
>
|
||||
{Array.from(Array(params().quantity), (_tile, index) =>
|
||||
createTile(index),
|
||||
)}
|
||||
</div>
|
||||
class={classNames({ tile: true, active: active() })}
|
||||
style={{ "--delay": pos() + "s" }}
|
||||
onClick={() => doEffect(x(), y())}
|
||||
/>
|
||||
)
|
||||
}, [params, position, active, count])
|
||||
}
|
||||
|
||||
return createTiles()
|
||||
const colors = [
|
||||
"rgb(229, 57, 53)",
|
||||
"rgb(253, 216, 53)",
|
||||
"rgb(244, 81, 30)",
|
||||
"rgb(76, 175, 80)",
|
||||
"rgb(33, 150, 243)",
|
||||
"rgb(156, 39, 176)",
|
||||
]
|
||||
|
||||
return (
|
||||
<div
|
||||
id="tiles"
|
||||
style={{
|
||||
"--columns": params().columns,
|
||||
"--rows": params().rows,
|
||||
"--bg-color-1": colors[count() % colors.length],
|
||||
"--bg-color-2": colors[(count() + 1) % colors.length],
|
||||
}}
|
||||
>
|
||||
<For each={Array.from(Array(params().quantity))}>
|
||||
{(_tile, i) => <Tile index={i()} />}
|
||||
</For>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Grid
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import classNames from "classnames"
|
||||
import { createEffect, createMemo, createSignal } from "solid-js"
|
||||
import { For, createEffect, createSignal } from "solid-js"
|
||||
|
||||
function Grid2() {
|
||||
function floorClient(number: number) {
|
||||
|
@ -25,7 +25,7 @@ function Grid2() {
|
|||
}
|
||||
handleResize()
|
||||
window.addEventListener("resize", handleResize)
|
||||
}, [])
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
const timeout = setTimeout(() => {
|
||||
|
@ -36,85 +36,82 @@ function Grid2() {
|
|||
})
|
||||
}, 500)
|
||||
return () => clearTimeout(timeout)
|
||||
}, [columns, rows])
|
||||
})
|
||||
|
||||
const createTiles = createMemo(() => {
|
||||
const sentences = [
|
||||
"Ethem ...",
|
||||
"hat ...",
|
||||
"lange ...",
|
||||
"Hörner 🐂",
|
||||
"Grüße von Mallorca 🌊 🦦 ☀️",
|
||||
]
|
||||
const sentences = [
|
||||
"Ethem ...",
|
||||
"hat ...",
|
||||
"lange ...",
|
||||
"Hörner 🐂",
|
||||
"Grüße von Mallorca 🌊 🦦 ☀️",
|
||||
]
|
||||
|
||||
function createTile(index: number) {
|
||||
const x = index % params().columns
|
||||
const y = Math.floor(index / params().columns)
|
||||
const xDiff = (x - position()[0]) / 20
|
||||
const yDiff = (y - position()[1]) / 20
|
||||
const pos = Math.sqrt(xDiff * xDiff + yDiff * yDiff).toFixed(2)
|
||||
function Tile(props: { index: number }) {
|
||||
const x = () => props.index % params().columns
|
||||
const y = () => Math.floor(props.index / params().columns)
|
||||
const xDiff = () => (x() - position()[0]) / 20
|
||||
const yDiff = () => (y() - position()[1]) / 20
|
||||
const pos = () =>
|
||||
Math.sqrt(xDiff() * xDiff() + yDiff() * yDiff()).toFixed(2)
|
||||
|
||||
function doEffect(posX: number, posY: number) {
|
||||
if (action()) return
|
||||
setPosition([posX, posY])
|
||||
setActve((e) => !e)
|
||||
setAction(true)
|
||||
function doEffect(posX: number, posY: number) {
|
||||
if (action()) return
|
||||
setPosition([posX, posY])
|
||||
setActve((e) => !e)
|
||||
setAction(true)
|
||||
|
||||
function xDiff(x: number) {
|
||||
return (x - posX) / 20
|
||||
}
|
||||
function yDiff(y: number) {
|
||||
return (y - posY) / 20
|
||||
}
|
||||
function pos(x: number, y: number) {
|
||||
return Math.sqrt(xDiff(x) * xDiff(x) + yDiff(y) * yDiff(y))
|
||||
}
|
||||
const diagonals = [
|
||||
pos(0, 0),
|
||||
pos(params().columns, 0),
|
||||
pos(0, params().rows),
|
||||
pos(params().columns, params().rows),
|
||||
]
|
||||
|
||||
setTimeout(
|
||||
() => {
|
||||
setAction(false)
|
||||
if (active()) setCount((e) => e + 1)
|
||||
},
|
||||
Math.max(...diagonals) * 1000 + 1000,
|
||||
)
|
||||
function xDiff(x: number) {
|
||||
return (x - posX) / 20
|
||||
}
|
||||
function yDiff(y: number) {
|
||||
return (y - posY) / 20
|
||||
}
|
||||
function pos(x: number, y: number) {
|
||||
return Math.sqrt(xDiff(x) * xDiff(x) + yDiff(y) * yDiff(y))
|
||||
}
|
||||
const diagonals = [
|
||||
pos(0, 0),
|
||||
pos(params().columns, 0),
|
||||
pos(0, params().rows),
|
||||
pos(params().columns, params().rows),
|
||||
]
|
||||
|
||||
return (
|
||||
<div
|
||||
class={classNames("tile", active() ? "active" : "inactive")}
|
||||
style={{ "--delay": pos + "s" }}
|
||||
onClick={() => doEffect(x, y)}
|
||||
/>
|
||||
setTimeout(
|
||||
() => {
|
||||
setAction(false)
|
||||
if (active()) setCount((e) => e + 1)
|
||||
},
|
||||
Math.max(...diagonals) * 1000 + 1000,
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
id="tiles"
|
||||
style={{
|
||||
"--columns": params().columns,
|
||||
"--rows": params().rows,
|
||||
}}
|
||||
>
|
||||
<div class="center-div">
|
||||
<h1 class={classNames("headline", !active ? "active" : "inactive")}>
|
||||
{sentences[count() % sentences.length]}
|
||||
</h1>
|
||||
</div>
|
||||
{Array.from(Array(params().quantity), (_tile, index) =>
|
||||
createTile(index),
|
||||
)}
|
||||
</div>
|
||||
class={classNames("tile", active() ? "active" : "inactive")}
|
||||
style={{ "--delay": pos() + "s" }}
|
||||
onClick={() => doEffect(x(), y())}
|
||||
/>
|
||||
)
|
||||
}, [params, position, active, action, count])
|
||||
}
|
||||
|
||||
return createTiles()
|
||||
return (
|
||||
<div
|
||||
id="tiles"
|
||||
style={{
|
||||
"--columns": params().columns,
|
||||
"--rows": params().rows,
|
||||
}}
|
||||
>
|
||||
<div class="center-div">
|
||||
<h1 class={classNames("headline", !active ? "active" : "inactive")}>
|
||||
{sentences[count() % sentences.length]}
|
||||
</h1>
|
||||
</div>
|
||||
<For each={Array.from(Array(params().quantity))}>
|
||||
{(_tile, i) => <Tile index={i()} />}
|
||||
</For>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Grid2
|
||||
|
|
|
@ -1,14 +1,7 @@
|
|||
import classNames from "classnames"
|
||||
import { JSX } from "solid-js"
|
||||
|
||||
function Button({
|
||||
type,
|
||||
disabled,
|
||||
onClick,
|
||||
children,
|
||||
latching,
|
||||
isLatched,
|
||||
}: {
|
||||
function Button(props: {
|
||||
type: "red" | "orange" | "green" | "gray"
|
||||
disabled?: boolean
|
||||
onClick: () => void
|
||||
|
@ -18,26 +11,26 @@ function Button({
|
|||
}) {
|
||||
return (
|
||||
<button
|
||||
disabled={disabled}
|
||||
disabled={props.disabled}
|
||||
class={classNames(
|
||||
"font-farro rounded-xl px-8 py-4 text-5xl font-medium duration-100",
|
||||
disabled
|
||||
props.disabled
|
||||
? "border-4 border-dashed"
|
||||
: latching
|
||||
? isLatched
|
||||
: props.latching
|
||||
? props.isLatched
|
||||
? "mx-1 my-0.5 border-t-4"
|
||||
: "mx-1 my-0.5 border-b-4"
|
||||
: "mx-1 my-0.5 border-b-4 active:border-b-0 active:border-t-4",
|
||||
{
|
||||
"border-red-600 bg-red-500": type === "red",
|
||||
"border-orange-400 bg-warn": type === "orange",
|
||||
"border-green-600 bg-green-500": type === "green",
|
||||
"border-gray-600 bg-gray-500": type === "gray",
|
||||
"border-red-600 bg-red-500": props.type === "red",
|
||||
"border-orange-400 bg-warn": props.type === "orange",
|
||||
"border-green-600 bg-green-500": props.type === "green",
|
||||
"border-gray-600 bg-gray-500": props.type === "gray",
|
||||
},
|
||||
)}
|
||||
onClick={onClick}
|
||||
onClick={() => props.onClick()}
|
||||
>
|
||||
{children}
|
||||
{props.children}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
import { JSX } from "solid-js"
|
||||
|
||||
function Icon({
|
||||
src,
|
||||
children,
|
||||
onClick,
|
||||
}: {
|
||||
function Icon(props: {
|
||||
src: string
|
||||
children: JSX.Element
|
||||
onClick?: () => void
|
||||
|
@ -12,14 +8,14 @@ function Icon({
|
|||
return (
|
||||
<button
|
||||
class="mx-4 mt-4 flex flex-col items-center border-none"
|
||||
onClick={onClick}
|
||||
onClick={() => props.onClick && props.onClick()}
|
||||
>
|
||||
<img
|
||||
class="pixelart mb-1 box-content w-16 rounded-xl bg-white p-1"
|
||||
src={"/assets/" + src}
|
||||
alt={src}
|
||||
src={"/assets/" + props.src}
|
||||
alt={props.src}
|
||||
/>
|
||||
<span class="font-semibold">{children}</span>
|
||||
<span class="font-semibold">{props.children}</span>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import {
|
|||
faRightFromBracket,
|
||||
faSpinnerThird,
|
||||
} from "@fortawesome/pro-solid-svg-icons"
|
||||
import { JSX, createEffect, createMemo, createSignal } from "solid-js"
|
||||
import { JSX, createEffect, createSignal } from "solid-js"
|
||||
import { useNavigate } from "solid-start"
|
||||
import { FontAwesomeIcon } from "~/components/FontAwesomeIcon"
|
||||
import { useGameProps } from "~/hooks/useGameProps"
|
||||
|
@ -14,38 +14,35 @@ import Button from "./Button"
|
|||
import Icon from "./Icon"
|
||||
import Player from "./Player"
|
||||
|
||||
function WithDots({ children }: { children: JSX.Element }) {
|
||||
function WithDots(props: { children: JSX.Element }) {
|
||||
const [dots, setDots] = createSignal(1)
|
||||
|
||||
createEffect(() => {
|
||||
const interval = setInterval(() => setDots((e) => (e % 3) + 1), 1000)
|
||||
return () => clearInterval(interval)
|
||||
}, [])
|
||||
})
|
||||
|
||||
return (
|
||||
<>
|
||||
{children + " "}
|
||||
{props.children + " "}
|
||||
{Array.from(Array(dots()), () => ".").join("")}
|
||||
{Array.from(Array(3 - dots()), (_, i) => (
|
||||
<span> </span>
|
||||
{Array.from(Array(3 - dots()), () => (
|
||||
<> </>
|
||||
))}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function LobbyFrame({ openSettings }: { openSettings: () => void }) {
|
||||
function LobbyFrame(props: { openSettings: () => void }) {
|
||||
const { payload, userStates, full, leave, reset } = useGameProps()
|
||||
const { isConnected } = useSocket()
|
||||
const navigate = useNavigate()
|
||||
const session = useSession()
|
||||
const [launchTime, setLaunchTime] = createSignal(3)
|
||||
|
||||
const launching = createMemo(
|
||||
() =>
|
||||
payload?.users.length === 2 &&
|
||||
!userStates.filter((user) => !user.isReady).length,
|
||||
[payload?.users.length, userStates],
|
||||
)
|
||||
const launching = () =>
|
||||
payload?.users.length === 2 &&
|
||||
!userStates.filter((user) => !user.isReady).length
|
||||
|
||||
createEffect(() => {
|
||||
if (!launching() || launchTime() > 0) return
|
||||
|
@ -66,7 +63,7 @@ function LobbyFrame({ openSettings }: { openSettings: () => void }) {
|
|||
createEffect(() => {
|
||||
if (payload?.game?.id || !isConnected) return
|
||||
socket.emit("update", full)
|
||||
}, [full, payload?.game?.id, isConnected])
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
if (
|
||||
|
@ -91,7 +88,7 @@ function LobbyFrame({ openSettings }: { openSettings: () => void }) {
|
|||
) : (
|
||||
<>
|
||||
{"Game-PIN: "}
|
||||
{isConnected ? (
|
||||
{isConnected() ? (
|
||||
<span class="underline">{payload?.gamePin ?? "----"}</span>
|
||||
) : (
|
||||
<FontAwesomeIcon icon={faSpinnerThird} spin />
|
||||
|
@ -99,12 +96,12 @@ function LobbyFrame({ openSettings }: { openSettings: () => void }) {
|
|||
</>
|
||||
)}
|
||||
</h1>
|
||||
<Icon src="gear.png" onClick={openSettings}>
|
||||
<Icon src="gear.png" onClick={props.openSettings}>
|
||||
Settings
|
||||
</Icon>
|
||||
</div>
|
||||
<div class="flex items-center justify-around">
|
||||
{isConnected ? (
|
||||
{isConnected() ? (
|
||||
<>
|
||||
<Player
|
||||
src="player_blue.png"
|
||||
|
|
|
@ -8,7 +8,7 @@ import {
|
|||
} from "@fortawesome/pro-solid-svg-icons"
|
||||
import { faCaretDown } from "@fortawesome/sharp-solid-svg-icons"
|
||||
import classNames from "classnames"
|
||||
import { createEffect, createMemo, createSignal } from "solid-js"
|
||||
import { createEffect, createSignal } from "solid-js"
|
||||
import { FontAwesomeIcon } from "~/components/FontAwesomeIcon"
|
||||
import { useGameProps } from "~/hooks/useGameProps"
|
||||
import { socket } from "~/lib/socket"
|
||||
|
@ -20,9 +20,9 @@ function HourGlass() {
|
|||
createEffect(() => {
|
||||
const interval = setInterval(() => setCount((e) => (e + 1) % 4), 1000)
|
||||
return () => clearInterval(interval)
|
||||
}, [])
|
||||
})
|
||||
|
||||
const icon = createMemo(() => {
|
||||
const icon = () => {
|
||||
switch (count()) {
|
||||
case 0:
|
||||
return faHourglass3
|
||||
|
@ -35,26 +35,20 @@ function HourGlass() {
|
|||
default:
|
||||
return faHourglassClock
|
||||
}
|
||||
}, [count])
|
||||
}
|
||||
|
||||
return (
|
||||
<FontAwesomeIcon icon={icon()} class="ml-4 w-12" spin={count() === 0} />
|
||||
)
|
||||
}
|
||||
|
||||
function Player({
|
||||
src,
|
||||
i,
|
||||
userId,
|
||||
}: {
|
||||
src: string
|
||||
i: number
|
||||
userId?: string
|
||||
}) {
|
||||
function Player(props: { src: string; i: number; userId?: string }) {
|
||||
const { payload, userStates, setIsReady } = useGameProps()
|
||||
const player = createMemo(() => payload?.users[i], [i, payload?.users])
|
||||
const { isReady, isConnected } = userStates[i]
|
||||
const primary = createMemo(() => userId && userId === payload?.users[i]?.id)
|
||||
const player = () => payload?.users[props.i]
|
||||
const isReady = () => userStates[props.i].isReady
|
||||
const isConnected = () => userStates[props.i].isConnected
|
||||
const primary = () =>
|
||||
props.userId && props.userId === payload?.users[props.i]?.id
|
||||
|
||||
return (
|
||||
<div class="flex w-96 flex-col items-center gap-4 p-4">
|
||||
|
@ -67,7 +61,11 @@ function Player({
|
|||
{player?.name ?? "Spieler " + (player()?.index === 2 ? "2" : "1")}
|
||||
</p>
|
||||
<div class="relative">
|
||||
<img class="pixelart w-64" src={"/assets/" + src} alt={src} />
|
||||
<img
|
||||
class="pixelart w-64"
|
||||
src={"/assets/" + props.src}
|
||||
alt={props.src}
|
||||
/>
|
||||
{primary() ? (
|
||||
<button class="absolute right-4 top-4 h-14 w-14 rounded-lg border-2 border-dashed border-warn bg-gray-800 bg-opacity-90">
|
||||
<FontAwesomeIcon
|
||||
|
@ -78,21 +76,21 @@ function Player({
|
|||
) : null}
|
||||
</div>
|
||||
<Button
|
||||
type={isConnected ? (isReady ? "green" : "orange") : "gray"}
|
||||
type={isConnected() ? (isReady() ? "green" : "orange") : "gray"}
|
||||
latching
|
||||
isLatched={!!isReady}
|
||||
isLatched={!!isReady()}
|
||||
onClick={() => {
|
||||
if (!player) return
|
||||
if (!player()) return
|
||||
setIsReady({
|
||||
i,
|
||||
isReady: !isReady,
|
||||
i: props.i,
|
||||
isReady: !isReady(),
|
||||
})
|
||||
socket.emit("isReady", !isReady)
|
||||
socket.emit("isReady", !isReady())
|
||||
}}
|
||||
disabled={!primary}
|
||||
disabled={!primary()}
|
||||
>
|
||||
Ready
|
||||
{isReady && isConnected ? (
|
||||
{isReady() && isConnected() ? (
|
||||
<FontAwesomeIcon icon={faCheck} class="ml-4 w-12" />
|
||||
) : primary() ? (
|
||||
<FontAwesomeIcon
|
||||
|
|
|
@ -3,26 +3,20 @@ import {
|
|||
faToggleLargeOn,
|
||||
} from "@fortawesome/pro-solid-svg-icons"
|
||||
import classNames from "classnames"
|
||||
import { JSX, createMemo } from "solid-js"
|
||||
import { JSX } from "solid-js"
|
||||
import { FontAwesomeIcon } from "~/components/FontAwesomeIcon"
|
||||
import { setGameSetting } from "~/components/Gamefield/EventBar"
|
||||
import { useGameProps } from "~/hooks/useGameProps"
|
||||
import { GameSettingKeys } from "../../../interfaces/frontend"
|
||||
|
||||
function Setting({
|
||||
children,
|
||||
prop,
|
||||
}: {
|
||||
children: JSX.Element
|
||||
prop: GameSettingKeys
|
||||
}) {
|
||||
function Setting(props: { children: JSX.Element; key: GameSettingKeys }) {
|
||||
const { payload, setSetting, full } = useGameProps()
|
||||
const state = createMemo(() => payload?.game?.[prop], [payload?.game, prop])
|
||||
const state = () => payload?.game?.[props.key]
|
||||
|
||||
return (
|
||||
<label class="flex items-center justify-between" for={prop}>
|
||||
<label class="flex items-center justify-between" for={props.key}>
|
||||
<span class="col-span-2 w-96 select-none text-5xl text-white drop-shadow-md">
|
||||
{children}
|
||||
{props.children}
|
||||
</span>
|
||||
<FontAwesomeIcon
|
||||
class={classNames(
|
||||
|
@ -39,11 +33,11 @@ function Setting({
|
|||
class="bg-none"
|
||||
checked={state()}
|
||||
type="checkbox"
|
||||
id={prop}
|
||||
id={props.key}
|
||||
onChange={() =>
|
||||
setGameSetting(
|
||||
{
|
||||
[prop]: !state,
|
||||
[props.key]: !state,
|
||||
},
|
||||
setSetting,
|
||||
full,
|
||||
|
|
|
@ -7,7 +7,7 @@ import { socket } from "~/lib/socket"
|
|||
import { GameSettings } from "../../../interfaces/frontend"
|
||||
import Setting from "./Setting"
|
||||
|
||||
function Settings({ closeSettings }: { closeSettings: () => void }) {
|
||||
function Settings(props: { closeSettings: () => void }) {
|
||||
const { setSetting, full } = useGameProps()
|
||||
|
||||
const gameSetting = (payload: GameSettings) => {
|
||||
|
@ -29,7 +29,7 @@ function Settings({ closeSettings }: { closeSettings: () => void }) {
|
|||
</h1>
|
||||
<button
|
||||
class="right-6 top-6 ml-auto h-14 w-14"
|
||||
onClick={closeSettings}
|
||||
onClick={() => props.closeSettings()}
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
class="h-full w-full text-gray-800 drop-shadow-md"
|
||||
|
@ -59,10 +59,10 @@ function Settings({ closeSettings }: { closeSettings: () => void }) {
|
|||
</button>
|
||||
</div>
|
||||
<div class="flex flex-col gap-8">
|
||||
<Setting prop="allowSpectators">Erlaube Zuschauer</Setting>
|
||||
<Setting prop="allowSpecials">Erlaube spezial Items</Setting>
|
||||
<Setting prop="allowChat">Erlaube den Chat</Setting>
|
||||
<Setting prop="allowMarkDraw">Erlaube zeichen/makieren</Setting>
|
||||
<Setting key="allowSpectators">Erlaube Zuschauer</Setting>
|
||||
<Setting key="allowSpecials">Erlaube spezial Items</Setting>
|
||||
<Setting key="allowChat">Erlaube den Chat</Setting>
|
||||
<Setting key="allowMarkDraw">Erlaube zeichen/makieren</Setting>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,50 +1,50 @@
|
|||
import classNames from "classnames"
|
||||
|
||||
function Logo({ small }: { small?: boolean }) {
|
||||
function Logo(props: { small?: boolean }) {
|
||||
return (
|
||||
<div class="relative flex flex-col items-center rounded-sm border-x-4 border-y-2 border-shield-gray bg-shield-lightgray md:border-x-8 md:border-y-4">
|
||||
<h1
|
||||
class={classNames(
|
||||
"font-checkpoint mx-16 my-2 flex flex-col gap-2 border-y-2 border-slate-700 text-center text-2xl leading-tight tracking-widest sm:mx-24 sm:my-3 sm:gap-3 sm:border-y-[3px] sm:text-4xl md:mx-36 md:my-4 md:gap-4 md:border-y-4 md:text-5xl",
|
||||
{ "xl:gap-6 xl:py-2 xl:text-6xl": !small },
|
||||
{ "xl:gap-6 xl:py-2 xl:text-6xl": !props.small },
|
||||
)}
|
||||
>
|
||||
<span>Leaky</span>
|
||||
<span>Ships</span>
|
||||
</h1>
|
||||
<Screws small={small} />
|
||||
<Screws small={props.small} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function Screws({ small }: { small?: boolean }) {
|
||||
function Screws(props: { small?: boolean }) {
|
||||
return (
|
||||
<>
|
||||
<Screw
|
||||
small={small}
|
||||
small={props.small}
|
||||
orientation={classNames("top-1 left-1 sm:top-2 sm:left-2", {
|
||||
"xl:top-4 xl:left-4": !small,
|
||||
"xl:top-4 xl:left-4": !props.small,
|
||||
})}
|
||||
rotation="rotate-[135deg]"
|
||||
/>
|
||||
<Screw
|
||||
small={small}
|
||||
small={props.small}
|
||||
orientation={classNames("top-1 right-1 sm:top-2 sm:right-2", {
|
||||
"xl:top-4 xl:right-4": !small,
|
||||
"xl:top-4 xl:right-4": !props.small,
|
||||
})}
|
||||
rotation="rotate-[5deg]"
|
||||
/>
|
||||
<Screw
|
||||
small={small}
|
||||
small={props.small}
|
||||
orientation={classNames("bottom-1 right-1 sm:bottom-2 sm:right-2", {
|
||||
"xl:bottom-4 xl:right-4": !small,
|
||||
"xl:bottom-4 xl:right-4": !props.small,
|
||||
})}
|
||||
rotation="rotate-[150deg]"
|
||||
/>
|
||||
<Screw
|
||||
small={small}
|
||||
small={props.small}
|
||||
orientation={classNames("bottom-1 left-1 sm:bottom-2 sm:left-2", {
|
||||
"xl:bottom-4 xl:left-4": !small,
|
||||
"xl:bottom-4 xl:left-4": !props.small,
|
||||
})}
|
||||
rotation="rotate-[20deg]"
|
||||
/>
|
||||
|
@ -52,11 +52,7 @@ function Screws({ small }: { small?: boolean }) {
|
|||
)
|
||||
}
|
||||
|
||||
function Screw({
|
||||
orientation,
|
||||
rotation,
|
||||
small,
|
||||
}: {
|
||||
function Screw(props: {
|
||||
orientation: string
|
||||
rotation: string
|
||||
small?: boolean
|
||||
|
@ -65,15 +61,15 @@ function Screw({
|
|||
<div
|
||||
class={classNames(
|
||||
"absolute flex h-3 w-3 flex-col items-center justify-center rounded-full border-[1px] border-neutral-700 bg-neutral-400 sm:h-5 sm:w-5 sm:border-2 md:h-6 md:w-6",
|
||||
{ "xl:h-8 xl:w-8": !small },
|
||||
orientation,
|
||||
{ "xl:h-8 xl:w-8": !props.small },
|
||||
props.orientation,
|
||||
)}
|
||||
>
|
||||
<hr
|
||||
class={classNames(
|
||||
"color w-full border-neutral-500 sm:border-t-2",
|
||||
{ "xl:border-t-4": !small },
|
||||
rotation,
|
||||
{ "xl:border-t-4": !props.small },
|
||||
props.rotation,
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -1,37 +1,36 @@
|
|||
import { IconDefinition } from "@fortawesome/fontawesome-svg-core"
|
||||
import classNames from "classnames"
|
||||
import { JSX } from "solid-js"
|
||||
import { JSX, Show } from "solid-js"
|
||||
import { FontAwesomeIcon } from "./FontAwesomeIcon"
|
||||
|
||||
function OptionButton({
|
||||
id,
|
||||
icon,
|
||||
callback,
|
||||
node,
|
||||
disabled,
|
||||
}: {
|
||||
id: string
|
||||
function OptionButton(props: {
|
||||
text: string
|
||||
icon: IconDefinition
|
||||
callback?: () => void
|
||||
node?: JSX.Element
|
||||
nodeWhen?: boolean
|
||||
disabled?: boolean
|
||||
}) {
|
||||
return (
|
||||
<button
|
||||
class={classNames(
|
||||
"flex w-full flex-row items-center justify-between rounded-xl py-2 pl-8 pr-4 text-lg text-grayish duration-100 first:mt-4 last:mt-4 sm:py-4 sm:pl-16 sm:pr-8 sm:text-4xl sm:first:mt-8 sm:last:mt-8",
|
||||
!disabled
|
||||
!props.disabled
|
||||
? "border-b-4 border-shield-gray bg-voidDark active:border-b-0 active:border-t-4"
|
||||
: "border-4 border-dashed border-slate-600 bg-red-950",
|
||||
)}
|
||||
onClick={() => callback && setTimeout(callback, 200)}
|
||||
disabled={disabled}
|
||||
title={!disabled ? "" : "Please login"}
|
||||
onClick={() => props.callback && setTimeout(props.callback, 200)}
|
||||
disabled={props.disabled}
|
||||
title={!props.disabled ? "" : "Please login"}
|
||||
>
|
||||
<span class="mx-auto">{node ? node : id}</span>
|
||||
<Show
|
||||
when={props.nodeWhen}
|
||||
fallback={<span class="mx-auto">{props.text}</span>}
|
||||
children={props.node}
|
||||
/>
|
||||
<FontAwesomeIcon
|
||||
class="ml-2 w-10 text-xl sm:ml-12 sm:text-4xl"
|
||||
icon={icon}
|
||||
icon={props.icon}
|
||||
/>
|
||||
</button>
|
||||
)
|
||||
|
|
|
@ -8,7 +8,7 @@ function drawLine({ prevPoint, currentPoint, ctx, color }: Draw) {
|
|||
const lineColor = color
|
||||
const lineWidth = 5
|
||||
|
||||
let startPoint = prevPoint ?? currentPoint
|
||||
const startPoint = prevPoint ?? currentPoint
|
||||
ctx.beginPath()
|
||||
ctx.lineWidth = lineWidth
|
||||
ctx.strokeStyle = lineColor
|
||||
|
@ -79,7 +79,7 @@ export const useDraw = () => {
|
|||
canvas.removeEventListener("mousemove", handler)
|
||||
window.removeEventListener("mouseup", mouseUpHandler)
|
||||
}
|
||||
}, [color, mouseDown])
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
const canvas = canvasRef
|
||||
|
@ -94,7 +94,7 @@ export const useDraw = () => {
|
|||
socket.emit("canvas-state", canvasRef.toDataURL())
|
||||
}
|
||||
|
||||
const canvasStateFromServer = (state: string, index: number) => {
|
||||
const canvasStateFromServer = (state: string) => {
|
||||
console.log("I received the state")
|
||||
const img = new Image()
|
||||
img.src = state
|
||||
|
@ -103,10 +103,9 @@ export const useDraw = () => {
|
|||
}
|
||||
}
|
||||
|
||||
const socketDrawLine = (props: DrawLineProps, index: number) => {
|
||||
const { prevPoint, currentPoint, color } = props
|
||||
const socketDrawLine = (props: DrawLineProps) => {
|
||||
if (!ctx) return console.log("no ctx here")
|
||||
drawLine({ prevPoint, currentPoint, ctx, color })
|
||||
drawLine({ ctx, ...props })
|
||||
}
|
||||
|
||||
socket.on("playerEvent", playerEvent)
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { GameState, MoveType } from "@prisma/client"
|
||||
import { produce } from "immer"
|
||||
import { getPayloadwithChecksum } from "~/lib/getPayloadwithChecksum"
|
||||
import { socket } from "~/lib/socket"
|
||||
import {
|
||||
|
@ -17,6 +16,7 @@ import {
|
|||
// import { toast } from "react-toastify"
|
||||
import create from "solid-zustand"
|
||||
|
||||
import { Accessor, Setter, createSignal } from "solid-js"
|
||||
import {
|
||||
EventBarModes,
|
||||
GameSettings,
|
||||
|
@ -29,8 +29,10 @@ import {
|
|||
|
||||
const initialState: optionalGamePropsSchema & {
|
||||
userStates: {
|
||||
isReady: boolean
|
||||
isConnected: boolean
|
||||
isReady: Accessor<boolean>
|
||||
setIsReady: Setter<boolean>
|
||||
isConnected: Accessor<boolean>
|
||||
setIsConnected: Setter<boolean>
|
||||
}[]
|
||||
menu: keyof EventBarModes
|
||||
mode: number
|
||||
|
@ -45,10 +47,11 @@ const initialState: optionalGamePropsSchema & {
|
|||
target: initlialTarget,
|
||||
targetPreview: initlialTargetPreview,
|
||||
mouseCursor: initlialMouseCursor,
|
||||
userStates: Array.from(Array(2), () => ({
|
||||
isReady: false,
|
||||
isConnected: false,
|
||||
})),
|
||||
userStates: Array.from(Array(2), () => {
|
||||
const [isReady, setIsReady] = createSignal(false)
|
||||
const [isConnected, setIsConnected] = createSignal(false)
|
||||
return { isReady, setIsReady, isConnected, setIsConnected }
|
||||
}),
|
||||
}
|
||||
|
||||
export type State = typeof initialState
|
||||
|
@ -78,137 +81,129 @@ export type Action = {
|
|||
export const useGameProps = create<State & Action>()((set) => ({
|
||||
...initialState,
|
||||
setActiveIndex: (i, selfIndex) =>
|
||||
set(
|
||||
produce((state: State) => {
|
||||
if (!state.payload) return
|
||||
state.payload.activeIndex = i
|
||||
if (i === selfIndex) {
|
||||
state.menu = "moves"
|
||||
state.mode = 0
|
||||
} else {
|
||||
state.menu = "main"
|
||||
state.mode = -1
|
||||
}
|
||||
}),
|
||||
),
|
||||
set((state: State) => {
|
||||
if (!state.payload) return state
|
||||
state.payload.activeIndex = i
|
||||
if (i === selfIndex) {
|
||||
state.menu = "moves"
|
||||
state.mode = 0
|
||||
} else {
|
||||
state.menu = "main"
|
||||
state.mode = -1
|
||||
}
|
||||
return state
|
||||
}),
|
||||
DispatchMove: (move, i) =>
|
||||
set(
|
||||
produce((state: State) => {
|
||||
if (!state.payload) return
|
||||
const list = targetList(move, move.type)
|
||||
state.payload.users.map((e) => {
|
||||
if (!e) return e
|
||||
if (i === e.index) e.moves.push(move)
|
||||
else if (move.type !== MoveType.radar)
|
||||
e.hits.push(
|
||||
...list.map(({ x, y }) => ({
|
||||
hit: !!intersectingShip(e.ships, {
|
||||
...move,
|
||||
size: 1,
|
||||
variant: 0,
|
||||
}).fields.length,
|
||||
x,
|
||||
y,
|
||||
})),
|
||||
)
|
||||
|
||||
return e
|
||||
})
|
||||
}),
|
||||
),
|
||||
setTarget: (dispatch) =>
|
||||
set(
|
||||
produce((state: State) => {
|
||||
if (typeof dispatch === "function")
|
||||
state.target = dispatch(state.target)
|
||||
else state.target = dispatch
|
||||
}),
|
||||
),
|
||||
setTargetPreview: (dispatch) =>
|
||||
set(
|
||||
produce((state: State) => {
|
||||
if (typeof dispatch === "function")
|
||||
state.targetPreview = dispatch(state.targetPreview)
|
||||
else state.targetPreview = dispatch
|
||||
}),
|
||||
),
|
||||
setMouseCursor: (dispatch) =>
|
||||
set(
|
||||
produce((state: State) => {
|
||||
if (typeof dispatch === "function")
|
||||
state.mouseCursor = dispatch(state.mouseCursor)
|
||||
else state.mouseCursor = dispatch
|
||||
}),
|
||||
),
|
||||
setShips: (ships, index) =>
|
||||
set(
|
||||
produce((state: State) => {
|
||||
if (!state.payload) return
|
||||
state.payload.users = state.payload.users.map((e) => {
|
||||
if (!e || e.index !== index) return e
|
||||
e.ships = ships
|
||||
return e
|
||||
})
|
||||
}),
|
||||
),
|
||||
removeShip: ({ size, variant, x, y }, index) =>
|
||||
set(
|
||||
produce((state: State) => {
|
||||
state.payload?.users.map((e) => {
|
||||
if (!e || e.index !== index) return
|
||||
const indexToRemove = e.ships.findIndex(
|
||||
(ship) =>
|
||||
ship.size === size &&
|
||||
ship.variant === variant &&
|
||||
ship.x === x &&
|
||||
ship.y === y,
|
||||
set((state: State) => {
|
||||
if (!state.payload) return state
|
||||
const list = targetList(move, move.type)
|
||||
state.payload.users.map((e) => {
|
||||
if (!e) return e
|
||||
if (i === e.index) e.moves.push(move)
|
||||
else if (move.type !== MoveType.radar)
|
||||
e.hits.push(
|
||||
...list.map(({ x, y }) => ({
|
||||
hit: !!intersectingShip(e.ships, {
|
||||
...move,
|
||||
size: 1,
|
||||
variant: 0,
|
||||
}).fields.length,
|
||||
x,
|
||||
y,
|
||||
})),
|
||||
)
|
||||
e.ships.splice(indexToRemove, 1)
|
||||
return e
|
||||
})
|
||||
}),
|
||||
),
|
||||
|
||||
return e
|
||||
})
|
||||
return state
|
||||
}),
|
||||
setTarget: (dispatch) =>
|
||||
set((state: State) => {
|
||||
if (typeof dispatch === "function") state.target = dispatch(state.target)
|
||||
else state.target = dispatch
|
||||
return state
|
||||
}),
|
||||
setTargetPreview: (dispatch) =>
|
||||
set((state: State) => {
|
||||
if (typeof dispatch === "function")
|
||||
state.targetPreview = dispatch(state.targetPreview)
|
||||
else state.targetPreview = dispatch
|
||||
return state
|
||||
}),
|
||||
setMouseCursor: (dispatch) =>
|
||||
set((state: State) => {
|
||||
if (typeof dispatch === "function")
|
||||
state.mouseCursor = dispatch(state.mouseCursor)
|
||||
else state.mouseCursor = dispatch
|
||||
return state
|
||||
}),
|
||||
setShips: (ships, index) =>
|
||||
set((state: State) => {
|
||||
if (!state.payload) return state
|
||||
state.payload.users = state.payload.users.map((e) => {
|
||||
if (!e || e.index !== index) return e
|
||||
e.ships = ships
|
||||
return e
|
||||
})
|
||||
return state
|
||||
}),
|
||||
removeShip: ({ size, variant, x, y }, index) =>
|
||||
set((state: State) => {
|
||||
state.payload?.users.map((e) => {
|
||||
if (!e || e.index !== index) return
|
||||
const indexToRemove = e.ships.findIndex(
|
||||
(ship) =>
|
||||
ship.size === size &&
|
||||
ship.variant === variant &&
|
||||
ship.x === x &&
|
||||
ship.y === y,
|
||||
)
|
||||
e.ships.splice(indexToRemove, 1)
|
||||
return e
|
||||
})
|
||||
return state
|
||||
}),
|
||||
setPlayer: (payload) => {
|
||||
let hash: string | null = null
|
||||
set(
|
||||
produce((state: State) => {
|
||||
if (!state.payload) return
|
||||
state.payload.users = payload.users
|
||||
const body = getPayloadwithChecksum(state.payload)
|
||||
if (!body.hash) {
|
||||
// toast.warn("Something is wrong... ", {
|
||||
// toastId: "st_wrong",
|
||||
// theme: "colored",
|
||||
// })
|
||||
return
|
||||
}
|
||||
hash = body.hash
|
||||
state.hash = hash
|
||||
}),
|
||||
)
|
||||
set((state: State) => {
|
||||
if (!state.payload) return state
|
||||
state.payload.users = payload.users
|
||||
const body = getPayloadwithChecksum(state.payload)
|
||||
if (!body.hash) {
|
||||
// toast.warn("Something is wrong... ", {
|
||||
// toastId: "st_wrong",
|
||||
// theme: "colored",
|
||||
// })
|
||||
|
||||
return state
|
||||
}
|
||||
hash = body.hash
|
||||
state.hash = hash
|
||||
return state
|
||||
})
|
||||
return hash
|
||||
},
|
||||
setSetting: (settings) => {
|
||||
let hash: string | null = null
|
||||
set(
|
||||
produce((state: State) => {
|
||||
if (!state.payload?.game) return
|
||||
Object.assign(state.payload.game, settings)
|
||||
const body = getPayloadwithChecksum(state.payload)
|
||||
if (!body.hash) {
|
||||
// toast.warn("Something is wrong... ", {
|
||||
// toastId: "st_wrong",
|
||||
// theme: "colored",
|
||||
// })
|
||||
return
|
||||
}
|
||||
hash = body.hash
|
||||
state.hash = hash
|
||||
}),
|
||||
)
|
||||
set((state: State) => {
|
||||
if (!state.payload?.game) return state
|
||||
Object.assign(state.payload.game, settings)
|
||||
const body = getPayloadwithChecksum(state.payload)
|
||||
if (!body.hash) {
|
||||
// toast.warn("Something is wrong... ", {
|
||||
// toastId: "st_wrong",
|
||||
// theme: "colored",
|
||||
// })
|
||||
return state
|
||||
}
|
||||
hash = body.hash
|
||||
state.hash = hash
|
||||
return state
|
||||
})
|
||||
return hash
|
||||
},
|
||||
full: (newGameProps) =>
|
||||
// eslint-disable-next-line solid/reactivity
|
||||
set((state) => {
|
||||
if (state.hash === newGameProps.hash) {
|
||||
console.log("Everything up to date.")
|
||||
|
@ -239,31 +234,26 @@ export const useGameProps = create<State & Action>()((set) => ({
|
|||
})
|
||||
},
|
||||
setIsReady: ({ i, isReady }) =>
|
||||
set(
|
||||
produce((state: State) => {
|
||||
state.userStates[i].isReady = isReady
|
||||
state.userStates[i].isConnected = true
|
||||
}),
|
||||
),
|
||||
set((state: State) => {
|
||||
state.userStates[i].setIsReady(isReady)
|
||||
state.userStates[i].setIsConnected(true)
|
||||
return state
|
||||
}),
|
||||
gameState: (newState: GameState) =>
|
||||
set(
|
||||
produce((state: State) => {
|
||||
if (!state.payload?.game) return
|
||||
state.payload.game.state = newState
|
||||
state.userStates = state.userStates.map((e) => ({
|
||||
...e,
|
||||
isReady: false,
|
||||
}))
|
||||
}),
|
||||
),
|
||||
set((state: State) => {
|
||||
if (!state.payload?.game) return state
|
||||
state.payload.game.state = newState
|
||||
state.userStates.forEach((e) => {
|
||||
e.setIsReady(false)
|
||||
})
|
||||
return state
|
||||
}),
|
||||
setIsConnected: ({ i, isConnected }) =>
|
||||
set(
|
||||
produce((state: State) => {
|
||||
state.userStates[i].isConnected = isConnected
|
||||
if (isConnected) return
|
||||
state.userStates[i].isReady = false
|
||||
}),
|
||||
),
|
||||
set((state: State) => {
|
||||
state.userStates[i].setIsConnected(isConnected)
|
||||
if (!isConnected) state.userStates[i].setIsReady(false)
|
||||
return state
|
||||
}),
|
||||
reset: () => {
|
||||
set(initialState)
|
||||
},
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { createMemo } from "solid-js"
|
||||
import { ShipProps } from "../interfaces/frontend"
|
||||
import { useGameProps } from "./useGameProps"
|
||||
import useIndex from "./useIndex"
|
||||
|
@ -7,10 +6,8 @@ function useShips() {
|
|||
const gameProps = useGameProps()
|
||||
const { selfIndex } = useIndex()
|
||||
|
||||
const ships = createMemo(
|
||||
() =>
|
||||
gameProps.payload?.users.find((e) => e?.index === selfIndex)?.ships ?? [],
|
||||
)
|
||||
const ships = () =>
|
||||
gameProps.payload?.users.find((e) => e?.index === selfIndex)?.ships ?? []
|
||||
const setShips = (ships: ShipProps[]) => gameProps.setShips(ships, selfIndex)
|
||||
const removeShip = (ship: ShipProps) => gameProps.removeShip(ship, selfIndex)
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import status from "http-status"
|
||||
// import { toast } from "react-toastify"
|
||||
import { createEffect, createMemo, createSignal } from "solid-js"
|
||||
import { createEffect, createSignal } from "solid-js"
|
||||
import { useNavigate } from "solid-start"
|
||||
import { socket } from "~/lib/socket"
|
||||
import { GamePropsSchema } from "~/lib/zodSchemas"
|
||||
|
@ -28,11 +28,8 @@ function useSocket() {
|
|||
} = useGameProps()
|
||||
const navigate = useNavigate()
|
||||
|
||||
const isConnected = createMemo(
|
||||
() =>
|
||||
selfIndex >= 0 ? userStates[selfIndex].isConnected : isConnectedState(),
|
||||
[selfIndex, isConnectedState(), userStates],
|
||||
)
|
||||
const isConnected = () =>
|
||||
selfIndex >= 0 ? userStates[selfIndex].isConnected() : isConnectedState()
|
||||
|
||||
createEffect(() => {
|
||||
if (selfIndex < 0) return
|
||||
|
@ -40,7 +37,7 @@ function useSocket() {
|
|||
i: selfIndex,
|
||||
isConnected: isConnectedState(),
|
||||
})
|
||||
}, [selfIndex, isConnectedState(), setIsConnected])
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
const connect = () => {
|
||||
|
@ -66,7 +63,7 @@ function useSocket() {
|
|||
|
||||
const playerEvent = (event: PlayerEvent) => {
|
||||
const { type, i } = event
|
||||
let message: string
|
||||
// let message: string
|
||||
console.log("playerEvent", type)
|
||||
switch (type) {
|
||||
case "disconnect":
|
||||
|
@ -74,11 +71,11 @@ function useSocket() {
|
|||
i,
|
||||
isConnected: false,
|
||||
})
|
||||
message = "Player is disconnected."
|
||||
// message = "Player is disconnected."
|
||||
break
|
||||
|
||||
case "leave":
|
||||
message = "Player has left the lobby."
|
||||
// message = "Player has left the lobby."
|
||||
break
|
||||
|
||||
case "connect":
|
||||
|
@ -86,12 +83,12 @@ function useSocket() {
|
|||
i,
|
||||
isConnected: true,
|
||||
})
|
||||
socket.emit("isReady", userStates[selfIndex].isReady)
|
||||
message = "Player has joined the lobby."
|
||||
socket.emit("isReady", userStates[selfIndex].isReady())
|
||||
// message = "Player has joined the lobby."
|
||||
break
|
||||
|
||||
default:
|
||||
message = "Not defined yet."
|
||||
// message = "Not defined yet."
|
||||
break
|
||||
}
|
||||
// toast.info(message, { toastId: message })
|
||||
|
@ -172,7 +169,7 @@ function useSocket() {
|
|||
|
||||
return {
|
||||
isConnected:
|
||||
selfIndex >= 0 ? userStates[selfIndex].isConnected : isConnectedState(),
|
||||
selfIndex >= 0 ? userStates[selfIndex].isConnected : isConnectedState,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ export interface TargetList extends Position {
|
|||
edges: string[]
|
||||
}
|
||||
export interface Mode {
|
||||
pointerGrid: any[][]
|
||||
pointerGrid: void[][]
|
||||
type: MoveType
|
||||
}
|
||||
export interface ItemProps {
|
||||
|
|
|
@ -2,7 +2,7 @@ import { APIEvent, json } from "solid-start"
|
|||
import { rejectionError } from "./errors"
|
||||
import logging from "./logging"
|
||||
|
||||
export default function sendError<T>(
|
||||
export default function sendError(
|
||||
request: APIEvent["request"],
|
||||
err: rejectionError | Error,
|
||||
) {
|
||||
|
|
|
@ -153,8 +153,8 @@ export const shipProps = (
|
|||
orientation: targetPreview.orientation,
|
||||
})
|
||||
export function shipFields(ship: ShipProps, i?: number) {
|
||||
let fields: IndexedPosition[] = []
|
||||
let borders: IndexedPosition[] = []
|
||||
const fields: IndexedPosition[] = []
|
||||
const borders: IndexedPosition[] = []
|
||||
for (
|
||||
let x = ship.x;
|
||||
x <= (ship.orientation === "h" ? ship.x + ship.size - 1 : ship.x);
|
||||
|
|
|
@ -11,6 +11,7 @@ interface Data {
|
|||
}
|
||||
|
||||
export async function GET({ request }: APIEvent) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const body = request.json() as any //TODO
|
||||
const gameId = body.query.id
|
||||
const session = await getSession(request, authOptions)
|
||||
|
@ -39,6 +40,6 @@ export async function GET({ request }: APIEvent) {
|
|||
|
||||
sendResponse(request, {
|
||||
message: "Here is the game.",
|
||||
body: { game },
|
||||
body: { game } as Data,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ export async function POST({ request }: APIEvent) {
|
|||
.toString()
|
||||
.padStart(4, "0")
|
||||
|
||||
let created = false
|
||||
const created = false
|
||||
|
||||
let game = await getAnyRunningGame(id)
|
||||
if (game) {
|
||||
|
|
|
@ -74,6 +74,7 @@ export async function POST({ request }: APIEvent) {
|
|||
body,
|
||||
type: ["debug", "infoCyan"],
|
||||
})
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (err: any) {
|
||||
await logging(
|
||||
"HERE".red + err.code + err.meta + err.message,
|
||||
|
|
|
@ -33,7 +33,7 @@ export async function GET({ request }: { request: RequestWithSocket }) {
|
|||
// io.use(authenticate)
|
||||
io.use(async (socket, next) => {
|
||||
try {
|
||||
// @ts-ignore
|
||||
// @ts-expect-error TODO add correct server
|
||||
const session = await getSession(socket.request, authOptions)
|
||||
if (!session) return next(new Error(status["401"]))
|
||||
socket.data.user = session.user
|
||||
|
@ -66,7 +66,7 @@ export async function GET({ request }: { request: RequestWithSocket }) {
|
|||
})
|
||||
|
||||
next()
|
||||
} catch (err: any) {
|
||||
} catch {
|
||||
logging("Unkonwn error - " + status["401"], ["warn"], socket.request)
|
||||
next(new Error(status["401"]))
|
||||
}
|
||||
|
|
|
@ -1,40 +1,40 @@
|
|||
// import { toast } from "react-toastify"
|
||||
import { createEffect } from "solid-js"
|
||||
import { useNavigate } from "solid-start"
|
||||
import { useGameProps } from "~/hooks/useGameProps"
|
||||
import { useSession } from "~/hooks/useSession"
|
||||
// import { createEffect } from "solid-js"
|
||||
// import { useNavigate } from "solid-start"
|
||||
// import { useGameProps } from "~/hooks/useGameProps"
|
||||
// import { useSession } from "~/hooks/useSession"
|
||||
|
||||
export default function Game() {
|
||||
const { payload } = useGameProps()
|
||||
const navigate = useNavigate()
|
||||
const session = useSession()
|
||||
// const { payload } = useGameProps()
|
||||
// const navigate = useNavigate()
|
||||
// const session = useSession()
|
||||
|
||||
createEffect(() => {
|
||||
const gameId = payload?.game?.id
|
||||
const path = gameId ? "/game" : "/start"
|
||||
// toast.promise(navigate(path), {
|
||||
// pending: {
|
||||
// render: "Wird weitergeleitet...",
|
||||
// toastId: "pageLoad",
|
||||
// },
|
||||
// success: {
|
||||
// render: gameId
|
||||
// ? "Spiel gefunden!"
|
||||
// : session?.user.id
|
||||
// ? "Kein laufendes Spiel."
|
||||
// : "Kein laufendes Spiel. Bitte anmelden.",
|
||||
// toastId: "pageLoad",
|
||||
// theme: session?.user.id ? "dark" : undefined,
|
||||
// type: gameId ? "success" : "info",
|
||||
// },
|
||||
// error: {
|
||||
// render: "Es ist ein Fehler aufgetreten 🤯",
|
||||
// type: "error",
|
||||
// toastId: "pageLoad",
|
||||
// theme: "colored",
|
||||
// },
|
||||
// })
|
||||
})
|
||||
// createEffect(() => {
|
||||
// const gameId = payload?.game?.id
|
||||
// const path = gameId ? "/game" : "/start"
|
||||
// toast.promise(navigate(path), {
|
||||
// pending: {
|
||||
// render: "Wird weitergeleitet...",
|
||||
// toastId: "pageLoad",
|
||||
// },
|
||||
// success: {
|
||||
// render: gameId
|
||||
// ? "Spiel gefunden!"
|
||||
// : session?.user.id
|
||||
// ? "Kein laufendes Spiel."
|
||||
// : "Kein laufendes Spiel. Bitte anmelden.",
|
||||
// toastId: "pageLoad",
|
||||
// theme: session?.user.id ? "dark" : undefined,
|
||||
// type: gameId ? "success" : "info",
|
||||
// },
|
||||
// error: {
|
||||
// render: "Es ist ein Fehler aufgetreten 🤯",
|
||||
// type: "error",
|
||||
// toastId: "pageLoad",
|
||||
// theme: "colored",
|
||||
// },
|
||||
// })
|
||||
// })
|
||||
|
||||
return (
|
||||
<div class="h-full bg-theme">
|
||||
|
|
|
@ -4,7 +4,7 @@ import { GamePropsSchema } from "~/lib/zodSchemas"
|
|||
// import OtpInput from "react-otp-input"
|
||||
// import { Icons, toast } from "react-toastify"
|
||||
import status from "http-status"
|
||||
import { createEffect, createMemo, createSignal } from "solid-js"
|
||||
import { createEffect, createSignal } from "solid-js"
|
||||
import { useLocation, useNavigate, useSearchParams } from "solid-start"
|
||||
import BurgerMenu from "~/components/BurgerMenu"
|
||||
import { FontAwesomeIcon } from "~/components/FontAwesomeIcon"
|
||||
|
@ -30,28 +30,28 @@ export function isAuthenticated(res: Response) {
|
|||
// })
|
||||
}
|
||||
|
||||
const handleConfirmation = () => {
|
||||
const toastId = "confirm"
|
||||
// toast.warn(
|
||||
// <div id="toast-confirm">
|
||||
// <h4>You are already in another round, do you want to:</h4>
|
||||
// <button onClick={() => toast.dismiss(toastId)}>Join</button>
|
||||
// or
|
||||
// <button onClick={() => toast.dismiss(toastId)}>Leave</button>
|
||||
// </div>,
|
||||
// { autoClose: false, toastId },
|
||||
// )
|
||||
}
|
||||
// const handleConfirmation = () => {
|
||||
// const toastId = "confirm"
|
||||
// toast.warn(
|
||||
// <div id="toast-confirm">
|
||||
// <h4>You are already in another round, do you want to:</h4>
|
||||
// <button onClick={() => toast.dismiss(toastId)}>Join</button>
|
||||
// or
|
||||
// <button onClick={() => toast.dismiss(toastId)}>Leave</button>
|
||||
// </div>,
|
||||
// { autoClose: false, toastId },
|
||||
// )
|
||||
// }
|
||||
|
||||
export default function Start() {
|
||||
const [otp, setOtp] = createSignal("")
|
||||
const [otp] = createSignal("")
|
||||
const gameProps = useGameProps()
|
||||
const location = useLocation()
|
||||
const navigate = useNavigate()
|
||||
const session = useSession()
|
||||
const [searchParams] = useSearchParams()
|
||||
|
||||
const query = createMemo(() => {
|
||||
const query = () => {
|
||||
switch (searchParams["q"]) {
|
||||
case "join":
|
||||
return { join: true }
|
||||
|
@ -60,7 +60,7 @@ export default function Start() {
|
|||
default:
|
||||
return {}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const gameFetch = async (pin?: string) => {
|
||||
const gameRequestPromise = fetch(
|
||||
|
@ -73,8 +73,8 @@ export default function Start() {
|
|||
.then(isAuthenticated)
|
||||
.then((game) => GamePropsSchema.parse(game))
|
||||
|
||||
const move = !pin ? "erstellt" : "angefragt"
|
||||
const toastId = "pageLoad"
|
||||
// const move = !pin ? "erstellt" : "angefragt"
|
||||
// const toastId = "pageLoad"
|
||||
// toast("Raum wird " + move, {
|
||||
// icon: Icons.spinner(),
|
||||
// toastId,
|
||||
|
@ -163,13 +163,13 @@ export default function Start() {
|
|||
</div>
|
||||
<div class="flex flex-col items-center gap-6 sm:gap-12">
|
||||
<OptionButton
|
||||
id="Raum erstellen"
|
||||
text="Raum erstellen"
|
||||
callback={gameFetch}
|
||||
icon={faPlus}
|
||||
disabled={!session.latest}
|
||||
/>
|
||||
<OptionButton
|
||||
id="Raum beitreten"
|
||||
text="Raum beitreten"
|
||||
callback={() =>
|
||||
navigate(
|
||||
location.pathname.concat(
|
||||
|
@ -180,9 +180,11 @@ export default function Start() {
|
|||
}
|
||||
icon={faUserPlus}
|
||||
disabled={!session.latest}
|
||||
nodeWhen={query().join && !!session.latest}
|
||||
node={
|
||||
query().join && session.latest
|
||||
? // <OtpInput
|
||||
<>
|
||||
{
|
||||
// <OtpInput
|
||||
// shouldAutoFocus
|
||||
// containerStyle={{ color: "initial" }}
|
||||
// value={otp}
|
||||
|
@ -194,24 +196,27 @@ export default function Start() {
|
|||
// renderSeparator={<span>-</span>}
|
||||
// renderInput={(props) => <input {...props} />}
|
||||
// />
|
||||
null
|
||||
: null
|
||||
}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<OptionButton
|
||||
id="Zuschauen"
|
||||
text="Zuschauen"
|
||||
icon={faEye}
|
||||
callback={() =>
|
||||
navigate(
|
||||
location.pathname.concat(
|
||||
"?",
|
||||
new URLSearchParams({ q: "watch" }).toString(),
|
||||
),
|
||||
location.pathname +
|
||||
"?" +
|
||||
new URLSearchParams({
|
||||
q: "watch",
|
||||
}).toString(),
|
||||
)
|
||||
}
|
||||
nodeWhen={query().watch}
|
||||
node={
|
||||
query().watch
|
||||
? // <OtpInput
|
||||
<>
|
||||
{
|
||||
// <OtpInput
|
||||
// shouldAutoFocus
|
||||
// containerStyle={{ color: "initial" }}
|
||||
// value={otp}
|
||||
|
@ -223,8 +228,8 @@ export default function Start() {
|
|||
// renderSeparator={<span>-</span>}
|
||||
// renderInput={(props) => <input {...props} />}
|
||||
// />
|
||||
null
|
||||
: null
|
||||
}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue