You've already forked matrix-react-sdk
							
							
				mirror of
				https://github.com/matrix-org/matrix-react-sdk.git
				synced 2025-11-04 11:51:45 +03:00 
			
		
		
		
	Merge branch 'develop' into bwindels/stylepreviewbar
This commit is contained in:
		@@ -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'"
 | 
			
		||||
 
 | 
			
		||||
@@ -36,6 +36,12 @@ body {
 | 
			
		||||
    color: $warning-color;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
b {
 | 
			
		||||
    // On Firefox, the default weight for `<b>` 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;
 | 
			
		||||
 
 | 
			
		||||
@@ -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";
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										19
									
								
								res/css/structures/_GenericErrorPage.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								res/css/structures/_GenericErrorPage.scss
									
									
									
									
									
										Normal file
									
								
							@@ -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;
 | 
			
		||||
}
 | 
			
		||||
@@ -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;
 | 
			
		||||
}
 | 
			
		||||
@@ -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) {
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
 
 | 
			
		||||
@@ -155,7 +155,7 @@ export const CommandMap = {
 | 
			
		||||
                            <p>
 | 
			
		||||
                                {_t(
 | 
			
		||||
                                    "Please confirm that you'd like to go forward with upgrading this room " +
 | 
			
		||||
                                    "from <oldVersion /> to <newVersion />",
 | 
			
		||||
                                    "from <oldVersion /> to <newVersion />.",
 | 
			
		||||
                                    {},
 | 
			
		||||
                                    {
 | 
			
		||||
                                        oldVersion: () => <code>{room ? room.getVersion() : "1"}</code>,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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;
 | 
			
		||||
 | 
			
		||||
            // Don't apply vertical motion to horizontal scrolls. This is meant to eliminate
 | 
			
		||||
            // trackpads causing excessive scroll motion.
 | 
			
		||||
            if (e.deltaX >= xyThreshold) return;
 | 
			
		||||
            // 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;
 | 
			
		||||
 | 
			
		||||
            if (Math.abs(e.deltaX) < xyThreshold) {
 | 
			
		||||
                // noinspection JSSuspiciousNameCombination
 | 
			
		||||
            this._scrollElement.scrollLeft += e.deltaY * yReduction;
 | 
			
		||||
                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,30 +83,11 @@ 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,
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    render() {
 | 
			
		||||
        // inline style as this will be swapped around in future commits
 | 
			
		||||
 
 | 
			
		||||
@@ -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.
 | 
			
		||||
 
 | 
			
		||||
@@ -142,7 +142,7 @@
 | 
			
		||||
    "Room upgrades are usually recommended when a room version is considered <i>unstable</i>. Unstable room versions might have bugs, missing features, or security vulnerabilities.": "Room upgrades are usually recommended when a room version is considered <i>unstable</i>. Unstable room versions might have bugs, missing features, or security vulnerabilities.",
 | 
			
		||||
    "Room upgrades usually only affect <i>server-side</i> processing of the room. If you're having problems with your Riot client, please file an issue with <issueLink />.": "Room upgrades usually only affect <i>server-side</i> processing of the room. If you're having problems with your Riot client, please file an issue with <issueLink />.",
 | 
			
		||||
    "<b>Warning</b>: Upgrading a room will <i>not automatically migrate room members to the new version of the room.</i> 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.": "<b>Warning</b>: Upgrading a room will <i>not automatically migrate room members to the new version of the room.</i> 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 <oldVersion /> to <newVersion />": "Please confirm that you'd like to go forward with upgrading this room from <oldVersion /> to <newVersion />",
 | 
			
		||||
    "Please confirm that you'd like to go forward with upgrading this room from <oldVersion /> to <newVersion />.": "Please confirm that you'd like to go forward with upgrading this room from <oldVersion /> to <newVersion />.",
 | 
			
		||||
    "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 <b>not backing up your keys</b>.": "This device is <b>not backing up your keys</b>.",
 | 
			
		||||
    "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 <b>not backing up your keys</b>, but you do have an existing backup you can restore from and add to going forward.": "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.",
 | 
			
		||||
    "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 <verify>unknown</verify> device with ID %(deviceId)s.": "Backup has a signature from <verify>unknown</verify> device with ID %(deviceId)s.",
 | 
			
		||||
@@ -485,6 +485,7 @@
 | 
			
		||||
    "Backup version: ": "Backup version: ",
 | 
			
		||||
    "Algorithm: ": "Algorithm: ",
 | 
			
		||||
    "Your keys are <b>not being backed up from this device</b>.": "Your keys are <b>not being backed up from this device</b>.",
 | 
			
		||||
    "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 <a>register</a> to use this functionality": "You must <a>register</a> 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.",
 | 
			
		||||
    "<h1>HTML for your community's page</h1>\n<p>\n    Use the long description to introduce new members to the community, or distribute\n    some important <a href=\"foo\">links</a>\n</p>\n<p>\n    You can even use 'img' tags\n</p>\n": "<h1>HTML for your community's page</h1>\n<p>\n    Use the long description to introduce new members to the community, or distribute\n    some important <a href=\"foo\">links</a>\n</p>\n<p>\n    You can even use 'img' tags\n</p>\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 <a>contact your service administrator</a> to continue using this service.": "Please <a>contact your service administrator</a> to continue using this service.",
 | 
			
		||||
 
 | 
			
		||||
@@ -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) {
 | 
			
		||||
 
 | 
			
		||||
@@ -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'),
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user