Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit e15041b

Browse files
committed
Add a custom widget API action for viewing a different room
1 parent 294876f commit e15041b

File tree

5 files changed

+89
-7
lines changed

5 files changed

+89
-7
lines changed

src/components/views/dialogs/ModalWidgetDialog.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export default class ModalWidgetDialog extends React.PureComponent<IProps, IStat
6161
}
6262

6363
public componentDidMount() {
64-
const driver = new StopGapWidgetDriver( []);
64+
const driver = new StopGapWidgetDriver( [], this.widget.type);
6565
const messaging = new ClientWidgetApi(this.widget, this.appFrame.current, driver);
6666
this.setState({messaging});
6767
}

src/stores/widgets/ElementWidgetActions.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,17 @@
1414
* limitations under the License.
1515
*/
1616

17+
import { IWidgetApiRequest } from "matrix-widget-api";
18+
1719
export enum ElementWidgetActions {
1820
ClientReady = "im.vector.ready",
1921
HangupCall = "im.vector.hangup",
2022
OpenIntegrationManager = "integration_manager_open",
23+
ViewRoom = "io.element.view_room",
24+
}
25+
26+
export interface IViewRoomApiRequest extends IWidgetApiRequest {
27+
data: {
28+
room_id: string;
29+
};
2130
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/*
2+
* Copyright 2020 The Matrix.org Foundation C.I.C.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
export enum ElementWidgetCapabilities {
18+
CanChangeViewedRoom = "io.element.view_room",
19+
}

src/stores/widgets/StopGapWidget.ts

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import {
3232
Widget,
3333
WidgetApiToWidgetAction,
3434
WidgetApiFromWidgetAction,
35-
IModalWidgetOpenRequest,
35+
IModalWidgetOpenRequest, IWidgetApiErrorResponseData,
3636
} from "matrix-widget-api";
3737
import { StopGapWidgetDriver } from "./StopGapWidgetDriver";
3838
import { EventEmitter } from "events";
@@ -47,13 +47,14 @@ import { WidgetType } from "../../widgets/WidgetType";
4747
import ActiveWidgetStore from "../ActiveWidgetStore";
4848
import { objectShallowClone } from "../../utils/objects";
4949
import defaultDispatcher from "../../dispatcher/dispatcher";
50-
import { ElementWidgetActions } from "./ElementWidgetActions";
50+
import { ElementWidgetActions, IViewRoomApiRequest } from "./ElementWidgetActions";
5151
import Modal from "../../Modal";
5252
import WidgetOpenIDPermissionsDialog from "../../components/views/dialogs/WidgetOpenIDPermissionsDialog";
5353
import {ModalWidgetStore} from "../ModalWidgetStore";
5454
import ThemeWatcher from "../../settings/watchers/ThemeWatcher";
5555
import {getCustomTheme} from "../../theme";
5656
import CountlyAnalytics from "../../CountlyAnalytics";
57+
import { ElementWidgetCapabilities } from "./ElementWidgetCapabilities";
5758

5859
// TODO: Destroy all of this code
5960

@@ -286,7 +287,8 @@ export class StopGapWidget extends EventEmitter {
286287

287288
public start(iframe: HTMLIFrameElement) {
288289
if (this.started) return;
289-
const driver = new StopGapWidgetDriver( this.appTileProps.whitelistCapabilities || []);
290+
const allowedCapabilities = this.appTileProps.whitelistCapabilities || [];
291+
const driver = new StopGapWidgetDriver( allowedCapabilities, this.mockWidget.type);
290292
this.messaging = new ClientWidgetApi(this.mockWidget, iframe, driver);
291293
this.messaging.on("preparing", () => this.emit("preparing"));
292294
this.messaging.on("ready", () => this.emit("ready"));
@@ -298,6 +300,35 @@ export class StopGapWidget extends EventEmitter {
298300
ActiveWidgetStore.setRoomId(this.mockWidget.id, this.appTileProps.room.roomId);
299301
}
300302

303+
// Always attach a handler for ViewRoom, but permission check it internally
304+
this.messaging.on(`action:${ElementWidgetActions.ViewRoom}`, (ev: CustomEvent<IViewRoomApiRequest>) => {
305+
ev.preventDefault(); // stop the widget API from auto-rejecting this
306+
307+
// Check up front if this is even a valid request
308+
const targetRoomId = (ev.detail.data || {}).room_id;
309+
if (!targetRoomId) {
310+
return this.messaging.transport.reply(ev.detail, <IWidgetApiErrorResponseData>{
311+
error: {message: "Invalid room ID."},
312+
});
313+
}
314+
315+
// Check the widget's permission
316+
if (!this.messaging.hasCapability(ElementWidgetCapabilities.CanChangeViewedRoom)) {
317+
return this.messaging.transport.reply(ev.detail, <IWidgetApiErrorResponseData>{
318+
error: {message: "This widget does not have permission for this action (denied)."},
319+
});
320+
}
321+
322+
// at this point we can change rooms, so do that
323+
defaultDispatcher.dispatch({
324+
action: 'view_room',
325+
room_id: targetRoomId,
326+
});
327+
328+
// acknowledge so the widget doesn't freak out
329+
this.messaging.transport.reply(ev.detail, <IWidgetApiRequestEmptyData>{});
330+
});
331+
301332
if (WidgetType.JITSI.matches(this.mockWidget.type)) {
302333
this.messaging.on("action:set_always_on_screen",
303334
(ev: CustomEvent<IStickyActionRequest>) => {

src/stores/widgets/StopGapWidgetDriver.ts

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,40 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { Capability, WidgetDriver } from "matrix-widget-api";
17+
import { Capability, WidgetDriver, WidgetType } from "matrix-widget-api";
1818
import { iterableUnion } from "../../utils/iterables";
19+
import { MatrixClientPeg } from "../../MatrixClientPeg";
20+
import { arrayFastClone } from "../../utils/arrays";
21+
import { ElementWidgetCapabilities } from "./ElementWidgetCapabilities";
1922

2023
// TODO: Purge this from the universe
2124

2225
export class StopGapWidgetDriver extends WidgetDriver {
23-
constructor(private allowedCapabilities: Capability[]) {
26+
constructor(private allowedCapabilities: Capability[], private forType: WidgetType) {
2427
super();
2528
}
2629

2730
public async validateCapabilities(requested: Set<Capability>): Promise<Set<Capability>> {
28-
return new Set(iterableUnion(requested, this.allowedCapabilities));
31+
// TODO: All of this should be a capabilities prompt.
32+
// See https://github.com/vector-im/element-web/issues/13111
33+
34+
// Note: None of this well-known widget permissions stuff is documented intentionally. We
35+
// do not want to encourage people relying on this, but need to be able to support it at
36+
// the moment.
37+
//
38+
// If you're a widget developer and seeing this message, please ask the Element team if
39+
// it is safe for you to use this permissions system before trying to use it - it might
40+
// not be here in the future.
41+
42+
const wkPerms = (MatrixClientPeg.get().getClientWellKnown() || {})['io.element.widget_permissions'];
43+
const allowedCaps = arrayFastClone(this.allowedCapabilities);
44+
if (wkPerms) {
45+
if (Array.isArray(wkPerms["view_room_action"])) {
46+
if (wkPerms["view_room_action"].includes(this.forType)) {
47+
allowedCaps.push(ElementWidgetCapabilities.CanChangeViewedRoom);
48+
}
49+
}
50+
}
51+
return new Set(iterableUnion(requested, allowedCaps));
2952
}
3053
}

0 commit comments

Comments
 (0)