2018-04-12 01:58:04 +03:00
|
|
|
/*
|
|
|
|
Copyright 2016 OpenMarket 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';
|
2018-04-13 02:43:44 +03:00
|
|
|
import MatrixClientPeg from '../../../MatrixClientPeg';
|
2018-04-12 01:58:04 +03:00
|
|
|
import {instanceForInstanceId} from '../../../utils/DirectoryUtils';
|
|
|
|
|
2019-01-11 04:37:28 +03:00
|
|
|
const DEFAULT_ICON_URL = require("../../../../res/img/network-matrix.svg");
|
2018-04-12 01:58:04 +03:00
|
|
|
|
|
|
|
export default class NetworkDropdown extends React.Component {
|
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
|
|
|
|
|
|
|
this.dropdownRootElement = null;
|
|
|
|
this.ignoreEvent = null;
|
|
|
|
|
|
|
|
this.onInputClick = this.onInputClick.bind(this);
|
|
|
|
this.onRootClick = this.onRootClick.bind(this);
|
|
|
|
this.onDocumentClick = this.onDocumentClick.bind(this);
|
|
|
|
this.onMenuOptionClick = this.onMenuOptionClick.bind(this);
|
|
|
|
this.onInputKeyUp = this.onInputKeyUp.bind(this);
|
|
|
|
this.collectRoot = this.collectRoot.bind(this);
|
|
|
|
this.collectInputTextBox = this.collectInputTextBox.bind(this);
|
|
|
|
|
|
|
|
this.inputTextBox = null;
|
|
|
|
|
|
|
|
const server = MatrixClientPeg.getHomeServerName();
|
|
|
|
this.state = {
|
|
|
|
expanded: false,
|
|
|
|
selectedServer: server,
|
2019-02-27 02:25:31 +03:00
|
|
|
selectedInstanceId: null,
|
|
|
|
includeAllNetworks: true,
|
2018-04-12 01:58:04 +03:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
componentWillMount() {
|
|
|
|
// Listen for all clicks on the document so we can close the
|
|
|
|
// menu when the user clicks somewhere else
|
|
|
|
document.addEventListener('click', this.onDocumentClick, false);
|
|
|
|
|
|
|
|
// fire this now so the defaults can be set up
|
2019-02-27 02:25:31 +03:00
|
|
|
const {selectedServer, selectedInstanceId, includeAllNetworks} = this.state;
|
|
|
|
this.props.onOptionChange(selectedServer, selectedInstanceId, includeAllNetworks);
|
2018-04-12 01:58:04 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
componentWillUnmount() {
|
|
|
|
document.removeEventListener('click', this.onDocumentClick, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
componentDidUpdate() {
|
|
|
|
if (this.state.expanded && this.inputTextBox) {
|
|
|
|
this.inputTextBox.focus();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
onDocumentClick(ev) {
|
|
|
|
// Close the dropdown if the user clicks anywhere that isn't
|
|
|
|
// within our root element
|
|
|
|
if (ev !== this.ignoreEvent) {
|
|
|
|
this.setState({
|
|
|
|
expanded: false,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
onRootClick(ev) {
|
|
|
|
// This captures any clicks that happen within our elements,
|
|
|
|
// such that we can then ignore them when they're seen by the
|
|
|
|
// click listener on the document handler, ie. not close the
|
|
|
|
// dropdown immediately after opening it.
|
|
|
|
// NB. We can't just stopPropagation() because then the event
|
|
|
|
// doesn't reach the React onClick().
|
|
|
|
this.ignoreEvent = ev;
|
|
|
|
}
|
|
|
|
|
|
|
|
onInputClick(ev) {
|
|
|
|
this.setState({
|
|
|
|
expanded: !this.state.expanded,
|
|
|
|
});
|
|
|
|
ev.preventDefault();
|
|
|
|
}
|
|
|
|
|
|
|
|
onMenuOptionClick(server, instance, includeAll) {
|
|
|
|
this.setState({
|
|
|
|
expanded: false,
|
|
|
|
selectedServer: server,
|
|
|
|
selectedInstanceId: instance ? instance.instance_id : null,
|
2019-02-27 02:25:31 +03:00
|
|
|
includeAllNetworks: includeAll,
|
2018-04-12 01:58:04 +03:00
|
|
|
});
|
|
|
|
this.props.onOptionChange(server, instance ? instance.instance_id : null, includeAll);
|
|
|
|
}
|
|
|
|
|
|
|
|
onInputKeyUp(e) {
|
2019-02-27 02:25:31 +03:00
|
|
|
if (e.key === 'Enter') {
|
2018-04-12 01:58:04 +03:00
|
|
|
this.setState({
|
|
|
|
expanded: false,
|
|
|
|
selectedServer: e.target.value,
|
|
|
|
selectedNetwork: null,
|
2019-02-27 02:25:31 +03:00
|
|
|
includeAllNetworks: true,
|
2018-04-12 01:58:04 +03:00
|
|
|
});
|
|
|
|
this.props.onOptionChange(e.target.value, null);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
collectRoot(e) {
|
|
|
|
if (this.dropdownRootElement) {
|
|
|
|
this.dropdownRootElement.removeEventListener('click', this.onRootClick, false);
|
|
|
|
}
|
|
|
|
if (e) {
|
|
|
|
e.addEventListener('click', this.onRootClick, false);
|
|
|
|
}
|
|
|
|
this.dropdownRootElement = e;
|
|
|
|
}
|
|
|
|
|
|
|
|
collectInputTextBox(e) {
|
|
|
|
this.inputTextBox = e;
|
|
|
|
}
|
|
|
|
|
|
|
|
_getMenuOptions() {
|
|
|
|
const options = [];
|
|
|
|
|
|
|
|
let servers = [];
|
2019-02-22 04:40:01 +03:00
|
|
|
if (this.props.config.roomDirectory.servers) {
|
|
|
|
servers = servers.concat(this.props.config.roomDirectory.servers);
|
2018-04-12 01:58:04 +03:00
|
|
|
}
|
|
|
|
|
2019-03-01 12:39:39 +03:00
|
|
|
if (!servers.includes(MatrixClientPeg.getHomeServerName())) {
|
2018-04-12 01:58:04 +03:00
|
|
|
servers.unshift(MatrixClientPeg.getHomeServerName());
|
|
|
|
}
|
|
|
|
|
|
|
|
// For our own HS, we can use the instance_ids given in the third party protocols
|
|
|
|
// response to get the server to filter the room list by network for us.
|
|
|
|
// We can't get thirdparty protocols for remote server yet though, so for those
|
|
|
|
// we can only show the default room list.
|
|
|
|
for (const server of servers) {
|
|
|
|
options.push(this._makeMenuOption(server, null, true));
|
2019-03-01 12:39:39 +03:00
|
|
|
if (server === MatrixClientPeg.getHomeServerName()) {
|
2018-04-12 01:58:04 +03:00
|
|
|
options.push(this._makeMenuOption(server, null, false));
|
|
|
|
if (this.props.protocols) {
|
|
|
|
for (const proto of Object.keys(this.props.protocols)) {
|
|
|
|
if (!this.props.protocols[proto].instances) continue;
|
|
|
|
|
|
|
|
const sortedInstances = this.props.protocols[proto].instances;
|
|
|
|
sortedInstances.sort(function(x, y) {
|
2018-10-27 06:50:35 +03:00
|
|
|
const a = x.desc;
|
|
|
|
const b = y.desc;
|
2018-04-12 01:58:04 +03:00
|
|
|
if (a < b) {
|
|
|
|
return -1;
|
|
|
|
} else if (a > b) {
|
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
for (const instance of sortedInstances) {
|
|
|
|
if (!instance.instance_id) continue;
|
|
|
|
options.push(this._makeMenuOption(server, instance, false));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return options;
|
|
|
|
}
|
|
|
|
|
|
|
|
_makeMenuOption(server, instance, includeAll, handleClicks) {
|
|
|
|
if (handleClicks === undefined) handleClicks = true;
|
|
|
|
|
|
|
|
let icon;
|
|
|
|
let name;
|
|
|
|
let key;
|
|
|
|
|
|
|
|
if (!instance && includeAll) {
|
|
|
|
key = server;
|
|
|
|
name = server;
|
|
|
|
} else if (!instance) {
|
|
|
|
key = server + '_all';
|
|
|
|
name = 'Matrix';
|
2019-01-11 04:37:28 +03:00
|
|
|
icon = <img src={require("../../../../res/img/network-matrix.svg")} />;
|
2018-04-12 01:58:04 +03:00
|
|
|
} else {
|
|
|
|
key = server + '_inst_' + instance.instance_id;
|
|
|
|
const imgUrl = instance.icon ?
|
|
|
|
MatrixClientPeg.get().mxcUrlToHttp(instance.icon, 25, 25, 'crop', true) :
|
|
|
|
DEFAULT_ICON_URL;
|
|
|
|
icon = <img src={imgUrl} />;
|
|
|
|
name = instance.desc;
|
|
|
|
}
|
|
|
|
|
2019-03-01 12:39:39 +03:00
|
|
|
const clickHandler = handleClicks ? this.onMenuOptionClick.bind(this, server, instance, includeAll) : null;
|
2018-04-12 01:58:04 +03:00
|
|
|
|
2019-03-01 12:39:39 +03:00
|
|
|
return <div key={key} className="mx_NetworkDropdown_networkoption" onClick={clickHandler}>
|
2018-04-12 01:58:04 +03:00
|
|
|
{icon}
|
|
|
|
<span className="mx_NetworkDropdown_menu_network">{name}</span>
|
2018-10-27 06:50:35 +03:00
|
|
|
</div>;
|
2018-04-12 01:58:04 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
2019-03-01 12:39:39 +03:00
|
|
|
let currentValue;
|
2018-04-12 01:58:04 +03:00
|
|
|
|
|
|
|
let menu;
|
|
|
|
if (this.state.expanded) {
|
2019-03-01 12:39:39 +03:00
|
|
|
const menuOptions = this._getMenuOptions();
|
2018-04-12 01:58:04 +03:00
|
|
|
menu = <div className="mx_NetworkDropdown_menu">
|
2019-03-01 12:39:39 +03:00
|
|
|
{menuOptions}
|
2018-04-12 01:58:04 +03:00
|
|
|
</div>;
|
2019-03-01 12:39:39 +03:00
|
|
|
currentValue = <input type="text" className="mx_NetworkDropdown_networkoption"
|
2018-04-12 01:58:04 +03:00
|
|
|
ref={this.collectInputTextBox} onKeyUp={this.onInputKeyUp}
|
|
|
|
placeholder="matrix.org" // 'matrix.org' as an example of an HS name
|
2018-10-27 06:50:35 +03:00
|
|
|
/>;
|
2018-04-12 01:58:04 +03:00
|
|
|
} else {
|
|
|
|
const instance = instanceForInstanceId(this.props.protocols, this.state.selectedInstanceId);
|
2019-03-01 12:39:39 +03:00
|
|
|
currentValue = this._makeMenuOption(
|
2019-02-27 02:25:31 +03:00
|
|
|
this.state.selectedServer, instance, this.state.includeAllNetworks, false,
|
2018-04-12 01:58:04 +03:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return <div className="mx_NetworkDropdown" ref={this.collectRoot}>
|
2019-01-29 17:34:58 +03:00
|
|
|
<div className="mx_NetworkDropdown_input mx_no_textinput" onClick={this.onInputClick}>
|
2019-03-01 12:39:39 +03:00
|
|
|
{currentValue}
|
|
|
|
<span className="mx_NetworkDropdown_arrow" />
|
2018-04-12 01:58:04 +03:00
|
|
|
{menu}
|
|
|
|
</div>
|
|
|
|
</div>;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
NetworkDropdown.propTypes = {
|
|
|
|
onOptionChange: React.PropTypes.func.isRequired,
|
|
|
|
protocols: React.PropTypes.object,
|
|
|
|
// The room directory config. May have a 'servers' key that is a list of server names to include in the dropdown
|
|
|
|
config: React.PropTypes.object,
|
|
|
|
};
|
|
|
|
|
|
|
|
NetworkDropdown.defaultProps = {
|
|
|
|
protocols: {},
|
|
|
|
config: {},
|
|
|
|
};
|