Replace with module API.
This commit is contained in:
@@ -212,19 +212,6 @@ Starting with `branding`, the following subproperties are available:
|
||||
3. `auth_footer_links`: A list of links to add to the footer during login, registration, etc. Each entry must have a `text` and
|
||||
`url` property.
|
||||
|
||||
4. `title_template`: A template string that can be used to configure the title of the application when not viewing a room.
|
||||
5. `title_template_in_room`: A template string that can be used to configure the title of the application when viewing a room
|
||||
|
||||
#### `title_template` vars
|
||||
|
||||
- `$brand` The name of the web app, as configured by the `brand` config value.
|
||||
- `$room_name` The friendly name of a room. Only applicable to `title_template_in_room`.
|
||||
- `$status` The client's status, repesented as.
|
||||
- The notification count, when at least one room is unread.
|
||||
- "\*" when no rooms are unread, but notifications are not muted.
|
||||
- "Offline", when the client is offline.
|
||||
- "", when the client isn't logged in or notifications are muted.
|
||||
|
||||
`embedded_pages` can be configured as such:
|
||||
|
||||
1. `welcome_url`: A URL to an HTML page to show as a welcome page (landing on `#/welcome`). When not specified, the default
|
||||
|
||||
@@ -28,10 +28,6 @@ test.describe("Test with custom branding", () => {
|
||||
test.use({
|
||||
config: {
|
||||
brand: "TestBrand",
|
||||
branding: {
|
||||
title_template: "TestingApp $ignoredParameter $brand $status $ignoredParameter",
|
||||
title_template_in_room: "TestingApp $brand $status $room_name $ignoredParameter",
|
||||
},
|
||||
},
|
||||
});
|
||||
test("Shows custom branding when showing the home page", async ({ pageWithCredentials: page }) => {
|
||||
|
||||
@@ -50,8 +50,6 @@ export interface IConfigOptions {
|
||||
welcome_background_url?: string | string[]; // chosen at random if array
|
||||
auth_header_logo_url?: string;
|
||||
auth_footer_links?: { text: string; url: string }[];
|
||||
title_template?: string;
|
||||
title_template_in_room?: string;
|
||||
};
|
||||
|
||||
force_verification?: boolean; // if true, users must verify new logins
|
||||
|
||||
@@ -131,6 +131,7 @@ import { ConfirmSessionLockTheftView } from "./auth/ConfirmSessionLockTheftView"
|
||||
import { LoginSplashView } from "./auth/LoginSplashView";
|
||||
import { cleanUpDraftsIfRequired } from "../../DraftCleaner";
|
||||
import { InitialCryptoSetupStore } from "../../stores/InitialCryptoSetupStore";
|
||||
import { AppTitleContext } from "@matrix-org/react-sdk-module-api/lib/lifecycles/BrandingExtensions";
|
||||
|
||||
// legacy export
|
||||
export { default as Views } from "../../Views";
|
||||
@@ -223,18 +224,16 @@ 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 prevWindowWidth: number;
|
||||
|
||||
private readonly titleTemplate: string;
|
||||
private readonly titleTemplateInRoom: string;
|
||||
|
||||
private readonly loggedInView = createRef<LoggedInViewType>();
|
||||
private dispatcherRef?: string;
|
||||
private themeWatcher?: ThemeWatcher;
|
||||
private fontWatcher?: FontWatcher;
|
||||
private readonly stores: SdkContextClass;
|
||||
|
||||
private subtitleContext?: {unreadNotificationCount: number, userNotificationLevel: NotificationLevel, syncState: SyncState};
|
||||
|
||||
public constructor(props: IProps) {
|
||||
super(props);
|
||||
this.stores = SdkContextClass.instance;
|
||||
@@ -278,13 +277,6 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||
}
|
||||
|
||||
this.prevWindowWidth = UIStore.instance.windowWidth || 1000;
|
||||
|
||||
// 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.titleTemplate = props.config.branding?.title_template ?? "$brand $status";
|
||||
this.titleTemplateInRoom = props.config.branding?.title_template_in_room ?? "$brand $status | $room_name";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1109,7 +1101,6 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||
}
|
||||
this.setStateForNewView({
|
||||
view: Views.WELCOME,
|
||||
currentRoomId: null,
|
||||
});
|
||||
this.notifyNewScreen("welcome");
|
||||
ThemeController.isLogin = true;
|
||||
@@ -1119,7 +1110,6 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||
private viewLogin(otherState?: any): void {
|
||||
this.setStateForNewView({
|
||||
view: Views.LOGIN,
|
||||
currentRoomId: null,
|
||||
...otherState,
|
||||
});
|
||||
this.notifyNewScreen("login");
|
||||
@@ -1482,7 +1472,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||
collapseLhs: false,
|
||||
currentRoomId: null,
|
||||
});
|
||||
this.subTitleStatus = "";
|
||||
this.subtitleContext = undefined;
|
||||
this.setPageSubtitle();
|
||||
this.stores.onLoggedOut();
|
||||
}
|
||||
@@ -1498,7 +1488,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||
collapseLhs: false,
|
||||
currentRoomId: null,
|
||||
});
|
||||
this.subTitleStatus = "";
|
||||
this.subtitleContext = undefined;
|
||||
this.setPageSubtitle();
|
||||
}
|
||||
|
||||
@@ -1950,33 +1940,56 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||
}
|
||||
|
||||
private setPageSubtitle(): void {
|
||||
const params: {
|
||||
$brand: string;
|
||||
$status: string;
|
||||
$room_name: string | undefined;
|
||||
} = {
|
||||
$brand: SdkConfig.get().brand,
|
||||
$status: this.subTitleStatus,
|
||||
$room_name: undefined,
|
||||
const extraContext = this.subtitleContext;
|
||||
let context: AppTitleContext = {
|
||||
brand: SdkConfig.get().brand,
|
||||
syncError: extraContext?.syncState === SyncState.Error,
|
||||
};
|
||||
|
||||
if (this.state.currentRoomId) {
|
||||
const client = MatrixClientPeg.get();
|
||||
const room = client?.getRoom(this.state.currentRoomId);
|
||||
if (room) {
|
||||
params.$room_name = room.name;
|
||||
if (extraContext) {
|
||||
if (this.state.currentRoomId) {
|
||||
const client = MatrixClientPeg.get();
|
||||
const room = client?.getRoom(this.state.currentRoomId);
|
||||
context = {
|
||||
...context,
|
||||
roomId: this.state.currentRoomId,
|
||||
roomName: room?.name,
|
||||
notificationsMuted: extraContext.userNotificationLevel < NotificationLevel.Activity,
|
||||
unreadNotificationCount: extraContext.unreadNotificationCount,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const titleTemplate = params.$room_name ? this.titleTemplateInRoom : this.titleTemplate;
|
||||
const moduleTitle = ModuleRunner.instance.extensions.branding?.getAppTitle(context);
|
||||
if (moduleTitle) {
|
||||
if (document.title !== moduleTitle) {
|
||||
document.title = moduleTitle;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const title = Object.entries(params).reduce(
|
||||
(title: string, [key, value]) => title.replaceAll(key, (value ?? "").replaceAll("$", "$_DLR$")),
|
||||
titleTemplate,
|
||||
);
|
||||
let subtitle = "";
|
||||
if (context?.syncError) {
|
||||
subtitle += `[${_t("common|offline")}] `;
|
||||
}
|
||||
if ('unreadNotificationCount' in context && context.unreadNotificationCount > 0) {
|
||||
subtitle += `[${context.unreadNotificationCount}]`;
|
||||
} else if ('notificationsMuted' in context && !context.notificationsMuted) {
|
||||
subtitle += `*`;
|
||||
}
|
||||
|
||||
if ('roomId' in context && context.roomId) {
|
||||
if (context.roomName) {
|
||||
subtitle = `${subtitle} | ${context.roomName}`;
|
||||
}
|
||||
} else {
|
||||
subtitle = subtitle;
|
||||
}
|
||||
|
||||
const title = `${SdkConfig.get().brand} ${subtitle}`;
|
||||
|
||||
if (document.title !== title) {
|
||||
document.title = title.replaceAll("$_DLR$", "$");
|
||||
document.title = title;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1987,17 +2000,11 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||
PlatformPeg.get()!.setErrorStatus(state === SyncState.Error);
|
||||
PlatformPeg.get()!.setNotificationCount(numUnreadRooms);
|
||||
}
|
||||
|
||||
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.subtitleContext = {
|
||||
syncState: state,
|
||||
userNotificationLevel: notificationState.level,
|
||||
unreadNotificationCount: numUnreadRooms,
|
||||
};
|
||||
this.setPageSubtitle();
|
||||
};
|
||||
|
||||
|
||||
@@ -17,6 +17,10 @@ import {
|
||||
DefaultExperimentalExtensions,
|
||||
ProvideExperimentalExtensions,
|
||||
} from "@matrix-org/react-sdk-module-api/lib/lifecycles/ExperimentalExtensions";
|
||||
import {
|
||||
ProvideBrandingExtensions,
|
||||
} from "@matrix-org/react-sdk-module-api/lib/lifecycles/BrandingExtensions";
|
||||
|
||||
|
||||
import { AppModule } from "./AppModule";
|
||||
import { ModuleFactory } from "./ModuleFactory";
|
||||
@@ -30,6 +34,7 @@ class ExtensionsManager {
|
||||
// Private backing fields for extensions
|
||||
private cryptoSetupExtension: ProvideCryptoSetupExtensions;
|
||||
private experimentalExtension: ProvideExperimentalExtensions;
|
||||
private brandingExtension?: ProvideBrandingExtensions;
|
||||
|
||||
/** `true` if `cryptoSetupExtension` is the default implementation; `false` if it is implemented by a module. */
|
||||
private hasDefaultCryptoSetupExtension = true;
|
||||
@@ -67,6 +72,15 @@ class ExtensionsManager {
|
||||
return this.experimentalExtension;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides branding extension.
|
||||
*
|
||||
* @returns The registered extension. If no module provides this extension, undefined is returned..
|
||||
*/
|
||||
public get branding(): ProvideBrandingExtensions|undefined {
|
||||
return this.brandingExtension;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add any extensions provided by the module.
|
||||
*
|
||||
@@ -100,6 +114,16 @@ class ExtensionsManager {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (runtimeModule.extensions?.branding) {
|
||||
if (!this.brandingExtension) {
|
||||
this.brandingExtension = runtimeModule.extensions?.branding;
|
||||
} else {
|
||||
throw new Error(
|
||||
`adding experimental branding implementation from module ${runtimeModule.moduleName} but an implementation was already provided.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user