Issue 78 Notification Bell Empty - edmozley/freeitsm GitHub Wiki
The bell sat in the header showing a red 3. Clicking it gave you an empty panel. On any other page the same bell listed the same three notifications correctly.
Reported in issue #78 by tjedelhauser.
Fixed in 46f96b53, released as update #1106.
The bell itself is described in Notifications.
| Where | Badge | Panel |
|---|---|---|
| Home page | 3 β | empty β |
| Tasks, Tickets, Assets, everywhere else | 3 β | three notifications β |
The reporter's description was exactly right: "when i open freeitsm on the Mainpage, i see the Notification Bell, but they didn't show me entries⦠when i go to another menu, for example Tasks, then i see the entries."
Two details make this harder to read than it looks:
- The badge was correct. It said 3, and there really were 3. So the bell was clearly getting something from the server.
- Nothing was logged, and nothing looked broken. No error banner, no failed request in the network tab β because, as it turns out, there was no request at all.
The combination reads as "you have no new notifications" rather than as a fault, which is why it took an outside report to notice.
The bell is drawn by the shared header, so it appears on every page without any module knowing about it. This is what runs when you click it:
async function open() {
panel.classList.add('open');
list.innerHTML = 'β¦' + esc(window.t('common.notifications.loading')) + 'β¦'; // line 762
try {
const d = await (await fetch(API + 'get_notifications.php')).json(); // line 764
if (d.success) { render(d.notifications || []); paintBadge(d.unread); }
} catch (e) {
list.innerHTML = 'β¦' + esc(window.t('common.notifications.load_failed')); // line 767
}
}window.t is the translation lookup, defined by assets/js/i18n.js. Every page in the application loads that script.
Except the home page, which never did.
So window.t did not exist there, and line 762 β which asks for the word "Loadingβ¦" before doing anything else β threw TypeError: window.t is not a function.
Look at where line 762 sits: one line above the try.
- The
fetchon line 764 was never reached. The request for your notifications was never sent. - The
catchon line 767 was never entered either, because the failure happened before the block it guards. The message written specifically to say "couldn't load your notifications" could not fire, because the thing it was there to report broke one line too early.
And the badge kept working throughout, because the count is fetched by a different function that never touches a translation:
async function poll() {
const d = await (await fetch(API + 'get_notifications.php?count_only=1')).json();
if (d.success) paintBadge(d.unread); // no window.t anywhere
}Hence the exact symptom: a correct count over an empty list.
The general lesson: a
tryonly protects the lines inside it. Setup work placed immediately above β building a string, reading a global, resolving a label β is outside the net, and a failure there takes out the error handling along with the feature. If a line can throw, it belongs inside the block that reports it.
The reported page was the obvious one. Checking every page in the application for a bell without the translation script found two more with the identical fault:
| Page | What was wrong |
|---|---|
index.php (home) |
Never loaded i18n.js at all β the page reported |
system/index.php |
Prepared $translationNamespaces = ['common', 'system'] and then never emitted it, nor loaded the script. The wiring was started and not finished, so the variable sat there unused |
problem-management/index.php |
No translation setup at all. The module's own text is English-only, but the shared header it draws is not |
The System one is worth dwelling on: everything looked present. The namespaces were declared, the intent was visible in the code, and the one line that actually ships it to the browser was missing. Nothing about reading that file says "unfinished".
The home page was already translated on the server β its title and footer come from t('common.home.browser_title') and t('common.home.footer'). It was never a page that had no need of translation; it was a page that had only half its translation wired. So the fix is to ship the browser half too, on all three pages:
$translationNamespaces = ['common'];<script>window.translations = <?php echo json_encode(I18n::exportForJs($translationNamespaces), β¦); ?>;</script>
<script src="assets/js/i18n.js?v=2"></script>That also puts the home page in line to be translated in the browser, which it never has been.
Two other components call the same function defensively:
// assets/js/calendar.js and assets/js/war-room.js
return (typeof window.t === 'function') ? window.t(key, params) : key;The bell β the one component deliberately rendered on every page β calls it bare. Adding the same guard would mean a page that forgets the script in future degrades to untranslated English rather than silently doing nothing. That is a separate change and is not in this fix.
| File | Role |
|---|---|
includes/waffle-menu.php |
renderNotificationBell() β the bell, its markup and its script. open() is where the throw happened |
assets/js/i18n.js |
Defines window.t. Absent from three pages |
| File | Role |
|---|---|
index.php |
The home page β the one reported. Gained the export and the script |
system/index.php |
Namespaces were already declared; gained the two lines that ship them |
problem-management/index.php |
Gained the i18n include, I18n::initFromSession(), the namespaces and the script |
Every page was driven in a real browser with fetch instrumented, so the question "was a request made?" is answered by observation rather than inference.
| Page | Before | After |
|---|---|---|
| Home |
0 requests, threw window.t is not a function
|
1 request, 3 items |
| System | not yet tested | 1 request, 3 items |
| Problem Management | not yet tested | 1 request, 3 items |
| Tasks (control, never broken) | 1 request, 3 items | 1 request, 3 items |
The control matters: Tasks was untouched by the fix and had to keep behaving identically, or the change would have been doing something other than what it claimed.
An application-wide scan for "renders the bell, does not load i18n.js" was run before and after, and returns nothing now.
- The bell works on every page, including the home page. Nothing needs configuring.
- If your badge shows a number and the panel is empty, that is this bug β update.
- Nothing was ever lost. The notifications existed the whole time and were readable from any other page; only that one panel could not fetch them.
- Bugs resolved β the index of write-ups like this one
- Notifications β what the bell shows and the rules that keep it quiet
-
Internationalisation (i18n) β
window.t, the namespace export, and how a page wires up the browser half