Created a simple toolbar

- Audio/Video toggling
- Fullscreen
- Hangout (return to homepage)
This commit is contained in:
Michael H. Arieli 2018-11-22 11:35:44 -08:00
parent 21e3040dc3
commit ce7eed3541
6 changed files with 224 additions and 1 deletions

11
.editorconfig Normal file
View File

@ -0,0 +1,11 @@
; top-most EditorConfig file
root = true
; Unix-style newlines
[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true

2
.gitignore vendored
View File

@ -1,3 +1,5 @@
.DS_Store
yarn.lock
*.swp
*.swo
dist/

View File

@ -1,5 +1,6 @@
import Alerts, { AlertPropType } from './Alerts.js'
import * as constants from '../constants.js'
import Toolbar from './Toolbar.js'
import Input from './Input.js'
import Notifications, { NotificationPropTypes } from './Notifications.js'
import PropTypes from 'prop-types'
@ -38,6 +39,7 @@ export default class App extends React.PureComponent {
} = this.props
return (<div className="app">
<Toolbar stream={streams[constants.ME]} />
<Alerts alerts={alerts} dismiss={dismissAlert} />
<Notifications notifications={notifications} />
<Input notify={notify} sendMessage={sendMessage} />

View File

@ -0,0 +1,100 @@
import PropTypes from 'prop-types'
import React from 'react'
import { StreamPropType } from './video.js'
export default class Toolbar extends React.PureComponent {
static propTypes = {
stream: StreamPropType
}
handleMicClick = e => {
const { stream } = this.props
stream.mediaStream.getAudioTracks().forEach(track => {
track.enabled = !track.enabled
});
e.currentTarget.classList.toggle('on')
}
handleCamClick = e => {
const { stream } = this.props
stream.mediaStream.getVideoTracks().forEach(track => {
track.enabled = !track.enabled
});
e.currentTarget.classList.toggle('on')
}
handleFullscreenClick = e => {
const document = window.document;
const fs = document.getElementById('container');
if (
!document.fullscreenElement &&
!document.mozFullScreenElement &&
!document.webkitFullscreenElement &&
!document.msFullscreenElement
) {
if (fs.requestFullscreen) {
fs.requestFullscreen();
} else if (fs.msRequestFullscreen) {
fs.msRequestFullscreen();
} else if (fs.mozRequestFullScreen) {
fs.mozRequestFullScreen();
} else if (fs.webkitRequestFullscreen) {
fs.webkitRequestFullscreen();
}
} else {
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.msExitFullscreen) {
document.msExitFullscreen();
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen();
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen();
}
}
e.target.classList.toggle('on')
}
handleHangoutClick = e => {
location.href = '/'
}
render () {
const { stream } = this.props
return (
<div className="toolbar active">
{stream && (
<svg onClick={this.handleMicClick} className="mute-audio" xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 1024 1024">
<circle cx="24" cy="24" r="34">
<title>Mute audio</title>
</circle>
<path className="on" transform="scale(0.4), translate(800,800)" d="M182 149.333l714 714-54 54-178-178c-32 20-72 32-110 38v140h-84v-140c-140-20-256-140-256-286h72c0 128 108 216 226 216 34 0 68-8 98-22l-70-70c-8 2-18 4-28 4-70 0-128-58-128-128v-32l-256-256zm458 348l-256-254v-8c0-70 58-128 128-128s128 58 128 128v262zm170-6c0 50-14 98-38 140l-52-54c12-26 18-54 18-86h72z" fill="white"/>
<path className="off" transform="scale(0.4), translate(800,800)" d="M738 491.333h72c0 146-116 266-256 286v140h-84v-140c-140-20-256-140-256-286h72c0 128 108 216 226 216s226-88 226-216zm-226 128c-70 0-128-58-128-128v-256c0-70 58-128 128-128s128 58 128 128v256c0 70-58 128-128 128z" fill="white"/>
</svg>
)}
{stream && (
<svg onClick={this.handleCamClick} className="mute-video" xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 1024 1024">
<circle cx="24" cy="24" r="34">
<title>Mute video</title>
</circle>
<path className="on" transform="scale(0.4), translate(800,800)" d="M140 107.333l756 756-54 54-136-136c-6 4-16 8-24 8H170c-24 0-42-18-42-42v-428c0-24 18-42 42-42h32l-116-116zm756 192v456l-478-478h264c24 0 44 18 44 42v150z" fill="white"/>
<path className="off" transform="scale(0.4), translate(800,800)" d="M726 469.333l170-170v468l-170-170v150c0 24-20 42-44 42H170c-24 0-42-18-42-42v-428c0-24 18-42 42-42h512c24 0 44 18 44 42v150z" fill="white"/>
</svg>
)}
<svg onClick={this.handleFullscreenClick} className="fullscreen" xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 1024 1024">
<circle cx="24" cy="24" r="34">
<title>Enter fullscreen</title>
</circle>
<path className="on" transform="scale(0.4), translate(800,800)" d="M682 363.333h128v84H598v-212h84v128zm-84 468v-212h212v84H682v128h-84zm-256-468v-128h84v212H214v-84h128zm-128 340v-84h212v212h-84v-128H214z" fill="white"/>
<path className="off" transform="scale(0.4), translate(800,800)" d="M598 235.333h212v212h-84v-128H598v-84zm128 512v-128h84v212H598v-84h128zm-512-300v-212h212v84H298v128h-84zm84 172v128h128v84H214v-212h84z" fill="white"/>
</svg>
<svg onClick={this.handleHangoutClick} className="hangup" xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 1024 1024">
<circle cx="24" cy="24" r="34">
<title>Hangup</title>
</circle>
<path transform="scale(0.4), translate(800,800)" d="M512 405.333c-68 0-134 10-196 30v132c0 16-10 34-24 40-42 20-80 46-114 78-8 8-18 12-30 12s-22-4-30-12l-106-106c-8-8-12-18-12-30s4-22 12-30c130-124 306-200 500-200s370 76 500 200c8 8 12 18 12 30s-4 22-12 30l-106 106c-8 8-18 12-30 12s-22-4-30-12c-34-32-72-58-114-78-14-6-24-20-24-38v-132c-62-20-128-32-196-32z" fill="white"/>
</svg>
</div>
)
}
}

View File

@ -50,7 +50,7 @@ export default class Video extends React.PureComponent {
<div className={className}>
<video
autoPlay
muted={userId === ME}
//muted={userId === ME}
onClick={this.handleClick}
onLoadedMetadata={this.play}
playsInline

View File

@ -278,6 +278,114 @@ body.call {
}
}
.toolbar {
bottom: 80px;
left: 6vw;
position: absolute;
z-index: 1;
circle {
fill: #666;
fill-opacity: 0.6;
}
svg.on circle {
fill-opacity: 0;
}
/* on icons are hidden by default */
path {
&.on {
display: none;
}
&.off {
display: block;
}
}
/* off icons are displayed by default */
/* on icons are displayed when parent svg has class 'on' */
svg {
&.on path {
&.on {
display: block;
}
&.off {
display: none;
}
}
//background-color: #333;
border-radius: 48px;
box-shadow: 2px 2px 24px #444;
display: block;
margin: 0 0 3vh 0;
transform: translateX(calc(-6vw - 96px));
transition: all .1s;
transition-timing-function: ease-in-out;
&:hover {
box-shadow: 4px 4px 48px #666;
}
}
/* off icons are hidden when parent svg has class 'on' */
&.active svg {
transform: translateX(0);
}
.chat {
transition: 200ms;
&:hover {
background: #407cf7;
circle {
fill: #407cf7;
}
}
}
.mute-audio {
transition: 40ms;
&:hover, &.on {
background: #407cf7;
}
&:hover circle {
fill: #407cf7;
}
}
.mute-video {
transition: 120ms;
&:hover, &.on {
background: #407cf7;
}
&:hover circle {
fill: #407cf7;
}
}
.fullscreen {
transition: 280ms;
&:hover, &.on {
background: #407cf7;
}
&:hover circle {
fill: #407cf7;
}
}
.hangup {
transition: 360ms;
&:hover {
background: #dd2c00;
circle {
fill: #dd2c00;
}
}
}
}
}
.fade-enter {