fix(app): redraw the favicon badge when branding changes (#3314)

This commit is contained in:
Hampus authored and GitHub committed 2026-10-08 20:20:49 +02:00
1 parent 4ecbe9603c
commit 4b1afc953f
2 files changed
+46 -9

No files matched your search

@@ -1,6 +1,7 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import RuntimeConfig from '@app/features/app/state/RuntimeConfig';
import {getDocumentFaviconUrl} from '@app/features/app/state/RuntimeDocumentBranding';
import DesktopBackgroundGateway from '@app/features/gateway/transport/DesktopBackgroundGateway';
import GuildReadState from '@app/features/guild/state/GuildReadState';
import {Logger} from '@app/features/platform/utils/AppLogger';
@@ -18,14 +19,40 @@ const logger = new Logger('AppBadge');
const UNREAD_INDICATOR = -1;
let favico: Favico | null = null;
let favicoLink: HTMLLinkElement | null = null;
let favicoLinkHref: string | null = null;
let favicoSource: string | null = null;
const findIconLink = (): HTMLLinkElement | null => {
const links = document.head.querySelectorAll<HTMLLinkElement>('link');
for (let index = links.length - 1; index >= 0; index--) {
if (/(^|\s)icon(\s|$)/i.test(links[index].getAttribute('rel') ?? '')) {
return links[index];
}
}
return null;
};
const releaseFavico = (): void => {
if (favicoLink !== null && favicoLinkHref !== null && favicoLink.getAttribute('href')?.startsWith('data:')) {
favicoLink.setAttribute('href', favicoLinkHref);
}
favico = null;
favicoLink = null;
favicoLinkHref = null;
};
const initFavico = (): Favico | null => {
if (favico) return favico;
try {
favico = new Favico({animation: 'none'});
const link = findIconLink();
favicoLink = link;
favicoLinkHref = link?.getAttribute('href') ?? null;
favico = new Favico(link === null ? {animation: 'none'} : {animation: 'none', element: link});
return favico;
} catch (e) {
logger.warn('Failed to initialize Favico', e);
releaseFavico();
return null;
}
};
@@ -39,7 +66,11 @@ const setElectronBadge = (badge: number): void => {
logger.warn('Failed to set Electron badge', e);
}
};
const setFaviconBadge = (badge: number): void => {
const setFaviconBadge = (badge: number, source: string | null): void => {
if (source !== favicoSource) {
releaseFavico();
favicoSource = source;
}
if (badge === 0 && favico === null) return;
const fav = initFavico();
if (!fav) return;
@@ -69,9 +100,9 @@ const setPwaBadge = (badge: number): void => {
logger.warn('Failed to set PWA badge', e);
}
};
const setBadge = (badge: number): void => {
const setBadge = (badge: number, faviconSource: string | null): void => {
setElectronBadge(badge);
setFaviconBadge(badge);
setFaviconBadge(badge, faviconSource);
setPwaBadge(badge);
};
export const AppBadge: React.FC = observer(() => {
@@ -89,15 +120,16 @@ export const AppBadge: React.FC = observer(() => {
} else if (hasUnread && unreadMessageBadgeEnabled) {
badge = UNREAD_INDICATOR;
}
const faviconSource = getDocumentFaviconUrl(RuntimeConfig.getSnapshotOrNull()?.appPublic ?? null);
useEffect(() => {
setBadge(badge);
}, [badge]);
setBadge(badge, faviconSource);
}, [badge, faviconSource]);
useEffect(() => {
updateDocumentTitleBadge(totalCount, hasUnread && unreadMessageBadgeEnabled);
}, [totalCount, hasUnread, unreadMessageBadgeEnabled]);
useEffect(() => {
return () => {
setBadge(0);
setBadge(0, favicoSource);
updateDocumentTitleBadge(0, false);
};
}, []);
@@ -77,10 +77,15 @@ function applyDocumentBranding(productName: string, faviconUrl: string | null, t
}
}
export function getDocumentFaviconUrl(appPublic: InstanceAppPublic | null): string | null {
if (appPublic === null) return DEFAULT_APP_SHELL_BRANDING.faviconUrl;
return appPublic.branding.favicon_url ?? appPublic.branding.icon_url;
}
export function applyRuntimeDocumentBranding(appPublic: InstanceAppPublic): void {
applyDocumentBranding(
appPublic.branding.product_name,
appPublic.branding.favicon_url ?? appPublic.branding.icon_url,
getDocumentFaviconUrl(appPublic),
appPublic.branding.theme_color,
);
}
@@ -88,7 +93,7 @@ export function applyRuntimeDocumentBranding(appPublic: InstanceAppPublic): void
export function applyDefaultAppShellDocumentBranding(): void {
applyDocumentBranding(
DEFAULT_APP_SHELL_BRANDING.productName,
DEFAULT_APP_SHELL_BRANDING.faviconUrl,
getDocumentFaviconUrl(null),
DEFAULT_APP_SHELL_BRANDING.themeColor,
);
}