Refactor Toolbar, move video & notifications

This commit is contained in:
Jerko Steiner 2019-11-17 23:51:54 -03:00
parent 9d68e4c1f4
commit b88889665f
10 changed files with 134 additions and 110 deletions

View File

@ -107,10 +107,7 @@ export function setMediaVisible(visible: boolean): MediaVisibleAction {
export const play = makeAction('MEDIA_PLAY', async () => {
const promises = Array
.from(document.querySelectorAll('video'))
.filter(video => {
console.log('video', video.paused, video)
return video.paused
})
.filter(video => video.paused)
.map(video => video.play())
await Promise.all(promises)
})

View File

@ -75,9 +75,13 @@ export default class App extends React.PureComponent<AppProps, AppState> {
const { videos } = this.state
const chatVisibleClassName = classnames({
'chat-visible': this.state.chatVisible,
})
return (
<div className="app">
<Side align='end' left zIndex={1}>
<Side align='end' left zIndex={2}>
<Toolbar
chatVisible={this.state.chatVisible}
messagesCount={messagesCount}
@ -86,7 +90,7 @@ export default class App extends React.PureComponent<AppProps, AppState> {
stream={streams[constants.ME]}
/>
</Side>
<Side top zIndex={2}>
<Side className={chatVisibleClassName} top zIndex={1}>
<Notifications
dismiss={dismissNotification}
notifications={notifications}
@ -99,11 +103,7 @@ export default class App extends React.PureComponent<AppProps, AppState> {
sendMessage={sendMessage}
visible={this.state.chatVisible}
/>
<div
className={classnames('videos', {
'chat-visible': this.state.chatVisible,
})}
>
<div className={classnames('videos', chatVisibleClassName)}>
{streams[constants.ME] && (
<Video
videos={videos}

View File

@ -123,7 +123,6 @@ export const Media = c(React.memo(function Media(props: MediaProps) {
)}
</Alerts>
{props.autoplayError && <AutoplayMessage play={props.play} />}
<MediaForm {...props} />
</div>
)

View File

@ -7,6 +7,7 @@ export type Top = { top: true }
export type Bottom = { bottom: true }
export type SideProps = (Left | Right | Top | Bottom) & {
className?: string
zIndex: number
children: React.ReactNode
align?: 'baseline' | 'center' | 'end'
@ -14,11 +15,11 @@ export type SideProps = (Left | Right | Top | Bottom) & {
export const Side = React.memo(
function Side(props: SideProps) {
const className = classnames('side', { ...props })
const { className, zIndex, ...otherProps } = props
return (
<div
className={className}
style={{alignItems: props.align || 'center', zIndex: props.zIndex}}
className={classnames('side', className, { ...otherProps })}
style={{alignItems: props.align || 'center', zIndex }}
>
{props.children}
</div>

View File

@ -22,6 +22,43 @@ export interface ToolbarState {
fullScreenEnabled: boolean
}
export interface ToolbarButtonProps {
className?: string
badge?: string | number
blink?: boolean
onClick: () => void
icon: string
offIcon?: string
on?: boolean
title: string
}
function ToolbarButton(props: ToolbarButtonProps) {
const { blink, on } = props
const icon = !on && props.offIcon ? props.offIcon : props.icon
function onClick(event: React.MouseEvent<HTMLElement>) {
props.onClick()
document.activeElement &&
document.activeElement instanceof HTMLElement &&
document.activeElement.blur()
}
return (
<a
className={classnames('button', props.className, { blink, on })}
onClick={onClick}
href='#'
>
<span className={classnames('icon', icon)}>
{!!props.badge && <span className='badge'>{props.badge}</span>}
</span>
<span className="tooltip">{props.title}</span>
</a>
)
}
export default class Toolbar
extends React.PureComponent<ToolbarProps, ToolbarState> {
file = React.createRef<HTMLInputElement>()
@ -61,7 +98,7 @@ extends React.PureComponent<ToolbarProps, ToolbarState> {
screenfull.toggle()
this.setState({
...this.state,
fullScreenEnabled: !this.state.fullScreenEnabled,
fullScreenEnabled: !screenfull.isFullscreen,
})
}
}
@ -84,88 +121,73 @@ extends React.PureComponent<ToolbarProps, ToolbarState> {
}
render () {
const { messagesCount, stream } = this.props
const unreadCount = messagesCount - this.state.readMessages
const hasUnread = unreadCount > 0
return (
<div className="toolbar active">
<a onClick={this.handleToggleChat}
className={classnames('button chat', {
on: this.props.chatVisible,
})}
href='#'
data-blink={!this.props.chatVisible &&
messagesCount > this.state.readMessages}
title="Chat"
>
<span className="icon icon-question_answer" />
<span className="tooltip">Toggle Chat</span>
</a>
<a
className="button send-file"
<input
style={hidden}
type="file"
multiple
ref={this.file}
onChange={this.handleSelectFiles}
/>
<ToolbarButton
badge={unreadCount}
className='chat'
icon='icon-question_answer'
blink={!this.props.chatVisible && hasUnread}
onClick={this.handleToggleChat}
on={this.props.chatVisible}
title='Toggle Chat'
/>
<ToolbarButton
className='send-file'
icon='icon-file-text2'
onClick={this.handleSendFile}
title="Send file"
href='#'
>
<input
style={hidden}
type="file"
multiple
ref={this.file}
onChange={this.handleSelectFiles}
/>
<span className="icon icon-file-text2" />
<span className="tooltip">Send File</span>
</a>
title='Send File'
/>
{stream && (
<React.Fragment>
<a
<ToolbarButton
onClick={this.handleMicClick}
className={classnames('button mute-audio', {
on: this.state.micMuted,
})}
href='#'
title="Mute audio"
>
<span className="on icon icon-mic_off" />
<span className="off icon icon-mic" />
<span className="tooltip">Toggle Microphone</span>
</a>
<a onClick={this.handleCamClick}
className={classnames('button mute-video', {
on: this.state.camDisabled,
})}
href='#'
title="Mute video"
>
<span className="on icon icon-videocam_off" />
<span className="off icon icon-videocam" />
<span className="tooltip">Toggle Camera</span>
</a>
className='mute-audio'
on={this.state.micMuted}
icon='icon-mic_off'
offIcon='icon-mic'
title='Toggle Microphone'
/>
<ToolbarButton
onClick={this.handleCamClick}
className='mute-video'
on={this.state.camDisabled}
icon='icon-videocam_off'
offIcon='icon-videocam'
title='Toggle Camera'
/>
</React.Fragment>
)}
<a
<ToolbarButton
onClick={this.handleFullscreenClick}
href='#'
className={classnames('button fullscreen', {
on: this.state.fullScreenEnabled,
})}
title="Enter fullscreen"
>
<span className="on icon icon-fullscreen_exit" />
<span className="off icon icon-fullscreen" />
<span className="tooltip">Fullscreen</span>
</a>
className='fullscreen'
icon='icon-fullscreen_exit'
offIcon='icon-fullscreen'
on={this.state.fullScreenEnabled}
title='Toggle Fullscreen'
/>
<a
<ToolbarButton
onClick={this.handleHangoutClick}
className="button hangup"
href='#'
className='hangup'
icon='icon-call_end'
title="Hang Up"
>
<span className="icon icon-call_end" />
<span className="tooltip">Hang Up</span>
</a>
/>
</div>
)
}

View File

@ -52,10 +52,7 @@ export default class Video extends React.PureComponent<VideoProps> {
id={`video-${socket.id}`}
autoPlay
onClick={this.handleClick}
onLoadedMetadata={() => {
console.log('onLoadedMetadata')
this.props.play()
}}
onLoadedMetadata={() => this.props.play()}
playsInline
ref={this.videoRef}
muted={muted}

View File

@ -28,6 +28,15 @@
border-radius: 4px;
border-bottom: 2px solid darken(#fff, 10%);
text-align: center;
color: black;
background-color: white;
cursor: pointer;
&:hover {
background-color: darken(white, 5%);
}
&:active {
background-color: darken(white, 10%);
}
}
select, button {

View File

@ -9,6 +9,7 @@
width: 48px;
height: 48px;
border-radius: 48px;
background-color: rgba(0, 0, 0, 0.2);
box-shadow: 2px 2px 24px #444;
transition: all .1s;
transition-timing-function: ease-in-out;
@ -21,8 +22,13 @@
top: 12px;
}
&.on {
display: none;
.badge {
font-family: sans-serif;
font-weight: bold;
color: white;
position: relative;
font-size: 12px;
top: 23px;
}
}
@ -35,17 +41,18 @@
text-shadow: 0 0 5px black;
transition: opacity 200ms ease-in 25ms, transform 100ms ease-in;
transform: translateX(-100%);
z-index: 0;
pointer-events: none;
}
.button {
outline: none;
text-decoration: none;
display: flex;
flex-direction: row;
align-items: center;
cursor: pointer;
&:hover {
&:hover, &:focus {
.icon {
box-shadow: 4px 4px 48px #666;
cursor: pointer;
@ -62,12 +69,6 @@
}
&.on .icon {
background: lighten(#407cf7, 10%);
&.on {
display: inherit;
}
&.off {
display: none;
}
}
}
@ -75,12 +76,10 @@
margin-top: 1rem;
}
.chat {
&[data-blink="true"] .icon {
-webkit-animation: bg-blink 1s infinite;
-moz-animation: bg-blink 1s infinite;
animation: bg-blink 1s infinite;
}
.chat.blink .icon {
-webkit-animation: bg-blink 1s infinite;
-moz-animation: bg-blink 1s infinite;
animation: bg-blink 1s infinite;
}
}

View File

@ -2,8 +2,9 @@
position: fixed;
height: 100px;
bottom: 1rem;
right: 1rem;
right: 0;
text-align: right;
transition: right cubic-bezier(0.55, 0, 0, 1) 500ms;
$video-size: 100px;
@ -15,6 +16,7 @@
width: $video-size;
height: 100%;
z-index: 3;
margin-right: 1rem;
video {
border-radius: 10px;
@ -25,10 +27,6 @@
}
}
.video-container + .video-container {
margin-left: 1rem;
}
.video-container.active {
background-color: transparent;
box-shadow: none;
@ -40,6 +38,7 @@
top: 0;
bottom: 0;
z-index: -1;
transform: none;
video {
border-radius: 0;

View File

@ -165,6 +165,7 @@ body.call {
.side {
position: absolute;
display: flex;
transition: right cubic-bezier(0.55, 0, 0, 1) 500ms;
&.left {
flex-direction: row;
@ -195,6 +196,6 @@ body.call {
}
}
.chat-visible {
transform: translateX(-330px);
.app .chat-visible {
right: 320px;
}