From d84b2641500eeb6bad8cf20f4c16da06273047dd Mon Sep 17 00:00:00 2001 From: vickydotbat Date: Fri, 17 Jul 2026 18:13:54 +0200 Subject: [PATCH] feat: load unread topics into topbar drawer on open --- public/client.js | 71 ++++++++++++++++++++++++++++++ tests/unread-drawer-client.test.js | 24 ++++++++++ 2 files changed, 95 insertions(+) create mode 100644 tests/unread-drawer-client.test.js diff --git a/public/client.js b/public/client.js index 04839e8..82be8e3 100644 --- a/public/client.js +++ b/public/client.js @@ -159,6 +159,61 @@ }); } + function renderUnreadMenu(menuEl, topics) { + if (!menuEl) { + return; + } + + menuEl.textContent = ''; + const relativePath = (window.config && window.config.relative_path) || ''; + + function addItem(node) { + const li = document.createElement('li'); + li.appendChild(node); + menuEl.appendChild(li); + } + + const list = (topics || []).slice(0, 10); + if (!list.length) { + const empty = document.createElement('span'); + empty.className = 'dropdown-item disabled'; + empty.textContent = '[[unread:no-unread-topics]]'; + addItem(empty); + } + + list.forEach((topic) => { + const link = document.createElement('a'); + link.className = 'dropdown-item wg-unread-item text-truncate'; + link.href = `${relativePath}/topic/${topic.slug}`; + link.textContent = topic.title; + if (topic.category && topic.category.name) { + const category = document.createElement('small'); + category.className = 'wg-unread-item__category'; + category.textContent = topic.category.name; + link.appendChild(category); + } + addItem(link); + }); + + const divider = document.createElement('hr'); + divider.className = 'dropdown-divider'; + addItem(divider); + + const seeAll = document.createElement('a'); + seeAll.className = 'dropdown-item wg-unread-item--all'; + seeAll.href = `${relativePath}/unread`; + seeAll.textContent = '[[unread:title]]'; + addItem(seeAll); + + if (window.require) { + window.require(['translator'], function (translator) { + translator.translate(menuEl.innerHTML, (translated) => { + menuEl.innerHTML = translated; + }); + }); + } + } + function getWestgateTopbar() { if (!document || typeof document.querySelector !== 'function') { return null; @@ -225,11 +280,27 @@ .on('shown.bs.dropdown.westgateTopbar', '.wg-topbar [component="sidebar/search"]', function () { $(this).find('[component="search/fields"] input[name="query"]').trigger('focus'); }); + + $(document) + .off('show.bs.dropdown.westgateUnread') + .on('show.bs.dropdown.westgateUnread', '.wg-topbar [component="unread"]', function () { + const menuEl = this.querySelector('[data-wg-unread-menu]'); + require(['api'], function (api) { + api.get('/unread', {}, function (err, data) { + if (err) { + renderUnreadMenu(menuEl, []); + return; + } + renderUnreadMenu(menuEl, data && data.topics); + }); + }); + }); } window.westgateTheme = window.westgateTheme || {}; window.westgateTheme.wrapWikiTables = wrapWestgateWikiTables; window.westgateTheme.initTopbar = initWestgateTopbar; + window.westgateTheme.renderUnreadMenu = renderUnreadMenu; $(document).ready(function () { wrapWestgateWikiTables(document); diff --git a/tests/unread-drawer-client.test.js b/tests/unread-drawer-client.test.js new file mode 100644 index 0000000..8e82d62 --- /dev/null +++ b/tests/unread-drawer-client.test.js @@ -0,0 +1,24 @@ +'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.get('/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' +);