diff --git a/res/css/views/dialogs/_RoomSettingsDialog.scss b/res/css/views/dialogs/_RoomSettingsDialog.scss index 08839d8493..aa66e97f9e 100644 --- a/res/css/views/dialogs/_RoomSettingsDialog.scss +++ b/res/css/views/dialogs/_RoomSettingsDialog.scss @@ -29,6 +29,11 @@ limitations under the License. mask-image: url('$(res)/img/feather-customised/users-sm.svg'); } +.mx_RoomSettingsDialog_bridgesIcon::before { + // This icon is pants, please improve :) + mask-image: url('$(res)/img/feather-customised/bridge.svg'); +} + .mx_RoomSettingsDialog_warningIcon::before { mask-image: url('$(res)/img/feather-customised/warning-triangle.svg'); } @@ -50,3 +55,17 @@ limitations under the License. mask-size: 36px; 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/img/feather-customised/bridge.svg b/res/img/feather-customised/bridge.svg new file mode 100644 index 0000000000..f8f3468155 --- /dev/null +++ b/res/img/feather-customised/bridge.svg @@ -0,0 +1,50 @@ + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/src/components/views/dialogs/RoomSettingsDialog.js b/src/components/views/dialogs/RoomSettingsDialog.js index 740dc4d2c2..c31fe1992d 100644 --- a/src/components/views/dialogs/RoomSettingsDialog.js +++ b/src/components/views/dialogs/RoomSettingsDialog.js @@ -24,9 +24,11 @@ import RolesRoomSettingsTab from "../settings/tabs/room/RolesRoomSettingsTab"; import GeneralRoomSettingsTab from "../settings/tabs/room/GeneralRoomSettingsTab"; import SecurityRoomSettingsTab from "../settings/tabs/room/SecurityRoomSettingsTab"; import NotificationSettingsTab from "../settings/tabs/room/NotificationSettingsTab"; +import BridgeSettingsTab from "../settings/tabs/room/BridgeSettingsTab"; import sdk from "../../../index"; import MatrixClientPeg from "../../../MatrixClientPeg"; import dis from "../../../dispatcher"; +import SettingsStore from "../../../settings/SettingsStore"; export default class RoomSettingsDialog extends React.Component { static propTypes = { @@ -52,6 +54,9 @@ 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"), @@ -73,6 +78,15 @@ export default class RoomSettingsDialog extends React.Component { "mx_RoomSettingsDialog_rolesIcon", , )); + + if (shouldShowBridgeIcon) { + tabs.push(new Tab( + _td("Bridge Info"), + "mx_RoomSettingsDialog_bridgesIcon", + , + )); + } + tabs.push(new Tab( _td("Advanced"), "mx_RoomSettingsDialog_warningIcon", diff --git a/src/components/views/settings/tabs/room/BridgeSettingsTab.js b/src/components/views/settings/tabs/room/BridgeSettingsTab.js new file mode 100644 index 0000000000..3022885701 --- /dev/null +++ b/src/components/views/settings/tabs/room/BridgeSettingsTab.js @@ -0,0 +1,166 @@ +/* +Copyright 2019 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 {_t} from "../../../../../languageHandler"; +import MatrixClientPeg from "../../../../../MatrixClientPeg"; +import Pill from "../../../elements/Pill"; +import {makeUserPermalink} from "../../../../../utils/permalinks/Permalinks"; +import BaseAvatar from "../../../avatars/BaseAvatar"; +import { ContentRepo } from "matrix-js-sdk"; + +const BRIDGE_EVENT_TYPES = [ + "uk.half-shot.bridge", + // m.bridge +]; + +export default class BridgeSettingsTab extends React.Component { + static propTypes = { + roomId: PropTypes.string.isRequired, + }; + + _renderBridgeCard(event, room) { + const content = event.getContent(); + 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 = ContentRepo.getHttpUriForMxc( + MatrixClientPeg.get().getHomeserverUrl(), + network.avatar, 32, 32, "crop", + ); + networkIcon = ; + } + + let channelIcon = null; + if (channel.avatar) { + const avatarUrl = ContentRepo.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}

    +
    +
    +
  • ); + } + + static getBridgeStateEvents(roomId) { + const client = MatrixClientPeg.get(); + const roomState = (client.getRoom(roomId)).currentState; + + const bridgeEvents = Array.concat(...BRIDGE_EVENT_TYPES.map((typeName) => + Object.values(roomState.events[typeName] || {}), + )); + + return bridgeEvents; + } + + render() { + // This settings tab will only be invoked if the following function returns more + // than 0 events, so no validation is needed at this stage. + const bridgeEvents = BridgeSettingsTab.getBridgeStateEvents(this.props.roomId); + const client = MatrixClientPeg.get(); + const room = client.getRoom(this.props.roomId); + + return ( +
    +
    {_t("Bridge Info")}
    +
    +

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

    +
      + { bridgeEvents.map((event) => this._renderBridgeCard(event, room)) } +
    +
    +
    + ); + } +} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index cac3f2f619..5d32066fd8 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -361,6 +361,7 @@ "New DM invite dialog (under development)": "New DM invite dialog (under development)", "Enable cross-signing to verify per-user instead of per-device (in development)": "Enable cross-signing to verify per-user instead of per-device (in development)", "Enable local event indexing and E2EE search (requires restart)": "Enable local event indexing and E2EE search (requires restart)", + "Show info about bridges in room settings": "Show info about bridges in room settings", "Use the new, faster, composer for writing messages": "Use the new, faster, composer for writing messages", "Enable Emoji suggestions while typing": "Enable Emoji suggestions while typing", "Use compact timeline layout": "Use compact timeline layout", @@ -763,6 +764,13 @@ "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.", "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", diff --git a/src/settings/Settings.js b/src/settings/Settings.js index d606528ca3..1d24e81469 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -155,6 +155,12 @@ export const SETTINGS = { displayName: _td("Enable local event indexing and E2EE search (requires restart)"), default: false, }, + "feature_bridge_state": { + isFeature: true, + supportedLevels: LEVELS_FEATURE, + displayName: _td("Show info about bridges in room settings"), + default: false, + }, "useCiderComposer": { displayName: _td("Use the new, faster, composer for writing messages"), supportedLevels: LEVELS_ACCOUNT_SETTINGS,