Fix broken chat button, remove refs

This commit is contained in:
Jerko Steiner 2019-07-07 18:37:08 +08:00
parent 329108185d
commit ec31ea2a6b
4 changed files with 63 additions and 58 deletions

View File

@ -1,10 +1,10 @@
import Alerts, { AlertPropType } from './Alerts.js'
import * as constants from '../constants.js' import * as constants from '../constants.js'
import Toolbar from './Toolbar.js' import Alerts, { AlertPropType } from './Alerts.js'
import Notifications, { NotificationPropTypes } from './Notifications.js'
import Chat, { MessagePropTypes } from './Chat.js' import Chat, { MessagePropTypes } from './Chat.js'
import Notifications, { NotificationPropTypes } from './Notifications.js'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import React from 'react' import React from 'react'
import Toolbar from './Toolbar.js'
import Video, { StreamPropType } from './Video.js' import Video, { StreamPropType } from './Video.js'
import _ from 'underscore' import _ from 'underscore'
@ -25,10 +25,24 @@ export default class App extends React.PureComponent {
constructor () { constructor () {
super() super()
this.state = { this.state = {
videos: {} videos: {},
chatVisible: false
} }
this.chatRef = React.createRef() }
this.toolbarRef = React.createRef() handleShowChat = () => {
this.setState({
chatVisible: true
})
}
handleHideChat = () => {
this.setState({
chatVisible: false
})
}
handleToggleChat = () => {
return this.state.chatVisible
? this.handleHideChat()
: this.handleShowChat()
} }
componentDidMount () { componentDidMount () {
const { init } = this.props const { init } = this.props
@ -53,22 +67,21 @@ export default class App extends React.PureComponent {
return ( return (
<div className="app"> <div className="app">
<Toolbar <Toolbar
chatRef={this.chatRef} chatVisible={this.state.chatVisible}
messages={messages} messages={messages}
onToggleChat={this.handleToggleChat}
stream={streams[constants.ME]} stream={streams[constants.ME]}
ref={this.toolbarRef}
/> />
<Alerts alerts={alerts} dismiss={dismissAlert} /> <Alerts alerts={alerts} dismiss={dismissAlert} />
<Notifications notifications={notifications} /> <Notifications notifications={notifications} />
<div className="chat-container" ref={this.chatRef}> <Chat
<Chat messages={messages}
messages={messages} notify={notify}
videos={videos} onClose={this.handleHideChat}
notify={notify} sendMessage={sendMessage}
sendMessage={sendMessage} videos={videos}
toolbarRef={this.toolbarRef} visible={this.state.chatVisible}
/> />
</div>
<div className="videos"> <div className="videos">
<Video <Video
videos={videos} videos={videos}

View File

@ -1,7 +1,8 @@
import Input from './Input.js'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import React from 'react' import React from 'react'
import classnames from 'classnames'
import socket from '../socket.js' import socket from '../socket.js'
import Input from './Input.js'
export const MessagePropTypes = PropTypes.shape({ export const MessagePropTypes = PropTypes.shape({
userId: PropTypes.string.isRequired, userId: PropTypes.string.isRequired,
@ -12,18 +13,20 @@ export const MessagePropTypes = PropTypes.shape({
export default class Chat extends React.PureComponent { export default class Chat extends React.PureComponent {
static propTypes = { static propTypes = {
visible: PropTypes.bool.isRequired,
messages: PropTypes.arrayOf(MessagePropTypes).isRequired, messages: PropTypes.arrayOf(MessagePropTypes).isRequired,
videos: PropTypes.object.isRequired,
notify: PropTypes.func.isRequired, notify: PropTypes.func.isRequired,
onClose: PropTypes.func.isRequired,
sendMessage: PropTypes.func.isRequired, sendMessage: PropTypes.func.isRequired,
toolbarRef: PropTypes.object.isRequired videos: PropTypes.object.isRequired
} }
handleCloseChat = e => { constructor () {
const { toolbarRef } = this.props super()
toolbarRef.chatButton.click() this.chatHistoryRef = React.createRef()
} }
scrollToBottom = () => { scrollToBottom = () => {
this.chatScroll.scrollTop = this.chatScroll.scrollHeight const chatHistoryRef = this.chatHistoryRef.current
chatHistoryRef.scrollTop = chatHistoryRef.scrollHeight
} }
componentDidMount () { componentDidMount () {
this.scrollToBottom() this.scrollToBottom()
@ -34,16 +37,18 @@ export default class Chat extends React.PureComponent {
render () { render () {
const { messages, videos, notify, sendMessage } = this.props const { messages, videos, notify, sendMessage } = this.props
return ( return (
<div> <div className={classnames('chat-container', {
show: this.props.visible
})}>
<div className="chat-header"> <div className="chat-header">
<div className="chat-close" onClick={this.handleCloseChat}> <div className="chat-close" onClick={this.props.onClose}>
<div className="button button-icon"> <div className="button button-icon">
<span className="icon icon-arrow_forward" /> <span className="icon icon-arrow_forward" />
</div> </div>
</div> </div>
<div className="chat-title">Chat</div> <div className="chat-title">Chat</div>
</div> </div>
<div className="chat-history" ref={div => { this.chatScroll = div }}> <div className="chat-history" ref={this.chatHistoryRef}>
{messages.length ? ( {messages.length ? (
messages.map((message, i) => ( messages.map((message, i) => (

View File

@ -1,30 +1,16 @@
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import React from 'react' import React from 'react'
import classnames from 'classnames'
import screenfull from 'screenfull' import screenfull from 'screenfull'
import { MessagePropTypes } from './Chat.js' import { MessagePropTypes } from './Chat.js'
import { StreamPropType } from './Video.js' import { StreamPropType } from './Video.js'
export default class Toolbar extends React.PureComponent { export default class Toolbar extends React.PureComponent {
static propTypes = { static propTypes = {
chatRef: PropTypes.object.isRequired,
messages: PropTypes.arrayOf(MessagePropTypes).isRequired, messages: PropTypes.arrayOf(MessagePropTypes).isRequired,
stream: StreamPropType stream: StreamPropType,
} onToggleChat: PropTypes.func.isRequired,
constructor () { chatVisible: PropTypes.bool.isRequired
super()
this.state = {
isChatOpen: false,
totalMessages: 0
}
}
handleChatClick = () => {
const { chatRef, messages } = this.props
chatRef.classList.toggle('show')
this.chatButton.classList.toggle('on')
this.setState({
isChatOpen: chatRef.classList.contains('show'),
totalMessages: messages.length
})
} }
handleMicClick = () => { handleMicClick = () => {
const { stream } = this.props const { stream } = this.props
@ -51,14 +37,14 @@ export default class Toolbar extends React.PureComponent {
} }
render () { render () {
const { messages, stream } = this.props const { messages, stream } = this.props
const { isChatOpen, totalMessages } = this.state
return ( return (
<div className="toolbar active"> <div className="toolbar active">
<div onClick={this.handleChatClick} <div onClick={this.props.onToggleChat}
ref={node => { this.chatButton = node }} className={classnames('button chat', {
className="button chat" on: this.props.chatVisible
data-blink={messages.length !== totalMessages && !isChatOpen} })}
data-blink={this.props.chatVisible && messages.length}
title="Chat" title="Chat"
> >
<span className="icon icon-question_answer" /> <span className="icon icon-question_answer" />

View File

@ -5,7 +5,7 @@ import TestUtils from 'react-dom/test-utils'
import Toolbar from '../Toolbar.js' import Toolbar from '../Toolbar.js'
import { MediaStream } from '../../window.js' import { MediaStream } from '../../window.js'
describe('components/Video', () => { describe('components/Toolbar', () => {
class ToolbarWrapper extends React.PureComponent { class ToolbarWrapper extends React.PureComponent {
static propTypes = Toolbar.propTypes static propTypes = Toolbar.propTypes
@ -15,22 +15,22 @@ describe('components/Video', () => {
} }
render () { render () {
return <Toolbar return <Toolbar
chatRef={this.props.chatRef} chatVisible={this.props.chatVisible}
onToggleChat={this.props.onToggleChat}
messages={this.props.messages} messages={this.props.messages}
stream={this.state.stream || this.props.stream} stream={this.state.stream || this.props.stream}
/> />
} }
} }
let component, node, chatRef, mediaStream, url let component, node, mediaStream, url, onToggleChat
function render () { function render () {
mediaStream = new MediaStream() mediaStream = new MediaStream()
chatRef = ReactDOM.findDOMNode( onToggleChat = jest.fn()
TestUtils.renderIntoDocument(<div />)
)
component = TestUtils.renderIntoDocument( component = TestUtils.renderIntoDocument(
<ToolbarWrapper <ToolbarWrapper
chatRef={chatRef} chatVisible
onToggleChat={onToggleChat}
messages={[]} messages={[]}
stream={{ mediaStream, url }} stream={{ mediaStream, url }}
/> />
@ -46,9 +46,10 @@ describe('components/Video', () => {
describe('handleChatClick', () => { describe('handleChatClick', () => {
it('toggle chat', () => { it('toggle chat', () => {
expect(onToggleChat.mock.calls.length).toBe(0)
const button = node.querySelector('.chat') const button = node.querySelector('.chat')
TestUtils.Simulate.click(button) TestUtils.Simulate.click(button)
expect(button.classList.contains('on')).toBe(true) expect(onToggleChat.mock.calls.length).toBe(1)
}) })
}) })