diff --git a/src/app/_components/HomePage/HomePagePlayMode.tsx b/src/app/_components/HomePage/HomePagePlayMode.tsx index 55f6cbf9..6df3bef3 100644 --- a/src/app/_components/HomePage/HomePagePlayMode.tsx +++ b/src/app/_components/HomePage/HomePagePlayMode.tsx @@ -78,15 +78,17 @@ const HomePagePlayMode: React.FC = () => { - + {user && } {showTestGames && } - - - - + {user && + + + + } + {showTestGames && diff --git a/src/app/_components/Lobby/Deck/Deck.tsx b/src/app/_components/Lobby/Deck/Deck.tsx index 387c7934..d3d42cc6 100644 --- a/src/app/_components/Lobby/Deck/Deck.tsx +++ b/src/app/_components/Lobby/Deck/Deck.tsx @@ -111,8 +111,8 @@ const Deck: React.FC = () => { const connectedUser = lobbyState ? lobbyState.users.find((u: ILobbyUserProps) => u.id === connectedPlayer) : null; // set decks for connectedUser - const newDeck = connectedUser ? connectedUser.deck.deckCards || [] : []; - const sideBoard = connectedUser ? connectedUser.deck.sideboard || [] : []; + const newDeck = connectedUser ? connectedUser.deck ? connectedUser.deck.deckCards || [] : [] : []; + const sideBoard = connectedUser ? connectedUser.deck ? connectedUser.deck.sideboard || [] : [] : []; // Calculate the total counts const deckCount = newDeck.reduce( diff --git a/src/app/_components/Lobby/Players/Players.tsx b/src/app/_components/Lobby/Players/Players.tsx index 436c5830..b5cdb819 100644 --- a/src/app/_components/Lobby/Players/Players.tsx +++ b/src/app/_components/Lobby/Players/Players.tsx @@ -13,13 +13,13 @@ const Players: React.FC = ({ isLobbyView }) => { const opponentUser = lobbyState ? lobbyState.users.find((u: ILobbyUserProps) => u.id !== connectedPlayer) : null; // set connectedPlayer - const playerLeader = connectedUser ? connectedUser.deck.leader[0].card : null; - const playerBase = connectedUser ? connectedUser.deck.base[0].card : null; + const playerLeader = connectedUser ? connectedUser.deck ? connectedUser.deck.leader[0].card : null : null; + const playerBase = connectedUser ? connectedUser.deck ? connectedUser.deck.base[0].card : null : null; // set opponent const titleOpponent = opponentUser ? opponentUser.username : null; - const opponentLeader = opponentUser ? opponentUser.deck.leader[0].card : null; - const opponentBase = opponentUser ? opponentUser.deck.base[0].card : null; + const opponentLeader = opponentUser ? opponentUser.deck ? opponentUser.deck.leader[0].card : null : null; + const opponentBase = opponentUser ? opponentUser.deck ? opponentUser.deck.base[0].card : null : null; const cardStyle = { borderRadius: '1.1em', borderColor: '#FFFFFF00', diff --git a/src/app/_components/Lobby/SetUp/SetUp.tsx b/src/app/_components/Lobby/SetUp/SetUp.tsx index 297bee24..12f08193 100644 --- a/src/app/_components/Lobby/SetUp/SetUp.tsx +++ b/src/app/_components/Lobby/SetUp/SetUp.tsx @@ -32,7 +32,7 @@ const SetUp: React.FC = ({ const mainCardStyle = { borderRadius: '1.1em', height: '100%', - maxHeight: '72.5vh', + maxHeight: lobbyState && lobbyState.privacy === 'Public' ? '72.5vh': '64.4vh', width: '100%', display: 'flex', flexDirection: 'column', diff --git a/src/app/_components/Lobby/_subcomponents/SetUpCard/SetUpCard.tsx b/src/app/_components/Lobby/_subcomponents/SetUpCard/SetUpCard.tsx index d82ebf02..28f58abb 100644 --- a/src/app/_components/Lobby/_subcomponents/SetUpCard/SetUpCard.tsx +++ b/src/app/_components/Lobby/_subcomponents/SetUpCard/SetUpCard.tsx @@ -1,22 +1,26 @@ -import React from 'react'; +import React, { ChangeEvent, useState } from 'react'; import { Card, CardContent, Typography, TextField, Button, - Box, CardActions, + Box, CardActions, Link, FormControl, } from '@mui/material'; import { useGame } from '@/app/_contexts/Game.context'; import { useRouter } from 'next/navigation' import { ILobbyUserProps, ISetUpProps } from '@/app/_components/Lobby/LobbyTypes'; +import StyledTextField from '@/app/_components/_sharedcomponents/_styledcomponents/StyledTextField/StyledTextField'; +import { fetchDeckData } from '@/app/_utils/fetchDeckData'; const SetUpCard: React.FC = ({ readyStatus, owner, }) => { - const { sendMessage, lobbyState, connectedPlayer, sendLobbyMessage } = useGame(); + const { lobbyState, connectedPlayer, sendLobbyMessage } = useGame(); + const [deckLink, setDeckLink] = useState(''); const opponentUser = lobbyState ? lobbyState.users.find((u: ILobbyUserProps) => u.id !== connectedPlayer) : null; + const currentUser = lobbyState ? lobbyState.users.find((u: ILobbyUserProps) => u.id === connectedPlayer) : null; // Extract the player from the URL query params const router = useRouter(); @@ -25,6 +29,11 @@ const SetUpCard: React.FC = ({ sendLobbyMessage(['onStartGame']); router.push('/GameBoard'); }; + const handleOnChangeDeck = async () => { + console.log('SWUDB Deck Link:', deckLink); + const deckData = deckLink ? await fetchDeckData(deckLink) : null; + sendLobbyMessage(['changeDeck',deckData]) + } // ------------------------STYLES------------------------// const styles = { @@ -67,7 +76,7 @@ const SetUpCard: React.FC = ({ minWidth: '9rem', }, initiativeCardStyle: { - height: '15vh', + height: lobbyState && lobbyState.privacy === 'Public' ? '15vh' : '23vh', minHeight: '8.5rem', background: '#18325199', display: 'flex', @@ -87,6 +96,21 @@ const SetUpCard: React.FC = ({ color: 'white', alignSelf: 'flex-start', }, + labelTextStyle: { + mb: '.5em', + mt: '1.5em', + color: 'white', + }, + labelTextStyleSecondary: { + color: '#aaaaaa', + display: 'inline', + }, + submitButtonStyle: { + display: 'block', + ml: 'auto', + mr: 'auto', + mt: '10px', + }, } return ( @@ -102,7 +126,7 @@ const SetUpCard: React.FC = ({ - + @@ -146,6 +170,31 @@ const SetUpCard: React.FC = ({ )} )} + {lobbyState && lobbyState.privacy === 'Private' && <> + + + SWUDB + {' '} + or{' '} + + SW-Unlimited-DB + {' '} + Deck Link{' '} + + (use the URL or 'Deck Link' button) + + + ) => + setDeckLink(e.target.value) + } + /> + + } ); }; diff --git a/src/app/_components/_sharedcomponents/CreateGameForm/CreateGameForm.tsx b/src/app/_components/_sharedcomponents/CreateGameForm/CreateGameForm.tsx index e16ef748..b40623f2 100644 --- a/src/app/_components/_sharedcomponents/CreateGameForm/CreateGameForm.tsx +++ b/src/app/_components/_sharedcomponents/CreateGameForm/CreateGameForm.tsx @@ -15,33 +15,14 @@ import { } from '@mui/material'; import StyledTextField from '../_styledcomponents/StyledTextField/StyledTextField'; import { usePathname, useRouter } from 'next/navigation'; -import { updateIdsWithMapping, mapIdToInternalName, transformDeckWithCardData } from '@/app/_utils/s3Utils'; import { useUser } from '@/app/_contexts/User.context'; +import { fetchDeckData } from '@/app/_utils/fetchDeckData'; interface ICreateGameFormProps { format?: string | null; setFormat?: (format: string) => void; } -interface IDeckMetadata { - name: string; - author: string; -} - -interface IDeckCard { - id: string; - count: number; -} - -interface IDeckData { - metadata: IDeckMetadata; - leader: IDeckCard; - secondleader: IDeckCard | null; - base: IDeckCard; - deck: IDeckCard[]; - sideboard: IDeckCard[]; -} - const deckOptions: string[] = [ 'Order66', 'ThisIsTheWay', @@ -62,52 +43,10 @@ const CreateGameForm: React.FC = ({ const [favouriteDeck, setFavouriteDeck] = useState(''); const [deckLink, setDeckLink] = useState(''); const [saveDeck, setSaveDeck] = useState(false); - // let [deckData, setDeckData] = useState(null); Because of Async this won't set in the correct timeframe // Additional State for Non-Creategame Path const [gameName, setGameName] = useState(''); - const [privacy, setPrivacy] = useState('Public'); - - const fetchDeckData = async (deckLink: string) => { - try { - const response = await fetch( - `/api/swudbdeck?deckLink=${encodeURIComponent(deckLink)}` - ); - if (!response.ok) { - throw new Error(`Failed to fetch deck: ${response.statusText}`); - } - - const data: IDeckData = await response.json(); - - // Fetch setToId mapping from the s3bucket endpoint - const setCodeMapping = await fetch('/api/s3bucket?jsonFile=_setCodeMap.json'); // Adjust to your actual endpoint if different - if (!setCodeMapping.ok) { - throw new Error('Failed to fetch card mapping'); - } - - const jsonData = await setCodeMapping.json(); - const deckWithIds = updateIdsWithMapping(data, jsonData); - - // Fetch codeToInternalname mapping - const codeInternalnameMapping = await fetch('/api/s3bucket?jsonFile=_cardMap.json'); // Adjust to your actual endpoint if different - if (!codeInternalnameMapping.ok) { - throw new Error('Failed to fetch card mapping'); - } - - const codeInternalnameJson = await codeInternalnameMapping.json(); - const deckWithInternalNames = mapIdToInternalName(codeInternalnameJson, deckWithIds) - - // Fetch internalNameToCardMapping - const finalDeckForm = await transformDeckWithCardData(deckWithInternalNames); - return finalDeckForm - } catch (error) { - if (error instanceof Error) { - console.error('Error fetching deck:', error.message); - } else { - console.error('Unexpected error:', error); - } - } - }; + const [privacy, setPrivacy] = useState(user ? 'Public' : 'Private'); // Handle Create Game Submission const handleCreateGameSubmit = async (event: FormEvent) => { @@ -121,7 +60,8 @@ const CreateGameForm: React.FC = ({ try { const payload = { user: user, - deck: deckData + deck: deckData, + privacy: privacy }; const response = await fetch('http://localhost:9500/api/create-lobby', { @@ -136,7 +76,12 @@ const CreateGameForm: React.FC = ({ if (!response.ok) { throw new Error('Failed to create game'); } - + const responseJson = await response.json(); + console.log('Response data:', responseJson); + // Store unknownUserId in local storage (so we can retrieve it in GameContext) + if(privacy === 'Private') { + localStorage.setItem('unknownUserId', responseJson.newUserId); + } router.push('/lobby'); } catch (error) { console.error(error); @@ -191,7 +136,7 @@ const CreateGameForm: React.FC = ({
{/* Favourite Decks Input */} - + {user && Favourite Decks = ({ ))} - + } {/* SWUDB Deck Link Input */} @@ -234,7 +179,7 @@ const CreateGameForm: React.FC = ({ {/* Save Deck To Favourites Checkbox */} - = ({ } /> + } {/* Additional Fields for Non-Creategame Path */} {!isCreateGamePath && ( @@ -289,7 +235,9 @@ const CreateGameForm: React.FC = ({ ))} - + + Log In to be able to create public games or join a quick game. + {/* Privacy Selection */} = ({ value: string ) => setPrivacy(value)} > - } label={ @@ -309,6 +257,7 @@ const CreateGameForm: React.FC = ({ } /> + } } diff --git a/src/app/_components/_sharedcomponents/QuickGameForm/QuickGameForm.tsx b/src/app/_components/_sharedcomponents/QuickGameForm/QuickGameForm.tsx index c5404f81..076863a5 100644 --- a/src/app/_components/_sharedcomponents/QuickGameForm/QuickGameForm.tsx +++ b/src/app/_components/_sharedcomponents/QuickGameForm/QuickGameForm.tsx @@ -4,31 +4,13 @@ import StyledTextField from '../_styledcomponents/StyledTextField/StyledTextFiel import { useRouter } from 'next/navigation'; import { mapIdToInternalName, transformDeckWithCardData, updateIdsWithMapping } from '@/app/_utils/s3Utils'; import { useUser } from '@/app/_contexts/User.context'; +import { fetchDeckData } from '@/app/_utils/fetchDeckData'; interface ICreateGameFormProps { format?: string | null; setFormat?: (format: string) => void; } -interface IDeckMetadata { - name: string; - author: string; -} - -interface IDeckCard { - id: string; - count: number; -} - -interface IDeckData { - metadata: IDeckMetadata; - leader: IDeckCard; - secondleader: IDeckCard | null; - base: IDeckCard; - deck: IDeckCard[]; - sideboard: IDeckCard[]; -} - const QuickGameForm: React.FC = () => { const router = useRouter(); @@ -45,46 +27,6 @@ const QuickGameForm: React.FC = () => { ]; - const fetchDeckData = async (deckLink: string) => { - try { - const response = await fetch( - `/api/swudbdeck?deckLink=${encodeURIComponent(deckLink)}` - ); - if (!response.ok) { - throw new Error(`Failed to fetch deck: ${response.statusText}`); - } - - const data: IDeckData = await response.json(); - - // Fetch setToId mapping from the s3bucket endpoint - const setCodeMapping = await fetch('/api/s3bucket?jsonFile=_setCodeMap.json'); // Adjust to your actual endpoint if different - if (!setCodeMapping.ok) { - throw new Error('Failed to fetch card mapping'); - } - - const jsonData = await setCodeMapping.json(); - const deckWithIds = updateIdsWithMapping(data, jsonData); - - // Fetch codeToInternalname mapping - const codeInternalnameMapping = await fetch('/api/s3bucket?jsonFile=_cardMap.json'); // Adjust to your actual endpoint if different - if (!codeInternalnameMapping.ok) { - throw new Error('Failed to fetch card mapping'); - } - - const codeInternalnameJson = await codeInternalnameMapping.json(); - const deckWithInternalNames = mapIdToInternalName(codeInternalnameJson, deckWithIds) - - // Fetch internalNameToCardMapping - return await transformDeckWithCardData(deckWithInternalNames) - } catch (error) { - if (error instanceof Error) { - console.error('Error fetching deck:', error.message); - } else { - console.error('Unexpected error:', error); - } - } - }; - // Handle Create Game Submission const handleJoinGameQueue = async (event: FormEvent) => { event.preventDefault(); diff --git a/src/app/_contexts/Game.context.tsx b/src/app/_contexts/Game.context.tsx index 86d5d3e9..a1937415 100644 --- a/src/app/_contexts/Game.context.tsx +++ b/src/app/_contexts/Game.context.tsx @@ -10,6 +10,7 @@ import React, { } from 'react'; import io, { Socket } from 'socket.io-client'; import { useUser } from './User.context'; +import { useSearchParams } from 'next/navigation'; interface IGameContextType { gameState: any; @@ -29,23 +30,30 @@ export const GameProvider = ({ children }: { children: ReactNode }) => { const [socket, setSocket] = useState(undefined); const [connectedPlayer, setConnectedPlayer] = useState(''); const { user } = useUser(); + const searchParams = useSearchParams(); useEffect(() => { - if (!user) return; - setConnectedPlayer(user.id || ''); + const lobbyId = searchParams.get('lobbyId'); + // we get the lobbyId + const storedUnknownUserId = localStorage.getItem('unknownUserId') || lobbyId+'-GuestId2'; + setConnectedPlayer(user ? user.id || '' : storedUnknownUserId ? storedUnknownUserId : ''); const newSocket = io('http://localhost:9500', { query: { - user: JSON.stringify(user) + user: JSON.stringify(user ? user : { id:storedUnknownUserId }), + lobbyId: lobbyId ? lobbyId : null }, }); newSocket.on('connect', () => { - console.log(`Connected to server as ${user.username}`); + console.log(`Connected to server as ${user ? user.username : ''}`); }); newSocket.on('gamestate', (gameState: any) => { setGameState(gameState); console.log('Game state received:', gameState); }); + newSocket.on('connectedUser', () =>{ + localStorage.removeItem('unknownUserId'); + }); newSocket.on('lobbystate', (lobbyState: any) => { setLobbyState(lobbyState); console.log('Lobby state received:', lobbyState); diff --git a/src/app/_utils/fetchDeckData.ts b/src/app/_utils/fetchDeckData.ts new file mode 100644 index 00000000..82f58ad6 --- /dev/null +++ b/src/app/_utils/fetchDeckData.ts @@ -0,0 +1,61 @@ +// app/_utils/fetchDeckData.ts + +import { updateIdsWithMapping, mapIdToInternalName, transformDeckWithCardData } from '@/app/_utils/s3Utils'; +interface IDeckMetadata { + name: string; + author: string; +} + +interface IDeckCard { + id: string; + count: number; +} + +interface IDeckData { + metadata: IDeckMetadata; + leader: IDeckCard; + secondleader: IDeckCard | null; + base: IDeckCard; + deck: IDeckCard[]; + sideboard: IDeckCard[]; +} + +export const fetchDeckData = async (deckLink: string) => { + try { + const response = await fetch( + `/api/swudbdeck?deckLink=${encodeURIComponent(deckLink)}` + ); + if (!response.ok) { + throw new Error(`Failed to fetch deck: ${response.statusText}`); + } + + const data: IDeckData = await response.json(); + + // Fetch setToId mapping + const setCodeMapping = await fetch('/api/s3bucket?jsonFile=_setCodeMap.json'); + if (!setCodeMapping.ok) { + throw new Error('Failed to fetch card mapping'); + } + const jsonData = await setCodeMapping.json(); + const deckWithIds = updateIdsWithMapping(data, jsonData); + + // Fetch codeToInternalname mapping + const codeInternalnameMapping = await fetch('/api/s3bucket?jsonFile=_cardMap.json'); + if (!codeInternalnameMapping.ok) { + throw new Error('Failed to fetch card mapping'); + } + const codeInternalnameJson = await codeInternalnameMapping.json(); + const deckWithInternalNames = mapIdToInternalName(codeInternalnameJson, deckWithIds); + + // Transform deck with final card data + const finalDeckForm = await transformDeckWithCardData(deckWithInternalNames); + return finalDeckForm; + } catch (error) { + if (error instanceof Error) { + console.error('Error fetching deck:', error.message); + } else { + console.error('Unexpected error:', error); + } + return null; // or throw error again + } +}; \ No newline at end of file