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 @@ - + diff --git a/app/resources/images/icon_restart_ice_white.svg b/app/resources/images/icon_restart_ice_white.svg index 2190d8c..6be67f1 100644 --- a/app/resources/images/icon_restart_ice_white.svg +++ b/app/resources/images/icon_restart_ice_white.svg @@ -1,4 +1,4 @@ - + diff --git a/app/resources/images/icon_settings_black.svg b/app/resources/images/icon_settings_black.svg new file mode 100644 index 0000000..9ca9954 --- /dev/null +++ b/app/resources/images/icon_settings_black.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/resources/images/icon_settings_white.svg b/app/resources/images/icon_settings_white.svg new file mode 100644 index 0000000..250a969 --- /dev/null +++ b/app/resources/images/icon_settings_white.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/stylus/components/Room.styl b/app/stylus/components/Room.styl index 6685afd..cb51f02 100644 --- a/app/stylus/components/Room.styl +++ b/app/stylus/components/Room.styl @@ -232,6 +232,16 @@ } } + &.settings { + &.off { + background-image: url('/resources/images/icon_settings_white.svg'); + } + + &.on { + background-image: url('/resources/images/icon_settings_black.svg'); + } + } + &.screen { &.on { background-image: url('/resources/images/no-share-screen-black.svg'); diff --git a/app/stylus/components/Settings.styl b/app/stylus/components/Settings.styl new file mode 100644 index 0000000..f1eab49 --- /dev/null +++ b/app/stylus/components/Settings.styl @@ -0,0 +1,149 @@ +[data-component='Settings'] { + position: fixed; + top: 0; + bottom: 0; + left: 0; + right: 0; + z-index: 19999; + background-color: rgba(000, 000, 000, 0.5); + + > .dialog { + position: absolute; + height: 50vmin; + width: 60vmin; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + background-color: #fff; + border-radius: 4px; + box-shadow: 0px 3px 12px 2px rgba(#111, 0.4); + padding: 1vmin; + + > .header { + > span { + font-size: 2vmin; + font-weight: 400; + } + } + + > .settings { + height: 100%; + width: 100%; + + .Dropdown-root { + position: relative; + } + + .Dropdown-control { + position: relative; + overflow: hidden; + background-color: white; + border: 1px solid #ccc; + border-radius: 2px; + box-sizing: border-box; + color: #333; + cursor: default; + outline: none; + padding: 8px 52px 8px 10px; + transition: all 200ms ease; + } + + .Dropdown-control:hover { + box-shadow: 0 1px 0 rgba(0, 0, 0, 0.06); + } + + .Dropdown-arrow { + border-color: #999 transparent transparent; + border-style: solid; + border-width: 5px 5px 0; + content: ' '; + display: block; + height: 0; + margin-top: -ceil(2.5); + position: absolute; + right: 10px; + top: 14px; + width: 0 + } + + .is-open .Dropdown-arrow { + border-color: transparent transparent #999; + border-width: 0 5px 5px; + } + + .Dropdown-menu { + background-color: white; + border: 1px solid #ccc; + box-shadow: 0 1px 0 rgba(0, 0, 0, 0.06); + box-sizing: border-box; + margin-top: -1px; + max-height: 200px; + overflow-y: auto; + position: absolute; + top: 100%; + width: 100%; + z-index: 1000; + -webkit-overflow-scrolling: touch; + } + + .Dropdown-menu .Dropdown-group > .Dropdown-title { + padding: 8px 10px; + color: rgba(51, 51, 51, 1.2); + font-weight: bold; + text-transform: capitalize; + } + + .Dropdown-option { + box-sizing: border-box; + color: rgba(51, 51, 51, 0.8); + cursor: pointer; + display: block; + padding: 8px 10px; + } + + .Dropdown-option:last-child { + border-bottom-right-radius: 2px; + border-bottom-left-radius: 2px; + } + + .Dropdown-option:hover { + background-color: #f2f9fc; + color: #333; + } + + .Dropdown-option.is-selected { + background-color: #f2f9fc; + color: #333; + } + + .Dropdown-noresults { + box-sizing: border-box; + color: #ccc; + cursor: default; + display: block; + padding: 8px 10px; + } + } + + > .footer { + position: absolute; + bottom: 0; + right: 0; + left: 0; + display: flex; + flex-direction: column; + justify-content: flex-end; + align-items: flex-end; + + > .button { + flex: 0 0 auto; + margin: 1vmin; + background-color: rgba(#000, 0.8); + color: #fff; + cursor: pointer; + border-radius: 4px; + padding: 0.5vmin; + } + } + } +} diff --git a/app/stylus/index.styl b/app/stylus/index.styl index f73898b..238808c 100644 --- a/app/stylus/index.styl +++ b/app/stylus/index.styl @@ -42,6 +42,7 @@ body { @import './components/PeerView'; @import './components/Notifications'; @import './components/Chat'; + @import './components/Settings'; } // Hack to detect in JS the current media query