(feat): Queue/Dequeue button and waiting for match text
This commit is contained in:
parent
6bd3a01f5f
commit
272c6f319c
8 changed files with 154 additions and 20 deletions
4
Makefile
4
Makefile
|
|
@ -144,8 +144,8 @@ nginx-dev/nginx-selfsigned.crt nginx-dev/nginx-selfsigned.key &:
|
||||||
|
|
||||||
fnginx: nginx-dev/nginx nginx-dev/nginx-selfsigned.crt nginx-dev/nginx-selfsigned.key
|
fnginx: nginx-dev/nginx nginx-dev/nginx-selfsigned.crt nginx-dev/nginx-selfsigned.key
|
||||||
nginx-dev/nginx -p ./nginx-dev -c nginx.conf -e /dev/null >/dev/null 2>/dev/null &
|
nginx-dev/nginx -p ./nginx-dev -c nginx.conf -e /dev/null >/dev/null 2>/dev/null &
|
||||||
-(cd ./frontend && pnpm exec tsc --noEmit --watch --preserveWatchOutput) &
|
-(cd ./frontend && npx pnpm exec tsc --noEmit --watch --preserveWatchOutput) &
|
||||||
-(cd ./frontend && pnpm exec vite --clearScreen false)
|
-(cd ./frontend && npx pnpm exec vite --clearScreen false)
|
||||||
wait
|
wait
|
||||||
|
|
||||||
# phony
|
# phony
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ apis/index.ts
|
||||||
index.ts
|
index.ts
|
||||||
models/AllowGuestMessage200Response.ts
|
models/AllowGuestMessage200Response.ts
|
||||||
models/AllowGuestMessage403Response.ts
|
models/AllowGuestMessage403Response.ts
|
||||||
|
models/ApiChatBroadcastPostRequest.ts
|
||||||
models/ChangeDesc200Response.ts
|
models/ChangeDesc200Response.ts
|
||||||
models/ChangeDesc400Response.ts
|
models/ChangeDesc400Response.ts
|
||||||
models/ChangeDesc403Response.ts
|
models/ChangeDesc403Response.ts
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import * as runtime from '../runtime';
|
||||||
import type {
|
import type {
|
||||||
AllowGuestMessage200Response,
|
AllowGuestMessage200Response,
|
||||||
AllowGuestMessage403Response,
|
AllowGuestMessage403Response,
|
||||||
|
ApiChatBroadcastPostRequest,
|
||||||
ChangeDesc200Response,
|
ChangeDesc200Response,
|
||||||
ChangeDesc400Response,
|
ChangeDesc400Response,
|
||||||
ChangeDesc403Response,
|
ChangeDesc403Response,
|
||||||
|
|
@ -71,6 +72,8 @@ import {
|
||||||
AllowGuestMessage200ResponseToJSON,
|
AllowGuestMessage200ResponseToJSON,
|
||||||
AllowGuestMessage403ResponseFromJSON,
|
AllowGuestMessage403ResponseFromJSON,
|
||||||
AllowGuestMessage403ResponseToJSON,
|
AllowGuestMessage403ResponseToJSON,
|
||||||
|
ApiChatBroadcastPostRequestFromJSON,
|
||||||
|
ApiChatBroadcastPostRequestToJSON,
|
||||||
ChangeDesc200ResponseFromJSON,
|
ChangeDesc200ResponseFromJSON,
|
||||||
ChangeDesc200ResponseToJSON,
|
ChangeDesc200ResponseToJSON,
|
||||||
ChangeDesc400ResponseFromJSON,
|
ChangeDesc400ResponseFromJSON,
|
||||||
|
|
@ -169,6 +172,10 @@ import {
|
||||||
TttHistory404ResponseToJSON,
|
TttHistory404ResponseToJSON,
|
||||||
} from '../models/index';
|
} from '../models/index';
|
||||||
|
|
||||||
|
export interface ApiChatBroadcastPostOperationRequest {
|
||||||
|
apiChatBroadcastPostRequest: ApiChatBroadcastPostRequest;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ChangeDescOperationRequest {
|
export interface ChangeDescOperationRequest {
|
||||||
changeDescRequest: ChangeDescRequest;
|
changeDescRequest: ChangeDescRequest;
|
||||||
}
|
}
|
||||||
|
|
@ -260,6 +267,53 @@ export class OpenapiOtherApi extends runtime.BaseAPI {
|
||||||
return await response.value();
|
return await response.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
async apiChatBroadcastPostRaw(requestParameters: ApiChatBroadcastPostOperationRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<void>> {
|
||||||
|
if (requestParameters['apiChatBroadcastPostRequest'] == null) {
|
||||||
|
throw new runtime.RequiredError(
|
||||||
|
'apiChatBroadcastPostRequest',
|
||||||
|
'Required parameter "apiChatBroadcastPostRequest" was null or undefined when calling apiChatBroadcastPost().'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const queryParameters: any = {};
|
||||||
|
|
||||||
|
const headerParameters: runtime.HTTPHeaders = {};
|
||||||
|
|
||||||
|
headerParameters['Content-Type'] = 'application/json';
|
||||||
|
|
||||||
|
|
||||||
|
let urlPath = `/api/chat/broadcast`;
|
||||||
|
|
||||||
|
const response = await this.request({
|
||||||
|
path: urlPath,
|
||||||
|
method: 'POST',
|
||||||
|
headers: headerParameters,
|
||||||
|
query: queryParameters,
|
||||||
|
body: ApiChatBroadcastPostRequestToJSON(requestParameters['apiChatBroadcastPostRequest']),
|
||||||
|
}, initOverrides);
|
||||||
|
|
||||||
|
// CHANGED: Handle all status codes defined in the OpenAPI spec, not just 2xx responses
|
||||||
|
// This allows typed access to error responses (4xx, 5xx) and other status codes.
|
||||||
|
// The code routes responses based on the actual HTTP status code and returns
|
||||||
|
// appropriately typed ApiResponse wrappers for each status code.
|
||||||
|
if (response.status === 200) {
|
||||||
|
// No body response for status 200
|
||||||
|
return new runtime.VoidApiResponse(response);
|
||||||
|
}
|
||||||
|
// CHANGED: Throw error if status code is not handled by any of the defined responses
|
||||||
|
// This ensures all code paths return a value and provides clear error messages for unexpected status codes
|
||||||
|
// Only throw if responses were defined but none matched the actual status code
|
||||||
|
throw new runtime.ResponseError(response, `Unexpected status code: ${response.status}. Expected one of: 200`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
async apiChatBroadcastPost(requestParameters: ApiChatBroadcastPostOperationRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<void> {
|
||||||
|
await this.apiChatBroadcastPostRaw(requestParameters, initOverrides);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
async changeDescRaw(requestParameters: ChangeDescOperationRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<ChangeDesc200Response | ChangeDesc400Response | ChangePassword401Response | ChangeDesc403Response>> {
|
async changeDescRaw(requestParameters: ChangeDescOperationRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<ChangeDesc200Response | ChangeDesc400Response | ChangePassword401Response | ChangeDesc403Response>> {
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
export * from './AllowGuestMessage200Response';
|
export * from './AllowGuestMessage200Response';
|
||||||
export * from './AllowGuestMessage403Response';
|
export * from './AllowGuestMessage403Response';
|
||||||
|
export * from './ApiChatBroadcastPostRequest';
|
||||||
export * from './ChangeDesc200Response';
|
export * from './ChangeDesc200Response';
|
||||||
export * from './ChangeDesc400Response';
|
export * from './ChangeDesc400Response';
|
||||||
export * from './ChangeDesc403Response';
|
export * from './ChangeDesc403Response';
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<div class="displaybox">
|
<div class="displaybox">
|
||||||
<div id="mainbox" class="mainboxDisplay">
|
<div id="mainbox" class="mainboxDisplay">
|
||||||
<button id="JoinQueueBtn" class="btn-style absolute top-4 right-6">New Game</button>
|
<button id="JoinQueueBtn" class="btn-style absolute top-4 right-6">Join Queue</button>
|
||||||
<h1 class="text-3xl font-bold text-gray-800">
|
<h1 class="text-3xl font-bold text-gray-800">
|
||||||
Tic-tac-toe Box<span id="t-username"></span>
|
Tic-tac-toe Box<span id="t-username"></span>
|
||||||
</h1><br>
|
</h1><br>
|
||||||
|
|
@ -9,18 +9,18 @@
|
||||||
X
|
X
|
||||||
</div>
|
</div>
|
||||||
<div class="text-left">
|
<div class="text-left">
|
||||||
<div class="font-semibold text-gray-800" id="playerX-name">${playerX.name}</div>
|
<div class="font-semibold text-gray-800" id="playerX-name"></div>
|
||||||
<div class="text-lg text-gray-800" id="playerX-timer">${playerX.timer}</div>
|
<div class="text-lg text-gray-800" id="playerX-timer"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="text-center text-sm text-gray-800 px-4 whitespace-nowrap">
|
<div class="text-center text-sm text-gray-800 px-4 whitespace-nowrap">
|
||||||
<div id="currentPlayer" class='text-7xl font-bold'></div>
|
<div id="currentPlayer" class='text-7xl font-bold'></div>
|
||||||
<div id="currentPlayerTimer">${currentPlayerTimer}</div>
|
<div id="currentPlayerTimer">Waiting for match...</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="text-right">
|
<div class="text-right">
|
||||||
<div class="font-semibold text-gray-800" id="playerO-name">${playerO.name}</div>
|
<div class="font-semibold text-gray-800" id="playerO-name"></div>
|
||||||
<div class="text-lg text-gray-800" id="playerO-timer">${playerO.timer}</div>
|
<div class="text-lg text-gray-800" id="playerO-timer"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-7xl text-gray-800 mx-4 font-bold" id="playerO">
|
<div class="text-7xl text-gray-800 mx-4 font-bold" id="playerO">
|
||||||
O
|
O
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,12 @@ declare module 'ft_state' {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum QueueState {
|
||||||
|
InQueue = "In Queue",
|
||||||
|
InGame = "In Game",
|
||||||
|
Idle = "Join Queue",
|
||||||
|
};
|
||||||
|
|
||||||
document.addEventListener("ft:pageChange", () => {
|
document.addEventListener("ft:pageChange", () => {
|
||||||
if (window.__state.tttSock !== undefined) window.__state.tttSock.close();
|
if (window.__state.tttSock !== undefined) window.__state.tttSock.close();
|
||||||
if (window.__state.keepAliveInterval !== undefined) clearInterval(window.__state.keepAliveInterval);
|
if (window.__state.keepAliveInterval !== undefined) clearInterval(window.__state.keepAliveInterval);
|
||||||
|
|
@ -48,13 +54,27 @@ async function handleTTT(): Promise<RouteHandlerReturn> {
|
||||||
|
|
||||||
const userXString = document.getElementById("playerX-name");
|
const userXString = document.getElementById("playerX-name");
|
||||||
const userOString = document.getElementById("playerO-name");
|
const userOString = document.getElementById("playerO-name");
|
||||||
if (!userXString || !userOString) {
|
|
||||||
return showError('fatal error');
|
|
||||||
}
|
|
||||||
const currentPlayerIndicator = document.getElementById("currentPlayer");
|
const currentPlayerIndicator = document.getElementById("currentPlayer");
|
||||||
if (!currentPlayerIndicator) {
|
const joinQueueBtn = document.getElementById("JoinQueueBtn");
|
||||||
|
const currentPlayerTimer = document.getElementById("currentPlayerTimer")
|
||||||
|
if (!userXString || !userOString || !currentPlayerIndicator || !joinQueueBtn || !currentPlayerTimer) {
|
||||||
return showError('fatal error');
|
return showError('fatal error');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
joinQueueBtn.addEventListener("click", () => {
|
||||||
|
console.log('===== JOIN QUEUE BUTTON PRESSED =====');
|
||||||
|
if (joinQueueBtn.innerText !== QueueState.Idle) {
|
||||||
|
console.log("== Entering in first if ==");
|
||||||
|
if (joinQueueBtn.innerText === QueueState.InQueue) {
|
||||||
|
console.log("== Entering in second if ==");
|
||||||
|
socket.emit("dequeue");
|
||||||
|
joinQueueBtn.innerText = QueueState.Idle;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
joinQueueBtn.innerText = QueueState.InQueue;
|
||||||
|
socket.emit("enqueue");
|
||||||
|
});
|
||||||
|
|
||||||
let curGame: CurrentGameInfo | null = null;
|
let curGame: CurrentGameInfo | null = null;
|
||||||
let curGameX: {id: string, name: string} | null = null;
|
let curGameX: {id: string, name: string} | null = null;
|
||||||
|
|
@ -64,6 +84,9 @@ async function handleTTT(): Promise<RouteHandlerReturn> {
|
||||||
socket.on('queueEvent', (e) => showInfo(`QueueEvent: ${e}`));
|
socket.on('queueEvent', (e) => showInfo(`QueueEvent: ${e}`));
|
||||||
socket.on('newGame', async (gameState) => {
|
socket.on('newGame', async (gameState) => {
|
||||||
showInfo(`newGame: ${gameState.gameId}`)
|
showInfo(`newGame: ${gameState.gameId}`)
|
||||||
|
|
||||||
|
currentPlayerTimer.innerText = "";
|
||||||
|
joinQueueBtn.innerText = QueueState.InGame;
|
||||||
curGame = { ...gameState, lastState: null };
|
curGame = { ...gameState, lastState: null };
|
||||||
|
|
||||||
let resX = await client.getUser({user: curGame.playerX});
|
let resX = await client.getUser({user: curGame.playerX});
|
||||||
|
|
@ -86,7 +109,8 @@ async function handleTTT(): Promise<RouteHandlerReturn> {
|
||||||
userOString.classList.remove('text-gray-800');
|
userOString.classList.remove('text-gray-800');
|
||||||
userXString.classList.remove('text-red-800');
|
userXString.classList.remove('text-red-800');
|
||||||
userXString.classList.add('text-gray-800');
|
userXString.classList.add('text-gray-800');
|
||||||
} else if (user.id === curGameX.id) {
|
}
|
||||||
|
else if (user.id === curGameX.id) {
|
||||||
userXString.classList.add('text-red-800');
|
userXString.classList.add('text-red-800');
|
||||||
userXString.classList.remove('text-gray-800');
|
userXString.classList.remove('text-gray-800');
|
||||||
userOString.classList.remove('text-red-800');
|
userOString.classList.remove('text-red-800');
|
||||||
|
|
@ -95,7 +119,6 @@ async function handleTTT(): Promise<RouteHandlerReturn> {
|
||||||
userXString.innerText = curGameX.name;
|
userXString.innerText = curGameX.name;
|
||||||
userOString.innerText = curGameO.name;
|
userOString.innerText = curGameO.name;
|
||||||
});
|
});
|
||||||
socket.emit('enqueue');
|
|
||||||
|
|
||||||
const cells = app.querySelectorAll<HTMLDivElement>(".ttt-cell");
|
const cells = app.querySelectorAll<HTMLDivElement>(".ttt-cell");
|
||||||
|
|
||||||
|
|
@ -131,11 +154,10 @@ async function handleTTT(): Promise<RouteHandlerReturn> {
|
||||||
|
|
||||||
socket.on('gameEnd', () => {
|
socket.on('gameEnd', () => {
|
||||||
curGame = null;
|
curGame = null;
|
||||||
socket.emit('enqueue');
|
currentPlayerTimer.innerText = "Waiting for match...";
|
||||||
showInfo('Game is finished, enqueuing directly')
|
joinQueueBtn.innerText = QueueState.Idle;
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
socket.on('gameBoard', (u) => {
|
socket.on('gameBoard', (u) => {
|
||||||
if (curGame === null) {
|
if (curGame === null) {
|
||||||
return showError('Got game State, but no in a game ?');
|
return showError('Got game State, but no in a game ?');
|
||||||
|
|
@ -153,8 +175,6 @@ async function handleTTT(): Promise<RouteHandlerReturn> {
|
||||||
updateUI(u.boardState);
|
updateUI(u.boardState);
|
||||||
|
|
||||||
if (u.gameState && u.gameState !== "ongoing") {
|
if (u.gameState && u.gameState !== "ongoing") {
|
||||||
// grid?.classList.add("pointer-events-none");
|
|
||||||
|
|
||||||
if (u.gameState !== curGame.lastState) {
|
if (u.gameState !== curGame.lastState) {
|
||||||
curGame.lastState = u.gameState;
|
curGame.lastState = u.gameState;
|
||||||
switch (u.gameState) {
|
switch (u.gameState) {
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,35 @@
|
||||||
"components": {
|
"components": {
|
||||||
"schemas": {}
|
"schemas": {}
|
||||||
},
|
},
|
||||||
"paths": {},
|
"paths": {
|
||||||
|
"/api/chat/broadcast": {
|
||||||
|
"post": {
|
||||||
|
"requestBody": {
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"nextGame"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"nextGame": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Default Response"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"servers": [
|
"servers": [
|
||||||
{
|
{
|
||||||
"url": "https://local.maix.me:8888",
|
"url": "https://local.maix.me:8888",
|
||||||
|
|
|
||||||
|
|
@ -1917,6 +1917,36 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/chat/broadcast": {
|
||||||
|
"post": {
|
||||||
|
"requestBody": {
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"nextGame"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"nextGame": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Default Response"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tags": [
|
||||||
|
"openapi_other"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/ttt/history/{user}": {
|
"/api/ttt/history/{user}": {
|
||||||
"get": {
|
"get": {
|
||||||
"operationId": "tttHistory",
|
"operationId": "tttHistory",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue