dudi/public/js/app.js

107 lines
4 KiB
JavaScript
Raw Permalink Normal View History

2026-05-08 09:48:06 +00:00
import { state } from './state.js';
import { applyLocale } from './i18n.js';
import { api, loadGoals } from './api.js';
import { updateHeader } from './ui.js';
import { showLogin, showRegister, showResetPassword } from './auth.js';
import { openNew, openData } from './sheets.js';
import { render } from './render.js';
document.addEventListener('session-expired', () => showLogin());
document.getElementById('btnNew').onclick = openNew;
document.getElementById('btnData').onclick = openData;
document.querySelector('.hdr-logo').onclick = () => {
loadGoals().then(g => { state.goals = g; render(); }).catch(() => {});
};
updateHeader();
const _qs = new URLSearchParams(window.location.search);
const inviteToken = _qs.get('invite');
const resetSelector = _qs.get('reset_selector');
const resetToken = _qs.get('reset_token');
if (inviteToken || resetSelector) history.replaceState(null, '', location.pathname);
if (resetSelector && resetToken) {
applyLocale(null); render(); showResetPassword(resetSelector, resetToken);
} else {
api('GET', 'me')
.then(r => {
state.userName = r.name || ''; state.isAdmin = r.is_admin || false;
applyLocale(r.locale); updateHeader();
return loadGoals();
})
.then(g => { state.goals = g; render(); })
.catch(() => {
applyLocale(null); render();
if (inviteToken) showRegister(inviteToken);
else showLogin();
});
}
function scheduleMidnight() {
const n = new Date();
const ms = new Date(n.getFullYear(), n.getMonth(), n.getDate() + 1, 0, 0, 5).getTime() - n.getTime();
setTimeout(() => {
state.TODAY = new Date(); state.TODAY.setHours(0, 0, 0, 0);
state.selDay = {}; state.collapsed = {};
updateHeader(); render(); scheduleMidnight();
}, ms);
}
scheduleMidnight();
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'visible') {
const n = new Date(); n.setHours(0, 0, 0, 0);
if (n.getTime() !== state.TODAY.getTime()) {
state.TODAY = n; state.selDay = {}; state.collapsed = {};
render(); scheduleMidnight();
}
loadGoals().then(g => { state.goals = g; render(); }).catch(() => {});
}
});
(function() {
const swEl = document.getElementById('sw');
let swState = 0, start = 0, elapsed = 0, raf = null, wakeLock = null;
function acquireWakeLock() {
if (!('wakeLock' in navigator)) return;
navigator.wakeLock.request('screen').then(s => { wakeLock = s; }).catch(() => {});
}
function releaseWakeLock() {
if (wakeLock) { wakeLock.release(); wakeLock = null; }
}
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'visible' && swState === 1) acquireWakeLock();
});
function getMs() { return swState === 1 ? elapsed + (Date.now() - start) : elapsed; }
function updateFillBtns() {
const show = getMs() >= 1000;
document.querySelectorAll('.btn-sw-fill').forEach(b => { b.style.display = show ? '' : 'none'; });
}
function fmt(ms) { return (ms / 1000).toFixed(2) + 's'; }
function tick() { swEl.textContent = fmt(Date.now() - start + elapsed); updateFillBtns(); raf = requestAnimationFrame(tick); }
swEl.addEventListener('click', () => {
if (swState === 0) {
start = Date.now(); elapsed = 0; swEl.classList.add('running');
swState = 1; tick(); acquireWakeLock();
} else if (swState === 1) {
cancelAnimationFrame(raf); elapsed += Date.now() - start;
swEl.textContent = fmt(elapsed); swEl.classList.remove('running');
swState = 2; updateFillBtns(); releaseWakeLock();
} else {
cancelAnimationFrame(raf); elapsed = 0; swEl.textContent = '0.00s';
swEl.classList.remove('running'); swState = 0; updateFillBtns(); releaseWakeLock();
}
});
document.addEventListener('click', e => {
if (!e.target.classList.contains('btn-sw-fill')) return;
const inp = e.target.closest('.add-row, .qb-row').querySelector('.num-in');
if (inp) { inp.value = Math.floor(getMs() / 1000); inp.dispatchEvent(new Event('input')); }
});
})();