From 40c3624792f3f6e20b21ef6bc4b695b7dfb07a9a Mon Sep 17 00:00:00 2001 From: Dan Bastin Date: Tue, 26 Nov 2024 15:09:01 -0500 Subject: [PATCH 1/2] Game lobby (#19) Established user flow through the game lobby. Updates Game context to only be used for the lobby and gameboard and provides a helper method for game specific socket messages --- package-lock.json | 136 ++++++++++++++++++ src/app/ClientLayout.tsx | 25 +++- src/app/GameBoard/page.tsx | 4 +- .../ClientProviders/ClientProviders.tsx | 5 +- .../OpponentCardTray/OpponentCardTray.tsx | 4 +- .../PlayerCardTray/PlayerCardTray.tsx | 4 +- .../Overlays/Prompts/BasicPrompt.tsx | 12 +- .../ResourcesOverlay/ResourcesOverlay.tsx | 4 +- .../PlayerTray/CardActionTray.tsx | 12 +- .../_subcomponents/PlayerTray/Resources.tsx | 4 +- .../Gameboard/_subcomponents/UnitsBoard.tsx | 4 +- src/app/_components/Lobby/LobbyTypes.ts | 5 - src/app/_components/Lobby/SetUp/SetUp.tsx | 52 ++----- .../Cards/GameCard/GameCard.tsx | 6 +- .../Cards/LeaderBaseCard/LeaderBaseCard.tsx | 63 ++++---- .../CreateGameForm/CreateGameForm.tsx | 44 +++--- .../LeaderBaseBoard/LeaderBase/LeaderBase.tsx | 4 +- .../LeaderBaseBoard/LeaderBaseBoard.tsx | 4 +- .../{Player.context.tsx => Game.context.tsx} | 32 +++-- src/app/lobby/page.tsx | 27 ---- 20 files changed, 284 insertions(+), 167 deletions(-) rename src/app/_contexts/{Player.context.tsx => Game.context.tsx} (65%) diff --git a/package-lock.json b/package-lock.json index 2a023c14..6e8745d9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@emotion/styled": "^11.13.5", "@mui/icons-material": "^6.1.8", "@mui/material": "^6.1.8", + "html-react-parser": "^5.1.18", "next": "14.2.14", "react": "^18", "react-dom": "^18", @@ -1951,6 +1952,57 @@ "csstype": "^3.0.2" } }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -2001,6 +2053,17 @@ "node": ">=10.13.0" } }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -3125,6 +3188,53 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "license": "MIT" }, + "node_modules/html-dom-parser": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/html-dom-parser/-/html-dom-parser-5.0.10.tgz", + "integrity": "sha512-GwArYL3V3V8yU/mLKoFF7HlLBv80BZ2Ey1BzfVNRpAci0cEKhFHI/Qh8o8oyt3qlAMLlK250wsxLdYX4viedvg==", + "dependencies": { + "domhandler": "5.0.3", + "htmlparser2": "9.1.0" + } + }, + "node_modules/html-react-parser": { + "version": "5.1.18", + "resolved": "https://registry.npmjs.org/html-react-parser/-/html-react-parser-5.1.18.tgz", + "integrity": "sha512-65BwC0zzrdeW96jB2FRr5f1ovBhRMpLPJNvwkY5kA8Ay5xdL9t/RH2/uUTM7p+cl5iM88i6dDk4LXtfMnRmaJQ==", + "dependencies": { + "domhandler": "5.0.3", + "html-dom-parser": "5.0.10", + "react-property": "2.0.2", + "style-to-js": "1.1.16" + }, + "peerDependencies": { + "@types/react": "0.14 || 15 || 16 || 17 || 18", + "react": "0.14 || 15 || 16 || 17 || 18" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/htmlparser2": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", + "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.1.0", + "entities": "^4.5.0" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -3180,6 +3290,11 @@ "dev": true, "license": "ISC" }, + "node_modules/inline-style-parser": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz", + "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==" + }, "node_modules/internal-slot": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", @@ -4387,6 +4502,11 @@ "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "license": "MIT" }, + "node_modules/react-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/react-property/-/react-property-2.0.2.tgz", + "integrity": "sha512-+PbtI3VuDV0l6CleQMsx2gtK0JZbZKbpdu5ynr+lbsuvtmgbNcS3VM0tuY2QjFNOcWxvXeHjDpy42RO+4U2rug==" + }, "node_modules/react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", @@ -4987,6 +5107,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/style-to-js": { + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.16.tgz", + "integrity": "sha512-/Q6ld50hKYPH3d/r6nr117TZkHR0w0kGGIVfpG9N6D8NymRPM9RqCUv4pRpJ62E5DqOYx2AFpbZMyCPnjQCnOw==", + "dependencies": { + "style-to-object": "1.0.8" + } + }, + "node_modules/style-to-object": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.8.tgz", + "integrity": "sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g==", + "dependencies": { + "inline-style-parser": "0.2.4" + } + }, "node_modules/styled-jsx": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", diff --git a/src/app/ClientLayout.tsx b/src/app/ClientLayout.tsx index 8d14f88a..5a915762 100644 --- a/src/app/ClientLayout.tsx +++ b/src/app/ClientLayout.tsx @@ -2,6 +2,8 @@ 'use client'; import dynamic from 'next/dynamic'; +import { usePathname } from 'next/navigation'; +import { GameProvider } from "@/app/_contexts/Game.context"; const ClientProviders = dynamic( () => import('@/app/_components/ClientProviders/ClientProviders'), @@ -11,10 +13,25 @@ const ClientProviders = dynamic( const Navbar = dynamic(() => import('./Navigation/NavBar'), { ssr: false }); export default function ClientLayout({ children }: { children: React.ReactNode }) { + const path = usePathname(); + const pagesWithWebsocket = ['/GameBoard', '/lobby']; + const isPageWithWebsocket = pagesWithWebsocket.includes(path); + return ( - - - {children} - + <> + {isPageWithWebsocket ? ( + + + + {children} + + + ) : ( + + + {children} + + )} + ); } \ No newline at end of file diff --git a/src/app/GameBoard/page.tsx b/src/app/GameBoard/page.tsx index bcf84048..054f7295 100644 --- a/src/app/GameBoard/page.tsx +++ b/src/app/GameBoard/page.tsx @@ -8,11 +8,11 @@ import Board from "../_components/Gameboard/Board/Board"; import PlayerCardTray from "../_components/Gameboard/PlayerCardTray/PlayerCardTray"; import ResourcesOverlay from "../_components/Gameboard/_subcomponents/Overlays/ResourcesOverlay/ResourcesOverlay"; import BasicPrompt from "../_components/Gameboard/_subcomponents/Overlays/Prompts/BasicPrompt"; -import { usePlayer } from "../_contexts/Player.context"; +import { useGame } from "../_contexts/Game.context"; import { useSidebar } from "../_contexts/Sidebar.context"; const GameBoard = () => { - const { getOpponent, connectedPlayer, gameState } = usePlayer(); + const { getOpponent, connectedPlayer, gameState } = useGame(); const { sidebarOpen, toggleSidebar } = useSidebar(); const [chatMessage, setChatMessage] = useState(""); const [chatHistory, setChatHistory] = useState([]); diff --git a/src/app/_components/ClientProviders/ClientProviders.tsx b/src/app/_components/ClientProviders/ClientProviders.tsx index 25d3be37..4f571056 100644 --- a/src/app/_components/ClientProviders/ClientProviders.tsx +++ b/src/app/_components/ClientProviders/ClientProviders.tsx @@ -1,7 +1,6 @@ "use client"; import { SidebarProvider } from "@/app/_contexts/Sidebar.context"; -import { PlayerProvider } from "@/app/_contexts/Player.context"; import { ThemeContextProvider } from "@/app/_contexts/Theme.context"; import { UserProvider } from "@/app/_contexts/User.context"; @@ -13,9 +12,7 @@ const ClientProviders: React.FC = ({ children }) => { return ( - - {children} - + {children} ); diff --git a/src/app/_components/Gameboard/OpponentCardTray/OpponentCardTray.tsx b/src/app/_components/Gameboard/OpponentCardTray/OpponentCardTray.tsx index c3fb5db9..c2f70db7 100644 --- a/src/app/_components/Gameboard/OpponentCardTray/OpponentCardTray.tsx +++ b/src/app/_components/Gameboard/OpponentCardTray/OpponentCardTray.tsx @@ -3,7 +3,7 @@ import Grid from "@mui/material/Grid2"; import Resources from "../_subcomponents/PlayerTray/Resources"; import PlayerHand from "../_subcomponents/PlayerTray/PlayerHand"; import { OpponentCardTrayProps } from "@/app/_components/Gameboard/GameboardTypes"; -import { usePlayer } from "@/app/_contexts/Player.context"; +import { useGame } from "@/app/_contexts/Game.context"; const OpponentCardTray: React.FC = ({ trayPlayer }) => { //---------------Styles------------------- // @@ -31,7 +31,7 @@ const OpponentCardTray: React.FC = ({ trayPlayer }) => { pt: "2em", }; - const { gameState, connectedPlayer, getOpponent } = usePlayer(); + const { gameState, connectedPlayer, getOpponent } = useGame(); return ( diff --git a/src/app/_components/Gameboard/PlayerCardTray/PlayerCardTray.tsx b/src/app/_components/Gameboard/PlayerCardTray/PlayerCardTray.tsx index 455f909d..72fd91ae 100644 --- a/src/app/_components/Gameboard/PlayerCardTray/PlayerCardTray.tsx +++ b/src/app/_components/Gameboard/PlayerCardTray/PlayerCardTray.tsx @@ -5,14 +5,14 @@ import Resources from "../_subcomponents/PlayerTray/Resources"; import CardActionTray from "../_subcomponents/PlayerTray/CardActionTray"; import PlayerHand from "../_subcomponents/PlayerTray/PlayerHand"; import { PlayerCardTrayProps } from "@/app/_components/Gameboard/GameboardTypes"; -import { usePlayer } from "@/app/_contexts/Player.context"; +import { useGame } from "@/app/_contexts/Game.context"; const PlayerCardTray: React.FC = ({ trayPlayer, handleModalToggle, }) => { // -------------- Contexts ---------------- // - const { gameState, connectedPlayer } = usePlayer(); + const { gameState, connectedPlayer } = useGame(); //---------------Styles------------------- // const leftColumnStyle = { diff --git a/src/app/_components/Gameboard/_subcomponents/Overlays/Prompts/BasicPrompt.tsx b/src/app/_components/Gameboard/_subcomponents/Overlays/Prompts/BasicPrompt.tsx index 9c891142..f5345a41 100644 --- a/src/app/_components/Gameboard/_subcomponents/Overlays/Prompts/BasicPrompt.tsx +++ b/src/app/_components/Gameboard/_subcomponents/Overlays/Prompts/BasicPrompt.tsx @@ -11,14 +11,14 @@ import { Button, } from "@mui/material"; import { Close } from "@mui/icons-material"; -import { usePlayer } from "@/app/_contexts/Player.context"; +import { useGame } from "@/app/_contexts/Game.context"; import { BasicPromptProps } from "@/app/_components/Gameboard/GameboardTypes"; const BasicPrompt: React.FC = ({ isBasicPromptOpen, handleBasicPromptToggle, }) => { - const { connectedPlayer, gameState, sendMessage } = usePlayer(); + const { connectedPlayer, gameState, sendGameMessage } = useGame(); if (!gameState) { return null; } @@ -58,7 +58,7 @@ const BasicPrompt: React.FC = ({ ))} @@ -84,7 +84,7 @@ const BasicPrompt: React.FC = ({ interface PromptButtonProps { button: ButtonsProps - sendMessage: (args: [string, string, string]) => void; + sendGameMessage: (args: [string, string, string]) => void; } interface ButtonsProps { @@ -94,11 +94,11 @@ interface ButtonsProps { uuid: string; } -const PromptButton: React.FC = ({ button, sendMessage }) => { +const PromptButton: React.FC = ({ button, sendGameMessage }) => { return ( diff --git a/src/app/_components/Gameboard/_subcomponents/Overlays/ResourcesOverlay/ResourcesOverlay.tsx b/src/app/_components/Gameboard/_subcomponents/Overlays/ResourcesOverlay/ResourcesOverlay.tsx index d7be7e49..455d36d8 100644 --- a/src/app/_components/Gameboard/_subcomponents/Overlays/ResourcesOverlay/ResourcesOverlay.tsx +++ b/src/app/_components/Gameboard/_subcomponents/Overlays/ResourcesOverlay/ResourcesOverlay.tsx @@ -12,14 +12,14 @@ import { import { Close } from "@mui/icons-material"; import CardArea from "../../../../_sharedcomponents/CardArea/CardArea"; import { ResourcesOverlayProps } from "@/app/_components/Gameboard/GameboardTypes"; -import { usePlayer } from "@/app/_contexts/Player.context"; +import { useGame } from "@/app/_contexts/Game.context"; const ResourcesOverlay: React.FC = ({ isModalOpen, handleModalToggle, }) => { - const { gameState, connectedPlayer } = usePlayer(); + const { gameState, connectedPlayer } = useGame(); return ( { //------------------------STYLES------------------------// @@ -9,7 +9,7 @@ const CardActionTray: React.FC = () => { mt: "1vh", }; - const { sendMessage, gameState, connectedPlayer } = usePlayer(); + const { sendGameMessage, gameState, connectedPlayer } = useGame(); const playerState = gameState.players[connectedPlayer]; return ( @@ -29,7 +29,7 @@ const CardActionTray: React.FC = () => { ))} @@ -40,7 +40,7 @@ const CardActionTray: React.FC = () => { interface PromptButtonProps { button: ButtonsProps - sendMessage: (args: [string, string, string]) => void; + sendGameMessage: (args: [string, string, string]) => void; } interface ButtonsProps { @@ -50,11 +50,11 @@ interface ButtonsProps { uuid: string; } -const PromptButton: React.FC = ({ button, sendMessage }) => { +const PromptButton: React.FC = ({ button, sendGameMessage }) => { return ( diff --git a/src/app/_components/Gameboard/_subcomponents/PlayerTray/Resources.tsx b/src/app/_components/Gameboard/_subcomponents/PlayerTray/Resources.tsx index db33605c..e68b2ff0 100644 --- a/src/app/_components/Gameboard/_subcomponents/PlayerTray/Resources.tsx +++ b/src/app/_components/Gameboard/_subcomponents/PlayerTray/Resources.tsx @@ -2,7 +2,7 @@ import React from "react"; import { Card, CardContent, Box, Typography } from "@mui/material"; import Image from "next/image"; import { ResourcesProps } from "@/app/_components/Gameboard/GameboardTypes"; -import { usePlayer } from "@/app/_contexts/Player.context"; +import { useGame } from "@/app/_contexts/Game.context"; const Resources: React.FC = ({ trayPlayer, @@ -48,7 +48,7 @@ const Resources: React.FC = ({ color: "white", }; - const { gameState } = usePlayer(); + const { gameState } = useGame(); const availableResources = gameState.players[trayPlayer].availableResources; const totalResources = gameState.players[trayPlayer].cardPiles.resources.length; diff --git a/src/app/_components/Gameboard/_subcomponents/UnitsBoard.tsx b/src/app/_components/Gameboard/_subcomponents/UnitsBoard.tsx index 8a6957b1..da9390f0 100644 --- a/src/app/_components/Gameboard/_subcomponents/UnitsBoard.tsx +++ b/src/app/_components/Gameboard/_subcomponents/UnitsBoard.tsx @@ -3,7 +3,7 @@ import { Box, Grid2 as Grid } from "@mui/material"; import GameCard from "../../_sharedcomponents/Cards/GameCard/GameCard"; import { CardData } from "../../_sharedcomponents/Cards/CardTypes"; import { UnitsBoardProps } from "@/app/_components/Gameboard/GameboardTypes"; -import { usePlayer } from "@/app/_contexts/Player.context"; +import { useGame } from "@/app/_contexts/Game.context"; const UnitsBoard: React.FC = ({ sidebarOpen, @@ -47,7 +47,7 @@ const UnitsBoard: React.FC = ({ }; //------------------------CONTEXT------------------------// - const { gameState, connectedPlayer, getOpponent } = usePlayer(); + const { gameState, connectedPlayer, getOpponent } = useGame(); const playerUnits = gameState?.players[connectedPlayer].cardPiles[arena]; const opponentUnits = gameState?.players[getOpponent(connectedPlayer)].cardPiles[arena]; diff --git a/src/app/_components/Lobby/LobbyTypes.ts b/src/app/_components/Lobby/LobbyTypes.ts index 61e8678c..a64abd3b 100644 --- a/src/app/_components/Lobby/LobbyTypes.ts +++ b/src/app/_components/Lobby/LobbyTypes.ts @@ -5,12 +5,7 @@ export interface PlayersProps { export interface SetUpProps { chatHistory: string[]; chatMessage: string; - playerRoll: number; - opponentRoll: number; - isRolling: boolean; - isRollSame: boolean; setChatMessage: (message: string) => void; handleChatSubmit: () => void; - handleRollInitiative: () => void; } diff --git a/src/app/_components/Lobby/SetUp/SetUp.tsx b/src/app/_components/Lobby/SetUp/SetUp.tsx index ca44e33c..bef1d584 100644 --- a/src/app/_components/Lobby/SetUp/SetUp.tsx +++ b/src/app/_components/Lobby/SetUp/SetUp.tsx @@ -1,43 +1,30 @@ -import React, { useEffect, useRef } from "react"; import { Card, Typography, - CardContent, CardActions, + Button } from "@mui/material"; import Chat from "@/app/_components/_sharedcomponents/Chat/Chat"; import GameLinkCard from "../_subcomponents/GameLinkCard/GameLinkCard"; +import { useGame } from "@/app/_contexts/Game.context"; import { SetUpProps } from "../LobbyTypes"; +import { useRouter } from "next/navigation" const SetUp: React.FC = ({ chatHistory, chatMessage, - playerRoll, - opponentRoll, - isRolling, - isRollSame, setChatMessage, handleChatSubmit, - handleRollInitiative, }) => { - const getInitiativeMessage = () => { - if (isRolling) { - return "Rolling initiative..."; - } - if (isRollSame) { - return "Initiative tied, rolling initiative again"; - } - return ""; - }; - const hasRolled = useRef(false); + const { sendMessage } = useGame(); + const router = useRouter(); + - useEffect(() => { - if (!hasRolled.current) { - handleRollInitiative(); - hasRolled.current = true; - } - }, [handleRollInitiative]); + const handleStartGame = () => { + sendMessage("startGame"); + router.push("/GameBoard"); + } //------------------------STYLES------------------------// @@ -62,14 +49,6 @@ const SetUp: React.FC = ({ justifyContent: "center", }; - const initiativeMessageStyle = { - fontSize: "1.5em", - fontWeight: "400", - color: "white", - textAlign: "center", - width: "100%", - }; - const buttonsContainerStyle = { display: "flex", justifyContent: "center", @@ -87,15 +66,8 @@ const SetUp: React.FC = ({ return ( - - {hasRolled.current && getInitiativeMessage() && ( - - {getInitiativeMessage()} - - )} - - + Set Up @@ -106,8 +78,6 @@ const SetUp: React.FC = ({ chatMessage={chatMessage} setChatMessage={setChatMessage} handleChatSubmit={handleChatSubmit} - playerRoll={playerRoll} - opponentRoll={opponentRoll} /> ); diff --git a/src/app/_components/_sharedcomponents/Cards/GameCard/GameCard.tsx b/src/app/_components/_sharedcomponents/Cards/GameCard/GameCard.tsx index 6ee43b12..b584083e 100644 --- a/src/app/_components/_sharedcomponents/Cards/GameCard/GameCard.tsx +++ b/src/app/_components/_sharedcomponents/Cards/GameCard/GameCard.tsx @@ -8,7 +8,7 @@ import { } from "@mui/material"; import Image from "next/image"; import { GameCardProps, CardData } from "@/app/_components/_sharedcomponents/Cards/CardTypes"; -import { usePlayer } from "@/app/_contexts/Player.context"; +import { useGame } from "@/app/_contexts/Game.context"; const GameCard: React.FC = ({ card @@ -90,7 +90,7 @@ const GameCard: React.FC = ({ fontSize: isLobbyView ? "2em" : "1.6em", }; - const { sendMessage } = usePlayer(); + const { sendGameMessage } = useGame(); const cardBorderColor = (card: CardData) => { if (card.selected) return "yellow"; @@ -103,7 +103,7 @@ const GameCard: React.FC = ({ { if (card.selectable) { - sendMessage(["cardClicked", card.uuid]); + sendGameMessage(["cardClicked", card.uuid]); } }} > diff --git a/src/app/_components/_sharedcomponents/Cards/LeaderBaseCard/LeaderBaseCard.tsx b/src/app/_components/_sharedcomponents/Cards/LeaderBaseCard/LeaderBaseCard.tsx index 1c3feb31..34009825 100644 --- a/src/app/_components/_sharedcomponents/Cards/LeaderBaseCard/LeaderBaseCard.tsx +++ b/src/app/_components/_sharedcomponents/Cards/LeaderBaseCard/LeaderBaseCard.tsx @@ -8,7 +8,7 @@ import { } from "@mui/material"; import { LeaderBaseCardProps } from "../CardTypes"; import { CardData } from "../CardTypes"; -import { usePlayer } from "@/app/_contexts/Player.context"; +import { useGame } from "@/app/_contexts/Game.context"; const LeaderBaseCard: React.FC = ({ @@ -18,6 +18,7 @@ const LeaderBaseCard: React.FC = ({ card }) => { const cardBorderColor = (card: CardData) => { + if (!card) return ""; if (card.selected) return "yellow"; if (card.selectable) return "green"; if (card.exhausted) return "gray"; @@ -79,7 +80,7 @@ const LeaderBaseCard: React.FC = ({ fontSize: "1em", }; - const { sendMessage } = usePlayer(); + const { sendGameMessage } = useGame(); return ( @@ -90,34 +91,40 @@ const LeaderBaseCard: React.FC = ({ )} - { - if (card.selectable) { - sendMessage(["cardClicked", card.uuid]); - } - }} - > - - - - {card.damage} + {isLobbyView ? ( + + ) : ( + { + if (card.selectable) { + sendGameMessage(["cardClicked", card.uuid]); + } + }} + > + + + + {card.damage} + + + {card.name} + + + + + {/* Show title inside a red box at the bottom if not in lobby view and variant is leader */} + {variant === "leader" && !isLobbyView && title && ( + + + {title} + - - {card.name} - - - + )} + + )} - {/* Show title inside a red box at the bottom if not in lobby view and variant is leader */} - {variant === "leader" && !isLobbyView && title && ( - - - {title} - - - )} - + ); }; diff --git a/src/app/_components/_sharedcomponents/CreateGameForm/CreateGameForm.tsx b/src/app/_components/_sharedcomponents/CreateGameForm/CreateGameForm.tsx index 3b844c45..6c348158 100644 --- a/src/app/_components/_sharedcomponents/CreateGameForm/CreateGameForm.tsx +++ b/src/app/_components/_sharedcomponents/CreateGameForm/CreateGameForm.tsx @@ -13,7 +13,7 @@ import { RadioGroup, } from "@mui/material"; import StyledTextField from "../_styledcomponents/StyledTextField/StyledTextField"; -import { usePathname } from "next/navigation"; +import { usePathname, useRouter } from "next/navigation"; interface CreateGameFormProps { format?: string | null; @@ -21,10 +21,8 @@ interface CreateGameFormProps { } const deckOptions: string[] = [ - "Vader Green Ramp", - "Obi-Wan Blue Control", - "Darth Red Aggro", - "Leia White Midrange", + "Order66", + "ThisIsTheWay", ]; const formatOptions: string[] = ["Premier", "Twin Suns", "Draft", "Sealed"]; @@ -34,11 +32,12 @@ const CreateGameForm: React.FC = ({ setFormat, }) => { const pathname = usePathname(); + const router = useRouter(); const isCreateGamePath = pathname === "/creategame"; // Common State const [favouriteDeck, setFavouriteDeck] = - useState("Vader Green Ramp"); + useState("Order66"); const [deckLink, setDeckLink] = useState(""); const [saveDeck, setSaveDeck] = useState(false); @@ -47,19 +46,33 @@ const CreateGameForm: React.FC = ({ const [privacy, setPrivacy] = useState("Public"); // Handle Create Game Submission - const handleCreateGameSubmit = (event: FormEvent) => { + const handleCreateGameSubmit = async (event: FormEvent) => { event.preventDefault(); - console.log("Favourite Deck:", favouriteDeck); - console.log("SWUDB Deck Link:", deckLink); - console.log("Save Deck To Favourites:", saveDeck); - if (!isCreateGamePath) { - console.log("Game Name:", gameName); - console.log("Format:", format); - console.log("Privacy:", privacy); + try { + const payload = { + user: favouriteDeck + }; + const response = await fetch("http://localhost:9500/api/create-lobby", + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(payload), + } + ); + + if (!response.ok) { + throw new Error("Failed to create game"); + } + + router.push("/lobby"); + + } catch (error) { + console.error(error); } - // TODO: Implement actual game creation logic here }; //------------------------STYLES------------------------// @@ -173,7 +186,6 @@ const CreateGameForm: React.FC = ({ onChange={(e: ChangeEvent) => setDeckLink(e.target.value) } - required /> diff --git a/src/app/_components/_sharedcomponents/LeaderBaseBoard/LeaderBase/LeaderBase.tsx b/src/app/_components/_sharedcomponents/LeaderBaseBoard/LeaderBase/LeaderBase.tsx index 37973040..611a2556 100644 --- a/src/app/_components/_sharedcomponents/LeaderBaseBoard/LeaderBase/LeaderBase.tsx +++ b/src/app/_components/_sharedcomponents/LeaderBaseBoard/LeaderBase/LeaderBase.tsx @@ -2,7 +2,7 @@ import React from "react"; import Grid from "@mui/material/Grid2"; import LeaderBaseCard from "../../Cards/LeaderBaseCard/LeaderBaseCard"; import { LeaderBaseProps } from "../LeaderBaseBoardTypes"; -import { usePlayer } from "@/app/_contexts/Player.context"; +import { useGame } from "@/app/_contexts/Game.context"; const LeaderBase: React.FC = ({ player, @@ -31,7 +31,7 @@ const LeaderBase: React.FC = ({ // : 0, }; - const { gameState, connectedPlayer } = usePlayer(); + const { gameState, connectedPlayer } = useGame(); const playerLeader = gameState?.players[player].leader; const playerBase = gameState?.players[player].base; diff --git a/src/app/_components/_sharedcomponents/LeaderBaseBoard/LeaderBaseBoard.tsx b/src/app/_components/_sharedcomponents/LeaderBaseBoard/LeaderBaseBoard.tsx index c3614d34..adb1b7c6 100644 --- a/src/app/_components/_sharedcomponents/LeaderBaseBoard/LeaderBaseBoard.tsx +++ b/src/app/_components/_sharedcomponents/LeaderBaseBoard/LeaderBaseBoard.tsx @@ -1,14 +1,14 @@ import React from "react"; import Grid from "@mui/material/Grid2"; import LeaderBase from "./LeaderBase/LeaderBase"; -import { usePlayer } from "@/app/_contexts/Player.context"; +import { useGame } from "@/app/_contexts/Game.context"; import { LeaderBaseBoardProps } from "./LeaderBaseBoardTypes"; const LeaderBaseBoard: React.FC = ({ isLobbyView, }) => { - const { connectedPlayer, getOpponent } = usePlayer(); + const { connectedPlayer, getOpponent } = useGame(); const titleOpponent = connectedPlayer === "ThisIsTheWay" ? "Order66" : "ThisIsTheWay"; //------------------------STYLES------------------------// diff --git a/src/app/_contexts/Player.context.tsx b/src/app/_contexts/Game.context.tsx similarity index 65% rename from src/app/_contexts/Player.context.tsx rename to src/app/_contexts/Game.context.tsx index 0a9ed864..2db34202 100644 --- a/src/app/_contexts/Player.context.tsx +++ b/src/app/_contexts/Game.context.tsx @@ -1,4 +1,4 @@ -// contexts/PlayerContext.tsx +// contexts/GameContext.tsx /* eslint-disable @typescript-eslint/no-explicit-any */ "use client"; import React, { @@ -10,20 +10,22 @@ import React, { } from "react"; import io, { Socket } from "socket.io-client"; -interface PlayerContextType { +interface GameContextType { gameState: any; - sendMessage: (args: any[]) => void; + sendMessage: (message: string, args?: any[]) => void; + sendGameMessage: (args: any[]) => void; getOpponent: (player: string) => string; connectedPlayer: string; } -const PlayerContext = createContext(undefined); +const GameContext = createContext(undefined); -export const PlayerProvider = ({ children }: { children: ReactNode }) => { +export const GameProvider = ({ children }: { children: ReactNode }) => { const [gameState, setGameState] = useState(null); const [socket, setSocket] = useState(undefined); const [connectedPlayer, setConnectedPlayer] = useState(""); + useEffect(() => { const urlParams = new URLSearchParams(window.location.search); const playerName = urlParams.get("player") || "Order66"; @@ -51,33 +53,41 @@ export const PlayerProvider = ({ children }: { children: ReactNode }) => { }; }, []); - const sendMessage = (args: any[]) => { + const sendMessage = (message: string, args: any[] = []) => { + console.log("sending message", message, args); + socket?.emit(message, ...args); + }; + + const sendGameMessage = (args: any[]) => { + console.log("sending game message", args); socket?.emit("game", ...args); }; const getOpponent = (player: string) => { + if (!gameState) return ""; const playerNames = Object.keys(gameState.players); return playerNames.find((name) => name !== player) || ""; }; return ( - {children} - + ); }; -export const usePlayer = () => { - const context = useContext(PlayerContext); +export const useGame = () => { + const context = useContext(GameContext); if (!context) { - throw new Error("usePlayer must be used within a PlayerProvider"); + throw new Error("useGame must be used within a GameProvider"); } return context; }; diff --git a/src/app/lobby/page.tsx b/src/app/lobby/page.tsx index 07c0f1a8..d0161185 100644 --- a/src/app/lobby/page.tsx +++ b/src/app/lobby/page.tsx @@ -13,10 +13,6 @@ const Lobby = () => { const [chatMessage, setChatMessage] = useState(""); const [chatHistory, setChatHistory] = useState([]); - const [playerRoll, setPlayerRoll] = useState(0); - const [isRolling, setIsRolling] = useState(false); - const [isRollSame, setIsRollSame] = useState(false); - const [opponentRoll, setOpponentRoll] = useState(0); const handleChatSubmit = () => { if (chatMessage.trim()) { @@ -25,24 +21,6 @@ const Lobby = () => { } }; - const handleRollInitiative = () => { - setIsRolling(true); - setIsRollSame(false); - setTimeout(() => { - const playerDieRoll = Math.floor(Math.random() * 20) + 1; - const opponentDieRoll = Math.floor(Math.random() * 20) + 1; - if (playerDieRoll === opponentDieRoll) { - handleRollInitiative(); - setIsRollSame(true); - setIsRolling(false); - return; - } - setPlayerRoll(playerDieRoll); - setOpponentRoll(opponentDieRoll) - - setIsRolling(false); - }, Math.random() * 3000 + 2000); - }; //------------------------STYLES------------------------// @@ -77,11 +55,6 @@ const Lobby = () => { chatHistory={chatHistory} handleChatSubmit={handleChatSubmit} setChatMessage={setChatMessage} - handleRollInitiative={handleRollInitiative} - playerRoll={playerRoll} - opponentRoll={opponentRoll} - isRolling={isRolling} - isRollSame={isRollSame} /> From 1ed359f7c04702a18c3fb0dc9077149c413f799f Mon Sep 17 00:00:00 2001 From: Ruben Ag <108239354+wanderwayward@users.noreply.github.com> Date: Tue, 26 Nov 2024 14:18:19 -0600 Subject: [PATCH 2/2] Google Auth is set up (#17) You need to have the secret and clientid in your .env.local do I paste them here or google chat? --------- Co-authored-by: Dan Bastin --- package-lock.json | 268 +++++++++--------- package.json | 1 + src/app/Navigation/NavBar.tsx | 43 ++- src/app/_components/Auth/AuthTypes.ts | 22 +- src/app/_components/Auth/Login/Login.tsx | 261 ++++------------- src/app/_components/Auth/SignUp/SignUp.tsx | 160 ----------- .../ClientProviders/ClientProviders.tsx | 13 +- .../ControlHub/ControlHub.tsx | 166 ++++++----- .../ControlHub/ControlHubTypes.ts | 4 +- src/app/_contexts/User.context.tsx | 42 ++- src/app/_contexts/UserTypes.ts | 13 + src/app/api/auth/[...nextauth]/route.ts | 43 +++ src/app/auth/page.tsx | 90 +----- src/global.d.ts | 5 - src/next-auth.d.ts | 24 ++ 15 files changed, 428 insertions(+), 727 deletions(-) delete mode 100644 src/app/_components/Auth/SignUp/SignUp.tsx create mode 100644 src/app/_contexts/UserTypes.ts create mode 100644 src/app/api/auth/[...nextauth]/route.ts create mode 100644 src/next-auth.d.ts diff --git a/package-lock.json b/package-lock.json index 6e8745d9..697f093e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "@mui/material": "^6.1.8", "html-react-parser": "^5.1.18", "next": "14.2.14", + "next-auth": "^4.24.10", "react": "^18", "react-dom": "^18", "react-icons": "^5.3.0", @@ -765,134 +766,6 @@ "glob": "10.3.10" } }, - "node_modules/@next/swc-darwin-arm64": { - "version": "14.2.14", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.14.tgz", - "integrity": "sha512-bsxbSAUodM1cjYeA4o6y7sp9wslvwjSkWw57t8DtC8Zig8aG8V6r+Yc05/9mDzLKcybb6EN85k1rJDnMKBd9Gw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-darwin-x64": { - "version": "14.2.14", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.14.tgz", - "integrity": "sha512-cC9/I+0+SK5L1k9J8CInahduTVWGMXhQoXFeNvF0uNs3Bt1Ub0Azb8JzTU9vNCr0hnaMqiWu/Z0S1hfKc3+dww==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-gnu": { - "version": "14.2.14", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.14.tgz", - "integrity": "sha512-RMLOdA2NU4O7w1PQ3Z9ft3PxD6Htl4uB2TJpocm+4jcllHySPkFaUIFacQ3Jekcg6w+LBaFvjSPthZHiPmiAUg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-musl": { - "version": "14.2.14", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.14.tgz", - "integrity": "sha512-WgLOA4hT9EIP7jhlkPnvz49iSOMdZgDJVvbpb8WWzJv5wBD07M2wdJXLkDYIpZmCFfo/wPqFsFR4JS4V9KkQ2A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-gnu": { - "version": "14.2.14", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.14.tgz", - "integrity": "sha512-lbn7svjUps1kmCettV/R9oAvEW+eUI0lo0LJNFOXoQM5NGNxloAyFRNByYeZKL3+1bF5YE0h0irIJfzXBq9Y6w==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-musl": { - "version": "14.2.14", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.14.tgz", - "integrity": "sha512-7TcQCvLQ/hKfQRgjxMN4TZ2BRB0P7HwrGAYL+p+m3u3XcKTraUFerVbV3jkNZNwDeQDa8zdxkKkw2els/S5onQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-arm64-msvc": { - "version": "14.2.14", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.14.tgz", - "integrity": "sha512-8i0Ou5XjTLEje0oj0JiI0Xo9L/93ghFtAUYZ24jARSeTMXLUx8yFIdhS55mTExq5Tj4/dC2fJuaT4e3ySvXU1A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-ia32-msvc": { - "version": "14.2.14", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.14.tgz", - "integrity": "sha512-2u2XcSaDEOj+96eXpyjHjtVPLhkAFw2nlaz83EPeuK4obF+HmtDJHqgR1dZB7Gb6V/d55FL26/lYVd0TwMgcOQ==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/@next/swc-win32-x64-msvc": { "version": "14.2.14", "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.14.tgz", @@ -957,6 +830,14 @@ "node": ">=12.4.0" } }, + "node_modules/@panva/hkdf": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.2.1.tgz", + "integrity": "sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -1771,6 +1652,14 @@ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", "license": "MIT" }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/cosmiconfig": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", @@ -3760,6 +3649,14 @@ "@pkgjs/parseargs": "^0.11.0" } }, + "node_modules/jose": { + "version": "4.15.9", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", + "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -4077,6 +3974,42 @@ } } }, + "node_modules/next-auth": { + "version": "4.24.10", + "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-4.24.10.tgz", + "integrity": "sha512-8NGqiRO1GXBcVfV8tbbGcUgQkAGsX4GRzzXXea4lDikAsJtD5KiEY34bfhUOjHLvr6rT6afpcxw2H8EZqOV6aQ==", + "dependencies": { + "@babel/runtime": "^7.20.13", + "@panva/hkdf": "^1.0.2", + "cookie": "^0.7.0", + "jose": "^4.15.5", + "oauth": "^0.9.15", + "openid-client": "^5.4.0", + "preact": "^10.6.3", + "preact-render-to-string": "^5.1.19", + "uuid": "^8.3.2" + }, + "peerDependencies": { + "@auth/core": "0.34.2", + "next": "^12.2.5 || ^13 || ^14 || ^15", + "nodemailer": "^6.6.5", + "react": "^17.0.2 || ^18", + "react-dom": "^17.0.2 || ^18" + }, + "peerDependenciesMeta": { + "@auth/core": { + "optional": true + }, + "nodemailer": { + "optional": true + } + } + }, + "node_modules/oauth": { + "version": "0.9.15", + "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", + "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==" + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -4086,6 +4019,14 @@ "node": ">=0.10.0" } }, + "node_modules/object-hash": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", + "engines": { + "node": ">= 6" + } + }, "node_modules/object-inspect": { "version": "1.13.3", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", @@ -4195,6 +4136,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/oidc-token-hash": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.3.tgz", + "integrity": "sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw==", + "engines": { + "node": "^10.13.0 || >=12.0.0" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -4205,6 +4154,31 @@ "wrappy": "1" } }, + "node_modules/openid-client": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.7.1.tgz", + "integrity": "sha512-jDBPgSVfTnkIh71Hg9pRvtJc6wTwqjRkN88+gCFtYWrlP4Yx2Dsrow8uPi3qLr/aeymPF3o2+dS+wOpglK04ew==", + "dependencies": { + "jose": "^4.15.9", + "lru-cache": "^6.0.0", + "object-hash": "^2.2.0", + "oidc-token-hash": "^5.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/openid-client/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -4404,6 +4378,26 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/preact": { + "version": "10.25.0", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.25.0.tgz", + "integrity": "sha512-6bYnzlLxXV3OSpUxLdaxBmE7PMOu0aR3pG6lryK/0jmvcDFPlcXGQAt5DpK3RITWiDrfYZRI0druyaK/S9kYLg==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/preact-render-to-string": { + "version": "5.2.6", + "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.6.tgz", + "integrity": "sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==", + "dependencies": { + "pretty-format": "^3.8.0" + }, + "peerDependencies": { + "preact": ">=10" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -4414,6 +4408,11 @@ "node": ">= 0.8.0" } }, + "node_modules/pretty-format": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz", + "integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==" + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -5389,6 +5388,14 @@ "punycode": "^2.1.0" } }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -5635,6 +5642,11 @@ "node": ">=0.4.0" } }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/yaml": { "version": "1.10.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", diff --git a/package.json b/package.json index dc568ae6..5b787b31 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "@mui/material": "^6.1.8", "html-react-parser": "^5.1.18", "next": "14.2.14", + "next-auth": "^4.24.10", "react": "^18", "react-dom": "^18", "react-icons": "^5.3.0", diff --git a/src/app/Navigation/NavBar.tsx b/src/app/Navigation/NavBar.tsx index 4dc2b4a5..ce6059a1 100644 --- a/src/app/Navigation/NavBar.tsx +++ b/src/app/Navigation/NavBar.tsx @@ -3,41 +3,40 @@ import { usePathname } from "next/navigation"; import { Typography, Grid2 as Grid } from "@mui/material"; import ControlHub from "../_components/_sharedcomponents/ControlHub/ControlHub"; import { useSidebar } from "../_contexts/Sidebar.context"; -import { useUser } from "../_contexts/User.context"; // Import UserContext +import { useUser } from "../_contexts/User.context"; const Navbar = () => { - const { user, logout } = useUser(); // Get user and logout from UserContext + const { user, logout } = useUser(); const pathname = usePathname(); const { sidebarOpen, toggleSidebar } = useSidebar(); - //------------------------STYLES------------------------// - - const navbarContainerStyle = { - position: "fixed", - top: 0, - left: 0, - right: 0, - zIndex: 1100, - width: "100%", - }; - - const gameLobbyTextStyle = { - fontFamily: "var(--font-barlow), sans-serif", - fontSize: "2.6em", - fontWeight: "600", - color: "#fff", - ml: ".5vw", + // ---------------------- Styles ---------------------- // + const navbarStyles = { + container: { + position: "fixed", + top: 0, + left: 0, + right: 0, + zIndex: 1100, + width: "100%", + }, + gameLobbyText: { + fontFamily: "var(--font-barlow), sans-serif", + fontSize: "2.6em", + fontWeight: "600", + color: "#fff", + ml: ".5vw", + }, }; - return ( {pathname === "/lobby" && ( - + GAME LOBBY )} diff --git a/src/app/_components/Auth/AuthTypes.ts b/src/app/_components/Auth/AuthTypes.ts index d0509e1b..b8e71b8f 100644 --- a/src/app/_components/Auth/AuthTypes.ts +++ b/src/app/_components/Auth/AuthTypes.ts @@ -1,27 +1,7 @@ import React from "react"; export interface LoginProps { - username: string; - setUsername: (value: string) => void; - password: string; - setPassword: (value: string) => void; - rememberMe: boolean; - setRememberMe: (value: boolean) => void; - handleSubmit: (event: React.FormEvent) => void; - toggleAuth: () => void; -} - -export interface SignUpProps { - email: string; - setEmail: (value: string) => void; - password: string; - setPassword: (value: string) => void; - confirmPassword: string; - setConfirmPassword: (value: string) => void; - passwordError: boolean; - passwordMatchError: boolean; - handleSubmit: (event: React.FormEvent) => void; - toggleAuth: () => void; + handleSubmit: (provider: "google" | "discord") => void; } export interface StyledTextFieldProps { diff --git a/src/app/_components/Auth/Login/Login.tsx b/src/app/_components/Auth/Login/Login.tsx index e0c2bd78..025302d1 100644 --- a/src/app/_components/Auth/Login/Login.tsx +++ b/src/app/_components/Auth/Login/Login.tsx @@ -1,223 +1,66 @@ -import React, { ChangeEvent } from "react"; -import { - Box, - Button, - Card, - CardContent, - Checkbox, - FormControl, - FormControlLabel, - Typography, -} from "@mui/material"; -import Link from "next/link"; -import StyledTextField from "../../_sharedcomponents/_styledcomponents/StyledTextField/StyledTextField"; -import { LoginProps } from "@/app/_components/Auth/AuthTypes"; +"use client"; -const Login: React.FC = ({ - username, - setUsername, - password, - setPassword, - rememberMe, - setRememberMe, - handleSubmit, - toggleAuth, -}) => { - //------------------------STYLES------------------------// - - const containerStyle = { - display: "flex", - flexDirection: "column", - alignItems: "center", - pt: "12em", - }; - - const primaryCardStyle = { - width: { xs: "90vw", sm: "70vw", md: "60vw", lg: "30vw" }, - p: "2.5em", - borderRadius: "1.5em", - backgroundColor: "#000000E6", - backdropFilter: "blur(20px)", - mb: "2em", - }; - - const headingStyle = { - fontFamily: "var(--font-barlow), sans-serif", - fontWeight: "800", - fontSize: "2em", - color: "#fff", - mb: ".5em", - }; - const formControlStyle = { - mb: ".5em", - }; - - const labelTextStyle = { - fontFamily: "var(--font-barlow), sans-serif", - fontSize: "1.3em", - color: "#fff", - mb: ".5em", - }; +import React from "react"; +import { Box, Button, Card, CardContent, Typography } from "@mui/material"; +import { LoginProps } from "../AuthTypes"; - const checkboxStyle = { - color: "#fff", - "&.Mui-checked": { +const Login: React.FC = ({ handleSubmit }) => { + //------------------------STYLES------------------------// + const loginStyles = { + container: { + display: "flex", + flexDirection: "column", + alignItems: "center", + pt: "12em", + }, + primaryCard: { + width: { xs: "90vw", sm: "70vw", md: "60vw", lg: "30vw" }, + p: "2.5em", + borderRadius: "1.5em", + backgroundColor: "#000000E6", + backdropFilter: "blur(20px)", + mb: "2em", + }, + heading: { + fontFamily: "var(--font-barlow), sans-serif", + fontWeight: "800", + fontSize: "2em", color: "#fff", + mb: ".5em", }, - }; - - const checkboxLabelStyle = { - color: "#fff", - fontSize: "1rem", - }; - - const submitButtonStyle = { - display: "block", - width: "10em", - height: "3em", - borderRadius: "0.5em", - backgroundColor: "#292929", - fontFamily: "var(--font-barlow), sans-serif", - fontSize: "1.2em", - ml: "auto", - mr: "auto", - mb: ".8em", - "&:hover": { - backgroundColor: "#3a3a3a", + button: { + width: "100%", + height: "3em", + borderRadius: "0.5em", + fontFamily: "var(--font-barlow), sans-serif", + fontSize: "1.2em", + mb: "1em", }, }; - const forgotPasswordStyle = { - color: "#fff", - fontSize: ".5rem", - textDecoration: "underline", - }; - - const toggleSignUpTextStyle = { - color: "#fff", - fontSize: ".7rem", - }; - - const toggleButtonStyle = { - color: "#1976d2", - textDecoration: "underline", - fontSize: ".7rem", - }; - - const secondaryCardStyle = { - borderRadius: "1.5em", - backgroundColor: "#18325199", - width: { xs: "90vw", sm: "70vw", md: "40vw", lg: "25vw" }, - boxShadow: "0 4px 20px rgba(0,0,0,0.3)", - p: "2em", - }; - - const secondaryTextStyle = { - fontFamily: "var(--font-barlow), sans-serif", - fontSize: "1em", - textAlign: "left", - color: "#fff", - mb: 2, - }; - - const privacyPolicyLinkStyle = { - fontFamily: "var(--font-barlow), sans-serif", - fontSize: "1em", - textDecoration: "underline", - color: "#fff", - }; - - //------------------------RETURN------------------------// - return ( - - {/* Primary Card - Login Form */} - + + - + Login -
- {/* Username Input */} - - Username - ) => - setUsername(e.target.value) - } - required - /> - - {/* Password Input */} - - Password - ) => - setPassword(e.target.value) - } - required - /> - - {/* Remember Me Checkbox */} - , - checked: boolean - ) => setRememberMe(checked)} - /> - } - label={ - Remember Me - } - sx={{ mb: 3 }} - /> - {/* Submit Button */} - - - {/* Forgot Password Link */} - - - Forgot Password? - - - {/* Toggle to Sign Up */} - - - Don't have an account? - - - -
-
- - {/* Secondary Card - Remember Me Info */} - - - - By using the Remember Me function, you consent to a cookie being - stored in your browser for the purpose of identifying your account - on future visits. - - {/* Privacy Policy Link */} - - - - Privacy Policy - - - + {/* Login with Google */} + + {/* Login with Discord */} +
diff --git a/src/app/_components/Auth/SignUp/SignUp.tsx b/src/app/_components/Auth/SignUp/SignUp.tsx deleted file mode 100644 index 557f925d..00000000 --- a/src/app/_components/Auth/SignUp/SignUp.tsx +++ /dev/null @@ -1,160 +0,0 @@ -import React, { ChangeEvent } from "react"; -import { - Box, - Button, - Card, - CardContent, - FormControl, - Typography, -} from "@mui/material"; -import StyledTextField from "../../_sharedcomponents/_styledcomponents/StyledTextField/StyledTextField"; -import { SignUpProps } from "../AuthTypes"; - -const SignUp: React.FC = ({ - email, - setEmail, - password, - setPassword, - confirmPassword, - setConfirmPassword, - passwordError, - passwordMatchError, - handleSubmit, - toggleAuth, -}) => { - //------------------------STYLES------------------------// - - const containerStyle = { - display: "flex", - flexDirection: "column", - alignItems: "center", - pt: "12em", - }; - - const cardStyle = { - width: { xs: "90vw", sm: "70vw", md: "60vw", lg: "30vw" }, - borderRadius: "1.5em", - backgroundColor: "#000000E6", - backdropFilter: "blur(20px)", - p: "2.5em", - mb: "2em", - }; - const headingStyle = { - fontFamily: "var(--font-barlow), sans-serif", - fontWeight: "800", - fontSize: "2em", - color: "#fff", - mb: ".5em", - }; - - const formControlStyle = { - mb: ".5em", - }; - - const labelTextStyle = { - fontFamily: "var(--font-barlow), sans-serif", - fontSize: "1.3em", - color: "#fff", - mb: ".5em", - }; - - const submitButtonStyle = { - display: "block", - width: "10em", - height: "3em", - borderRadius: "0.5em", - backgroundColor: "#292929", - fontFamily: "var(--font-barlow), sans-serif", - fontSize: "1.2em", - ml: "auto", - mr: "auto", - mb: ".8em", - "&:hover": { - backgroundColor: "#3a3a3a", - }, - }; - const toggleTextStyle = { - color: "#fff", - fontSize: ".7rem", - }; - - const toggleButtonStyle = { - color: "#1976d2", - textDecoration: "underline", - fontSize: ".7rem", - }; - - return ( - - - - - Sign Up - -
- {/* Email Input */} - - Email - ) => - setEmail(e.target.value) - } - required - /> - - - {/* Password Input */} - - Password - ) => - setPassword(e.target.value) - } - errorMessage={ - passwordError ? "Password must be at least 8 characters" : "" - } - required - /> - - - {/* Confirm Password Input */} - - Confirm Password - ) => - setConfirmPassword(e.target.value) - } - errorMessage={ - passwordMatchError ? "Passwords do not match" : "" - } - required - /> - - - {/* Submit Button */} - -
- {/* Toggle to Login */} - - - Already have an account?{" "} - - - -
-
-
- ); -}; - -export default SignUp; diff --git a/src/app/_components/ClientProviders/ClientProviders.tsx b/src/app/_components/ClientProviders/ClientProviders.tsx index 4f571056..d4e0320e 100644 --- a/src/app/_components/ClientProviders/ClientProviders.tsx +++ b/src/app/_components/ClientProviders/ClientProviders.tsx @@ -3,6 +3,7 @@ import { SidebarProvider } from "@/app/_contexts/Sidebar.context"; import { ThemeContextProvider } from "@/app/_contexts/Theme.context"; import { UserProvider } from "@/app/_contexts/User.context"; +import { SessionProvider } from "next-auth/react"; interface ClientProvidersProps { children: React.ReactNode; @@ -10,11 +11,13 @@ interface ClientProvidersProps { const ClientProviders: React.FC = ({ children }) => { return ( - - - {children} - - + + + + {children} + + + ); }; diff --git a/src/app/_components/_sharedcomponents/ControlHub/ControlHub.tsx b/src/app/_components/_sharedcomponents/ControlHub/ControlHub.tsx index ae30d3e3..09525c53 100644 --- a/src/app/_components/_sharedcomponents/ControlHub/ControlHub.tsx +++ b/src/app/_components/_sharedcomponents/ControlHub/ControlHub.tsx @@ -25,91 +25,88 @@ const ControlHub: React.FC = ({ } }; - //------------------------STYLES------------------------// + // ---------------------- Styles ---------------------- // - const containerStyle = { - position: "absolute", - top: 10, - right: isLobbyView || isGameboardView ? 10 : 0, - display: "flex", - alignItems: "center", - zIndex: 1, - }; - - const defaultMainContainerStyle = { - display: "flex", - gap: 1, - alignItems: "center", - ml: "1rem", - }; - - const backButtonStyle = { - color: "#fff", - mt: ".5vh", - fontFamily: "var(--font-barlow), sans-serif", - fontWeight: "600", - fontSize: "1.5rem", - }; - - const exitTextStyle = { - fontFamily: "var(--font-barlow), sans-serif", - fontWeight: "600", - color: "#fff", - mt: ".5vh", - mr: ".5vw", - }; - - const profileBoxStyle = { - display: "flex", - borderRadius: "50px", - backgroundColor: "#000000E6", - backdropFilter: "blur(20px)", - height: "48px", - justifyContent: "space-around", - alignItems: "center", - alignContent: "center", - p: "0.5rem 1rem", - }; - - const profileLinkStyle = { - fontFamily: "var(--font-barlow), sans-serif", - fontWeight: "400", - fontSize: "1em", - textDecoration: "none", - color: "#fff", - "&:hover": { - color: "#00ffff", + const controlHubStyles = { + container: (isLobbyView: boolean, isGameboardView: boolean) => ({ + position: "absolute", + top: 10, + right: isLobbyView || isGameboardView ? 10 : 0, + display: "flex", + alignItems: "center", + zIndex: 1, + }), + defaultMainContainer: { + display: "flex", + gap: 1, + alignItems: "center", + ml: "1rem", + }, + backButton: { + color: "#fff", + mt: ".5vh", + fontFamily: "var(--font-barlow), sans-serif", + fontWeight: "600", + fontSize: "1.5rem", + }, + exitText: { + fontFamily: "var(--font-barlow), sans-serif", + fontWeight: "600", + color: "#fff", + mt: ".5vh", + mr: ".5vw", + }, + profileBox: { + display: "flex", + borderRadius: "50px", + backgroundColor: "#000000E6", + backdropFilter: "blur(20px)", + height: "48px", + justifyContent: "space-around", + alignItems: "center", + alignContent: "center", + p: "0.5rem 1rem", + }, + profileLink: { + fontFamily: "var(--font-barlow), sans-serif", + fontWeight: "400", + fontSize: "1em", + textDecoration: "none", + color: "#fff", + "&:hover": { + color: "#00ffff", + }, + }, + socialIconsBox: { + display: "flex", + height: "48px", + borderRadius: "50px 0 0 50px", + backgroundColor: "#000000E6", + backdropFilter: "blur(20px)", + alignItems: "center", + p: "0.5rem", + }, + iconButton: { + color: "#fff", + "&:hover": { color: "#00ffff" }, }, - }; - - const socialIconsBoxStyle = { - display: "flex", - height: "48px", - borderRadius: "50px 0 0 50px", - backgroundColor: "#000000E6", - backdropFilter: "blur(20px)", - alignItems: "center", - p: "0.5rem", - }; - - const iconButtonStyle = { - color: "#fff", - "&:hover": { color: "#00ffff" }, }; return ( - + {isLobbyView ? ( <> - + - + EXIT ) : isGameboardView ? ( - // Gameboard View: Settings and Menu Button <> @@ -121,13 +118,11 @@ const ControlHub: React.FC = ({ )} ) : ( - // Default View: Conditional Profile/Login and Social Icons - - {/* Conditionally render Profile/Log Out or Log In */} - + + {user ? ( <> - + PROFILE = ({ flexItem sx={{ borderColor: "#ffffff4D", mx: 1 }} /> - + LOG OUT ) : ( - + LOG IN )} - {/* Social Icons Chip */} - + - + @@ -161,7 +159,7 @@ const ControlHub: React.FC = ({ target="_blank" rel="noopener noreferrer" > - + @@ -170,7 +168,7 @@ const ControlHub: React.FC = ({ target="_blank" rel="noopener noreferrer" > - + diff --git a/src/app/_components/_sharedcomponents/ControlHub/ControlHubTypes.ts b/src/app/_components/_sharedcomponents/ControlHub/ControlHubTypes.ts index be1003b5..50fc6d23 100644 --- a/src/app/_components/_sharedcomponents/ControlHub/ControlHubTypes.ts +++ b/src/app/_components/_sharedcomponents/ControlHub/ControlHubTypes.ts @@ -1,7 +1,9 @@ +import { User } from "./../../../_contexts/UserTypes"; + export interface ControlHubProps { path?: string; sidebarOpen?: boolean; toggleSidebar?: () => void; - user?: string | null; + user?: User | null; logout?: () => void; } diff --git a/src/app/_contexts/User.context.tsx b/src/app/_contexts/User.context.tsx index 39842b2e..b1d922e5 100644 --- a/src/app/_contexts/User.context.tsx +++ b/src/app/_contexts/User.context.tsx @@ -1,5 +1,14 @@ "use client"; -import React, { createContext, useContext, useState, ReactNode } from "react"; + +import React, { + createContext, + useContext, + ReactNode, + useEffect, + useState, +} from "react"; +import { useSession, signIn, signOut } from "next-auth/react"; +import { UserContextType } from "./UserTypes"; const UserContext = createContext({ user: null, @@ -10,16 +19,37 @@ const UserContext = createContext({ export const UserProvider: React.FC<{ children: ReactNode }> = ({ children, }) => { - const [user, setUser] = useState(null); + const { data: session } = useSession(); // Get session from next-auth + const [user, setUser] = useState(null); + + useEffect(() => { + // Keep context in sync with next-auth session + if (session?.user) { + setUser({ + id: session.user.id || null, + name: session.user.name || null, + email: session.user.email || null, + image: session.user.image || null, + provider: session.user.provider || null, + }); + } else { + setUser(null); + } + }, [session]); + + const login = (provider: "google" | "discord") => { + console.log("Logging in with", provider); + + signIn(provider, { + callbackUrl: "/", + }); - const login = (username: string) => { - setUser(username); - console.log("User logged in:", username); + console.log("Logged in with", provider); }; const logout = () => { + signOut(); setUser(null); - console.log("User logged out"); }; return ( diff --git a/src/app/_contexts/UserTypes.ts b/src/app/_contexts/UserTypes.ts new file mode 100644 index 00000000..b3240835 --- /dev/null +++ b/src/app/_contexts/UserTypes.ts @@ -0,0 +1,13 @@ +export interface User { + id: string | null; + name: string | null; + email: string | null; + image: string | null; + provider: string | null; +} + +export interface UserContextType { + user: User | null; + login: (provider: "google" | "discord") => void; + logout: () => void; +} diff --git a/src/app/api/auth/[...nextauth]/route.ts b/src/app/api/auth/[...nextauth]/route.ts new file mode 100644 index 00000000..d5665daa --- /dev/null +++ b/src/app/api/auth/[...nextauth]/route.ts @@ -0,0 +1,43 @@ +import NextAuth from "next-auth"; +import GoogleProvider from "next-auth/providers/google"; +import DiscordProvider from "next-auth/providers/discord"; + +const handler = NextAuth({ + providers: [ + GoogleProvider({ + clientId: process.env.GOOGLE_CLIENT_ID!, + clientSecret: process.env.GOOGLE_CLIENT_SECRET!, + }), + DiscordProvider({ + clientId: process.env.DISCORD_CLIENT_ID!, + clientSecret: process.env.DISCORD_CLIENT_SECRET!, + }), + ], + callbacks: { + async jwt({ token, account, user }) { + if (account) { + token.provider = account.provider; + token.sub = user?.id || token.sub; + token.name = user?.name || token.name; + token.email = user?.email || token.email; + token.picture = user?.image || token.picture; + } + return token; + }, + async session({ session, token }) { + session.user = { + id: token.sub || null, + name: token.name || session.user?.name || null, + email: token.email || session.user?.email || null, + image: token.picture || session.user?.image || null, + provider: token.provider || "google", + }; + return session; + }, + }, + pages: { + signIn: "/auth", + }, +}); + +export { handler as GET, handler as POST }; diff --git a/src/app/auth/page.tsx b/src/app/auth/page.tsx index 22faade7..2cb59edd 100644 --- a/src/app/auth/page.tsx +++ b/src/app/auth/page.tsx @@ -1,70 +1,13 @@ "use client"; -import React, { useState, FormEvent } from "react"; + +import React from "react"; import { Box } from "@mui/material"; import KarabastBanner from "../_components/_sharedcomponents/Banner/Banner"; import Login from "../_components/Auth/Login/Login"; -import SignUp from "../_components/Auth/SignUp/SignUp"; import { useUser } from "../_contexts/User.context"; -import { useRouter } from "next/navigation"; const Auth: React.FC = () => { - const router = useRouter(); - - const [isLogin, setIsLogin] = useState(true); - - // Login State Hooks - const [loginUsername, setLoginUsername] = useState(""); - const [loginPassword, setLoginPassword] = useState(""); - const [rememberMe, setRememberMe] = useState(false); - - // Sign Up State Hooks (if needed later) - const [signUpEmail, setSignUpEmail] = useState(""); - const [signUpPassword, setSignUpPassword] = useState(""); - const [signUpConfirmPassword, setSignUpConfirmPassword] = - useState(""); - const [signUpPasswordError, setSignUpPasswordError] = - useState(false); - const [signUpPasswordMatchError, setSignUpPasswordMatchError] = - useState(false); - - console.log( - "temporary use of variables setSignUpPasswordError, setSignUpPasswordMatchError to avoid unused vars", - setSignUpPasswordError, - setSignUpPasswordMatchError - ); - - const { login } = useUser(); // Get the login function from UserContext - - // Handle Login Submission - const handleLoginSubmit = (event: FormEvent) => { - event.preventDefault(); - console.log("Login - Username:", loginUsername); - console.log("Login - Password:", loginPassword); - console.log("Login - Remember Me:", rememberMe); - - // Simulate login and set the user in the context - login(loginUsername); - router.push("/"); // Redirect to the home page - // Temporary - storing the token or credentials (remove later) - document.cookie = `user=${loginUsername}; max-age=3600; path=/`; - }; - - // Handle Sign Up Submission (not implemented yet) - const handleSignUpSubmit = (event: FormEvent) => { - event.preventDefault(); - console.log("Sign Up - Email:", signUpEmail); - console.log("Sign Up - Password:", signUpPassword); - - // Simulate sign-up logic if needed - if (!signUpPasswordError && !signUpPasswordMatchError) { - console.log("Sign-up successful", { - email: signUpEmail, - password: signUpPassword, - }); - } - }; - - //-------------------------Styles-------------------------// + const { login } = useUser(); const mainContainerStyle = { position: "relative", @@ -77,32 +20,7 @@ const Auth: React.FC = () => { return ( - - {isLogin ? ( - setIsLogin(false)} - /> - ) : ( - setIsLogin(true)} - /> - )} + ); }; diff --git a/src/global.d.ts b/src/global.d.ts index 73a2360e..e69de29b 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -1,5 +0,0 @@ -interface UserContextType { - user: string | null; - login: (username: string) => void; - logout: () => void; -} diff --git a/src/next-auth.d.ts b/src/next-auth.d.ts new file mode 100644 index 00000000..762813a7 --- /dev/null +++ b/src/next-auth.d.ts @@ -0,0 +1,24 @@ +import NextAuth from "next-auth"; + +declare module "next-auth" { + interface Session { + user: { + id: string | null; + name?: string | null; + email?: string | null; + image?: string | null; + provider: string; + }; + } + + interface User { + id: string; + } +} + +declare module "next-auth/jwt" { + interface JWT { + sub?: string; + provider?: string; + } +}