Adds an Unread dropdown to the topbar utilities (before notifications), per docs/superpowers/plans/2026-07-17-topbar-unread-drawer.md.
- New partial `templates/partials/header/unread-drawer.tpl`: toggle with live `component="unread/count"` badge (core socket updates), server-rendered empty/footer rows.
- `public/client.js`: on dropdown open, fetches `GET /api/unread` and injects up to 10 topic rows (titles/categories via textContent only); fetch failure falls back to the empty state.
- Mobile burger drawer gets an Unread row with the same badge.
- SCSS: unread dropdown joins the shared dropdown width rule; flex row styles for topic items.
Deviations from the plan's example code, found by live verification against the dev forum:
- Plan's `api.get('/unread')` hits the v3 write API and 404s → replaced with a fetch of the `/api/unread` read route.
- NodeBB 4 has no global `window.require`, so runtime translation of the static `[[unread:...]]` strings never ran → those strings are now server-rendered in the template (also removes the plan's translate-over-innerHTML step, which would have re-parsed untrusted titles as HTML).
Verified: full plain-node test suite passes; Playwright against the dev container passed all checks (badge count, item ordering before notifications, dropdown topic links + navigation, /unread footer link, mobile drawer badge at 390px).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Reviewed-on: #28
Co-authored-by: vickydotbat <vickydotbat@tutamail.com>
Co-committed-by: vickydotbat <vickydotbat@tutamail.com>
25 lines
693 B
JavaScript
25 lines
693 B
JavaScript
'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.westgateUnread'"),
|
|
'client.js should fetch unread topics when the unread dropdown opens'
|
|
);
|
|
assert(
|
|
client.includes("'/api/unread'"),
|
|
'client.js should load the unread list from the /unread API'
|
|
);
|
|
assert(
|
|
client.includes('renderUnreadMenu'),
|
|
'client.js should expose the unread menu renderer'
|
|
);
|
|
assert(
|
|
/textContent/.test(client) && !/innerHTML\s*=[^=]*title/.test(client),
|
|
'Topic titles must be inserted via textContent, never innerHTML'
|
|
);
|