implement experimental module APIs for additional room sec settings

This commit is contained in:
Matthew Hodgson 2026-04-10 17:52:06 +01:00
parent 6b4a7833db
commit 29be1e29d8
5 changed files with 65 additions and 1 deletions

View File

@ -1617,6 +1617,29 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
// ignore if we don't have a room yet
if (!this.state.room || this.state.room.roomId !== state.roomId || !this.context.client) return;
// Notify module state event listeners and force a re-render so
// module-provided UI (banners, composer components) updates.
const listeners = ModuleApi.instance.client.stateEventListeners;
if (listeners.length) {
const eventId = ev.getId();
const roomId = ev.getRoomId();
const sender = ev.getSender();
if (eventId && roomId && sender) {
const moduleEvent = {
content: ev.getContent(),
eventId,
originServerTs: ev.getTs(),
roomId,
sender,
stateKey: ev.getStateKey(),
type: ev.getType(),
unsigned: ev.getUnsigned(),
};
for (const cb of listeners) cb(moduleEvent);
this.forceUpdate();
}
}
switch (ev.getType()) {
case EventType.RoomTombstone:
this.setState({ tombstone: this.getRoomTombstone() });

View File

@ -28,6 +28,7 @@ import { SettingLevel } from "../../../../../settings/SettingLevel";
import SettingsStore from "../../../../../settings/SettingsStore";
import { UIFeature } from "../../../../../settings/UIFeature";
import AccessibleButton from "../../../elements/AccessibleButton";
import { ModuleApi } from "../../../../../modules/Api";
import SettingsFlag from "../../../elements/SettingsFlag";
import createRoom from "../../../../../createRoom";
import CreateRoomDialog from "../../../dialogs/CreateRoomDialog";
@ -575,6 +576,10 @@ export default class SecurityRoomSettingsTab extends React.Component<IProps, ISt
</>
)}
</SettingsFieldset>
{ModuleApi.instance.extras.roomSettingsSecurityCallbacks.map((cb, i) => {
const el = cb(this.props.room.roomId);
return el ? <React.Fragment key={i}>{el}</React.Fragment> : null;
})}
{this.renderJoinRule()}
{historySection}
</SettingsSection>

View File

@ -136,6 +136,7 @@ export class ModuleApi implements Api {
}
return original.call(this, event, ...args);
};
}
}

View File

@ -4,7 +4,7 @@ Copyright 2025 Element Creations 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 { ClientApi as IClientApi, Room } from "@element-hq/element-web-module-api";
import type { ClientApi as IClientApi, Room, MatrixEvent as ModuleMatrixEvent } from "@element-hq/element-web-module-api";
import { Room as ModuleRoom } from "./models/Room";
import { AccountDataApi } from "./AccountDataApi";
import { MatrixClientPeg } from "../MatrixClientPeg";
@ -18,6 +18,35 @@ export class ClientApi implements IClientApi {
return null;
}
public async uploadContent(content: Blob | File, contentType?: string): Promise<string> {
const client = MatrixClientPeg.safeGet();
const { content_uri: mxcUrl } = await client.uploadContent(content, {
includeFilename: false,
type: contentType,
});
return mxcUrl;
}
public async sendStateEvent(
roomId: string,
eventType: string,
content: Record<string, unknown>,
stateKey: string = "",
): Promise<void> {
const client = MatrixClientPeg.safeGet();
await client.sendStateEvent(roomId, eventType, content, stateKey);
}
public stateEventListeners: Array<(event: ModuleMatrixEvent) => void> = [];
public onStateEvent(callback: (event: ModuleMatrixEvent) => void): () => void {
this.stateEventListeners.push(callback);
return () => {
const idx = this.stateEventListeners.indexOf(callback);
if (idx >= 0) this.stateEventListeners.splice(idx, 1);
};
}
public async downloadMxc(mxcUrl: string): Promise<string> {
const client = MatrixClientPeg.safeGet();
// useAuthentication=true produces the authenticated /_matrix/client/v1/media/download URL

View File

@ -14,6 +14,7 @@ import {
type RoomBannerCallback,
type ComposerLeftComponentCallback,
type EventContentTransformCallback,
type RoomSettingsSecurityCallback,
} from "@element-hq/element-web-module-api";
import { TypedEventEmitter } from "matrix-js-sdk/src/matrix";
@ -39,6 +40,7 @@ export class ElementWebExtrasApi extends TypedEventEmitter<keyof EmittedEvents,
public composerLeftComponentCallbacks: ComposerLeftComponentCallback[] = [];
public eventContentTransformCallbacks: EventContentTransformCallback[] = [];
public encryptedEnvelopeTransformCallbacks: EventContentTransformCallback[] = [];
public roomSettingsSecurityCallbacks: RoomSettingsSecurityCallback[] = [];
public setSpacePanelItem(spacekey: string, item: SpacePanelItemProps): void {
this.spacePanelItems.set(spacekey, item);
@ -68,6 +70,10 @@ export class ElementWebExtrasApi extends TypedEventEmitter<keyof EmittedEvents,
public addEncryptedEnvelopeTransformCallback(cb: EventContentTransformCallback): void {
this.encryptedEnvelopeTransformCallbacks.push(cb);
}
public addRoomSettingsSecurityCallback(cb: RoomSettingsSecurityCallback): void {
this.roomSettingsSecurityCallbacks.push(cb);
}
}
export function useModuleSpacePanelItems(api: ElementWebExtrasApi): ModuleSpacePanelItem[] {