Room dir: New filtering & 3rd party networks

Changes filtering on 3rd party networks to divide into portal / non portal rooms and not show portal rooms by default. Adds a special '_matrix' network for all rooms that aren't portal rooms.

Also adds ability to query 3rd party directory servers.
pull/2362/head
David Baker 2016-09-27 19:39:20 +01:00
parent 7b258bc229
commit 85ea45a64a
4 changed files with 129 additions and 36 deletions

View File

@ -53,6 +53,7 @@ module.exports = React.createClass({
publicRooms: [], publicRooms: [],
loading: true, loading: true,
filterByNetwork: null, filterByNetwork: null,
roomServer: null,
} }
}, },
@ -76,10 +77,6 @@ module.exports = React.createClass({
// }); // });
}, },
componentDidMount: function() {
this.refreshRoomList();
},
componentWillUnmount: function() { componentWillUnmount: function() {
// dis.dispatch({ // dis.dispatch({
// action: 'ui_opacity', // action: 'ui_opacity',
@ -102,6 +99,9 @@ module.exports = React.createClass({
const my_filter_string = this.filterString; const my_filter_string = this.filterString;
const opts = {limit: 20}; const opts = {limit: 20};
if (this.state.roomServer != MatrixClientPeg.getHomeServerName()) {
opts.server = this.state.roomServer;
}
if (this.nextBatch) opts.since = this.nextBatch; if (this.nextBatch) opts.since = this.nextBatch;
if (this.filterString) opts.filter = { generic_search_term: my_filter_string } ; if (this.filterString) opts.filter = { generic_search_term: my_filter_string } ;
return MatrixClientPeg.get().publicRooms(opts).then((data) => { return MatrixClientPeg.get().publicRooms(opts).then((data) => {
@ -194,18 +194,11 @@ module.exports = React.createClass({
} }
}, },
onNetworkChange: function(network) { onOptionChange: function(server, network) {
this.setState({ this.setState({
roomServer: server,
filterByNetwork: network, filterByNetwork: network,
}, () => { }, this.refreshRoomList);
// we just filtered out a bunch of rooms, so check to see if
// we need to fill up the scrollpanel again
// NB. Because we filter the results, the HS can keep giving
// us more rooms and we'll keep requesting more if none match
// the filter, which is pretty terrible. We need a way
// to filter by network on the server.
if (this.scrollPanel) this.scrollPanel.checkFillState();
});
}, },
onFillRequest: function(backwards) { onFillRequest: function(backwards) {
@ -295,7 +288,7 @@ module.exports = React.createClass({
var rooms = this.state.publicRooms.filter((a) => { var rooms = this.state.publicRooms.filter((a) => {
if (this.state.filterByNetwork) { if (this.state.filterByNetwork) {
if (!this._isRoomInNetwork(a, this.state.filterByNetwork)) return false; if (!this._isRoomInNetwork(a, this.state.roomServer, this.state.filterByNetwork)) return false;
} }
return true; return true;
@ -365,14 +358,30 @@ module.exports = React.createClass({
* Terrible temporary function that guess what network a public room * Terrible temporary function that guess what network a public room
* entry is in, until synapse is able to tell us * entry is in, until synapse is able to tell us
*/ */
_isRoomInNetwork(room, network) { _isRoomInNetwork(room, server, network) {
if (room.aliases && this.networkPatterns[network]) { // We carve rooms into two categories here. 'portal' rooms are
for (const alias of room.aliases) { // rooms created by a user joining a bridge 'portal' alias to
if (this.networkPatterns[network].test(alias)) return true; // participate in that room or a foreign network. A room is a
// portal room if it has exactly one alias and that alias matches
// a pattern defined in the config. It's network is the key
// of the pattern that it matches.
// All other rooms are considered 'native matrix' rooms, and
// go into the special '_matrix' network.
let roomNetwork = '_matrix';
if (room.aliases && room.aliases.length == 1) {
if (this.props.config.serverConfig && this.props.config.serverConfig[server] && this.props.config.serverConfig[server].networks) {
for (const n of this.props.config.serverConfig[server].networks) {
const pat = this.networkPatterns[n];
if (pat && pat) {
if (this.networkPatterns[n].test(room.aliases[0])) {
roomNetwork = n;
}
}
}
} }
} }
return roomNetwork == network;
return false;
}, },
render: function() { render: function() {
@ -411,7 +420,7 @@ module.exports = React.createClass({
className="mx_RoomDirectory_searchbox" className="mx_RoomDirectory_searchbox"
onChange={this.onFilterChange} onClear={this.onFilterClear} onJoinClick={this.onJoinClick} onChange={this.onFilterChange} onClear={this.onFilterClear} onJoinClick={this.onJoinClick}
/> />
<NetworkDropdown config={this.props.config} onNetworkChange={this.onNetworkChange} /> <NetworkDropdown config={this.props.config} onOptionChange={this.onOptionChange} />
</div> </div>
{content} {content}
</div> </div>

View File

@ -15,6 +15,7 @@ limitations under the License.
*/ */
import React from 'react'; import React from 'react';
import MatrixClientPeg from 'matrix-react-sdk/lib/MatrixClientPeg';
export default class NetworkDropdown extends React.Component { export default class NetworkDropdown extends React.Component {
constructor() { constructor() {
@ -26,12 +27,17 @@ export default class NetworkDropdown extends React.Component {
this.onInputClick = this.onInputClick.bind(this); this.onInputClick = this.onInputClick.bind(this);
this.onRootClick = this.onRootClick.bind(this); this.onRootClick = this.onRootClick.bind(this);
this.onDocumentClick = this.onDocumentClick.bind(this); this.onDocumentClick = this.onDocumentClick.bind(this);
this.onNetworkClick = this.onNetworkClick.bind(this); this.onMenuOptionClick = this.onMenuOptionClick.bind(this);
this.onInputKeyUp = this.onInputKeyUp.bind(this);
this.collectRoot = this.collectRoot.bind(this); this.collectRoot = this.collectRoot.bind(this);
this.collectInputTextBox = this.collectInputTextBox.bind(this);
this.inputTextBox = null;
this.state = { this.state = {
expanded: false, expanded: false,
selectedNetwork: null, selectedServer: MatrixClientPeg.getHomeServerName(),
selectedNetwork: '_matrix',
}; };
} }
@ -39,6 +45,9 @@ export default class NetworkDropdown extends React.Component {
// Listen for all clicks on the document so we can close the // Listen for all clicks on the document so we can close the
// menu when the user clicks somewhere else // menu when the user clicks somewhere else
document.addEventListener('click', this.onDocumentClick, false); document.addEventListener('click', this.onDocumentClick, false);
// fire this now so the defaults can be set up
this.props.onOptionChange(this.state.selectedServer, this.state.selectedNetwork);
} }
componentWillUnmount() { componentWillUnmount() {
@ -72,12 +81,24 @@ export default class NetworkDropdown extends React.Component {
ev.preventDefault(); ev.preventDefault();
} }
onNetworkClick(network, ev) { onMenuOptionClick(server, network, ev) {
this.setState({ this.setState({
expanded: false, expanded: false,
selectedServer: server,
selectedNetwork: network, selectedNetwork: network,
}); });
this.props.onNetworkChange(network); this.props.onOptionChange(server, network);
}
onInputKeyUp(e) {
if (e.key == 'Enter') {
this.setState({
expanded: false,
selectedServer: e.target.value,
selectedNetwork: null,
});
this.props.onOptionChange(e.target.value, null);
}
} }
collectRoot(e) { collectRoot(e) {
@ -90,41 +111,86 @@ export default class NetworkDropdown extends React.Component {
this.dropdownRootElement = e; this.dropdownRootElement = e;
} }
_optionForNetwork(network, wire_onclick) { collectInputTextBox(e) {
this.inputTextBox = e;
}
_getMenuOptions() {
const options = [];
let servers = [];
if (this.props.config.servers) {
servers = servers.concat(this.props.config.servers);
}
if (servers.indexOf(MatrixClientPeg.getHomeServerName()) == -1) {
servers.unshift(MatrixClientPeg.getHomeServerName());
}
for (const server of servers) {
options.push(this._makeMenuOption(server, null));
if (this.props.config.serverConfig && this.props.config.serverConfig[server] && this.props.config.serverConfig[server].networks) {
for (const network of this.props.config.serverConfig[server].networks) {
options.push(this._makeMenuOption(server, network));
}
}
}
return options;
}
_makeMenuOption(server, network, wire_onclick) {
if (wire_onclick === undefined) wire_onclick = true; if (wire_onclick === undefined) wire_onclick = true;
let icon; let icon;
let name; let name;
let span_class; let span_class;
if (network === null) { if (network === null) {
name = 'All networks'; name = server;
span_class = 'mx_NetworkDropdown_menu_all'; span_class = 'mx_NetworkDropdown_menu_all';
} else if (network == '_matrix') {
name = 'Matrix';
icon = <img src="/img/network-matrix.svg" width="16" height="16" />;
span_class = 'mx_NetworkDropdown_menu_network';
} else { } else {
name = this.props.config.networkNames[network]; name = this.props.config.networkNames[network];
icon = <img src={this.props.config.networkIcons[network]} />; icon = <img src={this.props.config.networkIcons[network]} />;
span_class = 'mx_NetworkDropdown_menu_network'; span_class = 'mx_NetworkDropdown_menu_network';
} }
const click_handler = wire_onclick ? this.onNetworkClick.bind(this, network) : null; const click_handler = wire_onclick ? this.onMenuOptionClick.bind(this, server, network) : null;
return <div key={network} className="mx_NetworkDropdown_networkoption" onClick={click_handler}> let key = server;
if (network !== null) {
key += '_' + network;
}
return <div key={key} className="mx_NetworkDropdown_networkoption" onClick={click_handler}>
{icon} {icon}
<span className={span_class}>{name}</span> <span className={span_class}>{name}</span>
</div>; </div>;
} }
componentDidUpdate() {
if (this.state.expanded && this.inputTextBox) {
this.inputTextBox.focus();
}
}
render() { render() {
const current_value = this._optionForNetwork(this.state.selectedNetwork, false); let current_value = this._makeMenuOption(
this.state.selectedServer, this.state.selectedNetwork, false
);
let menu; let menu;
if (this.state.expanded) { if (this.state.expanded) {
const menu_options = [this._optionForNetwork(null)]; const menu_options = this._getMenuOptions();
for (const network of this.props.config.networks) {
menu_options.push(this._optionForNetwork(network));
}
menu = <div className="mx_NetworkDropdown_menu"> menu = <div className="mx_NetworkDropdown_menu">
{menu_options} {menu_options}
</div>; </div>;
current_value = <input type="text" className="mx_NetworkDropdown_networkoption"
ref={this.collectInputTextBox} onKeyUp={this.onInputKeyUp}
/>
} }
return <div className="mx_NetworkDropdown" ref={this.collectRoot}> return <div className="mx_NetworkDropdown" ref={this.collectRoot}>
@ -138,7 +204,7 @@ export default class NetworkDropdown extends React.Component {
} }
NetworkDropdown.propTypes = { NetworkDropdown.propTypes = {
onNetworkChange: React.PropTypes.func.isRequired, onOptionChange: React.PropTypes.func.isRequired,
config: React.PropTypes.object, config: React.PropTypes.object,
}; };

View File

@ -52,6 +52,10 @@ limitations under the License.
vertical-align: middle; vertical-align: middle;
} }
input.mx_NetworkDropdown_networkoption, input.mx_NetworkDropdown_networkoption:focus {
border: 0;
}
.mx_NetworkDropdown_menu { .mx_NetworkDropdown_menu {
position: absolute; position: absolute;
left: -1px; left: -1px;

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 520 520" style="enable-background:new 0 0 520 520;" xml:space="preserve">
<path d="M13.7,11.9v496.2h35.7V520H0V0h49.4v11.9H13.7z"/>
<path d="M166.3,169.2v25.1h0.7c6.7-9.6,14.8-17,24.2-22.2c9.4-5.3,20.3-7.9,32.5-7.9c11.7,0,22.4,2.3,32.1,6.8
c9.7,4.5,17,12.6,22.1,24c5.5-8.1,13-15.3,22.4-21.5c9.4-6.2,20.6-9.3,33.5-9.3c9.8,0,18.9,1.2,27.3,3.6c8.4,2.4,15.5,6.2,21.5,11.5
c6,5.3,10.6,12.1,14,20.6c3.3,8.5,5,18.7,5,30.7v124.1h-50.9V249.6c0-6.2-0.2-12.1-0.7-17.6c-0.5-5.5-1.8-10.3-3.9-14.3
c-2.2-4.1-5.3-7.3-9.5-9.7c-4.2-2.4-9.9-3.6-17-3.6c-7.2,0-13,1.4-17.4,4.1c-4.4,2.8-7.9,6.3-10.4,10.8c-2.5,4.4-4.2,9.4-5,15.1
c-0.8,5.6-1.3,11.3-1.3,17v103.3h-50.9v-104c0-5.5-0.1-10.9-0.4-16.3c-0.2-5.4-1.3-10.3-3.1-14.9c-1.8-4.5-4.8-8.2-9-10.9
c-4.2-2.7-10.3-4.1-18.5-4.1c-2.4,0-5.6,0.5-9.5,1.6c-3.9,1.1-7.8,3.1-11.5,6.1c-3.7,3-6.9,7.3-9.5,12.9c-2.6,5.6-3.9,13-3.9,22.1
v107.6h-50.9V169.2H166.3z"/>
<path d="M506.3,508.1V11.9h-35.7V0H520v520h-49.4v-11.9H506.3z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB