Skip to content

Commit

Permalink
Replace with module API.
Browse files Browse the repository at this point in the history
  • Loading branch information
Half-Shot committed Jan 17, 2025
1 parent ee03527 commit 2814247
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 64 deletions.
13 changes: 0 additions & 13 deletions docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 0 additions & 4 deletions playwright/e2e/branding/title.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 }) => {
Expand Down
2 changes: 0 additions & 2 deletions src/IConfigOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
97 changes: 52 additions & 45 deletions src/components/structures/MatrixChat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,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";
Expand Down Expand Up @@ -225,18 +226,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;
Expand Down Expand Up @@ -280,13 +279,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";
}

/**
Expand Down Expand Up @@ -1112,7 +1104,6 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
}
this.setStateForNewView({
view: Views.WELCOME,
currentRoomId: null,
});
this.notifyNewScreen("welcome");
ThemeController.isLogin = true;
Expand All @@ -1122,7 +1113,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");
Expand Down Expand Up @@ -1490,7 +1480,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
collapseLhs: false,
currentRoomId: null,
});
this.subTitleStatus = "";
this.subtitleContext = undefined;
this.setPageSubtitle();
this.stores.onLoggedOut();
}
Expand All @@ -1506,7 +1496,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
collapseLhs: false,
currentRoomId: null,
});
this.subTitleStatus = "";
this.subtitleContext = undefined;
this.setPageSubtitle();
}

Expand Down Expand Up @@ -1958,33 +1948,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;
}
}

Expand All @@ -1995,17 +2008,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();
};

Expand Down
24 changes: 24 additions & 0 deletions src/modules/ModuleRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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;
Expand Down Expand Up @@ -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.
*
Expand Down Expand Up @@ -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.`,
);
}
}
}
}

Expand Down

0 comments on commit 2814247

Please sign in to comment.