added timer dialogue after finish up
This commit is contained in:
@@ -338,6 +338,10 @@
|
||||
background-color: rgba(0,0,0,0.8);
|
||||
}
|
||||
|
||||
.modal.no-close {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background-color: #2a2a2a;
|
||||
margin: 15% auto;
|
||||
@@ -346,6 +350,11 @@
|
||||
width: 90%;
|
||||
max-width: 500px;
|
||||
border: 2px solid #404040;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.modal.no-close .modal-content {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.modal h2 {
|
||||
@@ -676,10 +685,9 @@
|
||||
</div>
|
||||
|
||||
<!-- Finish-Up Modal -->
|
||||
<div id="finishUpModal" class="modal">
|
||||
<div id="finishUpModal" class="modal no-close">
|
||||
<div class="modal-content" style="max-width: 400px;">
|
||||
<h2>🔔 Timer Completed!</h2>
|
||||
|
||||
<p id="finishUpMessage" style="margin: 20px 0; color: #e0e0e0;"></p>
|
||||
<p style="color: #e0e0e0;">Would you like 2 minutes of finish-up time?</p>
|
||||
<div class="timer-controls">
|
||||
@@ -689,10 +697,22 @@
|
||||
</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 id="timerAudio" preload="auto">
|
||||
<source src="/timer-complete.mp3" type="audio/mpeg">
|
||||
Your browser does not support the audio element.
|
||||
<audio id="timerAudio" preload="auto" loop>
|
||||
<source src="timer-complete.mp3" type="audio/mpeg">
|
||||
<source src="timer-complete.wav" type="audio/wav">
|
||||
<source src="timer-complete.ogg" type="audio/ogg">
|
||||
</audio>
|
||||
|
||||
<script>
|
||||
@@ -757,11 +777,10 @@
|
||||
socket.on('timerComplete', (data) => {
|
||||
const { pointId, type, offerFinishUp } = data;
|
||||
|
||||
// Play completion sound
|
||||
playCompletionSound();
|
||||
|
||||
if (offerFinishUp) {
|
||||
// Show custom finish-up modal
|
||||
// Play sound and show finish-up modal
|
||||
startAlarmSound();
|
||||
|
||||
const point = findPoint(pointId);
|
||||
const pointName = point ? (point.customName || point.type) : 'Timer';
|
||||
document.getElementById('finishUpMessage').textContent = `${pointName} point completed!`;
|
||||
@@ -769,14 +788,24 @@
|
||||
|
||||
// Set up button handlers
|
||||
document.getElementById('finishUpYes').onclick = () => {
|
||||
stopAlarmSound();
|
||||
socket.emit('finishUpResponse', { pointId, accepted: true });
|
||||
closeModal('finishUpModal');
|
||||
};
|
||||
|
||||
document.getElementById('finishUpNo').onclick = () => {
|
||||
stopAlarmSound();
|
||||
socket.emit('finishUpResponse', { pointId, accepted: false });
|
||||
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
|
||||
function playCompletionSound() {
|
||||
let audioPlaying = false;
|
||||
|
||||
function startAlarmSound() {
|
||||
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 => {
|
||||
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) {
|
||||
const modals = document.querySelectorAll('.modal');
|
||||
const modals = document.querySelectorAll('.modal:not(.no-close)');
|
||||
modals.forEach(modal => {
|
||||
if (event.target === modal) {
|
||||
modal.style.display = 'none';
|
||||
|
77
server.js
77
server.js
@@ -53,6 +53,7 @@ const initialState = {
|
||||
activityLog: [],
|
||||
togetherUsedToday: 0,
|
||||
lastResetDate: new Date().toDateString(),
|
||||
lastWeeklyReset: '',
|
||||
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
|
||||
server.listen(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
|
||||
checkDayReset();
|
||||
|
||||
// Start scheduled resets
|
||||
scheduleResets();
|
||||
|
||||
// Restore any active timers
|
||||
Object.entries(gameState.activeTimers).forEach(([pointId, timer]) => {
|
||||
const elapsed = Date.now() - timer.startTime;
|
||||
|
Reference in New Issue
Block a user