'use strict'; // --------------------------------------------------------------------------- // Focus Timer // --------------------------------------------------------------------------- const TIMER_WORK = 25 * 60; const TIMER_BREAK = 5 * 60; let timerSeconds = TIMER_WORK; let timerRunning = false; let timerIsBreak = false; let timerInterval = null; function timerTick() { timerSeconds--; renderTimer(); if (timerSeconds <= 0) { clearInterval(timerInterval); timerInterval = null; timerRunning = false; if (!timerIsBreak) { // work session ended → start break timerIsBreak = true; recordFocusSession(); timerSeconds = TIMER_BREAK; showTimerNotification('Pause! 5 Minuten.'); // auto-pause playback during break if (audio.src && !audio.paused) audio.pause(); } else { // break ended → reset to work timerIsBreak = false; timerSeconds = TIMER_WORK; showTimerNotification('Pause vorbei. Weiter geht’s.'); } renderTimer(); } } function toggleTimer() { if (timerRunning) { clearInterval(timerInterval); timerInterval = null; timerRunning = false; } else { if ('Notification' in window && Notification.permission === 'default') { Notification.requestPermission(); } timerRunning = true; timerInterval = setInterval(timerTick, 1000); } renderTimer(); } function resetTimer() { clearInterval(timerInterval); timerInterval = null; timerRunning = false; timerIsBreak = false; timerSeconds = TIMER_WORK; renderTimer(); } function renderTimer() { const m = String(Math.floor(timerSeconds / 60)).padStart(2, '0'); const s = String(timerSeconds % 60).padStart(2, '0'); const display = $('timer-display'); const btn = $('timer-toggle-btn'); const label = $('timer-phase-label'); if (display) display.textContent = `${m}:${s}`; if (btn) btn.textContent = timerRunning ? '⏸' : '▶'; if (label) label.textContent = timerIsBreak ? 'Pause' : 'Fokus'; // colour the display red when break if (display) display.style.color = timerIsBreak ? '#e63946' : ''; } function showTimerNotification(msg) { if ('Notification' in window && Notification.permission === 'granted') { new Notification('diora', { body: msg }); } // also flash in the timer label const label = $('timer-phase-label'); if (label) { label.textContent = msg; setTimeout(() => renderTimer(), 3000); } } // --------------------------------------------------------------------------- // Focus session recording // --------------------------------------------------------------------------- async function recordFocusSession() { try { await fetch('/radio/focus/record/', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': getCsrfToken() }, body: JSON.stringify({ station_name: currentStation ? currentStation.name : '', duration_minutes: 25, }), }); loadFocusStats(); } catch (e) {} } async function loadFocusStats() { try { const res = await fetch('/radio/focus/stats/'); const data = await res.json(); const widget = document.getElementById('focus-today-widget'); if (widget) { if (data.today_sessions > 0) { widget.textContent = `Heute: ${data.today_sessions} ${data.today_sessions !== 1 ? 'Sitzungen' : 'Sitzung'} · ${data.today_minutes} min`; widget.style.display = ''; } else { widget.style.display = 'none'; } } // populate focus tab const tbody = document.getElementById('focus-tbody'); if (!tbody) return; tbody.innerHTML = ''; if (!data.sessions.length) { tbody.innerHTML = 'Noch keine Fokus-Sitzungen. Starte den Timer!'; return; } data.sessions.forEach(s => { const tr = document.createElement('tr'); const dt = new Date(s.completed_at).toLocaleString([], {dateStyle: 'short', timeStyle: 'short'}); tr.innerHTML = `${dt}${escapeHtml(s.station_name || '—')}${s.duration_minutes} min`; tbody.appendChild(tr); }); } catch (e) {} } // --------------------------------------------------------------------------- // Do Not Disturb / focus mode // --------------------------------------------------------------------------- let dndActive = false; function toggleDNDLight() { document.body.classList.toggle('dnd-dark'); const btn = $('dnd-light-btn'); if (btn) btn.style.opacity = document.body.classList.contains('dnd-dark') ? '0.4' : '1'; } function toggleDND() { dndActive = !dndActive; if (!dndActive) document.body.classList.remove('dnd-dark'); document.body.classList.toggle('dnd-mode', dndActive); const btn = $('dnd-btn'); if (btn) btn.classList.toggle('active', dndActive); if (dndActive) { const el = document.documentElement; if (el.requestFullscreen) el.requestFullscreen(); else if (el.webkitRequestFullscreen) el.webkitRequestFullscreen(); } else { if (document.fullscreenElement && document.exitFullscreen) document.exitFullscreen(); else if (document.webkitFullscreenElement && document.webkitExitFullscreen) document.webkitExitFullscreen(); } } // Exit DND on Escape (browser also exits fullscreen on Escape, so sync state) document.addEventListener('fullscreenchange', () => { if (!document.fullscreenElement && dndActive) { dndActive = false; document.body.classList.remove('dnd-mode'); const btn = $('dnd-btn'); if (btn) btn.classList.remove('active'); } }); document.addEventListener('keydown', e => { if (e.key === 'Escape' && dndActive) toggleDND(); });