Merge remote-tracking branch 'origin/develop' into rav/handle_received_room_key_requests

This commit is contained in:
Richard van der Hoff 2017-06-02 10:31:14 +01:00
commit a3a262b150
40 changed files with 787 additions and 198 deletions

View file

@ -18,9 +18,11 @@ limitations under the License.
import * as Matrix from 'matrix-js-sdk';
import React from 'react';
import UserSettingsStore from '../../UserSettingsStore';
import KeyCode from '../../KeyCode';
import Notifier from '../../Notifier';
import PageTypes from '../../PageTypes';
import CallMediaHandler from '../../CallMediaHandler';
import sdk from '../../index';
import dis from '../../dispatcher';
@ -63,6 +65,13 @@ export default React.createClass({
};
},
getInitialState: function() {
return {
// use compact timeline view
useCompactLayout: UserSettingsStore.getSyncedSetting('useCompactLayout'),
};
},
componentWillMount: function() {
// stash the MatrixClient in case we log out before we are unmounted
this._matrixClient = this.props.matrixClient;
@ -71,11 +80,15 @@ export default React.createClass({
// RoomView.getScrollState()
this._scrollStateMap = {};
CallMediaHandler.loadDevices();
document.addEventListener('keydown', this._onKeyDown);
this._matrixClient.on("accountData", this.onAccountData);
},
componentWillUnmount: function() {
document.removeEventListener('keydown', this._onKeyDown);
this._matrixClient.removeListener("accountData", this.onAccountData);
},
getScrollStateForRoom: function(roomId) {
@ -89,6 +102,14 @@ export default React.createClass({
return this.refs.roomView.canResetTimeline();
},
onAccountData: function(event) {
if (event.getType() === "im.vector.web.settings") {
this.setState({
useCompactLayout: event.getContent().useCompactLayout
});
}
},
_onKeyDown: function(ev) {
/*
// Remove this for now as ctrl+alt = alt-gr so this breaks keyboards which rely on alt-gr for numbers
@ -245,6 +266,9 @@ export default React.createClass({
if (topBar) {
bodyClasses += ' mx_MatrixChat_toolbarShowing';
}
if (this.state.useCompactLayout) {
bodyClasses += ' mx_MatrixChat_useCompactLayout';
}
return (
<div className='mx_MatrixChat_wrapper'>

View file

@ -24,6 +24,7 @@ const dis = require("../../dispatcher");
const q = require('q');
const packageJson = require('../../../package.json');
const UserSettingsStore = require('../../UserSettingsStore');
const CallMediaHandler = require('../../CallMediaHandler');
const GeminiScrollbar = require('react-gemini-scrollbar');
const Email = require('../../email');
const AddThreepid = require('../../AddThreepid');
@ -79,11 +80,11 @@ const SETTINGS_LABELS = [
id: 'showTwelveHourTimestamps',
label: 'Show timestamps in 12 hour format (e.g. 2:30pm)',
},
/*
{
id: 'useCompactLayout',
label: 'Use compact timeline layout',
},
/*
{
id: 'useFixedWidthFont',
label: 'Use fixed width font',
@ -176,6 +177,7 @@ module.exports = React.createClass({
email_add_pending: false,
vectorVersion: undefined,
rejectingInvites: false,
mediaDevices: null,
};
},
@ -196,6 +198,8 @@ module.exports = React.createClass({
});
}
this._refreshMediaDevices();
// Bulk rejecting invites:
// /sync won't have had time to return when UserSettings re-renders from state changes, so getRooms()
// will still return rooms with invites. To get around this, add a listener for
@ -257,6 +261,20 @@ module.exports = React.createClass({
this.setState({ electron_settings: settings });
},
_refreshMediaDevices: function() {
q().then(() => {
return CallMediaHandler.getDevices();
}).then((mediaDevices) => {
// console.log("got mediaDevices", mediaDevices, this._unmounted);
if (this._unmounted) return;
this.setState({
mediaDevices,
activeAudioInput: this._localSettings['webrtc_audioinput'],
activeVideoInput: this._localSettings['webrtc_videoinput'],
});
});
},
_refreshFromServer: function() {
const self = this;
q.all([
@ -767,6 +785,7 @@ module.exports = React.createClass({
_renderLabs: function() {
// default to enabled if undefined
if (this.props.enableLabs === false) return null;
UserSettingsStore.doTranslations();
const features = UserSettingsStore.LABS_FEATURES.map((feature) => (
<div key={feature.id} className="mx_UserSettings_toggle">
@ -882,6 +901,110 @@ module.exports = React.createClass({
</div>;
},
_mapWebRtcDevicesToSpans: function(devices) {
return devices.map((device) => <span key={device.deviceId}>{device.label}</span>);
},
_setAudioInput: function(deviceId) {
this.setState({activeAudioInput: deviceId});
CallMediaHandler.setAudioInput(deviceId);
},
_setVideoInput: function(deviceId) {
this.setState({activeVideoInput: deviceId});
CallMediaHandler.setVideoInput(deviceId);
},
_requestMediaPermissions: function(event) {
const getUserMedia = (
window.navigator.getUserMedia || window.navigator.webkitGetUserMedia || window.navigator.mozGetUserMedia
);
if (getUserMedia) {
return getUserMedia.apply(window.navigator, [
{ video: true, audio: true },
this._refreshMediaDevices,
function() {
const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog');
Modal.createDialog(ErrorDialog, {
title: _t('No media permissions'),
description: _t('You may need to manually permit Riot to access your microphone/webcam'),
});
},
]);
}
},
_renderWebRtcSettings: function() {
if (this.state.mediaDevices === false) {
return <div>
<h3>{_t('VoIP')}</h3>
<div className="mx_UserSettings_section">
<p className="mx_UserSettings_link" onClick={this._requestMediaPermissions}>
{_t('Missing Media Permissions, click here to request.')}
</p>
</div>
</div>;
} else if (!this.state.mediaDevices) return;
const Dropdown = sdk.getComponent('elements.Dropdown');
let microphoneDropdown = <p>{_t('No Microphones detected')}</p>;
let webcamDropdown = <p>{_t('No Webcams detected')}</p>;
const defaultOption = {
deviceId: '',
label: _t('Default Device'),
};
const audioInputs = this.state.mediaDevices.audioinput.slice(0);
if (audioInputs.length > 0) {
let defaultInput = '';
if (!audioInputs.some((input) => input.deviceId === 'default')) {
audioInputs.unshift(defaultOption);
} else {
defaultInput = 'default';
}
microphoneDropdown = <div>
<h4>{_t('Microphone')}</h4>
<Dropdown
className="mx_UserSettings_webRtcDevices_dropdown"
value={this.state.activeAudioInput || defaultInput}
onOptionChange={this._setAudioInput}>
{this._mapWebRtcDevicesToSpans(audioInputs)}
</Dropdown>
</div>;
}
const videoInputs = this.state.mediaDevices.videoinput.slice(0);
if (videoInputs.length > 0) {
let defaultInput = '';
if (!videoInputs.some((input) => input.deviceId === 'default')) {
videoInputs.unshift(defaultOption);
} else {
defaultInput = 'default';
}
webcamDropdown = <div>
<h4>{_t('Camera')}</h4>
<Dropdown
className="mx_UserSettings_webRtcDevices_dropdown"
value={this.state.activeVideoInput || defaultInput}
onOptionChange={this._setVideoInput}>
{this._mapWebRtcDevicesToSpans(videoInputs)}
</Dropdown>
</div>;
}
return <div>
<h3>{_t('VoIP')}</h3>
<div className="mx_UserSettings_section">
{microphoneDropdown}
{webcamDropdown}
</div>
</div>;
},
_showSpoiler: function(event) {
const target = event.target;
target.innerHTML = target.getAttribute('data-spoiler');
@ -1079,6 +1202,7 @@ module.exports = React.createClass({
{this._renderUserInterfaceSettings()}
{this._renderLabs()}
{this._renderWebRtcSettings()}
{this._renderDevicesPanel()}
{this._renderCryptoInfo()}
{this._renderBulkOptions()}

View file

@ -40,14 +40,7 @@ export default class LanguageDropdown extends React.Component {
}
componentWillMount() {
languageHandler.getAllLanguageKeysFromJson().then((langKeys) => {
const langs = [];
langKeys.forEach((languageKey) => {
langs.push({
value: languageKey,
label: _t(languageKey)
});
});
languageHandler.getAllLanguagesFromJson().then((langs) => {
langs.sort(function(a, b){
if(a.label < b.label) return -1;
if(a.label > b.label) return 1;

View file

@ -182,7 +182,7 @@ class PasswordLogin extends React.Component {
<div>
<form onSubmit={this.onSubmitForm}>
<div className="mx_Login_type_container">
<label className="mx_Login_type_label">{ _t('I want to sign in with') }</label>
<label className="mx_Login_type_label">{ _t('Sign in with') }</label>
<Dropdown
className="mx_Login_type_dropdown"
value={this.state.loginType}

View file

@ -132,7 +132,7 @@ module.exports = React.createClass({
var toggleButton;
if (this.props.withToggleButton) {
toggleButton = (
<div style={{ textAlign: 'center' }}>
<div className="mx_ServerConfig_selector">
<input className="mx_Login_radio" id="basic" name="configVisible" type="radio"
checked={!this.state.configVisible}
onChange={this.onServerConfigVisibleChange.bind(this, false)} />

View file

@ -506,7 +506,7 @@ export default class MessageComposerInput extends React.Component {
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
title: _t("Server error"),
description: ((err && err.message) ? err.message : _t("Server unavailable, overloaded, or something else went wrong") + "."),
description: ((err && err.message) ? err.message : _t("Server unavailable, overloaded, or something else went wrong.")),
});
});
}

View file

@ -316,7 +316,7 @@ export default React.createClass({
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
title: _t("Server error"),
description: ((err && err.message) ? err.message : _t("Server unavailable, overloaded, or something else went wrong") + "."),
description: ((err && err.message) ? err.message : _t("Server unavailable, overloaded, or something else went wrong.")),
});
});
}