Use <Side /> instead of position absolute

Some components still use position fixed. This could change in the
future.
This commit is contained in:
Jerko Steiner 2019-11-17 17:24:23 -03:00
parent 58039eb086
commit fddb88f5b8
13 changed files with 167 additions and 81 deletions

View File

@ -1,16 +1,18 @@
import classnames from 'classnames'
import map from 'lodash/map'
import React from 'react'
import Peer from 'simple-peer'
import { Message } from '../actions/ChatActions'
import { Notification, dismissNotification } from '../actions/NotifyActions'
import { dismissNotification, Notification } from '../actions/NotifyActions'
import { TextMessage } from '../actions/PeerActions'
import { AddStreamPayload } from '../actions/StreamActions'
import * as constants from '../constants'
import Chat from './Chat'
import { Media } from './Media'
import Notifications from './Notifications'
import { Side } from './Side'
import Toolbar from './Toolbar'
import Video from './Video'
import { Media } from './Media'
export interface AppProps {
active: string | null
@ -75,47 +77,61 @@ export default class App extends React.PureComponent<AppProps, AppState> {
return (
<div className="app">
<Toolbar
chatVisible={this.state.chatVisible}
messagesCount={messagesCount}
onToggleChat={this.handleToggleChat}
onSendFile={onSendFile}
stream={streams[constants.ME]}
/>
<Side align='end' left zIndex={1}>
<Toolbar
chatVisible={this.state.chatVisible}
messagesCount={messagesCount}
onToggleChat={this.handleToggleChat}
onSendFile={onSendFile}
stream={streams[constants.ME]}
/>
</Side>
<Side top zIndex={2}>
<Notifications
dismiss={dismissNotification}
notifications={notifications}
/>
<Media />
<Media />
</Side>
<Chat
messages={messages}
onClose={this.handleHideChat}
sendMessage={sendMessage}
visible={this.state.chatVisible}
/>
<div className="videos">
<Video
videos={videos}
active={active === constants.ME}
onClick={toggleActive}
play={play}
stream={streams[constants.ME]}
userId={constants.ME}
muted
mirrored
/>
{map(peers, (_, userId) => (
<div
className={classnames('videos', {
'chat-visible': this.state.chatVisible,
})}
>
{streams[constants.ME] && (
<Video
active={userId === active}
key={userId}
videos={videos}
active={active === constants.ME}
onClick={toggleActive}
play={play}
stream={streams[userId]}
userId={userId}
videos={videos}
stream={streams[constants.ME]}
userId={constants.ME}
muted
mirrored
/>
))}
)}
{
map(peers, (_, userId) => userId)
.filter(stream => !!stream)
.map(userId =>
<Video
active={userId === active}
key={userId}
onClick={toggleActive}
play={play}
stream={streams[userId]}
userId={userId}
videos={videos}
/>,
)
}
</div>
</div>
)

View File

@ -101,7 +101,7 @@ export const AutoplayMessage = React.memo(
function Autoplay(props: AutoplayProps) {
return (
<React.Fragment>
The browser has blocked video autoplay on this page.
Your browser has blocked video autoplay on this page.
To continue with your call, please press the play button:
&nbsp;
<button className='button' onClick={props.play}>

View File

@ -42,7 +42,7 @@ const Notification = React.memo(
export default class Notifications
extends React.PureComponent<NotificationsProps> {
static defaultProps = {
max: 10,
max: 5,
}
render () {
const { dismiss, notifications, max } = this.props

View File

@ -0,0 +1,27 @@
import React from 'react'
import classnames from 'classnames'
export type Left = { left: true }
export type Right = { right: true }
export type Top = { top: true }
export type Bottom = { bottom: true }
export type SideProps = (Left | Right | Top | Bottom) & {
zIndex: number
children: React.ReactNode
align?: 'baseline' | 'center' | 'end'
}
export const Side = React.memo(
function Side(props: SideProps) {
const className = classnames('side', { ...props })
return (
<div
className={className}
style={{alignItems: props.align || 'center', zIndex: props.zIndex}}
>
{props.children}
</div>
)
},
)

View File

@ -87,7 +87,7 @@ extends React.PureComponent<ToolbarProps, ToolbarState> {
return (
<div className="toolbar active">
<div onClick={this.handleToggleChat}
<a onClick={this.handleToggleChat}
className={classnames('button chat', {
on: this.props.chatVisible,
})}
@ -96,8 +96,8 @@ extends React.PureComponent<ToolbarProps, ToolbarState> {
title="Chat"
>
<span className="icon icon-question_answer" />
</div>
<div
</a>
<a
className="button send-file"
onClick={this.handleSendFile}
title="Send file"
@ -110,11 +110,11 @@ extends React.PureComponent<ToolbarProps, ToolbarState> {
onChange={this.handleSelectFiles}
/>
<span className="icon icon-file-text2" />
</div>
</a>
{stream && (
<div>
<div
<React.Fragment>
<a
onClick={this.handleMicClick}
className={classnames('button mute-audio', {
on: this.state.micMuted,
@ -123,8 +123,8 @@ extends React.PureComponent<ToolbarProps, ToolbarState> {
>
<span className="on icon icon-mic_off" />
<span className="off icon icon-mic" />
</div>
<div onClick={this.handleCamClick}
</a>
<a onClick={this.handleCamClick}
className={classnames('button mute-video', {
on: this.state.camDisabled,
})}
@ -132,11 +132,11 @@ extends React.PureComponent<ToolbarProps, ToolbarState> {
>
<span className="on icon icon-videocam_off" />
<span className="off icon icon-videocam" />
</div>
</div>
</a>
</React.Fragment>
)}
<div onClick={this.handleFullscreenClick}
<a onClick={this.handleFullscreenClick}
className={classnames('button fullscreen', {
on: this.state.fullScreenEnabled,
})}
@ -144,14 +144,14 @@ extends React.PureComponent<ToolbarProps, ToolbarState> {
>
<span className="on icon icon-fullscreen_exit" />
<span className="off icon icon-fullscreen" />
</div>
</a>
<div onClick={this.handleHangoutClick}
<a onClick={this.handleHangoutClick}
className="button hangup"
title="Hangup"
>
<span className="icon icon-call_end" />
</div>
</a>
</div>
)
}

View File

@ -64,11 +64,26 @@ describe('App', () => {
})
})
describe('chat toggle', () => {
it('toggles chat state', async () => {
await render()
const chatButton = node.querySelector('.toolbar .button.chat')!
expect(chatButton).toBeTruthy()
TestUtils.Simulate.click(chatButton)
TestUtils.Simulate.click(chatButton)
})
})
describe('state', () => {
beforeEach(async () => {
state.streams = {
test: {
userId: 'test',
[constants.ME]: {
userId: constants.ME,
stream: new MediaStream(),
url: 'blob://',
},
'other-user': {
userId: 'other-user',
stream: new MediaStream(),
url: 'blob://',
},

View File

@ -1,15 +1,13 @@
.alert {
background-color: black;
background-color: rgba(0, 0, 0, 0.3);
left: 0;
opacity: 1;
position: fixed;
right: 0;
text-align: center;
top: 0;
transition: visibility 100ms ease-in, opacity 100ms ease-in;
z-index: 4;
pointer-events: none;
color: $color-warning;
text-shadow: 0px 0px 3px black;
padding: 1rem;
font-size: 1.2rem;
span {
display: inline-block;
@ -18,12 +16,9 @@
}
button {
line-height: 1.4rem;
border: none;
border-radius: 0.3rem;
color: $color-info;
background-color: $color-fg;
vertical-align: middle;
@include button($color-primary, $color-warning);
font-size: 1.2rem;
padding: 1rem;
}
}

View File

@ -10,7 +10,7 @@
transition: transform 0.5s cubic-bezier(0.55, 0, 0, 1), box-shadow 0.5s cubic-bezier(0.55, 0, 0, 1);
width: 320px;
margin: 0 auto;
z-index: 3;
z-index: 4;
&.show {
-ms-transform: none;

View File

@ -1,11 +1,7 @@
.media-container form.media {
margin: 1rem auto 0;
max-width: 440px;
text-align: center;
position: absolute;
z-index: 100;
left: 50%;
top: 100px;
transform: translateX(-50%);
& > * {
margin: 0.25rem;

View File

@ -2,17 +2,15 @@
pointer-events: none;
font-family: $font-monospace;
font-size: 10px;
left: 1rem;
position: fixed;
right: 1rem;
text-align: right;
top: 1rem;
z-index: 3;
width: 100%;
padding: 1rem;
height: 100px;
.notification {
color: $color-info;
text-shadow: 0px 0px 3px black;
padding: 0.25rem;
background-color: rgba(0, 0, 0, 0.2);
}
.notification.error {

View File

@ -1,9 +1,5 @@
.toolbar {
bottom: 20px;
left: 6vw;
position: absolute;
z-index: 3;
margin: 0 0 1rem 1rem;
/* on icons are hidden by default */
.icon {
@ -38,7 +34,6 @@
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;
@ -48,6 +43,10 @@
}
}
.button + .button {
margin-top: 1rem;
}
/* off icons are hidden when parent svg has class 'on' */
&.active .button {

View File

@ -1,8 +1,8 @@
.videos {
position: fixed;
height: 100px;
bottom: 15px;
right: 0px;
bottom: 1rem;
right: 1rem;
text-align: right;
$video-size: 100px;
@ -12,10 +12,9 @@
box-shadow: 0px 0px 5px black;
border-radius: 10px;
display: inline-block;
margin-right: 10px;
width: $video-size;
height: 100%;
z-index: 2;
z-index: 3;
video {
border-radius: 10px;
@ -26,6 +25,10 @@
}
}
.video-container + .video-container {
margin-left: 1rem;
}
.video-container.active {
background-color: transparent;
box-shadow: none;

View File

@ -161,3 +161,40 @@ body.call {
opacity: 0.01;
transition: opacity 100ms ease-in;
}
.side {
position: absolute;
display: flex;
&.left {
flex-direction: row;
left: 0;
top: 0;
bottom: 0;
}
&.right {
flex-direction: row;
right: 0;
top: 0;
bottom: 0;
}
&.top {
flex-direction: column;
top: 0;
left: 0;
right: 0;
}
&.bottom {
flex-direction: column;
bottom: 0;
left: 0;
right: 0;
}
}
.chat-visible {
transform: translateX(-330px);
}