diff --git a/src/component-index.js b/src/component-index.js
index a2ee1ad4e..ec597609a 100644
--- a/src/component-index.js
+++ b/src/component-index.js
@@ -38,6 +38,7 @@ module.exports.components['views.context_menus.MessageContextMenu'] = require('.
module.exports.components['views.context_menus.NotificationStateContextMenu'] = require('./components/views/context_menus/NotificationStateContextMenu');
module.exports.components['views.context_menus.RoomTagContextMenu'] = require('./components/views/context_menus/RoomTagContextMenu');
module.exports.components['views.dialogs.ChangelogDialog'] = require('./components/views/dialogs/ChangelogDialog');
+module.exports.components['views.directory.NetworkDropdown'] = require('./components/views/directory/NetworkDropdown');
module.exports.components['views.elements.ImageView'] = require('./components/views/elements/ImageView');
module.exports.components['views.elements.Spinner'] = require('./components/views/elements/Spinner');
module.exports.components['views.globals.GuestWarningBar'] = require('./components/views/globals/GuestWarningBar');
diff --git a/src/components/structures/RoomDirectory.js b/src/components/structures/RoomDirectory.js
index e4a8524b9..3d8249968 100644
--- a/src/components/structures/RoomDirectory.js
+++ b/src/components/structures/RoomDirectory.js
@@ -35,15 +35,36 @@ linkifyMatrix(linkify);
module.exports = React.createClass({
displayName: 'RoomDirectory',
+ propTypes: {
+ config: React.PropTypes.object,
+ },
+
+ getDefaultProps: function() {
+ return {
+ config: {
+ networks: [],
+ },
+ }
+ },
+
getInitialState: function() {
return {
publicRooms: [],
roomAlias: '',
loading: true,
+ filterByNetwork: null,
}
},
componentWillMount: function() {
+ // precompile Regexps
+ this.networkPatterns = {};
+ if (this.props.config.networkPatterns) {
+ for (const network of Object.keys(this.props.config.networkPatterns)) {
+ this.networkPatterns[network] = new RegExp(this.props.config.networkPatterns[network]);
+ }
+ }
+
// dis.dispatch({
// action: 'ui_opacity',
// sideOpacity: 0.3,
@@ -143,6 +164,12 @@ module.exports = React.createClass({
}
},
+ onNetworkChange: function(network) {
+ this.setState({
+ filterByNetwork: network,
+ });
+ },
+
showRoomAlias: function(alias) {
this.showRoom(null, alias);
},
@@ -192,9 +219,13 @@ module.exports = React.createClass({
if (!this.state.publicRooms) return [];
- var rooms = this.state.publicRooms.filter(function(a) {
+ var rooms = this.state.publicRooms.filter((a) => {
// FIXME: if incrementally typing, keep narrowing down the search set
// incrementally rather than starting over each time.
+ if (this.state.filterByNetwork) {
+ if (!this._isRoomInNetwork(a, this.state.filterByNetwork)) return false;
+ }
+
return (((a.name && a.name.toLowerCase().search(filter.toLowerCase()) >= 0) ||
(a.aliases && a.aliases[0].toLowerCase().search(filter.toLowerCase()) >= 0)) &&
a.num_joined_members > 0);
@@ -266,6 +297,20 @@ module.exports = React.createClass({
}
},
+ /**
+ * Terrible temporary function that guess what network a public room
+ * entry is in, until synapse is able to tell us
+ */
+ _isRoomInNetwork(room, network) {
+ if (room.aliases && this.networkPatterns[network]) {
+ for (const alias of room.aliases) {
+ if (this.networkPatterns[network].test(alias)) return true;
+ }
+ }
+
+ return false;
+ },
+
render: function() {
if (this.state.loading) {
var Loader = sdk.getComponent("elements.Spinner");
@@ -276,12 +321,16 @@ module.exports = React.createClass({
);
}
- var SimpleRoomHeader = sdk.getComponent('rooms.SimpleRoomHeader');
+ const SimpleRoomHeader = sdk.getComponent('rooms.SimpleRoomHeader');
+ const NetworkDropdown = sdk.getComponent('directory.NetworkDropdown');
return (
-
+
+
+
+
diff --git a/src/components/views/directory/NetworkDropdown.js b/src/components/views/directory/NetworkDropdown.js
new file mode 100644
index 000000000..e1de4ffea
--- /dev/null
+++ b/src/components/views/directory/NetworkDropdown.js
@@ -0,0 +1,150 @@
+/*
+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';
+
+export default class NetworkDropdown extends React.Component {
+ constructor() {
+ super();
+
+ 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.onNetworkClick = this.onNetworkClick.bind(this);
+ this.collectRoot = this.collectRoot.bind(this);
+
+ this.state = {
+ expanded: false,
+ selectedNetwork: null,
+ };
+ }
+
+ 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);
+ }
+
+ componentWillUnmount() {
+ document.removeEventListener('click', this.onDocumentClick, false);
+ }
+
+ 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();
+ }
+
+ onNetworkClick(network, ev) {
+ this.setState({
+ expanded: false,
+ selectedNetwork: network,
+ });
+ this.props.onNetworkChange(network);
+ }
+
+ collectRoot(e) {
+ if (this.dropdownRootElement) {
+ this.dropdownRootElement.removeEventListener('click', this.onRootClick, false);
+ }
+ if (e) {
+ e.addEventListener('click', this.onRootClick, false);
+ }
+ this.dropdownRootElement = e;
+ }
+
+ _optionForNetwork(network, wire_onclick) {
+ if (wire_onclick === undefined) wire_onclick = true;
+ let icon;
+ let name;
+ let span_class;
+
+ if (network === null) {
+ name = 'All networks';
+ span_class = 'mx_NetworkDropdown_menu_all';
+ } else {
+ name = this.props.config.networkNames[network];
+ icon = ;
+ span_class = 'mx_NetworkDropdown_menu_network';
+ }
+
+ const click_handler = wire_onclick ? this.onNetworkClick.bind(this, network) : null;
+
+ return
+ {icon}
+ {name}
+
;
+ }
+
+ render() {
+ const current_value = this._optionForNetwork(this.state.selectedNetwork, false);
+
+ let menu;
+ if (this.state.expanded) {
+ const menu_options = [this._optionForNetwork(null)];
+ for (const network of this.props.config.networks) {
+ menu_options.push(this._optionForNetwork(network));
+ }
+ menu =