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

Commit 5f47077

Browse files
authored
Merge pull request #5385 from matrix-org/travis/msc-send-widget-events
Add new widget API actions for changing rooms and sending/receiving events
2 parents 8c2f1b4 + 2446fb1 commit 5f47077

File tree

16 files changed

+835
-15
lines changed

16 files changed

+835
-15
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@
7979
"linkifyjs": "^2.1.9",
8080
"lodash": "^4.17.19",
8181
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop",
82-
"matrix-widget-api": "^0.1.0-beta.8",
82+
"matrix-widget-api": "^0.1.0-beta.9",
8383
"minimist": "^1.2.5",
8484
"pako": "^1.0.11",
8585
"parse5": "^5.1.1",

res/css/_common.scss

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ pre, code {
6060
color: $accent-color;
6161
}
6262

63+
.text-muted {
64+
color: $muted-fg-color;
65+
}
66+
6367
b {
6468
// On Firefox, the default weight for `<b>` is `bolder` which results in no bold
6569
// effect since we only have specific weights of our fonts available.
@@ -364,6 +368,11 @@ input[type=text]:focus, input[type=password]:focus, textarea:focus {
364368
.mx_Dialog_buttons {
365369
margin-top: 20px;
366370
text-align: right;
371+
372+
.mx_Dialog_buttons_additive {
373+
// The consumer is responsible for positioning their elements.
374+
float: left;
375+
}
367376
}
368377

369378
/* XXX: Our button style are a mess: buttons that happen to appear in dialogs get special styles applied

res/css/_components.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@
9191
@import "./views/dialogs/_TermsDialog.scss";
9292
@import "./views/dialogs/_UploadConfirmDialog.scss";
9393
@import "./views/dialogs/_UserSettingsDialog.scss";
94+
@import "./views/dialogs/_WidgetCapabilitiesPromptDialog.scss";
9495
@import "./views/dialogs/_WidgetOpenIDPermissionsDialog.scss";
9596
@import "./views/dialogs/security/_AccessSecretStorageDialog.scss";
9697
@import "./views/dialogs/security/_CreateCrossSigningDialog.scss";
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
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+
18+
.mx_WidgetCapabilitiesPromptDialog {
19+
.text-muted {
20+
font-size: $font-12px;
21+
}
22+
23+
.mx_Dialog_content {
24+
margin-bottom: 16px;
25+
}
26+
27+
.mx_WidgetCapabilitiesPromptDialog_cap {
28+
margin-top: 20px;
29+
font-size: $font-15px;
30+
line-height: $font-15px;
31+
32+
.mx_WidgetCapabilitiesPromptDialog_byline {
33+
color: $muted-fg-color;
34+
margin-left: 26px;
35+
font-size: $font-12px;
36+
line-height: $font-12px;
37+
}
38+
}
39+
40+
.mx_Dialog_buttons {
41+
margin-top: 40px; // double normal
42+
}
43+
44+
.mx_SettingsFlag {
45+
line-height: calc($font-14px + 7px + 7px); // 7px top & bottom padding
46+
color: $muted-fg-color;
47+
font-size: $font-12px;
48+
49+
.mx_ToggleSwitch {
50+
display: inline-block;
51+
vertical-align: middle;
52+
margin-right: 8px;
53+
54+
// downsize the switch + ball
55+
width: $font-32px;
56+
height: $font-15px;
57+
58+
59+
&.mx_ToggleSwitch_on > .mx_ToggleSwitch_ball {
60+
left: calc(100% - $font-15px);
61+
}
62+
63+
.mx_ToggleSwitch_ball {
64+
width: $font-15px;
65+
height: $font-15px;
66+
border-radius: $font-15px;
67+
}
68+
}
69+
70+
.mx_SettingsFlag_label {
71+
display: inline-block;
72+
vertical-align: middle;
73+
}
74+
}
75+
}

src/components/views/dialogs/ModalWidgetDialog.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import {
3131
ModalButtonKind,
3232
Widget,
3333
WidgetApiFromWidgetAction,
34+
WidgetKind,
3435
} from "matrix-widget-api";
3536
import {StopGapWidgetDriver} from "../../../stores/widgets/StopGapWidgetDriver";
3637
import {MatrixClientPeg} from "../../../MatrixClientPeg";
@@ -72,7 +73,7 @@ export default class ModalWidgetDialog extends React.PureComponent<IProps, IStat
7273
}
7374

7475
public componentDidMount() {
75-
const driver = new StopGapWidgetDriver( []);
76+
const driver = new StopGapWidgetDriver( [], this.widget, WidgetKind.Modal);
7677
const messaging = new ClientWidgetApi(this.widget, this.appFrame.current, driver);
7778
this.setState({messaging});
7879
}
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
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+
import React from 'react';
18+
import BaseDialog from "./BaseDialog";
19+
import { _t } from "../../../languageHandler";
20+
import { IDialogProps } from "./IDialogProps";
21+
import {
22+
Capability,
23+
Widget,
24+
WidgetEventCapability,
25+
WidgetKind,
26+
} from "matrix-widget-api";
27+
import { objectShallowClone } from "../../../utils/objects";
28+
import StyledCheckbox from "../elements/StyledCheckbox";
29+
import DialogButtons from "../elements/DialogButtons";
30+
import LabelledToggleSwitch from "../elements/LabelledToggleSwitch";
31+
import { CapabilityText } from "../../../widgets/CapabilityText";
32+
33+
export function getRememberedCapabilitiesForWidget(widget: Widget): Capability[] {
34+
return JSON.parse(localStorage.getItem(`widget_${widget.id}_approved_caps`) || "[]");
35+
}
36+
37+
function setRememberedCapabilitiesForWidget(widget: Widget, caps: Capability[]) {
38+
localStorage.setItem(`widget_${widget.id}_approved_caps`, JSON.stringify(caps));
39+
}
40+
41+
interface IProps extends IDialogProps {
42+
requestedCapabilities: Set<Capability>;
43+
widget: Widget;
44+
widgetKind: WidgetKind; // TODO: Refactor into the Widget class
45+
}
46+
47+
interface IBooleanStates {
48+
// @ts-ignore - TS wants a string key, but we know better
49+
[capability: Capability]: boolean;
50+
}
51+
52+
interface IState {
53+
booleanStates: IBooleanStates;
54+
rememberSelection: boolean;
55+
}
56+
57+
export default class WidgetCapabilitiesPromptDialog extends React.PureComponent<IProps, IState> {
58+
private eventPermissionsMap = new Map<Capability, WidgetEventCapability>();
59+
60+
constructor(props: IProps) {
61+
super(props);
62+
63+
const parsedEvents = WidgetEventCapability.findEventCapabilities(this.props.requestedCapabilities);
64+
parsedEvents.forEach(e => this.eventPermissionsMap.set(e.raw, e));
65+
66+
const states: IBooleanStates = {};
67+
this.props.requestedCapabilities.forEach(c => states[c] = true);
68+
69+
this.state = {
70+
booleanStates: states,
71+
rememberSelection: true,
72+
};
73+
}
74+
75+
private onToggle = (capability: Capability) => {
76+
const newStates = objectShallowClone(this.state.booleanStates);
77+
newStates[capability] = !newStates[capability];
78+
this.setState({booleanStates: newStates});
79+
};
80+
81+
private onRememberSelectionChange = (newVal: boolean) => {
82+
this.setState({rememberSelection: newVal});
83+
};
84+
85+
private onSubmit = async (ev) => {
86+
this.closeAndTryRemember(Object.entries(this.state.booleanStates)
87+
.filter(([_, isSelected]) => isSelected)
88+
.map(([cap]) => cap));
89+
};
90+
91+
private onReject = async (ev) => {
92+
this.closeAndTryRemember([]); // nothing was approved
93+
};
94+
95+
private closeAndTryRemember(approved: Capability[]) {
96+
if (this.state.rememberSelection) {
97+
setRememberedCapabilitiesForWidget(this.props.widget, approved);
98+
}
99+
this.props.onFinished({approved});
100+
}
101+
102+
public render() {
103+
const checkboxRows = Object.entries(this.state.booleanStates).map(([cap, isChecked], i) => {
104+
const text = CapabilityText.for(cap, this.props.widgetKind);
105+
const byline = text.byline
106+
? <span className="mx_WidgetCapabilitiesPromptDialog_byline">{text.byline}</span>
107+
: null;
108+
109+
return (
110+
<div className="mx_WidgetCapabilitiesPromptDialog_cap" key={cap + i}>
111+
<StyledCheckbox
112+
checked={isChecked}
113+
onChange={() => this.onToggle(cap)}
114+
>{text.primary}</StyledCheckbox>
115+
{byline}
116+
</div>
117+
);
118+
});
119+
120+
return (
121+
<BaseDialog
122+
className="mx_WidgetCapabilitiesPromptDialog"
123+
onFinished={this.props.onFinished}
124+
title={_t("Approve widget permissions")}
125+
>
126+
<form onSubmit={this.onSubmit}>
127+
<div className="mx_Dialog_content">
128+
<div className="text-muted">{_t("This widget would like to:")}</div>
129+
{checkboxRows}
130+
<DialogButtons
131+
primaryButton={_t("Approve")}
132+
cancelButton={_t("Decline All")}
133+
onPrimaryButtonClick={this.onSubmit}
134+
onCancel={this.onReject}
135+
additive={
136+
<LabelledToggleSwitch
137+
value={this.state.rememberSelection}
138+
toggleInFront={true}
139+
onChange={this.onRememberSelectionChange}
140+
label={_t("Remember my selection for this widget")} />}
141+
/>
142+
</div>
143+
</form>
144+
</BaseDialog>
145+
);
146+
}
147+
}

src/components/views/elements/DialogButtons.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ export default class DialogButtons extends React.Component {
5454

5555
// disables only the primary button
5656
primaryDisabled: PropTypes.bool,
57+
58+
// something to stick next to the buttons, optionally
59+
additive: PropTypes.element,
5760
};
5861

5962
static defaultProps = {
@@ -85,8 +88,14 @@ export default class DialogButtons extends React.Component {
8588
</button>;
8689
}
8790

91+
let additive = null;
92+
if (this.props.additive) {
93+
additive = <div className="mx_Dialog_buttons_additive">{this.props.additive}</div>;
94+
}
95+
8896
return (
8997
<div className="mx_Dialog_buttons">
98+
{ additive }
9099
{ cancelButton }
91100
{ this.props.children }
92101
<button type={this.props.primaryIsSubmit ? 'submit' : 'button'}

src/i18n/strings/en_EN.json

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,62 @@
569569
"%(names)s and %(count)s others are typing …|other": "%(names)s and %(count)s others are typing …",
570570
"%(names)s and %(count)s others are typing …|one": "%(names)s and one other is typing …",
571571
"%(names)s and %(lastPerson)s are typing …": "%(names)s and %(lastPerson)s are typing …",
572+
"Remain on your screen when viewing another room, when running": "Remain on your screen when viewing another room, when running",
573+
"Remain on your screen while running": "Remain on your screen while running",
574+
"Send stickers into this room": "Send stickers into this room",
575+
"Send stickers into your active room": "Send stickers into your active room",
576+
"Change which room you're viewing": "Change which room you're viewing",
577+
"Change the topic of this room": "Change the topic of this room",
578+
"See when the topic changes in this room": "See when the topic changes in this room",
579+
"Change the topic of your active room": "Change the topic of your active room",
580+
"See when the topic changes in your active room": "See when the topic changes in your active room",
581+
"Change the name of this room": "Change the name of this room",
582+
"See when the name changes in this room": "See when the name changes in this room",
583+
"Change the name of your active room": "Change the name of your active room",
584+
"See when the name changes in your active room": "See when the name changes in your active room",
585+
"Change the avatar of this room": "Change the avatar of this room",
586+
"See when the avatar changes in this room": "See when the avatar changes in this room",
587+
"Change the avatar of your active room": "Change the avatar of your active room",
588+
"See when the avatar changes in your active room": "See when the avatar changes in your active room",
589+
"Send stickers to this room as you": "Send stickers to this room as you",
590+
"See when a sticker is posted in this room": "See when a sticker is posted in this room",
591+
"Send stickers to your active room as you": "Send stickers to your active room as you",
592+
"See when anyone posts a sticker to your active room": "See when anyone posts a sticker to your active room",
593+
"with an empty state key": "with an empty state key",
594+
"with state key %(stateKey)s": "with state key %(stateKey)s",
595+
"Send <b>%(eventType)s</b> events as you in this room": "Send <b>%(eventType)s</b> events as you in this room",
596+
"See <b>%(eventType)s</b> events posted to this room": "See <b>%(eventType)s</b> events posted to this room",
597+
"Send <b>%(eventType)s</b> events as you in your active room": "Send <b>%(eventType)s</b> events as you in your active room",
598+
"See <b>%(eventType)s</b> events posted to your active room": "See <b>%(eventType)s</b> events posted to your active room",
599+
"The <b>%(capability)s</b> capability": "The <b>%(capability)s</b> capability",
600+
"Send messages as you in this room": "Send messages as you in this room",
601+
"Send messages as you in your active room": "Send messages as you in your active room",
602+
"See messages posted to this room": "See messages posted to this room",
603+
"See messages posted to your active room": "See messages posted to your active room",
604+
"Send text messages as you in this room": "Send text messages as you in this room",
605+
"Send text messages as you in your active room": "Send text messages as you in your active room",
606+
"See text messages posted to this room": "See text messages posted to this room",
607+
"See text messages posted to your active room": "See text messages posted to your active room",
608+
"Send emotes as you in this room": "Send emotes as you in this room",
609+
"Send emotes as you in your active room": "Send emotes as you in your active room",
610+
"See emotes posted to this room": "See emotes posted to this room",
611+
"See emotes posted to your active room": "See emotes posted to your active room",
612+
"Send images as you in this room": "Send images as you in this room",
613+
"Send images as you in your active room": "Send images as you in your active room",
614+
"See images posted to this room": "See images posted to this room",
615+
"See images posted to your active room": "See images posted to your active room",
616+
"Send videos as you in this room": "Send videos as you in this room",
617+
"Send videos as you in your active room": "Send videos as you in your active room",
618+
"See videos posted to this room": "See videos posted to this room",
619+
"See videos posted to your active room": "See videos posted to your active room",
620+
"Send general files as you in this room": "Send general files as you in this room",
621+
"Send general files as you in your active room": "Send general files as you in your active room",
622+
"See general files posted to this room": "See general files posted to this room",
623+
"See general files posted to your active room": "See general files posted to your active room",
624+
"Send <b>%(msgtype)s</b> messages as you in this room": "Send <b>%(msgtype)s</b> messages as you in this room",
625+
"Send <b>%(msgtype)s</b> messages as you in your active room": "Send <b>%(msgtype)s</b> messages as you in your active room",
626+
"See <b>%(msgtype)s</b> messages posted to this room": "See <b>%(msgtype)s</b> messages posted to this room",
627+
"See <b>%(msgtype)s</b> messages posted to your active room": "See <b>%(msgtype)s</b> messages posted to your active room",
572628
"Cannot reach homeserver": "Cannot reach homeserver",
573629
"Ensure you have a stable internet connection, or get in touch with the server admin": "Ensure you have a stable internet connection, or get in touch with the server admin",
574630
"Your %(brand)s is misconfigured": "Your %(brand)s is misconfigured",
@@ -2125,9 +2181,13 @@
21252181
"Upload Error": "Upload Error",
21262182
"Verify other session": "Verify other session",
21272183
"Verification Request": "Verification Request",
2184+
"Approve widget permissions": "Approve widget permissions",
2185+
"This widget would like to:": "This widget would like to:",
2186+
"Approve": "Approve",
2187+
"Decline All": "Decline All",
2188+
"Remember my selection for this widget": "Remember my selection for this widget",
21282189
"A widget would like to verify your identity": "A widget would like to verify your identity",
21292190
"A widget located at %(widgetUrl)s would like to verify your identity. By allowing this, the widget will be able to verify your user ID, but not perform actions as you.": "A widget located at %(widgetUrl)s would like to verify your identity. By allowing this, the widget will be able to verify your user ID, but not perform actions as you.",
2130-
"Remember my selection for this widget": "Remember my selection for this widget",
21312191
"Allow": "Allow",
21322192
"Deny": "Deny",
21332193
"Wrong file type": "Wrong file type",

0 commit comments

Comments
 (0)