mirror of
				https://github.com/vector-im/element-web.git
				synced 2025-11-04 02:02:14 +01:00 
			
		
		
		
	Merge pull request #2219 from vector-im/dbkr/directory_network_selector
Directory network selector
This commit is contained in:
		
						commit
						135c22c99d
					
				@ -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');
 | 
			
		||||
 | 
			
		||||
@ -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 (
 | 
			
		||||
            <div className="mx_RoomDirectory">
 | 
			
		||||
                <SimpleRoomHeader title="Directory" />
 | 
			
		||||
                <div className="mx_RoomDirectory_list">
 | 
			
		||||
                    <input ref="roomAlias" placeholder="Join a room (e.g. #foo:domain.com)" className="mx_RoomDirectory_input" size="64" onKeyUp={ this.onKeyUp }/>
 | 
			
		||||
                    <div className="mx_RoomDirectory_listheader">
 | 
			
		||||
                        <input ref="roomAlias" placeholder="Join a room (e.g. #foo:domain.com)" className="mx_RoomDirectory_input" size="64" onKeyUp={ this.onKeyUp }/>
 | 
			
		||||
                        <NetworkDropdown config={this.props.config} onNetworkChange={this.onNetworkChange} />
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <GeminiScrollbar className="mx_RoomDirectory_tableWrapper">
 | 
			
		||||
                        <table ref="directory_table" className="mx_RoomDirectory_table">
 | 
			
		||||
                            <tbody>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										150
									
								
								src/components/views/directory/NetworkDropdown.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								src/components/views/directory/NetworkDropdown.js
									
									
									
									
									
										Normal file
									
								
							@ -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 = <img src={this.props.config.networkIcons[network]} />;
 | 
			
		||||
            span_class = 'mx_NetworkDropdown_menu_network';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const click_handler = wire_onclick ? this.onNetworkClick.bind(this, network) : null;
 | 
			
		||||
 | 
			
		||||
        return <div key={network} className="mx_NetworkDropdown_networkoption" onClick={click_handler}>
 | 
			
		||||
            {icon}
 | 
			
		||||
            <span className={span_class}>{name}</span>
 | 
			
		||||
        </div>;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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 = <div className="mx_NetworkDropdown_menu">
 | 
			
		||||
                {menu_options}
 | 
			
		||||
            </div>;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return <div className="mx_NetworkDropdown" ref={this.collectRoot}>
 | 
			
		||||
            <div className="mx_NetworkDropdown_input" onClick={this.onInputClick}>
 | 
			
		||||
                {current_value}
 | 
			
		||||
                <span className="mx_NetworkDropdown_arrow"></span>
 | 
			
		||||
                {menu}
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
NetworkDropdown.propTypes = {
 | 
			
		||||
    onNetworkChange: React.PropTypes.func.isRequired,
 | 
			
		||||
    config: React.PropTypes.object,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
NetworkDropdown.defaultProps = {
 | 
			
		||||
    config: {
 | 
			
		||||
        networks: [],
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -46,15 +46,26 @@ limitations under the License.
 | 
			
		||||
    -webkit-flex-direction: column;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.mx_RoomDirectory_listheader {
 | 
			
		||||
    display: table;
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    margin-top: 12px;
 | 
			
		||||
    margin-bottom: 12px;
 | 
			
		||||
    border-spacing: 5px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.mx_RoomDirectory_input {
 | 
			
		||||
    margin: auto;
 | 
			
		||||
    display: table-cell;
 | 
			
		||||
    border-radius: 3px;
 | 
			
		||||
    border: 1px solid #c7c7c7;
 | 
			
		||||
    font-weight: 300;
 | 
			
		||||
    font-size: 13px;
 | 
			
		||||
    padding: 9px;
 | 
			
		||||
    margin-top: 12px;
 | 
			
		||||
    margin-bottom: 12px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.mx_RoomDirectory_listheader .mx_NetworkDropdown {
 | 
			
		||||
    display: table-cell;
 | 
			
		||||
    width: 100%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.mx_RoomDirectory_tableWrapper {
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,77 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2015, 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.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
.mx_NetworkDropdown {
 | 
			
		||||
    position: relative;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.mx_NetworkDropdown_input {
 | 
			
		||||
    position: relative;
 | 
			
		||||
    border-radius: 3px;
 | 
			
		||||
    border: 1px solid #c7c7c7;
 | 
			
		||||
    font-weight: 300;
 | 
			
		||||
    font-size: 13px;
 | 
			
		||||
    margin-top: 12px;
 | 
			
		||||
    margin-bottom: 12px;
 | 
			
		||||
    user-select: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.mx_NetworkDropdown_arrow {
 | 
			
		||||
    border-color: #4a4a4a transparent transparent;
 | 
			
		||||
    border-style: solid;
 | 
			
		||||
    border-width: 5px 5px 0;
 | 
			
		||||
    display: block;
 | 
			
		||||
    height: 0;
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    right: 10px;
 | 
			
		||||
    top: 14px;
 | 
			
		||||
    width: 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.mx_NetworkDropdown_networkoption {
 | 
			
		||||
    height: 35px;
 | 
			
		||||
    line-height: 35px;
 | 
			
		||||
    padding-left: 8px;
 | 
			
		||||
    padding-right: 8px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.mx_NetworkDropdown_networkoption img {
 | 
			
		||||
    margin: 5px;
 | 
			
		||||
    width: 25px;
 | 
			
		||||
    vertical-align: middle;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.mx_NetworkDropdown_menu {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    left: -1px;
 | 
			
		||||
    right: -1px;
 | 
			
		||||
    top: 100%;
 | 
			
		||||
    z-index: 2;
 | 
			
		||||
    margin: 0;
 | 
			
		||||
    padding: 0px;
 | 
			
		||||
    border-radius: 3px;
 | 
			
		||||
    border: 1px solid #76cfa6;
 | 
			
		||||
    background-color: white;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.mx_NetworkDropdown_menu .mx_NetworkDropdown_networkoption:hover {
 | 
			
		||||
    background-color: #ddd;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.mx_NetworkDropdown_menu_network {
 | 
			
		||||
    font-weight: bold;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -4,5 +4,35 @@
 | 
			
		||||
    "brand": "Vector",
 | 
			
		||||
    "integrations_ui_url": "http://localhost:8081/",
 | 
			
		||||
    "integrations_rest_url": "http://localhost:5050",
 | 
			
		||||
    "enableLabs": true
 | 
			
		||||
    "enableLabs": true,
 | 
			
		||||
    "roomDirectory": {
 | 
			
		||||
        "networks": [
 | 
			
		||||
            "matrix:example_com",
 | 
			
		||||
            "matrix:matrix_org",
 | 
			
		||||
            "gitter",
 | 
			
		||||
            "irc:freenode",
 | 
			
		||||
            "irc:mozilla"
 | 
			
		||||
        ],
 | 
			
		||||
        "networkPatterns": {
 | 
			
		||||
            "matrix:example_com": "#.*:example.com",
 | 
			
		||||
            "matrix:matrix_org": "#.*:matrix.org",
 | 
			
		||||
            "gitter": "#gitter_.*:matrix.org",
 | 
			
		||||
            "irc:freenode": "#freenode_.*:matrix.org",
 | 
			
		||||
            "irc:mozilla": "#mozilla_.*:matrix.org"
 | 
			
		||||
        },
 | 
			
		||||
        "networkNames": {
 | 
			
		||||
            "matrix:example_com": "example.com",
 | 
			
		||||
            "matrix:matrix_org": "matrix.org",
 | 
			
		||||
            "irc:freenode": "Freenode",
 | 
			
		||||
            "irc:mozilla": "Mozilla",
 | 
			
		||||
            "gitter": "Gitter"
 | 
			
		||||
        },
 | 
			
		||||
        "networkIcons": {
 | 
			
		||||
            "matrix:example_com": "//matrix.org/favicon.ico",
 | 
			
		||||
            "matrix:matrix_org": "//matrix.org/favicon.ico",
 | 
			
		||||
            "irc:freenode": "//matrix.org/_matrix/media/v1/download/matrix.org/DHLHpDDgWNNejFmrewvwEAHX",
 | 
			
		||||
            "irc:mozilla": "//matrix.org/_matrix/media/v1/download/matrix.org/DHLHpDDgWNNejFmrewvwEAHX",
 | 
			
		||||
            "gitter": "//gitter.im/favicon.ico"
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user