Move top AppBar to its own component.

master
Håvar Aambø Fosstveit 2019-11-05 15:27:04 +01:00
parent 99575f330b
commit 0414aa6b7b
2 changed files with 341 additions and 296 deletions

View File

@ -0,0 +1,329 @@
import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import {
lobbyPeersKeySelector
} from '../Selectors';
import * as appPropTypes from '../appPropTypes';
import { withRoomContext } from '../../RoomContext';
import { withStyles } from '@material-ui/core/styles';
import * as roomActions from '../../actions/roomActions';
import * as toolareaActions from '../../actions/toolareaActions';
import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import Typography from '@material-ui/core/Typography';
import IconButton from '@material-ui/core/IconButton';
import MenuIcon from '@material-ui/icons/Menu';
import Avatar from '@material-ui/core/Avatar';
import Badge from '@material-ui/core/Badge';
import AccountCircle from '@material-ui/icons/AccountCircle';
import FullScreenIcon from '@material-ui/icons/Fullscreen';
import FullScreenExitIcon from '@material-ui/icons/FullscreenExit';
import SettingsIcon from '@material-ui/icons/Settings';
import SecurityIcon from '@material-ui/icons/Security';
import LockIcon from '@material-ui/icons/Lock';
import LockOpenIcon from '@material-ui/icons/LockOpen';
import Button from '@material-ui/core/Button';
import Tooltip from '@material-ui/core/Tooltip';
const styles = (theme) =>
({
menuButton :
{
margin : 0,
padding : 0
},
logo :
{
display : 'none',
marginLeft : 20,
[theme.breakpoints.up('sm')] :
{
display : 'block'
}
},
show :
{
opacity : 1,
transition : 'opacity .5s'
},
hide :
{
opacity : 0,
transition : 'opacity .5s'
},
grow :
{
flexGrow : 1
},
title :
{
display : 'none',
marginLeft : 20,
[theme.breakpoints.up('sm')] :
{
display : 'block'
}
},
actionButtons :
{
display : 'flex'
},
actionButton :
{
margin : theme.spacing(1),
padding : 0
}
});
const PulsingBadge = withStyles((theme) =>
({
badge :
{
backgroundColor : theme.palette.secondary.main,
'&::after' :
{
position : 'absolute',
width : '100%',
height : '100%',
borderRadius : '50%',
animation : '$ripple 1.2s infinite ease-in-out',
border : `3px solid ${theme.palette.secondary.main}`,
content : '""'
}
},
'@keyframes ripple' :
{
'0%' :
{
transform : 'scale(.8)',
opacity : 1
},
'100%' :
{
transform : 'scale(2.4)',
opacity : 0
}
}
}))(Badge);
const TopBar = (props) =>
{
const {
roomClient,
room,
lobbyPeers,
myPicture,
loggedIn,
loginEnabled,
fullscreenEnabled,
fullscreen,
onFullscreen,
setSettingsOpen,
setLockDialogOpen,
toggleToolArea,
unread,
classes
} = props;
return (
<AppBar
position='fixed'
className={room.toolbarsVisible ? classes.show : classes.hide}
>
<Toolbar>
<PulsingBadge
color='secondary'
badgeContent={unread}
>
<IconButton
color='inherit'
aria-label='Open drawer'
onClick={() => toggleToolArea()}
className={classes.menuButton}
>
<MenuIcon />
</IconButton>
</PulsingBadge>
{ window.config.logo && <img alt='Logo' className={classes.logo} src={window.config.logo} /> }
<Typography
className={classes.title}
variant='h6'
color='inherit'
noWrap
>
{ window.config.title }
</Typography>
<div className={classes.grow} />
<div className={classes.actionButtons}>
<Tooltip title={`${room.locked ? 'Unlock' : 'Lock'} room`}>
<IconButton
aria-label='Lock room'
className={classes.actionButton}
color='inherit'
onClick={() =>
{
if (room.locked)
{
roomClient.unlockRoom();
}
else
{
roomClient.lockRoom();
}
}}
>
{ room.locked ?
<LockIcon />
:
<LockOpenIcon />
}
</IconButton>
</Tooltip>
{ lobbyPeers.length > 0 &&
<Tooltip title='Show lobby'>
<IconButton
aria-label='Lobby'
color='inherit'
onClick={() => setLockDialogOpen(!room.lockDialogOpen)}
>
<PulsingBadge
color='secondary'
badgeContent={lobbyPeers.length}
>
<SecurityIcon />
</PulsingBadge>
</IconButton>
</Tooltip>
}
{ fullscreenEnabled &&
<Tooltip title={`${fullscreen ? 'Leave' : 'Enter'} fullscreen`}>
<IconButton
aria-label='Fullscreen'
className={classes.actionButton}
color='inherit'
onClick={onFullscreen}
>
{ fullscreen ?
<FullScreenExitIcon />
:
<FullScreenIcon />
}
</IconButton>
</Tooltip>
}
<Tooltip title='Show settings'>
<IconButton
aria-label='Settings'
className={classes.actionButton}
color='inherit'
onClick={() => setSettingsOpen(!room.settingsOpen)}
>
<SettingsIcon />
</IconButton>
</Tooltip>
{ loginEnabled &&
<Tooltip title={`Log ${loggedIn ? 'out' : 'in'}`}>
<IconButton
aria-label='Account'
className={classes.actionButton}
color='inherit'
onClick={() =>
{
loggedIn ? roomClient.logout() : roomClient.login();
}}
>
{ myPicture ?
<Avatar src={myPicture} />
:
<AccountCircle />
}
</IconButton>
</Tooltip>
}
<Button
aria-label='Leave meeting'
className={classes.actionButton}
variant='contained'
color='secondary'
onClick={() => roomClient.close()}
>
Leave
</Button>
</div>
</Toolbar>
</AppBar>
);
};
TopBar.propTypes =
{
roomClient : PropTypes.object.isRequired,
room : appPropTypes.Room.isRequired,
lobbyPeers : PropTypes.array,
myPicture : PropTypes.string,
loggedIn : PropTypes.bool.isRequired,
loginEnabled : PropTypes.bool.isRequired,
fullscreenEnabled : PropTypes.bool,
fullscreen : PropTypes.bool,
onFullscreen : PropTypes.func.isRequired,
setToolbarsVisible : PropTypes.func.isRequired,
setSettingsOpen : PropTypes.func.isRequired,
setLockDialogOpen : PropTypes.func.isRequired,
toggleToolArea : PropTypes.func.isRequired,
unread : PropTypes.number.isRequired,
classes : PropTypes.object.isRequired,
theme : PropTypes.object.isRequired
};
const mapStateToProps = (state) =>
({
room : state.room,
lobbyPeers : lobbyPeersKeySelector(state),
advancedMode : state.settings.advancedMode,
loggedIn : state.me.loggedIn,
loginEnabled : state.me.loginEnabled,
myPicture : state.me.picture,
unread : state.toolarea.unreadMessages +
state.toolarea.unreadFiles
});
const mapDispatchToProps = (dispatch) =>
({
setToolbarsVisible : (visible) =>
{
dispatch(roomActions.setToolbarsVisible(visible));
},
setSettingsOpen : (settingsOpen) =>
{
dispatch(roomActions.setSettingsOpen({ settingsOpen }));
},
setLockDialogOpen : (lockDialogOpen) =>
{
dispatch(roomActions.setLockDialogOpen({ lockDialogOpen }));
},
toggleToolArea : () =>
{
dispatch(toolareaActions.toggleToolArea());
}
});
export default withRoomContext(connect(
mapStateToProps,
mapDispatchToProps,
null,
{
areStatesEqual : (next, prev) =>
{
return (
prev.room === next.room &&
prev.lobbyPeers === next.lobbyPeers &&
prev.me.loggedIn === next.me.loggedIn &&
prev.me.loginEnabled === next.me.loginEnabled &&
prev.me.picture === next.me.picture &&
prev.toolarea.unreadMessages === next.toolarea.unreadMessages &&
prev.toolarea.unreadFiles === next.toolarea.unreadFiles
);
}
}
)(withStyles(styles, { withTheme: true })(TopBar)));

