Move old settings modal to the new component

This commit is contained in:
Oswaldo Acauan 2016-09-01 13:56:41 +00:00
parent 7a33f9ee67
commit 249dd85f92
23 changed files with 725 additions and 21 deletions

View File

@ -1,5 +1,7 @@
import React, { Component, PropTypes } from 'react';
import { FormattedMessage } from 'react-intl';
import LoadingScreen from '../loading-screen/component';
import KickedScreen from '../kicked-screen/component';
@ -14,11 +16,15 @@ const propTypes = {
sidebarRight: PropTypes.element,
media: PropTypes.element,
actionsbar: PropTypes.element,
settings: PropTypes.element,
captions: PropTypes.element,
modal: PropTypes.element,
};
export default class App extends Component {
constructor(props) {
super(props);
}
renderNavBar() {
const { navbar } = this.props;
@ -115,26 +121,22 @@ export default class App extends Component {
return false;
}
renderSettings() {
const { settings } = this.props;
if (settings) {
return (
<section>
{settings}
</section>
);
}
return false;
}
renderAudioElement() {
return (
<audio id="remote-media" autoPlay="autoplay"></audio>
);
}
renderModal() {
const { modal } = this.props;
if (modal) {
return (<div>{modal}</div>);
}
return false;
}
render() {
if (this.props.wasKicked) {
return (
@ -170,8 +172,8 @@ export default class App extends Component {
</div>
{this.renderSidebar()}
</section>
{this.renderSettings()}
{this.renderAudioElement()}
{this.renderModal()}
</main>
);
}

View File

@ -1,11 +1,10 @@
import React, { Component, PropTypes, cloneElement } from 'react';
import { createContainer } from 'meteor/react-meteor-data';
import App from './component';
import { subscribeForData, wasUserKicked, redirectToLogoutUrl } from './service';
import { subscribeForData, wasUserKicked, redirectToLogoutUrl, getModal } from './service';
import NavBarContainer from '../nav-bar/container';
import ActionsBarContainer from '../actions-bar/container';
import MediaContainer from '../media/container';
import SettingsModal from '../modals/settings/component';
import ClosedCaptionsContainer from '../closed-captions/container';
const defaultProps = {
@ -58,6 +57,7 @@ export default createContainer(() => {
return {
wasKicked: wasUserKicked(),
isLoading: getLoading(),
modal: getModal(),
redirectToLogoutUrl,
};
}, AppContainer);

View File

@ -86,11 +86,28 @@ function wasUserKicked() {
return wasKicked;
}
let modal = null;
const modalDep = new Tracker.Dependency;
const getModal = () => {
modalDep.depend();
return modal;
};
const showModal = (val) => {
if (val !== modal) {
modal = val;
modalDep.changed();
}
};
export {
subscribeForData,
setCredentials,
subscribeFor,
subscribeToCollections,
wasUserKicked,
redirectToLogoutUrl
redirectToLogoutUrl,
getModal,
showModal,
};

View File

@ -5,8 +5,8 @@ import Button from '/imports/ui/components/button/component';
import classNames from 'classnames';
import styles from './styles';
import { FormattedMessage } from 'react-intl';
import SettingsModal from '../modals/settings/component';
import SessionMenu from '../modals/settings/submenus/session/component';
import SettingsModal from '../modals-old/settings/component';
import SessionMenu from '../modals-old/settings/submenus/session/component';
import Dropdown from './dropdown-menu/component';
import DropdownTrigger from './dropdown-trigger/component';
import DropdownContent from './dropdown-content/component';

View File

@ -0,0 +1,203 @@
import React from 'react';
import Icon from '/imports/ui/components/icon/component';
import Button from '/imports/ui/components/button/component';
import Modal from '/imports/ui/components/modal/component';
import AudioMenu from './submenus/audio/component';
import VideoMenu from './submenus/video/component';
import ApplicationMenu from './submenus/application/component';
import UsersMenu from './submenus/users/component';
import classNames from 'classnames';
import ReactDOM from 'react-dom';
import styles from './styles.scss';
export default class Settings extends React.Component {
constructor(props) {
super(props);
this.submenus = [];
}
componentWillMount() {
/* activeSubmenu represents the submenu in the submenus array to be displayed to the user,
* initialized to 0
*/
this.setState({ activeSubmenu: 0 });
/* focusSubmenu represents the submenu in the submenus array which currently has focus,
* initialized to 0
*/
this.setState({ focusSubmenu: 0 });
this.submenus.push({ componentName: AudioMenu, tabIndex: 3,
props: { title: 'Audio', prependIconName: 'icon-', icon: 'bbb-audio', }, });
this.submenus.push({ componentName: VideoMenu, tabIndex: 4,
props: { title: 'Video', prependIconName: 'icon-', icon: 'bbb-video', }, });
this.submenus.push({ componentName: ApplicationMenu, tabIndex: 5,
props: { title: 'Application', prependIconName: 'icon-', icon: 'bbb-application', }, });
this.submenus.push({ componentName: UsersMenu, tabIndex: 6,
props: { title: 'Participants', prependIconName: 'icon-', icon: 'bbb-user', }, });
}
createMenu() {
const curr = this.state.activeSubmenu === undefined ? 0 : this.state.activeSubmenu;
let props = {
title: this.submenus[curr].props.title,
prependIconName: this.submenus[curr].props.prependIconName,
icon: this.submenus[curr].props.icon,
};
const Submenu = this.submenus[curr].componentName;
return <Submenu {...props}/>;
}
/* When an option in the menu is clicked, set the activeSubmenu and focusSubmenu
* to the value of index. If clicked out of bounds set to 0 or end of submenus array accordingly.
*
* activeSubmenu: the submenu to be displayed to the user
* focusSubmenu: the submenu to set focus to
*/
clickSubmenu(i) {
if (i <= 0) {
this.setState({ activeSubmenu: 0, focusSubmenu: 0, });
return;
}
if (i >= this.submenus.length) {
this.setState({ activeSubmenu: this.submenus.length - 1,
focusSubmenu: this.submenus.length - 1, });
return;
} else {
this.setState({ activeSubmenu: i, focusSubmenu: i, });
}
}
/* calls the focus method on an object in the submenu */
setFocus() {
ReactDOM.findDOMNode(this.refs[`submenu${this.state.focusSubmenu}`]).focus();
}
/* Checks for key presses within the submenu list. Key behaviour varies.
*
* Tab: changes focus to next submenu or element outside of menu
* Shift+Tab: changes focus to previous submenu or element outside of menu
* Up Arrow: changes focus to previous submenu, can cycle through menu
* Down Arrow: changes focus to next submenu, can cycle through menu
* Spacebar: selects submenu in focus and sets as active
* Enter: selects submenu in focus and sets as active
*/
handleKeyDown(event) {
// tab
if (event.keyCode === 9) {
let newIndex = 0;
if (this.state.focusSubmenu >= this.submenus.length - 1) {
newIndex = this.submenus.length - 1;
} else {
newIndex = this.state.focusSubmenu + 1;
}
this.setState({ focusSubmenu: newIndex });
return;
}
// shift+tab
if (event.shiftKey && event.keyCode === 9) {
let newIndex = 0;
if (this.state.focusSubmenu <= 0) {
newIndex = 0;
} else {
newIndex = this.state.focusSubmenu - 1;
}
this.setState({ focusSubmenu: newIndex });
return;
}
// up arrow
if (event.keyCode === 38) {
if (this.state.focusSubmenu <= 0) {
this.setState({ focusSubmenu: this.submenus.length - 1 }, function () {
this.setFocus();
});
} else {
this.setState({ focusSubmenu: this.state.focusSubmenu - 1 }, function () {
this.setFocus();
});
}
return;
}
// down arrow
if (event.keyCode === 40) {
if (this.state.focusSubmenu >= this.submenus.length - 1) {
this.setState({ focusSubmenu: 0 }, function () {
this.setFocus();
});
} else {
this.setState({ focusSubmenu: this.state.focusSubmenu + 1 }, function () {
this.setFocus();
});
}
return;
}
// spacebar or enter
if (event.keyCode === 32 || event.keyCode === 13) {
this.setState({ activeSubmenu: this.state.focusSubmenu });
return;
}
}
/* Keeps the focusSubmenu variable at the correct value when
* tabbing or shift-tabbing out of the submenu array
*/
handleFocus(index) {
this.setState({ focusSubmenu: index });
}
render() {
return (
<Modal
title="Settings"
confirm={{
callback: (() => {
this.setState({ activeSubmenu: 0, focusSubmenu: 0 });
console.log('SHOULD APPLY SETTINGS CHANGES');
}),
label: 'Done',
description: 'Saves the changes and close the settings menu',
}}
dismiss={{
callback: (() => {
this.setState({ activeSubmenu: 0, focusSubmenu: 0 });
console.log('SHOULD DISCART SETTINGS CHANGES');
}),
label: 'Cancel',
description: 'Discart the changes and close the settings menu',
}}>
<div className={styles.full} role='presentation'>
<div className={styles.settingsMenuLeft}>
<ul className={styles.settingsSubmenu} role='menu'>
{this.submenus.map((value, index) => (
<li key={index} ref={'submenu' + index} role='menuitem' tabIndex={value.tabIndex}
onClick={this.clickSubmenu.bind(this, index)}
onKeyDown={this.handleKeyDown.bind(this)}
onFocus={this.handleFocus.bind(this, index)}
className={classNames(styles.settingsSubmenuItem,
index == this.state.activeSubmenu ? styles.settingsSubmenuItemActive : null)}>
<Icon key={index} prependIconName={value.props.prependIconName}
iconName={value.props.icon} title={value.props.title}/>
<span className={styles.settingsSubmenuItemText}>{value.props.title}</span>
</li>
))}
</ul>
</div>
<div className={styles.settingsMenuRight} role='presentation'>
{this.createMenu()}
</div>
</div>
</Modal>
);
}
};
Settings.defaultProps = { title: 'Settings' };

View File

@ -0,0 +1,49 @@
@import "../../stylesheets/variables/_all";
.full {
clear: both;
height: 100%;
width: 100%;
}
.settingsMenuLeft {
border-right: 2px solid $color-gray-light;
border-top: 2px solid $color-gray-light;
float: left;
width: 30%;
height: 100%;
overflow: auto;
}
.settingsMenuRight {
border-top: 2px solid $color-gray-light;
float: right;
padding-left: 10px;
width: 70%;
height: 100%;
overflow: auto;
color: $color-heading;
}
.settingsSubmenu {
list-style-type: none;
padding-left: 0px;
}
.settingsSubmenuItem {
color: $color-heading;
padding-top: 5px;
padding-bottom: 5px;
margin-top: 10px;
margin-bottom: 10px;
}
.settingsSubmenuItemText {
padding-left: 10px;
vertical-align: middle;
}
.settingsSubmenuItemActive {
border-right: 4px solid $color-primary;
color: $color-primary;
}

View File

@ -0,0 +1,74 @@
import React from 'react';
import Modal from 'react-modal';
import Icon from '/imports/ui/components/icon/component';
import Button from '/imports/ui/components/button/component';
import BaseMenu from '../base/component';
import ReactDOM from 'react-dom';
import FontControl from '/imports/api/FontControl';
import styles from '../styles.scss';
export default class ApplicationMenu extends BaseMenu {
constructor(props) {
super(props);
this.state = {
currentFontSize: FontControl.fontSizeEnum.MEDIUM,
};
}
getContent() {
return (
<div className={styles.full} role='presentation'>
<div className={styles.row} role='presentation'>
<label><input type='checkbox' tabIndex='7' aria-labelledby='audioNotifLabel'
aria-describedby='audioNotifDesc' />Audio notifications for chat</label>
<div id='audioNotifLabel' hidden>Audio notifications</div>
<div id='audioNotifDesc' hidden>
Toggles the audio notifications for chat.</div>
</div>
<div className={styles.row} role='presentation'>
<label><input type='checkbox' tabIndex='8' aria-labelledby='pushNotifLabel'
aria-describedby='pushNotifDesc' />Push notifications for chat</label>
<div id='pushNotifLabel' hidden>Push notifications</div>
<div id='pushNotifDesc' hidden>
Toggles the push notifications for chat.</div>
</div>
<div className={styles.applicationFontContainer} role='presentation'>
<div className={styles.fontBarLeft}>
<p>Font size</p>
</div>
<div className={styles.fontBarMid}>
<p>{FontControl.getFontSizeName.call(this)}</p>
</div>
<div className={styles.fontBarRight} role='presentation'>
<Button
onClick={FontControl.increaseFontSize.bind(this)}
icon={'circle-add'}
circle={true}
tabIndex={9}
hideLabel={true}
label={'Increase Font'}
aria-labelledby={'sizeUpLabel'}
aria-describedby={'sizeUpDesc'}
/>
<div id='sizeUpLabel' hidden>Font size up</div>
<div id='sizeUpDesc' hidden>
Increases the font size of the application.</div>
<Button
onClick={FontControl.decreaseFontSize.bind(this)}
icon={'circle-minus'}
circle={true}
tabIndex={10}
hideLabel={true}
label={'Decrease Font'}
aria-labelledby={'sizeDownLabel'}
aria-describedby={'sizeDownDesc'}
/>
<div id='sizeDownLabel' hidden>Font size down</div>
<div id='sizeDownDesc' hidden>
Decreases the font size of the application.</div>
</div>
</div>
</div>
);
}
};

View File

@ -0,0 +1,79 @@
import React from 'react';
import Modal from 'react-modal';
import Icon from '/imports/ui/components/icon/component';
import Button from '/imports/ui/components/button/component';
import BaseMenu from '../base/component';
import styles from '../styles.scss';
import {joinListenOnly, joinMicrophone, exitAudio} from '/imports/api/phone';
export default class AudioMenu extends BaseMenu {
constructor(props) {
super(props);
this.testAudio = this.testAudio.bind(this);
}
testAudio() {
}
getContent() {
return (
<div className={styles.full} role='presentation'>
<div className={styles.containerLeftHalf}>
<label htmlFor='microphone'>Microphone source</label>
</div>
<div className={styles.containerRightHalf}>
<label htmlFor='audioVolume'>Your audio stream volume</label>
</div>
<div className={styles.containerLeftHalfContent} role='presentation'>
<select id='microphone' defaultValue='0' tabIndex='7' role='listbox'
aria-labelledby='micLabel' aria-describedby='micDesc'>
<option value='0' disabled>Displace Audio</option>
<option value='1' role='option'>audio 1</option>
<option value='2' role='option'>audio 2</option>
<option value='3' role='option'>audio 3</option>
</select>
<div id='micLabel' hidden>Select microphone source</div>
<div id='micDesc' hidden>
Chooses a microphone source from the dropdown menu.</div>
</div>
<div className={styles.containerRightHalfContent}>
<input style={{ width: '90%' }} type='text' placeholder='volume bar placeholder'
tabIndex='-1' />
</div>
<div className={styles.containerLeftHalf}>
<label htmlFor='speaker'>Speaker source</label>
</div>
<div className={styles.containerRightHalf}><br/></div>
<div className={styles.containerLeftHalfContent} role='presentation'>
<select id='speaker' defaultValue='0' tabIndex='8' role='listbox'
aria-labelledby='spkrLabel' aria-describedby='spkrDesc'>
<option value='0' disabled>Displace Audio</option>
<option value='1' role='option'>audio 1</option>
<option value='2' role='option'>audio 2</option>
<option value='3' role='option'>audio 3</option>
</select>
<div id='spkrLabel' hidden>Select speaker source</div>
<div id='spkrDesc' hidden>
Chooses a speaker source from the dropdown menu.</div>
</div>
<div className={styles.containerRightHalfContent} role='presentation'>
<Button className={styles.testAudioBtn}
onClick={this.testAudio}
label={'Test Audio'}
color={'primary'}
ghost={true}
icon={'audio'}
tabIndex={9}
aria-labelledby={'testAudioLabel'}
aria-describedby={'testAudioDesc'}
/>
<div id='testAudioLabel' hidden>Test audio</div>
<div id='testAudioDesc' hidden>
Previews the audio output of your microphone.</div>
</div>
</div>
);
}
};

View File

@ -0,0 +1,26 @@
import React from 'react';
import Modal from 'react-modal';
import Icon from '/imports/ui/components/icon/component';
import Button from '/imports/ui/components/button/component';
import styles from '../styles.scss';
export default class BaseMenu extends React.Component {
constructor(props) {
super(props);
}
getContent() {
return (<div>parent content</div>);
}
render() {
return (
<div className={styles.full} role='presentation'>
<h3 className={styles.submenuTitle}>{this.props.title}</h3>
<div className={styles.submenuContent} role='presentation'>
{this.getContent()}
</div>
</div>
);
}
};

View File

@ -0,0 +1,140 @@
@import "../../../stylesheets/variables/_all";
/* General Submenu */
.submenuTitle {
height: 45px;
margin: 0px;
padding-top: 12px;
font-size: $font-size-large;
font-weight: $headings-font-weight;
}
.submenuContent {
height: 85%;
margin: 0px;
padding-top: 12px;
font-size: $font-size-base;
color: $color-gray-light;
}
div.submenuContent select {
width: 90%;
padding-top: 5px;
border-top: none;
border-left: none;
border-right: none;
/*outline: none;*/
color: $color-gray-light;
}
.containerLeftHalf {
width: 50%;
float: left;
}
.containerRightHalf {
width: 50%;
float: right;
}
.containerLeftHalfContent {
@extend .containerLeftHalf;
@extend .row;
}
.containerRightHalfContent {
@extend .containerRightHalf;
@extend .row;
}
div.containerLeftHalf label, div.containerRightHalf label {
font-size: 0.75em;
font-weight: 600;
color: $color-primary;
margin-bottom: 5px;
}
input[type=checkbox] {
float: right;
}
ul {
list-style-type: none;
margin: 0px;
}
ul li {
padding-bottom: 9%;
}
/* Application Submenu */
.applicationFontContainer {
border-top: solid 1px $color-gray-light;
margin-top: 10%;
padding-top: 10%;
}
.fontBarLeft {
float: left;
margin: 0px;
height: 100%;
width: 25%;
text-align: left;
}
.fontBarMid {
float: left;
margin: 0px;
height: 100%;
width: 50%;
text-align: center;
font-weight: 600;
}
.fontBarRight {
float: left;
margin: 0px;
height: 100%;
width: 25%;
text-align: right;
}
.fontBarLeft p, .fontBarMid p {
margin: 0px;
}
/* Users Submenu */
.checkboxOffset {
margin-right: 5%;
}
/* Buttons */
.defaultBtn {
width: 90px;
border-radius: 18px;
border: none;
box-shadow: none;
}
.testAudioBtn {
@extend .defaultBtn;
width: 140px;
float: left;
}
/* Submenu containers */
.full {
clear: both;
height: 100%;
width: 100%;
}
.row {
height: 42px;
}
.indentedRow {
@extend .row;
padding-left: 10%;
}

View File

@ -0,0 +1,62 @@
import React from 'react';
import Modal from 'react-modal';
import Icon from '/imports/ui/components/icon/component';
import Button from '/imports/ui/components/button/component';
import BaseMenu from '../base/component';
import styles from '../styles.scss';
export default class UsersMenu extends BaseMenu {
constructor(props) {
super(props);
}
getContent() {
return (
<div className={styles.full} role='presentation'>
<div className={styles.row} role='presentation'>
<label><input className={styles.checkboxOffset} type='checkbox' tabIndex='7'
aria-labelledby='muteALlLabel' aria-describedby='muteAllDesc' />
Mute all except the presenter</label>
</div>
<div id='muteAllLabel' hidden>Mute all</div>
<div id='muteAllDesc' hidden>Mutes all participants except the presenter.</div>
<div className={styles.row} role='presentation'>
<label><input className={styles.checkboxOffset} type="checkbox" tabIndex='8'
aria-labelledby='lockAllLabel' aria-describedby='lockAllDesc' />
Lock all participants</label>
</div>
<div id='lockAllLabel' hidden>Lock all</div>
<div id='lockAllDesc' hidden>Toggles locked status for all participants.</div>
<div className={styles.indentedRow} role='presentation'>
<label><input className={styles.checkboxOffset} type='checkbox' tabIndex='9'
aria-labelledby='webcamLabel' aria-describedby='webcamDesc' />Webcam</label>
</div>
<div id='webcamLabel' hidden>Webcam lock</div>
<div id='webcamDesc' hidden>Disables the webcam for all locked participants.</div>
<div className={styles.indentedRow} role='presentation'>
<label><input className={styles.checkboxOffset} type='checkbox' tabIndex='10'
aria-labelledby='micLabel' aria-describedby='micDesc' />Microphone</label>
</div>
<div id='micLabel' hidden>Microphone lock</div>
<div id='micDesc' hidden>Disables the microphone for all locked participants.</div>
<div className={styles.indentedRow} role='presentation'>
<label><input className={styles.checkboxOffset} type='checkbox' tabIndex='11'
aria-labelledby='pubChatLabel' aria-describedby='pubChatDesc' />Public chat</label>
</div>
<div id='pubChatLabel' hidden>Public chat lock</div>
<div id='pubChatDesc' hidden>Disables public chat for all locked participants.</div>
<div className={styles.indentedRow} role='presentation'>
<label><input className={styles.checkboxOffset} type='checkbox' tabIndex='12'
aria-labelledby='privChatLabel' aria-describedby='privChatDesc' />Private chat</label>
</div>
<div id='privChatLabel' hidden>Private chat lock</div>
<div id='privChatDesc' hidden>Disables private chat for all locked participants.</div>
</div>
);
}
};

View File

@ -0,0 +1,52 @@
import React from 'react';
import Modal from 'react-modal';
import Icon from '/imports/ui/components/icon/component';
import Button from '/imports/ui/components/button/component';
import BaseMenu from '../base/component';
import styles from '../styles.scss';
export default class VideoMenu extends BaseMenu {
constructor(props) {
super(props);
}
getContent() {
return (
<div className={styles.full} role='presentation'>
<div className={styles.containerLeftHalf}>
<label htmlFor='camera'>Select camera</label>
</div>
<div className={styles.containerRightHalf}>
<label htmlFor='quality' >Video quality</label>
</div>
<div className={styles.containerLeftHalfContent} role='presentation'>
<select id='camera' defaultValue='0' tabIndex='7'
aria-labelledby='camLabel' aria-describedby='camDesc'>
<option value='0' disabled>Select camera</option>
<option value='1'>Camera 1</option>
<option value='2'>Camera 2</option>
<option value='3'>Camera 3</option>
</select>
<div id='camLabel' hidden>Camera source</div>
<div id='camDesc' hidden>
Chooses a camera source from the dropdown menu.</div>
</div>
<div className={styles.containerRightHalfContent} role='presentation'>
<select id='quality' defaultValue='0' tabIndex='8'
aria-labelledby='vidLabel' aria-describedby='vidDesc'>
<option value='0' disabled>Select quality</option>
<option value='1'>Low</option>
<option value='2'>Medium</option>
<option value='3'>High</option>
</select>
<div id='vidLabel' hidden>Video quality</div>
<div id='vidDesc' hidden>
Chooses the video quality level from the dropdown menu.</div>
</div>
<div className={styles.row}>
<div>Viewing participants webcams</div>
</div>
</div>
);
}
};