feat: keep mobile Attention actionable alongside Today (Closes #593)
CI / lint (pull_request) Failing after 1m26s
CI / build-release (pull_request) Has been skipped
CI / release-candidate (pull_request) Has been skipped

This commit is contained in:
timmy committed 2026-08-11 21:25:27 +00:00
1 parent 614ffae188
commit 87f63370f6
16 files changed
+76 -61

No files matched your search

+1
View File
@@ -566,6 +566,7 @@ textarea { resize: vertical; min-height: 120px; }
.composer-keyboard-active .pull-comment-composer,
.composer-keyboard-active .update-reply { scroll-margin-block:12px; padding-bottom:env(safe-area-inset-bottom); }
.mobile-task-dock { position:fixed; inset:auto 0 0; z-index:45; display:grid; grid-template-columns:repeat(5,minmax(0,1fr)); gap:2px; padding:6px 8px; padding-bottom:env(safe-area-inset-bottom); border-top:1px solid #2a496e; background:rgba(11,21,38,.98); backdrop-filter:blur(12px); }
.mobile-task-dock[data-attention="true"] { grid-template-columns:repeat(6,minmax(0,1fr)); }
}
.obi { width:14px; height:14px; background: url('data:image/svg+xml;utf8,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22><rect width=%2224%22 height=%2224%22 rx=%226%22 fill=%22%230b1526%22/><circle cx=%2212%22 cy=%2212%22 r=%226%22 fill=%22%2360a5fa%22/></svg>') center/contain no-repeat; display:inline-block; }
.footer { padding: 12px; text-align: center; color:#4e6b8a; font-size:12px; }
+11 -5
View File
@@ -45,9 +45,12 @@
);
const mobileTaskOverlays = Array.from(document.querySelectorAll('[role="dialog"], #whiteboard-modal, #markdown-modal'));
function openMobileWorkFallback() {
const counts = countMyWork(activeMyWork);
const filter = counts.attention ? 'attention' : 'all';
qs('[data-work-filter="' + filter + '"]').click();
qs('[data-work-filter="all"]').click();
qs('#my-work').scrollIntoView({block:'start'});
qs('#my-work').focus();
}
function openMobileAttention() {
qs('[data-work-filter="attention"]').click();
qs('#my-work').scrollIntoView({block:'start'});
qs('#my-work').focus();
}
@@ -68,6 +71,7 @@
overlays: mobileTaskOverlays,
actions: {
work: () => mobileWorkEntry.open(),
attention: openMobileAttention,
find: () => qs('#find-work').click(),
new: () => qs('#new-issue').click(),
search: () => qs('#open-palette').click(),
@@ -977,7 +981,8 @@
qs('#resume-today-session').hidden = active || !todayMyWork.length || !workSession.resumable();
qs('#end-today-session').hidden = !active;
updateDetailDeferLabels(active);
mobileTaskDock.updateWork(mobileWorkEntry.mode(), countMyWork(activeMyWork).attention);
mobileTaskDock.updateWork(mobileWorkEntry.mode());
mobileTaskDock.updateAttention(countMyWork(activeMyWork).attention);
}
const workSession = createWorkSession({
@@ -1920,7 +1925,8 @@
const element = qs('[data-work-count="' + filter + '"]');
if (element) element.textContent = count;
});
mobileTaskDock.updateWork(mobileWorkEntry.mode(), countMyWork(activeMyWork).attention);
mobileTaskDock.updateWork(mobileWorkEntry.mode());
mobileTaskDock.updateAttention(countMyWork(activeMyWork).attention);
const activeQueue = qs('[data-work-filter="' + selectedWorkFilter + '"]');
qs('#active-work-queue').textContent = activeQueue.firstChild.textContent.trim() +
' (' + (counts[selectedWorkFilter] || 0) + ')';
+2 -1
View File
@@ -808,7 +808,8 @@
</dialog>
<nav class="mobile-task-dock" id="mobile-task-dock" aria-label="Primary tasks">
<button class="mobile-task-action" data-mobile-task="work" type="button" aria-current="page"><span id="mobile-work-label">Work</span> <span class="mobile-task-count" id="mobile-attention-count" hidden>0</span></button>
<button class="mobile-task-action" data-mobile-task="work" type="button" aria-current="page"><span id="mobile-work-label">Work</span></button>
<button class="mobile-task-action" data-mobile-task="attention" type="button" aria-label="Attention, 0 items" hidden>Attention <span class="mobile-task-count" id="mobile-attention-count" hidden>0</span></button>
<button class="mobile-task-action" data-mobile-task="find" type="button">Find</button>
<button class="mobile-task-action" data-mobile-task="new" type="button">New</button>
<button class="mobile-task-action" data-mobile-task="search" type="button">Search</button>
+14 -12
View File
@@ -35,7 +35,7 @@
refreshVisibility();
}
function updateWork(mode, count) {
function updateWork(mode) {
const labels = {
continue: ['Continue', 'Continue Today'],
resume: ['Resume', 'Resume Today'],
@@ -43,23 +43,25 @@
work: ['Work', 'Work'],
};
workMode = labels[mode] ? mode : 'work';
const total = Math.max(0, Number(count) || 0);
const badge = options.attentionBadge;
if (options.workLabel) options.workLabel.textContent = labels[workMode][0];
if (badge) {
badge.textContent = String(total);
badge.hidden = total === 0;
}
if (buttons.work) {
buttons.work.setAttribute(
'aria-label',
labels[workMode][1] + (total ? ', ' + total + ' items need attention' : '')
);
buttons.work.setAttribute('aria-label', labels[workMode][1]);
}
}
function updateAttention(count) {
updateWork(workMode, count);
const total = Math.max(0, Number(count) || 0);
const visible = total > 0;
if (options.attentionBadge) {
options.attentionBadge.textContent = String(total);
options.attentionBadge.hidden = !visible;
}
if (buttons.attention) {
buttons.attention.hidden = !visible;
buttons.attention.setAttribute('aria-label', 'Attention, ' + total + ' items');
}
if (visible) nav.setAttribute('data-attention', 'true');
else nav.removeAttribute('data-attention');
}
return {start, refreshVisibility, updateAttention, updateWork};
+1 -1
View File
@@ -1,6 +1,6 @@
const BASE = new URL('./', self.location.href).pathname;
importScripts(BASE + 'static/background-issue-sync.js');
const CACHE = 'stackchain-dashboard-shell-v89';
const CACHE = 'stackchain-dashboard-shell-v90';
const OFFLINE_LEASE_URL = new URL(BASE + '__offline-session-lease', self.location.origin).href;
const OUTAGE_STATUSES = new Set([500, 502, 503, 504]);
const NAVIGATION_TIMEOUT_MS = self.__STACKCHAIN_NAVIGATION_TIMEOUT_MS || 4000;