From cc799475f2a31d5b029822ac1b6729147e200e46 Mon Sep 17 00:00:00 2001 From: vickydotbat Date: Fri, 24 Jul 2026 23:28:00 +0200 Subject: [PATCH] Take the support drawer back out of the topbar (sow-nodebb-plugin-support#10) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Support case notifications now live in the notification bell, as a "Cases" filter button added by nodebb-plugin-support on core's own hooks. The plugin owns all of it, so this theme's second bell — its icon, drawer, client code, language keys and tests — comes out. The theme was the wrong home for it. The drawer's markup and JS lived here, so whether the support plugin could notify anyone depended on which theme a forum ran. Nothing about support belongs in a theme. The `/support` link is unaffected; it comes from the ACP navigation, which this theme already renders in both the desktop bar and the mobile drawer. Checked in a browser on the dev stack: no support icon in the topbar, and the bell's new tab filters a mixed notification list correctly. Co-Authored-By: Claude Opus 5 (1M context) --- languages/en-GB/westgate.json | 5 +- public/client.js | 110 ------------------- templates/partials/header/support-drawer.tpl | 52 --------- templates/partials/header/topbar.tpl | 9 -- tests/global-topbar-contract.test.js | 58 ++-------- tests/support-drawer-client.test.js | 28 ----- 6 files changed, 8 insertions(+), 254 deletions(-) delete mode 100644 templates/partials/header/support-drawer.tpl delete mode 100644 tests/support-drawer-client.test.js diff --git a/languages/en-GB/westgate.json b/languages/en-GB/westgate.json index 10ff7cf..a23dbe8 100644 --- a/languages/en-GB/westgate.json +++ b/languages/en-GB/westgate.json @@ -20,8 +20,5 @@ "footer.how-to-join": "How to Join", "footer.copyright": "© %1 Shadows Over Westgate · A NWN:EE Persistent World", "footer.setting": "Forgotten Realms · Westgate · Sea of Fallen Stars", - "footer.powered-by": "Powered by", - "support-inbox.title": "Support inbox", - "support-inbox.empty": "Nothing new", - "support-inbox.view-all": "Go to Support" + "footer.powered-by": "Powered by" } diff --git a/public/client.js b/public/client.js index 329611a..6365ba4 100644 --- a/public/client.js +++ b/public/client.js @@ -208,80 +208,6 @@ }); } - /* - * Support inbox drawer. The support plugin owns its own notification list - * (core's unread count is one aggregate with no hook to carve a type out - * of it), but everything the user sees is core's: the rows are rendered - * with core's own `partials/notifications_list` partial and driven by the - * same handlers as the bell — filter buttons, per-entry read dot, mark all - * read. Only the API paths differ. - */ - - function setSupportBadge(count) { - toArray(document.querySelectorAll('[component="support/count"]')).forEach((badge) => { - badge.textContent = count > 99 ? '99+' : String(count); - badge.classList.toggle('hidden', count <= 0); - }); - } - - function adjustSupportBadge(delta) { - const badge = document.querySelector('[component="support/count"]'); - const current = parseInt(badge && badge.textContent, 10) || 0; - setSupportBadge(Math.max(0, current + delta)); - } - - // Same shape as core's markNotification: PUT marks read, DELETE marks unread. - function markSupportEntry(entryEl, read) { - const nid = encodeURIComponent(entryEl.attr('data-nid')); - const path = '/plugins/support/notifications/' + nid + '/read'; - require(['api'], function (api) { - (read ? api.put(path) : api.del(path)).then(function () { - entryEl.toggleClass('unread', !read); - entryEl.find('.mark-read .unread').toggleClass('hidden', read); - entryEl.find('.mark-read .read').toggleClass('hidden', !read); - adjustSupportBadge(read ? -1 : 1); - }); - }); - } - - function renderSupportMenu(triggerEl) { - const dropdownEl = triggerEl.parent().find('.dropdown-menu'); - const listEl = dropdownEl.find('[component="support/list"]'); - dropdownEl.find('[data-filter="all"]').addClass('active'); - dropdownEl.find('[data-filter="unread"]').removeClass('active'); - - require(['api', 'translator'], function (api, translator) { - api.get('/plugins/support/notifications', {}).then(function (data) { - const notifications = (data && data.notifications) || []; - const relativePath = (window.config && window.config.relative_path) || ''; - notifications.forEach((notification) => { - notification.path = relativePath + notification.path; - notification.timeagoLong = $.timeago(new Date(notification.datetime)); - }); - app.parseAndTranslate('partials/notifications_list', { notifications }, function (html) { - listEl.html(html); - setSupportBadge((data && data.unreadCount) || 0); - // core's partial says "no notifications"; this box only ever holds - // support activity, so say that instead. - translator.translate('[[westgate:support-inbox.empty]]', function (text) { - listEl.find('.no-notifs .fw-semibold').text(text); - }); - listEl.off('click').on('click', '[component="notifications/item/link"]', function () { - const entryEl = $(this).parents('[data-nid]'); - if (entryEl.hasClass('unread')) { - markSupportEntry(entryEl, true); - } - triggerEl.dropdown('toggle'); - }); - listEl.on('click', '.mark-read', function () { - const entryEl = $(this).parents('[data-nid]'); - markSupportEntry(entryEl, entryEl.hasClass('unread')); - }); - }); - }); - }); - } - function getWestgateTopbar() { if (!document || typeof document.querySelector !== 'function') { return null; @@ -367,48 +293,12 @@ renderUnreadMenu(menuEl, []); }); }); - - // ponytail: unlike core's bell, this badge gets no socket push, so it - // only catches up when the drawer is opened or the page reloads. Add a - // socket event in the support plugin if that gap ever matters. - $(document) - .off('show.bs.dropdown.westgateSupport') - .on('show.bs.dropdown.westgateSupport', '.wg-topbar [component="support"] [data-bs-toggle="dropdown"]', function () { - renderSupportMenu($(this)); - }); - - $(document) - .off('click.westgateSupport') - .on('click.westgateSupport', '.wg-topbar [component="support"] .mark-all-read', function (event) { - event.preventDefault(); - const entries = $(this).closest('.dropdown-menu').find('[data-nid]'); - require(['api'], function (api) { - api.post('/plugins/support/notifications/read', {}).then(function () { - setSupportBadge(0); - entries.removeClass('unread'); - entries.find('.mark-read .unread').addClass('hidden'); - entries.find('.mark-read .read').removeClass('hidden'); - }); - }); - }) - // Core's own filter behaviour: hide read rows client-side, nothing refetched. - .on('click.westgateSupport', '.wg-topbar [component="support"] [data-filter]', function () { - const dropdownEl = $(this).closest('.dropdown-menu'); - dropdownEl.find('[data-filter]').removeClass('active'); - this.classList.add('active'); - const unreadOnly = this.getAttribute('data-filter') === 'unread'; - dropdownEl.find('[data-nid]').each(function () { - this.classList.toggle('hidden', unreadOnly && !this.classList.contains('unread')); - }); - dropdownEl.find('.no-notifs').toggleClass('hidden', dropdownEl.find('[data-nid]:not(.hidden)').length !== 0); - }); } window.westgateTheme = window.westgateTheme || {}; window.westgateTheme.wrapWikiTables = wrapWestgateWikiTables; window.westgateTheme.initTopbar = initWestgateTopbar; window.westgateTheme.renderUnreadMenu = renderUnreadMenu; - window.westgateTheme.renderSupportMenu = renderSupportMenu; $(document).ready(function () { wrapWestgateWikiTables(document); diff --git a/templates/partials/header/support-drawer.tpl b/templates/partials/header/support-drawer.tpl deleted file mode 100644 index 6597613..0000000 --- a/templates/partials/header/support-drawer.tpl +++ /dev/null @@ -1,52 +0,0 @@ - - diff --git a/templates/partials/header/topbar.tpl b/templates/partials/header/topbar.tpl index 3a2ce69..5938994 100644 --- a/templates/partials/header/topbar.tpl +++ b/templates/partials/header/topbar.tpl @@ -45,12 +45,6 @@ - {{{ if support.visible }}} - - {{{ end }}} - {{{ if canChat }}}