diff --git a/.buildkite/pipeline.yaml b/.buildkite/pipeline.yaml
index 7cb859234e..60964de469 100644
--- a/.buildkite/pipeline.yaml
+++ b/.buildkite/pipeline.yaml
@@ -9,6 +9,10 @@ steps:
image: "node:10"
- label: ":chains: End-to-End Tests"
+ agents:
+ # We use a medium sized instance instead of the normal small ones because
+ # e2e tests otherwise take +-8min
+ queue: "medium"
command:
# TODO: Remove hacky chmod for BuildKite
- "echo '--- Setup'"
diff --git a/res/css/_common.scss b/res/css/_common.scss
index 9ed5c65d4a..d3cf1921e0 100644
--- a/res/css/_common.scss
+++ b/res/css/_common.scss
@@ -36,6 +36,12 @@ body {
color: $warning-color;
}
+b {
+ // On Firefox, the default weight for `` is `bolder` which results in no bold
+ // effect since we only have specific weights of our fonts available.
+ font-weight: bold;
+}
+
h2 {
color: $primary-fg-color;
font-weight: 400;
diff --git a/res/css/_components.scss b/res/css/_components.scss
index 25022acd2e..1f896d270d 100644
--- a/res/css/_components.scss
+++ b/res/css/_components.scss
@@ -6,6 +6,7 @@
@import "./structures/_CreateRoom.scss";
@import "./structures/_CustomRoomTagPanel.scss";
@import "./structures/_FilePanel.scss";
+@import "./structures/_GenericErrorPage.scss";
@import "./structures/_GroupView.scss";
@import "./structures/_HeaderButtons.scss";
@import "./structures/_HomePage.scss";
diff --git a/res/css/structures/_GenericErrorPage.scss b/res/css/structures/_GenericErrorPage.scss
new file mode 100644
index 0000000000..9c973af411
--- /dev/null
+++ b/res/css/structures/_GenericErrorPage.scss
@@ -0,0 +1,19 @@
+.mx_GenericErrorPage {
+ width: 100%;
+ height: 100%;
+ background-color: #fff;
+}
+
+.mx_GenericErrorPage_box {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ margin: auto;
+ width: 500px;
+ height: 200px;
+ border: 1px solid #f22;
+ padding: 10px;
+ background-color: #fcc;
+}
diff --git a/res/css/views/settings/tabs/_SettingsTab.scss b/res/css/views/settings/tabs/_SettingsTab.scss
index 88735f99b2..def28bfbd2 100644
--- a/res/css/views/settings/tabs/_SettingsTab.scss
+++ b/res/css/views/settings/tabs/_SettingsTab.scss
@@ -72,3 +72,7 @@ limitations under the License.
// give them more visual distinction between the sections.
margin-top: 30px;
}
+
+.mx_SettingsTab a {
+ color: $accent-color-alt;
+}
\ No newline at end of file
diff --git a/src/ContentMessages.js b/src/ContentMessages.js
index bb0852d3d6..ee3e8f1390 100644
--- a/src/ContentMessages.js
+++ b/src/ContentMessages.js
@@ -492,6 +492,9 @@ export default class ContentMessages {
this.inprogress.push(upload);
dis.dispatch({action: 'upload_started'});
+ // Focus the composer view
+ dis.dispatch({action: 'focus_composer'});
+
let error;
function onProgress(ev) {
diff --git a/src/SdkConfig.js b/src/SdkConfig.js
index 78dd050a1e..eb18dad453 100644
--- a/src/SdkConfig.js
+++ b/src/SdkConfig.js
@@ -41,6 +41,12 @@ class SdkConfig {
static unset() {
global.mxReactSdkConfig = undefined;
}
+
+ static add(cfg) {
+ const liveConfig = SdkConfig.get();
+ const newConfig = Object.assign({}, liveConfig, cfg);
+ SdkConfig.put(newConfig);
+ }
}
module.exports = SdkConfig;
diff --git a/src/SlashCommands.js b/src/SlashCommands.js
index 8db9c4400d..f72ba1e005 100644
--- a/src/SlashCommands.js
+++ b/src/SlashCommands.js
@@ -155,7 +155,7 @@ export const CommandMap = {
{_t(
"Please confirm that you'd like to go forward with upgrading this room " +
- "from to ",
+ "from to .",
{},
{
oldVersion: () => {room ? room.getVersion() : "1"}
,
diff --git a/src/components/structures/GenericErrorPage.js b/src/components/structures/GenericErrorPage.js
new file mode 100644
index 0000000000..f28f66064c
--- /dev/null
+++ b/src/components/structures/GenericErrorPage.js
@@ -0,0 +1,38 @@
+/*
+Copyright 2019 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 {_t} from "../../languageHandler";
+
+export default class GenericErrorPage extends React.PureComponent {
+ static propTypes = {
+ message: PropTypes.string.isRequired,
+ };
+
+ render() {
+ return
+
+
{_t("Error loading Riot")}
+
{this.props.message}
+
{_t(
+ "If this is unexpected, please contact your system administrator " +
+ "or technical support representative.",
+ )}
+
+
;
+ }
+}
diff --git a/src/components/structures/IndicatorScrollbar.js b/src/components/structures/IndicatorScrollbar.js
index 2470f9fa11..a615717104 100644
--- a/src/components/structures/IndicatorScrollbar.js
+++ b/src/components/structures/IndicatorScrollbar.js
@@ -29,13 +29,6 @@ export default class IndicatorScrollbar extends React.Component {
// scroll horizontally rather than vertically. This should only be used on components
// with no vertical scroll opportunity.
verticalScrollsHorizontally: PropTypes.bool,
-
- // An object containing 2 numbers: xyThreshold and yReduction. xyThreshold is the amount
- // of horizontal movement required in order to ignore any vertical changes in scroll, and
- // only applies when verticalScrollsHorizontally is true. yReduction is the factor to
- // multiply the vertical delta by when verticalScrollsHorizontally is true. The default
- // behaviour is to have an xyThreshold of infinity and a yReduction of 0.8
- scrollTolerances: PropTypes.object,
};
constructor(props) {
@@ -127,20 +120,19 @@ export default class IndicatorScrollbar extends React.Component {
onMouseWheel = (e) => {
if (this.props.verticalScrollsHorizontally && this._scrollElement) {
- const xyThreshold = this.props.scrollTolerances
- ? this.props.scrollTolerances.xyThreshold
- : Number.MAX_SAFE_INTEGER;
+ // xyThreshold is the amount of horizontal motion required for the component to
+ // ignore the vertical delta in a scroll. Used to stop trackpads from acting in
+ // strange ways. Should be positive.
+ const xyThreshold = 0;
- const yReduction = this.props.scrollTolerances
- ? this.props.scrollTolerances.yReduction
- : 0.8;
+ // yRetention is the factor multiplied by the vertical delta to try and reduce
+ // the harshness of the scroll behaviour. Should be a value between 0 and 1.
+ const yRetention = 1.0;
- // Don't apply vertical motion to horizontal scrolls. This is meant to eliminate
- // trackpads causing excessive scroll motion.
- if (e.deltaX >= xyThreshold) return;
-
- // noinspection JSSuspiciousNameCombination
- this._scrollElement.scrollLeft += e.deltaY * yReduction;
+ if (Math.abs(e.deltaX) < xyThreshold) {
+ // noinspection JSSuspiciousNameCombination
+ this._scrollElement.scrollLeft += e.deltaY * yRetention;
+ }
}
};
diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js
index b9530cdbd6..2a206276dc 100644
--- a/src/components/structures/MatrixChat.js
+++ b/src/components/structures/MatrixChat.js
@@ -565,23 +565,6 @@ export default React.createClass({
},
});
break;
- case 'view_user':
- // FIXME: ugly hack to expand the RightPanel and then re-dispatch.
- if (this.state.collapsedRhs) {
- setTimeout(()=>{
- dis.dispatch({
- action: 'show_right_panel',
- });
- dis.dispatch({
- action: 'view_user',
- member: payload.member,
- });
- }, 0);
- }
- break;
- // different from view_user,
- // this show the user panel outside of the context
- // of a room, like a /user/ url
case 'view_user_info':
this._viewUser(payload.userId);
break;
@@ -1820,7 +1803,7 @@ export default React.createClass({
},
_setPageSubtitle: function(subtitle='') {
- document.title = `Riot ${subtitle}`;
+ document.title = `${SdkConfig.get().brand || 'Riot'} ${subtitle}`;
},
updateStatusIndicator: function(state, prevState) {
diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js
index 85bfd10bcc..96d915e9de 100644
--- a/src/components/structures/RoomView.js
+++ b/src/components/structures/RoomView.js
@@ -272,6 +272,28 @@ module.exports = React.createClass({
return this.state.room ? this.state.room.roomId : this.state.roomId;
},
+ _getPermalinkCreatorForRoom: function(room) {
+ if (!this._permalinkCreators) this._permalinkCreators = {};
+ if (this._permalinkCreators[room.roomId]) return this._permalinkCreators[room.roomId];
+
+ this._permalinkCreators[room.roomId] = new RoomPermalinkCreator(room);
+ if (this.state.room && room.roomId === this.state.room.roomId) {
+ // We want to watch for changes in the creator for the primary room in the view, but
+ // don't need to do so for search results.
+ this._permalinkCreators[room.roomId].start();
+ } else {
+ this._permalinkCreators[room.roomId].load();
+ }
+ return this._permalinkCreators[room.roomId];
+ },
+
+ _stopAllPermalinkCreators: function() {
+ if (!this._permalinkCreators) return;
+ for (const roomId of Object.keys(this._permalinkCreators)) {
+ this._permalinkCreators[roomId].stop();
+ }
+ },
+
_onWidgetEchoStoreUpdate: function() {
this.setState({
showApps: this._shouldShowApps(this.state.room),
@@ -436,9 +458,7 @@ module.exports = React.createClass({
}
// stop tracking room changes to format permalinks
- if (this.state.permalinkCreator) {
- this.state.permalinkCreator.stop();
- }
+ this._stopAllPermalinkCreators();
if (this.refs.roomView) {
// disconnect the D&D event listeners from the room view. This
@@ -651,11 +671,6 @@ module.exports = React.createClass({
this._loadMembersIfJoined(room);
this._calculateRecommendedVersion(room);
this._updateE2EStatus(room);
- if (!this.state.permalinkCreator) {
- const permalinkCreator = new RoomPermalinkCreator(room);
- permalinkCreator.start();
- this.setState({permalinkCreator});
- }
},
_calculateRecommendedVersion: async function(room) {
@@ -1169,6 +1184,7 @@ module.exports = React.createClass({
const mxEv = result.context.getEvent();
const roomId = mxEv.getRoomId();
+ const room = cli.getRoom(roomId);
if (!EventTile.haveTileForEvent(mxEv)) {
// XXX: can this ever happen? It will make the result count
@@ -1178,7 +1194,6 @@ module.exports = React.createClass({
if (this.state.searchScope === 'All') {
if (roomId != lastRoomId) {
- const room = cli.getRoom(roomId);
// XXX: if we've left the room, we might not know about
// it. We should tell the js sdk to go and find out about
@@ -1199,7 +1214,7 @@ module.exports = React.createClass({
searchResult={result}
searchHighlights={this.state.searchHighlights}
resultLink={resultLink}
- permalinkCreator={this.state.permalinkCreator}
+ permalinkCreator={this._getPermalinkCreatorForRoom(room)}
onHeightChanged={onHeightChanged} />);
}
return ret;
@@ -1715,7 +1730,7 @@ module.exports = React.createClass({
disabled={this.props.disabled}
showApps={this.state.showApps}
e2eStatus={this.state.e2eStatus}
- permalinkCreator={this.state.permalinkCreator}
+ permalinkCreator={this._getPermalinkCreatorForRoom(this.state.room)}
/>;
}
@@ -1812,7 +1827,7 @@ module.exports = React.createClass({
showUrlPreview = {this.state.showUrlPreview}
className="mx_RoomView_messagePanel"
membersLoaded={this.state.membersLoaded}
- permalinkCreator={this.state.permalinkCreator}
+ permalinkCreator={this._getPermalinkCreatorForRoom(this.state.room)}
resizeNotifier={this.props.resizeNotifier}
/>);
diff --git a/src/components/structures/auth/Login.js b/src/components/structures/auth/Login.js
index 61b55b49d6..2940346a4f 100644
--- a/src/components/structures/auth/Login.js
+++ b/src/components/structures/auth/Login.js
@@ -42,7 +42,12 @@ const PHASES_ENABLED = true;
// These are used in several places, and come from the js-sdk's autodiscovery
// stuff. We define them here so that they'll be picked up by i18n.
_td("Invalid homeserver discovery response");
+_td("Failed to get autodiscovery configuration from server");
+_td("Invalid base_url for m.homeserver");
+_td("Homeserver URL does not appear to be a valid Matrix homeserver");
_td("Invalid identity server discovery response");
+_td("Invalid base_url for m.identity_server");
+_td("Identity server URL does not appear to be a valid identity server");
_td("General failure");
/**
diff --git a/src/components/views/dialogs/RoomSettingsDialog.js b/src/components/views/dialogs/RoomSettingsDialog.js
index 22b7857264..05ed262078 100644
--- a/src/components/views/dialogs/RoomSettingsDialog.js
+++ b/src/components/views/dialogs/RoomSettingsDialog.js
@@ -52,7 +52,7 @@ export default class RoomSettingsDialog extends React.Component {
tabs.push(new Tab(
_td("Advanced"),
"mx_RoomSettingsDialog_warningIcon",
- ,
+ ,
));
return tabs;
diff --git a/src/components/views/right_panel/GroupHeaderButtons.js b/src/components/views/right_panel/GroupHeaderButtons.js
index 13379d49e3..ec14331ad2 100644
--- a/src/components/views/right_panel/GroupHeaderButtons.js
+++ b/src/components/views/right_panel/GroupHeaderButtons.js
@@ -19,23 +19,30 @@ limitations under the License.
import React from 'react';
import { _t } from '../../../languageHandler';
-import dis from '../../../dispatcher';
import HeaderButton from './HeaderButton';
import HeaderButtons from './HeaderButtons';
import RightPanel from '../../structures/RightPanel';
+const GROUP_PHASES = [
+ RightPanel.Phase.GroupMemberInfo,
+ RightPanel.Phase.GroupMemberList,
+];
+const ROOM_PHASES = [
+ RightPanel.Phase.GroupRoomList,
+ RightPanel.Phase.GroupRoomInfo,
+];
+
export default class GroupHeaderButtons extends HeaderButtons {
constructor(props) {
super(props, RightPanel.Phase.GroupMemberList);
+ this._onMembersClicked = this._onMembersClicked.bind(this);
+ this._onRoomsClicked = this._onRoomsClicked.bind(this);
}
onAction(payload) {
super.onAction(payload);
if (payload.action === "view_user") {
- dis.dispatch({
- action: 'show_right_panel',
- });
if (payload.member) {
this.setPhase(RightPanel.Phase.RoomMemberInfo, {member: payload.member});
} else {
@@ -54,27 +61,26 @@ export default class GroupHeaderButtons extends HeaderButtons {
}
}
- renderButtons() {
- const groupPhases = [
- RightPanel.Phase.GroupMemberInfo,
- RightPanel.Phase.GroupMemberList,
- ];
- const roomPhases = [
- RightPanel.Phase.GroupRoomList,
- RightPanel.Phase.GroupRoomInfo,
- ];
+ _onMembersClicked() {
+ this.togglePhase(RightPanel.Phase.GroupMemberList, GROUP_PHASES);
+ }
+ _onRoomsClicked() {
+ this.togglePhase(RightPanel.Phase.GroupRoomList, ROOM_PHASES);
+ }
+
+ renderButtons() {
return [
,
,
];
diff --git a/src/components/views/right_panel/HeaderButton.js b/src/components/views/right_panel/HeaderButton.js
index cc78373117..2c1e15898e 100644
--- a/src/components/views/right_panel/HeaderButton.js
+++ b/src/components/views/right_panel/HeaderButton.js
@@ -20,7 +20,6 @@ limitations under the License.
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
-import dis from '../../../dispatcher';
import Analytics from '../../../Analytics';
import AccessibleButton from '../elements/AccessibleButton';
@@ -32,11 +31,7 @@ export default class HeaderButton extends React.Component {
onClick(ev) {
Analytics.trackEvent(...this.props.analytics);
- dis.dispatch({
- action: 'view_right_panel_phase',
- phase: this.props.clickPhase,
- fromHeader: true,
- });
+ this.props.onClick();
}
render() {
@@ -59,9 +54,8 @@ export default class HeaderButton extends React.Component {
HeaderButton.propTypes = {
// Whether this button is highlighted
isHighlighted: PropTypes.bool.isRequired,
- // The phase to swap to when the button is clicked
- clickPhase: PropTypes.string.isRequired,
-
+ // click handler
+ onClick: PropTypes.func.isRequired,
// The badge to display above the icon
badge: PropTypes.node,
// The parameters to track the click event
diff --git a/src/components/views/right_panel/HeaderButtons.js b/src/components/views/right_panel/HeaderButtons.js
index b7155b922f..2fa9935ab8 100644
--- a/src/components/views/right_panel/HeaderButtons.js
+++ b/src/components/views/right_panel/HeaderButtons.js
@@ -40,14 +40,36 @@ export default class HeaderButtons extends React.Component {
dis.unregister(this.dispatcherRef);
}
+ componentDidUpdate(prevProps) {
+ if (!prevProps.collapsedRhs && this.props.collapsedRhs) {
+ this.setState({
+ phase: null,
+ });
+ }
+ }
+
setPhase(phase, extras) {
- // TODO: delay?
+ if (this.props.collapsedRhs) {
+ dis.dispatch({
+ action: 'show_right_panel',
+ });
+ }
dis.dispatch(Object.assign({
action: 'view_right_panel_phase',
phase: phase,
}, extras));
}
+ togglePhase(phase, validPhases = [phase]) {
+ if (validPhases.includes(this.state.phase)) {
+ dis.dispatch({
+ action: 'hide_right_panel',
+ });
+ } else {
+ this.setPhase(phase);
+ }
+ }
+
isPhase(phases) {
if (this.props.collapsedRhs) {
return false;
@@ -61,28 +83,9 @@ export default class HeaderButtons extends React.Component {
onAction(payload) {
if (payload.action === "view_right_panel_phase") {
- // only actions coming from header buttons should collapse the right panel
- if (this.state.phase === payload.phase && payload.fromHeader) {
- dis.dispatch({
- action: 'hide_right_panel',
- });
- this.setState({
- phase: null,
- });
- } else {
- if (this.props.collapsedRhs && payload.fromHeader) {
- dis.dispatch({
- action: 'show_right_panel',
- });
- // emit payload again as the RightPanel didn't exist up
- // till show_right_panel, just without the fromHeader flag
- // as that would hide the right panel again
- dis.dispatch(Object.assign({}, payload, {fromHeader: false}));
- }
- this.setState({
- phase: payload.phase,
- });
- }
+ this.setState({
+ phase: payload.phase,
+ });
}
}
diff --git a/src/components/views/right_panel/RoomHeaderButtons.js b/src/components/views/right_panel/RoomHeaderButtons.js
index 51e436bfc2..950fa30e38 100644
--- a/src/components/views/right_panel/RoomHeaderButtons.js
+++ b/src/components/views/right_panel/RoomHeaderButtons.js
@@ -19,28 +19,33 @@ limitations under the License.
import React from 'react';
import { _t } from '../../../languageHandler';
-import dis from '../../../dispatcher';
import HeaderButton from './HeaderButton';
import HeaderButtons from './HeaderButtons';
import RightPanel from '../../structures/RightPanel';
+const MEMBER_PHASES = [
+ RightPanel.Phase.RoomMemberList,
+ RightPanel.Phase.RoomMemberInfo,
+ RightPanel.Phase.Room3pidMemberInfo,
+];
+
export default class RoomHeaderButtons extends HeaderButtons {
constructor(props) {
super(props, RightPanel.Phase.RoomMemberList);
+ this._onMembersClicked = this._onMembersClicked.bind(this);
+ this._onFilesClicked = this._onFilesClicked.bind(this);
+ this._onNotificationsClicked = this._onNotificationsClicked.bind(this);
}
onAction(payload) {
super.onAction(payload);
if (payload.action === "view_user") {
- dis.dispatch({
- action: 'show_right_panel',
- });
if (payload.member) {
this.setPhase(RightPanel.Phase.RoomMemberInfo, {member: payload.member});
} else {
this.setPhase(RightPanel.Phase.RoomMemberList);
}
- } else if (payload.action === "view_room") {
+ } else if (payload.action === "view_room" && !this.props.collapsedRhs) {
this.setPhase(RightPanel.Phase.RoomMemberList);
} else if (payload.action === "view_3pid_invite") {
if (payload.event) {
@@ -51,30 +56,36 @@ export default class RoomHeaderButtons extends HeaderButtons {
}
}
- renderButtons() {
- const membersPhases = [
- RightPanel.Phase.RoomMemberList,
- RightPanel.Phase.RoomMemberInfo,
- RightPanel.Phase.Room3pidMemberInfo,
- ];
+ _onMembersClicked() {
+ this.togglePhase(RightPanel.Phase.RoomMemberList, MEMBER_PHASES);
+ }
+ _onFilesClicked() {
+ this.togglePhase(RightPanel.Phase.FilePanel);
+ }
+
+ _onNotificationsClicked() {
+ this.togglePhase(RightPanel.Phase.NotificationPanel);
+ }
+
+ renderButtons() {
return [
,
,
,
];
diff --git a/src/components/views/rooms/RoomBreadcrumbs.js b/src/components/views/rooms/RoomBreadcrumbs.js
index 7c5947e049..fff1fa7f3c 100644
--- a/src/components/views/rooms/RoomBreadcrumbs.js
+++ b/src/components/views/rooms/RoomBreadcrumbs.js
@@ -33,12 +33,7 @@ const MAX_ROOMS = 20;
export default class RoomBreadcrumbs extends React.Component {
constructor(props) {
super(props);
-
- const tolerances = SettingsStore.getValue("breadcrumb_scroll_tolerances");
- this.state = {rooms: [], scrollTolerances: tolerances};
-
- // Record this for debugging purposes
- console.log("Breadcrumbs scroll tolerances:", tolerances);
+ this.state = {rooms: []};
this.onAction = this.onAction.bind(this);
this._dispatcherRef = null;
@@ -343,8 +338,7 @@ export default class RoomBreadcrumbs extends React.Component {
});
return (
+ trackHorizontalOverflow={true} verticalScrollsHorizontally={true}>
{ avatars }
);
diff --git a/src/components/views/settings/KeyBackupPanel.js b/src/components/views/settings/KeyBackupPanel.js
index 7667e11d46..2ba05a0e6b 100644
--- a/src/components/views/settings/KeyBackupPanel.js
+++ b/src/components/views/settings/KeyBackupPanel.js
@@ -187,12 +187,17 @@ export default class KeyBackupPanel extends React.PureComponent {
clientBackupStatus =
{encryptedMessageAreEncrypted}
{_t(
- "This device is not backing up your keys.", {},
+ "This device is not backing up your keys, " +
+ "but you do have an existing backup you can restore from " +
+ "and add to going forward.", {},
{b: sub => {sub}},
)}
-
{_t("Back up your keys before signing out to avoid losing them.")}
+
{_t(
+ "Connect this device to key backup before signing out to avoid " +
+ "losing any keys that may only be on this device.",
+ )}
;
- restoreButtonCaption = _t("Use key backup");
+ restoreButtonCaption = _t("Connect this device to Key Backup");
}
let uploadStatus;
@@ -221,7 +226,10 @@ export default class KeyBackupPanel extends React.PureComponent {
{sub}
;
const device = sub => {deviceName};
- const fromThisDevice = sig.device.getFingerprint() === MatrixClientPeg.get().getDeviceEd25519Key();
+ const fromThisDevice = (
+ sig.device &&
+ sig.device.getFingerprint() === MatrixClientPeg.get().getDeviceEd25519Key()
+ );
let sigStatus;
if (!sig.device) {
sigStatus = _t(
diff --git a/src/components/views/settings/tabs/room/AdvancedRoomSettingsTab.js b/src/components/views/settings/tabs/room/AdvancedRoomSettingsTab.js
index 20c270fe29..101cd036e5 100644
--- a/src/components/views/settings/tabs/room/AdvancedRoomSettingsTab.js
+++ b/src/components/views/settings/tabs/room/AdvancedRoomSettingsTab.js
@@ -21,10 +21,12 @@ import MatrixClientPeg from "../../../../../MatrixClientPeg";
import sdk from "../../../../..";
import AccessibleButton from "../../../elements/AccessibleButton";
import Modal from "../../../../../Modal";
+import dis from "../../../../../dispatcher";
export default class AdvancedRoomSettingsTab extends React.Component {
static propTypes = {
roomId: PropTypes.string.isRequired,
+ closeSettingsFn: PropTypes.func.isRequired,
};
constructor() {
@@ -41,9 +43,21 @@ export default class AdvancedRoomSettingsTab extends React.Component {
const room = MatrixClientPeg.get().getRoom(this.props.roomId);
room.getRecommendedVersion().then((v) => {
const tombstone = room.currentState.getStateEvents("m.room.tombstone", "");
+
+ const additionalStateChanges = {};
+ const createEvent = room.currentState.getStateEvents("m.room.create", "");
+ const predecessor = createEvent ? createEvent.getContent().predecessor : null;
+ if (predecessor && predecessor.room_id) {
+ additionalStateChanges['oldRoomId'] = predecessor.room_id;
+ additionalStateChanges['oldEventId'] = predecessor.event_id;
+ additionalStateChanges['hasPreviousRoom'] = true;
+ }
+
+
this.setState({
upgraded: tombstone && tombstone.getContent().replacement_room,
upgradeRecommendation: v,
+ ...additionalStateChanges,
});
});
}
@@ -59,6 +73,18 @@ export default class AdvancedRoomSettingsTab extends React.Component {
Modal.createDialog(DevtoolsDialog, {roomId: this.props.roomId});
};
+ _onOldRoomClicked = (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+
+ dis.dispatch({
+ action: 'view_room',
+ room_id: this.state.oldRoomId,
+ event_id: this.state.oldEventId,
+ });
+ this.props.closeSettingsFn();
+ };
+
render() {
const client = MatrixClientPeg.get();
const room = client.getRoom(this.props.roomId);
@@ -91,6 +117,18 @@ export default class AdvancedRoomSettingsTab extends React.Component {
);
}
+ let oldRoomLink;
+ if (this.state.hasPreviousRoom) {
+ let name = _t("this room");
+ const room = MatrixClientPeg.get().getRoom(this.props.roomId);
+ if (room && room.name) name = room.name;
+ oldRoomLink = (
+
+ {_t("View older messages in %(roomName)s.", {roomName: name})}
+
+ );
+ }
+
return (
{_t("Advanced")}
@@ -108,6 +146,7 @@ export default class AdvancedRoomSettingsTab extends React.Component {
{_t("Room version:")}
{room.getVersion()}
+ {oldRoomLink}
{roomUpgradeButton}
diff --git a/src/components/views/settings/tabs/user/HelpUserSettingsTab.js b/src/components/views/settings/tabs/user/HelpUserSettingsTab.js
index e45b0d0389..996618bcca 100644
--- a/src/components/views/settings/tabs/user/HelpUserSettingsTab.js
+++ b/src/components/views/settings/tabs/user/HelpUserSettingsTab.js
@@ -136,7 +136,7 @@ export default class HelpUserSettingsTab extends React.Component {
The
default cover photo is (C)
- Jesús Roncero
+ Jesús Roncero{' '}
used under the terms of
CC-BY-SA 4.0. No warranties are given.
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index 5b80ae731d..2b74b9f8ae 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -142,7 +142,7 @@
"Room upgrades are usually recommended when a room version is considered unstable. Unstable room versions might have bugs, missing features, or security vulnerabilities.": "Room upgrades are usually recommended when a room version is considered unstable. Unstable room versions might have bugs, missing features, or security vulnerabilities.",
"Room upgrades usually only affect server-side processing of the room. If you're having problems with your Riot client, please file an issue with .": "Room upgrades usually only affect server-side processing of the room. If you're having problems with your Riot client, please file an issue with .",
"Warning: Upgrading a room will not automatically migrate room members to the new version of the room. We'll post a link to the new room in the old version of the room - room members will have to click this link to join the new room.": "Warning: Upgrading a room will not automatically migrate room members to the new version of the room. We'll post a link to the new room in the old version of the room - room members will have to click this link to join the new room.",
- "Please confirm that you'd like to go forward with upgrading this room from to ": "Please confirm that you'd like to go forward with upgrading this room from to ",
+ "Please confirm that you'd like to go forward with upgrading this room from to .": "Please confirm that you'd like to go forward with upgrading this room from to .",
"Upgrade": "Upgrade",
"Changes your display nickname": "Changes your display nickname",
"Changes your display nickname in the current room only": "Changes your display nickname in the current room only",
@@ -467,9 +467,9 @@
"Unable to load key backup status": "Unable to load key backup status",
"Restore from Backup": "Restore from Backup",
"This device is backing up your keys. ": "This device is backing up your keys. ",
- "This device is not backing up your keys.": "This device is not backing up your keys.",
- "Back up your keys before signing out to avoid losing them.": "Back up your keys before signing out to avoid losing them.",
- "Use key backup": "Use key backup",
+ "This device is not backing up your keys, but you do have an existing backup you can restore from and add to going forward.": "This device is not backing up your keys, but you do have an existing backup you can restore from and add to going forward.",
+ "Connect this device to key backup before signing out to avoid losing any keys that may only be on this device.": "Connect this device to key backup before signing out to avoid losing any keys that may only be on this device.",
+ "Connect this device to Key Backup": "Connect this device to Key Backup",
"Backing up %(sessionsRemaining)s keys...": "Backing up %(sessionsRemaining)s keys...",
"All keys backed up": "All keys backed up",
"Backup has a signature from unknown device with ID %(deviceId)s.": "Backup has a signature from unknown device with ID %(deviceId)s.",
@@ -485,6 +485,7 @@
"Backup version: ": "Backup version: ",
"Algorithm: ": "Algorithm: ",
"Your keys are not being backed up from this device.": "Your keys are not being backed up from this device.",
+ "Back up your keys before signing out to avoid losing them.": "Back up your keys before signing out to avoid losing them.",
"Start using Key Backup": "Start using Key Backup",
"Error saving email notification preferences": "Error saving email notification preferences",
"An error occurred whilst saving your email notification preferences.": "An error occurred whilst saving your email notification preferences.",
@@ -599,6 +600,8 @@
"Voice & Video": "Voice & Video",
"This room is not accessible by remote Matrix servers": "This room is not accessible by remote Matrix servers",
"Upgrade this room to the recommended room version": "Upgrade this room to the recommended room version",
+ "this room": "this room",
+ "View older messages in %(roomName)s": "View older messages in %(roomName)s",
"Room information": "Room information",
"Internal room ID:": "Internal room ID:",
"Room version": "Room version",
@@ -1347,6 +1350,8 @@
"You must register to use this functionality": "You must register to use this functionality",
"You must join the room to see its files": "You must join the room to see its files",
"There are no visible files in this room": "There are no visible files in this room",
+ "Error loading Riot": "Error loading Riot",
+ "If this is unexpected, please contact your system administrator or technical support representative.": "If this is unexpected, please contact your system administrator or technical support representative.",
"HTML for your community's page
\n\n Use the long description to introduce new members to the community, or distribute\n some important links\n
\n\n You can even use 'img' tags\n
\n": "HTML for your community's page
\n\n Use the long description to introduce new members to the community, or distribute\n some important links\n
\n\n You can even use 'img' tags\n
\n",
"Add rooms to the community summary": "Add rooms to the community summary",
"Which rooms would you like to add to this summary?": "Which rooms would you like to add to this summary?",
@@ -1487,7 +1492,12 @@
"Return to login screen": "Return to login screen",
"Set a new password": "Set a new password",
"Invalid homeserver discovery response": "Invalid homeserver discovery response",
+ "Failed to get autodiscovery configuration from server": "Failed to get autodiscovery configuration from server",
+ "Invalid base_url for m.homeserver": "Invalid base_url for m.homeserver",
+ "Homeserver URL does not appear to be a valid Matrix homeserver": "Homeserver URL does not appear to be a valid Matrix homeserver",
"Invalid identity server discovery response": "Invalid identity server discovery response",
+ "Invalid base_url for m.identity_server": "Invalid base_url for m.identity_server",
+ "Identity server URL does not appear to be a valid identity server": "Identity server URL does not appear to be a valid identity server",
"General failure": "General failure",
"This homeserver does not support login using email address.": "This homeserver does not support login using email address.",
"Please contact your service administrator to continue using this service.": "Please contact your service administrator to continue using this service.",
diff --git a/src/matrix-to.js b/src/matrix-to.js
index bb7ddfbd94..a198bb422e 100644
--- a/src/matrix-to.js
+++ b/src/matrix-to.js
@@ -77,6 +77,7 @@ export class RoomPermalinkCreator {
this._bannedHostsRegexps = null;
this._allowedHostsRegexps = null;
this._serverCandidates = null;
+ this._started = false;
this.onMembership = this.onMembership.bind(this);
this.onRoomState = this.onRoomState.bind(this);
@@ -101,11 +102,17 @@ export class RoomPermalinkCreator {
this.load();
this._room.on("RoomMember.membership", this.onMembership);
this._room.on("RoomState.events", this.onRoomState);
+ this._started = true;
}
stop() {
this._room.removeListener("RoomMember.membership", this.onMembership);
this._room.removeListener("RoomState.events", this.onRoomState);
+ this._started = false;
+ }
+
+ isStarted() {
+ return this._started;
}
forEvent(eventId) {
diff --git a/src/settings/Settings.js b/src/settings/Settings.js
index dcd6f91aef..35baa718b9 100644
--- a/src/settings/Settings.js
+++ b/src/settings/Settings.js
@@ -262,13 +262,6 @@ export const SETTINGS = {
supportedLevels: ['account'],
default: [],
},
- "breadcrumb_scroll_tolerances": {
- supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS,
- default: {
- xyThreshold: 10,
- yReduction: 0.8,
- },
- },
"analyticsOptIn": {
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
displayName: _td('Send analytics data'),