diff --git a/src/customisations/WidgetVariables.ts b/src/customisations/WidgetVariables.ts new file mode 100644 index 0000000000..db3a56436d --- /dev/null +++ b/src/customisations/WidgetVariables.ts @@ -0,0 +1,53 @@ +/* + * Copyright 2021 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Populate this class with the details of your customisations when copying it. +import { ITemplateParams } from "matrix-widget-api"; + +/** + * Provides a partial set of the variables needed to render any widget. If + * variables are missing or not provided then they will be filled with the + * application-determined defaults. + * + * This will not be called until after isReady() resolves. + * @returns {Partial>} The variables. + */ +function provideVariables(): Partial> { + return {}; +} + +/** + * Resolves to whether or not the customisation point is ready for variables + * to be provided. This will block widgets being rendered. + * @returns {Promise} Resolves when ready. + */ +async function isReady(): Promise { + return; // default no waiting +} + +// This interface summarises all available customisation points and also marks +// them all as optional. This allows customisers to only define and export the +// customisations they need while still maintaining type safety. +export interface IWidgetVariablesCustomisations { + provideVariables?: typeof provideVariables; + + // If not provided, the app will assume that the customisation is always ready. + isReady?: typeof isReady; +} + +// A real customisation module will define and export one or more of the +// customisation points that make up the interface above. +export const WidgetVariableCustomisations: IWidgetVariablesCustomisations = {}; diff --git a/src/stores/widgets/StopGapWidget.ts b/src/stores/widgets/StopGapWidget.ts index 24869b5edc..daa1e0e787 100644 --- a/src/stores/widgets/StopGapWidget.ts +++ b/src/stores/widgets/StopGapWidget.ts @@ -54,6 +54,7 @@ import { ElementWidgetCapabilities } from "./ElementWidgetCapabilities"; import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import { ELEMENT_CLIENT_ID } from "../../identifiers"; import { getUserLanguage } from "../../languageHandler"; +import { WidgetVariableCustomisations } from "../../customisations/WidgetVariables"; // TODO: Destroy all of this code @@ -191,7 +192,8 @@ export class StopGapWidget extends EventEmitter { } private runUrlTemplate(opts = { asPopout: false }): string { - const templated = this.mockWidget.getCompleteUrl({ + const fromCustomisation = WidgetVariableCustomisations?.provideVariables?.() ?? {}; + const defaults: ITemplateParams = { widgetRoomId: this.roomId, currentUserId: MatrixClientPeg.get().getUserId(), userDisplayName: OwnProfileStore.instance.displayName, @@ -199,7 +201,8 @@ export class StopGapWidget extends EventEmitter { clientId: ELEMENT_CLIENT_ID, clientTheme: SettingsStore.getValue("theme"), clientLanguage: getUserLanguage(), - }, opts?.asPopout); + }; + const templated = this.mockWidget.getCompleteUrl(Object.assign(defaults, fromCustomisation), opts?.asPopout); const parsed = new URL(templated); @@ -363,6 +366,9 @@ export class StopGapWidget extends EventEmitter { } public async prepare(): Promise { + // Ensure the variables are ready for us to be rendered before continuing + await (WidgetVariableCustomisations?.isReady?.() ?? Promise.resolve()); + if (this.scalarToken) return; const existingMessaging = WidgetMessagingStore.instance.getMessaging(this.mockWidget); if (existingMessaging) this.messaging = existingMessaging;