Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Enable Display of Bridge Errors #3246

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions res/css/_components.scss
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@
@import "./views/groups/_GroupPublicityToggle.scss";
@import "./views/groups/_GroupRoomList.scss";
@import "./views/groups/_GroupUserSettings.scss";
@import "./views/messages/_BridgeError.scss";
@import "./views/messages/_CreateEvent.scss";
@import "./views/messages/_DateSeparator.scss";
@import "./views/messages/_MEmoteBody.scss";
Expand Down
23 changes: 23 additions & 0 deletions res/css/views/messages/_BridgeError.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
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.
*/

.mx_BridgeError {
background: gray;
color: white;
V02460 marked this conversation as resolved.
Show resolved Hide resolved
border-radius: 0.2em;
padding: 0 0.5em;
font-size: 0.8em;
}
201 changes: 201 additions & 0 deletions src/components/views/messages/BridgeError.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
/*
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 { EventTimeline } from 'matrix-js-sdk';

import { _t, _td } from '../../../languageHandler';


/**
* No-op if str is a string, else returns the empty string.
* @param {Any} str
* @returns {string}
*/
function assureString(str) {
return typeof str === 'string' ? str : "";
}

/**
* No-op if arr is an Array, else returns an empty Array.
* @param {Any} arr
* @returns {Array}
*/
function assureArray(arr) {
return Array.isArray(arr) ? arr : [];
}


