Skip to content

Commit 3e80e8d

Browse files
authored
Merge pull request #38 from element-hq/t3chguy/restricted-guests-api
2 parents 85f231d + 3bdb8a5 commit 3e80e8d

File tree

15 files changed

+593
-89
lines changed

15 files changed

+593
-89
lines changed

packages/element-web-module-api/element-web-module-api.api.md

Lines changed: 88 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,26 @@
44
55
```ts
66

7+
import { ComponentType } from 'react';
78
import { JSX } from 'react';
89
import { ModuleApi } from '@matrix-org/react-sdk-module-api';
910
import { Root } from 'react-dom/client';
1011
import { RuntimeModule } from '@matrix-org/react-sdk-module-api';
1112

13+
// @public
14+
export interface AccountAuthApiExtension {
15+
overwriteAccountAuth(accountInfo: AccountAuthInfo): Promise<void>;
16+
}
17+
18+
// @public
19+
export interface AccountAuthInfo {
20+
accessToken: string;
21+
deviceId: string;
22+
homeserverUrl: string;
23+
refreshToken?: string;
24+
userId: string;
25+
}
26+
1227
// @alpha @deprecated (undocumented)
1328
export interface AliasCustomisations {
1429
// (undocumented)
@@ -19,12 +34,13 @@ export interface AliasCustomisations {
1934
// Warning: (ae-incompatible-release-tags) The symbol "Api" is marked as @public, but its signature references "LegacyCustomisationsApiExtension" which is marked as @alpha
2035
//
2136
// @public
22-
export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiExtension {
37+
export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiExtension, DialogApiExtension, AccountAuthApiExtension, ProfileApiExtension {
2338
readonly config: ConfigApi;
2439
createRoot(element: Element): Root;
2540
// @alpha
2641
readonly customComponents: CustomComponentsApi;
2742
readonly i18n: I18nApi;
43+
readonly navigation: NavigationApi;
2844
readonly rootNode: HTMLElement;
2945
}
3046

@@ -63,6 +79,7 @@ export interface ConfigApi {
6379
// @alpha
6480
export interface CustomComponentsApi {
6581
registerMessageRenderer(eventTypeOrFilter: string | ((mxEvent: MatrixEvent) => boolean), renderer: CustomMessageRenderFunction, hints?: CustomMessageRenderHints): void;
82+
registerRoomPreviewBar(renderer: CustomRoomPreviewBarRenderFunction): void;
6683
}
6784

6885
// @alpha
@@ -73,14 +90,50 @@ export type CustomMessageComponentProps = {
7390
// @alpha
7491
export type CustomMessageRenderFunction = (
7592
props: CustomMessageComponentProps,
76-
originalComponent?: (props?: OriginalComponentProps) => React.JSX.Element) => JSX.Element;
93+
originalComponent?: (props?: OriginalMessageComponentProps) => React.JSX.Element) => JSX.Element;
7794

7895
// @alpha
7996
export type CustomMessageRenderHints = {
8097
allowEditingEvent?: boolean;
8198
allowDownloadingMedia?: (mxEvent: MatrixEvent) => Promise<boolean>;
8299
};
83100

101+
// @alpha
102+
export type CustomRoomPreviewBarComponentProps = {
103+
roomId?: string;
104+
roomAlias?: string;
105+
};
106+
107+
// @alpha
108+
export type CustomRoomPreviewBarRenderFunction = (
109+
props: CustomRoomPreviewBarComponentProps,
110+
originalComponent: (props: CustomRoomPreviewBarComponentProps) => JSX.Element) => JSX.Element;
111+
112+
// @public
113+
export interface DialogApiExtension {
114+
openDialog<M, P extends object>(initialOptions: DialogOptions, dialog: ComponentType<P & DialogProps<M>>, props: P): DialogHandle<M>;
115+
}
116+
117+
// @public
118+
export type DialogHandle<M> = {
119+
finished: Promise<{
120+
ok: boolean;
121+
model: M | null;
122+
}>;
123+
close(): void;
124+
};
125+
126+
// @public
127+
export interface DialogOptions {
128+
title: string;
129+
}
130+
131+
// @public
132+
export type DialogProps<M> = {
133+
onSubmit(model: M): void;
134+
onCancel(): void;
135+
};
136+
84137
// @alpha @deprecated (undocumented)
85138
export interface DirectoryCustomisations {
86139
// (undocumented)
@@ -217,11 +270,28 @@ export class ModuleLoader {
217270
start(): Promise<void>;
218271
}
219272

273+
// @public
274+
export interface NavigationApi {
275+
toMatrixToLink(link: string, join?: boolean): Promise<void>;
276+
}
277+
220278
// @alpha
221-
export type OriginalComponentProps = {
279+
export type OriginalMessageComponentProps = {
222280
showUrlPreview?: boolean;
223281
};
224282

283+
// @public
284+
export interface Profile {
285+
displayName?: string;
286+
isGuest?: boolean;
287+
userId?: string;
288+
}
289+
290+
// @public
291+
export interface ProfileApiExtension {
292+
readonly profile: Watchable<Profile>;
293+
}
294+
225295
// @alpha @deprecated (undocumented)
226296
export interface RoomListCustomisations<Room> {
227297
isRoomVisible?(room: Room): boolean;
@@ -243,12 +313,27 @@ export interface UserIdentifierCustomisations {
243313
}): string | null;
244314
}
245315

316+
// @public
317+
export function useWatchable<T>(watchable: Watchable<T>): T;
318+
246319
// @public
247320
export type Variables = {
248321
count?: number;
249322
[key: string]: number | string | undefined;
250323
};
251324

325+
// @public
326+
export class Watchable<T> {
327+
constructor(currentValue: T);
328+
// (undocumented)
329+
unwatch(listener: (value: T) => void): void;
330+
// (undocumented)
331+
get value(): T;
332+
set value(value: T);
333+
// (undocumented)
334+
watch(listener: (value: T) => void): void;
335+
}
336+
252337
// @alpha @deprecated (undocumented)
253338
export interface WidgetPermissionsCustomisations<Widget, Capability> {
254339
preapproveCapabilities?(widget: Widget, requestedCapabilities: Set<Capability>): Promise<Set<Capability>>;

packages/element-web-module-api/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,12 @@
3939
"@types/semver": "^7.5.8",
4040
"@vitest/coverage-v8": "^3.0.4",
4141
"matrix-web-i18n": "^3.3.0",
42+
"rollup-plugin-external-globals": "^0.13.0",
4243
"semver": "^7.6.3",
4344
"typescript": "^5.7.3",
4445
"vite": "^6.1.6",
4546
"vite-plugin-dts": "^4.5.0",
46-
"vitest": "^3.0.5",
47+
"vitest": "^3.2.4",
4748
"vitest-sonar-reporter": "^2.0.0"
4849
},
4950
"peerDependencies": {
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
Copyright 2025 New Vector Ltd.
3+
4+
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
5+
Please see LICENSE files in the repository root for full details.
6+
*/
7+
8+
/**
9+
* Interface for account authentication information, used for overwriting the current account's authentication state.
10+
* @public
11+
*/
12+
export interface AccountAuthInfo {
13+
/**
14+
* The user ID.
15+
*/
16+
userId: string;
17+
/**
18+
* The device ID.
19+
*/
20+
deviceId: string;
21+
/**
22+
* The access token belonging to this device ID and user ID.
23+
*/
24+
accessToken: string;
25+
/**
26+
* The refresh token belonging to this device ID and user ID.
27+
*/
28+
refreshToken?: string;
29+
/**
30+
* The homeserver URL where the credentials are valid.
31+
*/
32+
homeserverUrl: string;
33+
}
34+
35+
/**
36+
* Methods to manage authentication in the application.
37+
* @public
38+
*/
39+
export interface AccountAuthApiExtension {
40+
/**
41+
* Overwrite the current account's authentication state with the provided account information.
42+
* @param accountInfo - The account authentication information to overwrite the current state with.
43+
*/
44+
overwriteAccountAuth(accountInfo: AccountAuthInfo): Promise<void>;
45+
}

packages/element-web-module-api/src/api/custom-components.ts

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export type CustomMessageComponentProps = {
2424
* Properties to alter the render function of the original component.
2525
* @alpha Subject to change.
2626
*/
27-
export type OriginalComponentProps = {
27+
export type OriginalMessageComponentProps = {
2828
/**
2929
* Should previews be shown for this event.
3030
* This may be overriden by user preferences.
@@ -64,7 +64,31 @@ export type CustomMessageRenderFunction = (
6464
/**
6565
* Render function for the original component. This may be omitted if the message would not normally be rendered.
6666
*/
67-
originalComponent?: (props?: OriginalComponentProps) => React.JSX.Element,
67+
originalComponent?: (props?: OriginalMessageComponentProps) => React.JSX.Element,
68+
) => JSX.Element;
69+
70+
/**
71+
* Properties for all message components.
72+
* @alpha Subject to change.
73+
*/
74+
export type CustomRoomPreviewBarComponentProps = {
75+
roomId?: string;
76+
roomAlias?: string;
77+
};
78+
79+
/**
80+
* Function used to render a room preview bar component.
81+
* @alpha Unlikely to change
82+
*/
83+
export type CustomRoomPreviewBarRenderFunction = (
84+
/**
85+
* Properties for the room preview bar to be rendered.
86+
*/
87+
props: CustomRoomPreviewBarComponentProps,
88+
/**
89+
* Render function for the original component.
90+
*/
91+
originalComponent: (props: CustomRoomPreviewBarComponentProps) => JSX.Element,
6892
) => JSX.Element;
6993

7094
/**
@@ -101,4 +125,22 @@ export interface CustomComponentsApi {
101125
renderer: CustomMessageRenderFunction,
102126
hints?: CustomMessageRenderHints,
103127
): void;
128+
129+
/**
130+
* Register a renderer for the room preview bar.
131+
*
132+
* The render function should return a rendered component.
133+
*
134+
* @param renderer - The render function for the room preview bar.
135+
* @example
136+
* ```
137+
* customComponents.registerRoomPreviewBar((props, OriginalComponent) => {
138+
* if (props.roomId === "!some_special_room_id:server") {
139+
* return <YourCustomRoomPreviewBarComponent {...props} />;
140+
* }
141+
* return <YourCustomComponent mxEvent={props.mxEvent} />;
142+
* });
143+
* ```
144+
*/
145+
registerRoomPreviewBar(renderer: CustomRoomPreviewBarRenderFunction): void;
104146
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
Copyright 2025 New Vector Ltd.
3+
4+
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
5+
Please see LICENSE files in the repository root for full details.
6+
*/
7+
8+
import { ComponentType } from "react";
9+
10+
/**
11+
* Options for {@link Api#openDialog}.
12+
* @public
13+
*/
14+
export interface DialogOptions {
15+
/**
16+
* The title of the dialog.
17+
*/
18+
title: string;
19+
}
20+
21+
/**
22+
* Handle returned by {@link Api#openDialog}.
23+
* @public
24+
*/
25+
export type DialogHandle<M> = {
26+
/**
27+
* Promise that resolves when the dialog is finished.
28+
*/
29+
finished: Promise<{ ok: boolean; model: M | null }>;
30+
/**
31+
* Method to close the dialog.
32+
*/
33+
close(): void;
34+
};
35+
36+
/**
37+
* Props passed to the dialog body component.
38+
* @public
39+
*/
40+
export type DialogProps<M> = {
41+
/**
42+
* Callback to submit the dialog.
43+
* @param model - The model to submit with the dialog. This is typically the data collected.
44+
*/
45+
onSubmit(model: M): void;
46+
/**
47+
* Cancel the dialog programmatically.
48+
*/
49+
onCancel(): void;
50+
};
51+
52+
/**
53+
* Methods to manage dialogs in the application.
54+
* @public
55+
*/
56+
export interface DialogApiExtension {
57+
/**
58+
* Open a dialog with the given options and body component and return a handle to it.
59+
* @param initialOptions - The initial options for the dialog, such as title and action label.
60+
* @param dialog - The body component to render in the dialog. This component should accept props of type `P`.
61+
* @param props - Additional props to pass to the body
62+
*/
63+
openDialog<M, P extends object>(
64+
initialOptions: DialogOptions,
65+
dialog: ComponentType<P & DialogProps<M>>,
66+
props: P,
67+
): DialogHandle<M>;
68+
}

packages/element-web-module-api/src/api/index.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ import { LegacyCustomisationsApiExtension } from "./legacy-customisations";
1111
import { ConfigApi } from "./config";
1212
import { I18nApi } from "./i18n";
1313
import { CustomComponentsApi } from "./custom-components";
14+
import { NavigationApi } from "./navigation.ts";
15+
import { DialogApiExtension } from "./dialog.ts";
16+
import { AccountAuthApiExtension } from "./auth.ts";
17+
import { ProfileApiExtension } from "./profile.ts";
1418

1519
/**
1620
* Module interface for modules to implement.
@@ -69,7 +73,12 @@ export function isModule(module: unknown): module is ModuleExport {
6973
* The API for modules to interact with the application.
7074
* @public
7175
*/
72-
export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiExtension {
76+
export interface Api
77+
extends LegacyModuleApiExtension,
78+
LegacyCustomisationsApiExtension,
79+
DialogApiExtension,
80+
AccountAuthApiExtension,
81+
ProfileApiExtension {
7382
/**
7483
* The API to read config.json values.
7584
* Keys should be scoped to the module in reverse domain name notation.
@@ -93,6 +102,13 @@ export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiEx
93102
* @alpha
94103
*/
95104
readonly customComponents: CustomComponentsApi;
105+
106+
/**
107+
* API to navigate the application.
108+
* @public
109+
*/
110+
readonly navigation: NavigationApi;
111+
96112
/**
97113
* Create a ReactDOM root for rendering React components.
98114
* Exposed to allow modules to avoid needing to bundle their own ReactDOM.

0 commit comments

Comments
 (0)