View File

@ -1,11 +1,7 @@
import React from 'react'; import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import {
lobbyPeersKeySelector
} from './Selectors';
import * as appPropTypes from './appPropTypes'; import * as appPropTypes from './appPropTypes';
import { withRoomContext } from '../RoomContext';
import { withStyles } from '@material-ui/core/styles'; import { withStyles } from '@material-ui/core/styles';
import * as roomActions from '../actions/roomActions'; import * as roomActions from '../actions/roomActions';
import * as toolareaActions from '../actions/toolareaActions'; import * as toolareaActions from '../actions/toolareaActions';
@ -13,16 +9,8 @@ import { idle } from '../utils';
import FullScreen from './FullScreen'; import FullScreen from './FullScreen';
import CookieConsent from 'react-cookie-consent'; import CookieConsent from 'react-cookie-consent';
import CssBaseline from '@material-ui/core/CssBaseline'; import CssBaseline from '@material-ui/core/CssBaseline';
import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import SwipeableDrawer from '@material-ui/core/SwipeableDrawer'; import SwipeableDrawer from '@material-ui/core/SwipeableDrawer';
import Hidden from '@material-ui/core/Hidden'; import Hidden from '@material-ui/core/Hidden';
import Typography from '@material-ui/core/Typography';
import IconButton from '@material-ui/core/IconButton';
import MenuIcon from '@material-ui/icons/Menu';
import Avatar from '@material-ui/core/Avatar';
import Badge from '@material-ui/core/Badge';
import AccountCircle from '@material-ui/icons/AccountCircle';
import Notifications from './Notifications/Notifications'; import Notifications from './Notifications/Notifications';
import MeetingDrawer from './MeetingDrawer/MeetingDrawer'; import MeetingDrawer from './MeetingDrawer/MeetingDrawer';
import Democratic from './MeetingViews/Democratic'; import Democratic from './MeetingViews/Democratic';
@ -30,18 +18,11 @@ import Filmstrip from './MeetingViews/Filmstrip';
import AudioPeers from './PeerAudio/AudioPeers'; import AudioPeers from './PeerAudio/AudioPeers';
import FullScreenView from './VideoContainers/FullScreenView'; import FullScreenView from './VideoContainers/FullScreenView';
import VideoWindow from './VideoWindow/VideoWindow'; import VideoWindow from './VideoWindow/VideoWindow';
import FullScreenIcon from '@material-ui/icons/Fullscreen';
import FullScreenExitIcon from '@material-ui/icons/FullscreenExit';
import SettingsIcon from '@material-ui/icons/Settings';
import SecurityIcon from '@material-ui/icons/Security';
import LockDialog from './AccessControl/LockDialog/LockDialog'; import LockDialog from './AccessControl/LockDialog/LockDialog';
import LockIcon from '@material-ui/icons/Lock';
import LockOpenIcon from '@material-ui/icons/LockOpen';
import Button from '@material-ui/core/Button';
import Settings from './Settings/Settings'; import Settings from './Settings/Settings';
import Tooltip from '@material-ui/core/Tooltip'; import TopBar from './Controls/TopBar';
const TIMEOUT = 10 * 1000; const TIMEOUT = 5 * 1000;
const styles = (theme) => const styles = (theme) =>
({ ({
@ -57,44 +38,6 @@ const styles = (theme) =>
backgroundSize : 'cover', backgroundSize : 'cover',
backgroundRepeat : 'no-repeat' backgroundRepeat : 'no-repeat'
}, },
message :
{
position : 'absolute',
display : 'flex',
top : '50%',
left : '50%',
transform : 'translateX(-50%) translateY(-50%)',
width : '30vw',
padding : theme.spacing(2),
flexDirection : 'column',
justifyContent : 'center',
alignItems : 'center'
},
menuButton :
{
margin : 0,
padding : 0
},
logo :
{
display : 'none',
marginLeft : 20,
[theme.breakpoints.up('sm')] :
{
display : 'block'
}
},
show :
{
opacity : 1,
transition : 'opacity .5s'
},
hide :
{
opacity : 0,
transition : 'opacity .5s'
},
toolbar : theme.mixins.toolbar,
drawerPaper : drawerPaper :
{ {
width : '30vw', width : '30vw',
@ -114,79 +57,9 @@ const styles = (theme) =>
{ {
width : '90vw' width : '90vw'
} }
},
grow :
{
flexGrow : 1
},
title :
{
display : 'none',
marginLeft : 20,
[theme.breakpoints.up('sm')] :
{
display : 'block'
}
},
actionButtons :
{
display : 'flex'
},
actionButton :
{
margin : theme.spacing(1),
padding : 0
},
meContainer :
{
position : 'fixed',
zIndex : 110,
overflow : 'hidden',
boxShadow : 'var(--me-shadow)',
transitionProperty : 'border-color',
transitionDuration : '0.15s',
top : '5em',
left : '1em',
border : 'var(--me-border)',
'&.active-speaker' :
{
borderColor : 'var(--active-speaker-border-color)'
}
} }
}); });
const PulsingBadge = withStyles((theme) =>
({
badge :
{
backgroundColor : theme.palette.secondary.main,
// boxShadow : `0 0 0 2px ${theme.palette.secondary.main}`,
'&::after' :
{
position : 'absolute',
width : '100%',
height : '100%',
borderRadius : '50%',
animation : '$ripple 1.2s infinite ease-in-out',
border : `3px solid ${theme.palette.secondary.main}`,
content : '""'
}
},
'@keyframes ripple' :
{
'0%' :
{
transform : 'scale(.8)',
opacity : 1
},
'100%' :
{
transform : 'scale(2.4)',
opacity : 0
}
}
}))(Badge);
class Room extends React.PureComponent class Room extends React.PureComponent
{ {
constructor(props) constructor(props)
@ -262,18 +135,10 @@ class Room extends React.PureComponent
render() render()
{ {
const { const {
roomClient,
room, room,
lobbyPeers,
advancedMode, advancedMode,
myPicture,
loggedIn,
loginEnabled,
setSettingsOpen,
setLockDialogOpen,
toolAreaOpen, toolAreaOpen,
toggleToolArea, toggleToolArea,
unread,
classes, classes,
theme theme
} = this.props; } = this.props;
@ -300,132 +165,12 @@ class Room extends React.PureComponent
<CssBaseline /> <CssBaseline />
<AppBar <TopBar
position='fixed' fullscreenEnabled={this.fullscreen.fullscreenEnabled}
className={room.toolbarsVisible ? classes.show : classes.hide} fullscreen={this.state.fullscreen}
> onFullscreen={this.handleToggleFullscreen}
<Toolbar> />
<PulsingBadge
color='secondary'
badgeContent={unread}
>
<IconButton
color='inherit'
aria-label='Open drawer'
onClick={() => toggleToolArea()}
className={classes.menuButton}
>
<MenuIcon />
</IconButton>
</PulsingBadge>
{ window.config.logo && <img alt='Logo' className={classes.logo} src={window.config.logo} /> }
<Typography
className={classes.title}
variant='h6'
color='inherit'
noWrap
>
{ window.config.title }
</Typography>
<div className={classes.grow} />
<div className={classes.actionButtons}>
<Tooltip title={`${room.locked ? 'Unlock' : 'Lock'} room`}>
<IconButton
aria-label='Lock room'
className={classes.actionButton}
color='inherit'
onClick={() =>
{
if (room.locked)
{
roomClient.unlockRoom();
}
else
{
roomClient.lockRoom();
}
}}
>
{ room.locked ?
<LockIcon />
:
<LockOpenIcon />
}
</IconButton>
</Tooltip>
{ lobbyPeers.length > 0 &&
<Tooltip title='Show lobby'>
<IconButton
aria-label='Lobby'
color='inherit'
onClick={() => setLockDialogOpen(!room.lockDialogOpen)}
>
<PulsingBadge
color='secondary'
badgeContent={lobbyPeers.length}
>
<SecurityIcon />
</PulsingBadge>
</IconButton>
</Tooltip>
}
{ this.fullscreen.fullscreenEnabled &&
<Tooltip title={`${this.state.fullscreen ? 'Leave' : 'Enter'} fullscreen`}>
<IconButton
aria-label='Fullscreen'
className={classes.actionButton}
color='inherit'
onClick={this.handleToggleFullscreen}
>
{ this.state.fullscreen ?
<FullScreenExitIcon />
:
<FullScreenIcon />
}
</IconButton>
</Tooltip>
}
<Tooltip title='Show settings'>
<IconButton
aria-label='Settings'
className={classes.actionButton}
color='inherit'
onClick={() => setSettingsOpen(!room.settingsOpen)}
>
<SettingsIcon />
</IconButton>
</Tooltip>
{ loginEnabled &&
<Tooltip title={`Log ${loggedIn ? 'out' : 'in'}`}>
<IconButton
aria-label='Account'
className={classes.actionButton}
color='inherit'
onClick={() =>
{
loggedIn ? roomClient.logout() : roomClient.login();
}}
>
{ myPicture ?
<Avatar src={myPicture} />
:
<AccountCircle />
}
</IconButton>
</Tooltip>
}
<Button
aria-label='Leave meeting'
className={classes.actionButton}
variant='contained'
color='secondary'
onClick={() => roomClient.close()}
>
Leave
</Button>
</div>
</Toolbar>
</AppBar>
<nav> <nav>
<Hidden implementation='css'> <Hidden implementation='css'>
<SwipeableDrawer <SwipeableDrawer
@ -455,19 +200,11 @@ class Room extends React.PureComponent
Room.propTypes = Room.propTypes =
{ {
roomClient : PropTypes.object.isRequired,
room : appPropTypes.Room.isRequired, room : appPropTypes.Room.isRequired,
lobbyPeers : PropTypes.array,
advancedMode : PropTypes.bool.isRequired, advancedMode : PropTypes.bool.isRequired,
myPicture : PropTypes.string,
loggedIn : PropTypes.bool.isRequired,
loginEnabled : PropTypes.bool.isRequired,
toolAreaOpen : PropTypes.bool.isRequired, toolAreaOpen : PropTypes.bool.isRequired,
setToolbarsVisible : PropTypes.func.isRequired, setToolbarsVisible : PropTypes.func.isRequired,
setSettingsOpen : PropTypes.func.isRequired,
setLockDialogOpen : PropTypes.func.isRequired,
toggleToolArea : PropTypes.func.isRequired, toggleToolArea : PropTypes.func.isRequired,
unread : PropTypes.number.isRequired,
classes : PropTypes.object.isRequired, classes : PropTypes.object.isRequired,
theme : PropTypes.object.isRequired theme : PropTypes.object.isRequired
}; };
@ -475,14 +212,8 @@ Room.propTypes =
const mapStateToProps = (state) => const mapStateToProps = (state) =>
({ ({
room : state.room, room : state.room,
lobbyPeers : lobbyPeersKeySelector(state),
advancedMode : state.settings.advancedMode, advancedMode : state.settings.advancedMode,
loggedIn : state.me.loggedIn, toolAreaOpen : state.toolarea.toolAreaOpen
loginEnabled : state.me.loginEnabled,
myPicture : state.me.picture,
toolAreaOpen : state.toolarea.toolAreaOpen,
unread : state.toolarea.unreadMessages +
state.toolarea.unreadFiles
}); });
const mapDispatchToProps = (dispatch) => const mapDispatchToProps = (dispatch) =>
@ -491,21 +222,13 @@ const mapDispatchToProps = (dispatch) =>
{ {
dispatch(roomActions.setToolbarsVisible(visible)); dispatch(roomActions.setToolbarsVisible(visible));
}, },
setSettingsOpen : (settingsOpen) =>
{
dispatch(roomActions.setSettingsOpen({ settingsOpen }));
},
setLockDialogOpen : (lockDialogOpen) =>
{
dispatch(roomActions.setLockDialogOpen({ lockDialogOpen }));
},
toggleToolArea : () => toggleToolArea : () =>
{ {
dispatch(toolareaActions.toggleToolArea()); dispatch(toolareaActions.toggleToolArea());
} }
}); });
export default withRoomContext(connect( export default connect(
mapStateToProps, mapStateToProps,
mapDispatchToProps, mapDispatchToProps,
null, null,
@ -514,15 +237,8 @@ export default withRoomContext(connect(
{ {
return ( return (
prev.room === next.room && prev.room === next.room &&
prev.lobbyPeers === next.lobbyPeers && prev.toolarea.toolAreaOpen === next.toolarea.toolAreaOpen
prev.me.loggedIn === next.me.loggedIn &&
prev.me.loginEnabled === next.me.loginEnabled &&
prev.me.picture === next.me.picture &&
prev.toolarea.toolAreaOpen === next.toolarea.toolAreaOpen &&
prev.toolarea.unreadMessages === next.toolarea.unreadMessages &&
prev.toolarea.unreadFiles === next.toolarea.unreadFiles &&
prev.settings.advancedMode === next.settings.advancedMode
); );
} }
} }
)(withStyles(styles, { withTheme: true })(Room))); )(withStyles(styles, { withTheme: true })(Room));