Use <Side /> instead of position absolute
Some components still use position fixed. This could change in the future.
This commit is contained in:
parent
58039eb086
commit
fddb88f5b8
@ -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>
|
||||
)
|
||||
|
||||
@ -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:
|
||||
|
||||
<button className='button' onClick={props.play}>
|
||||
|
||||
@ -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
|
||||
|
||||
27
src/client/components/Side.tsx
Normal file
27
src/client/components/Side.tsx
Normal 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>
|
||||
)
|
||||
},
|
||||
)
|
||||
@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@ -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://',
|
||||
},
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user