Merge branch 'develop' into bwindels/stylepreviewbar
This commit is contained in:
commit
22874f62ab
25 changed files with 280 additions and 139 deletions
38
src/components/structures/GenericErrorPage.js
Normal file
38
src/components/structures/GenericErrorPage.js
Normal file
|
@ -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 <div className='mx_GenericErrorPage'>
|
||||
<div className='mx_GenericErrorPage_box'>
|
||||
<h1>{_t("Error loading Riot")}</h1>
|
||||
<p>{this.props.message}</p>
|
||||
<p>{_t(
|
||||
"If this is unexpected, please contact your system administrator " +
|
||||
"or technical support representative.",
|
||||
)}</p>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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/<id> 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) {
|
||||
|
|
|
@ -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}
|
||||
/>);
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
||||
/**
|
||||
|
|
|
@ -52,7 +52,7 @@ export default class RoomSettingsDialog extends React.Component {
|
|||
tabs.push(new Tab(
|
||||
_td("Advanced"),
|
||||
"mx_RoomSettingsDialog_warningIcon",
|
||||
<AdvancedRoomSettingsTab roomId={this.props.roomId} />,
|
||||
<AdvancedRoomSettingsTab roomId={this.props.roomId} closeSettingsFn={this.props.onFinished} />,
|
||||
));
|
||||
|
||||
return tabs;
|
||||
|
|
|
@ -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 [
|
||||
<HeaderButton key="groupMembersButton" name="groupMembersButton"
|
||||
title={_t('Members')}
|
||||
isHighlighted={this.isPhase(groupPhases)}
|
||||
clickPhase={RightPanel.Phase.GroupMemberList}
|
||||
isHighlighted={this.isPhase(GROUP_PHASES)}
|
||||
onClick={this._onMembersClicked}
|
||||
analytics={['Right Panel', 'Group Member List Button', 'click']}
|
||||
/>,
|
||||
<HeaderButton key="roomsButton" name="roomsButton"
|
||||
title={_t('Rooms')}
|
||||
isHighlighted={this.isPhase(roomPhases)}
|
||||
clickPhase={RightPanel.Phase.GroupRoomList}
|
||||
isHighlighted={this.isPhase(ROOM_PHASES)}
|
||||
onClick={this._onRoomsClicked}
|
||||
analytics={['Right Panel', 'Group Room List Button', 'click']}
|
||||
/>,
|
||||
];
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 [
|
||||
<HeaderButton key="membersButton" name="membersButton"
|
||||
title={_t('Members')}
|
||||
isHighlighted={this.isPhase(membersPhases)}
|
||||
clickPhase={RightPanel.Phase.RoomMemberList}
|
||||
isHighlighted={this.isPhase(MEMBER_PHASES)}
|
||||
onClick={this._onMembersClicked}
|
||||
analytics={['Right Panel', 'Member List Button', 'click']}
|
||||
/>,
|
||||
<HeaderButton key="filesButton" name="filesButton"
|
||||
title={_t('Files')}
|
||||
isHighlighted={this.isPhase(RightPanel.Phase.FilePanel)}
|
||||
clickPhase={RightPanel.Phase.FilePanel}
|
||||
onClick={this._onFilesClicked}
|
||||
analytics={['Right Panel', 'File List Button', 'click']}
|
||||
/>,
|
||||
<HeaderButton key="notifsButton" name="notifsButton"
|
||||
title={_t('Notifications')}
|
||||
isHighlighted={this.isPhase(RightPanel.Phase.NotificationPanel)}
|
||||
clickPhase={RightPanel.Phase.NotificationPanel}
|
||||
onClick={this._onNotificationsClicked}
|
||||
analytics={['Right Panel', 'Notification List Button', 'click']}
|
||||
/>,
|
||||
];
|
||||
|
|
|
@ -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 (
|
||||
<IndicatorScrollbar ref="scroller" className="mx_RoomBreadcrumbs"
|
||||
trackHorizontalOverflow={true} verticalScrollsHorizontally={true}
|
||||
scrollTolerances={this.state.scrollTolerances}>
|
||||
trackHorizontalOverflow={true} verticalScrollsHorizontally={true}>
|
||||
{ avatars }
|
||||
</IndicatorScrollbar>
|
||||
);
|
||||
|
|
|
@ -187,12 +187,17 @@ export default class KeyBackupPanel extends React.PureComponent {
|
|||
clientBackupStatus = <div>
|
||||
<p>{encryptedMessageAreEncrypted}</p>
|
||||
<p>{_t(
|
||||
"This device is <b>not backing up your keys</b>.", {},
|
||||
"This device is <b>not backing up your keys</b>, " +
|
||||
"but you do have an existing backup you can restore from " +
|
||||
"and add to going forward.", {},
|
||||
{b: sub => <b>{sub}</b>},
|
||||
)}</p>
|
||||
<p>{_t("Back up your keys before signing out to avoid losing them.")}</p>
|
||||
<p>{_t(
|
||||
"Connect this device to key backup before signing out to avoid " +
|
||||
"losing any keys that may only be on this device.",
|
||||
)}</p>
|
||||
</div>;
|
||||
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}
|
||||
</span>;
|
||||
const device = sub => <span className="mx_KeyBackupPanel_deviceName">{deviceName}</span>;
|
||||
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(
|
||||
|
|
|
@ -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 = (
|
||||
<AccessibleButton element='a' onClick={this._onOldRoomClicked}>
|
||||
{_t("View older messages in %(roomName)s.", {roomName: name})}
|
||||
</AccessibleButton>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mx_SettingsTab">
|
||||
<div className="mx_SettingsTab_heading">{_t("Advanced")}</div>
|
||||
|
@ -108,6 +146,7 @@ export default class AdvancedRoomSettingsTab extends React.Component {
|
|||
<span>{_t("Room version:")}</span>
|
||||
{room.getVersion()}
|
||||
</div>
|
||||
{oldRoomLink}
|
||||
{roomUpgradeButton}
|
||||
</div>
|
||||
<div className='mx_SettingsTab_section mx_SettingsTab_subsectionText'>
|
||||
|
|
|
@ -136,7 +136,7 @@ export default class HelpUserSettingsTab extends React.Component {
|
|||
<li>
|
||||
The <a href="themes/riot/img/backgrounds/valley.jpg" rel="noopener" target="_blank">
|
||||
default cover photo</a> is (C)
|
||||
<a href="https://www.flickr.com/golan" rel="noopener" target="_blank">Jesús Roncero</a>
|
||||
<a href="https://www.flickr.com/golan" rel="noopener" target="_blank">Jesús Roncero</a>{' '}
|
||||
used under the terms of
|
||||
<a href="https://creativecommons.org/licenses/by-sa/4.0/" rel="noopener" target="_blank">
|
||||
CC-BY-SA 4.0</a>. No warranties are given.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue