Implement the "Voice & Video" tab of new user settings
This commit is contained in:
parent
13e36bcead
commit
27ee6625ee
6 changed files with 228 additions and 11 deletions
|
@ -138,6 +138,7 @@
|
||||||
@import "./views/settings/tabs/_GeneralSettingsTab.scss";
|
@import "./views/settings/tabs/_GeneralSettingsTab.scss";
|
||||||
@import "./views/settings/tabs/_PreferencesSettingsTab.scss";
|
@import "./views/settings/tabs/_PreferencesSettingsTab.scss";
|
||||||
@import "./views/settings/tabs/_SettingsTab.scss";
|
@import "./views/settings/tabs/_SettingsTab.scss";
|
||||||
|
@import "./views/settings/tabs/_VoiceSettingsTab.scss";
|
||||||
@import "./views/voip/_CallView.scss";
|
@import "./views/voip/_CallView.scss";
|
||||||
@import "./views/voip/_IncomingCallbox.scss";
|
@import "./views/voip/_IncomingCallbox.scss";
|
||||||
@import "./views/voip/_VideoView.scss";
|
@import "./views/voip/_VideoView.scss";
|
||||||
|
|
24
res/css/views/settings/tabs/_VoiceSettingsTab.scss
Normal file
24
res/css/views/settings/tabs/_VoiceSettingsTab.scss
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
Copyright 2019 New Vector 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_VoiceSettingsTab .mx_Field select {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_VoiceSettingsTab .mx_Field {
|
||||||
|
margin-right: 100px; // align with the rest of the fields
|
||||||
|
}
|
|
@ -69,4 +69,16 @@ export default {
|
||||||
SettingsStore.setValue("webrtc_videoinput", null, SettingLevel.DEVICE, deviceId);
|
SettingsStore.setValue("webrtc_videoinput", null, SettingLevel.DEVICE, deviceId);
|
||||||
Matrix.setMatrixCallVideoInput(deviceId);
|
Matrix.setMatrixCallVideoInput(deviceId);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getAudioOutput: function() {
|
||||||
|
return SettingsStore.getValueAt(SettingLevel.DEVICE, "webrtc_audiooutput");
|
||||||
|
},
|
||||||
|
|
||||||
|
getAudioInput: function() {
|
||||||
|
return SettingsStore.getValueAt(SettingLevel.DEVICE, "webrtc_audioinput");
|
||||||
|
},
|
||||||
|
|
||||||
|
getVideoInput: function() {
|
||||||
|
return SettingsStore.getValueAt(SettingLevel.DEVICE, "webrtc_videoinput");
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -25,6 +25,7 @@ import SettingsStore from "../../../settings/SettingsStore";
|
||||||
import LabsSettingsTab from "../settings/tabs/LabsSettingsTab";
|
import LabsSettingsTab from "../settings/tabs/LabsSettingsTab";
|
||||||
import NotificationSettingsTab from "../settings/tabs/NotificationSettingsTab";
|
import NotificationSettingsTab from "../settings/tabs/NotificationSettingsTab";
|
||||||
import PreferencesSettingsTab from "../settings/tabs/PreferencesSettingsTab";
|
import PreferencesSettingsTab from "../settings/tabs/PreferencesSettingsTab";
|
||||||
|
import VoiceSettingsTab from "../settings/tabs/VoiceSettingsTab";
|
||||||
|
|
||||||
// TODO: Ditch this whole component
|
// TODO: Ditch this whole component
|
||||||
export class TempTab extends React.Component {
|
export class TempTab extends React.Component {
|
||||||
|
@ -68,7 +69,7 @@ export default class UserSettingsDialog extends React.Component {
|
||||||
tabs.push(new Tab(
|
tabs.push(new Tab(
|
||||||
_td("Voice & Video"),
|
_td("Voice & Video"),
|
||||||
"mx_UserSettingsDialog_voiceIcon",
|
"mx_UserSettingsDialog_voiceIcon",
|
||||||
<div>Voice Test</div>,
|
<VoiceSettingsTab />,
|
||||||
));
|
));
|
||||||
tabs.push(new Tab(
|
tabs.push(new Tab(
|
||||||
_td("Security & Privacy"),
|
_td("Security & Privacy"),
|
||||||
|
|
177
src/components/views/settings/tabs/VoiceSettingsTab.js
Normal file
177
src/components/views/settings/tabs/VoiceSettingsTab.js
Normal file
|
@ -0,0 +1,177 @@
|
||||||
|
/*
|
||||||
|
Copyright 2019 New Vector 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 {_t} from "../../../../languageHandler";
|
||||||
|
import CallMediaHandler from "../../../../CallMediaHandler";
|
||||||
|
import Field from "../../elements/Field";
|
||||||
|
import AccessibleButton from "../../elements/AccessibleButton";
|
||||||
|
import {SettingLevel} from "../../../../settings/SettingsStore";
|
||||||
|
const Modal = require("../../../../Modal");
|
||||||
|
const sdk = require("../../../../index");
|
||||||
|
const MatrixClientPeg = require("../../../../MatrixClientPeg");
|
||||||
|
|
||||||
|
export default class VoiceSettingsTab extends React.Component {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
mediaDevices: null,
|
||||||
|
activeAudioOutput: null,
|
||||||
|
activeAudioInput: null,
|
||||||
|
activeVideoInput: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillMount(): void {
|
||||||
|
this._refreshMediaDevices();
|
||||||
|
}
|
||||||
|
|
||||||
|
_refreshMediaDevices = async () => {
|
||||||
|
this.setState({
|
||||||
|
mediaDevices: await CallMediaHandler.getDevices(),
|
||||||
|
activeAudioOutput: CallMediaHandler.getAudioOutput(),
|
||||||
|
activeAudioInput: CallMediaHandler.getAudioInput(),
|
||||||
|
activeVideoInput: CallMediaHandler.getVideoInput(),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
_requestMediaPermissions = () => {
|
||||||
|
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.createTrackedDialog('No media permissions', '', ErrorDialog, {
|
||||||
|
title: _t('No media permissions'),
|
||||||
|
description: _t('You may need to manually permit Riot to access your microphone/webcam'),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
_setAudioOutput = (e) => {
|
||||||
|
CallMediaHandler.setAudioOutput(e.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
_setAudioInput = (e) => {
|
||||||
|
CallMediaHandler.setAudioInput(e.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
_setVideoInput = (e) => {
|
||||||
|
CallMediaHandler.setVideoInput(e.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
_setForceTurn = (forced) => {
|
||||||
|
MatrixClientPeg.get().setForceTURN(forced);
|
||||||
|
};
|
||||||
|
|
||||||
|
_renderDeviceOptions(devices, category) {
|
||||||
|
return devices.map((d) => <option key={`${category}-${d.deviceId}`} value={d.deviceId}>{d.label}</option>);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const SettingsFlag = sdk.getComponent("views.elements.SettingsFlag");
|
||||||
|
|
||||||
|
let requestButton = null;
|
||||||
|
let speakerDropdown = null;
|
||||||
|
let microphoneDropdown = null;
|
||||||
|
let webcamDropdown = null;
|
||||||
|
if (this.state.mediaDevices === false) {
|
||||||
|
requestButton = (
|
||||||
|
<div>
|
||||||
|
<span className="mx_SettingsTab_subsectionText">{_t("Missing media permissions, click the button below to request.")}</span>
|
||||||
|
<AccessibleButton onClick={this._requestMediaPermissions} kind="primary">
|
||||||
|
{_t("Request media permissions")}
|
||||||
|
</AccessibleButton>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else if (this.state.mediaDevices) {
|
||||||
|
speakerDropdown = <p>{ _t('No Audio Outputs detected') }</p>;
|
||||||
|
microphoneDropdown = <p>{ _t('No Microphones detected') }</p>;
|
||||||
|
webcamDropdown = <p>{ _t('No Webcams detected') }</p>;
|
||||||
|
|
||||||
|
const defaultOption = {
|
||||||
|
deviceId: '',
|
||||||
|
label: _t('Default Device'),
|
||||||
|
};
|
||||||
|
const getDefaultDevice = (devices) => {
|
||||||
|
if (!devices.some((i) => i.deviceId === 'default')) {
|
||||||
|
devices.unshift(defaultOption);
|
||||||
|
return '';
|
||||||
|
} else {
|
||||||
|
return 'default';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const audioOutputs = this.state.mediaDevices.audiooutput.slice(0);
|
||||||
|
if (audioOutputs.length > 0) {
|
||||||
|
const defaultDevice = getDefaultDevice(audioOutputs);
|
||||||
|
speakerDropdown = (
|
||||||
|
<Field element="select" label={_t("Audio Output")} id="audioOutput"
|
||||||
|
value={this.state.activeAudioOutput || defaultDevice}
|
||||||
|
onChange={this._setAudioOutput}>
|
||||||
|
{this._renderDeviceOptions(audioOutputs, 'audioOutput')}
|
||||||
|
</Field>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const audioInputs = this.state.mediaDevices.audioinput.slice(0);
|
||||||
|
if (audioInputs.length > 0) {
|
||||||
|
const defaultDevice = getDefaultDevice(audioInputs);
|
||||||
|
microphoneDropdown = (
|
||||||
|
<Field element="select" label={_t("Microphone")} id="audioInput"
|
||||||
|
value={this.state.activeAudioInput || defaultDevice}
|
||||||
|
onChange={this._setAudioInput}>
|
||||||
|
{this._renderDeviceOptions(audioInputs, 'audioInput')}
|
||||||
|
</Field>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const videoInputs = this.state.mediaDevices.videoinput.slice(0);
|
||||||
|
if (videoInputs.length > 0) {
|
||||||
|
const defaultDevice = getDefaultDevice(videoInputs);
|
||||||
|
webcamDropdown = (
|
||||||
|
<Field element="select" label={_t("Camera")} id="videoInput"
|
||||||
|
value={this.state.activeVideoInput || defaultDevice}
|
||||||
|
onChange={this._setVideoInput}>
|
||||||
|
{this._renderDeviceOptions(videoInputs, 'videoInput')}
|
||||||
|
</Field>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Make 'webRtcForceTURN' be positively worded
|
||||||
|
return (
|
||||||
|
<div className="mx_SettingsTab mx_VoiceSettingsTab">
|
||||||
|
<div className="mx_SettingsTab_heading">{_t("Voice & Video")}</div>
|
||||||
|
<div className="mx_SettingsTab_section">
|
||||||
|
{requestButton}
|
||||||
|
{speakerDropdown}
|
||||||
|
{microphoneDropdown}
|
||||||
|
{webcamDropdown}
|
||||||
|
<SettingsFlag name='VideoView.flipVideoHorizontally' level={SettingLevel.ACCOUNT} />
|
||||||
|
<SettingsFlag name='webRtcForceTURN' level={SettingLevel.DEVICE} onChange={this._setForceTurn} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -450,6 +450,18 @@
|
||||||
"Timeline": "Timeline",
|
"Timeline": "Timeline",
|
||||||
"Advanced": "Advanced",
|
"Advanced": "Advanced",
|
||||||
"Autocomplete delay (ms)": "Autocomplete delay (ms)",
|
"Autocomplete delay (ms)": "Autocomplete delay (ms)",
|
||||||
|
"No media permissions": "No media permissions",
|
||||||
|
"You may need to manually permit Riot to access your microphone/webcam": "You may need to manually permit Riot to access your microphone/webcam",
|
||||||
|
"Missing media permissions, click the button below to request.": "Missing media permissions, click the button below to request.",
|
||||||
|
"Request media permissions": "Request media permissions",
|
||||||
|
"No Audio Outputs detected": "No Audio Outputs detected",
|
||||||
|
"No Microphones detected": "No Microphones detected",
|
||||||
|
"No Webcams detected": "No Webcams detected",
|
||||||
|
"Default Device": "Default Device",
|
||||||
|
"Audio Output": "Audio Output",
|
||||||
|
"Microphone": "Microphone",
|
||||||
|
"Camera": "Camera",
|
||||||
|
"Voice & Video": "Voice & Video",
|
||||||
"Cannot add any more widgets": "Cannot add any more widgets",
|
"Cannot add any more widgets": "Cannot add any more widgets",
|
||||||
"The maximum permitted number of widgets have already been added to this room.": "The maximum permitted number of widgets have already been added to this room.",
|
"The maximum permitted number of widgets have already been added to this room.": "The maximum permitted number of widgets have already been added to this room.",
|
||||||
"Add a widget": "Add a widget",
|
"Add a widget": "Add a widget",
|
||||||
|
@ -1044,7 +1056,6 @@
|
||||||
"Room contains unknown devices": "Room contains unknown devices",
|
"Room contains unknown devices": "Room contains unknown devices",
|
||||||
"\"%(RoomName)s\" contains devices that you haven't seen before.": "\"%(RoomName)s\" contains devices that you haven't seen before.",
|
"\"%(RoomName)s\" contains devices that you haven't seen before.": "\"%(RoomName)s\" contains devices that you haven't seen before.",
|
||||||
"Unknown devices": "Unknown devices",
|
"Unknown devices": "Unknown devices",
|
||||||
"Voice & Video": "Voice & Video",
|
|
||||||
"Security & Privacy": "Security & Privacy",
|
"Security & Privacy": "Security & Privacy",
|
||||||
"Help & About": "Help & About",
|
"Help & About": "Help & About",
|
||||||
"Visit old settings": "Visit old settings",
|
"Visit old settings": "Visit old settings",
|
||||||
|
@ -1300,16 +1311,7 @@
|
||||||
"Reject all %(invitedRooms)s invites": "Reject all %(invitedRooms)s invites",
|
"Reject all %(invitedRooms)s invites": "Reject all %(invitedRooms)s invites",
|
||||||
"Bulk Options": "Bulk Options",
|
"Bulk Options": "Bulk Options",
|
||||||
"Desktop specific": "Desktop specific",
|
"Desktop specific": "Desktop specific",
|
||||||
"No media permissions": "No media permissions",
|
|
||||||
"You may need to manually permit Riot to access your microphone/webcam": "You may need to manually permit Riot to access your microphone/webcam",
|
|
||||||
"Missing Media Permissions, click here to request.": "Missing Media Permissions, click here to request.",
|
"Missing Media Permissions, click here to request.": "Missing Media Permissions, click here to request.",
|
||||||
"No Audio Outputs detected": "No Audio Outputs detected",
|
|
||||||
"No Microphones detected": "No Microphones detected",
|
|
||||||
"No Webcams detected": "No Webcams detected",
|
|
||||||
"Default Device": "Default Device",
|
|
||||||
"Audio Output": "Audio Output",
|
|
||||||
"Microphone": "Microphone",
|
|
||||||
"Camera": "Camera",
|
|
||||||
"VoIP": "VoIP",
|
"VoIP": "VoIP",
|
||||||
"Email": "Email",
|
"Email": "Email",
|
||||||
"Add email address": "Add email address",
|
"Add email address": "Add email address",
|
||||||
|
|
Loading…
Reference in a new issue