From 761600f3253140f77248b184f78aaee38750224d Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 29 Sep 2016 15:30:54 +0100 Subject: [PATCH 01/10] Add native joining of 3p networks to room dir Use the 3rd party location lookup API to accept third-party locations in their native form and look up the corresponding portal room for that location. Also give the network dropdown some placeholder text. Fixes https://github.com/vector-im/vector-web/issues/2374 --- src/components/structures/RoomDirectory.js | 166 +++++++++++++++--- .../views/directory/NetworkDropdown.js | 18 +- vector/config.sample.json | 59 +++++-- 3 files changed, 205 insertions(+), 38 deletions(-) diff --git a/src/components/structures/RoomDirectory.js b/src/components/structures/RoomDirectory.js index 0111ee31..4c306a7a 100644 --- a/src/components/structures/RoomDirectory.js +++ b/src/components/structures/RoomDirectory.js @@ -52,23 +52,41 @@ module.exports = React.createClass({ return { publicRooms: [], loading: true, - filterByNetwork: null, + network: null, roomServer: null, + filterString: 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]); + this.portalRoomPatterns = {}; + this.nativePatterns = {}; + if (this.props.config.networks) { + for (const network of Object.keys(this.props.config.networks)) { + if (this.props.config.networks[network].portalRoomPattern) { + this.portalRoomPatterns[network] = new RegExp(this.props.config.networks[network].portalRoomPattern); + } + if (this.props.config.networks[network].nativePattern) { + this.nativePatterns[network] = new RegExp(this.props.config.networks[network].nativePattern); + } } } + this.nextBatch = null; - this.filterString = null; this.filterTimeout = null; this.scrollPanel = null; + this.protocols = null; + + MatrixClientPeg.get().getThirdpartyProtocols().done((response) => { + this.protocols = response; + }, (err) => { + const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + Modal.createDialog(ErrorDialog, { + title: "Failed to get protocol list from Home Server", + description: "The Home Server may be too old to support third party networks", + }); + }); // dis.dispatch({ // action: 'ui_opacity', @@ -97,7 +115,7 @@ module.exports = React.createClass({ getMoreRooms: function() { if (!MatrixClientPeg.get()) return q(); - const my_filter_string = this.filterString; + const my_filter_string = this.state.filterString; const my_server = this.state.roomServer; // remember the next batch token when we sent the request // too. If it's changed, appending to the list will corrupt it. @@ -107,10 +125,10 @@ module.exports = React.createClass({ opts.server = my_server; } if (this.nextBatch) opts.since = this.nextBatch; - if (this.filterString) opts.filter = { generic_search_term: my_filter_string } ; + if (this.state.filterString) opts.filter = { generic_search_term: my_filter_string } ; return MatrixClientPeg.get().publicRooms(opts).then((data) => { if ( - my_filter_string != this.filterString || + my_filter_string != this.state.filterString || my_server != this.state.roomServer || my_next_batch != this.nextBatch) { @@ -129,7 +147,7 @@ module.exports = React.createClass({ return Boolean(data.next_batch); }, (err) => { if ( - my_filter_string != this.filterString || + my_filter_string != this.state.filterString || my_server != this.state.roomServer || my_next_batch != this.nextBatch) { @@ -215,7 +233,7 @@ module.exports = React.createClass({ // to clear the list anyway. publicRooms: [], roomServer: server, - filterByNetwork: network, + network: network, }, this.refreshRoomList); // We also refresh the room list each time even though this // filtering is client-side. It hopefully won't be client side @@ -232,7 +250,9 @@ module.exports = React.createClass({ }, onFilterChange: function(alias) { - this.filterString = alias || null; + this.setState({ + filterString: alias || null, + }); // don't send the request for a little bit, // no point hammering the server with a @@ -248,17 +268,52 @@ module.exports = React.createClass({ }, onFilterClear: function() { - this.filterString = null; + // update immediately + this.setState({ + filterString: null, + }, this.refreshRoomList); if (this.filterTimeout) { clearTimeout(this.filterTimeout); } - // update immediately - this.refreshRoomList(); }, onJoinClick: function(alias) { - this.showRoomAlias(alias); + // If we're on the 'Matrix' network (or all networks), + // just show that rooms alias + if (this.state.network == null || this.state.network == '_matrix') { + this.showRoomAlias(alias); + } else { + // This is a 3rd party protocol. Let's see if we + // can join it + const fields = this._getFieldsForThirdPartyLocation(alias, this.state.network); + if (!fields) { + const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + Modal.createDialog(ErrorDialog, { + title: "Unable to join network", + description: "Riot does not know how to join a room on this network", + }); + return; + } + const protocol = this._protocolForThirdPartyNetwork(this.state.network); + MatrixClientPeg.get().getThirdpartyLocation(protocol, fields).done((resp) => { + if (resp.length > 0 && resp[0].alias) { + this.showRoomAlias(resp[0].alias); + } else { + const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + Modal.createDialog(ErrorDialog, { + title: "Room not found", + description: "Couldn't find a matching Matrix room", + }); + } + }, (e) => { + const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + Modal.createDialog(ErrorDialog, { + title: "Fetching third party location failed", + description: "Unable to look up room ID from server", + }); + }); + } }, showRoomAlias: function(alias) { @@ -311,8 +366,8 @@ module.exports = React.createClass({ if (!this.state.publicRooms) return []; var rooms = this.state.publicRooms.filter((a) => { - if (this.state.filterByNetwork) { - if (!this._isRoomInNetwork(a, this.state.roomServer, this.state.filterByNetwork)) return false; + if (this.state.network) { + if (!this._isRoomInNetwork(a, this.state.roomServer, this.state.network)) return false; } return true; @@ -382,7 +437,7 @@ 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, server, network) { + _isRoomInNetwork: function(room, server, network) { // We carve rooms into two categories here. 'portal' rooms are // rooms created by a user joining a bridge 'portal' alias to // participate in that room or a foreign network. A room is a @@ -396,7 +451,7 @@ module.exports = React.createClass({ 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]; + const pat = this.portalRoomPatterns[n]; if (pat && pat.test(room.aliases[0])) { roomNetwork = n; } @@ -406,6 +461,59 @@ module.exports = React.createClass({ return roomNetwork == network; }, + _stringLooksLikeId: function(s, network) { + let pat = /^#[^\s]+:[^\s]/; + if ( + network && network != '_matrix' && + this.nativePatterns[network] + ) { + pat = this.nativePatterns[network]; + } + + return pat.test(s); + }, + + _protocolForThirdPartyNetwork: function(network) { + if ( + this.props.config.networks && + this.props.config.networks[network] && + this.props.config.networks[network].protocol + ) { + return this.props.config.networks[network].protocol; + } + }, + + _getFieldsForThirdPartyLocation: function(user_input, network) { + const getfields_funcs = { + irc: (user_input, network) => { + if (!this.protocols.irc) return; + const domain = this.props.config.networks[network].domain; + // search through to make sure this is a domain actually + // recognised by the HS + for (const inst of this.protocols.irc.instances) { + if (inst.fields && inst.fields.domain == domain) { + return { + domain: domain, + channel: user_input, + } + } + } + return null; + }, + gitter: (user_input, network) => { + if (!this.protocols.gitter) return; + return { + room: user_input, + } + }, + }; + + const protocol = this._protocolForThirdPartyNetwork(network); + if (getfields_funcs[protocol]) { + return getfields_funcs[protocol](user_input, network); + } + }, + render: function() { let content; if (this.state.loading) { @@ -430,6 +538,23 @@ module.exports = React.createClass({ ; } + let placeholder = 'Search for a room'; + if (this.state.network === null || this.state.network === '_matrix') { + placeholder = '#example:' + this.state.roomServer; + } else if ( + this.props.config.networks && + this.props.config.networks[this.state.network] && + this.props.config.networks[this.state.network].example && + this._getFieldsForThirdPartyLocation(this.state.filterString, this.state.network) + ) { + placeholder = this.props.config.networks[this.state.network].example; + } + + const showJoinButton = ( + this._stringLooksLikeId(this.state.filterString, this.state.network) && + this._getFieldsForThirdPartyLocation(this.state.filterString, this.state.network) + ); + const SimpleRoomHeader = sdk.getComponent('rooms.SimpleRoomHeader'); const NetworkDropdown = sdk.getComponent('directory.NetworkDropdown'); const DirectorySearchBox = sdk.getComponent('elements.DirectorySearchBox'); @@ -441,6 +566,7 @@ module.exports = React.createClass({ diff --git a/src/components/views/directory/NetworkDropdown.js b/src/components/views/directory/NetworkDropdown.js index e6596319..cacea190 100644 --- a/src/components/views/directory/NetworkDropdown.js +++ b/src/components/views/directory/NetworkDropdown.js @@ -40,7 +40,7 @@ export default class NetworkDropdown extends React.Component { this.props.config.serverConfig && this.props.config.serverConfig[server] && this.props.config.serverConfig[server].networks && - '_matrix' in this.props.config.serverConfig[server].networks + this.props.config.serverConfig[server].networks.indexOf('_matrix') > -1 ) { defaultNetwork = '_matrix'; } @@ -170,8 +170,19 @@ export default class NetworkDropdown extends React.Component { icon = ; span_class = 'mx_NetworkDropdown_menu_network'; } else { - name = this.props.config.networkNames[network]; - icon = ; + if (this.props.config.networks[network]) { + if (this.props.config.networks[network].name) { + name = this.props.config.networks[network].name; + } else { + name = network; + } + if (this.props.config.networks[network].icon) { + icon = ; + } else { + icon = ; + } + } + span_class = 'mx_NetworkDropdown_menu_network'; } @@ -199,6 +210,7 @@ export default class NetworkDropdown extends React.Component { ; current_value = } else { current_value = this._makeMenuOption( diff --git a/vector/config.sample.json b/vector/config.sample.json index ad7cec95..fd4a0b31 100644 --- a/vector/config.sample.json +++ b/vector/config.sample.json @@ -15,24 +15,53 @@ "_matrix", "gitter", "irc:freenode", - "irc:mozilla" + "irc:mozilla", + "irc:snoonet", + "irc:oftc" ] } }, - "networkPatterns": { - "gitter": "#gitter_.*:matrix.org", - "irc:freenode": "#freenode_.*:matrix.org", - "irc:mozilla": "#mozilla_.*:matrix.org" - }, - "networkNames": { - "irc:freenode": "Freenode", - "irc:mozilla": "Mozilla", - "gitter": "Gitter" - }, - "networkIcons": { - "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" + "networks": { + "gitter": { + "protocol": "gitter", + "portalRoomPattern": "#gitter_.*:matrix.org", + "name": "Gitter", + "icon": "//gitter.im/favicon.ico", + "example": "org/community", + "nativePattern": "[^\\s]+/[^\\s]+$" + }, + "irc:freenode": { + "portalRoomPattern": "#freenode_.*:matrix.org", + "name": "Freenode", + "icon": "//matrix.org/_matrix/media/v1/download/matrix.org/DHLHpDDgWNNejFmrewvwEAHX", + "example": "#channel", + "nativePattern": "^#[^\\s]+$" + }, + "irc:mozilla": { + "portalRoomPattern": "#mozilla_.*:matrix.org", + "name": "Mozilla", + "icon": "//matrix.org/_matrix/media/v1/download/matrix.org/DHLHpDDgWNNejFmrewvwEAHX", + "example": "#channel", + "nativePattern": "^#[^\\s]+$" + }, + "irc:snoonet": { + "protocol": "irc", + "domain": "ipv6-irc.snoonet.org", + "portalRoomPattern": "#_snoonet_.*:matrix.org", + "name": "Snoonet", + "icon": "//matrix.org/_matrix/media/v1/download/matrix.org/DHLHpDDgWNNejFmrewvwEAHX", + "example": "#channel", + "nativePattern": "^#[^\\s]+$" + }, + "irc:oftc": { + "protocol": "irc", + "domain": "irc.oftc.net", + "portalRoomPattern": "#_oftc_.*:matrix.org", + "name": "OFTC", + "icon": "//matrix.org/_matrix/media/v1/download/matrix.org/DHLHpDDgWNNejFmrewvwEAHX", + "example": "#channel", + "nativePattern": "^#[^\\s]+$" + } } } } From b71b1b55358d502bd3106b11c41821563112ef1c Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 30 Sep 2016 09:39:30 +0100 Subject: [PATCH 02/10] Use more variables --- src/components/structures/RoomDirectory.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/components/structures/RoomDirectory.js b/src/components/structures/RoomDirectory.js index 4c306a7a..5fc848cb 100644 --- a/src/components/structures/RoomDirectory.js +++ b/src/components/structures/RoomDirectory.js @@ -64,11 +64,12 @@ module.exports = React.createClass({ this.nativePatterns = {}; if (this.props.config.networks) { for (const network of Object.keys(this.props.config.networks)) { - if (this.props.config.networks[network].portalRoomPattern) { - this.portalRoomPatterns[network] = new RegExp(this.props.config.networks[network].portalRoomPattern); + const network_info = this.props.config.networks[network]; + if (network_info.portalRoomPattern) { + this.portalRoomPatterns[network] = new RegExp(network_info.portalRoomPattern); } - if (this.props.config.networks[network].nativePattern) { - this.nativePatterns[network] = new RegExp(this.props.config.networks[network].nativePattern); + if (network_info.nativePattern) { + this.nativePatterns[network] = new RegExp(network_info.nativePattern); } } } From 776fe2ad708c60d62e09e697882402d460332e12 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 3 Oct 2016 13:42:55 +0100 Subject: [PATCH 03/10] Different way of getting fields for 3p location Try to match protocol insance from 'domain' field and use its fields for all but the last field. Assume the last takes the user input. --- src/components/structures/RoomDirectory.js | 67 +++++++++++++--------- 1 file changed, 41 insertions(+), 26 deletions(-) diff --git a/src/components/structures/RoomDirectory.js b/src/components/structures/RoomDirectory.js index 5fc848cb..1b89ee2a 100644 --- a/src/components/structures/RoomDirectory.js +++ b/src/components/structures/RoomDirectory.js @@ -485,34 +485,49 @@ module.exports = React.createClass({ }, _getFieldsForThirdPartyLocation: function(user_input, network) { - const getfields_funcs = { - irc: (user_input, network) => { - if (!this.protocols.irc) return; - const domain = this.props.config.networks[network].domain; - // search through to make sure this is a domain actually - // recognised by the HS - for (const inst of this.protocols.irc.instances) { - if (inst.fields && inst.fields.domain == domain) { - return { - domain: domain, - channel: user_input, - } - } - } - return null; - }, - gitter: (user_input, network) => { - if (!this.protocols.gitter) return; - return { - room: user_input, - } - }, - }; + if (!this.props.config.networks || !this.props.config.networks[network]) return null; - const protocol = this._protocolForThirdPartyNetwork(network); - if (getfields_funcs[protocol]) { - return getfields_funcs[protocol](user_input, network); + const network_info = this.props.config.networks[network]; + if (!network_info.protocol) return null; + + if (!this.protocols) return null; + + let instance; + // Try to find which instance in the 'protocols' response + // matches this network. We look for a matching protocol + // and the existence of a 'domain' field and if present, + // its value. + if (this.protocols[network_info.protocol].instances.length == 1) { + const the_instance = this.protocols[network_info.protocol].instances[0]; + // If there's only one instance in this protocol, use it + // as long as it has no domain (which we assume to mean it's + // there is only one possible instance). + if (the_instance.fields.domain === undefined && network_info.domain === undefined) { + instance = this.protocols[network_info.protocol].instances[0]; + } + } else if (network_info.domain) { + // otherwise, we look for one with a matching domain. + for (const this_instance of this.protocols[network_info.protocol].instances) { + if (this_instance.fields.domain == network_info.domain) { + instance = this_instance; + } + } } + + if (instance === undefined) return null; + + // now make an object with the fields specified by that protocol. We + // require that the values of all but the last field come from the + // instance. The last is the user input. + const required_fields = this.protocols[network_info.protocol].location_fields; + const fields = {}; + for (let i = 0; i < required_fields.length - 1; ++i) { + const this_field = required_fields[i]; + if (instance.fields[this_field] === undefined) return null; + fields[this_field] = instance.fields[this_field]; + } + fields[required_fields[required_fields.length - 1]] = user_input; + return fields; }, render: function() { From 000ca35727ff20389d37c4391b76d11dad465394 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 3 Oct 2016 16:30:57 +0100 Subject: [PATCH 04/10] Don't pop up errors when this API fails for guests --- src/components/structures/RoomDirectory.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/components/structures/RoomDirectory.js b/src/components/structures/RoomDirectory.js index 1b89ee2a..a232d845 100644 --- a/src/components/structures/RoomDirectory.js +++ b/src/components/structures/RoomDirectory.js @@ -82,6 +82,12 @@ module.exports = React.createClass({ MatrixClientPeg.get().getThirdpartyProtocols().done((response) => { this.protocols = response; }, (err) => { + if (MatrixClientPeg.get().isGuest()) { + // Guests currently aren't allowed to use this API, so + // ignore this as otherwise this error is literally the + // thing you see when loading the client! + return; + } const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); Modal.createDialog(ErrorDialog, { title: "Failed to get protocol list from Home Server", From 304e5b997aee292f2ca1da379318b14e8f6d95e4 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 4 Oct 2016 11:04:01 +0100 Subject: [PATCH 05/10] PR Freedback --- src/components/structures/RoomDirectory.js | 14 +++++------ .../views/directory/NetworkDropdown.js | 25 ++++++++++--------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/components/structures/RoomDirectory.js b/src/components/structures/RoomDirectory.js index a232d845..3117d52f 100644 --- a/src/components/structures/RoomDirectory.js +++ b/src/components/structures/RoomDirectory.js @@ -132,7 +132,7 @@ module.exports = React.createClass({ opts.server = my_server; } if (this.nextBatch) opts.since = this.nextBatch; - if (this.state.filterString) opts.filter = { generic_search_term: my_filter_string } ; + if (my_filter_string) opts.filter = { generic_search_term: my_filter_string } ; return MatrixClientPeg.get().publicRooms(opts).then((data) => { if ( my_filter_string != this.state.filterString || @@ -498,7 +498,7 @@ module.exports = React.createClass({ if (!this.protocols) return null; - let instance; + let matched_instance; // Try to find which instance in the 'protocols' response // matches this network. We look for a matching protocol // and the existence of a 'domain' field and if present, @@ -509,18 +509,18 @@ module.exports = React.createClass({ // as long as it has no domain (which we assume to mean it's // there is only one possible instance). if (the_instance.fields.domain === undefined && network_info.domain === undefined) { - instance = this.protocols[network_info.protocol].instances[0]; + matched_instance = this.protocols[network_info.protocol].instances[0]; } } else if (network_info.domain) { // otherwise, we look for one with a matching domain. for (const this_instance of this.protocols[network_info.protocol].instances) { if (this_instance.fields.domain == network_info.domain) { - instance = this_instance; + matched_instance = this_instance; } } } - if (instance === undefined) return null; + if (matched_instance === undefined) return null; // now make an object with the fields specified by that protocol. We // require that the values of all but the last field come from the @@ -529,8 +529,8 @@ module.exports = React.createClass({ const fields = {}; for (let i = 0; i < required_fields.length - 1; ++i) { const this_field = required_fields[i]; - if (instance.fields[this_field] === undefined) return null; - fields[this_field] = instance.fields[this_field]; + if (matched_instance.fields[this_field] === undefined) return null; + fields[this_field] = matched_instance.fields[this_field]; } fields[required_fields[required_fields.length - 1]] = user_input; return fields; diff --git a/src/components/views/directory/NetworkDropdown.js b/src/components/views/directory/NetworkDropdown.js index cacea190..c113fbc4 100644 --- a/src/components/views/directory/NetworkDropdown.js +++ b/src/components/views/directory/NetworkDropdown.js @@ -170,17 +170,18 @@ export default class NetworkDropdown extends React.Component { icon = ; span_class = 'mx_NetworkDropdown_menu_network'; } else { - if (this.props.config.networks[network]) { - if (this.props.config.networks[network].name) { - name = this.props.config.networks[network].name; - } else { - name = network; - } - if (this.props.config.networks[network].icon) { - icon = ; - } else { - icon = ; - } + if (this.props.config.networks[network] === undefined) { + throw new Error(network + ' network missing from config'); + } + if (this.props.config.networks[network].name) { + name = this.props.config.networks[network].name; + } else { + name = network; + } + if (this.props.config.networks[network].icon) { + icon = ; + } else { + icon = ; } span_class = 'mx_NetworkDropdown_menu_network'; @@ -210,7 +211,7 @@ export default class NetworkDropdown extends React.Component { ; current_value = } else { current_value = this._makeMenuOption( From b80b08f04f8174cdfe9eb51fe735a67fe1c88c6c Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 4 Oct 2016 11:14:36 +0100 Subject: [PATCH 06/10] Specify width on icons & comment --- src/components/views/directory/NetworkDropdown.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/views/directory/NetworkDropdown.js b/src/components/views/directory/NetworkDropdown.js index c113fbc4..eb60c4a5 100644 --- a/src/components/views/directory/NetworkDropdown.js +++ b/src/components/views/directory/NetworkDropdown.js @@ -179,7 +179,9 @@ export default class NetworkDropdown extends React.Component { name = network; } if (this.props.config.networks[network].icon) { - icon = ; + // omit height here so if people define a non-square logo in the config, it + // will keep the aspect when it scales + icon = ; } else { icon = ; } From de9bf4bb472d0ae0227069b9b85fac0b6a8fd2f7 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 4 Oct 2016 15:18:07 +0100 Subject: [PATCH 07/10] Actually use variable --- src/components/structures/RoomDirectory.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/RoomDirectory.js b/src/components/structures/RoomDirectory.js index 3117d52f..ccb4c794 100644 --- a/src/components/structures/RoomDirectory.js +++ b/src/components/structures/RoomDirectory.js @@ -509,7 +509,7 @@ module.exports = React.createClass({ // as long as it has no domain (which we assume to mean it's // there is only one possible instance). if (the_instance.fields.domain === undefined && network_info.domain === undefined) { - matched_instance = this.protocols[network_info.protocol].instances[0]; + matched_instance = the_instance; } } else if (network_info.domain) { // otherwise, we look for one with a matching domain. From c0a457406965012534c22188bab49614fe84a50f Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 4 Oct 2016 15:30:46 +0100 Subject: [PATCH 08/10] For single instance, allow domain present & match --- src/components/structures/RoomDirectory.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/components/structures/RoomDirectory.js b/src/components/structures/RoomDirectory.js index ccb4c794..d066932f 100644 --- a/src/components/structures/RoomDirectory.js +++ b/src/components/structures/RoomDirectory.js @@ -508,7 +508,16 @@ module.exports = React.createClass({ // If there's only one instance in this protocol, use it // as long as it has no domain (which we assume to mean it's // there is only one possible instance). - if (the_instance.fields.domain === undefined && network_info.domain === undefined) { + if ( + ( + the_instance.fields.domain === undefined && + network_info.domain === undefined + ) || + ( + the_instance.fields.domain !== undefined && + the_instance.fields.domain == network_info.domain + ) + ) { matched_instance = the_instance; } } else if (network_info.domain) { From 381c9009fb9b870243d8713767e5b9fdc49d6b21 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 4 Oct 2016 16:21:39 +0100 Subject: [PATCH 09/10] Doc roomDirectory config section --- README.md | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 394e7c35..bfe74ef5 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,32 @@ You can configure the app by copying `vector/config.sample.json` to addresses) to matrix IDs: see http://matrix.org/docs/spec/identity_service/unstable.html for more details. Currently the only public matrix identity servers are https://matrix.org and https://vector.im. In future identity servers will be decentralised. - +1. `roomDirectory`: config for the public room directory. This section encodes behaviour + on the room directory screen for filtering the list by server / network type and joining + third party networks. This config section will disappear once APIs are available to + get this information for home servers. This section is optional. +1. `roomDirectory.servers`: List of other Home Servers' directories to include in the drop + down list. Optional. +1. `roomDirectory.serverConfig`: Config for each server in `roomDirectory.servers`. Optional. +1. `roomDirectory.serverConfig..networks`: List of networks (named + in `roomDirectory.networks`) to include for this server. Optional. +1. `roomDirectory.networks`: config for each network type. Optional. +1. `roomDirectory..name`: Human-readable name for the network. Required. +1. `roomDirectory..protocol`: Protocol as given by the server in + `/_matrix/client/unstable/thirdparty/protocolss` response. Required to be able to join + this type of third party network. +1. `roomDirectory..domain`: Domain as given by the server in + `/_matrix/client/unstable/thirdparty/protocols` response, if present. Required to be + able to join this type of third party network, if present in `thirdparty/protocols`. +1. `roomDirectory..portalRoomPattern`: Regular expression matching aliases + for portal rooms to locations on this network. Required. +1. `roomDirectory..icon`: URL to an icon to be displayed for this network. Required. +1. `roomDirectory..example`: Textual example of a location on this network, + eg. '#channel' for an IRC network. Optional. +1. `roomDirectory..nativePattern`: Regular expression that matches a + valid location on this network. This is used as a hint to the user to indicate + when a valid location has been entered so it's not necessary for this to be + exactly correct. Optional. Running as a Desktop app ======================== From 562b0473626dda4e6a6b34c059221616aca6dc09 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 5 Oct 2016 13:49:30 +0100 Subject: [PATCH 10/10] Typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bfe74ef5..6b819cce 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ You can configure the app by copying `vector/config.sample.json` to 1. `roomDirectory.networks`: config for each network type. Optional. 1. `roomDirectory..name`: Human-readable name for the network. Required. 1. `roomDirectory..protocol`: Protocol as given by the server in - `/_matrix/client/unstable/thirdparty/protocolss` response. Required to be able to join + `/_matrix/client/unstable/thirdparty/protocols` response. Required to be able to join this type of third party network. 1. `roomDirectory..domain`: Domain as given by the server in `/_matrix/client/unstable/thirdparty/protocols` response, if present. Required to be