133 lines
4.0 KiB
JavaScript
133 lines
4.0 KiB
JavaScript
(function (root, factory) {
|
|
if (typeof module === 'object' && module.exports) module.exports = factory;
|
|
else root.createMobileStartDay = factory;
|
|
})(typeof self !== 'undefined' ? self : this, function createMobileStartDay(options) {
|
|
const checkpointKey = 'stackchain.mobile-start-day.v1';
|
|
const storage = options.storage || (typeof localStorage !== 'undefined' ? localStorage : null);
|
|
const reviewOrder = [
|
|
['agenda', 'Agenda'],
|
|
['attention', 'Attention'],
|
|
['update', 'Updates'],
|
|
['filed', 'Filed'],
|
|
];
|
|
|
|
function count(value) {
|
|
return Math.max(0, Number(value) || 0);
|
|
}
|
|
|
|
function localDay() {
|
|
const now = new Date();
|
|
const pad = value => String(value).padStart(2, '0');
|
|
return now.getFullYear() + '-' + pad(now.getMonth() + 1) + '-' + pad(now.getDate());
|
|
}
|
|
|
|
function identity() {
|
|
try {
|
|
return {
|
|
login: String(options.getLogin ? options.getLogin() : '').trim(),
|
|
day: String(options.getDay ? options.getDay() : localDay()),
|
|
};
|
|
} catch (_) {
|
|
return {login:'', day:''};
|
|
}
|
|
}
|
|
|
|
function checkpoint() {
|
|
if (!storage) return null;
|
|
const current = identity();
|
|
if (!current.login || !current.day) return null;
|
|
try {
|
|
const saved = JSON.parse(storage.getItem(checkpointKey) || 'null');
|
|
return saved?.login === current.login && saved?.day === current.day ? saved : null;
|
|
} catch (_) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function saveCheckpoint() {
|
|
if (!storage) return false;
|
|
const current = identity();
|
|
if (!current.login || !current.day) return false;
|
|
try {
|
|
storage.setItem(checkpointKey, JSON.stringify(current));
|
|
return true;
|
|
} catch (_) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function clearCheckpoint() {
|
|
if (!storage || !checkpoint()) return false;
|
|
try {
|
|
storage.removeItem(checkpointKey);
|
|
return true;
|
|
} catch (_) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function briefing() {
|
|
const counts = options.getCounts ? options.getCounts() : {};
|
|
const phases = reviewOrder
|
|
.map(([name, label]) => ({name, label, count: count(counts[name])}))
|
|
.filter(phase => phase.count > 0);
|
|
const total = phases.reduce((sum, phase) => sum + phase.count, 0);
|
|
const today = count(counts.today);
|
|
const next = phases.length ? phases[0].name : (today ? 'today' : 'find');
|
|
const nextLabel = phases.length ? 'Review ' + phases[0].label : (today ? 'Continue Today' : 'Find Work');
|
|
return {
|
|
total,
|
|
next,
|
|
label: nextLabel,
|
|
summary: total ? total + ' items before Today · ' + today + ' planned' :
|
|
(today ? 'Review clear · ' + today + ' planned' : 'Review clear · Today is empty'),
|
|
phases,
|
|
};
|
|
}
|
|
|
|
function startNext() {
|
|
const next = briefing().next;
|
|
if (reviewOrder.some(([name]) => name === next)) saveCheckpoint();
|
|
else clearCheckpoint();
|
|
options.openQueue(next);
|
|
return next;
|
|
}
|
|
|
|
function state() {
|
|
const current = briefing();
|
|
return {active:Boolean(checkpoint()), next:current.next, label:current.label};
|
|
}
|
|
|
|
function completePhase() {
|
|
if (!checkpoint()) return false;
|
|
const current = render();
|
|
if (options.onHandoff) options.onHandoff(current);
|
|
return true;
|
|
}
|
|
|
|
function finish() {
|
|
const cleared = clearCheckpoint();
|
|
render();
|
|
return cleared;
|
|
}
|
|
|
|
function render() {
|
|
const current = briefing();
|
|
if (!options.elements) return current;
|
|
options.elements.summary.textContent = current.summary;
|
|
options.elements.phases.textContent = current.phases.length ?
|
|
current.phases.map(phase => phase.label + ' ' + phase.count).join(' · ') :
|
|
'All urgent queues reviewed';
|
|
options.elements.action.textContent = checkpoint() ? 'Resume preparation · ' + current.label : current.label;
|
|
if (options.elements.finish) options.elements.finish.hidden = !checkpoint();
|
|
return current;
|
|
}
|
|
|
|
function start() {
|
|
render();
|
|
if (options.elements) options.elements.action.addEventListener('click', startNext);
|
|
}
|
|
|
|
return {briefing, completePhase, finish, render, start, startNext, state};
|
|
});
|