diff --git a/res/css/_components.scss b/res/css/_components.scss
index 2b513474c9..51087eba62 100644
--- a/res/css/_components.scss
+++ b/res/css/_components.scss
@@ -68,6 +68,7 @@
@import "./views/dialogs/_MessageEditHistoryDialog.scss";
@import "./views/dialogs/_NewSessionReviewDialog.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/_RoomSettingsDialog.scss b/res/css/views/dialogs/_RoomSettingsDialog.scss
index aa66e97f9e..2a4e62f9aa 100644
--- a/res/css/views/dialogs/_RoomSettingsDialog.scss
+++ b/res/css/views/dialogs/_RoomSettingsDialog.scss
@@ -56,16 +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: 5px;
- border-width: 1px 0px;
- border-color: #dee1f3;
- border-style: solid;
-}
-
diff --git a/res/css/views/dialogs/_RoomSettingsDialogBridges.scss b/res/css/views/dialogs/_RoomSettingsDialogBridges.scss
new file mode 100644
index 0000000000..a1793cc75e
--- /dev/null
+++ b/res/css/views/dialogs/_RoomSettingsDialogBridges.scss
@@ -0,0 +1,112 @@
+/*
+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_RoomSettingsDialog_BridgeList {
+ padding: 0;
+
+ .mx_AccessibleButton {
+ display: inline;
+ margin: 0;
+ 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;
+
+ .column-icon {
+ float: left;
+ padding-right: 10px;
+
+ * {
+ border-radius: 5px;
+ border: 1px solid $input-darker-bg-color;
+ }
+
+ .noProtocolIcon {
+ width: 48px;
+ height: 48px;
+ 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%;
+
+ > h3 {
+ margin-top: 0px;
+ margin-bottom: 0px;
+ font-size: 16pt;
+ color: $primary-fg-color;
+ }
+
+ > * {
+ margin-top: 4px;
+ margin-bottom: 0;
+ }
+
+ .workspace-channel-details {
+ color: $primary-fg-color;
+ font-weight: 600;
+
+ .channel {
+ margin-left: 5px;
+ }
+ }
+
+ .mx_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;
+ }
+ }
+}
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/BridgeTile.js b/src/components/views/settings/BridgeTile.js
new file mode 100644
index 0000000000..5b74e44c9e
--- /dev/null
+++ b/src/components/views/settings/BridgeTile.js
@@ -0,0 +1,114 @@
+/*
+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 {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";
+import {replaceableComponent} from "../../../utils/replaceableComponent";
+
+@replaceableComponent("views.settings.BridgeTile")
+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 id = this.props.ev.getId();
+ const metadataClassname = "metadata" + (this.state.visible ? " visible" : "");
+ return (
+
+ {networkIcon}
+
+
+
{protocolName}
+
+ {_t("Workspace: %(networkName)s", {networkName})}
+ {_t("Channel: %(channelName)s", {channelName})}
+
+
+ {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 19c19d3bc6..d66732de55 100644
--- a/src/components/views/settings/tabs/room/BridgeSettingsTab.js
+++ b/src/components/views/settings/tabs/room/BridgeSettingsTab.js
@@ -18,16 +18,15 @@ 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",
// m.bridge
];
+const BRIDGES_LINK = "https://matrix.org/bridges/";
+
export default class BridgeSettingsTab extends React.Component {
static propTypes = {
roomId: PropTypes.string.isRequired,
@@ -38,99 +37,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: ,
- })}
);
- 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}
-
-
- );
+ return ;
}
static getBridgeStateEvents(roomId) {
@@ -151,14 +58,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 => {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 => {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 7ccce9e7f6..1079b56269 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -524,6 +524,12 @@
"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": "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!",
"Upload new:": "Upload new:",
"No display name": "No display name",
@@ -792,13 +798,9 @@
"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 .",
- "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.",
+ "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",
@@ -1485,7 +1487,6 @@
"Recent Conversations": "Recent Conversations",
"Suggestions": "Suggestions",
"Recently Direct Messaged": "Recently Direct Messaged",
- "Show more": "Show more",
"If you can't find someone, ask them for their username, share your username (%(userId)s) or profile link.": "If you can't find someone, ask them for their username, share your username (%(userId)s) or profile link.",
"Go": "Go",
"If you can't find someone, ask them for their username (e.g. @user:server.com) or share this room.": "If you can't find someone, ask them for their username (e.g. @user:server.com) or share this room.",