Add a support inbox icon to the topbar (sow-nodebb-plugin-support#10)

Mirrors the existing unread-drawer pattern: a new dropdown next to the
notification bell, gated on templateData.support.visible (injected by
the support plugin's filter:middleware.renderHeader hook, on every page
render, for viewers with any support access at all). Opening it fetches
GET /plugins/support/notifications via the api module, renders up to
10 entries by textContent (never innerHTML), and marks everything read
via POST .../notifications/read, clearing the badge client-side.

Also present in the mobile burger drawer, same gating.

ponytail: no live socket push for this badge (core's own unread/notification
counts get one); it only catches up on the next full page load after the
box is opened. Noted in client.js as the upgrade path if that gap matters.

Verified live against sow-nodebb-plugin-support's dev NodeBB stack (theme +
plugin both mounted): icon hidden for a user with no support access, badge
count and dropdown contents correct for a reviewer, badge clears on open.
Full plain-node test suite passes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-24 21:29:57 +02:00
co-authored by Claude Sonnet 5
parent 0bdb59aaa0
commit ca2aefa156
6 changed files with 165 additions and 1 deletions
+36
View File
@@ -180,3 +180,39 @@ assertIncludes(
'.unread-dropdown',
'Topbar styles should cover the unread dropdown'
);
const supportDrawer = read('templates/partials/header/support-drawer.tpl');
assertIncludes(
topbar,
'<!-- IMPORT partials/header/support-drawer.tpl -->',
'Topbar should mount the support inbox drawer'
);
assertIncludes(
topbar,
'{{{ if support.visible }}}',
'Support inbox drawer should be gated on the viewer having any support access'
);
assert(
topbar.indexOf('partials/sidebar/notifications.tpl') < topbar.indexOf('partials/header/support-drawer.tpl'),
'Support inbox should sit next to (after) the notifications bell'
);
assertIncludes(
supportDrawer,
'component="support/count"',
'Support toggle should carry its own unread count badge'
);
assertIncludes(
supportDrawer,
'data-wg-support-menu',
'Support dropdown menu should expose the JS mount hook'
);
assertMatches(
topbar,
/href="\{relative_path\}\/support"[^>]*>[\s\S]*?component="support\/count"/,
'Mobile drawer actions should include a Support inbox link with count badge'
);
assertIncludes(
stylesheet,
'.support-dropdown',
'Topbar styles should cover the support inbox dropdown'
);
+28
View File
@@ -0,0 +1,28 @@
'use strict';
const assert = require('assert');
const fs = require('fs');
const path = require('path');
const client = fs.readFileSync(path.join(__dirname, '..', 'public', 'client.js'), 'utf8');
assert(
client.includes("'show.bs.dropdown.westgateSupport'"),
'client.js should fetch the support inbox when its dropdown opens'
);
assert(
client.includes("'/plugins/support/notifications'"),
'client.js should load the support inbox from the plugin API via the api module (for CSRF-safe writes)'
);
assert(
client.includes("'/plugins/support/notifications/read'"),
'client.js should mark the support inbox read when it is opened with unread entries'
);
assert(
client.includes('renderSupportMenu'),
'client.js should expose the support inbox menu renderer'
);
assert(
/textContent/.test(client) && !/innerHTML\s*=[^=]*bodyShort/.test(client),
'Support inbox entry text must be inserted via textContent, never innerHTML'
);