Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions res/css/_common.scss
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,22 @@ input[type=text]:focus, input[type=password]:focus, textarea:focus {
color: $username-variant8-color;
}

@define-mixin mx_Tooltip_dark {
box-shadow: none;
background-color: $tooltip-timeline-bg-color;
color: $tooltip-timeline-fg-color;
border: none;
border-radius: 3px;
padding: 6px 8px;
}

// This is a workaround for our mixins not supporting child selectors
.mx_Tooltip_dark {
.mx_Tooltip_chevron::after {
border-right-color: $tooltip-timeline-bg-color;
}
}

@define-mixin mx_Settings_fullWidthField {
margin-right: 100px;
}
Expand Down
62 changes: 37 additions & 25 deletions res/css/views/rooms/_AppsDrawer.scss
Original file line number Diff line number Diff line change
Expand Up @@ -294,49 +294,61 @@ form.mx_Custom_Widget_Form div {

.mx_AppPermissionWarning {
text-align: center;
background-color: $primary-bg-color;
background-color: $widget-menu-bar-bg-color;
display: flex;
height: 100%;
flex-direction: column;
justify-content: center;
align-items: center;
font-size: 16px;
}

.mx_AppPermissionWarningImage {
margin: 10px 0;
.mx_AppPermissionWarning_row {
margin-bottom: 12px;
}

.mx_AppPermissionWarningImage img {
width: 100px;
.mx_AppPermissionWarning_smallText {
font-size: 12px;
}

.mx_AppPermissionWarningText {
max-width: 90%;
margin: 10px auto 10px auto;
color: $primary-fg-color;
.mx_AppPermissionWarning_bolder {
font-weight: 600;
}

.mx_AppPermissionWarningTextLabel {
font-weight: bold;
display: block;
.mx_AppPermissionWarning h4 {
margin: 0;
padding: 0;
}

.mx_AppPermissionWarningTextURL {
.mx_AppPermissionWarning_helpIcon {
margin-top: 1px;
margin-right: 2px;
width: 10px;
height: 10px;
display: inline-block;
max-width: 100%;
color: $accent-color;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}

.mx_AppPermissionButton {
border: none;
padding: 5px 20px;
border-radius: 5px;
background-color: $button-bg-color;
color: $button-fg-color;
cursor: pointer;
.mx_AppPermissionWarning_helpIcon::before {
display: inline-block;
background-color: $accent-color;
mask-repeat: no-repeat;
mask-size: 12px;
width: 12px;
height: 12px;
mask-position: center;
content: '';
vertical-align: middle;
mask-image: url('$(res)/img/feather-customised/help-circle.svg');
}

.mx_AppPermissionWarning_tooltip {
@mixin mx_Tooltip_dark;

ul {
list-style-position: inside;
padding-left: 2px;
margin-left: 0;
}
}

.mx_AppLoading {
Expand Down
152 changes: 98 additions & 54 deletions src/components/views/elements/AppPermission.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,79 +19,123 @@ limitations under the License.
import React from 'react';
import PropTypes from 'prop-types';
import url from 'url';
import sdk from '../../../index';
import { _t } from '../../../languageHandler';
import WidgetUtils from "../../../utils/WidgetUtils";
import MatrixClientPeg from "../../../MatrixClientPeg";

export default class AppPermission extends React.Component {
static propTypes = {
url: PropTypes.string.isRequired,
creatorUserId: PropTypes.string.isRequired,
roomId: PropTypes.string.isRequired,
onPermissionGranted: PropTypes.func.isRequired,
};

static defaultProps = {
onPermissionGranted: () => {},
};

constructor(props) {
super(props);

const curlBase = this.getCurlBase();
this.state = { curlBase: curlBase};
}
// The first step is to pick apart the widget so we can render information about it
const urlInfo = this.parseWidgetUrl();

// Return string representation of content URL without query parameters
getCurlBase() {
const wurl = url.parse(this.props.url);
let curl;
let curlString;
// The second step is to find the user's profile so we can show it on the prompt
const room = MatrixClientPeg.get().getRoom(this.props.roomId);
let roomMember;
if (room) roomMember = room.getMember(this.props.creatorUserId);

const searchParams = new URLSearchParams(wurl.search);
// Set all this into the initial state
this.state = {
...urlInfo,
roomMember,
};
}

if (WidgetUtils.isScalarUrl(wurl) && searchParams && searchParams.get('url')) {
curl = url.parse(searchParams.get('url'));
if (curl) {
curl.search = curl.query = "";
curlString = curl.format();
}
}
if (!curl && wurl) {
wurl.search = wurl.query = "";
curlString = wurl.format();
parseWidgetUrl() {
const widgetUrl = url.parse(this.props.url);
const params = new URLSearchParams(widgetUrl.search);

// HACK: We're relying on the query params when we should be relying on the widget's `data`.
// This is a workaround for Scalar.
if (WidgetUtils.isScalarUrl(widgetUrl) && params && params.get('url')) {
const unwrappedUrl = url.parse(params.get('url'));
return {
widgetDomain: unwrappedUrl.host || unwrappedUrl.hostname,
isWrapped: true,
};
} else {
return {
widgetDomain: widgetUrl.host || widgetUrl.hostname,
isWrapped: false,
};
}
return curlString;
}

render() {
let e2eWarningText;
if (this.props.isRoomEncrypted) {
e2eWarningText =
<span className='mx_AppPermissionWarningTextLabel'>{ _t('NOTE: Apps are not end-to-end encrypted') }</span>;
}
const cookieWarning =
<span className='mx_AppPermissionWarningTextLabel'>
{ _t('Warning: This widget might use cookies.') }
</span>;
const AccessibleButton = sdk.getComponent("views.elements.AccessibleButton");
const MemberAvatar = sdk.getComponent("views.avatars.MemberAvatar");
const BaseAvatar = sdk.getComponent("views.avatars.BaseAvatar");
const TextWithTooltip = sdk.getComponent("views.elements.TextWithTooltip");

const displayName = this.state.roomMember ? this.state.roomMember.name : this.props.creatorUserId;
const userId = displayName === this.props.creatorUserId ? null : this.props.creatorUserId;

const avatar = this.state.roomMember
? <MemberAvatar member={this.state.roomMember} width={38} height={38} />
: <BaseAvatar name={this.props.creatorUserId} width={38} height={38} />;

const warningTooltipText = (
<div>
{_t("Any of the following data may be shared:")}
<ul>
<li>{_t("Your display name")}</li>
<li>{_t("Your avatar URL")}</li>
<li>{_t("Your user ID")}</li>
<li>{_t("Your theme")}</li>
<li>{_t("Riot URL")}</li>
<li>{_t("Room ID")}</li>
<li>{_t("Widget ID")}</li>
</ul>
</div>
);
const warningTooltip = (
<TextWithTooltip tooltip={warningTooltipText} tooltipClass='mx_AppPermissionWarning_tooltip mx_Tooltip_dark'>
<span className='mx_AppPermissionWarning_helpIcon' />
</TextWithTooltip>
);

// Due to i18n limitations, we can't dedupe the code for variables in these two messages.
const warning = this.state.isWrapped
? _t("Using this widget may share data <helpIcon /> with %(widgetDomain)s & your Integration Manager.",
{widgetDomain: this.state.widgetDomain}, {helpIcon: () => warningTooltip})
: _t("Using this widget may share data <helpIcon /> with %(widgetDomain)s.",
{widgetDomain: this.state.widgetDomain}, {helpIcon: () => warningTooltip});

return (
<div className='mx_AppPermissionWarning'>
<div className='mx_AppPermissionWarningImage'>
<img src={require("../../../../res/img/feather-customised/warning-triangle.svg")} alt={_t('Warning!')} />
<div className='mx_AppPermissionWarning_row mx_AppPermissionWarning_bolder mx_AppPermissionWarning_smallText'>
{_t("Widget added by")}
</div>
<div className='mx_AppPermissionWarningText'>
<span className='mx_AppPermissionWarningTextLabel'>{_t('Do you want to load widget from URL:')}</span>
<span className='mx_AppPermissionWarningTextURL'
title={this.state.curlBase}
>{this.state.curlBase}</span>
{ e2eWarningText }
{ cookieWarning }
<div className='mx_AppPermissionWarning_row'>
{avatar}
<h4 className='mx_AppPermissionWarning_bolder'>{displayName}</h4>
<div className='mx_AppPermissionWarning_smallText'>{userId}</div>
</div>
<div className='mx_AppPermissionWarning_row mx_AppPermissionWarning_smallText'>
{warning}
</div>
<div className='mx_AppPermissionWarning_row mx_AppPermissionWarning_smallText'>
{_t("This widget may use cookies.")}
</div>
<div className='mx_AppPermissionWarning_row'>
<AccessibleButton kind='primary_sm' onClick={this.props.onPermissionGranted}>
{_t("Continue")}
</AccessibleButton>
</div>
<input
className='mx_AppPermissionButton'
type='button'
value={_t('Allow')}
onClick={this.props.onPermissionGranted}
/>
</div>
);
}
}

AppPermission.propTypes = {
isRoomEncrypted: PropTypes.bool,
url: PropTypes.string.isRequired,
onPermissionGranted: PropTypes.func.isRequired,
};
AppPermission.defaultProps = {
isRoomEncrypted: false,
onPermissionGranted: function() {},
};
4 changes: 2 additions & 2 deletions src/components/views/elements/AppTile.js
Original file line number Diff line number Diff line change
Expand Up @@ -569,11 +569,11 @@ export default class AppTile extends React.Component {
</div>
);
if (!this.state.hasPermissionToLoad) {
const isRoomEncrypted = MatrixClientPeg.get().isRoomEncrypted(this.props.room.roomId);
appTileBody = (
<div className={appTileBodyClass}>
<AppPermission
isRoomEncrypted={isRoomEncrypted}
roomId={this.props.room.roomId}
creatorUserId={this.props.creatorUserId}
url={this.state.widgetUrl}
onPermissionGranted={this._grantWidgetPermission}
/>
Expand Down
4 changes: 3 additions & 1 deletion src/components/views/elements/TextWithTooltip.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ import sdk from '../../../index';
export default class TextWithTooltip extends React.Component {
static propTypes = {
class: PropTypes.string,
tooltip: PropTypes.string.isRequired,
tooltipClass: PropTypes.string,
tooltip: PropTypes.node.isRequired,
};

constructor() {
Expand Down Expand Up @@ -49,6 +50,7 @@ export default class TextWithTooltip extends React.Component {
<Tooltip
label={this.props.tooltip}
visible={this.state.hover}
tooltipClassName={this.props.tooltipClass}
className={"mx_TextWithTooltip_tooltip"} />
</span>
);
Expand Down
17 changes: 13 additions & 4 deletions src/i18n/strings/en_EN.json
Original file line number Diff line number Diff line change
Expand Up @@ -1183,10 +1183,18 @@
"Quick Reactions": "Quick Reactions",
"Cancel search": "Cancel search",
"Unknown Address": "Unknown Address",
"NOTE: Apps are not end-to-end encrypted": "NOTE: Apps are not end-to-end encrypted",
"Warning: This widget might use cookies.": "Warning: This widget might use cookies.",
"Do you want to load widget from URL:": "Do you want to load widget from URL:",
"Allow": "Allow",
"Any of the following data may be shared:": "Any of the following data may be shared:",
"Your display name": "Your display name",
"Your avatar URL": "Your avatar URL",
"Your user ID": "Your user ID",
"Your theme": "Your theme",
"Riot URL": "Riot URL",
"Room ID": "Room ID",
"Widget ID": "Widget ID",
"Using this widget may share data <helpIcon /> with %(widgetDomain)s & your Integration Manager.": "Using this widget may share data <helpIcon /> with %(widgetDomain)s & your Integration Manager.",
"Using this widget may share data <helpIcon /> with %(widgetDomain)s.": "Using this widget may share data <helpIcon /> with %(widgetDomain)s.",
"Widget added by": "Widget added by",
"This widget may use cookies.": "This widget may use cookies.",
"Delete Widget": "Delete Widget",
"Deleting a widget removes it for all users in this room. Are you sure you want to delete this widget?": "Deleting a widget removes it for all users in this room. Are you sure you want to delete this widget?",
"Delete widget": "Delete widget",
Expand Down Expand Up @@ -1494,6 +1502,7 @@
"A widget would like to verify your identity": "A widget would like to verify your identity",
"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.",
"Remember my selection for this widget": "Remember my selection for this widget",
"Allow": "Allow",
"Deny": "Deny",
"Unable to load backup status": "Unable to load backup status",
"Recovery Key Mismatch": "Recovery Key Mismatch",
Expand Down