Merge pull request #2557 from matrix-org/bwindels/e2eicons
Add e2e icon to room header/composer/member info, more ...
This commit is contained in:
commit
45e982ac13
23 changed files with 251 additions and 94 deletions
|
@ -107,6 +107,7 @@
|
|||
@import "./views/rooms/_AppsDrawer.scss";
|
||||
@import "./views/rooms/_Autocomplete.scss";
|
||||
@import "./views/rooms/_AuxPanel.scss";
|
||||
@import "./views/rooms/_E2EIcon.scss";
|
||||
@import "./views/rooms/_EntityTile.scss";
|
||||
@import "./views/rooms/_EventTile.scss";
|
||||
@import "./views/rooms/_JumpToBottomButton.scss";
|
||||
|
|
|
@ -121,7 +121,7 @@ limitations under the License.
|
|||
|
||||
.mx_RoomStatusBar_connectionLostBar img {
|
||||
padding-left: 10px;
|
||||
padding-right: 22px;
|
||||
padding-right: 10px;
|
||||
vertical-align: middle;
|
||||
float: left;
|
||||
}
|
||||
|
|
33
res/css/views/rooms/_E2EIcon.scss
Normal file
33
res/css/views/rooms/_E2EIcon.scss
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
.mx_E2EIcon {
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
mask-repeat: no-repeat;
|
||||
mask-position: center 0;
|
||||
margin: 0 9px;
|
||||
}
|
||||
|
||||
.mx_E2EIcon_verified {
|
||||
mask-image: url('$(res)/img/feather-icons/e2e/lock-verified.svg');
|
||||
background-color: $accent-color;
|
||||
}
|
||||
|
||||
.mx_E2EIcon_warning {
|
||||
mask-image: url('$(res)/img/feather-icons/e2e/lock-warning.svg');
|
||||
background-color: $warning-color;
|
||||
}
|
|
@ -281,9 +281,24 @@ limitations under the License.
|
|||
.mx_EventTile_e2eIcon {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 9px;
|
||||
top: 8px;
|
||||
left: 46px;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
cursor: pointer;
|
||||
mask-size: 14px;
|
||||
mask-repeat: no-repeat;
|
||||
mask-position: 0;
|
||||
}
|
||||
|
||||
.mx_EventTile_e2eIcon_undecryptable, .mx_EventTile_e2eIcon_unverified {
|
||||
mask-image: url('$(res)/img/feather-icons/e2e/warning.svg');
|
||||
background-color: $warning-color;
|
||||
}
|
||||
|
||||
.mx_EventTile_e2eIcon_unencrypted {
|
||||
mask-image: url('$(res)/img/feather-icons/e2e/warning.svg');
|
||||
background-color: $composer-e2e-icon-color;
|
||||
}
|
||||
|
||||
.mx_EventTile_e2eIcon_hidden {
|
||||
|
|
|
@ -20,6 +20,25 @@ limitations under the License.
|
|||
align-items: start;
|
||||
}
|
||||
|
||||
.mx_MemberDeviceInfo_icon {
|
||||
margin-top: 4px;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
mask-repeat: no-repeat;
|
||||
}
|
||||
.mx_MemberDeviceInfo_icon_blacklisted {
|
||||
mask-image: url('$(res)/img/feather-icons/e2e/blacklisted.svg');
|
||||
background-color: $warning-color;
|
||||
}
|
||||
.mx_MemberDeviceInfo_icon_verified {
|
||||
mask-image: url('$(res)/img/feather-icons/e2e/verified.svg');
|
||||
background-color: $accent-color;
|
||||
}
|
||||
.mx_MemberDeviceInfo_icon_unverified {
|
||||
mask-image: url('$(res)/img/feather-icons/e2e/warning.svg');
|
||||
background-color: $warning-color;
|
||||
}
|
||||
|
||||
.mx_MemberDeviceInfo > .mx_DeviceVerifyButtons {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
|
|
@ -26,6 +26,10 @@ limitations under the License.
|
|||
display: flex;
|
||||
}
|
||||
|
||||
.mx_MemberInfo_name > .mx_E2EIcon {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.mx_MemberInfo_cancel {
|
||||
height: 16px;
|
||||
padding: 10px 15px;
|
||||
|
|
|
@ -23,6 +23,10 @@ limitations under the License.
|
|||
padding-left: 84px;
|
||||
}
|
||||
|
||||
.mx_MessageComposer_wrapper.mx_MessageComposer_hasE2EIcon {
|
||||
padding-left: 109px;
|
||||
}
|
||||
|
||||
.mx_MessageComposer_replaced_wrapper {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
|
@ -71,9 +75,10 @@ limitations under the License.
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
.mx_MessageComposer_e2eIcon {
|
||||
.mx_MessageComposer_e2eIcon.mx_E2EIcon {
|
||||
position: absolute;
|
||||
left: 60px;
|
||||
background-color: $composer-e2e-icon-color;
|
||||
}
|
||||
|
||||
.mx_MessageComposer_noperm_error {
|
||||
|
|
6
res/img/feather-icons/e2e/blacklisted.svg
Normal file
6
res/img/feather-icons/e2e/blacklisted.svg
Normal file
|
@ -0,0 +1,6 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="12" height="12" viewBox="0 0 12 12">
|
||||
<defs>
|
||||
<path id="a" d="M5 10A5 5 0 1 0 5 0a5 5 0 0 0 0 10zM2.5 3.5h5a1.5 1.5 0 0 1 0 3h-5a1.5 1.5 0 0 1 0-3z"/>
|
||||
</defs>
|
||||
<use fill="#F56679" fill-rule="evenodd" stroke="#F56679" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.2" transform="translate(1 1)" xlink:href="#a"/>
|
||||
</svg>
|
After Width: | Height: | Size: 442 B |
6
res/img/feather-icons/e2e/lock-verified.svg
Normal file
6
res/img/feather-icons/e2e/lock-verified.svg
Normal file
|
@ -0,0 +1,6 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="25" viewBox="0 0 22 25">
|
||||
<g fill="none" fill-rule="evenodd" stroke="#7AC9A1" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke-width="2" d="M8.23 21.01l-5.233-.007a1.995 1.995 0 0 1-1.997-2V11a2 2 0 0 1 2-2h14c1.259 0 2 .939 2 1M5 9V6a5 5 0 1 1 10 0v3"/>
|
||||
<path fill="#7AC9A1" d="M15.5 24s5.5-2.4 5.5-6v-4.2L15.5 12 10 13.8V18c0 3.6 5.5 6 5.5 6z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 454 B |
9
res/img/feather-icons/e2e/lock-warning.svg
Normal file
9
res/img/feather-icons/e2e/lock-warning.svg
Normal file
|
@ -0,0 +1,9 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="23" height="25" viewBox="0 0 23 25">
|
||||
<defs>
|
||||
<path id="a" d="M15 23a6 6 0 1 0 0-12 6 6 0 0 0 0 12zm0-10.5a1.5 1.5 0 0 1 1.5 1.5v3a1.5 1.5 0 0 1-3 0v-3a1.5 1.5 0 0 1 1.5-1.5zm0 9a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z"/>
|
||||
</defs>
|
||||
<g fill="none" fill-rule="evenodd" stroke="#F56679" stroke-linecap="round" stroke-linejoin="round" transform="translate(1 1)">
|
||||
<path stroke-width="2" d="M7.23 20.01l-5.233-.007a2 2 0 0 1-1.997-2V10a2 2 0 0 1 2-2h14c1.259 0 2 .939 2 1M4 8V5a5 5 0 1 1 10 0v3"/>
|
||||
<use fill="#F56679" xlink:href="#a"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 665 B |
3
res/img/feather-icons/e2e/verified.svg
Normal file
3
res/img/feather-icons/e2e/verified.svg
Normal file
|
@ -0,0 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="11" height="12" viewBox="0 0 11 12">
|
||||
<path fill="#7AC9A1" fill-rule="evenodd" stroke="#7AC9A1" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.2" d="M5.5 11S10 9 10 6V2.5L5.5 1 1 2.5V6c0 3 4.5 5 4.5 5z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 278 B |
6
res/img/feather-icons/e2e/warning.svg
Normal file
6
res/img/feather-icons/e2e/warning.svg
Normal file
|
@ -0,0 +1,6 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="12" height="12" viewBox="0 0 12 12">
|
||||
<defs>
|
||||
<path id="a" d="M5 10A5 5 0 1 0 5 0a5 5 0 0 0 0 10zM5 .5A1.5 1.5 0 0 1 6.5 2v3a1.5 1.5 0 0 1-3 0V2A1.5 1.5 0 0 1 5 .5zm0 9a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z"/>
|
||||
</defs>
|
||||
<use fill="#F56679" fill-rule="evenodd" stroke="#F56679" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.2" transform="translate(1 1)" xlink:href="#a"/>
|
||||
</svg>
|
After Width: | Height: | Size: 500 B |
|
@ -142,6 +142,8 @@ $roomheader-addroom-color: #91A1C0;
|
|||
$roomtopic-color: #9fa9ba;
|
||||
$eventtile-meta-color: $roomtopic-color;
|
||||
|
||||
$composer-e2e-icon-color: #c9ced6;
|
||||
|
||||
// ********************
|
||||
|
||||
$roomtile-name-color: #61708b;
|
||||
|
|
|
@ -134,6 +134,9 @@ $roomheader-color: $primary-fg-color;
|
|||
$roomheader-addroom-color: $primary-bg-color;
|
||||
$roomtopic-color: $settings-grey-fg-color;
|
||||
$eventtile-meta-color: $roomtopic-color;
|
||||
|
||||
$composer-e2e-icon-color: #c9ced6;
|
||||
|
||||
// ********************
|
||||
|
||||
$roomtile-name-color: rgba(69, 69, 69, 0.8);
|
||||
|
|
|
@ -290,7 +290,7 @@ module.exports = React.createClass({
|
|||
}
|
||||
|
||||
return <div className="mx_RoomStatusBar_connectionLostBar">
|
||||
<img src={require("../../../res/img/warning.svg")} width="24" height="23" title={_t("Warning")} alt="" />
|
||||
<img src={require("../../../res/img/feather-icons/e2e/warning.svg")} width="24" height="24" title={_t("Warning")} alt="" />
|
||||
<div>
|
||||
<div className="mx_RoomStatusBar_connectionLostBar_title">
|
||||
{ title }
|
||||
|
@ -309,7 +309,7 @@ module.exports = React.createClass({
|
|||
if (this._shouldShowConnectionError()) {
|
||||
return (
|
||||
<div className="mx_RoomStatusBar_connectionLostBar">
|
||||
<img src={require("../../../res/img/warning.svg")} width="24" height="23" title="/!\ " alt="/!\ " />
|
||||
<img src={require("../../../res/img/feather-icons/e2e/warning.svg")} width="24" height="24" title="/!\ " alt="/!\ " />
|
||||
<div>
|
||||
<div className="mx_RoomStatusBar_connectionLostBar_title">
|
||||
{ _t('Connectivity to the server has been lost.') }
|
||||
|
|
|
@ -168,6 +168,7 @@ module.exports = React.createClass({
|
|||
MatrixClientPeg.get().on("Room.myMembership", this.onMyMembership);
|
||||
MatrixClientPeg.get().on("accountData", this.onAccountData);
|
||||
MatrixClientPeg.get().on("crypto.keyBackupStatus", this.onKeyBackupStatus);
|
||||
MatrixClientPeg.get().on("deviceVerificationChanged", this.onDeviceVerificationChanged);
|
||||
this._fetchMediaConfig();
|
||||
// Start listening for RoomViewStore updates
|
||||
this._roomStoreToken = RoomViewStore.addListener(this._onRoomViewStoreUpdate);
|
||||
|
@ -457,6 +458,7 @@ module.exports = React.createClass({
|
|||
MatrixClientPeg.get().removeListener("RoomState.members", this.onRoomStateMember);
|
||||
MatrixClientPeg.get().removeListener("accountData", this.onAccountData);
|
||||
MatrixClientPeg.get().removeListener("crypto.keyBackupStatus", this.onKeyBackupStatus);
|
||||
MatrixClientPeg.get().removeListener("deviceVerificationChanged", this.onDeviceVerificationChanged);
|
||||
}
|
||||
|
||||
window.removeEventListener('beforeunload', this.onPageUnload);
|
||||
|
@ -589,6 +591,10 @@ module.exports = React.createClass({
|
|||
this._updatePreviewUrlVisibility(room);
|
||||
}
|
||||
|
||||
if (ev.getType() === "m.room.encryption") {
|
||||
this._updateE2EStatus(room);
|
||||
}
|
||||
|
||||
// ignore anything but real-time updates at the end of the room:
|
||||
// updates from pagination will happen when the paginate completes.
|
||||
if (toStartOfTimeline || !data || !data.liveEvent) return;
|
||||
|
@ -642,6 +648,7 @@ module.exports = React.createClass({
|
|||
this._updatePreviewUrlVisibility(room);
|
||||
this._loadMembersIfJoined(room);
|
||||
this._calculateRecommendedVersion(room);
|
||||
this._updateE2EStatus(room);
|
||||
},
|
||||
|
||||
_calculateRecommendedVersion: async function(room) {
|
||||
|
@ -733,6 +740,23 @@ module.exports = React.createClass({
|
|||
});
|
||||
},
|
||||
|
||||
onDeviceVerificationChanged: function(userId, device) {
|
||||
const room = this.state.room;
|
||||
if (!room.currentState.getMember(userId)) {
|
||||
return;
|
||||
}
|
||||
this._updateE2EStatus(room);
|
||||
},
|
||||
|
||||
_updateE2EStatus: function(room) {
|
||||
if (!MatrixClientPeg.get().isRoomEncrypted(room.roomId)) {
|
||||
return;
|
||||
}
|
||||
room.hasUnverifiedDevices().then((hasUnverifiedDevices) => {
|
||||
this.setState({e2eStatus: hasUnverifiedDevices ? "warning" : "verified"});
|
||||
});
|
||||
},
|
||||
|
||||
updateTint: function() {
|
||||
const room = this.state.room;
|
||||
if (!room) return;
|
||||
|
@ -1575,6 +1599,7 @@ module.exports = React.createClass({
|
|||
room={this.state.room}
|
||||
oobData={this.props.oobData}
|
||||
collapsedRhs={this.props.collapsedRhs}
|
||||
e2eStatus={this.state.e2eStatus}
|
||||
/>
|
||||
<div className="mx_RoomView_body">
|
||||
<div className="mx_RoomView_auxPanel">
|
||||
|
@ -1622,6 +1647,7 @@ module.exports = React.createClass({
|
|||
ref="header"
|
||||
room={this.state.room}
|
||||
collapsedRhs={this.props.collapsedRhs}
|
||||
e2eStatus={this.state.e2eStatus}
|
||||
/>
|
||||
<div className="mx_RoomView_body">
|
||||
<div className="mx_RoomView_auxPanel">
|
||||
|
@ -1767,6 +1793,7 @@ module.exports = React.createClass({
|
|||
disabled={this.props.disabled}
|
||||
showApps={this.state.showApps}
|
||||
uploadAllowed={this.isFileUploadAllowed}
|
||||
e2eStatus={this.state.e2eStatus}
|
||||
/>;
|
||||
}
|
||||
|
||||
|
@ -1917,6 +1944,7 @@ module.exports = React.createClass({
|
|||
onCancelClick={(aux && !hideCancel) ? this.onCancelClick : null}
|
||||
onForgetClick={(myMembership === "leave") ? this.onForgetClick : null}
|
||||
onLeaveClick={(myMembership === "join") ? this.onLeaveClick : null}
|
||||
e2eStatus={this.state.e2eStatus}
|
||||
/>
|
||||
<MainSplit panel={rightPanel} collapsedRhs={this.props.collapsedRhs}>
|
||||
<div className={fadableSectionClasses}>
|
||||
|
|
39
src/components/views/rooms/E2EIcon.js
Normal file
39
src/components/views/rooms/E2EIcon.js
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { _t } from '../../../languageHandler';
|
||||
|
||||
export default function(props) {
|
||||
const isWarning = props.status === "warning";
|
||||
const isVerified = props.status === "verified";
|
||||
const e2eIconClasses = classNames({
|
||||
mx_E2EIcon: true,
|
||||
mx_E2EIcon_warning: isWarning,
|
||||
mx_E2EIcon_verified: isVerified,
|
||||
}, props.className);
|
||||
let e2eTitle;
|
||||
if (isWarning) {
|
||||
e2eTitle = props.isUser ?
|
||||
_t("Some devices for this user are not trusted") :
|
||||
_t("Some devices in this encrypted room are not trusted");
|
||||
} else if (isVerified) {
|
||||
e2eTitle = props.isUser ?
|
||||
_t("All devices for this user are trusted") :
|
||||
_t("All devices in this encrypted room are trusted");
|
||||
}
|
||||
return (<div className={e2eIconClasses} title={e2eTitle} />);
|
||||
}
|
|
@ -459,17 +459,21 @@ module.exports = withMatrixClient(React.createClass({
|
|||
|
||||
// event is encrypted, display padlock corresponding to whether or not it is verified
|
||||
if (ev.isEncrypted()) {
|
||||
return this.state.verified ? <E2ePadlockVerified {...props} /> : <E2ePadlockUnverified {...props} />;
|
||||
if (this.state.verified) {
|
||||
return; // no icon for verified
|
||||
} else {
|
||||
return (<E2ePadlockUnverified {...props} />);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.props.matrixClient.isRoomEncrypted(ev.getRoomId())) {
|
||||
// else if room is encrypted
|
||||
// and event is being encrypted or is not_sent (Unknown Devices/Network Error)
|
||||
if (ev.status === EventStatus.ENCRYPTING) {
|
||||
return <E2ePadlockEncrypting {...props} />;
|
||||
return;
|
||||
}
|
||||
if (ev.status === EventStatus.NOT_SENT) {
|
||||
return <E2ePadlockNotSent {...props} />;
|
||||
return;
|
||||
}
|
||||
// if the event is not encrypted, but it's an e2e room, show the open padlock
|
||||
return <E2ePadlockUnencrypted {...props} />;
|
||||
|
@ -767,57 +771,29 @@ module.exports.haveTileForEvent = function(e) {
|
|||
|
||||
function E2ePadlockUndecryptable(props) {
|
||||
return (
|
||||
<E2ePadlock alt={_t("Undecryptable")}
|
||||
src={require("../../../../res/img/e2e-blocked.svg")} width="12" height="12"
|
||||
style={{ marginLeft: "-1px" }} {...props} />
|
||||
);
|
||||
}
|
||||
|
||||
function E2ePadlockEncrypting(props) {
|
||||
return (
|
||||
<E2ePadlock alt={_t("Encrypting")}
|
||||
src={require("../../../../res/img/e2e-encrypting.svg")} width="10" height="12"
|
||||
{...props} />
|
||||
);
|
||||
}
|
||||
|
||||
function E2ePadlockNotSent(props) {
|
||||
return (
|
||||
<E2ePadlock alt={_t("Encrypted, not sent")}
|
||||
src={require("../../../../res/img/e2e-not_sent.svg")} width="10" height="12"
|
||||
{...props} />
|
||||
);
|
||||
}
|
||||
|
||||
function E2ePadlockVerified(props) {
|
||||
return (
|
||||
<E2ePadlock alt={_t("Encrypted by a verified device")}
|
||||
src={require("../../../../res/img/e2e-verified.svg")} width="10" height="12"
|
||||
{...props} />
|
||||
<E2ePadlock title={_t("Undecryptable")} icon="undecryptable" />
|
||||
);
|
||||
}
|
||||
|
||||
function E2ePadlockUnverified(props) {
|
||||
return (
|
||||
<E2ePadlock alt={_t("Encrypted by an unverified device")}
|
||||
src={require("../../../../res/img/e2e-warning.svg")} width="15" height="12"
|
||||
style={{ marginLeft: "-2px" }} {...props} />
|
||||
<E2ePadlock title={_t("Encrypted by an unverified device")} icon="unverified" />
|
||||
);
|
||||
}
|
||||
|
||||
function E2ePadlockUnencrypted(props) {
|
||||
return (
|
||||
<E2ePadlock alt={_t("Unencrypted message")}
|
||||
src={require("../../../../res/img/e2e-unencrypted.svg")} width="12" height="12"
|
||||
{...props} />
|
||||
<E2ePadlock title={_t("Unencrypted message")} icon="unencrypted" />
|
||||
);
|
||||
}
|
||||
|
||||
function E2ePadlock(props) {
|
||||
if (SettingsStore.getValue("alwaysShowEncryptionIcons")) {
|
||||
return <img className="mx_EventTile_e2eIcon" {...props} />;
|
||||
return <div
|
||||
className={`mx_EventTile_e2eIcon mx_EventTile_e2eIcon_${props.icon}`}
|
||||
title={props.title} onClick={props.onClick} />;
|
||||
} else {
|
||||
return <img className="mx_EventTile_e2eIcon mx_EventTile_e2eIcon_hidden" {...props} />;
|
||||
return <div className="mx_EventTile_e2eIcon mx_EventTile_e2eIcon_hidden" onClick={props.onClick} />;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,32 +18,18 @@ import React from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import sdk from '../../../index';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import classNames from 'classnames';
|
||||
|
||||
export default class MemberDeviceInfo extends React.Component {
|
||||
render() {
|
||||
let indicator = null;
|
||||
const DeviceVerifyButtons = sdk.getComponent('elements.DeviceVerifyButtons');
|
||||
|
||||
if (this.props.device.isBlocked()) {
|
||||
indicator = (
|
||||
<div className="mx_MemberDeviceInfo_blacklisted">
|
||||
<img src={require("../../../../res/img/e2e-blocked.svg")} width="12" height="12" style={{ marginLeft: "-1px" }} alt={_t("Blacklisted")} />
|
||||
</div>
|
||||
);
|
||||
} else if (this.props.device.isVerified()) {
|
||||
indicator = (
|
||||
<div className="mx_MemberDeviceInfo_verified">
|
||||
<img src={require("../../../../res/img/e2e-verified.svg")} width="10" height="12" alt={_t("Verified")} />
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
indicator = (
|
||||
<div className="mx_MemberDeviceInfo_unverified">
|
||||
<img src={require("../../../../res/img/e2e-warning.svg")} width="15" height="12" style={{ marginLeft: "-2px" }} alt={_t("Unverified")} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const iconClasses = classNames({
|
||||
mx_MemberDeviceInfo_icon: true,
|
||||
mx_MemberDeviceInfo_icon_blacklisted: this.props.device.isBlocked(),
|
||||
mx_MemberDeviceInfo_icon_verified: this.props.device.isVerified(),
|
||||
mx_MemberDeviceInfo_icon_unverified: this.props.device.isUnverified(),
|
||||
});
|
||||
const indicator = (<div className={iconClasses} />);
|
||||
const deviceName = this.props.device.ambiguous ?
|
||||
(this.props.device.getDisplayName() ? this.props.device.getDisplayName() : "") + " (" + this.props.device.deviceId + ")" :
|
||||
this.props.device.getDisplayName();
|
||||
|
@ -52,10 +38,10 @@ export default class MemberDeviceInfo extends React.Component {
|
|||
return (
|
||||
<div className="mx_MemberDeviceInfo"
|
||||
title={_t("device id: ") + this.props.device.deviceId} >
|
||||
{ indicator }
|
||||
<div className="mx_MemberDeviceInfo_deviceInfo">
|
||||
<div className="mx_MemberDeviceInfo_deviceId">
|
||||
{ deviceName }
|
||||
{ indicator }
|
||||
</div>
|
||||
</div>
|
||||
<DeviceVerifyButtons userId={this.props.userId} device={this.props.device} />
|
||||
|
|
|
@ -43,6 +43,7 @@ import RoomViewStore from '../../../stores/RoomViewStore';
|
|||
import SdkConfig from '../../../SdkConfig';
|
||||
import MultiInviter from "../../../utils/MultiInviter";
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import E2EIcon from "./E2EIcon";
|
||||
|
||||
module.exports = withMatrixClient(React.createClass({
|
||||
displayName: 'MemberInfo',
|
||||
|
@ -153,11 +154,19 @@ module.exports = withMatrixClient(React.createClass({
|
|||
// Promise.resolve to handle transition from static result to promise; can be removed
|
||||
// in future
|
||||
Promise.resolve(this.props.matrixClient.getStoredDevicesForUser(userId)).then((devices) => {
|
||||
this.setState({devices: devices});
|
||||
this.setState({
|
||||
devices: devices,
|
||||
e2eStatus: this._getE2EStatus(devices),
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_getE2EStatus: function(devices) {
|
||||
const hasUnverifiedDevice = devices.some((device) => device.isUnverified());
|
||||
return hasUnverifiedDevice ? "warning" : "verified";
|
||||
},
|
||||
|
||||
onRoom: function(room) {
|
||||
this.forceUpdate();
|
||||
},
|
||||
|
@ -234,8 +243,13 @@ module.exports = withMatrixClient(React.createClass({
|
|||
// we got cancelled - presumably a different user now
|
||||
return;
|
||||
}
|
||||
|
||||
self._disambiguateDevices(devices);
|
||||
self.setState({devicesLoading: false, devices: devices});
|
||||
self.setState({
|
||||
devicesLoading: false,
|
||||
devices: devices,
|
||||
e2eStatus: self._getE2EStatus(devices),
|
||||
});
|
||||
}, function(err) {
|
||||
console.log("Error downloading devices", err);
|
||||
self.setState({devicesLoading: false});
|
||||
|
@ -965,6 +979,7 @@ module.exports = withMatrixClient(React.createClass({
|
|||
<AccessibleButton className="mx_MemberInfo_cancel" onClick={this.onCancel}>
|
||||
<img src={require("../../../../res/img/minimise.svg")} width="10" height="16" className="mx_filterFlipColor" alt={_t('Close')} />
|
||||
</AccessibleButton>
|
||||
{ this.state.e2eStatus ? <E2EIcon status={this.state.e2eStatus} isUser={true} /> : undefined }
|
||||
<EmojiText element="h2">{ memberName }</EmojiText>
|
||||
</div>
|
||||
{ avatarElement }
|
||||
|
|
|
@ -26,6 +26,9 @@ import RoomViewStore from '../../../stores/RoomViewStore';
|
|||
import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore";
|
||||
import Stickerpicker from './Stickerpicker';
|
||||
import { makeRoomPermalink } from '../../../matrix-to';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import E2EIcon from './E2EIcon';
|
||||
|
||||
const formatButtonList = [
|
||||
_td("bold"),
|
||||
|
@ -316,25 +319,14 @@ export default class MessageComposer extends React.Component {
|
|||
);
|
||||
}
|
||||
|
||||
let e2eImg; let e2eTitle; let e2eClass;
|
||||
const roomIsEncrypted = MatrixClientPeg.get().isRoomEncrypted(this.props.room.roomId);
|
||||
if (roomIsEncrypted) {
|
||||
// FIXME: show a /!\ if there are untrusted devices in the room...
|
||||
e2eImg = require("../../../../res/img/e2e-verified.svg");
|
||||
e2eTitle = _t('Encrypted room');
|
||||
e2eClass = 'mx_MessageComposer_e2eIcon';
|
||||
} else {
|
||||
e2eImg = require("../../../../res/img/e2e-unencrypted.svg");
|
||||
e2eTitle = _t('Unencrypted room');
|
||||
e2eClass = 'mx_MessageComposer_e2eIcon mx_filterFlipColor';
|
||||
if (this.props.e2eStatus) {
|
||||
controls.push(<E2EIcon
|
||||
status={this.props.e2eStatus}
|
||||
key="e2eIcon"
|
||||
className="mx_MessageComposer_e2eIcon" />
|
||||
);
|
||||
}
|
||||
|
||||
controls.push(
|
||||
<img key="e2eIcon" className={e2eClass} src={e2eImg} width="12" height="12"
|
||||
alt={e2eTitle} title={e2eTitle}
|
||||
/>,
|
||||
);
|
||||
|
||||
let callButton;
|
||||
let videoCallButton;
|
||||
let hangupButton;
|
||||
|
@ -413,6 +405,7 @@ export default class MessageComposer extends React.Component {
|
|||
key="controls_formatting" />
|
||||
) : null;
|
||||
|
||||
const roomIsEncrypted = MatrixClientPeg.get().isRoomEncrypted(this.props.room.roomId);
|
||||
let placeholderText;
|
||||
if (this.state.isQuoting) {
|
||||
if (roomIsEncrypted) {
|
||||
|
@ -509,9 +502,13 @@ export default class MessageComposer extends React.Component {
|
|||
</div>;
|
||||
}
|
||||
|
||||
const wrapperClasses = classNames({
|
||||
mx_MessageComposer_wrapper: true,
|
||||
mx_MessageComposer_hasE2EIcon: !!this.props.e2eStatus,
|
||||
});
|
||||
return (
|
||||
<div className="mx_MessageComposer">
|
||||
<div className="mx_MessageComposer_wrapper">
|
||||
<div className={wrapperClasses}>
|
||||
<div className="mx_MessageComposer_row">
|
||||
{ controls }
|
||||
</div>
|
||||
|
|
|
@ -33,6 +33,7 @@ import ManageIntegsButton from '../elements/ManageIntegsButton';
|
|||
import {CancelButton} from './SimpleRoomHeader';
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import RoomHeaderButtons from '../right_panel/RoomHeaderButtons';
|
||||
import E2EIcon from './E2EIcon';
|
||||
|
||||
linkifyMatrix(linkify);
|
||||
|
||||
|
@ -52,6 +53,7 @@ module.exports = React.createClass({
|
|||
onSearchClick: PropTypes.func,
|
||||
onLeaveClick: PropTypes.func,
|
||||
onCancelClick: PropTypes.func,
|
||||
e2eStatus: PropTypes.string,
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
|
@ -237,6 +239,10 @@ module.exports = React.createClass({
|
|||
);
|
||||
}
|
||||
|
||||
const e2eIcon = this.props.e2eStatus ?
|
||||
<E2EIcon status={this.props.e2eStatus} /> :
|
||||
undefined;
|
||||
|
||||
if (this.props.onCancelClick) {
|
||||
cancelButton = <CancelButton onClick={this.props.onCancelClick} />;
|
||||
}
|
||||
|
@ -413,6 +419,7 @@ module.exports = React.createClass({
|
|||
<div className={"mx_RoomHeader light-panel " + (this.props.editing ? "mx_RoomHeader_editing" : "")}>
|
||||
<div className="mx_RoomHeader_wrapper">
|
||||
<div className="mx_RoomHeader_avatar">{ roomAvatar }</div>
|
||||
{ e2eIcon }
|
||||
{ name }
|
||||
{ topicElement }
|
||||
{ spinner }
|
||||
|
|
|
@ -571,6 +571,10 @@
|
|||
" (unsupported)": " (unsupported)",
|
||||
"Join as <voiceText>voice</voiceText> or <videoText>video</videoText>.": "Join as <voiceText>voice</voiceText> or <videoText>video</videoText>.",
|
||||
"Ongoing conference call%(supportedText)s.": "Ongoing conference call%(supportedText)s.",
|
||||
"Some devices for this user are not trusted": "Some devices for this user are not trusted",
|
||||
"Some devices in this encrypted room are not trusted": "Some devices in this encrypted room are not trusted",
|
||||
"All devices for this user are trusted": "All devices for this user are trusted",
|
||||
"All devices in this encrypted room are trusted": "All devices in this encrypted room are trusted",
|
||||
"This event could not be displayed": "This event could not be displayed",
|
||||
"%(senderName)s sent an image": "%(senderName)s sent an image",
|
||||
"%(senderName)s sent a video": "%(senderName)s sent a video",
|
||||
|
@ -582,16 +586,10 @@
|
|||
"Key request sent.": "Key request sent.",
|
||||
"<requestLink>Re-request encryption keys</requestLink> from your other devices.": "<requestLink>Re-request encryption keys</requestLink> from your other devices.",
|
||||
"Undecryptable": "Undecryptable",
|
||||
"Encrypting": "Encrypting",
|
||||
"Encrypted, not sent": "Encrypted, not sent",
|
||||
"Encrypted by a verified device": "Encrypted by a verified device",
|
||||
"Encrypted by an unverified device": "Encrypted by an unverified device",
|
||||
"Unencrypted message": "Unencrypted message",
|
||||
"Please select the destination room for this message": "Please select the destination room for this message",
|
||||
"Scroll to bottom of page": "Scroll to bottom of page",
|
||||
"Blacklisted": "Blacklisted",
|
||||
"Verified": "Verified",
|
||||
"Unverified": "Unverified",
|
||||
"device id: ": "device id: ",
|
||||
"Disinvite": "Disinvite",
|
||||
"Kick": "Kick",
|
||||
|
@ -643,8 +641,6 @@
|
|||
"Are you sure you want to upload the following files?": "Are you sure you want to upload the following files?",
|
||||
"The following files cannot be uploaded:": "The following files cannot be uploaded:",
|
||||
"Upload Files": "Upload Files",
|
||||
"Encrypted room": "Encrypted room",
|
||||
"Unencrypted room": "Unencrypted room",
|
||||
"Hangup": "Hangup",
|
||||
"Voice call": "Voice call",
|
||||
"Video call": "Video call",
|
||||
|
@ -1437,6 +1433,7 @@
|
|||
"Users": "Users",
|
||||
"unknown device": "unknown device",
|
||||
"NOT verified": "NOT verified",
|
||||
"Blacklisted": "Blacklisted",
|
||||
"verified": "verified",
|
||||
"Name": "Name",
|
||||
"Verification": "Verification",
|
||||
|
|
Loading…
Reference in a new issue