From b2fc4a1c4de9251284c392c3d1efe3ae06fd222c Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Tue, 21 Jan 2020 18:41:43 +0000 Subject: [PATCH 01/19] Style bridge settings tab according to design Signed-off-by: Half-Shot --- .../views/dialogs/_RoomSettingsDialog.scss | 58 ++++++- .../views/dialogs/RoomSettingsDialog.js | 7 +- .../settings/tabs/room/BridgeSettingsTab.js | 156 ++++++++++-------- src/i18n/strings/en_EN.json | 11 +- 4 files changed, 144 insertions(+), 88 deletions(-) diff --git a/res/css/views/dialogs/_RoomSettingsDialog.scss b/res/css/views/dialogs/_RoomSettingsDialog.scss index aa66e97f9e..0e8deb018e 100644 --- a/res/css/views/dialogs/_RoomSettingsDialog.scss +++ b/res/css/views/dialogs/_RoomSettingsDialog.scss @@ -63,9 +63,59 @@ limitations under the License. .mx_RoomSettingsDialog_BridgeList li { list-style-type: none; padding: 5px; - margin-bottom: 5px; - border-width: 1px 0px; - border-color: #dee1f3; - border-style: solid; + margin-bottom: 8px; + border-width: 1px 1px; + border-color: $primary-hairline-color; + border-radius: 5px; + + .protocol-icon { + float: left; + margin-right: 30px; + img { + border-radius: 5px; + border-width: 1px 1px; + border-color: $primary-hairline-color; + border-style: solid; + } + span { + /* Correct letter placement */ + left: auto; + } + } + + h3 { + margin-top: 0; + margin-bottom: 4px; + font-size: 16pt; + } + + .column-icon { + float: left; + } + + .column-data { + display: inline-block; + width: 85%; + } + + .workspace-channel-details { + margin-top: 0; + color: $primary-fg-color; + } + + .metadata { + color: $muted-fg-color; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + margin-bottom: 0; + } + + .metadata.visible { + overflow-y: visible; + text-overflow: ellipsis; + white-space: normal; + } + } diff --git a/src/components/views/dialogs/RoomSettingsDialog.js b/src/components/views/dialogs/RoomSettingsDialog.js index a99141870b..76faf60eef 100644 --- a/src/components/views/dialogs/RoomSettingsDialog.js +++ b/src/components/views/dialogs/RoomSettingsDialog.js @@ -54,9 +54,6 @@ export default class RoomSettingsDialog extends React.Component { _getTabs() { const tabs = []; - const featureFlag = SettingsStore.isFeatureEnabled("feature_bridge_state"); - const shouldShowBridgeIcon = featureFlag && - BridgeSettingsTab.getBridgeStateEvents(this.props.roomId).length > 0; tabs.push(new Tab( _td("General"), @@ -79,9 +76,9 @@ export default class RoomSettingsDialog extends React.Component { , )); - if (shouldShowBridgeIcon) { + if (SettingsStore.isFeatureEnabled("feature_bridge_state")) { tabs.push(new Tab( - _td("Bridge Info"), + _td("Bridges"), "mx_RoomSettingsDialog_bridgesIcon", , )); diff --git a/src/components/views/settings/tabs/room/BridgeSettingsTab.js b/src/components/views/settings/tabs/room/BridgeSettingsTab.js index 19c19d3bc6..8090cf7d4d 100644 --- a/src/components/views/settings/tabs/room/BridgeSettingsTab.js +++ b/src/components/views/settings/tabs/room/BridgeSettingsTab.js @@ -33,6 +33,21 @@ export default class BridgeSettingsTab extends React.Component { roomId: PropTypes.string.isRequired, }; + constructor() { + super(); + + this.state = { + showMoreCard: null, + }; + } + + + _showMoreDetails(eventId) { + this.setState({ + showMoreCard: eventId, + }); + } + _renderBridgeCard(event, room) { const content = event.getContent(); if (!content || !content.channel || !content.protocol) { @@ -45,90 +60,59 @@ export default class BridgeSettingsTab extends React.Component { let creator = null; if (content.creator) { - creator =

{ _t("This bridge was provisioned by ", {}, { + creator = _t("This bridge was provisioned by .", {}, { user: , - })}

; + }); } - const bot = (

{_t("This bridge is managed by .", {}, { + const bot = _t("This bridge is managed by .", {}, { user: , - })}

); - let channelLink = channelName; - if (channel.external_url) { - channelLink = {channelName}; - } - - let networkLink = networkName; - if (network && network.external_url) { - networkLink = {networkName}; - } - - const chanAndNetworkInfo = ( - _t("Bridged into , on ", {}, { - channelLink, - networkLink, - protocolName, - }) - ); - - let networkIcon = null; - if (networkName && network.avatar) { - const avatarUrl = getHttpUriForMxc( - MatrixClientPeg.get().getHomeserverUrl(), - network.avatar, 32, 32, "crop", - ); - networkIcon = ; - } - - let channelIcon = null; - if (channel.avatar) { - const avatarUrl = getHttpUriForMxc( - MatrixClientPeg.get().getHomeserverUrl(), - channel.avatar, 32, 32, "crop", - ); - channelIcon = ; - } - - const heading = _t("Connected to on ", { }, { - channelIcon, - channelName, - networkName, - networkIcon, }); - return (
  • -
    -

    {heading}

    -

    {_t("Connected via %(protocolName)s", { protocolName })}

    -
    - {creator} - {bot} -

    {chanAndNetworkInfo}

    -
    + const avatarUrl = network.avatar ? getHttpUriForMxc( + MatrixClientPeg.get().getHomeserverUrl(), + network.avatar, 32, 32, "crop", + ) : null; + + const networkIcon = ; + + const workspaceChannelDetails = _t("Workspace: %(networkName)s Channel: %(channelName)s", { + networkName, + channelName, + }); + const id = event.getId(); + const isVisible = this.state.showMoreCard === id; + const metadataClassname = "metadata " + (isVisible ? "visible" : ""); + return (
  • +
    + {networkIcon} +
    +
    +

    {protocolName}

    +

    + {workspaceChannelDetails} +

    +

    + {creator} {bot} +

    + this._showMoreDetails(isVisible ? null : id)}>Show { isVisible ? "less" : "more" }
  • ); } @@ -151,14 +135,40 @@ export default class BridgeSettingsTab extends React.Component { const client = MatrixClientPeg.get(); const room = client.getRoom(this.props.roomId); + let content = null; + + if (bridgeEvents.length > 0) { + content =
    +

    {_t( + "This room is bridging messages to the following platforms. " + + "Learn more.", {}, + { + // TODO: We don't have this link yet: this will prevent the translators + // having to re-translate the string when we do. + a: sub => '', + }, + )}

    +
      + { bridgeEvents.map((event) => this._renderBridgeCard(event, room)) } +
    +
    + } else { + content =

    {_t( + "This room isn’t bridging messages to any platforms. " + + "Learn more.", {}, + { + // TODO: We don't have this link yet: this will prevent the translators + // having to re-translate the string when we do. + a: sub => '', + }, + )}

    + } + return (
    -
    {_t("Bridge Info")}
    +
    {_t("Bridges")}
    -

    { _t("Below is a list of bridges connected to this room.") }

    -
      - { bridgeEvents.map((event) => this._renderBridgeCard(event, room)) } -
    + {content}
    ); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index f0eab6b12d..e4ab764989 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -781,13 +781,12 @@ "Room version:": "Room version:", "Developer options": "Developer options", "Open Devtools": "Open Devtools", - "This bridge was provisioned by ": "This bridge was provisioned by ", + "This bridge was provisioned by .": "This bridge was provisioned by .", "This bridge is managed by .": "This bridge is managed by .", - "Bridged into , on ": "Bridged into , on ", - "Connected to on ": "Connected to on ", - "Connected via %(protocolName)s": "Connected via %(protocolName)s", - "Bridge Info": "Bridge Info", - "Below is a list of bridges connected to this room.": "Below is a list of bridges connected to this room.", + "Workspace: %(networkName)s Channel: %(channelName)s": "Workspace: %(networkName)s Channel: %(channelName)s", + "This room is bridging messages to the following platforms. Learn more.": "This room is bridging messages to the following platforms. Learn more.", + "This room isn’t bridging messages to any platforms. Learn more.": "This room isn’t bridging messages to any platforms. Learn more.", + "Bridges": "Bridges", "Room Addresses": "Room Addresses", "Publish this room to the public in %(domain)s's room directory?": "Publish this room to the public in %(domain)s's room directory?", "URL Previews": "URL Previews", From c9a0e93a74f437978eef3266f8b2b279558e9857 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 27 Jan 2020 11:14:32 +0000 Subject: [PATCH 02/19] tidy up borders --- res/css/views/dialogs/_RoomSettingsDialog.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/res/css/views/dialogs/_RoomSettingsDialog.scss b/res/css/views/dialogs/_RoomSettingsDialog.scss index 0e8deb018e..66c34fd73d 100644 --- a/res/css/views/dialogs/_RoomSettingsDialog.scss +++ b/res/css/views/dialogs/_RoomSettingsDialog.scss @@ -66,16 +66,16 @@ limitations under the License. margin-bottom: 8px; border-width: 1px 1px; border-color: $primary-hairline-color; + border-style: solid; border-radius: 5px; .protocol-icon { float: left; - margin-right: 30px; + margin-right: 5px; img { border-radius: 5px; border-width: 1px 1px; border-color: $primary-hairline-color; - border-style: solid; } span { /* Correct letter placement */ From c0d1298c4f7296fc066b5721fa4f01695acdc95a Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 27 Jan 2020 14:05:22 +0000 Subject: [PATCH 03/19] Factor out into BridgeTile --- res/css/_components.scss | 1 + .../dialogs/_RoomSettingsDialogBridges.scss | 93 ++++++++++++++ src/components/views/settings/BridgeTile.js | 116 ++++++++++++++++++ .../settings/tabs/room/BridgeSettingsTab.js | 79 +----------- 4 files changed, 212 insertions(+), 77 deletions(-) create mode 100644 res/css/views/dialogs/_RoomSettingsDialogBridges.scss create mode 100644 src/components/views/settings/BridgeTile.js diff --git a/res/css/_components.scss b/res/css/_components.scss index 60f749de9c..f6a680c438 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -66,6 +66,7 @@ @import "./views/dialogs/_InviteDialog.scss"; @import "./views/dialogs/_MessageEditHistoryDialog.scss"; @import "./views/dialogs/_RoomSettingsDialog.scss"; +@import "./views/dialogs/_RoomSettingsDialogBridges.scss"; @import "./views/dialogs/_RoomUpgradeDialog.scss"; @import "./views/dialogs/_RoomUpgradeWarningDialog.scss"; @import "./views/dialogs/_SetEmailDialog.scss"; diff --git a/res/css/views/dialogs/_RoomSettingsDialogBridges.scss b/res/css/views/dialogs/_RoomSettingsDialogBridges.scss new file mode 100644 index 0000000000..85d5c76ffc --- /dev/null +++ b/res/css/views/dialogs/_RoomSettingsDialogBridges.scss @@ -0,0 +1,93 @@ +/* +Copyright 2020 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_RoomSettingsDialog_BridgeList { + padding: 0; + + .mx_AccessibleButton { + display: inline; + margin: 0; + padding: 0; + float: left; + } +} + +.mx_RoomSettingsDialog_BridgeList li { + list-style-type: none; + padding: 5px; + margin-bottom: 8px; + border-width: 1px 1px; + border-color: $primary-hairline-color; + border-style: solid; + border-radius: 5px; + + .protocol-icon { + float: left; + margin-right: 5px; + img { + border-radius: 5px; + border-width: 1px 1px; + border-color: $primary-hairline-color; + } + span { + /* Correct letter placement */ + left: auto; + } + } + + h3 { + margin-top: 0; + margin-bottom: 4px; + font-size: 16pt; + color: $primary-fg-color; + } + + .column-icon { + float: left; + padding-right: 10px; + + .noProtocolIcon { + width: 48px; + height: 48px; + background: $settings-profile-placeholder-bg-color; + border-radius: 5px; + } + } + + .column-data { + display: inline-block; + width: 85%; + } + + .workspace-channel-details { + margin-top: 0; + color: $primary-fg-color; + } + + .metadata { + color: $muted-fg-color; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + margin-bottom: 0; + } + + .metadata.visible { + overflow-y: visible; + text-overflow: ellipsis; + white-space: normal; + } +} diff --git a/src/components/views/settings/BridgeTile.js b/src/components/views/settings/BridgeTile.js new file mode 100644 index 0000000000..330af4a18a --- /dev/null +++ b/src/components/views/settings/BridgeTile.js @@ -0,0 +1,116 @@ +/* +Copyright 2020 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 React from 'react'; +import PropTypes from 'prop-types'; +import {getHttpUriForMxc} from "matrix-js-sdk/src/content-repo"; +import {_t} from "../../../languageHandler"; +import {MatrixClientPeg} from "../../../MatrixClientPeg"; +import Pill from "../elements/Pill"; +import {makeUserPermalink} from "../../../utils/permalinks/Permalinks"; +import BaseAvatar from "../avatars/BaseAvatar"; +import AccessibleButton from "../elements/AccessibleButton"; + +export default class BridgeTile extends React.PureComponent { + static propTypes = { + ev: PropTypes.object.isRequired, + room: PropTypes.object.isRequired, + } + + state = { + visible: false + } + + _toggleVisible() { + this.setState({ + visible: !this.state.visible, + }); + } + + render() { + const content = this.props.ev.getContent(); + const { channel, network, protocol } = content; + const protocolName = protocol.displayname || protocol.id; + const channelName = channel.displayname || channel.id; + const networkName = network ? network.displayname || network.id : protocolName; + + let creator = null; + if (content.creator) { + creator = _t("This bridge was provisioned by .", {}, { + user: , + }); + } + + const bot = _t("This bridge is managed by .", {}, { + user: , + }); + + let networkIcon; + + if (protocol.avatar) { + const avatarUrl = getHttpUriForMxc( + MatrixClientPeg.get().getHomeserverUrl(), + protocol.avatar, 64, 64, "crop", + ); + + networkIcon = ; + } else { + networkIcon =
    ; + } + + + const workspaceChannelDetails = _t("Workspace: %(networkName)s Channel: %(channelName)s", { + networkName, + channelName, + }); + const id = this.props.ev.getId(); + const metadataClassname = "metadata" + (this.state.visible ? " visible" : ""); + return (
  • +
    + {networkIcon} +
    +
    +

    {protocolName}

    +

    + {workspaceChannelDetails} +

    +

    + {creator} {bot} +

    + + Show { this.state.visible ? "less" : "more" } + +
    +
  • ); + } +} diff --git a/src/components/views/settings/tabs/room/BridgeSettingsTab.js b/src/components/views/settings/tabs/room/BridgeSettingsTab.js index 8090cf7d4d..7a859b0594 100644 --- a/src/components/views/settings/tabs/room/BridgeSettingsTab.js +++ b/src/components/views/settings/tabs/room/BridgeSettingsTab.js @@ -18,10 +18,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import {_t} from "../../../../../languageHandler"; import {MatrixClientPeg} from "../../../../../MatrixClientPeg"; -import Pill from "../../../elements/Pill"; -import {makeUserPermalink} from "../../../../../utils/permalinks/Permalinks"; -import BaseAvatar from "../../../avatars/BaseAvatar"; -import {getHttpUriForMxc} from "matrix-js-sdk/src/content-repo"; +import BridgeTile from "../../BridgeTile"; const BRIDGE_EVENT_TYPES = [ "uk.half-shot.bridge", @@ -35,17 +32,6 @@ export default class BridgeSettingsTab extends React.Component { constructor() { super(); - - this.state = { - showMoreCard: null, - }; - } - - - _showMoreDetails(eventId) { - this.setState({ - showMoreCard: eventId, - }); } _renderBridgeCard(event, room) { @@ -53,68 +39,7 @@ export default class BridgeSettingsTab extends React.Component { if (!content || !content.channel || !content.protocol) { return null; } - const { channel, network } = content; - const protocolName = content.protocol.displayname || content.protocol.id; - const channelName = channel.displayname || channel.id; - const networkName = network ? network.displayname || network.id : protocolName; - - let creator = null; - if (content.creator) { - creator = _t("This bridge was provisioned by .", {}, { - user: , - }); - } - - const bot = _t("This bridge is managed by .", {}, { - user: , - }); - - const avatarUrl = network.avatar ? getHttpUriForMxc( - MatrixClientPeg.get().getHomeserverUrl(), - network.avatar, 32, 32, "crop", - ) : null; - - const networkIcon = ; - - const workspaceChannelDetails = _t("Workspace: %(networkName)s Channel: %(channelName)s", { - networkName, - channelName, - }); - const id = event.getId(); - const isVisible = this.state.showMoreCard === id; - const metadataClassname = "metadata " + (isVisible ? "visible" : ""); - return (
  • -
    - {networkIcon} -
    -
    -

    {protocolName}

    -

    - {workspaceChannelDetails} -

    -

    - {creator} {bot} -

    - this._showMoreDetails(isVisible ? null : id)}>Show { isVisible ? "less" : "more" } -
    -
  • ); + return } static getBridgeStateEvents(roomId) { From 4d83288f4ea6ebfe61f6f7f0ef4784a9e175ad86 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 27 Jan 2020 14:42:46 +0000 Subject: [PATCH 04/19] linting --- src/components/views/settings/BridgeTile.js | 4 ++-- .../views/settings/tabs/room/BridgeSettingsTab.js | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/views/settings/BridgeTile.js b/src/components/views/settings/BridgeTile.js index 330af4a18a..ee9343ec39 100644 --- a/src/components/views/settings/BridgeTile.js +++ b/src/components/views/settings/BridgeTile.js @@ -29,9 +29,9 @@ export default class BridgeTile extends React.PureComponent { ev: PropTypes.object.isRequired, room: PropTypes.object.isRequired, } - + state = { - visible: false + visible: false, } _toggleVisible() { diff --git a/src/components/views/settings/tabs/room/BridgeSettingsTab.js b/src/components/views/settings/tabs/room/BridgeSettingsTab.js index 7a859b0594..65c59ff977 100644 --- a/src/components/views/settings/tabs/room/BridgeSettingsTab.js +++ b/src/components/views/settings/tabs/room/BridgeSettingsTab.js @@ -39,7 +39,7 @@ export default class BridgeSettingsTab extends React.Component { if (!content || !content.channel || !content.protocol) { return null; } - return + return ; } static getBridgeStateEvents(roomId) { @@ -76,8 +76,8 @@ export default class BridgeSettingsTab extends React.Component {
      { bridgeEvents.map((event) => this._renderBridgeCard(event, room)) }
    - - } else { + ; + } else { content =

    {_t( "This room isn’t bridging messages to any platforms. " + "Learn more.", {}, @@ -86,7 +86,7 @@ export default class BridgeSettingsTab extends React.Component { // having to re-translate the string when we do. a: sub => '', }, - )}

    + )}

    ; } return ( From 5851b10f72af580b2be3357799075ea0fcd1dd82 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 27 Jan 2020 14:44:11 +0000 Subject: [PATCH 05/19] strings --- src/i18n/strings/en_EN.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index e4ab764989..1afa3a33c9 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -521,6 +521,9 @@ "Accept to continue:": "Accept to continue:", "Upload": "Upload", "Remove": "Remove", + "This bridge was provisioned by .": "This bridge was provisioned by .", + "This bridge is managed by .": "This bridge is managed by .", + "Workspace: %(networkName)s Channel: %(channelName)s": "Workspace: %(networkName)s Channel: %(channelName)s", "Failed to upload profile picture!": "Failed to upload profile picture!", "Upload new:": "Upload new:", "No display name": "No display name", @@ -781,9 +784,6 @@ "Room version:": "Room version:", "Developer options": "Developer options", "Open Devtools": "Open Devtools", - "This bridge was provisioned by .": "This bridge was provisioned by .", - "This bridge is managed by .": "This bridge is managed by .", - "Workspace: %(networkName)s Channel: %(channelName)s": "Workspace: %(networkName)s Channel: %(channelName)s", "This room is bridging messages to the following platforms. Learn more.": "This room is bridging messages to the following platforms. Learn more.", "This room isn’t bridging messages to any platforms. Learn more.": "This room isn’t bridging messages to any platforms. Learn more.", "Bridges": "Bridges", From 1964e18315657f800257e66756f6f6e5505ba20b Mon Sep 17 00:00:00 2001 From: Zoe Date: Mon, 27 Jan 2020 16:40:56 +0000 Subject: [PATCH 06/19] Fix issue where we don't notice if our own devices shouldn't be trusted --- src/components/structures/RoomView.js | 2 +- src/components/views/rooms/RoomTile.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 60fff5f1e3..2d669f9243 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -811,7 +811,7 @@ export default createReactClass({ debuglog("e2e verified", verified, "unverified", unverified); /* Check all verified user devices. */ - for (const userId of verified) { + for (const userId of [...verified, cli.getUserId()]) { const devices = await cli.getStoredDevicesForUser(userId); const anyDeviceNotVerified = devices.some(({deviceId}) => { return !cli.checkDeviceTrust(userId, deviceId).isVerified(); diff --git a/src/components/views/rooms/RoomTile.js b/src/components/views/rooms/RoomTile.js index 41975fe7b8..41d43476ea 100644 --- a/src/components/views/rooms/RoomTile.js +++ b/src/components/views/rooms/RoomTile.js @@ -166,7 +166,7 @@ export default createReactClass({ }); /* Check all verified user devices. */ - for (const userId of verified) { + for (const userId of [...verified, cli.getUserId()]) { const devices = await cli.getStoredDevicesForUser(userId); const allDevicesVerified = devices.every(({deviceId}) => { return cli.checkDeviceTrust(userId, deviceId).isVerified(); From d014c5239be443911d3a3ad0b4650756c9e04a1a Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Mon, 27 Jan 2020 23:14:02 +0000 Subject: [PATCH 07/19] Add new session verification details dialog This gives more info on the session you're about to verify, including device name and ID. Fixes https://github.com/vector-im/riot-web/issues/11977 --- res/css/_components.scss | 1 + .../dialogs/_NewSessionReviewDialog.scss | 37 ++++++++ .../views/dialogs/NewSessionReviewDialog.js | 92 +++++++++++++++++++ .../views/elements/DialogButtons.js | 2 +- .../views/toasts/NewSessionToast.js | 5 +- src/i18n/strings/en_EN.json | 4 + 6 files changed, 138 insertions(+), 3 deletions(-) create mode 100644 res/css/views/dialogs/_NewSessionReviewDialog.scss create mode 100644 src/components/views/dialogs/NewSessionReviewDialog.js diff --git a/res/css/_components.scss b/res/css/_components.scss index 07e92bdc7b..de56ad77bb 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -65,6 +65,7 @@ @import "./views/dialogs/_IncomingSasDialog.scss"; @import "./views/dialogs/_InviteDialog.scss"; @import "./views/dialogs/_MessageEditHistoryDialog.scss"; +@import "./views/dialogs/_NewSessionReviewDialog.scss"; @import "./views/dialogs/_RoomSettingsDialog.scss"; @import "./views/dialogs/_RoomUpgradeDialog.scss"; @import "./views/dialogs/_RoomUpgradeWarningDialog.scss"; diff --git a/res/css/views/dialogs/_NewSessionReviewDialog.scss b/res/css/views/dialogs/_NewSessionReviewDialog.scss new file mode 100644 index 0000000000..7e35fe941e --- /dev/null +++ b/res/css/views/dialogs/_NewSessionReviewDialog.scss @@ -0,0 +1,37 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +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_NewSessionReviewDialog_header { + display: flex; + align-items: center; + margin-top: 0; +} + +.mx_NewSessionReviewDialog_headerIcon { + width: 24px; + height: 24px; + margin-right: 4px; + position: relative; +} + +.mx_NewSessionReviewDialog_deviceName { + font-weight: 600; +} + +.mx_NewSessionReviewDialog_deviceID { + font-size: 12px; + color: $notice-secondary-color; +} diff --git a/src/components/views/dialogs/NewSessionReviewDialog.js b/src/components/views/dialogs/NewSessionReviewDialog.js new file mode 100644 index 0000000000..c14f0f5614 --- /dev/null +++ b/src/components/views/dialogs/NewSessionReviewDialog.js @@ -0,0 +1,92 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +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 React from 'react'; +import PropTypes from 'prop-types'; +import * as sdk from "../../../index"; +import { _t } from '../../../languageHandler'; +import Modal from "../../../Modal"; + +export default class NewSessionReviewDialog extends React.PureComponent { + static propTypes = { + userId: PropTypes.string.isRequired, + device: PropTypes.object.isRequired, + onFinished: PropTypes.func.isRequired, + } + + onCancelClick = () => { + this.props.onFinished(false); + } + + onContinueClick = () => { + const DeviceVerifyDialog = + sdk.getComponent('views.dialogs.DeviceVerifyDialog'); + const { userId, device } = this.props; + Modal.createTrackedDialog('New Session Verification', 'Starting dialog', DeviceVerifyDialog, { + userId, + device, + }, null, /* priority = */ false, /* static = */ true); + } + + render() { + const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); + const DialogButtons = sdk.getComponent("views.elements.DialogButtons"); + + const { device } = this.props; + + const icon = ; + const titleText = _t("New session"); + + const title =

    + {icon} + {titleText} +

    ; + + return ( + +
    +

    {_t( + "Use this session to verify your new one, " + + "granting it access to encrypted messages:", + )}

    +
    +
    + + {device.getDisplayName()} + + ({device.deviceId}) + +
    +
    +

    {_t( + "If you didn’t sign in to this session, " + + "your account may be compromised.", + )}

    + +
    +
    + ); + } +} diff --git a/src/components/views/elements/DialogButtons.js b/src/components/views/elements/DialogButtons.js index ee15bfc3f2..9223b5ade8 100644 --- a/src/components/views/elements/DialogButtons.js +++ b/src/components/views/elements/DialogButtons.js @@ -83,7 +83,7 @@ export default createReactClass({ // primary in the DOM so will get form submissions unless we make it not a submit. type="button" onClick={this._onCancelClick} - className={this.props.cancelButtonClass} + className={this.props.cancelButtonClass} disabled={this.props.disabled} > { this.props.cancelButton || _t("Cancel") } diff --git a/src/components/views/toasts/NewSessionToast.js b/src/components/views/toasts/NewSessionToast.js index 3b60f59131..ed8b15e25f 100644 --- a/src/components/views/toasts/NewSessionToast.js +++ b/src/components/views/toasts/NewSessionToast.js @@ -34,11 +34,12 @@ export default class VerifySessionToast extends React.PureComponent { _onReviewClick = async () => { const cli = MatrixClientPeg.get(); - const DeviceVerifyDialog = sdk.getComponent('views.dialogs.DeviceVerifyDialog'); + const NewSessionReviewDialog = + sdk.getComponent('views.dialogs.NewSessionReviewDialog'); const device = await cli.getStoredDevice(cli.getUserId(), this.props.deviceId); - Modal.createTrackedDialog('New Session Verify', 'Starting dialog', DeviceVerifyDialog, { + Modal.createTrackedDialog('New Session Review', 'Starting dialog', NewSessionReviewDialog, { userId: MatrixClientPeg.get().getUserId(), device, }, null, /* priority = */ false, /* static = */ true); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 23ca730d97..7ccce9e7f6 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1508,6 +1508,10 @@ "Are you sure you want to sign out?": "Are you sure you want to sign out?", "Your homeserver doesn't seem to support this feature.": "Your homeserver doesn't seem to support this feature.", "Message edits": "Message edits", + "New session": "New session", + "Use this session to verify your new one, granting it access to encrypted messages:": "Use this session to verify your new one, granting it access to encrypted messages:", + "If you didn’t sign in to this session, your account may be compromised.": "If you didn’t sign in to this session, your account may be compromised.", + "This wasn't me": "This wasn't me", "If you run into any bugs or have feedback you'd like to share, please let us know on GitHub.": "If you run into any bugs or have feedback you'd like to share, please let us know on GitHub.", "To help avoid duplicate issues, please view existing issues first (and add a +1) or create a new issue if you can't find it.": "To help avoid duplicate issues, please view existing issues first (and add a +1) or create a new issue if you can't find it.", "Report bugs & give feedback": "Report bugs & give feedback", From bdaf9fd06d2ec85985753eda2d076c7f195dd367 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Tue, 28 Jan 2020 10:05:42 +0000 Subject: [PATCH 08/19] i18n --- src/components/views/settings/BridgeTile.js | 2 +- src/i18n/strings/en_EN.json | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/views/settings/BridgeTile.js b/src/components/views/settings/BridgeTile.js index ee9343ec39..a5672c271c 100644 --- a/src/components/views/settings/BridgeTile.js +++ b/src/components/views/settings/BridgeTile.js @@ -108,7 +108,7 @@ export default class BridgeTile extends React.PureComponent { {creator} {bot}

    - Show { this.state.visible ? "less" : "more" } + { this.state.visible ? _t("Show less") : _t("Show more") } ); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 1afa3a33c9..270d964e56 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -524,6 +524,8 @@ "This bridge was provisioned by .": "This bridge was provisioned by .", "This bridge is managed by .": "This bridge is managed by .", "Workspace: %(networkName)s Channel: %(channelName)s": "Workspace: %(networkName)s Channel: %(channelName)s", + "Show less": "Show less", + "Show more": "Show more", "Failed to upload profile picture!": "Failed to upload profile picture!", "Upload new:": "Upload new:", "No display name": "No display name", @@ -1461,7 +1463,6 @@ "Recent Conversations": "Recent Conversations", "Suggestions": "Suggestions", "Recently Direct Messaged": "Recently Direct Messaged", - "Show more": "Show more", "Direct Messages": "Direct Messages", "If you can't find someone, ask them for their username, or share your username (%(userId)s) or profile link.": "If you can't find someone, ask them for their username, or share your username (%(userId)s) or profile link.", "Go": "Go", From 67358e06bf5da0d40f58d00c75b589e70624bc79 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Tue, 28 Jan 2020 10:10:37 +0000 Subject: [PATCH 09/19] Use annotations and imports --- .../views/dialogs/NewSessionReviewDialog.js | 13 ++++++------- src/components/views/toasts/NewSessionToast.js | 8 ++++---- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/components/views/dialogs/NewSessionReviewDialog.js b/src/components/views/dialogs/NewSessionReviewDialog.js index c14f0f5614..2d2bcc8f35 100644 --- a/src/components/views/dialogs/NewSessionReviewDialog.js +++ b/src/components/views/dialogs/NewSessionReviewDialog.js @@ -16,10 +16,14 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; -import * as sdk from "../../../index"; import { _t } from '../../../languageHandler'; -import Modal from "../../../Modal"; +import Modal from '../../../Modal'; +import { replaceableComponent } from '../../../utils/replaceableComponent'; +import DeviceVerifyDialog from './DeviceVerifyDialog'; +import BaseDialog from './BaseDialog'; +import DialogButtons from '../elements/DialogButtons'; +@replaceableComponent("views.dialogs.NewSessionReviewDialog") export default class NewSessionReviewDialog extends React.PureComponent { static propTypes = { userId: PropTypes.string.isRequired, @@ -32,8 +36,6 @@ export default class NewSessionReviewDialog extends React.PureComponent { } onContinueClick = () => { - const DeviceVerifyDialog = - sdk.getComponent('views.dialogs.DeviceVerifyDialog'); const { userId, device } = this.props; Modal.createTrackedDialog('New Session Verification', 'Starting dialog', DeviceVerifyDialog, { userId, @@ -42,9 +44,6 @@ export default class NewSessionReviewDialog extends React.PureComponent { } render() { - const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); - const DialogButtons = sdk.getComponent("views.elements.DialogButtons"); - const { device } = this.props; const icon = ; diff --git a/src/components/views/toasts/NewSessionToast.js b/src/components/views/toasts/NewSessionToast.js index ed8b15e25f..80564f3494 100644 --- a/src/components/views/toasts/NewSessionToast.js +++ b/src/components/views/toasts/NewSessionToast.js @@ -16,12 +16,15 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; -import * as sdk from "../../../index"; import { _t } from '../../../languageHandler'; import Modal from "../../../Modal"; import { MatrixClientPeg } from '../../../MatrixClientPeg'; import DeviceListener from '../../../DeviceListener'; +import NewSessionReviewDialog from '../dialogs/NewSessionReviewDialog'; +import FormButton from '../elements/FormButton'; +import { replaceableComponent } from '../../../utils/replaceableComponent'; +@replaceableComponent("views.toasts.VerifySessionToast") export default class VerifySessionToast extends React.PureComponent { static propTypes = { toastKey: PropTypes.string.isRequired, @@ -34,8 +37,6 @@ export default class VerifySessionToast extends React.PureComponent { _onReviewClick = async () => { const cli = MatrixClientPeg.get(); - const NewSessionReviewDialog = - sdk.getComponent('views.dialogs.NewSessionReviewDialog'); const device = await cli.getStoredDevice(cli.getUserId(), this.props.deviceId); @@ -46,7 +47,6 @@ export default class VerifySessionToast extends React.PureComponent { }; render() { - const FormButton = sdk.getComponent("elements.FormButton"); return (
    {_t("Review & verify your new session")}
    From 785277d4b8d44ad011bb79cf0db2369584ee0730 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Tue, 28 Jan 2020 11:17:51 +0000 Subject: [PATCH 10/19] Review bits for travis --- .../views/dialogs/_RoomSettingsDialog.scss | 63 ------------------- .../dialogs/_RoomSettingsDialogBridges.scss | 7 ++- src/components/views/settings/BridgeTile.js | 13 ++-- .../settings/tabs/room/BridgeSettingsTab.js | 8 +-- src/i18n/strings/en_EN.json | 3 +- 5 files changed, 14 insertions(+), 80 deletions(-) diff --git a/res/css/views/dialogs/_RoomSettingsDialog.scss b/res/css/views/dialogs/_RoomSettingsDialog.scss index 66c34fd73d..2a4e62f9aa 100644 --- a/res/css/views/dialogs/_RoomSettingsDialog.scss +++ b/res/css/views/dialogs/_RoomSettingsDialog.scss @@ -56,66 +56,3 @@ limitations under the License. mask-position: center; } -.mx_RoomSettingsDialog_BridgeList { - padding: 0; -} - -.mx_RoomSettingsDialog_BridgeList li { - list-style-type: none; - padding: 5px; - margin-bottom: 8px; - border-width: 1px 1px; - border-color: $primary-hairline-color; - border-style: solid; - border-radius: 5px; - - .protocol-icon { - float: left; - margin-right: 5px; - img { - border-radius: 5px; - border-width: 1px 1px; - border-color: $primary-hairline-color; - } - span { - /* Correct letter placement */ - left: auto; - } - } - - h3 { - margin-top: 0; - margin-bottom: 4px; - font-size: 16pt; - } - - .column-icon { - float: left; - } - - .column-data { - display: inline-block; - width: 85%; - } - - .workspace-channel-details { - margin-top: 0; - color: $primary-fg-color; - } - - .metadata { - color: $muted-fg-color; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - margin-bottom: 0; - } - - .metadata.visible { - overflow-y: visible; - text-overflow: ellipsis; - white-space: normal; - } - -} - diff --git a/res/css/views/dialogs/_RoomSettingsDialogBridges.scss b/res/css/views/dialogs/_RoomSettingsDialogBridges.scss index 85d5c76ffc..ab54fb777b 100644 --- a/res/css/views/dialogs/_RoomSettingsDialogBridges.scss +++ b/res/css/views/dialogs/_RoomSettingsDialogBridges.scss @@ -1,5 +1,5 @@ /* -Copyright 2020 New Vector Ltd. +Copyright 2020 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -21,7 +21,6 @@ limitations under the License. display: inline; margin: 0; padding: 0; - float: left; } } @@ -75,6 +74,10 @@ limitations under the License. .workspace-channel-details { margin-top: 0; color: $primary-fg-color; + + .channel { + margin-left: 15px; + } } .metadata { diff --git a/src/components/views/settings/BridgeTile.js b/src/components/views/settings/BridgeTile.js index a5672c271c..dca23723aa 100644 --- a/src/components/views/settings/BridgeTile.js +++ b/src/components/views/settings/BridgeTile.js @@ -1,5 +1,5 @@ /* -Copyright 2020 New Vector Ltd +Copyright 2020 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -23,7 +23,8 @@ import Pill from "../elements/Pill"; import {makeUserPermalink} from "../../../utils/permalinks/Permalinks"; import BaseAvatar from "../avatars/BaseAvatar"; import AccessibleButton from "../elements/AccessibleButton"; - +import {replaceableComponent} from "../../../utils/replaceableComponent"; +@replaceableComponent("views.settings.BridgeTile") export default class BridgeTile extends React.PureComponent { static propTypes = { ev: PropTypes.object.isRequired, @@ -88,11 +89,6 @@ export default class BridgeTile extends React.PureComponent { networkIcon =
    ; } - - const workspaceChannelDetails = _t("Workspace: %(networkName)s Channel: %(channelName)s", { - networkName, - channelName, - }); const id = this.props.ev.getId(); const metadataClassname = "metadata" + (this.state.visible ? " visible" : ""); return (
  • @@ -102,7 +98,8 @@ export default class BridgeTile extends React.PureComponent {

    {protocolName}

    - {workspaceChannelDetails} + {_t("Workspace: %(networkName)s", {networkName})} + {_t("Channel: %(channelName)s", {channelName})}

    {creator} {bot} diff --git a/src/components/views/settings/tabs/room/BridgeSettingsTab.js b/src/components/views/settings/tabs/room/BridgeSettingsTab.js index 65c59ff977..12a72ab8f9 100644 --- a/src/components/views/settings/tabs/room/BridgeSettingsTab.js +++ b/src/components/views/settings/tabs/room/BridgeSettingsTab.js @@ -30,10 +30,6 @@ export default class BridgeSettingsTab extends React.Component { roomId: PropTypes.string.isRequired, }; - constructor() { - super(); - } - _renderBridgeCard(event, room) { const content = event.getContent(); if (!content || !content.channel || !content.protocol) { @@ -70,7 +66,7 @@ export default class BridgeSettingsTab extends React.Component { { // TODO: We don't have this link yet: this will prevent the translators // having to re-translate the string when we do. - a: sub => '', + a: sub => sub, }, )}

      @@ -84,7 +80,7 @@ export default class BridgeSettingsTab extends React.Component { { // TODO: We don't have this link yet: this will prevent the translators // having to re-translate the string when we do. - a: sub => '', + a: sub => sub, }, )}

      ; } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 270d964e56..61dcd90638 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -523,7 +523,8 @@ "Remove": "Remove", "This bridge was provisioned by .": "This bridge was provisioned by .", "This bridge is managed by .": "This bridge is managed by .", - "Workspace: %(networkName)s Channel: %(channelName)s": "Workspace: %(networkName)s Channel: %(channelName)s", + "Workspace: %(networkName)s": "Workspace: %(networkName)s", + "Channel: %(channelName)s": "Channel: %(channelName)s", "Show less": "Show less", "Show more": "Show more", "Failed to upload profile picture!": "Failed to upload profile picture!", From 71233a5affbfc8a793cd74e5ae9d6c3fd99dea63 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Tue, 28 Jan 2020 11:33:51 +0000 Subject: [PATCH 11/19] liney liney come back we need you --- src/components/views/settings/BridgeTile.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/views/settings/BridgeTile.js b/src/components/views/settings/BridgeTile.js index dca23723aa..6902639879 100644 --- a/src/components/views/settings/BridgeTile.js +++ b/src/components/views/settings/BridgeTile.js @@ -24,6 +24,7 @@ import {makeUserPermalink} from "../../../utils/permalinks/Permalinks"; import BaseAvatar from "../avatars/BaseAvatar"; import AccessibleButton from "../elements/AccessibleButton"; import {replaceableComponent} from "../../../utils/replaceableComponent"; + @replaceableComponent("views.settings.BridgeTile") export default class BridgeTile extends React.PureComponent { static propTypes = { From 9cf59ab16d6b08b0ae40a76e300e0b5d0e4612f7 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Tue, 28 Jan 2020 12:30:39 +0000 Subject: [PATCH 12/19] Enable cross-signing lab when key in storage When we're starting a new session and find the cross-signing keys in secret storage, auto-enable the lab for the new session. Fixes https://github.com/vector-im/riot-web/issues/12100 --- src/components/structures/MatrixChat.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 133d74db45..1b4d0e9609 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -1834,6 +1834,7 @@ export default createReactClass({ this._accountPassword = null; this._accountPasswordTimer = null; }, 60 * 5 * 1000); + // Wait for the client to be logged in (but not started) // which is enough to ask the server about account data. const loggedIn = new Promise(resolve => { @@ -1867,6 +1868,9 @@ export default createReactClass({ } if (masterKeyInStorage) { + // Auto-enable cross-signing for the new session when key found in + // secret storage. + SettingsStore.setFeatureEnabled("feature_cross_signing", true); this.setStateForNewView({ view: VIEWS.COMPLETE_SECURITY }); } else if (SettingsStore.isFeatureEnabled("feature_cross_signing")) { // This will only work if the feature is set to 'enable' in the config, From 21405b8f25ab36dc1967b53bdd349e872165dd18 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 28 Jan 2020 12:44:14 +0000 Subject: [PATCH 13/19] Fix skinning and babel tagets --- babel.config.js | 4 ++-- src/Skinner.js | 14 ++++++++------ src/utils/replaceableComponent.ts | 8 ++++++-- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/babel.config.js b/babel.config.js index c83be72518..944d9051bb 100644 --- a/babel.config.js +++ b/babel.config.js @@ -4,7 +4,7 @@ module.exports = { ["@babel/preset-env", { "targets": { "browsers": [ - "last 2 versions" + "last 2 Chrome versions", "last 2 Firefox versions", "last 2 Safari versions" ] }, "modules": "commonjs" @@ -14,7 +14,7 @@ module.exports = { "@babel/preset-react" ], "plugins": [ - ["@babel/plugin-proposal-decorators", { "legacy": true }], + ["@babel/plugin-proposal-decorators", {"legacy": false, decoratorsBeforeExport: true}], "@babel/plugin-proposal-export-default-from", "@babel/plugin-proposal-numeric-separator", "@babel/plugin-proposal-class-properties", diff --git a/src/Skinner.js b/src/Skinner.js index 3baecc9fb3..1e121b8808 100644 --- a/src/Skinner.js +++ b/src/Skinner.js @@ -20,6 +20,7 @@ class Skinner { } getComponent(name) { + if (!name) throw new Error(`Invalid component name: ${name}`); if (this.components === null) { throw new Error( "Attempted to get a component before a skin has been loaded."+ @@ -43,12 +44,6 @@ class Skinner { // Check the skin first let comp = doLookup(this.components); - // If that failed, check against our own components - if (!comp) { - // Lazily load our own components because they might end up calling .getComponent() - comp = doLookup(require("./component-index").components); - } - // Just return nothing instead of erroring - the consumer should be smart enough to // handle this at this point. if (!comp) { @@ -75,6 +70,13 @@ class Skinner { const comp = skinObject.components[compKeys[i]]; this.addComponent(compKeys[i], comp); } + + // Now that we have a skin, load our components too + const idx = require("./component-index"); + if (!idx || !idx.components) throw new Error("Invalid react-sdk component index"); + for (const c in idx.components) { + if (!this.components[c]) this.components[c] = idx.components[c]; + } } addComponent(name, comp) { diff --git a/src/utils/replaceableComponent.ts b/src/utils/replaceableComponent.ts index 9f617b27f3..92272e533c 100644 --- a/src/utils/replaceableComponent.ts +++ b/src/utils/replaceableComponent.ts @@ -32,9 +32,13 @@ import * as sdk from '../index'; * with a skinned version. If no skinned version is available, this component * will be used. */ -export function replaceableComponent(name: string, origComponent: React.Component) { +export function replaceableComponent(name: string) { // Decorators return a function to override the class (origComponent). This // ultimately assumes that `getComponent()` won't throw an error and instead // return a falsey value like `null` when the skin doesn't have a component. - return () => sdk.getComponent(name) || origComponent; + return (origComponent) => { + const c = sdk.getComponent(name) || origComponent; + c.kind = "class"; // appeases babel + return c; + }; } From d0c28adfb1a13a54e78a7b9c825f824a784db28c Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 28 Jan 2020 12:53:37 +0000 Subject: [PATCH 14/19] Appease the linter --- src/Skinner.js | 2 +- src/utils/replaceableComponent.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Skinner.js b/src/Skinner.js index 1e121b8808..87c5a7be7f 100644 --- a/src/Skinner.js +++ b/src/Skinner.js @@ -42,7 +42,7 @@ class Skinner { }; // Check the skin first - let comp = doLookup(this.components); + const comp = doLookup(this.components); // Just return nothing instead of erroring - the consumer should be smart enough to // handle this at this point. diff --git a/src/utils/replaceableComponent.ts b/src/utils/replaceableComponent.ts index 92272e533c..281ff4c1ac 100644 --- a/src/utils/replaceableComponent.ts +++ b/src/utils/replaceableComponent.ts @@ -32,7 +32,7 @@ import * as sdk from '../index'; * with a skinned version. If no skinned version is available, this component * will be used. */ -export function replaceableComponent(name: string) { +export function replaceableComponent(name: string) { // Decorators return a function to override the class (origComponent). This // ultimately assumes that `getComponent()` won't throw an error and instead // return a falsey value like `null` when the skin doesn't have a component. From a4778cc7e3371f12c32ec991a684c681911e0d1e Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 28 Jan 2020 14:18:12 +0000 Subject: [PATCH 15/19] Remove legacy --- babel.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/babel.config.js b/babel.config.js index 944d9051bb..333e5301af 100644 --- a/babel.config.js +++ b/babel.config.js @@ -14,7 +14,7 @@ module.exports = { "@babel/preset-react" ], "plugins": [ - ["@babel/plugin-proposal-decorators", {"legacy": false, decoratorsBeforeExport: true}], + ["@babel/plugin-proposal-decorators", {decoratorsBeforeExport: true}], "@babel/plugin-proposal-export-default-from", "@babel/plugin-proposal-numeric-separator", "@babel/plugin-proposal-class-properties", From 894568bf7aff3578970e6d577d28a14c19d2bce2 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 28 Jan 2020 14:19:06 +0000 Subject: [PATCH 16/19] Stop using deprecated stuff --- babel.config.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/babel.config.js b/babel.config.js index 333e5301af..3c0c3fcb85 100644 --- a/babel.config.js +++ b/babel.config.js @@ -2,12 +2,9 @@ module.exports = { "sourceMaps": "inline", "presets": [ ["@babel/preset-env", { - "targets": { - "browsers": [ - "last 2 Chrome versions", "last 2 Firefox versions", "last 2 Safari versions" - ] - }, - "modules": "commonjs" + "targets": [ + "last 2 Chrome versions", "last 2 Firefox versions", "last 2 Safari versions" + ], }], "@babel/preset-typescript", "@babel/preset-flow", From 85bcad0ea0e0a667de343efa81dac64e63c2cf85 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Tue, 28 Jan 2020 14:42:58 +0000 Subject: [PATCH 17/19] Styling for Nad --- .../dialogs/_RoomSettingsDialogBridges.scss | 100 ++++++++++-------- src/components/views/settings/BridgeTile.js | 2 +- .../settings/tabs/room/BridgeSettingsTab.js | 6 +- 3 files changed, 63 insertions(+), 45 deletions(-) diff --git a/res/css/views/dialogs/_RoomSettingsDialogBridges.scss b/res/css/views/dialogs/_RoomSettingsDialogBridges.scss index ab54fb777b..d77e019cbf 100644 --- a/res/css/views/dialogs/_RoomSettingsDialogBridges.scss +++ b/res/css/views/dialogs/_RoomSettingsDialogBridges.scss @@ -33,64 +33,80 @@ limitations under the License. border-style: solid; border-radius: 5px; - .protocol-icon { - float: left; - margin-right: 5px; - img { - border-radius: 5px; - border-width: 1px 1px; - border-color: $primary-hairline-color; - } - span { - /* Correct letter placement */ - left: auto; - } - } - - h3 { - margin-top: 0; - margin-bottom: 4px; - font-size: 16pt; - color: $primary-fg-color; - } - .column-icon { float: left; padding-right: 10px; + * { + border-radius: 5px; + border: 1px solid $input-darker-bg-color; + } + .noProtocolIcon { width: 48px; height: 48px; - background: $settings-profile-placeholder-bg-color; + background: $input-darker-bg-color; border-radius: 5px; } + + .protocol-icon { + float: left; + margin-right: 5px; + img { + border-radius: 5px; + border-width: 1px 1px; + border-color: $primary-hairline-color; + } + span { + /* Correct letter placement */ + left: auto; + } + } } .column-data { display: inline-block; width: 85%; - } - .workspace-channel-details { - margin-top: 0; - color: $primary-fg-color; + > h3 { + margin-top: 0px; + margin-bottom: 0px; + font-size: 16pt; + color: $primary-fg-color; + } - .channel { - margin-left: 15px; + > * { + margin-top: 4px; + margin-bottom: 0; + } + + .workspace-channel-details { + color: $primary-fg-color; + font-weight: 600; + + .channel { + margin-left: 5px; + } + } + + .showMore { + display: block; + text-align: left; + margin-top: 10px; + } + + .metadata { + color: $muted-fg-color; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + margin-bottom: 0; + } + + .metadata.visible { + overflow-y: visible; + text-overflow: ellipsis; + white-space: normal; } } - - .metadata { - color: $muted-fg-color; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - margin-bottom: 0; - } - - .metadata.visible { - overflow-y: visible; - text-overflow: ellipsis; - white-space: normal; - } } diff --git a/src/components/views/settings/BridgeTile.js b/src/components/views/settings/BridgeTile.js index 6902639879..1759c0f58d 100644 --- a/src/components/views/settings/BridgeTile.js +++ b/src/components/views/settings/BridgeTile.js @@ -105,7 +105,7 @@ export default class BridgeTile extends React.PureComponent {

      {creator} {bot}

      - + { this.state.visible ? _t("Show less") : _t("Show more") }
    diff --git a/src/components/views/settings/tabs/room/BridgeSettingsTab.js b/src/components/views/settings/tabs/room/BridgeSettingsTab.js index 12a72ab8f9..d66732de55 100644 --- a/src/components/views/settings/tabs/room/BridgeSettingsTab.js +++ b/src/components/views/settings/tabs/room/BridgeSettingsTab.js @@ -25,6 +25,8 @@ const BRIDGE_EVENT_TYPES = [ // m.bridge ]; +const BRIDGES_LINK = "https://matrix.org/bridges/"; + export default class BridgeSettingsTab extends React.Component { static propTypes = { roomId: PropTypes.string.isRequired, @@ -66,7 +68,7 @@ export default class BridgeSettingsTab extends React.Component { { // TODO: We don't have this link yet: this will prevent the translators // having to re-translate the string when we do. - a: sub => sub, + a: sub => {sub}, }, )}

      @@ -80,7 +82,7 @@ export default class BridgeSettingsTab extends React.Component { { // TODO: We don't have this link yet: this will prevent the translators // having to re-translate the string when we do. - a: sub => sub, + a: sub => {sub}, }, )}

      ; } From bfaa9d56fbf368eb3ffb4c985794633fd8014833 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Tue, 28 Jan 2020 16:05:27 +0000 Subject: [PATCH 18/19] prefixes --- res/css/views/dialogs/_RoomSettingsDialogBridges.scss | 2 +- src/components/views/settings/BridgeTile.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/res/css/views/dialogs/_RoomSettingsDialogBridges.scss b/res/css/views/dialogs/_RoomSettingsDialogBridges.scss index d77e019cbf..a1793cc75e 100644 --- a/res/css/views/dialogs/_RoomSettingsDialogBridges.scss +++ b/res/css/views/dialogs/_RoomSettingsDialogBridges.scss @@ -89,7 +89,7 @@ limitations under the License. } } - .showMore { + .mx_showMore { display: block; text-align: left; margin-top: 10px; diff --git a/src/components/views/settings/BridgeTile.js b/src/components/views/settings/BridgeTile.js index 1759c0f58d..5b74e44c9e 100644 --- a/src/components/views/settings/BridgeTile.js +++ b/src/components/views/settings/BridgeTile.js @@ -105,7 +105,7 @@ export default class BridgeTile extends React.PureComponent {

      {creator} {bot}

      - + { this.state.visible ? _t("Show less") : _t("Show more") }
  • From 330b489fd528b1f80bdbdd1ba1de091fec573cf7 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 28 Jan 2020 16:44:30 +0000 Subject: [PATCH 19/19] Switch back to legacy decorators Empirically the build is fine with these, but it is unfortunate that we have to reply on deprecated semantics. TypeScript should help fix this. --- babel.config.js | 2 +- src/utils/replaceableComponent.ts | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/babel.config.js b/babel.config.js index 3c0c3fcb85..d5a97d56ce 100644 --- a/babel.config.js +++ b/babel.config.js @@ -11,7 +11,7 @@ module.exports = { "@babel/preset-react" ], "plugins": [ - ["@babel/plugin-proposal-decorators", {decoratorsBeforeExport: true}], + ["@babel/plugin-proposal-decorators", {legacy: true}], "@babel/plugin-proposal-export-default-from", "@babel/plugin-proposal-numeric-separator", "@babel/plugin-proposal-class-properties", diff --git a/src/utils/replaceableComponent.ts b/src/utils/replaceableComponent.ts index 281ff4c1ac..9f617b27f3 100644 --- a/src/utils/replaceableComponent.ts +++ b/src/utils/replaceableComponent.ts @@ -32,13 +32,9 @@ import * as sdk from '../index'; * with a skinned version. If no skinned version is available, this component * will be used. */ -export function replaceableComponent(name: string) { +export function replaceableComponent(name: string, origComponent: React.Component) { // Decorators return a function to override the class (origComponent). This // ultimately assumes that `getComponent()` won't throw an error and instead // return a falsey value like `null` when the skin doesn't have a component. - return (origComponent) => { - const c = sdk.getComponent(name) || origComponent; - c.kind = "class"; // appeases babel - return c; - }; + return () => sdk.getComponent(name) || origComponent; }