SyntaxSquad/DADProject#16 fix: Cancle and Return is working and some other fixes involving game duration

This commit is contained in:
AfonsoCMSousa
2026-01-03 02:19:00 +00:00
parent 7e6766c726
commit 3dcd8df4c7
9 changed files with 227 additions and 143 deletions
@@ -244,4 +244,19 @@ class GameController extends Controller
'game' => $game->fresh(),
]);
}
public function destroy(Game $game)
{
// Ensure only the host can delete, and ONLY if it's still Pending
if ($game->player1_user_id !== auth()->id()) {
return response()->json(['message' => 'Unauthorized'], 403);
}
if ($game->status !== 'Pending') {
return response()->json(['message' => 'Cannot delete a game in progress'], 400);
}
$game->delete();
return response()->noContent();
}
}
+73 -19
View File
@@ -291,18 +291,88 @@ const handleBeforeUnload = (event) => {
}
};
onBeforeRouteLeave((to, from, next) => {
if (gameOver.value || !shouldShowBoard.value) {
// --- FIX: SURRENDER LOGIC ---
const handleSurrender = () => {
if(confirm('Are you sure you want to surrender? You will lose.')) {
wsStore.surrender(gameId.value)
}
}
const handleCloseModal = async () => {
// 1. If game is still in Lobby (Pending) and I am the Host, DELETE it.
if (!shouldShowBoard.value && amIHost.value && !gameOver.value) {
try {
// This calls Route::delete('/{game}', ...) in your api.php
await axios.delete(`${API_BASE_URL}/games/${gameId.value}`);
console.log("Lobby cancelled and deleted.");
} catch (e) {
console.error("Failed to delete pending game:", e);
}
}
// 2. Notify server of voluntary exit (to skip the 10s reconnect timer)
if (wsStore.socket) {
wsStore.socket.emit('notify_disconnect');
}
// 3. Disconnect and go home
wsStore.disconnect();
router.push({ name: 'home' });
}
// --- LIFECYCLE ---
onBeforeRouteLeave(async (to, from, next) => {
// CASE A: Game is Pending (Waiting Room)
if (!shouldShowBoard.value && !gameOver.value) {
if (amIHost.value) {
// Ask for confirmation to close the lobby
const confirmClose = window.confirm("This will cancel the lobby. Are you sure?");
if (confirmClose) {
try {
// Delete the game from DB so it doesn't get stuck as "Pending"
await axios.delete(`${API_BASE_URL}/games/${gameId.value}`);
} catch(e) { console.error(e) }
if(wsStore.socket) wsStore.socket.emit('notify_disconnect');
wsStore.disconnect();
next();
} else {
next(false); // Stay on page
}
return;
} else {
// If I am NOT host (just a guest waiting), just leave
if(wsStore.socket) wsStore.socket.emit('notify_disconnect');
wsStore.disconnect();
next();
return;
}
}
// CASE B: Game is Over (Standard exit)
if (gameOver.value) {
next();
return;
}
// CASE C: Game is Active/Playing (The Surrender Logic we built before)
const answer = window.confirm(
"If you leave, you will be register has 'losing' or 'surrendered', Are you sure you want to leave?"
);
if (answer) {
wsStore.surrender(gameId.value);
// ... (Your existing surrender logic) ...
try { await wsStore.surrender(gameId.value); } catch (e) {}
if (wsStore.socket) {
wsStore.socket.emit('notify_disconnect', () => {
wsStore.disconnect();
next();
});
setTimeout(() => { if (wsStore.connected) { wsStore.disconnect(); next(); } }, 500);
return;
}
wsStore.disconnect();
next();
} else {
@@ -310,22 +380,6 @@ onBeforeRouteLeave((to, from, next) => {
}
});
// --- FIX: SURRENDER LOGIC ---
const handleSurrender = () => {
if(confirm('Are you sure you want to surrender? You will lose.')) {
// Send event to server. Do NOT disconnect locally yet.
// Wait for the server to send "Game Over" event back.
wsStore.surrender(gameId.value)
}
}
const handleCloseModal = () => {
wsStore.disconnect()
router.push({ name: 'home' })
}
// --- LIFECYCLE ---
onMounted(async () => {
await fetchGameMetadata()
wsStore.connect()
+2
View File
@@ -97,6 +97,8 @@ const getPendingMatches = async (mode = selectedMode.value) => {
mLoading.value = true;
try {
// FIX: Incorrect endpoint used previously
// TAG: 404
const response = await axios.get(`${API_BASE_URL}/matches/open`, {
params: {
type: mode,
+7 -1
View File
@@ -621,7 +621,8 @@
</div>
<div class="text-right">
<div class="font-bold text-sm">{{ game.amount }} pts</div>
<div class="font-bold text-sm">{{ game.points }} pts</div>
<div class="text-[10px] text-muted-foreground">
{{ new Date(game.began_at).toLocaleDateString() }}
{{
@@ -1185,10 +1186,15 @@ const fetchData = async () => {
return {
id: game.id,
player1_id: game.player1.id,
player2_id: game.player2.id,
variant: `Type ${game.type}`,
outcome: outcome,
opponent: null,
amount: game.total_points || 0,
points: game.player1_user_id === authStore.currentUser.id
? game.player1_points
: game.player2_points,
duration: `${Math.floor(game.total_time / 60)}m ${game.total_time % 60}s`,
began_at: game.began_at,
details: {
-1
View File
@@ -92,7 +92,6 @@ export const useSocketStore = defineStore('websocket', () => {
socket.value.emit('play-card', { gameId, cardId })
}
// --- NEW: Surrender Action ---
const surrender = (gameId) => {
if (!socket.value?.connected) return
console.log('[Game] Surrendering:', gameId)
+1
View File
@@ -96,6 +96,7 @@ const performGameEnd = (io, game, gameId, winnerId, loserId, reason) => {
action: "game_ended",
winnerId: parseInt(winnerId),
loserId: parseInt(loserId),
totalTime: game.startTime ? Math.floor((Date.now() - game.startTime) / 1000) : 0,
isDraw: false,
player1_points: game.players[Object.keys(game.players)[0]]?.points || 0,
player2_points: game.players[Object.keys(game.players)[1]]?.points || 0,
+12 -3
View File
@@ -12,7 +12,9 @@ export const server = {
const API_URL = "http://localhost:8000/api/v1";
const disconnectTimers = new Map();
const DISCONNECT_TIMEOUT = 1 * 60 * 1000; // 1 minutes
const RECONNECT_GRACE_PERIOD = 1 * 60 * 1000; // 1 minutes
const userId = (socket) => socket.user && socket.user.id;
export const serverStart = (port) => {
server.io = new Server(port, {
@@ -75,16 +77,23 @@ export const serverStart = (port) => {
server.io.on("connection", (socket) => {
gameEvents(server.io, socket);
console.log(`[Connection] User ${userId} connected. Socket ID: ${socket.id}`);
matchEvents(server.io, socket);
handleConnectionEvents(server.io, socket);
socket.on("notify_disconnect", () => {
socket.isVoluntaryDisconnect = true;
});
socket.on("disconnect", () => {
// Remove from active socket list
removeUser(socket.id);
if (socket.isVoluntaryDisconnect) {
console.log(`[Disconnect] User ${userId} disconnected voluntarily. No surrender needed.`);
return;
}
if (userId) {
console.log(`[Disconnect] User ${userId} disconnected. Waiting ${RECONNECT_GRACE_PERIOD / 1000}s...`);
-2
View File
@@ -33,5 +33,3 @@ export const removeGame = (id) => {
}
};
export { createGame, getGame, removeGame };