106 lines
4 KiB
JavaScript
106 lines
4 KiB
JavaScript
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')); }
|
|
});
|
|
})();
|