leaky-ships/leaky-ships/hooks/useSocket.ts
2023-07-11 19:25:44 +02:00

179 lines
4.6 KiB
TypeScript

import { socket } from "@lib/socket"
import { GamePropsSchema } from "@lib/zodSchemas"
import status from "http-status"
import { useRouter } from "next/router"
import { useEffect, useMemo, useState } from "react"
import { toast } from "react-toastify"
import { isAuthenticated } from "../pages/start"
import { useGameProps } from "./useGameProps"
import useIndex from "./useIndex"
/** This function should only be called once per page, otherwise there will be multiple socket connections and duplicate event listeners. */
function useSocket() {
const [isConnectedState, setIsConnectedState] = useState(false)
const { selfIndex } = useIndex()
const {
payload,
userStates,
setPlayer,
setSetting,
full,
setIsReady,
gameState,
setIsConnected,
setActiveIndex,
DispatchMove,
setShips,
} = useGameProps()
const router = useRouter()
const isConnected = useMemo(
() =>
selfIndex >= 0 ? userStates[selfIndex].isConnected : isConnectedState,
[selfIndex, isConnectedState, userStates],
)
useEffect(() => {
if (selfIndex < 0) return
setIsConnected({
i: selfIndex,
isConnected: isConnectedState,
})
}, [selfIndex, isConnectedState, setIsConnected])
useEffect(() => {
socket.on("connect", () => {
console.log("connected")
toast.dismiss("connect_error")
setIsConnectedState(true)
})
socket.on("connect_error", (error) => {
console.log("Connection error:", error.message)
if (error.message === status["403"]) router.push("/")
if (error.message !== "xhr poll error") return
const toastId = "connect_error"
const isActive = toast.isActive(toastId)
console.log(toastId, isActive)
if (isActive)
toast.update(toastId, {
autoClose: 5000,
})
else
toast.warn("Es gibt Probleme mit der Echtzeitverbindung.", { toastId })
})
socket.on("gameSetting", (payload, hash) => {
const newHash = setSetting(payload)
if (!newHash || newHash === hash) return
console.log("hash", hash, newHash)
socket.emit("update", (body) => {
console.log("update")
full(body)
})
})
socket.on("playerEvent", (event) => {
const { type, i } = event
let message: string
console.log("playerEvent", type)
switch (type) {
case "disconnect":
setIsConnected({
i,
isConnected: false,
})
message = "Player is disconnected."
break
case "leave":
message = "Player has left the lobby."
break
case "connect":
setIsConnected({
i,
isConnected: true,
})
socket.emit("isReady", userStates[selfIndex].isReady)
message = "Player has joined the lobby."
break
default:
message = "Not defined yet."
break
}
toast.info(message, { toastId: message })
if (type === "disconnect") return
const { payload, hash } = event
const newHash = setPlayer(payload)
console.log(newHash, hash, !newHash, newHash === hash)
if (!newHash || newHash === hash) return
console.log("hash", hash, newHash)
socket.emit("update", (body) => {
console.log("update")
full(body)
})
})
socket.on("isReady", setIsReady)
socket.on("gameState", gameState)
socket.on("dispatchMove", DispatchMove)
socket.on("activeIndex", (i) => setActiveIndex(i, selfIndex))
socket.on("ships", setShips)
socket.on("disconnect", () => {
console.log("disconnect")
setIsConnectedState(false)
})
return () => {
socket.removeAllListeners()
}
}, [
DispatchMove,
full,
gameState,
router,
selfIndex,
setActiveIndex,
setIsConnected,
setIsReady,
setPlayer,
setSetting,
setShips,
userStates,
])
useEffect(() => {
if (!payload?.game?.id) {
socket.disconnect()
fetch("/api/game/running", {
method: "GET",
})
.then(isAuthenticated)
.then((game) => GamePropsSchema.parse(game))
.then((res) => full(res))
.catch((e) => console.log(e))
return
}
if (isConnected) return
socket.connect()
const start = Date.now()
socket.volatile.emit("ping", () => {
const duration = Date.now() - start
console.log("ping", duration)
})
}, [full, isConnected, payload?.game?.id])
return {
isConnected:
selfIndex >= 0 ? userStates[selfIndex].isConnected : isConnectedState,
}
}
export default useSocket