stackchain-dashboard/frontend/today-lock-screen.js
timmy a7f4f9714f
All checks were successful
CI / lint (pull_request) Successful in 2m55s
CI / build-release (pull_request) Successful in 6s
CI / browser-journey (pull_request) Successful in 2m21s
CI / release-candidate (pull_request) Has been skipped
feat: control Today from mobile lock screen (Closes #1026)
2026-08-17 14:47:07 +00:00

108 lines
3.4 KiB
JavaScript

function createTodayLockScreen({
storage,
getLogin,
serviceWorker,
NotificationRef,
control,
status,
locationRef,
historyRef,
onAction = () => {},
}) {
const preferenceKey = () => {
const login = String(getLogin?.() || '').trim().toLowerCase();
return login ? 'stackchain.today-lock-screen.v1.' + encodeURIComponent(login) : '';
};
const supported = Boolean(serviceWorker && NotificationRef);
const enabled = () => {
const key = preferenceKey();
return Boolean(key && storage?.getItem(key) === '1');
};
const setStatus = message => {
if (status) status.textContent = message;
};
const post = async message => {
const target = serviceWorker?.controller || (await serviceWorker?.ready)?.active;
target?.postMessage?.(message);
};
const hide = () => post({
type:'stackchain-today-lock-screen', active:false, running:false,
});
const render = () => {
if (control) {
control.checked = enabled();
control.disabled = !supported;
}
if (!supported) setStatus('Lock-screen controls are not supported on this device.');
else if (enabled()) setStatus('Lock-screen Today controls are on.');
};
const consumeAction = action => {
if (!enabled() || !['pause', 'resume'].includes(action)) return false;
onAction(action);
return true;
};
const api = {
enabled,
async enable() {
if (!supported) {
render();
return false;
}
const permission = NotificationRef.permission === 'granted' ?
'granted' : await NotificationRef.requestPermission();
if (permission !== 'granted') {
if (control) control.checked = false;
setStatus(permission === 'denied' ?
'Lock-screen controls are blocked in browser settings.' :
'Lock-screen controls were not enabled.');
return false;
}
const key = preferenceKey();
if (!key) return false;
storage?.setItem(key, '1');
render();
return true;
},
async disable() {
const key = preferenceKey();
if (key) storage?.removeItem(key);
if (control) control.checked = false;
setStatus('Lock-screen Today controls are off.');
await hide();
return true;
},
async sync(snapshot, active) {
if (!enabled() || NotificationRef?.permission !== 'granted') return false;
const visible = Boolean(active && snapshot?.identity);
await post({
type:'stackchain-today-lock-screen',
active:visible,
running:visible && Boolean(snapshot.running),
});
return true;
},
consumeLaunchAction() {
let url;
try { url = new URL(locationRef?.href || ''); }
catch (_error) { return false; }
const action = url.searchParams.get('today_timer_action');
if (!['pause', 'resume'].includes(action)) return false;
url.searchParams.delete('today_timer_action');
historyRef?.replaceState?.(null, '', url.pathname + url.search + url.hash);
return consumeAction(action);
},
render,
};
control?.addEventListener?.('change', () => {
if (control.checked) api.enable();
else api.disable();
});
serviceWorker?.addEventListener?.('message', event => {
if (event.data?.type === 'stackchain-today-timer-action') consumeAction(String(event.data.action || ''));
});
render();
return api;
}
if (typeof module !== 'undefined' && module.exports) module.exports = createTodayLockScreen;