feat: auto-save replays on game over and page unload
- Add autoSave() function that saves replay silently on game over - Add beforeunload listener to save incomplete games when navigating away - Add prominent 'Save Now' button in status bar (visible during play) - Show 'Replay auto-saved!' notification in status bar - Add gameSaved flag to prevent duplicate saves
This commit is contained in:
@@ -14,6 +14,7 @@ function initGame() {
|
||||
|
||||
let selectedPieceId = null;
|
||||
let currentPlacements = [];
|
||||
let gameSaved = false;
|
||||
|
||||
async function refresh() {
|
||||
const response = await fetch(`/api/game/${sessionId}/state`);
|
||||
@@ -87,6 +88,7 @@ function initGame() {
|
||||
sidebar.appendChild(controls);
|
||||
|
||||
document.getElementById('save-btn').addEventListener('click', saveReplay);
|
||||
document.getElementById('status-save-btn').addEventListener('click', saveReplay);
|
||||
}
|
||||
|
||||
function renderPiece(piece, playerColor) {
|
||||
@@ -194,27 +196,48 @@ function initGame() {
|
||||
const response = await fetch(`/api/game/${sessionId}/save`, { method: 'POST' });
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
gameSaved = true;
|
||||
alert('Replay saved!');
|
||||
} else {
|
||||
alert('Failed to save: ' + result.error);
|
||||
}
|
||||
}
|
||||
|
||||
function renderStatusBar(state) {
|
||||
async function autoSave() {
|
||||
if (gameSaved) return;
|
||||
try {
|
||||
const response = await fetch(`/api/game/${sessionId}/save`, { method: 'POST' });
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
gameSaved = true;
|
||||
const statusBar = document.getElementById('status-bar');
|
||||
statusBar.textContent += ' | Replay auto-saved!';
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Auto-save failed:', e);
|
||||
}
|
||||
}
|
||||
|
||||
function renderStatusBar(state) {
|
||||
const statusText = document.getElementById('status-text');
|
||||
const saveBtn = document.getElementById('status-save-btn');
|
||||
|
||||
if (state.game_over) {
|
||||
if (state.winner !== null) {
|
||||
statusBar.textContent = 'Game Over! Winner: ' + state.players[state.winner].name;
|
||||
statusText.textContent = 'Game Over! Winner: ' + state.players[state.winner].name;
|
||||
} else {
|
||||
statusBar.textContent = 'Game Over! Tie!';
|
||||
statusText.textContent = 'Game Over! Tie!';
|
||||
}
|
||||
saveBtn.style.display = 'none';
|
||||
} else {
|
||||
statusBar.textContent = 'Current player: ' + state.players[state.current_player].name;
|
||||
statusText.textContent = 'Current player: ' + state.players[state.current_player].name;
|
||||
saveBtn.style.display = 'inline-block';
|
||||
}
|
||||
}
|
||||
|
||||
function handleGameOver(state) {
|
||||
const statusBar = document.getElementById('status-bar');
|
||||
const statusText = document.getElementById('status-text');
|
||||
const saveBtn = document.getElementById('status-save-btn');
|
||||
let text = 'Game Over! ';
|
||||
if (state.winner !== null) {
|
||||
text += 'Winner: ' + state.players[state.winner].name;
|
||||
@@ -224,15 +247,24 @@ function initGame() {
|
||||
text += ' | Scores: ' + state.players.map(function(p) {
|
||||
return p.name + ': ' + p.score;
|
||||
}).join(', ');
|
||||
statusBar.textContent = text;
|
||||
statusText.textContent = text;
|
||||
saveBtn.style.display = 'none';
|
||||
|
||||
document.querySelectorAll('.piece').forEach(function(p) {
|
||||
p.style.pointerEvents = 'none';
|
||||
p.style.opacity = '0.5';
|
||||
});
|
||||
|
||||
autoSave();
|
||||
}
|
||||
|
||||
refresh();
|
||||
|
||||
window.addEventListener('beforeunload', function() {
|
||||
if (!gameSaved) {
|
||||
navigator.sendBeacon(`/api/game/${sessionId}/save`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function initReplay() {
|
||||
|
||||
@@ -342,6 +342,24 @@ h3 {
|
||||
font-weight: bold;
|
||||
font-size: 1.05em;
|
||||
min-height: 24px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#status-save-btn {
|
||||
padding: 4px 12px;
|
||||
background: var(--blue);
|
||||
color: var(--bg);
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 0.9em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#status-save-btn:hover {
|
||||
background: #5bb3ff;
|
||||
}
|
||||
|
||||
/* ===== Replay Page ===== */
|
||||
|
||||
@@ -15,7 +15,10 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="status-bar">Loading...</div>
|
||||
<div id="status-bar">
|
||||
<span id="status-text">Loading...</span>
|
||||
<button id="status-save-btn" style="display:none;">Save Now</button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
window.GAME_SESSION_ID = "{{ session_id }}";
|
||||
|
||||
Reference in New Issue
Block a user