Compare commits
59 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
902bf3a0a6 | ||
|
|
f736e5243c | ||
|
|
fb2c2ed09a | ||
|
|
ea38968be9 | ||
|
|
562b047362 | ||
|
|
381c9009fb | ||
|
|
c0a4574069 | ||
|
|
de9bf4bb47 | ||
|
|
c57bfccb9e | ||
|
|
52cae92dd7 | ||
|
|
b80b08f04f | ||
|
|
304e5b997a | ||
|
|
6b4dbfbd62 | ||
|
|
000ca35727 | ||
|
|
776fe2ad70 | ||
|
|
602727b7ad | ||
|
|
56deea9f10 | ||
|
|
8e0be29c13 | ||
|
|
b71b1b5535 | ||
|
|
5b3524f174 | ||
|
|
f9a70a8b04 | ||
|
|
b652ea5024 | ||
|
|
2a37a0cb19 | ||
|
|
761600f325 | ||
|
|
65f14c7d21 | ||
|
|
864ba52bc5 | ||
|
|
fdb5020c0c | ||
|
|
62344b5194 | ||
|
|
0db12fcd22 | ||
|
|
455ee4f91b | ||
|
|
5ca391f914 | ||
|
|
a32abae5a3 | ||
|
|
b2dd3ecf3a | ||
|
|
cc7017b62d | ||
|
|
f852e8a01d | ||
|
|
6ff1c30a4b | ||
|
|
85ea45a64a | ||
|
|
7b258bc229 | ||
|
|
5ba3499f56 | ||
|
|
f4a6a3c4b2 | ||
|
|
5967dc4983 | ||
|
|
bf58c340bc | ||
|
|
dc0b15bdfa | ||
|
|
be6fbc2432 | ||
|
|
51a5542446 | ||
|
|
3938abc5dd | ||
|
|
4961a97ed4 | ||
|
|
fb477fad1e | ||
|
|
a070bccffb | ||
|
|
8e1105b12a | ||
|
|
93e4a04118 | ||
|
|
6e3313b461 | ||
|
|
2e77b0a3c7 | ||
|
|
16c22e07b8 | ||
|
|
99b0f9eb7c | ||
|
|
5248bdd974 | ||
|
|
157ae836c0 | ||
|
|
2639d10f97 | ||
|
|
cc5c636fa5 |
26
CHANGELOG.md
@@ -1,3 +1,29 @@
|
||||
Changes in [0.8.2](https://github.com/vector-im/vector-web/releases/tag/v0.8.2) (2016-10-05)
|
||||
============================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/vector-web/compare/v0.8.1...v0.8.2)
|
||||
|
||||
* Add native joining of 3p networks to room dir
|
||||
[\#2379](https://github.com/vector-im/vector-web/pull/2379)
|
||||
* Update to linkify 2.1.3
|
||||
[\#2406](https://github.com/vector-im/vector-web/pull/2406)
|
||||
* Use 'Sign In' / 'Sign Out' universally
|
||||
[\#2383](https://github.com/vector-im/vector-web/pull/2383)
|
||||
* Prevent network dropdown resizing slightly
|
||||
[\#2382](https://github.com/vector-im/vector-web/pull/2382)
|
||||
* Room directory: indicate when there are no results
|
||||
[\#2380](https://github.com/vector-im/vector-web/pull/2380)
|
||||
* Room dir: New filtering & 3rd party networks
|
||||
[\#2362](https://github.com/vector-im/vector-web/pull/2362)
|
||||
* Update linkify version
|
||||
[\#2359](https://github.com/vector-im/vector-web/pull/2359)
|
||||
* Directory search join button
|
||||
[\#2339](https://github.com/vector-im/vector-web/pull/2339)
|
||||
|
||||
Changes in [0.8.1](https://github.com/vector-im/vector-web/releases/tag/v0.8.1) (2016-09-21)
|
||||
============================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/vector-web/compare/v0.8.0...v0.8.1)
|
||||
|
||||
|
||||
Changes in [0.8.0](https://github.com/vector-im/vector-web/releases/tag/v0.8.0) (2016-09-21)
|
||||
============================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/vector-web/compare/v0.7.5-r3...v0.8.0)
|
||||
|
||||
27
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.<server_name>.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.<network_type>.name`: Human-readable name for the network. Required.
|
||||
1. `roomDirectory.<network_type>.protocol`: Protocol as given by the server in
|
||||
`/_matrix/client/unstable/thirdparty/protocols` response. Required to be able to join
|
||||
this type of third party network.
|
||||
1. `roomDirectory.<network_type>.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.<network_type>.portalRoomPattern`: Regular expression matching aliases
|
||||
for portal rooms to locations on this network. Required.
|
||||
1. `roomDirectory.<network_type>.icon`: URL to an icon to be displayed for this network. Required.
|
||||
1. `roomDirectory.<network_type>.example`: Textual example of a location on this network,
|
||||
eg. '#channel' for an IRC network. Optional.
|
||||
1. `roomDirectory.<network_type>.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
|
||||
========================
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vector-web",
|
||||
"version": "0.8.0",
|
||||
"version": "0.8.2",
|
||||
"description": "Vector webapp",
|
||||
"author": "matrix.org",
|
||||
"repository": {
|
||||
@@ -46,9 +46,9 @@
|
||||
"gemini-scrollbar": "matrix-org/gemini-scrollbar#b302279",
|
||||
"gfm.css": "^1.1.1",
|
||||
"highlight.js": "^9.0.0",
|
||||
"linkifyjs": "2.0.0-beta.4",
|
||||
"matrix-js-sdk": "0.6.0",
|
||||
"matrix-react-sdk": "0.7.1",
|
||||
"linkifyjs": "^2.1.3",
|
||||
"matrix-js-sdk": "0.6.2",
|
||||
"matrix-react-sdk": "0.7.3",
|
||||
"modernizr": "^3.1.0",
|
||||
"q": "^1.4.1",
|
||||
"react": "^15.2.1",
|
||||
|
||||
@@ -52,22 +52,48 @@ 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)) {
|
||||
const network_info = this.props.config.networks[network];
|
||||
if (network_info.portalRoomPattern) {
|
||||
this.portalRoomPatterns[network] = new RegExp(network_info.portalRoomPattern);
|
||||
}
|
||||
if (network_info.nativePattern) {
|
||||
this.nativePatterns[network] = new RegExp(network_info.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) => {
|
||||
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",
|
||||
description: "The Home Server may be too old to support third party networks",
|
||||
});
|
||||
});
|
||||
|
||||
// dis.dispatch({
|
||||
// action: 'ui_opacity',
|
||||
@@ -76,10 +102,6 @@ module.exports = React.createClass({
|
||||
// });
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this.refreshRoomList();
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
// dis.dispatch({
|
||||
// action: 'ui_opacity',
|
||||
@@ -98,13 +120,26 @@ module.exports = React.createClass({
|
||||
},
|
||||
|
||||
getMoreRooms: function() {
|
||||
const my_filter_string = this.filterString;
|
||||
if (!MatrixClientPeg.get()) return q();
|
||||
|
||||
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.
|
||||
const my_next_batch = this.nextBatch;
|
||||
const opts = {limit: 20};
|
||||
if (my_server != MatrixClientPeg.getHomeServerName()) {
|
||||
opts.server = my_server;
|
||||
}
|
||||
if (this.nextBatch) opts.since = this.nextBatch;
|
||||
if (this.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.filterString) {
|
||||
// if the filter has changed since this request was sent,
|
||||
if (
|
||||
my_filter_string != this.state.filterString ||
|
||||
my_server != this.state.roomServer ||
|
||||
my_next_batch != this.nextBatch)
|
||||
{
|
||||
// if the filter or server has changed since this request was sent,
|
||||
// throw away the result (don't even clear the busy flag
|
||||
// since we must still have a request in flight)
|
||||
return;
|
||||
@@ -118,7 +153,11 @@ module.exports = React.createClass({
|
||||
});
|
||||
return Boolean(data.next_batch);
|
||||
}, (err) => {
|
||||
if (my_filter_string != this.filterString) {
|
||||
if (
|
||||
my_filter_string != this.state.filterString ||
|
||||
my_server != this.state.roomServer ||
|
||||
my_next_batch != this.nextBatch)
|
||||
{
|
||||
// as above: we don't care about errors for old
|
||||
// requests either
|
||||
return;
|
||||
@@ -192,18 +231,23 @@ module.exports = React.createClass({
|
||||
}
|
||||
},
|
||||
|
||||
onNetworkChange: function(network) {
|
||||
onOptionChange: function(server, network) {
|
||||
// clear next batch so we don't try to load more rooms
|
||||
this.nextBatch = null;
|
||||
this.setState({
|
||||
filterByNetwork: network,
|
||||
}, () => {
|
||||
// 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();
|
||||
});
|
||||
// Clear the public rooms out here otherwise we needlessly
|
||||
// spend time filtering lots of rooms when we're about to
|
||||
// to clear the list anyway.
|
||||
publicRooms: [],
|
||||
roomServer: server,
|
||||
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
|
||||
// for very long, and we may have fetched a thousand rooms to
|
||||
// find the five gitter ones, at which point we do not want
|
||||
// to render all those rooms when switching back to 'all networks'.
|
||||
// Easiest to just blow away the state & re-fetch.
|
||||
},
|
||||
|
||||
onFillRequest: function(backwards) {
|
||||
@@ -212,10 +256,10 @@ module.exports = React.createClass({
|
||||
return this.getMoreRooms();
|
||||
},
|
||||
|
||||
onFilterChange: function(ev) {
|
||||
const alias = ev.target.value;
|
||||
|
||||
this.filterString = alias || null;
|
||||
onFilterChange: function(alias) {
|
||||
this.setState({
|
||||
filterString: alias || null,
|
||||
});
|
||||
|
||||
// don't send the request for a little bit,
|
||||
// no point hammering the server with a
|
||||
@@ -230,9 +274,52 @@ module.exports = React.createClass({
|
||||
}, 300);
|
||||
},
|
||||
|
||||
onFilterKeyUp: function(ev) {
|
||||
if (ev.key == "Enter") {
|
||||
this.showRoomAlias(ev.target.value);
|
||||
onFilterClear: function() {
|
||||
// update immediately
|
||||
this.setState({
|
||||
filterString: null,
|
||||
}, this.refreshRoomList);
|
||||
|
||||
if (this.filterTimeout) {
|
||||
clearTimeout(this.filterTimeout);
|
||||
}
|
||||
},
|
||||
|
||||
onJoinClick: function(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",
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
@@ -286,8 +373,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.filterByNetwork)) return false;
|
||||
if (this.state.network) {
|
||||
if (!this._isRoomInNetwork(a, this.state.roomServer, this.state.network)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -357,14 +444,105 @@ 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;
|
||||
_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
|
||||
// portal room if it has exactly one alias and that alias matches
|
||||
// a pattern defined in the config. Its 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.portalRoomPatterns[n];
|
||||
if (pat && pat.test(room.aliases[0])) {
|
||||
roomNetwork = n;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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) {
|
||||
if (!this.props.config.networks || !this.props.config.networks[network]) return null;
|
||||
|
||||
const network_info = this.props.config.networks[network];
|
||||
if (!network_info.protocol) return null;
|
||||
|
||||
if (!this.protocols) return null;
|
||||
|
||||
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,
|
||||
// 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
|
||||
) ||
|
||||
(
|
||||
the_instance.fields.domain !== undefined &&
|
||||
the_instance.fields.domain == network_info.domain
|
||||
)
|
||||
) {
|
||||
matched_instance = the_instance;
|
||||
}
|
||||
} 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) {
|
||||
matched_instance = this_instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
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
|
||||
// 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 (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;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
@@ -375,33 +553,58 @@ module.exports = React.createClass({
|
||||
<Loader />
|
||||
</div>;
|
||||
} else {
|
||||
const ScrollPanel = sdk.getComponent("structures.ScrollPanel");
|
||||
content = <ScrollPanel ref={this.collectScrollPanel}
|
||||
className="mx_RoomDirectory_tableWrapper"
|
||||
onFillRequest={ this.onFillRequest }
|
||||
stickyBottom={false}
|
||||
startAtBottom={false}
|
||||
onResize={function(){}}
|
||||
>
|
||||
<table ref="directory_table" className="mx_RoomDirectory_table">
|
||||
<tbody>
|
||||
{ this.getRows() }
|
||||
</tbody>
|
||||
</table>
|
||||
</ScrollPanel>;
|
||||
const rows = this.getRows();
|
||||
if (rows.length == 0) {
|
||||
content = <i>No rooms to show</i>;
|
||||
} else {
|
||||
const ScrollPanel = sdk.getComponent("structures.ScrollPanel");
|
||||
content = <ScrollPanel ref={this.collectScrollPanel}
|
||||
className="mx_RoomDirectory_tableWrapper"
|
||||
onFillRequest={ this.onFillRequest }
|
||||
stickyBottom={false}
|
||||
startAtBottom={false}
|
||||
onResize={function(){}}
|
||||
>
|
||||
<table ref="directory_table" className="mx_RoomDirectory_table">
|
||||
<tbody>
|
||||
{ this.getRows() }
|
||||
</tbody>
|
||||
</table>
|
||||
</ScrollPanel>;
|
||||
}
|
||||
}
|
||||
|
||||
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');
|
||||
return (
|
||||
<div className="mx_RoomDirectory">
|
||||
<SimpleRoomHeader title="Directory" />
|
||||
<div className="mx_RoomDirectory_list">
|
||||
<div className="mx_RoomDirectory_listheader">
|
||||
<input type="text" placeholder="Find a room by keyword or room ID (#foo:matrix.org)"
|
||||
className="mx_RoomDirectory_input" size="64" onChange={this.onFilterChange} onKeyUp={this.onFilterKeyUp}
|
||||
<DirectorySearchBox
|
||||
className="mx_RoomDirectory_searchbox"
|
||||
onChange={this.onFilterChange} onClear={this.onFilterClear} onJoinClick={this.onJoinClick}
|
||||
placeholder={placeholder} showJoinButton={showJoinButton}
|
||||
/>
|
||||
<NetworkDropdown config={this.props.config} onNetworkChange={this.onNetworkChange} />
|
||||
<NetworkDropdown config={this.props.config} onOptionChange={this.onOptionChange} />
|
||||
</div>
|
||||
{content}
|
||||
</div>
|
||||
|
||||
@@ -15,10 +15,11 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import MatrixClientPeg from 'matrix-react-sdk/lib/MatrixClientPeg';
|
||||
|
||||
export default class NetworkDropdown extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.dropdownRootElement = null;
|
||||
this.ignoreEvent = null;
|
||||
@@ -26,12 +27,28 @@ export default class NetworkDropdown extends React.Component {
|
||||
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.onMenuOptionClick = this.onMenuOptionClick.bind(this);
|
||||
this.onInputKeyUp = this.onInputKeyUp.bind(this);
|
||||
this.collectRoot = this.collectRoot.bind(this);
|
||||
this.collectInputTextBox = this.collectInputTextBox.bind(this);
|
||||
|
||||
this.inputTextBox = null;
|
||||
|
||||
const server = MatrixClientPeg.getHomeServerName();
|
||||
let defaultNetwork = null;
|
||||
if (
|
||||
this.props.config.serverConfig &&
|
||||
this.props.config.serverConfig[server] &&
|
||||
this.props.config.serverConfig[server].networks &&
|
||||
this.props.config.serverConfig[server].networks.indexOf('_matrix') > -1
|
||||
) {
|
||||
defaultNetwork = '_matrix';
|
||||
}
|
||||
|
||||
this.state = {
|
||||
expanded: false,
|
||||
selectedNetwork: null,
|
||||
selectedServer: server,
|
||||
selectedNetwork: defaultNetwork,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -39,12 +56,21 @@ export default class NetworkDropdown extends React.Component {
|
||||
// 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);
|
||||
|
||||
// fire this now so the defaults can be set up
|
||||
this.props.onOptionChange(this.state.selectedServer, this.state.selectedNetwork);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
document.removeEventListener('click', this.onDocumentClick, false);
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
if (this.state.expanded && this.inputTextBox) {
|
||||
this.inputTextBox.focus();
|
||||
}
|
||||
}
|
||||
|
||||
onDocumentClick(ev) {
|
||||
// Close the dropdown if the user clicks anywhere that isn't
|
||||
// within our root element
|
||||
@@ -72,12 +98,24 @@ export default class NetworkDropdown extends React.Component {
|
||||
ev.preventDefault();
|
||||
}
|
||||
|
||||
onNetworkClick(network, ev) {
|
||||
onMenuOptionClick(server, network, ev) {
|
||||
this.setState({
|
||||
expanded: false,
|
||||
selectedServer: server,
|
||||
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) {
|
||||
@@ -90,41 +128,97 @@ export default class NetworkDropdown extends React.Component {
|
||||
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;
|
||||
let icon;
|
||||
let name;
|
||||
let span_class;
|
||||
|
||||
if (network === null) {
|
||||
name = 'All networks';
|
||||
name = server;
|
||||
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 {
|
||||
name = this.props.config.networkNames[network];
|
||||
icon = <img src={this.props.config.networkIcons[network]} />;
|
||||
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) {
|
||||
// omit height here so if people define a non-square logo in the config, it
|
||||
// will keep the aspect when it scales
|
||||
icon = <img src={this.props.config.networks[network].icon} width="16" />;
|
||||
} else {
|
||||
icon = <img src={iconPath} width="16" height="16" />;
|
||||
}
|
||||
|
||||
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}
|
||||
<span className={span_class}>{name}</span>
|
||||
</div>;
|
||||
}
|
||||
|
||||
render() {
|
||||
const current_value = this._optionForNetwork(this.state.selectedNetwork, false);
|
||||
let current_value;
|
||||
|
||||
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));
|
||||
}
|
||||
const menu_options = this._getMenuOptions();
|
||||
menu = <div className="mx_NetworkDropdown_menu">
|
||||
{menu_options}
|
||||
</div>;
|
||||
current_value = <input type="text" className="mx_NetworkDropdown_networkoption"
|
||||
ref={this.collectInputTextBox} onKeyUp={this.onInputKeyUp}
|
||||
placeholder="matrix.org" // 'matrix.org' as an example of an HS name
|
||||
/>
|
||||
} else {
|
||||
current_value = this._makeMenuOption(
|
||||
this.state.selectedServer, this.state.selectedNetwork, false
|
||||
);
|
||||
}
|
||||
|
||||
return <div className="mx_NetworkDropdown" ref={this.collectRoot}>
|
||||
@@ -138,7 +232,7 @@ export default class NetworkDropdown extends React.Component {
|
||||
}
|
||||
|
||||
NetworkDropdown.propTypes = {
|
||||
onNetworkChange: React.PropTypes.func.isRequired,
|
||||
onOptionChange: React.PropTypes.func.isRequired,
|
||||
config: React.PropTypes.object,
|
||||
};
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ module.exports = React.createClass({
|
||||
<div className="mx_GuestWarningBar">
|
||||
<img className="mx_GuestWarningBar_warning" src="img/warning.svg" width="24" height="23" alt="/!\"/>
|
||||
<div>
|
||||
You are Rioting as a guest. <a onClick={this.onRegisterClicked}>Register</a> or <a onClick={this.onLoginClicked}>log in</a> to access more rooms and features.
|
||||
You are Rioting as a guest. <a onClick={this.onRegisterClicked}>Register</a> or <a onClick={this.onLoginClicked}>sign in</a> to access more rooms and features.
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -30,7 +30,7 @@ module.exports = React.createClass({
|
||||
</div>
|
||||
<div className="mx_Dialog_content">
|
||||
<span>
|
||||
You can use the custom server options to log into other Matrix
|
||||
You can use the custom server options to sign into other Matrix
|
||||
servers by specifying a different Home server URL.
|
||||
<br/>
|
||||
This allows you to use Riot with an existing Matrix account on
|
||||
|
||||
@@ -119,7 +119,7 @@ module.exports = React.createClass({
|
||||
var emailPusherPromise;
|
||||
if (event.target.checked) {
|
||||
var data = {}
|
||||
data['brand'] = this.props.brand || 'Vector';
|
||||
data['brand'] = this.props.brand || 'Riot';
|
||||
emailPusherPromise = UserSettingsStore.addEmailPusher(address, data);
|
||||
} else {
|
||||
var emailPusher = UserSettingsStore.getEmailPusher(this.state.pushers, address);
|
||||
|
||||
@@ -47,6 +47,10 @@ limitations under the License.
|
||||
|
||||
/* Overrides for the attachment body tiles */
|
||||
|
||||
.mx_FilePanel .mx_EventTile {
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.mx_FilePanel .mx_EventTile .mx_MImageBody {
|
||||
margin-right: 0px;
|
||||
}
|
||||
@@ -60,7 +64,6 @@ limitations under the License.
|
||||
.mx_FilePanel .mx_EventTile .mx_MImageBody_downloadLink {
|
||||
flex: 1 1 auto;
|
||||
color: #747474;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.mx_FilePanel .mx_EventTile .mx_MImageBody_size {
|
||||
@@ -88,7 +91,6 @@ limitations under the License.
|
||||
font-size: 11px;
|
||||
opacity: 1.0;
|
||||
color: #acacac;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.mx_FilePanel .mx_EventTile .mx_MessageTimestamp {
|
||||
|
||||
@@ -41,10 +41,13 @@ limitations under the License.
|
||||
we should make EventTile a base CSS class and customise it specifically
|
||||
for usage in {Message,File,Notification}Panel. */
|
||||
|
||||
.mx_NotificationPanel .mx_EventTile {
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.mx_NotificationPanel .mx_EventTile_roomName {
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.mx_NotificationPanel .mx_EventTile_roomName a {
|
||||
@@ -63,7 +66,6 @@ limitations under the License.
|
||||
font-size: 12px;
|
||||
display: inline;
|
||||
padding-left: 0px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.mx_NotificationPanel .mx_EventTile_senderDetails {
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
.mx_DirectorySearchBox {
|
||||
position: relative;
|
||||
border-radius: 3px;
|
||||
border: 1px solid #c7c7c7;
|
||||
}
|
||||
|
||||
.mx_DirectorySearchBox_container {
|
||||
display: flex;
|
||||
display: -webkit-flex;
|
||||
padding-left: 9px;
|
||||
padding-right: 9px;
|
||||
}
|
||||
|
||||
.mx_DirectorySearchBox_input {
|
||||
flex-grow: 1;
|
||||
-webkit-flex-grow: 1;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
font-weight: 300;
|
||||
font-size: 13px;
|
||||
}
|
||||
input[type=text].mx_DirectorySearchBox_input:focus {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.mx_DirectorySearchBox_joinButton {
|
||||
display: table-cell;
|
||||
padding: 3px;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
background-color: #efefef;
|
||||
border-radius: 3px;
|
||||
background-image: url('img/icon-return.svg');
|
||||
background-position: 8px 70%;
|
||||
background-repeat: no-repeat;
|
||||
text-indent: 18px;
|
||||
font-weight: 600;
|
||||
font-size: 12px;
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.mx_DirectorySearchBox_clear_wrapper {
|
||||
display: table-cell;
|
||||
}
|
||||
|
||||
.mx_DirectorySearchBox_clear {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
background: url('img/icon_context_delete.svg');
|
||||
background-position: 0 50%;
|
||||
background-repeat: no-repeat;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -53,24 +53,20 @@ limitations under the License.
|
||||
|
||||
.mx_RoomDirectory_listheader {
|
||||
display: table;
|
||||
table-layout: fixed;
|
||||
width: 100%;
|
||||
margin-top: 12px;
|
||||
margin-bottom: 12px;
|
||||
border-spacing: 5px;
|
||||
}
|
||||
|
||||
.mx_RoomDirectory_input {
|
||||
.mx_RoomDirectory_searchbox {
|
||||
display: table-cell;
|
||||
border-radius: 3px;
|
||||
border: 1px solid #c7c7c7;
|
||||
font-weight: 300;
|
||||
font-size: 13px;
|
||||
padding: 9px;
|
||||
}
|
||||
|
||||
.mx_RoomDirectory_listheader .mx_NetworkDropdown {
|
||||
display: table-cell;
|
||||
width: 100%;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.mx_RoomDirectory_tableWrapper {
|
||||
|
||||
@@ -24,8 +24,6 @@ limitations under the License.
|
||||
border: 1px solid #c7c7c7;
|
||||
font-weight: 300;
|
||||
font-size: 13px;
|
||||
margin-top: 12px;
|
||||
margin-bottom: 12px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@@ -54,6 +52,12 @@ limitations under the License.
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
input.mx_NetworkDropdown_networkoption, input.mx_NetworkDropdown_networkoption:focus {
|
||||
border: 0;
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.mx_NetworkDropdown_menu {
|
||||
position: absolute;
|
||||
left: -1px;
|
||||
|
||||
18
src/skins/vector/img/icon-return.svg
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg width="12px" height="11px" viewBox="0 0 12 11" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: sketchtool 39.1 (31720) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>B542A09B-DBBF-41D4-A5FD-D05EE1E6BBC4</title>
|
||||
<desc>Created with sketchtool.</desc>
|
||||
<defs></defs>
|
||||
<g id="Create-group" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="Chat-Directory-search-result-ID" transform="translate(-553.000000, -97.000000)" stroke="#4A4A4A">
|
||||
<g id="icon_return" transform="translate(554.000000, 97.000000)">
|
||||
<polyline id="Rectangle" points="7.5 0.5 10.5 0.5 10.5 6.5 0 6.5"></polyline>
|
||||
<g id="Group" transform="translate(0.000000, 1.954545)" stroke-linecap="square">
|
||||
<path d="M0.227272727,4.40909091 L4.25946916,0.376894528" id="Line"></path>
|
||||
<path d="M0.227272727,8.44128729 L4.25946916,4.40909091" id="Line-Copy-7" transform="translate(2.243371, 6.425189) scale(1, -1) translate(-2.243371, -6.425189) "></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
14
src/skins/vector/img/network-matrix.svg
Normal 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 |
@@ -102,6 +102,7 @@ describe('joining a room', function () {
|
||||
var input = ReactTestUtils.findRenderedDOMComponentWithTag(
|
||||
roomDir, 'input');
|
||||
input.value = ROOM_ALIAS;
|
||||
ReactTestUtils.Simulate.change(input);
|
||||
ReactTestUtils.Simulate.keyUp(input, {key: 'Enter'});
|
||||
|
||||
// that should create a roomview which will start a peek; wait
|
||||
|
||||
@@ -1,38 +1,67 @@
|
||||
{
|
||||
"default_hs_url": "https://matrix.org",
|
||||
"default_is_url": "https://vector.im",
|
||||
"brand": "Vector",
|
||||
"brand": "Riot",
|
||||
"integrations_ui_url": "http://localhost:8081/",
|
||||
"integrations_rest_url": "http://localhost:5050",
|
||||
"enableLabs": true,
|
||||
"roomDirectory": {
|
||||
"networks": [
|
||||
"matrix:example_com",
|
||||
"matrix:matrix_org",
|
||||
"gitter",
|
||||
"irc:freenode",
|
||||
"irc:mozilla"
|
||||
"servers": [
|
||||
"matrix.org"
|
||||
],
|
||||
"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"
|
||||
"serverConfig": {
|
||||
"matrix.org": {
|
||||
"networks": [
|
||||
"_matrix",
|
||||
"gitter",
|
||||
"irc:freenode",
|
||||
"irc:mozilla",
|
||||
"irc:snoonet",
|
||||
"irc:oftc"
|
||||
]
|
||||
}
|
||||
},
|
||||
"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"
|
||||
"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]+$"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="vector-icons/apple-touch-icon-180x180.png">
|
||||
<link rel="manifest" href="vector-icons/manifest.json">
|
||||
<link rel="shortcut icon" href="vector-icons/favicon.ico">
|
||||
<meta name="apple-mobile-web-app-title" content="Vector">
|
||||
<meta name="application-name" content="Vector">
|
||||
<meta name="apple-mobile-web-app-title" content="Riot">
|
||||
<meta name="application-name" content="Riot">
|
||||
<meta name="msapplication-TileColor" content="#da532c">
|
||||
<meta name="msapplication-TileImage" content="vector-icons/mstile-144x144.png">
|
||||
<meta name="msapplication-config" content="vector-icons/browserconfig.xml">
|
||||
@@ -26,7 +26,7 @@
|
||||
<!-- load olm, if possible. -->
|
||||
<script src="olm.js"></script>
|
||||
<script src="bundle.js"></script>
|
||||
<noscript>Sorry, Vector requires JavaScript to be enabled.</noscript>
|
||||
<noscript>Sorry, Riot requires JavaScript to be enabled.</noscript>
|
||||
<link rel="stylesheet" href="bundle.css">
|
||||
<img src="img/warning.svg" width="24" height="23" style="visibility: hidden; position: absolute; top: 0px; left: 0px;"/>
|
||||
<audio id="messageAudio">
|
||||
@@ -53,7 +53,9 @@
|
||||
<script>
|
||||
if (
|
||||
window.location.host === 'www.vector.im' ||
|
||||
window.location.host === 'vector.im'
|
||||
window.location.host === 'vector.im' ||
|
||||
window.location.host === 'www.riot.im' ||
|
||||
window.location.host === 'riot.im'
|
||||
) {
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
@@ -63,19 +65,6 @@
|
||||
ga('create', 'UA-54779209-2', 'auto');
|
||||
ga('send', 'pageview', window.location.pathname + window.location.search + window.location.hash);
|
||||
}
|
||||
else if (
|
||||
window.location.host === 'www.riot.im' ||
|
||||
window.location.host === 'riot.im'
|
||||
) {
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
|
||||
|
||||
ga('create', 'UA-65410662-4', 'auto');
|
||||
ga('send', 'pageview');
|
||||
|
||||
}
|
||||
else {
|
||||
var ga = null;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "Vector",
|
||||
"name": "Riot",
|
||||
"icons": [
|
||||
{
|
||||
"src": "\/icons\/android-chrome-36x36.png",
|
||||
|
||||
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 8.0 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 56 KiB |