added timer dialogue after finish up

This commit is contained in:
2025-05-30 01:17:23 -04:00
parent 1a364391d1
commit 965f349817
2 changed files with 176 additions and 12 deletions

View File

@@ -338,6 +338,10 @@
background-color: rgba(0,0,0,0.8); background-color: rgba(0,0,0,0.8);
} }
.modal.no-close {
pointer-events: auto;
}
.modal-content { .modal-content {
background-color: #2a2a2a; background-color: #2a2a2a;
margin: 15% auto; margin: 15% auto;
@@ -346,6 +350,11 @@
width: 90%; width: 90%;
max-width: 500px; max-width: 500px;
border: 2px solid #404040; border: 2px solid #404040;
position: relative;
}
.modal.no-close .modal-content {
pointer-events: auto;
} }
.modal h2 { .modal h2 {
@@ -676,10 +685,9 @@
</div> </div>
<!-- Finish-Up Modal --> <!-- Finish-Up Modal -->
<div id="finishUpModal" class="modal"> <div id="finishUpModal" class="modal no-close">
<div class="modal-content" style="max-width: 400px;"> <div class="modal-content" style="max-width: 400px;">
<h2>🔔 Timer Completed!</h2> <h2>🔔 Timer Completed!</h2>
<p id="finishUpMessage" style="margin: 20px 0; color: #e0e0e0;"></p> <p id="finishUpMessage" style="margin: 20px 0; color: #e0e0e0;"></p>
<p style="color: #e0e0e0;">Would you like 2 minutes of finish-up time?</p> <p style="color: #e0e0e0;">Would you like 2 minutes of finish-up time?</p>
<div class="timer-controls"> <div class="timer-controls">
@@ -689,10 +697,22 @@
</div> </div>
</div> </div>
<!-- Timer Complete Modal -->
<div id="timerCompleteModal" class="modal no-close">
<div class="modal-content" style="max-width: 400px;">
<h2>🔔 Timer Completed!</h2>
<p id="timerCompleteMessage" style="margin: 20px 0; color: #e0e0e0;"></p>
<div class="timer-controls">
<button class="btn btn-primary" onclick="dismissTimerComplete()">Dismiss</button>
</div>
</div>
</div>
<!-- Audio element for timer sounds --> <!-- Audio element for timer sounds -->
<audio id="timerAudio" preload="auto"> <audio id="timerAudio" preload="auto" loop>
<source src="/timer-complete.mp3" type="audio/mpeg"> <source src="timer-complete.mp3" type="audio/mpeg">
Your browser does not support the audio element. <source src="timer-complete.wav" type="audio/wav">
<source src="timer-complete.ogg" type="audio/ogg">
</audio> </audio>
<script> <script>
@@ -757,11 +777,10 @@
socket.on('timerComplete', (data) => { socket.on('timerComplete', (data) => {
const { pointId, type, offerFinishUp } = data; const { pointId, type, offerFinishUp } = data;
// Play completion sound
playCompletionSound();
if (offerFinishUp) { if (offerFinishUp) {
// Show custom finish-up modal // Play sound and show finish-up modal
startAlarmSound();
const point = findPoint(pointId); const point = findPoint(pointId);
const pointName = point ? (point.customName || point.type) : 'Timer'; const pointName = point ? (point.customName || point.type) : 'Timer';
document.getElementById('finishUpMessage').textContent = `${pointName} point completed!`; document.getElementById('finishUpMessage').textContent = `${pointName} point completed!`;
@@ -769,14 +788,24 @@
// Set up button handlers // Set up button handlers
document.getElementById('finishUpYes').onclick = () => { document.getElementById('finishUpYes').onclick = () => {
stopAlarmSound();
socket.emit('finishUpResponse', { pointId, accepted: true }); socket.emit('finishUpResponse', { pointId, accepted: true });
closeModal('finishUpModal'); closeModal('finishUpModal');
}; };
document.getElementById('finishUpNo').onclick = () => { document.getElementById('finishUpNo').onclick = () => {
stopAlarmSound();
socket.emit('finishUpResponse', { pointId, accepted: false }); socket.emit('finishUpResponse', { pointId, accepted: false });
closeModal('finishUpModal'); closeModal('finishUpModal');
}; };
} else if (type === 'finishUp') {
// Finish-up completed - show completion dialog
startAlarmSound();
const point = findPoint(pointId);
const pointName = point ? (point.customName || point.type) : 'Timer';
document.getElementById('timerCompleteMessage').textContent = `${pointName} point finish-up time completed!`;
document.getElementById('timerCompleteModal').style.display = 'block';
} }
}); });
@@ -1281,16 +1310,74 @@
} }
// Audio functions // Audio functions
function playCompletionSound() { let audioPlaying = false;
function startAlarmSound() {
const audioElement = document.getElementById('timerAudio'); const audioElement = document.getElementById('timerAudio');
audioElement.loop = true;
audioElement.play().catch(error => {
console.log('Audio playback failed:', error);
// Fallback - try to create a beep sound
createBeepSound();
});
audioPlaying = true;
}
function stopAlarmSound() {
const audioElement = document.getElementById('timerAudio');
audioElement.pause();
audioElement.currentTime = 0;
audioPlaying = false;
}
function playCompletionSound() {
// Single play for non-alarm situations
const audioElement = document.getElementById('timerAudio');
audioElement.loop = false;
audioElement.play().catch(error => { audioElement.play().catch(error => {
console.log('Audio playback failed:', error); console.log('Audio playback failed:', error);
}); });
} }
// Close modals when clicking outside function createBeepSound() {
// Fallback beep using Web Audio API
try {
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
const oscillator = audioContext.createOscillator();
const gainNode = audioContext.createGain();
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);
oscillator.frequency.value = 800; // Frequency in Hz
gainNode.gain.value = 0.3; // Volume
oscillator.start();
// Create beeping pattern
const beepPattern = () => {
if (!audioPlaying) {
oscillator.stop();
return;
}
gainNode.gain.value = gainNode.gain.value > 0 ? 0 : 0.3;
setTimeout(beepPattern, 500);
};
beepPattern();
} catch (e) {
console.log('Web Audio API not supported');
}
}
function dismissTimerComplete() {
stopAlarmSound();
closeModal('timerCompleteModal');
}
// Close modals when clicking outside (except for no-close modals)
window.onclick = function(event) { window.onclick = function(event) {
const modals = document.querySelectorAll('.modal'); const modals = document.querySelectorAll('.modal:not(.no-close)');
modals.forEach(modal => { modals.forEach(modal => {
if (event.target === modal) { if (event.target === modal) {
modal.style.display = 'none'; modal.style.display = 'none';

View File

@@ -53,6 +53,7 @@ const initialState = {
activityLog: [], activityLog: [],
togetherUsedToday: 0, togetherUsedToday: 0,
lastResetDate: new Date().toDateString(), lastResetDate: new Date().toDateString(),
lastWeeklyReset: '',
connectedClients: 0 connectedClients: 0
}; };
@@ -579,6 +580,79 @@ app.get('/api/config', (req, res) => {
}); });
}); });
// Scheduled tasks
function scheduleResets() {
// Check every minute for reset times
setInterval(() => {
const now = new Date();
const hours = now.getHours();
const minutes = now.getMinutes();
const day = now.getDay(); // 0 = Sunday, 1 = Monday, etc.
// Daily reset at 00:01
if (hours === 0 && minutes === 1) {
// Reset daily limit
const today = new Date().toDateString();
if (gameState.lastResetDate !== today) {
gameState.togetherUsedToday = 0;
gameState.lastResetDate = today;
addLogEntry('Daily together limit reset automatically');
io.emit('stateUpdate', gameState);
saveGameState();
}
}
// Weekly reset on Monday at 00:01
if (day === 1 && hours === 0 && minutes === 1) {
// Check if we haven't already reset this week
const lastWeeklyReset = gameState.lastWeeklyReset || '';
const thisWeek = `${now.getFullYear()}-W${getWeekNumber(now)}`;
if (lastWeeklyReset !== thisWeek) {
// Reset to 5 solo and 4 together
gameState.soloPoints = Array(5).fill(null).map((_, i) => ({
id: `solo_${Date.now()}_${i}`,
type: 'solo',
status: 'available',
duration: CONFIG.timers.solo,
label: '15m'
}));
gameState.togetherPoints = Array(4).fill(null).map((_, i) => ({
id: `together_${Date.now()}_${i}`,
type: 'together',
status: 'available',
duration: CONFIG.timers.together,
label: '1h'
}));
// Clear custom points and timers
gameState.customPoints = [];
gameState.activeTimers = {};
gameState.pausedTimers = {};
gameState.lastWeeklyReset = thisWeek;
// Clear all timer intervals
timerIntervals.forEach((interval) => clearInterval(interval));
timerIntervals.clear();
addLogEntry('Weekly points reset automatically (Monday 00:01)');
io.emit('stateUpdate', gameState);
saveGameState();
}
}
}, 60000); // Check every minute
}
// Helper function to get week number
function getWeekNumber(date) {
const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
const dayNum = d.getUTCDay() || 7;
d.setUTCDate(d.getUTCDate() + 4 - dayNum);
const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
return Math.ceil((((d - yearStart) / 86400000) + 1) / 7);
}
// Start server // Start server
server.listen(CONFIG.port, () => { server.listen(CONFIG.port, () => {
console.log(`Point Tracker Server running on port ${CONFIG.port}`); console.log(`Point Tracker Server running on port ${CONFIG.port}`);
@@ -587,6 +661,9 @@ server.listen(CONFIG.port, () => {
// Check day reset on startup // Check day reset on startup
checkDayReset(); checkDayReset();
// Start scheduled resets
scheduleResets();
// Restore any active timers // Restore any active timers
Object.entries(gameState.activeTimers).forEach(([pointId, timer]) => { Object.entries(gameState.activeTimers).forEach(([pointId, timer]) => {
const elapsed = Date.now() - timer.startTime; const elapsed = Date.now() - timer.startTime;