fix: infinite loop in notification title counter

Signed-off-by: Heikki Hellgren <heikki.hellgren@op.fi>
This commit is contained in:
Heikki Hellgren
2024-04-21 21:21:49 +03:00
parent 4bfb02ed70
commit e1c7d6e4b7
6 changed files with 48 additions and 20 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-notifications': patch
---
Fix infinite loop in the notification title counter
+2
View File
@@ -20,9 +20,11 @@ import {
notificationsPlugin,
NotificationsSidebarItem,
} from '../src';
import { signalsPlugin } from '@backstage/plugin-signals';
createDevApp()
.registerPlugin(notificationsPlugin)
.registerPlugin(signalsPlugin)
.addPage({
element: (
<NotificationsPage
+1
View File
@@ -50,6 +50,7 @@
"@backstage/cli": "workspace:^",
"@backstage/core-app-api": "workspace:^",
"@backstage/dev-utils": "workspace:^",
"@backstage/plugin-signals": "workspace:^",
"@backstage/test-utils": "workspace:^",
"@testing-library/jest-dom": "^6.0.0",
"@testing-library/react": "^15.0.0",
@@ -96,11 +96,14 @@ export const NotificationsSidebarItem = (props?: {
useEffect(() => {
if (!loading && !error && value) {
setUnreadCount(value.unread);
if (titleCounterEnabled) {
setNotificationCount(value.unread);
}
}
}, [loading, error, value, titleCounterEnabled, setNotificationCount]);
}, [loading, error, value]);
useEffect(() => {
if (titleCounterEnabled) {
setNotificationCount(unreadCount);
}
}, [titleCounterEnabled, unreadCount, setNotificationCount]);
// TODO: Figure out if the count can be added to hasNotifications
return (
@@ -13,12 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useCallback, useEffect, useState } from 'react';
import { useCallback, useEffect, useRef, useState } from 'react';
/** @public */
export function useTitleCounter() {
const [title, setTitle] = useState(document.title);
const [count, setCount] = useState(0);
const titleTimer = useRef<undefined | number>(undefined);
const getPrefix = (value: number) => {
return value === 0 ? '' : `(${value}) `;
@@ -28,28 +29,43 @@ export function useTitleCounter() {
return currentTitle.replace(/^\(\d+\)\s/, '');
};
useEffect(() => {
document.title = title;
}, [title]);
useEffect(() => {
const baseTitle = cleanTitle(title);
setTitle(`${getPrefix(count)}${baseTitle}`);
const shownTitle = `${getPrefix(count)}${baseTitle}`;
if (document.title !== shownTitle) {
window.clearTimeout(titleTimer.current);
document.title = shownTitle;
// Need to do this in timeout as the React Helmet overrides the title after this effect
titleTimer.current = window.setTimeout(() => {
document.title = shownTitle;
}, 50);
}
return () => {
window.clearTimeout(titleTimer.current);
document.title = cleanTitle(title);
};
}, [title, count]);
const titleElement = document.querySelector('title');
if (titleElement) {
new MutationObserver(() => {
setTitle(document.title);
}).observe(titleElement, {
subtree: true,
characterData: true,
childList: true,
});
}
useEffect(() => {
const titleElement = document.querySelector('title');
let observer: MutationObserver | undefined;
if (titleElement) {
observer = new MutationObserver(mutations => {
if (mutations?.[0]?.target?.textContent) {
setTitle(mutations[0].target.textContent);
}
});
observer.observe(titleElement, {
characterData: true,
childList: true,
});
}
return () => {
if (observer) {
observer.disconnect();
}
};
}, []);
const setNotificationCount = useCallback(
(newCount: number) => setCount(newCount),
+1
View File
@@ -6057,6 +6057,7 @@ __metadata:
"@backstage/dev-utils": "workspace:^"
"@backstage/errors": "workspace:^"
"@backstage/plugin-notifications-common": "workspace:^"
"@backstage/plugin-signals": "workspace:^"
"@backstage/plugin-signals-react": "workspace:^"
"@backstage/test-utils": "workspace:^"
"@backstage/theme": "workspace:^"