Rewrite favicon code to support modules.

This commit is contained in:
Half-Shot 2025-06-23 12:42:43 +01:00
parent bb0f2d65ab
commit 3ffcaba20b
4 changed files with 74 additions and 21 deletions

View File

@ -494,15 +494,10 @@ export default abstract class BasePlatform {
}
private updateFavicon(): void {
let bgColor = "#d00";
let notif: string | number = this.notificationCount;
if (this.errorDidOccur) {
notif = notif || "×";
bgColor = "#f00";
}
this.favicon.badge(notif, { bgColor });
this.favicon.badge({
notificationCount: this.notificationCount,
errorDidOccur: this.errorDidOccur
});
}
/**

View File

@ -17,6 +17,8 @@ interface IParams {
isUp: boolean;
isLeft: boolean;
}
import { FaviconRenderFunction, FaviconRenderOptions } from "@element-hq/element-web-module-api";
import moduleApi from "./modules/Api.ts"
const defaults: IParams = {
bgColor: "#d00",
@ -184,9 +186,9 @@ export default class Favicon {
this.readyCb?.();
}
private setIcon(canvas: HTMLCanvasElement): void {
private setIcon(src: string): void {
setTimeout(() => {
this.setIconSrc(canvas.toDataURL("image/png"));
this.setIconSrc(src);
}, 0);
}
@ -209,21 +211,39 @@ export default class Favicon {
}
}
public badge(content: number | string, opts?: Partial<IParams>): void {
if (!this.isReady) {
this.readyCb = (): void => {
this.badge(content, opts);
};
return;
/**
* Default badge renderer, may be overridden by a module.
* @param opts Notification rendering options.
* @returns A data URL for the favicon.
*/
private renderBadge: FaviconRenderFunction = ({notificationCount, errorDidOccur}) => {
let bgColor = "#d00";
let notif: string | number = notificationCount;
if (errorDidOccur) {
notif = notif || "×";
bgColor = "#f00";
}
if (typeof content === "string" || content > 0) {
this.circle(content, opts);
if (errorDidOccur || notificationCount > 0) {
this.circle(notif, {...this.params, bgColor });
} else {
this.reset();
}
this.setIcon(this.canvas);
return this.canvas.toDataURL("image/png");
}
public badge(opts: FaviconRenderOptions): void {
if (!this.isReady) {
this.readyCb = (): void => {
this.badge(opts);
};
return;
}
const badgeUrl = moduleApi.faviconApi.renderFavicon(opts) || this.renderBadge(opts);
this.setIcon(badgeUrl);
}
private static getLinks(): HTMLLinkElement[] {
@ -237,7 +257,7 @@ export default class Favicon {
return icons;
}
private static getIcons(): HTMLLinkElement[] {
public static getIcons(): HTMLLinkElement[] {
// get favicon link elements
let elms = Favicon.getLinks();
if (elms.length === 0) {

View File

@ -22,6 +22,7 @@ import { WidgetVariableCustomisations } from "../customisations/WidgetVariables.
import { ConfigApi } from "./ConfigApi.ts";
import { I18nApi } from "./I18nApi.ts";
import { CustomComponentsApi } from "./customComponentApi.ts";
import { FaviconApi } from "./faviconApi.ts";
const legacyCustomisationsFactory = <T extends object>(baseCustomisations: T) => {
let used = false;
@ -61,6 +62,7 @@ class ModuleApi implements Api {
public readonly i18n = new I18nApi();
public readonly customComponents = new CustomComponentsApi();
public readonly rootNode = document.getElementById("matrixchat")!;
public readonly faviconApi = new FaviconApi();
public createRoot(element: Element): Root {
return createRoot(element);

36
src/modules/faviconApi.ts Normal file
View File

@ -0,0 +1,36 @@
/*
Copyright 2025 New Vector Ltd.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import type {
FaviconApi as IFaviconApi,
FaviconRenderFunction,
FaviconRenderOptions
} from "@element-hq/element-web-module-api";
export class FaviconApi implements IFaviconApi {
private registeredFunction?: FaviconRenderFunction;
public registerRenderer(
func: FaviconRenderFunction
): void {
if (this.registeredFunction) {
throw Error('A custom favicon rendering function has already been registered');
}
this.registeredFunction = func;
}
/**
* Returns a URL to a rendered favicon if a module has generated one, otherwise
* this returns undefined.
* @param opts Options to pass to the render function.
* @returns A URL string, or undefined.
*/
public renderFavicon(opts: FaviconRenderOptions): string|undefined {
return this.registeredFunction?.(opts);
}
}