export default class BridgeError extends React.PureComponent {
static propTypes = {
mxEvent: PropTypes.object.isRequired,
room: PropTypes.object.isRequired,
};

constructor(props) {
super(props);
// this.state = {}
}

/**
* Returns all bridge error relations for this event.
*
* @returns {MatrixEvent[]}
*/
_getRelations() {
const { mxEvent, room } = this.props;

// const room = matrixClient.getRoom(mxEvent.getRoomId());
const timelineSet = room.getUnfilteredTimelineSet();
const relations = timelineSet.getRelationsForEvent(
mxEvent.getId(),
"m.reference",
"de.nasnotfound.bridge_error",
);

return relations ? relations.getRelations() : [];
}

/**
* Returns a list of all users matched by the regex at the time of the event.
*
* @param {string} regexStr
* @returns {RoomMember[]}
*/
_findMembersFromRegex(regexStr) {
const { room } = this.props;
const regex = new RegExp(regexStr);

// TODO[V02460@gmail.com]: Get room state at the proper point in time
const roomState = room.getLiveTimeline().getState(EventTimeline.FORWARDS);
const members = roomState.getMembers();

return members.filter(m => regex.test(m.userId));
}

/**
* Sanitized infos from a relation.
* @typedef {Object} RelationInfo
* @property {RoomMember[]} affectedUsers
* @property {string} eventID
* @property {string} networkName
* @property {string} reason
*/

/**
* Returns the network name and the affected users for the given relation.
*
* @param {MatrixEvent} relation
* @returns {RelationInfo}
*/
_getRelationInfo(relation) {
if (!relation.event || !relation.event.content) {
return { networkName: "", affectedUsers: [] };
}
const content = relation.event.content;

const affectedUsersRegex = assureArray(content.affected_users);
const affectedUsers = affectedUsersRegex.flatMap(u =>
this._findMembersFromRegex(u),
);

return {
affectedUsers: affectedUsers,
eventID: assureString(content.event_id),
networkName: assureString(content.network_name),
reason: assureString(content.reason),
};
}

_errorMessages = {
"m.event_not_handled": _td(
"⚠ Not delivered to people on %(networkName)s (%(affectedUsers)s)",
),
"m.event_too_old": _td(
"⚠ It took so long. Gave up sending to people on %(networkName)s " +
"(%(affectedUsers)s)",
),
"m.internal_error": _td(
"⚠ Unexpected error while sending to people on %(networkName)s " +
"(%(affectedUsers)s)",
),
"m.foreign_network_error": _td(
"⚠ %(networkName)s did not deliver the message to the people " +
"there (%(affectedUsers)s)",
),
"m.event_unknown": _td(
"⚠ Was not understood by %(networkName)s, so people there didn't " +
V02460 marked this conversation as resolved.
Show resolved Hide resolved
"get this message (%(affectedUsers)s)",
),
}

/**
* Returns an error message for the given reason.
*
* Defaults to a generic message if the reason is unknown.
* @param {string} reason
* @returns {string}
*/
_getErrorMessage(reason) {
return (
this._errorMessages[reason] || this._errorMessages["m.event_not_handled"]
);
}

/**
* Returns the rendered element for the given relation.
*
* @param {RelationInfo} relationInfo
* @return {React.Element<'div'>}
*/
_renderInfo(relationInfo) {
const usernames = relationInfo.affectedUsers.map(u => u.name).join(", ");
const message = _t(
this._getErrorMessage(relationInfo.reason),
{
affectedUsers: usernames,
// count == 0 to add translations for when the network name is missing.
count: relationInfo.networkName ? 1 : 0,
networkName: relationInfo.networkName,
},
);

return (
<div key={ relationInfo.eventID }>
{ message }
</div>
);
}

render() {
V02460 marked this conversation as resolved.
Show resolved Hide resolved
const relations = this._getRelations();
const isBridgeError = !!relations.length;

if (!isBridgeError) {
return null;
}

const relationInfos = relations.map(r => this._getRelationInfo(r));
const renderedInfos = relationInfos.map(r => this._renderInfo(r));

return (
<div className="mx_BridgeError">
{ renderedInfos }
</div>
);
}
}
10 changes: 9 additions & 1 deletion src/components/views/rooms/EventTile.js
Original file line number Diff line number Diff line change
Expand Up @@ -645,6 +645,14 @@ module.exports = withMatrixClient(React.createClass({
const timestamp = this.props.mxEvent.getTs() ?
<MessageTimestamp showTwelveHour={this.props.isTwelveHour} ts={this.props.mxEvent.getTs()} /> : null;

const room = this.props.matrixClient.getRoom(this.props.mxEvent.getRoomId());

const BridgeError = sdk.getComponent('messages.BridgeError');
V02460 marked this conversation as resolved.
Show resolved Hide resolved
const bridgeError = (SettingsStore.isFeatureEnabled("feature_bridge_errors") ?
<BridgeError mxEvent={this.props.mxEvent} room={room} /> :
null
);

const keyRequestHelpText =
<div className="mx_EventTile_keyRequestInfo_tooltip_contents">
<p>
Expand Down Expand Up @@ -690,7 +698,6 @@ module.exports = withMatrixClient(React.createClass({

switch (this.props.tileShape) {
case 'notif': {
const room = this.props.matrixClient.getRoom(this.props.mxEvent.getRoomId());
return (
<div className={classes}>
<div className="mx_EventTile_roomName">
Expand Down Expand Up @@ -800,6 +807,7 @@ module.exports = withMatrixClient(React.createClass({
highlightLink={this.props.highlightLink}
showUrlPreview={this.props.showUrlPreview}
onHeightChanged={this.props.onHeightChanged} />
{ bridgeError }
{ keyRequestInfo }
{ reactionsRow }
{ actionBar }
Expand Down
6 changes: 6 additions & 0 deletions src/i18n/strings/en_EN.json
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@
"Sorry, your homeserver is too old to participate in this room.": "Sorry, your homeserver is too old to participate in this room.",
"Please contact your homeserver administrator.": "Please contact your homeserver administrator.",
"Failed to join room": "Failed to join room",
"Show Bridge Errors": "Show Bridge Errors",
"Message Pinning": "Message Pinning",
"Custom user status messages": "Custom user status messages",
"Group & filter rooms by custom tags (refresh to apply changes)": "Group & filter rooms by custom tags (refresh to apply changes)",
Expand Down Expand Up @@ -916,6 +917,11 @@
"When someone puts a URL in their message, a URL preview can be shown to give more information about that link such as the title, description, and an image from the website.": "When someone puts a URL in their message, a URL preview can be shown to give more information about that link such as the title, description, and an image from the website.",
"Members": "Members",
"Files": "Files",
"⚠ Not delivered to people on %(networkName)s (%(affectedUsers)s)": "⚠ Not delivered to people on %(networkName)s (%(affectedUsers)s)",
"⚠ It took so long. Gave up sending to people on %(networkName)s (%(affectedUsers)s)": "⚠ It took so long. Gave up sending to people on %(networkName)s (%(affectedUsers)s)",
"⚠ Unexpected error while sending to people on %(networkName)s (%(affectedUsers)s)": "⚠ Unexpected error while sending to people on %(networkName)s (%(affectedUsers)s)",
"⚠ %(networkName)s did not deliver the message to the people there (%(affectedUsers)s)": "⚠ %(networkName)s did not deliver the message to the people there (%(affectedUsers)s)",
"⚠ Was not understood by %(networkName)s, so people there didn't get this message (%(affectedUsers)s)": "⚠ Was not understood by %(networkName)s, so people there didn't get this message (%(affectedUsers)s)",
"Sunday": "Sunday",
"Monday": "Monday",
"Tuesday": "Tuesday",
Expand Down
6 changes: 6 additions & 0 deletions src/settings/Settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,12 @@ export const SETTINGS = {
// // not use this for new settings.
// invertedSettingName: "my-negative-setting",
// },
"feature_bridge_errors": {
V02460 marked this conversation as resolved.
Show resolved Hide resolved
isFeature: true,
displayName: _td("Show Bridge Errors"),
supportedLevels: LEVELS_FEATURE,
default: false,
},
"feature_pinning": {
isFeature: true,
displayName: _td("Message Pinning"),
Expand Down