Skip to content

Commit 135c22c

Browse files
authored
Merge pull request element-hq#2219 from vector-im/dbkr/directory_network_selector
Directory network selector
2 parents 6a13155 + 8f6d988 commit 135c22c

File tree

6 files changed

+325
-7
lines changed

6 files changed

+325
-7
lines changed

src/component-index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ module.exports.components['views.context_menus.MessageContextMenu'] = require('.
3838
module.exports.components['views.context_menus.NotificationStateContextMenu'] = require('./components/views/context_menus/NotificationStateContextMenu');
3939
module.exports.components['views.context_menus.RoomTagContextMenu'] = require('./components/views/context_menus/RoomTagContextMenu');
4040
module.exports.components['views.dialogs.ChangelogDialog'] = require('./components/views/dialogs/ChangelogDialog');
41+
module.exports.components['views.directory.NetworkDropdown'] = require('./components/views/directory/NetworkDropdown');
4142
module.exports.components['views.elements.ImageView'] = require('./components/views/elements/ImageView');
4243
module.exports.components['views.elements.Spinner'] = require('./components/views/elements/Spinner');
4344
module.exports.components['views.globals.GuestWarningBar'] = require('./components/views/globals/GuestWarningBar');

src/components/structures/RoomDirectory.js

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,36 @@ linkifyMatrix(linkify);
3535
module.exports = React.createClass({
3636
displayName: 'RoomDirectory',
3737

38+
propTypes: {
39+
config: React.PropTypes.object,
40+
},
41+
42+
getDefaultProps: function() {
43+
return {
44+
config: {
45+
networks: [],
46+
},
47+
}
48+
},
49+
3850
getInitialState: function() {
3951
return {
4052
publicRooms: [],
4153
roomAlias: '',
4254
loading: true,
55+
filterByNetwork: null,
4356
}
4457
},
4558

4659
componentWillMount: function() {
60+
// precompile Regexps
61+
this.networkPatterns = {};
62+
if (this.props.config.networkPatterns) {
63+
for (const network of Object.keys(this.props.config.networkPatterns)) {
64+
this.networkPatterns[network] = new RegExp(this.props.config.networkPatterns[network]);
65+
}
66+
}
67+
4768
// dis.dispatch({
4869
// action: 'ui_opacity',
4970
// sideOpacity: 0.3,
@@ -143,6 +164,12 @@ module.exports = React.createClass({
143164
}
144165
},
145166

167+
onNetworkChange: function(network) {
168+
this.setState({
169+
filterByNetwork: network,
170+
});
171+
},
172+
146173
showRoomAlias: function(alias) {
147174
this.showRoom(null, alias);
148175
},
@@ -192,9 +219,13 @@ module.exports = React.createClass({
192219

193220
if (!this.state.publicRooms) return [];
194221

195-
var rooms = this.state.publicRooms.filter(function(a) {
222+
var rooms = this.state.publicRooms.filter((a) => {
196223
// FIXME: if incrementally typing, keep narrowing down the search set
197224
// incrementally rather than starting over each time.
225+
if (this.state.filterByNetwork) {
226+
if (!this._isRoomInNetwork(a, this.state.filterByNetwork)) return false;
227+
}
228+
198229
return (((a.name && a.name.toLowerCase().search(filter.toLowerCase()) >= 0) ||
199230
(a.aliases && a.aliases[0].toLowerCase().search(filter.toLowerCase()) >= 0)) &&
200231
a.num_joined_members > 0);
@@ -266,6 +297,20 @@ module.exports = React.createClass({
266297
}
267298
},
268299

300+
/**
301+
* Terrible temporary function that guess what network a public room
302+
* entry is in, until synapse is able to tell us
303+
*/
304+
_isRoomInNetwork(room, network) {
305+
if (room.aliases && this.networkPatterns[network]) {
306+
for (const alias of room.aliases) {
307+
if (this.networkPatterns[network].test(alias)) return true;
308+
}
309+
}
310+
311+
return false;
312+
},
313+
269314
render: function() {
270315
if (this.state.loading) {
271316
var Loader = sdk.getComponent("elements.Spinner");
@@ -276,12 +321,16 @@ module.exports = React.createClass({
276321
);
277322
}
278323

279-
var SimpleRoomHeader = sdk.getComponent('rooms.SimpleRoomHeader');
324+
const SimpleRoomHeader = sdk.getComponent('rooms.SimpleRoomHeader');
325+
const NetworkDropdown = sdk.getComponent('directory.NetworkDropdown');
280326
return (
281327
<div className="mx_RoomDirectory">
282328
<SimpleRoomHeader title="Directory" />
283329
<div className="mx_RoomDirectory_list">
284-
<input ref="roomAlias" placeholder="Join a room (e.g. #foo:domain.com)" className="mx_RoomDirectory_input" size="64" onKeyUp={ this.onKeyUp }/>
330+
<div className="mx_RoomDirectory_listheader">
331+
<input ref="roomAlias" placeholder="Join a room (e.g. #foo:domain.com)" className="mx_RoomDirectory_input" size="64" onKeyUp={ this.onKeyUp }/>
332+
<NetworkDropdown config={this.props.config} onNetworkChange={this.onNetworkChange} />
333+
</div>
285334
<GeminiScrollbar className="mx_RoomDirectory_tableWrapper">
286335
<table ref="directory_table" className="mx_RoomDirectory_table">
287336
<tbody>
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
/*
2+
Copyright 2016 OpenMarket Ltd
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
import React from 'react';
18+
19+
export default class NetworkDropdown extends React.Component {
20+
constructor() {
21+
super();
22+
23+
this.dropdownRootElement = null;
24+
this.ignoreEvent = null;
25+
26+
this.onInputClick = this.onInputClick.bind(this);
27+
this.onRootClick = this.onRootClick.bind(this);
28+
this.onDocumentClick = this.onDocumentClick.bind(this);
29+
this.onNetworkClick = this.onNetworkClick.bind(this);
30+
this.collectRoot = this.collectRoot.bind(this);
31+
32+
this.state = {
33+
expanded: false,
34+
selectedNetwork: null,
35+
};
36+
}
37+
38+
componentWillMount() {
39+
// Listen for all clicks on the document so we can close the
40+
// menu when the user clicks somewhere else
41+
document.addEventListener('click', this.onDocumentClick, false);
42+
}
43+
44+
componentWillUnmount() {
45+
document.removeEventListener('click', this.onDocumentClick, false);
46+
}
47+
48+
onDocumentClick(ev) {
49+
// Close the dropdown if the user clicks anywhere that isn't
50+
// within our root element
51+
if (ev !== this.ignoreEvent) {
52+
this.setState({
53+
expanded: false,
54+
});
55+
}
56+
}
57+
58+
onRootClick(ev) {
59+
// This captures any clicks that happen within our elements,
60+
// such that we can then ignore them when they're seen by the
61+
// click listener on the document handler, ie. not close the
62+
// dropdown immediately after opening it.
63+
// NB. We can't just stopPropagation() because then the event
64+
// doesn't reach the React onClick().
65+
this.ignoreEvent = ev;
66+
}
67+
68+
onInputClick(ev) {
69+
this.setState({
70+
expanded: !this.state.expanded,
71+
});
72+
ev.preventDefault();
73+
}
74+
75+
onNetworkClick(network, ev) {
76+
this.setState({
77+
expanded: false,
78+
selectedNetwork: network,
79+
});
80+
this.props.onNetworkChange(network);
81+
}
82+
83+
collectRoot(e) {
84+
if (this.dropdownRootElement) {
85+
this.dropdownRootElement.removeEventListener('click', this.onRootClick, false);
86+
}
87+
if (e) {
88+
e.addEventListener('click', this.onRootClick, false);
89+
}
90+
this.dropdownRootElement = e;
91+
}
92+
93+
_optionForNetwork(network, wire_onclick) {
94+
if (wire_onclick === undefined) wire_onclick = true;
95+
let icon;
96+
let name;
97+
let span_class;
98+
99+
if (network === null) {
100+
name = 'All networks';
101+
span_class = 'mx_NetworkDropdown_menu_all';
102+
} else {
103+
name = this.props.config.networkNames[network];
104+
icon = <img src={this.props.config.networkIcons[network]} />;
105+
span_class = 'mx_NetworkDropdown_menu_network';
106+
}
107+
108+
const click_handler = wire_onclick ? this.onNetworkClick.bind(this, network) : null;
109+
110+
return <div key={network} className="mx_NetworkDropdown_networkoption" onClick={click_handler}>
111+
{icon}
112+
<span className={span_class}>{name}</span>
113+
</div>;
114+
}
115+
116+
render() {
117+
const current_value = this._optionForNetwork(this.state.selectedNetwork, false);
118+
119+
let menu;
120+
if (this.state.expanded) {
121+
const menu_options = [this._optionForNetwork(null)];
122+
for (const network of this.props.config.networks) {
123+
menu_options.push(this._optionForNetwork(network));
124+
}
125+
menu = <div className="mx_NetworkDropdown_menu">
126+
{menu_options}
127+
</div>;
128+
}
129+
130+
return <div className="mx_NetworkDropdown" ref={this.collectRoot}>
131+
<div className="mx_NetworkDropdown_input" onClick={this.onInputClick}>
132+
{current_value}
133+
<span className="mx_NetworkDropdown_arrow"></span>
134+
{menu}
135+
</div>
136+
</div>;
137+
}
138+
}
139+
140+
NetworkDropdown.propTypes = {
141+
onNetworkChange: React.PropTypes.func.isRequired,
142+
config: React.PropTypes.object,
143+
};
144+
145+
NetworkDropdown.defaultProps = {
146+
config: {
147+
networks: [],
148+
}
149+
};
150+

src/skins/vector/css/vector-web/structures/RoomDirectory.css

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,26 @@ limitations under the License.
4646
-webkit-flex-direction: column;
4747
}
4848

49+
.mx_RoomDirectory_listheader {
50+
display: table;
51+
width: 100%;
52+
margin-top: 12px;
53+
margin-bottom: 12px;
54+
border-spacing: 5px;
55+
}
56+
4957
.mx_RoomDirectory_input {
50-
margin: auto;
58+
display: table-cell;
5159
border-radius: 3px;
5260
border: 1px solid #c7c7c7;
5361
font-weight: 300;
5462
font-size: 13px;
5563
padding: 9px;
56-
margin-top: 12px;
57-
margin-bottom: 12px;
64+
}
65+
66+
.mx_RoomDirectory_listheader .mx_NetworkDropdown {
67+
display: table-cell;
68+
width: 100%;
5869
}
5970

6071
.mx_RoomDirectory_tableWrapper {
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
Copyright 2015, 2016 OpenMarket Ltd
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
.mx_NetworkDropdown {
18+
position: relative;
19+
}
20+
21+
.mx_NetworkDropdown_input {
22+
position: relative;
23+
border-radius: 3px;
24+
border: 1px solid #c7c7c7;
25+
font-weight: 300;
26+
font-size: 13px;
27+
margin-top: 12px;
28+
margin-bottom: 12px;
29+
user-select: none;
30+
}
31+
32+
.mx_NetworkDropdown_arrow {
33+
border-color: #4a4a4a transparent transparent;
34+
border-style: solid;
35+
border-width: 5px 5px 0;
36+
display: block;
37+
height: 0;
38+
position: absolute;
39+
right: 10px;
40+
top: 14px;
41+
width: 0
42+
}
43+
44+
.mx_NetworkDropdown_networkoption {
45+
height: 35px;
46+
line-height: 35px;
47+
padding-left: 8px;
48+
padding-right: 8px;
49+
}
50+
51+
.mx_NetworkDropdown_networkoption img {
52+
margin: 5px;
53+
width: 25px;
54+
vertical-align: middle;
55+
}
56+
57+
.mx_NetworkDropdown_menu {
58+
position: absolute;
59+
left: -1px;
60+
right: -1px;
61+
top: 100%;
62+
z-index: 2;
63+
margin: 0;
64+
padding: 0px;
65+
border-radius: 3px;
66+
border: 1px solid #76cfa6;
67+
background-color: white;
68+
}
69+
70+
.mx_NetworkDropdown_menu .mx_NetworkDropdown_networkoption:hover {
71+
background-color: #ddd;
72+
}
73+
74+
.mx_NetworkDropdown_menu_network {
75+
font-weight: bold;
76+
}
77+

0 commit comments

Comments
 (0)