mirror of
https://github.com/vector-im/element-web.git
synced 2026-05-05 12:16:53 +02:00
Rebrand to the "brand" API and add title support.
This commit is contained in:
parent
3ffcaba20b
commit
ced693c256
@ -141,6 +141,8 @@ import { type OpenForwardDialogPayload } from "../../dispatcher/payloads/OpenFor
|
||||
import { ShareFormat, type SharePayload } from "../../dispatcher/payloads/SharePayload";
|
||||
import Markdown from "../../Markdown";
|
||||
import { sanitizeHtmlParams } from "../../Linkify";
|
||||
import moduleApi from "../../modules/Api"
|
||||
import { TitleRenderOptions } from "@element-hq/element-web-module-api";
|
||||
|
||||
// legacy export
|
||||
export { default as Views } from "../../Views";
|
||||
@ -227,7 +229,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||
private tokenLogin?: boolean;
|
||||
// What to focus on next component update, if anything
|
||||
private focusNext: FocusNextType;
|
||||
private subTitleStatus: string;
|
||||
private subTitleState: TitleRenderOptions;
|
||||
private prevWindowWidth: number;
|
||||
|
||||
private readonly loggedInView = createRef<LoggedInViewType>();
|
||||
@ -283,7 +285,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||
|
||||
// object field used for tracking the status info appended to the title tag.
|
||||
// we don't do it as react state as i'm scared about triggering needless react refreshes.
|
||||
this.subTitleStatus = "";
|
||||
this.subTitleState = {};
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1505,7 +1507,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||
collapseLhs: false,
|
||||
currentRoomId: null,
|
||||
});
|
||||
this.subTitleStatus = "";
|
||||
this.subTitleState = {};
|
||||
this.setPageSubtitle();
|
||||
this.stores.onLoggedOut();
|
||||
}
|
||||
@ -1521,7 +1523,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||
collapseLhs: false,
|
||||
currentRoomId: null,
|
||||
});
|
||||
this.subTitleStatus = "";
|
||||
this.subTitleState = {};
|
||||
this.setPageSubtitle();
|
||||
}
|
||||
|
||||
@ -1991,19 +1993,43 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||
dis.dispatch({ action: "message_sent" });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private setPageSubtitle(subtitle = ""): void {
|
||||
private setPageSubtitle(): void {
|
||||
let roomName: string|undefined;
|
||||
if (this.state.currentRoomId) {
|
||||
const client = MatrixClientPeg.get();
|
||||
const room = client?.getRoom(this.state.currentRoomId);
|
||||
if (room) {
|
||||
subtitle = `${this.subTitleStatus} | ${room.name} ${subtitle}`;
|
||||
}
|
||||
} else {
|
||||
subtitle = `${this.subTitleStatus} ${subtitle}`;
|
||||
roomName = client?.getRoom(this.state.currentRoomId)?.name;
|
||||
}
|
||||
|
||||
let title = moduleApi.brandApi.renderTitle({...this.subTitleState, roomName, roomId: this.state.currentRoomId ?? undefined});
|
||||
|
||||
if (title === undefined) {
|
||||
// No module API implemented, fallback
|
||||
let subTitleStatus = "";
|
||||
|
||||
if (this.subTitleState.errorDidOccur) {
|
||||
subTitleStatus += `[${_t("common|offline")}] `
|
||||
}
|
||||
|
||||
if ((this.subTitleState.notificationCount ?? 0) > 0) {
|
||||
subTitleStatus += `[${this.subTitleState.notificationCount}]`;
|
||||
} else if (this.subTitleState.notificationsEnabled) {
|
||||
subTitleStatus += `*`;
|
||||
}
|
||||
|
||||
let subtitle;
|
||||
if (this.state.currentRoomId) {
|
||||
if (roomName) {
|
||||
subtitle = `${subTitleStatus} | ${roomName} ${subtitle}`;
|
||||
}
|
||||
} else {
|
||||
subtitle = `${subTitleStatus} ${subtitle}`;
|
||||
}
|
||||
|
||||
title = `${SdkConfig.get().brand} ${subtitle}`;
|
||||
}
|
||||
|
||||
const title = `${SdkConfig.get().brand} ${subtitle}`;
|
||||
|
||||
if (document.title !== title) {
|
||||
document.title = title;
|
||||
@ -2011,22 +2037,18 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||
}
|
||||
|
||||
private onUpdateStatusIndicator = (notificationState: SummarizedNotificationState, state: SyncState): void => {
|
||||
const numUnreadRooms = notificationState.numUnreadStates; // we know that states === rooms here
|
||||
const notificationCount = notificationState.numUnreadStates; // we know that states === rooms here
|
||||
|
||||
if (PlatformPeg.get()) {
|
||||
PlatformPeg.get()!.setErrorStatus(state === SyncState.Error);
|
||||
PlatformPeg.get()!.setNotificationCount(numUnreadRooms);
|
||||
PlatformPeg.get()!.setNotificationCount(notificationCount);
|
||||
}
|
||||
|
||||
this.subTitleStatus = "";
|
||||
if (state === SyncState.Error) {
|
||||
this.subTitleStatus += `[${_t("common|offline")}] `;
|
||||
}
|
||||
if (numUnreadRooms > 0) {
|
||||
this.subTitleStatus += `[${numUnreadRooms}]`;
|
||||
} else if (notificationState.level >= NotificationLevel.Activity) {
|
||||
this.subTitleStatus += `*`;
|
||||
}
|
||||
this.subTitleState = {
|
||||
errorDidOccur: state === SyncState.Error,
|
||||
notificationCount,
|
||||
notificationsEnabled: notificationState.level >= NotificationLevel.Activity,
|
||||
};
|
||||
|
||||
this.setPageSubtitle();
|
||||
};
|
||||
|
||||
@ -242,7 +242,7 @@ export default class Favicon {
|
||||
return;
|
||||
}
|
||||
|
||||
const badgeUrl = moduleApi.faviconApi.renderFavicon(opts) || this.renderBadge(opts);
|
||||
const badgeUrl = moduleApi.brandApi.renderFavicon(opts) || this.renderBadge(opts);
|
||||
this.setIcon(badgeUrl);
|
||||
}
|
||||
|
||||
|
||||
@ -22,7 +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";
|
||||
import { BrandApi } from "./brandApi.ts";
|
||||
|
||||
const legacyCustomisationsFactory = <T extends object>(baseCustomisations: T) => {
|
||||
let used = false;
|
||||
@ -62,7 +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 readonly brandApi = new BrandApi();
|
||||
|
||||
public createRoot(element: Element): Root {
|
||||
return createRoot(element);
|
||||
|
||||
58
src/modules/brandApi.ts
Normal file
58
src/modules/brandApi.ts
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
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 {
|
||||
BrandApi as IBrandApi,
|
||||
FaviconRenderFunction,
|
||||
FaviconRenderOptions,
|
||||
TitleRenderFunction,
|
||||
TitleRenderOptions
|
||||
} from "@element-hq/element-web-module-api";
|
||||
|
||||
|
||||
export class BrandApi implements IBrandApi {
|
||||
private registeredFaviconFunction?: FaviconRenderFunction;
|
||||
private registeredTitleFunction?: TitleRenderFunction;
|
||||
|
||||
public registerFaviconRenderer(
|
||||
func: FaviconRenderFunction
|
||||
): void {
|
||||
if (this.registeredFaviconFunction) {
|
||||
throw Error('A custom favicon rendering function has already been registered');
|
||||
}
|
||||
this.registeredFaviconFunction = func;
|
||||
}
|
||||
|
||||
public registerTitleRenderer(
|
||||
func: TitleRenderFunction
|
||||
): void {
|
||||
if (this.registeredTitleFunction) {
|
||||
throw Error('A custom title rendering function has already been registered');
|
||||
}
|
||||
this.registeredTitleFunction = 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.registeredFaviconFunction?.(opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the title text if a module has generated one, otherwise
|
||||
* this returns undefined.
|
||||
* @param opts Options to pass to the render function.
|
||||
* @returns Title text, or undefined.
|
||||
*/
|
||||
public renderTitle(opts: TitleRenderOptions): string|undefined {
|
||||
return this.registeredTitleFunction?.(opts);
|
||||
}
|
||||
}
|
||||
@ -1,36 +0,0 @@
|
||||
/*
|
||||
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);
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user