metamaps--metamaps/frontend/src/components/MapView/MapChat/index.js

189 lines
5.8 KiB
JavaScript
Raw Normal View History

react-router and rebuild app structure in react (#1091) * initial restructuring * stuff * lock version number * just keep using current mapinfobox * fix map upperRightUI layout * make mapsWidth work and add mobile * remove filterBoxOpen for now * redo the mobile menu in react * get account menu and invite lightbox working * fixed maps scrolling * make other routes work * fix signed out home page * fix accountbox toggling * add metacode edit routes * lots of fixes * fix map chat layout and tab bug * improve topic card readability and fix dragging bug * fixup mapchat stuff * fix up navigation to use react-router * jquery no longer handling access requests * handle case where user hasn't loaded yet * this shouldn't have been removed * add frame for topic view * rewrite map instructions * fix toast (and sign out bug) * fix apps pages and missing routes * made our request invite page look nice * filter box in react * forgot to add one proptype * remove extra comments * handle page title and mobile title updates * reenable google analytics * make filterbox use onclickoutside * reenable topic view in react * fix csrf auth token * fix little homepage styling issue * try putting preparevizdata in a timeout * installing render log to count * little fixes * fixup filters * make filter map function names more readable * eslint helps * renaming for clarity * use onclickoutside for account/sign in box * add some logging to see whether this is source of many renders * turns out chatview was heavily hogging memory * tiimeout not needed
2017-03-16 17:58:56 -04:00
import React, { PropTypes, Component } from 'react'
import Unread from './Unread'
import Participant from './Participant'
import Message from './Message'
import NewMessage from './NewMessage'
import Util from '../../../Metamaps/Util'
function makeList(messages) {
let currentHeader
return messages ? messages.map(m => {
let heading = false
if (!currentHeader) {
heading = true
currentHeader = m
} else {
// not same user or time diff of greater than 3 minutes
heading = m.user_id !== currentHeader.user_id || Math.floor(Math.abs(new Date(currentHeader.created_at) - new Date(m.created_at)) / 60000) > 3
currentHeader = heading ? m : currentHeader
}
return <Message {...m} key={m.id} heading={heading}/>
}) : null
}
class MapChat extends Component {
constructor(props) {
super(props)
this.state = {
messageText: '',
alertSound: true, // whether to play sounds on arrival of new messages or not
cursorsShowing: true,
videosShowing: true
}
}
componentDidUpdate(prevProps) {
const { messages } = this.props
const prevMessages = prevProps.messages
if (messages.length !== prevMessages.length) setTimeout(() => this.scroll(), 50)
}
reset = () => {
this.setState({
messageText: '',
alertSound: true, // whether to play sounds on arrival of new messages or not
cursorsShowing: true,
videosShowing: true
})
}
close = () => {
this.props.onClose()
}
open = () => {
this.props.onOpen()
setTimeout(() => this.scroll(), 50)
}
scroll = () => {
// hack: figure out how to do this right
this.messagesDiv.scrollTop = this.messagesDiv.scrollHeight + 100
}
toggleDrawer = () => {
if (this.props.chatOpen) this.close()
else if (!this.props.chatOpen) this.open()
}
toggleAlertSound = () => {
this.setState({alertSound: !this.state.alertSound})
this.props.soundToggleClick()
}
toggleCursorsShowing = () => {
this.setState({cursorsShowing: !this.state.cursorsShowing})
this.props.cursorToggleClick()
}
toggleVideosShowing = () => {
this.setState({videosShowing: !this.state.videosShowing})
this.props.videoToggleClick()
}
handleChange = key => e => {
this.setState({
[key]: e.target.value
})
}
handleTextareaKeyUp = e => {
if (e.which === 13) {
e.preventDefault()
const text = Util.removeEmoji(this.state.messageText)
this.props.handleInputMessage(text)
this.setState({ messageText: '' })
}
}
render = () => {
const { unreadMessages, chatOpen, conversationLive,
isParticipating, participants, messages, inviteACall, inviteToJoin } = this.props
const { videosShowing, cursorsShowing, alertSound } = this.state
const rightOffset = chatOpen ? '0' : '-300px'
return (
<div id="chat-box-wrapper">
<div className="chat-box"
style={{ right: rightOffset }}
>
<div className="junto-header">
PARTICIPANTS
<div onClick={this.toggleVideosShowing} className={`video-toggle ${videosShowing ? '' : 'active'}`} />
<div onClick={this.toggleCursorsShowing} className={`cursor-toggle ${cursorsShowing ? '' : 'active'}`} />
</div>
<div className="participants">
{conversationLive && <div className="conversation-live">
LIVE
{isParticipating && <span className="call-action leave" onClick={this.props.leaveCall}>
LEAVE
</span>}
{!isParticipating && <span className="call-action join" onClick={this.props.joinCall}>
JOIN
</span>}
</div>}
{participants.map(participant => <Participant
key={participant.id}
{...participant}
inviteACall={inviteACall}
inviteToJoin={inviteToJoin}
conversationLive={conversationLive}
mapperIsLive={isParticipating}/>
)}
</div>
<div className="chat-header">
CHAT
<div onClick={this.toggleAlertSound} className={`sound-toggle ${alertSound ? '' : 'active'}`}></div>
</div>
<div className={`chat-button ${conversationLive ? 'active' : ''}`} onClick={this.toggleDrawer}>
<div className="tooltips">Chat</div>
<Unread count={unreadMessages} />
</div>
<div className="chat-messages" ref={div => { this.messagesDiv = div }}>
{makeList(messages)}
</div>
{chatOpen && <NewMessage messageText={this.state.messageText}
focusMessageInput={this.focusMessageInput}
handleChange={this.handleChange('messageText')}
textAreaProps={{
className: 'chat-input',
ref: textarea => { textarea && textarea.focus() },
placeholder: 'Send a message...',
onKeyUp: this.handleTextareaKeyUp,
onFocus: this.props.inputFocus,
onBlur: this.props.inputBlur
}}
/>}
</div>
</div>
)
}
}
MapChat.propTypes = {
unreadMessages: PropTypes.number,
chatOpen: PropTypes.bool,
conversationLive: PropTypes.bool,
isParticipating: PropTypes.bool,
onOpen: PropTypes.func,
onClose: PropTypes.func,
leaveCall: PropTypes.func,
joinCall: PropTypes.func,
inviteACall: PropTypes.func,
inviteToJoin: PropTypes.func,
videoToggleClick: PropTypes.func,
cursorToggleClick: PropTypes.func,
soundToggleClick: PropTypes.func,
participants: PropTypes.arrayOf(PropTypes.shape({
color: PropTypes.string, // css color
id: PropTypes.number,
image: PropTypes.string, // image url
self: PropTypes.bool,
username: PropTypes.string,
isParticipating: PropTypes.bool,
isPending: PropTypes.bool
}))
}
export default MapChat