Merge pull request #5454 from matrix-org/hs/bridge-info-v2
Various fixes for Bridge Info page (MSC2346)pull/21833/head
						commit
						117d8843c4
					
				|  | @ -89,24 +89,18 @@ limitations under the License. | |||
|             } | ||||
|         } | ||||
| 
 | ||||
|         .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; | ||||
|             padding: 0; | ||||
| 
 | ||||
|             > li { | ||||
|                 padding: 0; | ||||
|                 border: 0; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -1,115 +0,0 @@ | |||
| /* | ||||
| 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"; | ||||
| import SettingsStore from "../../../settings/SettingsStore"; | ||||
| 
 | ||||
| @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 />.", {}, { | ||||
|                     user: <Pill | ||||
|                         type={Pill.TYPE_USER_MENTION} | ||||
|                         room={this.props.room} | ||||
|                         url={makeUserPermalink(content.creator)} | ||||
|                         shouldShowPillAvatar={SettingsStore.getValue("Pill.shouldShowPillAvatar")} | ||||
|                     />, | ||||
|             }); | ||||
|         } | ||||
| 
 | ||||
|         const bot = _t("This bridge is managed by <user />.", {}, { | ||||
|             user: <Pill | ||||
|                 type={Pill.TYPE_USER_MENTION} | ||||
|                 room={this.props.room} | ||||
|                 url={makeUserPermalink(this.props.ev.getSender())} | ||||
|                 shouldShowPillAvatar={SettingsStore.getValue("Pill.shouldShowPillAvatar")} | ||||
|                 />, | ||||
|         }); | ||||
| 
 | ||||
|         let networkIcon; | ||||
| 
 | ||||
|         if (protocol.avatar) { | ||||
|             const avatarUrl = getHttpUriForMxc( | ||||
|                 MatrixClientPeg.get().getHomeserverUrl(), | ||||
|                 protocol.avatar, 64, 64, "crop", | ||||
|             ); | ||||
| 
 | ||||
|             networkIcon = <BaseAvatar className="protocol-icon" | ||||
|                 width={48} | ||||
|                 height={48} | ||||
|                 resizeMethod='crop' | ||||
|                 name={ protocolName } | ||||
|                 idName={ protocolName } | ||||
|                 url={ avatarUrl } | ||||
|             />; | ||||
|         } else { | ||||
|             networkIcon = <div class="noProtocolIcon"></div>; | ||||
|         } | ||||
| 
 | ||||
|         const id = this.props.ev.getId(); | ||||
|         const metadataClassname = "metadata" + (this.state.visible ? " visible" : ""); | ||||
|         return (<li key={id}> | ||||
|             <div className="column-icon"> | ||||
|                 {networkIcon} | ||||
|             </div> | ||||
|             <div className="column-data"> | ||||
|                 <h3>{protocolName}</h3> | ||||
|                 <p className="workspace-channel-details"> | ||||
|                     <span>{_t("Workspace: %(networkName)s", {networkName})}</span> | ||||
|                     <span className="channel">{_t("Channel: %(channelName)s", {channelName})}</span> | ||||
|                 </p> | ||||
|                 <p className={metadataClassname}> | ||||
|                     {creator} {bot} | ||||
|                 </p> | ||||
|                 <AccessibleButton className="mx_showMore" kind="secondary" onClick={this._toggleVisible.bind(this)}> | ||||
|                     { this.state.visible ? _t("Show less") : _t("Show more") } | ||||
|                 </AccessibleButton> | ||||
|             </div> | ||||
|         </li>); | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,167 @@ | |||
| /* | ||||
| 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 SettingsStore from "../../../settings/SettingsStore"; | ||||
| import {MatrixEvent} from "matrix-js-sdk/src/models/event"; | ||||
| import { Room } from "matrix-js-sdk/src/models/room"; | ||||
| import { isUrlPermitted } from '../../../HtmlUtils'; | ||||
| 
 | ||||
| interface IProps { | ||||
|     ev: MatrixEvent; | ||||
|     room: Room; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * This should match https://github.com/matrix-org/matrix-doc/blob/hs/msc-bridge-inf/proposals/2346-bridge-info-state-event.md#mbridge
 | ||||
|  */ | ||||
| interface IBridgeStateEvent { | ||||
|     bridgebot: string; | ||||
|     creator?: string; | ||||
|     protocol: { | ||||
|         id: string; | ||||
|         displayname?: string; | ||||
|         // eslint-disable-next-line camelcase
 | ||||
|         avatar_url?: string; | ||||
|         // eslint-disable-next-line camelcase
 | ||||
|         external_url?: string; | ||||
|     }; | ||||
|     network?: { | ||||
|         id: string; | ||||
|         displayname?: string; | ||||
|         // eslint-disable-next-line camelcase
 | ||||
|         avatar_url?: string; | ||||
|         // eslint-disable-next-line camelcase
 | ||||
|         external_url?: string; | ||||
|     }; | ||||
|     channel: { | ||||
|         id: string; | ||||
|         displayname?: string; | ||||
|         // eslint-disable-next-line camelcase
 | ||||
|         avatar_url?: string; | ||||
|         // eslint-disable-next-line camelcase
 | ||||
|         external_url?: string; | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| export default class BridgeTile extends React.PureComponent<IProps> { | ||||
|     static propTypes = { | ||||
|         ev: PropTypes.object.isRequired, | ||||
|         room: PropTypes.object.isRequired, | ||||
|     } | ||||
| 
 | ||||
|     render() { | ||||
|         const content: IBridgeStateEvent = this.props.ev.getContent(); | ||||
|         // Validate
 | ||||
|         if (!content.channel?.id || !content.protocol?.id) { | ||||
|             console.warn(`Bridge info event ${this.props.ev.getId()} has missing content. Tile will not render`); | ||||
|             return null; | ||||
|         } | ||||
|         if (!content.bridgebot) { | ||||
|             // Bridgebot was not required previously, so in order to not break rooms we are allowing
 | ||||
|             // the sender to be used in place. When the proposal is merged, this should be removed.
 | ||||
|             console.warn(`Bridge info event ${this.props.ev.getId()} does not provide a 'bridgebot' key which` | ||||
|              + "is deprecated behaviour. Using sender for now."); | ||||
|             content.bridgebot = this.props.ev.getSender(); | ||||
|         } | ||||
|         const { channel, network, protocol } = content; | ||||
|         const protocolName = protocol.displayname || protocol.id; | ||||
|         const channelName = channel.displayname || channel.id; | ||||
| 
 | ||||
|         let creator = null; | ||||
|         if (content.creator) { | ||||
|             creator = <li>{_t("This bridge was provisioned by <user />.", {}, { | ||||
|                 user: () => <Pill | ||||
|                     type={Pill.TYPE_USER_MENTION} | ||||
|                     room={this.props.room} | ||||
|                     url={makeUserPermalink(content.creator)} | ||||
|                     shouldShowPillAvatar={SettingsStore.getValue("Pill.shouldShowPillAvatar")} | ||||
|                 />, | ||||
|             })}</li>; | ||||
|         } | ||||
| 
 | ||||
|         const bot = <li>{_t("This bridge is managed by <user />.", {}, { | ||||
|             user: () => <Pill | ||||
|                 type={Pill.TYPE_USER_MENTION} | ||||
|                 room={this.props.room} | ||||
|                 url={makeUserPermalink(content.bridgebot)} | ||||
|                 shouldShowPillAvatar={SettingsStore.getValue("Pill.shouldShowPillAvatar")} | ||||
|             />, | ||||
|         })}</li>; | ||||
| 
 | ||||
|         let networkIcon; | ||||
| 
 | ||||
|         if (protocol.avatar_url) { | ||||
|             const avatarUrl = getHttpUriForMxc( | ||||
|                 MatrixClientPeg.get().getHomeserverUrl(), | ||||
|                 protocol.avatar_url, 64, 64, "crop", | ||||
|             ); | ||||
| 
 | ||||
|             networkIcon = <BaseAvatar className="protocol-icon" | ||||
|                 width={48} | ||||
|                 height={48} | ||||
|                 resizeMethod='crop' | ||||
|                 name={ protocolName } | ||||
|                 idName={ protocolName } | ||||
|                 url={ avatarUrl } | ||||
|             />; | ||||
|         } else { | ||||
|             networkIcon = <div className="noProtocolIcon"></div>; | ||||
|         } | ||||
|         let networkItem = null; | ||||
|         if (network) { | ||||
|             const networkName = network.displayname || network.id; | ||||
|             let networkLink = <span>{networkName}</span>; | ||||
|             if (typeof network.external_url === "string" && isUrlPermitted(network.external_url)) { | ||||
|                 networkLink = <a href={network.external_url} target="_blank" rel="noreferrer noopener">{networkName}</a> | ||||
|             } | ||||
|             networkItem = _t("Workspace: <networkLink/>", {}, { | ||||
|                 networkLink: () => networkLink, | ||||
|             }); | ||||
|         } | ||||
| 
 | ||||
|         let channelLink = <span>{channelName}</span>; | ||||
|         if (typeof channel.external_url === "string" && isUrlPermitted(channel.external_url)) { | ||||
|             channelLink = <a href={channel.external_url} target="_blank" rel="noreferrer noopener">{channelName}</a> | ||||
|         } | ||||
| 
 | ||||
|         const id = this.props.ev.getId(); | ||||
|         return (<li key={id}> | ||||
|             <div className="column-icon"> | ||||
|                 {networkIcon} | ||||
|             </div> | ||||
|             <div className="column-data"> | ||||
|                 <h3>{protocolName}</h3> | ||||
|                 <p className="workspace-channel-details"> | ||||
|                     {networkItem} | ||||
|                     <span className="channel">{_t("Channel: <channelLink/>", {}, { | ||||
|                         channelLink: () => channelLink, | ||||
|                     })}</span> | ||||
|                 </p> | ||||
|                 <ul className="metadata"> | ||||
|                     {creator} {bot} | ||||
|                 </ul> | ||||
|             </div> | ||||
|         </li>); | ||||
|     } | ||||
| } | ||||
|  | @ -965,10 +965,8 @@ | |||
|     "Upload": "Upload", | ||||
|     "This bridge was provisioned by <user />.": "This bridge was provisioned by <user />.", | ||||
|     "This bridge is managed by <user />.": "This bridge is managed by <user />.", | ||||
|     "Workspace: %(networkName)s": "Workspace: %(networkName)s", | ||||
|     "Channel: %(channelName)s": "Channel: %(channelName)s", | ||||
|     "Show less": "Show less", | ||||
|     "Show more": "Show more", | ||||
|     "Workspace: <networkLink/>": "Workspace: <networkLink/>", | ||||
|     "Channel: <channelLink/>": "Channel: <channelLink/>", | ||||
|     "Failed to upload profile picture!": "Failed to upload profile picture!", | ||||
|     "Upload new:": "Upload new:", | ||||
|     "No display name": "No display name", | ||||
|  | @ -1532,6 +1530,7 @@ | |||
|     "Jump to first invite.": "Jump to first invite.", | ||||
|     "Show %(count)s more|other": "Show %(count)s more", | ||||
|     "Show %(count)s more|one": "Show %(count)s more", | ||||
|     "Show less": "Show less", | ||||
|     "Use default": "Use default", | ||||
|     "All messages": "All messages", | ||||
|     "Mentions & Keywords": "Mentions & Keywords", | ||||
|  | @ -1594,6 +1593,7 @@ | |||
|     "New published address (e.g. #alias:server)": "New published address (e.g. #alias:server)", | ||||
|     "Local Addresses": "Local Addresses", | ||||
|     "Set addresses for this room so users can find this room through your homeserver (%(localDomain)s)": "Set addresses for this room so users can find this room through your homeserver (%(localDomain)s)", | ||||
|     "Show more": "Show more", | ||||
|     "Error updating flair": "Error updating flair", | ||||
|     "There was an error updating the flair for this room. The server may not allow it or a temporary error occurred.": "There was an error updating the flair for this room. The server may not allow it or a temporary error occurred.", | ||||
|     "Invalid community ID": "Invalid community ID", | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Will Hunt
						Will Hunt