feat: launch Protect Today from deadline reminders (Closes #723)
CI / lint (pull_request) Successful in 1m22s
CI / build-release (pull_request) Successful in 5s
CI / release-candidate (pull_request) Has been skipped

This commit is contained in:
timmy committed 2026-08-13 08:57:32 +00:00
1 parent cbe4017bdc
commit 8a12524595
7 files changed
+89 -8

No files matched your search

+18 -1
View File
@@ -6058,10 +6058,27 @@
if (!preserveRoute) workRoute.queue(filter);
return true;
}
function openWorkQueueRoute(filter) {
async function openWorkQueueRoute(filter, action = null) {
if (!selectWorkQueue(filter, { preserveRoute:true })) return;
qs('#my-work').scrollIntoView({block:'start'});
qs('#my-work').focus();
if (action !== 'protect-today') return;
await completeAgendaIssues();
if (workPagination.issue?.has_more) {
qs('#my-work-action-status').textContent =
'Protect Today needs all assigned deadlines. Retry when connected.';
return;
}
const proposal = protectToday.propose({
agenda:agendaMyWork(activeMyWork), today:todayMyWork,
identity:item => todayWork.identity(item), limit:todayWork.limit,
});
if (!proposal.protected.length) {
qs('#protect-today-status').textContent = 'No overdue or due-today work needs protection.';
return;
}
pendingProtectToday = proposal;
openPlanToday(qs('#protect-today'));
}
qs('#work-milestone-filter').addEventListener('change', event => {
selectedWorkMilestone = event.target.value;
+23 -1
View File
@@ -314,12 +314,14 @@ self.addEventListener('push', event => {
try { payload = event.data?.json?.() || {}; }
catch (_error) { return; }
const route = String(payload.route || '');
const protectRoute = String(payload.protect_route || '');
const tag = String(payload.tag || '');
const notificationId = Number(payload.notification_id);
const updateCount = Number(payload.update_count);
const deadlineCount = Number(payload.deadline_count);
if (
route === '#/my-work/agenda'
&& protectRoute === '#/my-work/agenda/protect-today'
&& /^stackchain-deadline-digest-\d{4}-\d{2}-\d{2}$/.test(tag)
&& Number.isSafeInteger(deadlineCount)
&& deadlineCount > 0
@@ -330,7 +332,11 @@ self.addEventListener('push', event => {
{
body: 'Open Agenda to review or replan ' + (deadlineCount === 1 ? 'it.' : 'them.'),
tag,
data: {route},
actions: [
{ action: 'protect-today', title: 'Protect Today' },
{ action: 'open-agenda', title: 'Open Agenda' },
],
data: {route, protectRoute},
}
));
return;
@@ -380,6 +386,22 @@ async function openWorkRoute(route) {
self.addEventListener('notificationclick', event => {
const route = String(event.notification.data?.route || '');
if (event.action === 'protect-today') {
const protectRoute = String(event.notification.data?.protectRoute || route);
if (
protectRoute !== '#/my-work/agenda/protect-today'
|| !['#/my-work/agenda', protectRoute].includes(route)
) return;
event.notification.close();
event.waitUntil(openWorkRoute(protectRoute));
return;
}
if (event.action === 'open-agenda') {
if (route !== '#/my-work/agenda') return;
event.notification.close();
event.waitUntil(openWorkRoute(route));
return;
}
if (event.action === 'tomorrow') {
const notificationId = Number(event.notification.data?.notificationId);
if (
+4 -1
View File
@@ -20,6 +20,9 @@
if (queueFilters.includes(queue) && parts.length === 3) {
return { kind: 'queue', filter: queue };
}
if (queue === 'agenda' && parts[3] === 'protect-today' && parts.length === 4) {
return { kind: 'queue', filter: 'agenda', action: 'protect-today' };
}
if (parts[2] === 'update' && parts.length === 4) {
const notificationId = positiveInteger(parts[3]);
return notificationId ? { kind: 'update', notification_id: notificationId } : null;
@@ -114,7 +117,7 @@
if (active && active !== fragment) onClose();
if (active === fragment) return;
active = fragment;
onQueue(route.filter);
onQueue(route.filter, route.action || null);
return;
}
const item = items.find(candidate => sameRoute(candidate, route));