diff --git a/app/lib/RoomClient.js b/app/lib/RoomClient.js
index 119cf1b..8c5d0fe 100644
--- a/app/lib/RoomClient.js
+++ b/app/lib/RoomClient.js
@@ -366,9 +366,9 @@ export default class RoomClient
});
}
- changeWebcam()
+ changeWebcam(deviceId)
{
- logger.debug('changeWebcam()');
+ logger.debug('changeWebcam() [deviceId: %s]', deviceId);
this._dispatch(
stateActions.setWebcamInProgress(true));
@@ -376,22 +376,20 @@ export default class RoomClient
return Promise.resolve()
.then(() =>
{
- return this._updateWebcams();
+ logger.debug('changeWebcam() | calling enumerateDevices()');
+
+ return navigator.mediaDevices.enumerateDevices();
})
- .then(() =>
+ .then((devices) =>
{
- const array = Array.from(this._webcams.keys());
- const len = array.length;
- const deviceId =
- this._webcam.device ? this._webcam.device.deviceId : undefined;
- let idx = array.indexOf(deviceId);
+ for (const device of devices)
+ {
+ if (device.kind !== 'videoinput')
+ continue;
- if (idx < len - 1)
- idx++;
- else
- idx = 0;
-
- this._webcam.device = this._webcams.get(array[idx]);
+ if (device.deviceId == deviceId)
+ this._webcam.device = device;
+ }
logger.debug(
'changeWebcam() | new selected webcam [device:%o]',
@@ -1487,7 +1485,7 @@ export default class RoomClient
logger.debug('_updateWebcams()');
// Reset the list.
- this._webcams = new Map();
+ this._webcams = {};
return Promise.resolve()
.then(() =>
@@ -1503,25 +1501,33 @@ export default class RoomClient
if (device.kind !== 'videoinput')
continue;
- this._webcams.set(device.deviceId, device);
+ this._webcams[device.deviceId] = {
+ value : device.deviceId,
+ label : device.label
+ };
}
})
.then(() =>
{
- const array = Array.from(this._webcams.values());
- const len = array.length;
const currentWebcamId =
this._webcam.device ? this._webcam.device.deviceId : undefined;
- logger.debug('_updateWebcams() [webcams:%o]', array);
+ logger.debug('_updateWebcams() [webcams:%o]', this._webcams);
+
+ const len = Object.keys(this._webcams).length;
if (len === 0)
this._webcam.device = null;
- else if (!this._webcams.has(currentWebcamId))
- this._webcam.device = array[0];
+ else if (!this._webcams[currentWebcamId])
+ for (this._webcam.device in this._webcams)
+ if (this._webcams.hasOwnProperty(this._webcam.device))
+ break;
this._dispatch(
- stateActions.setCanChangeWebcam(this._webcams.size >= 2));
+ stateActions.setCanChangeWebcam(len >= 2));
+ if (len >= 1)
+ this._dispatch(
+ stateActions.setWebcamDevices(this._webcams));
});
}
diff --git a/app/lib/components/Me.jsx b/app/lib/components/Me.jsx
index 36251c8..e8286b1 100644
--- a/app/lib/components/Me.jsx
+++ b/app/lib/components/Me.jsx
@@ -35,8 +35,7 @@ class Me extends React.Component
onMuteMic,
onUnmuteMic,
onEnableWebcam,
- onDisableWebcam,
- onChangeWebcam
+ onDisableWebcam
} = this.props;
let micState;
@@ -59,13 +58,6 @@ class Me extends React.Component
else
webcamState = 'off';
- let changeWebcamState;
-
- if (Boolean(webcamProducer) && me.canChangeWebcam)
- changeWebcamState = 'on';
- else
- changeWebcamState = 'unsupported';
-
const videoVisible = (
Boolean(webcamProducer) &&
!webcamProducer.locallyPaused &&
@@ -104,13 +96,6 @@ class Me extends React.Component
webcamState === 'on' ? onDisableWebcam() : onEnableWebcam();
}}
/>
-
-
onChangeWebcam()}
- />
:null
}
@@ -179,8 +164,7 @@ Me.propTypes =
onMuteMic : PropTypes.func.isRequired,
onUnmuteMic : PropTypes.func.isRequired,
onEnableWebcam : PropTypes.func.isRequired,
- onDisableWebcam : PropTypes.func.isRequired,
- onChangeWebcam : PropTypes.func.isRequired
+ onDisableWebcam : PropTypes.func.isRequired
};
const mapStateToProps = (state) =>
@@ -209,8 +193,7 @@ const mapDispatchToProps = (dispatch) =>
onMuteMic : () => dispatch(requestActions.muteMic()),
onUnmuteMic : () => dispatch(requestActions.unmuteMic()),
onEnableWebcam : () => dispatch(requestActions.enableWebcam()),
- onDisableWebcam : () => dispatch(requestActions.disableWebcam()),
- onChangeWebcam : () => dispatch(requestActions.changeWebcam())
+ onDisableWebcam : () => dispatch(requestActions.disableWebcam())
};
};
diff --git a/app/lib/components/Room.jsx b/app/lib/components/Room.jsx
index 80f030b..89b5077 100644
--- a/app/lib/components/Room.jsx
+++ b/app/lib/components/Room.jsx
@@ -5,12 +5,14 @@ import PropTypes from 'prop-types';
import classnames from 'classnames';
import ClipboardButton from 'react-clipboard.js';
import * as appPropTypes from './appPropTypes';
+import * as stateActions from '../redux/stateActions';
import * as requestActions from '../redux/requestActions';
import { Appear } from './transitions';
import Me from './Me';
import Peers from './Peers';
import Notifications from './Notifications';
import ChatWidget from './ChatWidget';
+import Settings from './Settings';
class Room extends React.Component
{
@@ -24,6 +26,7 @@ class Room extends React.Component
onRoomLinkCopy,
onSetAudioMode,
onRestartIce,
+ onToggleSettings,
onShareScreen,
onUnShareScreen,
onNeedExtension,
@@ -158,6 +161,16 @@ class Room extends React.Component
onClick={() => onRestartIce()}
/>
+ onToggleSettings()}
+ />
+
+
+
@@ -238,6 +256,10 @@ const mapDispatchToProps = (dispatch) =>
{
dispatch(requestActions.restartIce());
},
+ onToggleSettings : () =>
+ {
+ dispatch(stateActions.toggleSettings());
+ },
onToggleHand : (enable) =>
{
if (enable)
diff --git a/app/lib/components/Settings.jsx b/app/lib/components/Settings.jsx
new file mode 100644
index 0000000..d8e4802
--- /dev/null
+++ b/app/lib/components/Settings.jsx
@@ -0,0 +1,89 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import * as appPropTypes from './appPropTypes';
+import * as requestActions from '../redux/requestActions';
+import PropTypes from 'prop-types';
+import Dropdown from 'react-dropdown';
+
+class Settings extends React.Component
+{
+ constructor(props)
+ {
+ super(props);
+ }
+
+ render()
+ {
+ const {
+ room,
+ me,
+ handleChangeWebcam,
+ onToggleSettings
+ } = this.props;
+
+ if (!room.showSettings)
+ return null;
+
+ const webcams = Object.values(me.webcamDevices);
+
+ return (
+
+
+
+ Settings
+
+
+
+
+
+ onToggleSettings()}
+ >
+ Close
+
+
+
+
+ );
+ }
+}
+
+Settings.propTypes =
+{
+ me : appPropTypes.Me.isRequired,
+ room : appPropTypes.Room.isRequired,
+ onToggleSettings : PropTypes.func.isRequired,
+ handleChangeWebcam : PropTypes.func.isRequired
+};
+
+const mapStateToProps = (state) =>
+{
+ return {
+ me : state.me,
+ room : state.room
+ };
+};
+
+const mapDispatchToProps = (dispatch) =>
+{
+ return {
+ handleChangeWebcam : (device) =>
+ {
+ dispatch(requestActions.changeWebcam(device.value));
+ }
+ };
+};
+
+const SettingsContainer = connect(
+ mapStateToProps,
+ mapDispatchToProps
+)(Settings);
+
+export default SettingsContainer;
diff --git a/app/lib/redux/reducers/me.js b/app/lib/redux/reducers/me.js
index 8f15038..785acd2 100644
--- a/app/lib/redux/reducers/me.js
+++ b/app/lib/redux/reducers/me.js
@@ -9,6 +9,7 @@ const initialState =
canShareScreen : false,
needExtension : false,
canChangeWebcam : false,
+ webcamDevices : null,
webcamInProgress : false,
screenShareInProgress : false,
audioOnly : false,
@@ -50,6 +51,13 @@ const me = (state = initialState, action) =>
return { ...state, canChangeWebcam };
}
+ case 'SET_WEBCAM_DEVICES':
+ {
+ const devices = action.payload;
+
+ return { ...state, webcamDevices: devices };
+ }
+
case 'SET_WEBCAM_IN_PROGRESS':
{
const { flag } = action.payload;
diff --git a/app/lib/redux/reducers/room.js b/app/lib/redux/reducers/room.js
index 622ba71..1122f82 100644
--- a/app/lib/redux/reducers/room.js
+++ b/app/lib/redux/reducers/room.js
@@ -4,7 +4,8 @@ const initialState =
state : 'new', // new/connecting/connected/disconnected/closed,
activeSpeakerName : null,
peerHeight : 300,
- peerWidth : 400
+ peerWidth : 400,
+ showSettings : false
};
const room = (state = initialState, action) =>
@@ -42,6 +43,13 @@ const room = (state = initialState, action) =>
return { ...state, peerWidth: peerWidth, peerHeight: peerHeight };
}
+ case 'TOGGLE_SETTINGS':
+ {
+ const showSettings = !state.showSettings;
+
+ return { ...state, showSettings };
+ }
+
default:
return state;
}
diff --git a/app/lib/redux/requestActions.js b/app/lib/redux/requestActions.js
index f8d2178..073464b 100644
--- a/app/lib/redux/requestActions.js
+++ b/app/lib/redux/requestActions.js
@@ -57,10 +57,11 @@ export const disableWebcam = () =>
};
};
-export const changeWebcam = () =>
+export const changeWebcam = (deviceId) =>
{
return {
- type : 'CHANGE_WEBCAM'
+ type : 'CHANGE_WEBCAM',
+ payload : { deviceId }
};
};
diff --git a/app/lib/redux/roomClientMiddleware.js b/app/lib/redux/roomClientMiddleware.js
index 628cba3..09df186 100644
--- a/app/lib/redux/roomClientMiddleware.js
+++ b/app/lib/redux/roomClientMiddleware.js
@@ -83,7 +83,9 @@ export default ({ dispatch, getState }) => (next) =>
case 'CHANGE_WEBCAM':
{
- client.changeWebcam();
+ const { deviceId } = action.payload;
+
+ client.changeWebcam(deviceId);
break;
}
diff --git a/app/lib/redux/stateActions.js b/app/lib/redux/stateActions.js
index 4c6b61d..1106dac 100644
--- a/app/lib/redux/stateActions.js
+++ b/app/lib/redux/stateActions.js
@@ -62,6 +62,14 @@ export const setCanChangeWebcam = (flag) =>
};
};
+export const setWebcamDevices = (devices) =>
+{
+ return {
+ type : 'SET_WEBCAM_DEVICES',
+ payload : devices
+ };
+};
+
export const setDisplayName = (displayName) =>
{
return {
@@ -110,6 +118,13 @@ export const setMyRaiseHandState = (flag) =>
};
};
+export const toggleSettings = () =>
+{
+ return {
+ type : 'TOGGLE_SETTINGS'
+ };
+};
+
export const setMyRaiseHandStateInProgress = (flag) =>
{
return {
diff --git a/app/package-lock.json b/app/package-lock.json
index 1dfaf80..920397c 100644
--- a/app/package-lock.json
+++ b/app/package-lock.json
@@ -591,6 +591,28 @@
}
}
},
+ "babel-helper-bindify-decorators": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-helper-bindify-decorators/-/babel-helper-bindify-decorators-6.24.1.tgz",
+ "integrity": "sha1-FMGeXxQte0fxmlJDHlKxzLxAozA=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0",
+ "babel-traverse": "6.26.0",
+ "babel-types": "6.26.0"
+ }
+ },
+ "babel-helper-builder-binary-assignment-operator-visitor": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz",
+ "integrity": "sha1-zORReto1b0IgvK6KAsKzRvmlZmQ=",
+ "dev": true,
+ "requires": {
+ "babel-helper-explode-assignable-expression": "6.24.1",
+ "babel-runtime": "6.26.0",
+ "babel-types": "6.26.0"
+ }
+ },
"babel-helper-builder-react-jsx": {
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-helper-builder-react-jsx/-/babel-helper-builder-react-jsx-6.26.0.tgz",
@@ -626,6 +648,29 @@
"lodash": "4.17.4"
}
},
+ "babel-helper-explode-assignable-expression": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz",
+ "integrity": "sha1-8luCz33BBDPFX3BZLVdGQArCLKo=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0",
+ "babel-traverse": "6.26.0",
+ "babel-types": "6.26.0"
+ }
+ },
+ "babel-helper-explode-class": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-helper-explode-class/-/babel-helper-explode-class-6.24.1.tgz",
+ "integrity": "sha1-fcKjkQ3uAHBW4eMdZAztPVTqqes=",
+ "dev": true,
+ "requires": {
+ "babel-helper-bindify-decorators": "6.24.1",
+ "babel-runtime": "6.26.0",
+ "babel-traverse": "6.26.0",
+ "babel-types": "6.26.0"
+ }
+ },
"babel-helper-function-name": {
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz",
@@ -680,6 +725,19 @@
"lodash": "4.17.4"
}
},
+ "babel-helper-remap-async-to-generator": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz",
+ "integrity": "sha1-XsWBgnrXI/7N04HxySg5BnbkVRs=",
+ "dev": true,
+ "requires": {
+ "babel-helper-function-name": "6.24.1",
+ "babel-runtime": "6.26.0",
+ "babel-template": "6.26.0",
+ "babel-traverse": "6.26.0",
+ "babel-types": "6.26.0"
+ }
+ },
"babel-helper-replace-supers": {
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz",
@@ -722,12 +780,72 @@
"babel-runtime": "6.26.0"
}
},
+ "babel-plugin-syntax-async-functions": {
+ "version": "6.13.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz",
+ "integrity": "sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU=",
+ "dev": true
+ },
+ "babel-plugin-syntax-async-generators": {
+ "version": "6.13.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-generators/-/babel-plugin-syntax-async-generators-6.13.0.tgz",
+ "integrity": "sha1-a8lj67FuzLrmuStZbrfzXDQqi5o=",
+ "dev": true
+ },
+ "babel-plugin-syntax-class-constructor-call": {
+ "version": "6.18.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-syntax-class-constructor-call/-/babel-plugin-syntax-class-constructor-call-6.18.0.tgz",
+ "integrity": "sha1-nLnTn+Q8hgC+yBRkVt3L1OGnZBY=",
+ "dev": true
+ },
+ "babel-plugin-syntax-class-properties": {
+ "version": "6.13.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz",
+ "integrity": "sha1-1+sjt5oxf4VDlixQW4J8fWysJ94=",
+ "dev": true
+ },
+ "babel-plugin-syntax-decorators": {
+ "version": "6.13.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-syntax-decorators/-/babel-plugin-syntax-decorators-6.13.0.tgz",
+ "integrity": "sha1-MSVjtNvePMgGzuPkFszurd0RrAs=",
+ "dev": true
+ },
+ "babel-plugin-syntax-do-expressions": {
+ "version": "6.13.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-syntax-do-expressions/-/babel-plugin-syntax-do-expressions-6.13.0.tgz",
+ "integrity": "sha1-V0d1YTmqJtOQ0JQQsDdEugfkeW0=",
+ "dev": true
+ },
+ "babel-plugin-syntax-dynamic-import": {
+ "version": "6.18.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz",
+ "integrity": "sha1-jWomIpyDdFqZgqRBBRVyyqF5sdo=",
+ "dev": true
+ },
+ "babel-plugin-syntax-exponentiation-operator": {
+ "version": "6.13.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz",
+ "integrity": "sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4=",
+ "dev": true
+ },
+ "babel-plugin-syntax-export-extensions": {
+ "version": "6.13.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-syntax-export-extensions/-/babel-plugin-syntax-export-extensions-6.13.0.tgz",
+ "integrity": "sha1-cKFITw+QiaToStRLrDU8lbmxJyE=",
+ "dev": true
+ },
"babel-plugin-syntax-flow": {
"version": "6.18.0",
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz",
"integrity": "sha1-TDqyCiryaqIM0lmVw5jE63AxDI0=",
"dev": true
},
+ "babel-plugin-syntax-function-bind": {
+ "version": "6.13.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-syntax-function-bind/-/babel-plugin-syntax-function-bind-6.13.0.tgz",
+ "integrity": "sha1-SMSV8Xe98xqYHnMvVa3AvdJgH0Y=",
+ "dev": true
+ },
"babel-plugin-syntax-jsx": {
"version": "6.18.0",
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz",
@@ -740,6 +858,80 @@
"integrity": "sha1-/WU28rzhODb/o6VFjEkDpZe7O/U=",
"dev": true
},
+ "babel-plugin-syntax-trailing-function-commas": {
+ "version": "6.22.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz",
+ "integrity": "sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM=",
+ "dev": true
+ },
+ "babel-plugin-transform-async-generator-functions": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-generator-functions/-/babel-plugin-transform-async-generator-functions-6.24.1.tgz",
+ "integrity": "sha1-8FiQAUX9PpkHpt3yjaWfIVJYpds=",
+ "dev": true,
+ "requires": {
+ "babel-helper-remap-async-to-generator": "6.24.1",
+ "babel-plugin-syntax-async-generators": "6.13.0",
+ "babel-runtime": "6.26.0"
+ }
+ },
+ "babel-plugin-transform-async-to-generator": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz",
+ "integrity": "sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E=",
+ "dev": true,
+ "requires": {
+ "babel-helper-remap-async-to-generator": "6.24.1",
+ "babel-plugin-syntax-async-functions": "6.13.0",
+ "babel-runtime": "6.26.0"
+ }
+ },
+ "babel-plugin-transform-class-constructor-call": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-class-constructor-call/-/babel-plugin-transform-class-constructor-call-6.24.1.tgz",
+ "integrity": "sha1-gNwoVQWsBn3LjWxl4vbxGrd2Xvk=",
+ "dev": true,
+ "requires": {
+ "babel-plugin-syntax-class-constructor-call": "6.18.0",
+ "babel-runtime": "6.26.0",
+ "babel-template": "6.26.0"
+ }
+ },
+ "babel-plugin-transform-class-properties": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz",
+ "integrity": "sha1-anl2PqYdM9NvN7YRqp3vgagbRqw=",
+ "dev": true,
+ "requires": {
+ "babel-helper-function-name": "6.24.1",
+ "babel-plugin-syntax-class-properties": "6.13.0",
+ "babel-runtime": "6.26.0",
+ "babel-template": "6.26.0"
+ }
+ },
+ "babel-plugin-transform-decorators": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-decorators/-/babel-plugin-transform-decorators-6.24.1.tgz",
+ "integrity": "sha1-eIAT2PjGtSIr33s0Q5Df13Vp4k0=",
+ "dev": true,
+ "requires": {
+ "babel-helper-explode-class": "6.24.1",
+ "babel-plugin-syntax-decorators": "6.13.0",
+ "babel-runtime": "6.26.0",
+ "babel-template": "6.26.0",
+ "babel-types": "6.26.0"
+ }
+ },
+ "babel-plugin-transform-do-expressions": {
+ "version": "6.22.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-do-expressions/-/babel-plugin-transform-do-expressions-6.22.0.tgz",
+ "integrity": "sha1-KMyvkoEtlJws0SgfaQyP3EaK6bs=",
+ "dev": true,
+ "requires": {
+ "babel-plugin-syntax-do-expressions": "6.13.0",
+ "babel-runtime": "6.26.0"
+ }
+ },
"babel-plugin-transform-es2015-arrow-functions": {
"version": "6.22.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz",
@@ -974,6 +1166,27 @@
"regexpu-core": "2.0.0"
}
},
+ "babel-plugin-transform-exponentiation-operator": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz",
+ "integrity": "sha1-KrDJx/MJj6SJB3cruBP+QejeOg4=",
+ "dev": true,
+ "requires": {
+ "babel-helper-builder-binary-assignment-operator-visitor": "6.24.1",
+ "babel-plugin-syntax-exponentiation-operator": "6.13.0",
+ "babel-runtime": "6.26.0"
+ }
+ },
+ "babel-plugin-transform-export-extensions": {
+ "version": "6.22.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-export-extensions/-/babel-plugin-transform-export-extensions-6.22.0.tgz",
+ "integrity": "sha1-U3OLR+deghhYnuqUbLvTkQm75lM=",
+ "dev": true,
+ "requires": {
+ "babel-plugin-syntax-export-extensions": "6.13.0",
+ "babel-runtime": "6.26.0"
+ }
+ },
"babel-plugin-transform-flow-strip-types": {
"version": "6.22.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.22.0.tgz",
@@ -984,6 +1197,16 @@
"babel-runtime": "6.26.0"
}
},
+ "babel-plugin-transform-function-bind": {
+ "version": "6.22.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-function-bind/-/babel-plugin-transform-function-bind-6.22.0.tgz",
+ "integrity": "sha1-xvuOlqwpajELjPjqQBRiQH3fapc=",
+ "dev": true,
+ "requires": {
+ "babel-plugin-syntax-function-bind": "6.13.0",
+ "babel-runtime": "6.26.0"
+ }
+ },
"babel-plugin-transform-object-assign": {
"version": "6.22.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-object-assign/-/babel-plugin-transform-object-assign-6.22.0.tgz",
@@ -1126,6 +1349,53 @@
"babel-preset-flow": "6.23.0"
}
},
+ "babel-preset-stage-0": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-preset-stage-0/-/babel-preset-stage-0-6.24.1.tgz",
+ "integrity": "sha1-VkLRUEL5E4TX5a+LyIsduVsDnmo=",
+ "dev": true,
+ "requires": {
+ "babel-plugin-transform-do-expressions": "6.22.0",
+ "babel-plugin-transform-function-bind": "6.22.0",
+ "babel-preset-stage-1": "6.24.1"
+ }
+ },
+ "babel-preset-stage-1": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-preset-stage-1/-/babel-preset-stage-1-6.24.1.tgz",
+ "integrity": "sha1-dpLNfc1oSZB+auSgqFWJz7niv7A=",
+ "dev": true,
+ "requires": {
+ "babel-plugin-transform-class-constructor-call": "6.24.1",
+ "babel-plugin-transform-export-extensions": "6.22.0",
+ "babel-preset-stage-2": "6.24.1"
+ }
+ },
+ "babel-preset-stage-2": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-preset-stage-2/-/babel-preset-stage-2-6.24.1.tgz",
+ "integrity": "sha1-2eKWD7PXEYfw5k7sYrwHdnIZvcE=",
+ "dev": true,
+ "requires": {
+ "babel-plugin-syntax-dynamic-import": "6.18.0",
+ "babel-plugin-transform-class-properties": "6.24.1",
+ "babel-plugin-transform-decorators": "6.24.1",
+ "babel-preset-stage-3": "6.24.1"
+ }
+ },
+ "babel-preset-stage-3": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-preset-stage-3/-/babel-preset-stage-3-6.24.1.tgz",
+ "integrity": "sha1-g2raCp56f6N8sTj7kyb4eTSkg5U=",
+ "dev": true,
+ "requires": {
+ "babel-plugin-syntax-trailing-function-commas": "6.22.0",
+ "babel-plugin-transform-async-generator-functions": "6.24.1",
+ "babel-plugin-transform-async-to-generator": "6.24.1",
+ "babel-plugin-transform-exponentiation-operator": "6.24.1",
+ "babel-plugin-transform-object-rest-spread": "6.26.0"
+ }
+ },
"babel-register": {
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz",
@@ -3850,8 +4120,8 @@
"dev": true,
"optional": true,
"requires": {
- "co": "4.6.0",
- "json-stable-stringify": "1.0.1"
+ "co": "^4.6.0",
+ "json-stable-stringify": "^1.0.1"
}
},
"ansi-regex": {
@@ -3924,7 +4194,7 @@
"bundled": true,
"dev": true,
"requires": {
- "inherits": "2.0.3"
+ "inherits": "~2.0.0"
}
},
"boom": {
@@ -3932,7 +4202,7 @@
"bundled": true,
"dev": true,
"requires": {
- "hoek": "2.16.3"
+ "hoek": "2.x.x"
}
},
"brace-expansion": {
@@ -3994,7 +4264,7 @@
"bundled": true,
"dev": true,
"requires": {
- "boom": "2.10.1"
+ "boom": "2.x.x"
}
},
"dashdash": {
@@ -4052,7 +4322,7 @@
"dev": true,
"optional": true,
"requires": {
- "jsbn": "0.1.1"
+ "jsbn": "~0.1.0"
}
},
"extend": {
@@ -4132,7 +4402,7 @@
"dev": true,
"optional": true,
"requires": {
- "assert-plus": "1.0.0"
+ "assert-plus": "^1.0.0"
},
"dependencies": {
"assert-plus": {
@@ -4261,7 +4531,7 @@
"dev": true,
"optional": true,
"requires": {
- "jsbn": "0.1.1"
+ "jsbn": "~0.1.0"
}
},
"jsbn": {
@@ -4282,7 +4552,7 @@
"dev": true,
"optional": true,
"requires": {
- "jsonify": "0.0.0"
+ "jsonify": "~0.0.0"
}
},
"json-stringify-safe": {
@@ -8579,6 +8849,14 @@
"prop-types": "15.6.0"
}
},
+ "react-dropdown": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/react-dropdown/-/react-dropdown-1.5.0.tgz",
+ "integrity": "sha512-rRv3a7NiP++yC1rzdjzkviC5ujq759i4SRa0M3C0Cr7loYT4Z3+JhSPekv1/04JiZNXX46cV3/g6A9kS7rkI4Q==",
+ "requires": {
+ "classnames": "2.2.5"
+ }
+ },
"react-redux": {
"version": "5.0.6",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-5.0.6.tgz",
diff --git a/app/package.json b/app/package.json
index 8c5a1c4..85f4bc4 100644
--- a/app/package.json
+++ b/app/package.json
@@ -21,6 +21,7 @@
"react": "^16.2.0",
"react-clipboard.js": "^1.1.3",
"react-dom": "^16.2.0",
+ "react-dropdown": "^1.5.0",
"react-redux": "^5.0.6",
"react-spinner": "^0.2.7",
"react-tooltip": "^3.4.0",
@@ -38,6 +39,7 @@
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.24.1",
+ "babel-preset-stage-0": "^6.24.1",
"babelify": "^8.0.0",
"browser-sync": "^2.23.6",
"browserify": "^16.1.0",
diff --git a/app/resources/images/icon_audio_only_white.svg b/app/resources/images/icon_audio_only_white.svg
index 12a0389..fd5a889 100644
--- a/app/resources/images/icon_audio_only_white.svg
+++ b/app/resources/images/icon_audio_only_white.svg
@@ -1,4 +1,4 @@
-