diff --git a/frontend/index.html b/frontend/index.html
index 1401d4f..5da019f 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -861,6 +861,7 @@
+
diff --git a/frontend/mobile-task-dock.js b/frontend/mobile-task-dock.js
index 12f4a50..022d50d 100644
--- a/frontend/mobile-task-dock.js
+++ b/frontend/mobile-task-dock.js
@@ -95,12 +95,17 @@
}
function updateQueues(counts) {
- const names = ['today', 'attention', 'later', 'draft'];
+ const names = ['today', 'attention', 'update', 'later', 'draft'];
const normalized = Object.fromEntries(names.map(name => [name, Math.max(0, Number(counts?.[name]) || 0)]));
- const total = names.reduce((sum, name) => sum + normalized[name], 0);
+ // Updates are already represented by the Attention destination, so keep
+ // the dock aggregate non-overlapping while exposing the useful drill-down.
+ const total = ['today', 'attention', 'later', 'draft'].reduce((sum, name) => sum + normalized[name], 0);
Object.entries(options.queueCounts || {}).forEach(([name, element]) => {
element.textContent = String(normalized[name] || 0);
});
+ options.queueRows?.update?.setAttribute(
+ 'aria-label', 'Updates, ' + normalized.update + ' unread conversations'
+ );
if (options.queueBadge) {
options.queueBadge.textContent = String(total);
options.queueBadge.hidden = total === 0;
diff --git a/tests/test_mobile_task_dock.py b/tests/test_mobile_task_dock.py
index 38283d7..0e88dfd 100644
--- a/tests/test_mobile_task_dock.py
+++ b/tests/test_mobile_task_dock.py
@@ -292,7 +292,7 @@ sheet.showModal = function () {{ this.open = true; }};
sheet.close = function () {{ this.open = false; this.listeners.close?.(); }};
const close = new FakeElement();
const badge = new FakeElement();
-const rows = Object.fromEntries(['today','attention','later','draft'].map(name => [name, new FakeElement()]));
+const rows = Object.fromEntries(['today','attention','update','later','draft'].map(name => [name, new FakeElement()]));
const counts = Object.fromEntries(Object.keys(rows).map(name => [name, new FakeElement()]));
const selected = [];
const dock = createDock({{
@@ -302,16 +302,17 @@ const dock = createDock({{
observe() {{}},
}});
dock.start();
-dock.updateQueues({{today:2, attention:1, later:3, draft:4}});
+dock.updateQueues({{today:2, attention:1, update:5, later:3, draft:4}});
queues.click();
const opened = sheet.open;
-rows.later.click();
+rows.update.click();
const closedAfterSelect = !sheet.open;
queues.click(); close.click();
process.stdout.write(JSON.stringify({{
opened, closedAfterSelect, selected,
badge:badge.textContent, badgeLabel:queues.attributes['aria-label'],
counts:Object.fromEntries(Object.entries(counts).map(([name, node]) => [name, node.textContent])),
+ updateLabel:rows.update.attributes['aria-label'],
queueFocuses:queues.focuses,
columnState:nav.attributes['data-attention'] || null,
}}));
@@ -322,10 +323,12 @@ process.stdout.write(JSON.stringify({{
assert json.loads(result.stdout) == {
"opened": True,
"closedAfterSelect": True,
- "selected": ["later"],
+ "selected": ["update"],
+ # Updates are a drill-down within Attention and must not inflate the aggregate badge.
"badge": "10",
"badgeLabel": "Queues, 10 items",
- "counts": {"today": "2", "attention": "1", "later": "3", "draft": "4"},
+ "counts": {"today": "2", "attention": "1", "update": "5", "later": "3", "draft": "4"},
+ "updateLabel": "Updates, 5 unread conversations",
"queueFocuses": 2,
"columnState": None,
}
@@ -340,6 +343,8 @@ async def test_dashboard_renders_and_wires_mobile_queue_switcher():
assert 'aria-labelledby="mobile-queue-heading"' in html
assert 'data-mobile-queue="today"' in html
assert 'data-mobile-queue="attention"' in html
+ assert 'data-mobile-queue="update"' in html
+ assert 'UpdatesUnread conversations' in html
assert 'data-mobile-queue="later"' in html
assert 'data-mobile-queue="draft"' in html
assert 'id="mobile-queue-count"' in html