Merge branch 'develop' into matthew/homepages
Conflicts: src/component-index.js src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomPreviewBar.scss
This commit is contained in:
commit
1f3ab4425a
178 changed files with 3223 additions and 1151 deletions
|
@ -45,10 +45,8 @@ module.exports = React.createClass({
|
|||
available or experimental in your current browser.
|
||||
</p>
|
||||
<p>
|
||||
Please install <a href="https://www.google.com/chrome">Chrome</a> or
|
||||
<a href="https://getfirefox.com">Firefox</a> for the best experience.
|
||||
<a href="http://apple.com/safari">Safari</a> and
|
||||
<a href="http://opera.com">Opera</a> work too.
|
||||
Please install <a href="https://www.google.com/chrome">Chrome</a> or <a href="https://getfirefox.com">Firefox</a> for
|
||||
the best experience. <a href="http://apple.com/safari">Safari</a> and <a href="http://opera.com">Opera</a> work too.
|
||||
</p>
|
||||
<p>
|
||||
With your current browser, the look and feel of the application may
|
||||
|
|
|
@ -31,6 +31,8 @@ var linkifyMatrix = require('matrix-react-sdk/lib/linkify-matrix');
|
|||
var sanitizeHtml = require('sanitize-html');
|
||||
var q = require('q');
|
||||
|
||||
import {instanceForInstanceId, protocolNameForInstanceId} from '../../utils/DirectoryUtils';
|
||||
|
||||
linkifyMatrix(linkify);
|
||||
|
||||
module.exports = React.createClass({
|
||||
|
@ -42,9 +44,7 @@ module.exports = React.createClass({
|
|||
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
config: {
|
||||
networks: [],
|
||||
},
|
||||
config: {},
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -52,36 +52,26 @@ module.exports = React.createClass({
|
|||
return {
|
||||
publicRooms: [],
|
||||
loading: true,
|
||||
network: null,
|
||||
protocolsLoading: true,
|
||||
instanceId: null,
|
||||
includeAll: false,
|
||||
roomServer: null,
|
||||
filterString: null,
|
||||
}
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
// precompile Regexps
|
||||
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.filterTimeout = null;
|
||||
this.scrollPanel = null;
|
||||
this.protocols = null;
|
||||
|
||||
this.setState({protocolsLoading: true});
|
||||
MatrixClientPeg.get().getThirdpartyProtocols().done((response) => {
|
||||
this.protocols = response;
|
||||
this.setState({protocolsLoading: false});
|
||||
}, (err) => {
|
||||
this.setState({protocolsLoading: false});
|
||||
if (MatrixClientPeg.get().isGuest()) {
|
||||
// Guests currently aren't allowed to use this API, so
|
||||
// ignore this as otherwise this error is literally the
|
||||
|
@ -131,6 +121,11 @@ module.exports = React.createClass({
|
|||
if (my_server != MatrixClientPeg.getHomeServerName()) {
|
||||
opts.server = my_server;
|
||||
}
|
||||
if (this.state.instanceId) {
|
||||
opts.third_party_instance_id = this.state.instanceId;
|
||||
} else if (this.state.includeAll) {
|
||||
opts.include_all_networks = true;
|
||||
}
|
||||
if (this.nextBatch) opts.since = this.nextBatch;
|
||||
if (my_filter_string) opts.filter = { generic_search_term: my_filter_string } ;
|
||||
return MatrixClientPeg.get().publicRooms(opts).then((data) => {
|
||||
|
@ -231,7 +226,7 @@ module.exports = React.createClass({
|
|||
}
|
||||
},
|
||||
|
||||
onOptionChange: function(server, network) {
|
||||
onOptionChange: function(server, instanceId, includeAll) {
|
||||
// clear next batch so we don't try to load more rooms
|
||||
this.nextBatch = null;
|
||||
this.setState({
|
||||
|
@ -240,7 +235,8 @@ module.exports = React.createClass({
|
|||
// to clear the list anyway.
|
||||
publicRooms: [],
|
||||
roomServer: server,
|
||||
network: network,
|
||||
instanceId: instanceId,
|
||||
includeAll: includeAll,
|
||||
}, this.refreshRoomList);
|
||||
// We also refresh the room list each time even though this
|
||||
// filtering is client-side. It hopefully won't be client side
|
||||
|
@ -271,7 +267,7 @@ module.exports = React.createClass({
|
|||
this.filterTimeout = setTimeout(() => {
|
||||
this.filterTimeout = null;
|
||||
this.refreshRoomList();
|
||||
}, 300);
|
||||
}, 700);
|
||||
},
|
||||
|
||||
onFilterClear: function() {
|
||||
|
@ -286,14 +282,19 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
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') {
|
||||
// If we don't have a particular instance id selected, just show that rooms alias
|
||||
if (!this.state.instanceId) {
|
||||
// If the user specified an alias without a domain, add on whichever server is selected
|
||||
// in the dropdown
|
||||
if (alias.indexOf(':') == -1) {
|
||||
alias = alias + ':' + this.state.roomServer;
|
||||
}
|
||||
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);
|
||||
// This is a 3rd party protocol. Let's see if we can join it
|
||||
const protocolName = protocolNameForInstanceId(this.protocols, this.state.instanceId);
|
||||
const instance = instanceForInstanceId(this.protocols, this.state.instanceId);
|
||||
const fields = protocolName ? this._getFieldsForThirdPartyLocation(alias, this.protocols[protocolName], instance) : null;
|
||||
if (!fields) {
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
|
@ -302,8 +303,7 @@ module.exports = React.createClass({
|
|||
});
|
||||
return;
|
||||
}
|
||||
const protocol = this._protocolForThirdPartyNetwork(this.state.network);
|
||||
MatrixClientPeg.get().getThirdpartyLocation(protocol, fields).done((resp) => {
|
||||
MatrixClientPeg.get().getThirdpartyLocation(protocolName, fields).done((resp) => {
|
||||
if (resp.length > 0 && resp[0].alias) {
|
||||
this.showRoomAlias(resp[0].alias);
|
||||
} else {
|
||||
|
@ -372,13 +372,7 @@ module.exports = React.createClass({
|
|||
|
||||
if (!this.state.publicRooms) return [];
|
||||
|
||||
var rooms = this.state.publicRooms.filter((a) => {
|
||||
if (this.state.network) {
|
||||
if (!this._isRoomInNetwork(a, this.state.roomServer, this.state.network)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
var rooms = this.state.publicRooms;
|
||||
var rows = [];
|
||||
var self = this;
|
||||
var guestRead, guestJoin, perms;
|
||||
|
@ -440,119 +434,46 @@ module.exports = React.createClass({
|
|||
this.scrollPanel = element;
|
||||
},
|
||||
|
||||
/**
|
||||
* Terrible temporary function that guess what network a public room
|
||||
* entry is in, until synapse is able to tell us
|
||||
*/
|
||||
_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) {
|
||||
_stringLooksLikeId: function(s, field_type) {
|
||||
let pat = /^#[^\s]+:[^\s]/;
|
||||
if (
|
||||
network && network != '_matrix' &&
|
||||
this.nativePatterns[network]
|
||||
) {
|
||||
pat = this.nativePatterns[network];
|
||||
if (field_type && field_type.regexp) {
|
||||
pat = new RegExp(field_type.regexp);
|
||||
}
|
||||
|
||||
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] &&
|
||||
this.protocols[network_info.protocol].instances &&
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (matched_instance === undefined) return null;
|
||||
|
||||
// now make an object with the fields specified by that protocol. We
|
||||
_getFieldsForThirdPartyLocation: function(userInput, protocol, instance) {
|
||||
// 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 requiredFields = protocol.location_fields;
|
||||
if (!requiredFields) return null;
|
||||
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];
|
||||
for (let i = 0; i < requiredFields.length - 1; ++i) {
|
||||
const thisField = requiredFields[i];
|
||||
if (instance.fields[thisField] === undefined) return null;
|
||||
fields[thisField] = instance.fields[thisField];
|
||||
}
|
||||
fields[required_fields[required_fields.length - 1]] = user_input;
|
||||
fields[requiredFields[requiredFields.length - 1]] = userInput;
|
||||
return fields;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
const SimpleRoomHeader = sdk.getComponent('rooms.SimpleRoomHeader');
|
||||
const Loader = sdk.getComponent("elements.Spinner");
|
||||
|
||||
if (this.state.protocolsLoading) {
|
||||
return (
|
||||
<div className="mx_RoomDirectory">
|
||||
<SimpleRoomHeader title="Directory" />
|
||||
<Loader />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
let content;
|
||||
if (this.state.loading) {
|
||||
const Loader = sdk.getComponent("elements.Spinner");
|
||||
content = <div className="mx_RoomDirectory">
|
||||
<Loader />
|
||||
</div>;
|
||||
|
@ -583,26 +504,35 @@ module.exports = React.createClass({
|
|||
</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)
|
||||
const protocolName = protocolNameForInstanceId(this.protocols, this.state.instanceId);
|
||||
let instance_expected_field_type;
|
||||
if (
|
||||
protocolName &&
|
||||
this.protocols &&
|
||||
this.protocols[protocolName] &&
|
||||
this.protocols[protocolName].location_fields.length > 0 &&
|
||||
this.protocols[protocolName].field_types
|
||||
) {
|
||||
placeholder = this.props.config.networks[this.state.network].example;
|
||||
const last_field = this.protocols[protocolName].location_fields.slice(-1)[0];
|
||||
instance_expected_field_type = this.protocols[protocolName].field_types[last_field];
|
||||
}
|
||||
|
||||
let showJoinButton = this._stringLooksLikeId(this.state.filterString, this.state.network);
|
||||
if (this.state.network && this.state.network != '_matrix') {
|
||||
if (this._getFieldsForThirdPartyLocation(this.state.filterString, this.state.network) === null) {
|
||||
|
||||
let placeholder = 'Search for a room';
|
||||
if (!this.state.instanceId) {
|
||||
placeholder = '#example:' + this.state.roomServer;
|
||||
} else if (instance_expected_field_type) {
|
||||
placeholder = instance_expected_field_type.placeholder;
|
||||
}
|
||||
|
||||
let showJoinButton = this._stringLooksLikeId(this.state.filterString, instance_expected_field_type);
|
||||
if (protocolName) {
|
||||
const instance = instanceForInstanceId(this.protocols, this.state.instanceId);
|
||||
if (this._getFieldsForThirdPartyLocation(this.state.filterString, this.protocols[protocolName], instance) === null) {
|
||||
showJoinButton = false;
|
||||
}
|
||||
}
|
||||
|
||||
const SimpleRoomHeader = sdk.getComponent('rooms.SimpleRoomHeader');
|
||||
const NetworkDropdown = sdk.getComponent('directory.NetworkDropdown');
|
||||
const DirectorySearchBox = sdk.getComponent('elements.DirectorySearchBox');
|
||||
return (
|
||||
|
@ -615,7 +545,7 @@ module.exports = React.createClass({
|
|||
onChange={this.onFilterChange} onClear={this.onFilterClear} onJoinClick={this.onJoinClick}
|
||||
placeholder={placeholder} showJoinButton={showJoinButton}
|
||||
/>
|
||||
<NetworkDropdown config={this.props.config} onOptionChange={this.onOptionChange} />
|
||||
<NetworkDropdown config={this.props.config} protocols={this.protocols} onOptionChange={this.onOptionChange} />
|
||||
</div>
|
||||
{content}
|
||||
</div>
|
||||
|
|
|
@ -22,7 +22,8 @@ module.exports = React.createClass({
|
|||
displayName: 'ViewSource',
|
||||
|
||||
propTypes: {
|
||||
onFinished: React.PropTypes.func.isRequired
|
||||
content: React.PropTypes.object.isRequired,
|
||||
onFinished: React.PropTypes.func.isRequired,
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
|
@ -45,10 +46,9 @@ module.exports = React.createClass({
|
|||
return (
|
||||
<div className="mx_ViewSource">
|
||||
<pre>
|
||||
{JSON.stringify(this.props.mxEvent.event, null, 2)}
|
||||
{JSON.stringify(this.props.content, null, 2)}
|
||||
</pre>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -47,7 +47,16 @@ module.exports = React.createClass({
|
|||
onViewSourceClick: function() {
|
||||
var ViewSource = sdk.getComponent('structures.ViewSource');
|
||||
Modal.createDialog(ViewSource, {
|
||||
mxEvent: this.props.mxEvent
|
||||
content: this.props.mxEvent.event,
|
||||
}, 'mx_Dialog_viewsource');
|
||||
if (this.props.onFinished) this.props.onFinished();
|
||||
},
|
||||
|
||||
onViewClearSourceClick: function() {
|
||||
const ViewSource = sdk.getComponent('structures.ViewSource');
|
||||
Modal.createDialog(ViewSource, {
|
||||
// FIXME: _clearEvent is private
|
||||
content: this.props.mxEvent._clearEvent,
|
||||
}, 'mx_Dialog_viewsource');
|
||||
if (this.props.onFinished) this.props.onFinished();
|
||||
},
|
||||
|
@ -97,6 +106,7 @@ module.exports = React.createClass({
|
|||
var eventStatus = this.props.mxEvent.status;
|
||||
var resendButton;
|
||||
var viewSourceButton;
|
||||
var viewClearSourceButton;
|
||||
var redactButton;
|
||||
var cancelButton;
|
||||
var permalinkButton;
|
||||
|
@ -133,6 +143,14 @@ module.exports = React.createClass({
|
|||
</div>
|
||||
);
|
||||
|
||||
if (this.props.mxEvent.getType() !== this.props.mxEvent.getWireType()) {
|
||||
viewClearSourceButton = (
|
||||
<div className="mx_MessageContextMenu_field" onClick={this.onViewClearSourceClick}>
|
||||
View Decrypted Source
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (this.props.eventTileOps) {
|
||||
if (this.props.eventTileOps.isWidgetHidden()) {
|
||||
unhidePreviewButton = (
|
||||
|
@ -174,6 +192,7 @@ module.exports = React.createClass({
|
|||
{redactButton}
|
||||
{cancelButton}
|
||||
{viewSourceButton}
|
||||
{viewClearSourceButton}
|
||||
{unhidePreviewButton}
|
||||
{permalinkButton}
|
||||
{UserSettingsStore.isFeatureEnabled('rich_text_editor') ? quoteButton : null}
|
||||
|
|
|
@ -120,22 +120,22 @@ module.exports = React.createClass({
|
|||
</div>
|
||||
<div className={ alertMeClasses } onClick={this._onClickAlertMe} >
|
||||
<img className="mx_NotificationStateContextMenu_activeIcon" src="img/notif-active.svg" width="12" height="12" />
|
||||
<img className="mx_NotificationStateContextMenu_icon" src="img/icon-context-mute-off-copy.svg" width="16" height="12" />
|
||||
<img className="mx_NotificationStateContextMenu_icon mx_filterFlipColor" src="img/icon-context-mute-off-copy.svg" width="16" height="12" />
|
||||
All messages (loud)
|
||||
</div>
|
||||
<div className={ allNotifsClasses } onClick={this._onClickAllNotifs} >
|
||||
<img className="mx_NotificationStateContextMenu_activeIcon" src="img/notif-active.svg" width="12" height="12" />
|
||||
<img className="mx_NotificationStateContextMenu_icon" src="img/icon-context-mute-off.svg" width="16" height="12" />
|
||||
<img className="mx_NotificationStateContextMenu_icon mx_filterFlipColor" src="img/icon-context-mute-off.svg" width="16" height="12" />
|
||||
All messages
|
||||
</div>
|
||||
<div className={ mentionsClasses } onClick={this._onClickMentions} >
|
||||
<img className="mx_NotificationStateContextMenu_activeIcon" src="img/notif-active.svg" width="12" height="12" />
|
||||
<img className="mx_NotificationStateContextMenu_icon" src="img/icon-context-mute-mentions.svg" width="16" height="12" />
|
||||
<img className="mx_NotificationStateContextMenu_icon mx_filterFlipColor" src="img/icon-context-mute-mentions.svg" width="16" height="12" />
|
||||
Mentions only
|
||||
</div>
|
||||
<div className={ muteNotifsClasses } onClick={this._onClickMute} >
|
||||
<img className="mx_NotificationStateContextMenu_activeIcon" src="img/notif-active.svg" width="12" height="12" />
|
||||
<img className="mx_NotificationStateContextMenu_icon" src="img/icon-context-mute.svg" width="16" height="12" />
|
||||
<img className="mx_NotificationStateContextMenu_icon mx_filterFlipColor" src="img/icon-context-mute.svg" width="16" height="12" />
|
||||
Mute
|
||||
</div>
|
||||
</div>
|
||||
|
|
127
src/components/views/dialogs/BugReportDialog.js
Normal file
127
src/components/views/dialogs/BugReportDialog.js
Normal file
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
Copyright 2017 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';
|
||||
import sdk from 'matrix-react-sdk';
|
||||
import rageshake from '../../../vector/rageshake';
|
||||
|
||||
export default class BugReportDialog extends React.Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this.state = {
|
||||
sendLogs: true,
|
||||
busy: false,
|
||||
err: null,
|
||||
text: "",
|
||||
};
|
||||
this._onSubmit = this._onSubmit.bind(this);
|
||||
this._onCancel = this._onCancel.bind(this);
|
||||
this._onTextChange = this._onTextChange.bind(this);
|
||||
this._onSendLogsChange = this._onSendLogsChange.bind(this);
|
||||
}
|
||||
|
||||
_onCancel(ev) {
|
||||
this.props.onFinished(false);
|
||||
}
|
||||
|
||||
_onSubmit(ev) {
|
||||
const sendLogs = this.state.sendLogs;
|
||||
const userText = this.state.text;
|
||||
if (!sendLogs && userText.trim().length === 0) {
|
||||
this.setState({
|
||||
err: "Please describe the bug and/or send logs.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.setState({ busy: true, err: null });
|
||||
rageshake.sendBugReport(userText, sendLogs).then(() => {
|
||||
this.setState({ busy: false });
|
||||
this.props.onFinished(false);
|
||||
}, (err) => {
|
||||
this.setState({ busy: false, err: `Failed: ${err.message}` });
|
||||
});
|
||||
}
|
||||
|
||||
_onTextChange(ev) {
|
||||
this.setState({ text: ev.target.value });
|
||||
}
|
||||
|
||||
_onSendLogsChange(ev) {
|
||||
this.setState({ sendLogs: ev.target.checked });
|
||||
}
|
||||
|
||||
render() {
|
||||
const Loader = sdk.getComponent("elements.Spinner");
|
||||
|
||||
let error = null;
|
||||
if (this.state.err) {
|
||||
error = <div className="error">
|
||||
{this.state.err}
|
||||
</div>;
|
||||
}
|
||||
|
||||
const okLabel = this.state.busy ? <Loader /> : 'Send';
|
||||
|
||||
let cancelButton = null;
|
||||
if (!this.state.busy) {
|
||||
cancelButton = <button onClick={this._onCancel}>
|
||||
Cancel
|
||||
</button>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mx_BugReportDialog">
|
||||
<div className="mx_Dialog_title">
|
||||
Report a bug
|
||||
</div>
|
||||
<div className="mx_Dialog_content">
|
||||
<p>Please describe the bug. What did you do?
|
||||
What did you expect to happen?
|
||||
What actually happened?</p>
|
||||
<textarea
|
||||
className="mx_BugReportDialog_input"
|
||||
rows={5}
|
||||
onChange={this._onTextChange}
|
||||
value={this.state.text}
|
||||
placeholder="Describe your problem here."
|
||||
/>
|
||||
<p>In order to diagnose problems, logs from this client will be sent with
|
||||
this bug report.
|
||||
If you would prefer to only send the text above, please untick:</p>
|
||||
<input type="checkbox" checked={this.state.sendLogs}
|
||||
onChange={this._onSendLogsChange} id="mx_BugReportDialog_logs"/>
|
||||
<label htmlFor="mx_BugReportDialog_logs">Send logs</label>
|
||||
{error}
|
||||
</div>
|
||||
<div className="mx_Dialog_buttons">
|
||||
<button
|
||||
className="mx_Dialog_primary danger"
|
||||
onClick={this._onSubmit}
|
||||
autoFocus={true}
|
||||
>
|
||||
{okLabel}
|
||||
</button>
|
||||
|
||||
{cancelButton}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
BugReportDialog.propTypes = {
|
||||
onFinished: React.PropTypes.func.isRequired,
|
||||
};
|
|
@ -31,9 +31,10 @@ export default class ChangelogDialog extends React.Component {
|
|||
const version = this.props.newVersion.split('-');
|
||||
const version2 = this.props.version.split('-');
|
||||
if(version == null || version2 == null) return;
|
||||
// parse versions of form: [vectorversion]-react-[react-sdk-version]-js-[js-sdk-version]
|
||||
for(let i=0; i<REPOS.length; i++) {
|
||||
const oldVersion = version2[2*i+1];
|
||||
const newVersion = version[2*i+1];
|
||||
const oldVersion = version2[2*i];
|
||||
const newVersion = version[2*i];
|
||||
request(`https://api.github.com/repos/${REPOS[i]}/compare/${oldVersion}...${newVersion}`, (a, b, body) => {
|
||||
if(body == null) return;
|
||||
this.setState({[REPOS[i]]: JSON.parse(body).commits});
|
||||
|
|
|
@ -16,6 +16,9 @@ limitations under the License.
|
|||
|
||||
import React from 'react';
|
||||
import MatrixClientPeg from 'matrix-react-sdk/lib/MatrixClientPeg';
|
||||
import {instanceForInstanceId} from '../../../utils/DirectoryUtils';
|
||||
|
||||
const DEFAULT_ICON_URL = "img/network-matrix.svg";
|
||||
|
||||
export default class NetworkDropdown extends React.Component {
|
||||
constructor(props) {
|
||||
|
@ -35,20 +38,11 @@ export default class NetworkDropdown extends React.Component {
|
|||
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,
|
||||
selectedServer: server,
|
||||
selectedNetwork: defaultNetwork,
|
||||
selectedInstance: null,
|
||||
includeAllNetworks: false,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -58,7 +52,7 @@ export default class NetworkDropdown extends React.Component {
|
|||
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);
|
||||
this.props.onOptionChange(this.state.selectedServer, this.state.selectedInstance, this.state.includeAllNetworks);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
|
@ -98,13 +92,14 @@ export default class NetworkDropdown extends React.Component {
|
|||
ev.preventDefault();
|
||||
}
|
||||
|
||||
onMenuOptionClick(server, network, ev) {
|
||||
onMenuOptionClick(server, instance, includeAll) {
|
||||
this.setState({
|
||||
expanded: false,
|
||||
selectedServer: server,
|
||||
selectedNetwork: network,
|
||||
selectedInstanceId: instance ? instance.instance_id : null,
|
||||
includeAll: includeAll,
|
||||
});
|
||||
this.props.onOptionChange(server, network);
|
||||
this.props.onOptionChange(server, instance ? instance.instance_id : null, includeAll);
|
||||
}
|
||||
|
||||
onInputKeyUp(e) {
|
||||
|
@ -144,11 +139,22 @@ export default class NetworkDropdown extends React.Component {
|
|||
servers.unshift(MatrixClientPeg.getHomeServerName());
|
||||
}
|
||||
|
||||
// For our own HS, we can use the instance_ids given in the third party protocols
|
||||
// response to get the server to filter the room list by network for us.
|
||||
// We can't get thirdparty protocols for remote server yet though, so for those
|
||||
// we can only show the default room list.
|
||||
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));
|
||||
options.push(this._makeMenuOption(server, null, true));
|
||||
if (server == MatrixClientPeg.getHomeServerName()) {
|
||||
options.push(this._makeMenuOption(server, null, false));
|
||||
if (this.props.protocols) {
|
||||
for (const proto of Object.keys(this.props.protocols)) {
|
||||
if (!this.props.protocols[proto].instances) continue;
|
||||
for (const instance of this.props.protocols[proto].instances) {
|
||||
if (!instance.instance_id) continue;
|
||||
options.push(this._makeMenuOption(server, instance, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -156,50 +162,36 @@ export default class NetworkDropdown extends React.Component {
|
|||
return options;
|
||||
}
|
||||
|
||||
_makeMenuOption(server, network, wire_onclick) {
|
||||
if (wire_onclick === undefined) wire_onclick = true;
|
||||
_makeMenuOption(server, instance, includeAll, handleClicks) {
|
||||
if (handleClicks === undefined) handleClicks = true;
|
||||
|
||||
let icon;
|
||||
let name;
|
||||
let span_class;
|
||||
let key;
|
||||
|
||||
if (network === null) {
|
||||
if (!instance && includeAll) {
|
||||
key = server;
|
||||
name = server;
|
||||
span_class = 'mx_NetworkDropdown_menu_all';
|
||||
} else if (network == '_matrix') {
|
||||
} else if (!instance) {
|
||||
key = server + '_all';
|
||||
name = 'Matrix';
|
||||
icon = <img src="img/network-matrix.svg" width="16" height="16" />;
|
||||
icon = <img src="img/network-matrix.svg" />;
|
||||
span_class = 'mx_NetworkDropdown_menu_network';
|
||||
} else {
|
||||
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" />;
|
||||
}
|
||||
|
||||
key = server + '_inst_' + instance.instance_id;
|
||||
icon = <img src={instance.icon || DEFAULT_ICON_URL} />;
|
||||
name = instance.desc;
|
||||
span_class = 'mx_NetworkDropdown_menu_network';
|
||||
}
|
||||
|
||||
const click_handler = wire_onclick ? this.onMenuOptionClick.bind(this, server, network) : null;
|
||||
|
||||
let key = server;
|
||||
if (network !== null) {
|
||||
key += '_' + network;
|
||||
}
|
||||
const click_handler = handleClicks ? this.onMenuOptionClick.bind(this, server, instance, includeAll) : null;
|
||||
|
||||
return <div key={key} className="mx_NetworkDropdown_networkoption" onClick={click_handler}>
|
||||
{icon}
|
||||
<span className={span_class}>{name}</span>
|
||||
</div>;
|
||||
<span className="mx_NetworkDropdown_menu_network">{name}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -216,8 +208,9 @@ export default class NetworkDropdown extends React.Component {
|
|||
placeholder="matrix.org" // 'matrix.org' as an example of an HS name
|
||||
/>
|
||||
} else {
|
||||
const instance = instanceForInstanceId(this.props.protocols, this.state.selectedInstanceId);
|
||||
current_value = this._makeMenuOption(
|
||||
this.state.selectedServer, this.state.selectedNetwork, false
|
||||
this.state.selectedServer, instance, this.state.includeAll, false
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -233,12 +226,12 @@ export default class NetworkDropdown extends React.Component {
|
|||
|
||||
NetworkDropdown.propTypes = {
|
||||
onOptionChange: React.PropTypes.func.isRequired,
|
||||
protocols: React.PropTypes.object,
|
||||
// The room directory config. May have a 'servers' key that is a list of server names to include in the dropdown
|
||||
config: React.PropTypes.object,
|
||||
};
|
||||
|
||||
NetworkDropdown.defaultProps = {
|
||||
config: {
|
||||
networks: [],
|
||||
}
|
||||
protocols: {},
|
||||
config: {},
|
||||
};
|
||||
|
||||
|
|
|
@ -23,11 +23,11 @@ import PlatformPeg from 'matrix-react-sdk/lib/PlatformPeg';
|
|||
|
||||
/**
|
||||
* Check a version string is compatible with the Changelog
|
||||
* dialog
|
||||
* dialog ([vectorversion]-react-[react-sdk-version]-js-[js-sdk-version])
|
||||
*/
|
||||
function checkVersion(ver) {
|
||||
const parts = ver.split('-');
|
||||
return parts[0] == 'vector' && parts[2] == 'react' && parts[4] == 'js';
|
||||
return parts.length == 5 && parts[1] == 'react' && parts[3] == 'js';
|
||||
}
|
||||
|
||||
export default React.createClass({
|
||||
|
|
|
@ -23,11 +23,14 @@ module.exports = React.createClass({
|
|||
statics: {
|
||||
replaces: 'LoginHeader',
|
||||
},
|
||||
propTypes: {
|
||||
icon: React.PropTypes.string,
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<div className="mx_Login_logo">
|
||||
<img src="img/logo.png" width="195" height="195" alt="Riot"/>
|
||||
<img src={this.props.icon || "img/logo.png"} alt="Riot"/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue