From b19f59e498f5cf4b2f7849e44db268d258733255 Mon Sep 17 00:00:00 2001 From: Janis Date: Wed, 10 Dec 2025 22:20:59 +0100 Subject: [PATCH 1/2] feat: FRO-31 Assemble In Game Component --- src/components/Ingame.vue | 61 +++++++++++++++++++ .../ingame/{GameInfo.vue => GameInfoC.vue} | 0 src/components/ingame/{Hand.vue => HandC.vue} | 0 .../{PlayedCards.vue => PlayedCardsC.vue} | 0 .../{Scoreboard.vue => ScoreboardC.vue} | 0 src/components/ingame/{Turn.vue => TurnC.vue} | 0 src/composables/useIngame.ts | 29 +++++++++ src/composables/useUserInfo.ts | 23 ++++++- src/composables/useWebsocket.ts | 6 ++ src/services/ws.ts | 30 ++++++++- src/views/Game.vue | 33 ++++++++++ 11 files changed, 179 insertions(+), 3 deletions(-) create mode 100644 src/components/Ingame.vue rename src/components/ingame/{GameInfo.vue => GameInfoC.vue} (100%) rename src/components/ingame/{Hand.vue => HandC.vue} (100%) rename src/components/ingame/{PlayedCards.vue => PlayedCardsC.vue} (100%) rename src/components/ingame/{Scoreboard.vue => ScoreboardC.vue} (100%) rename src/components/ingame/{Turn.vue => TurnC.vue} (100%) create mode 100644 src/composables/useIngame.ts create mode 100644 src/views/Game.vue diff --git a/src/components/Ingame.vue b/src/components/Ingame.vue new file mode 100644 index 0000000..0036a68 --- /dev/null +++ b/src/components/Ingame.vue @@ -0,0 +1,61 @@ + + + + + diff --git a/src/components/ingame/GameInfo.vue b/src/components/ingame/GameInfoC.vue similarity index 100% rename from src/components/ingame/GameInfo.vue rename to src/components/ingame/GameInfoC.vue diff --git a/src/components/ingame/Hand.vue b/src/components/ingame/HandC.vue similarity index 100% rename from src/components/ingame/Hand.vue rename to src/components/ingame/HandC.vue diff --git a/src/components/ingame/PlayedCards.vue b/src/components/ingame/PlayedCardsC.vue similarity index 100% rename from src/components/ingame/PlayedCards.vue rename to src/components/ingame/PlayedCardsC.vue diff --git a/src/components/ingame/Scoreboard.vue b/src/components/ingame/ScoreboardC.vue similarity index 100% rename from src/components/ingame/Scoreboard.vue rename to src/components/ingame/ScoreboardC.vue diff --git a/src/components/ingame/Turn.vue b/src/components/ingame/TurnC.vue similarity index 100% rename from src/components/ingame/Turn.vue rename to src/components/ingame/TurnC.vue diff --git a/src/composables/useIngame.ts b/src/composables/useIngame.ts new file mode 100644 index 0000000..459d505 --- /dev/null +++ b/src/composables/useIngame.ts @@ -0,0 +1,29 @@ +import { defineStore } from 'pinia' +import {ref, type Ref} from 'vue' +import type {GameInfo, LobbyInfo, TieInfo, TrumpInfo, WonInfo} from "@/types/GameTypes.ts"; +import axios from "axios"; + +const api = window?.__RUNTIME_CONFIG__?.API_URL; + +export const useIngame = defineStore('ingame', () => { + const state: Ref<'Lobby' | 'InGame' | 'SelectTrump' | 'TieBreak' | 'FinishedMatch' | null> = ref(null); + const data: Ref = ref(null); + + function setIngame(newState: 'Lobby' | 'InGame' | 'SelectTrump' | 'TieBreak' | 'FinishedMatch', newData: GameInfo | LobbyInfo | TieInfo | TrumpInfo | WonInfo) { + state.value = newState; + data.value = newData; + } + + function requestGame(gameId: string) { + axios.get(`${api}/status/${gameId}`, {withCredentials: true}).then((response) => { + setIngame(response.data.state, response.data.data); + }); + } + + function clearIngame() { + state.value = null; + data.value = null; + } + + return { state, data, requestGame, setIngame, clearIngame }; +}); diff --git a/src/composables/useUserInfo.ts b/src/composables/useUserInfo.ts index 220796d..781ba8a 100644 --- a/src/composables/useUserInfo.ts +++ b/src/composables/useUserInfo.ts @@ -1,19 +1,40 @@ import { defineStore } from 'pinia' import {ref, type Ref} from 'vue' +import axios from "axios"; + +const api = window?.__RUNTIME_CONFIG__?.API_URL; export const useUserInfo = defineStore('userInfo', () => { const username: Ref = ref(null); const userId: Ref = ref(null); + const gameId: Ref = ref(null); function setUserInfo(name: string, id: number) { username.value = name; userId.value = id; } + function setGameId(id: string) { + gameId.value = id; + } + + function requestState() { + axios.get(`${api}/status`, {withCredentials: true}).then((response) => { + username.value = response.data.username; + if (response.data.ingame) { + gameId.value = response.data.gameId; + } + }); + } + function clearUserInfo() { username.value = null; userId.value = null; } - return { username, userId, setUserInfo, clearUserInfo }; + function clearGameId() { + gameId.value = null; + } + + return { username, userId, gameId, setUserInfo, requestState, clearUserInfo, setGameId, clearGameId }; }); diff --git a/src/composables/useWebsocket.ts b/src/composables/useWebsocket.ts index 8de496d..12d721a 100644 --- a/src/composables/useWebsocket.ts +++ b/src/composables/useWebsocket.ts @@ -6,8 +6,13 @@ import { sendEventAndWait, onEvent, isWebSocketConnected, + setDefaultHandler, } from "@/services/ws"; +function defaultEventHandler(data: (data: T) => void) { + setDefaultHandler(data); +} + export function useWebSocket() { const isConnected = ref(isWebSocketConnected()); const lastMessage = ref(null); @@ -51,5 +56,6 @@ export function useWebSocket() { sendAndWait: sendEventAndWait, useEvent, + defaultEventHandler }; } diff --git a/src/services/ws.ts b/src/services/ws.ts index 220aa72..7b59a09 100644 --- a/src/services/ws.ts +++ b/src/services/ws.ts @@ -1,3 +1,6 @@ +import {useIngame} from "@/composables/useIngame.ts"; +import type {GameInfo, LobbyInfo, TieInfo, TrumpInfo, WonInfo} from "@/types/GameTypes.ts"; + const api = window.__RUNTIME_CONFIG__?.API_URL; // ---------- Types --------------------------------------------------------- @@ -6,6 +9,8 @@ export type ServerMessage = { id?: string; event?: string; status?: "success" | "error"; + state?: "Lobby" | "InGame" | "SelectTrump" | "TieBreak" | "FinishedMatch"; + stateData?: GameInfo | LobbyInfo | TieInfo | TrumpInfo | WonInfo; data?: T; error?: string; }; @@ -28,9 +33,12 @@ let ws: WebSocket | null = null; const pending = new Map(); const handlers = new Map(); +const uState = useIngame(); let heartbeatTimer: ReturnType | null = null; +let defaultHandler: HandlerFn | null = null; + function uuid(): string { return crypto.randomUUID(); } @@ -73,7 +81,7 @@ function setupSocketHandlers(socket: WebSocket) { return; } - const { id, event, status, data } = msg; + const { id, event, state, stateData, status, data } = msg; // RPC response branch if (id && status) { @@ -91,6 +99,10 @@ function setupSocketHandlers(socket: WebSocket) { return; } + if (state && stateData) { + uState.setIngame(state, stateData); + } + // Server event → handler branch if (id && event) { const handler = handlers.get(event); @@ -103,7 +115,17 @@ function setupSocketHandlers(socket: WebSocket) { if (!handler) { console.warn("[WS] No handler for event:", event); - reply("error", `No handler for '${event}'`); + if (defaultHandler) { + try { + await defaultHandler(data ?? {}); + reply("success"); + } catch (err) { + reply("error", (err as Error).message); + + } + } else { + reply("error", `No handler for '${event}'`); + } return; } @@ -232,5 +254,9 @@ export function onEvent(event: string, handler: HandlerFn) { handlers.set(event, handler); } +export function setDefaultHandler(handler: HandlerFn) { + defaultHandler = handler; +} + export const isWebSocketConnected = () => !!ws && ws.readyState === WebSocket.OPEN; diff --git a/src/views/Game.vue b/src/views/Game.vue new file mode 100644 index 0000000..ccc2934 --- /dev/null +++ b/src/views/Game.vue @@ -0,0 +1,33 @@ + + + + + -- 2.52.0 From 6d064e97109a25fefd6e930c63e4deb8cdab5304 Mon Sep 17 00:00:00 2001 From: Janis Date: Wed, 10 Dec 2025 22:34:48 +0100 Subject: [PATCH 2/2] feat: FRO-24 Create Played Cards Component --- src/components/Ingame.vue | 1 + src/components/ingame/HandC.vue | 39 ++++++++++++++++++++++++--------- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/components/Ingame.vue b/src/components/Ingame.vue index 0036a68..5b139b2 100644 --- a/src/components/Ingame.vue +++ b/src/components/Ingame.vue @@ -9,6 +9,7 @@ import ScoreboardC from "@/components/ingame/ScoreboardC.vue"; import TurnC from "@/components/ingame/TurnC.vue"; const ig = useIngame() +