Added chat support

This commit is contained in:
Håvar Aambø Fosstveit
2018-03-01 16:19:37 +01:00
parent 9a7e149c35
commit 494250153e
18 changed files with 617 additions and 7 deletions
+93
View File
@@ -0,0 +1,93 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import marked from 'marked';
import { connect } from 'react-redux';
const scrollToBottom = () =>
{
const messagesDiv = document.getElementById('messages');
messagesDiv.scrollTop = messagesDiv.scrollHeight;
};
const linkRenderer = new marked.Renderer();
linkRenderer.link = (href, title, text) =>
{
title = title ? title : href;
text = text ? text : href;
return (`<a target='_blank' href='${ href }' title='${ title }'>${ text }</a>`);
};
class MessageList extends Component
{
componentDidMount()
{
scrollToBottom();
}
componentDidUpdate()
{
scrollToBottom();
}
getTimeString(time)
{
return `${(time.getHours() < 10 ? '0' : '')}${time.getHours()}:${(time.getMinutes() < 10 ? '0' : '')}${time.getMinutes()}`;
}
render()
{
const {
chatmessages
} = this.props;
return (
<div data-component='MessageList' id='messages'>
{
chatmessages.map((message, i) =>
{
const messageTime = new Date(message.time);
return (
<div className='message' key={i}>
<div className={message.sender}>
<div
className='message-text'
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{ __html : marked.parse(
message.text,
{ sanitize: true, renderer: linkRenderer }
) }}
/>
<span className='message-time'>
{message.name} - {this.getTimeString(messageTime)}
</span>
</div>
</div>
);
})
}
</div>
);
}
}
MessageList.propTypes =
{
chatmessages : PropTypes.arrayOf(PropTypes.object).isRequired
};
const mapStateToProps = (state) =>
{
return {
chatmessages : state.chatmessages
};
};
const MessageListContainer = connect(
mapStateToProps
)(MessageList);
export default MessageListContainer;
+118
View File
@@ -0,0 +1,118 @@
import React, { Component } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import * as stateActions from '../redux/stateActions';
import * as requestActions from '../redux/requestActions';
import MessageList from './Chat/MessageList';
class ChatWidget extends Component
{
render()
{
const {
senderPlaceHolder,
onSendMessage,
onToggleChat,
showChat,
disabledInput,
badge,
autofocus,
displayName
} = this.props;
return (
<div data-component='ChatWidget'>
{
showChat &&
<div data-component='Conversation'>
<MessageList />
<form
data-component='Sender'
onSubmit={(e) => { onSendMessage(e, displayName); }}
>
<input
type='text'
className='new-message'
name='message'
placeholder={senderPlaceHolder}
disabled={disabledInput}
autoFocus={autofocus}
autoComplete='off'
/>
</form>
</div>
}
{
<div
className='launcher'
data-type='dark'
data-tip='Show room chat'
onClick={onToggleChat}
>
{
badge > 0 && <span className='badge'>{badge}</span>
}
</div>
}
</div>
);
}
}
ChatWidget.propTypes =
{
onToggleChat : PropTypes.func,
showChat : PropTypes.bool,
senderPlaceHolder : PropTypes.string,
onSendMessage : PropTypes.func,
disabledInput : PropTypes.bool,
badge : PropTypes.number,
autofocus : PropTypes.bool,
displayName : PropTypes.string
};
ChatWidget.defaultProps =
{
senderPlaceHolder : 'Type a message...',
badge : 0,
autofocus : true,
displayName : null
};
const mapStateToProps = (state) =>
{
return {
showChat : state.chatbehavior.showChat,
disabledInput : state.chatbehavior.disabledInput,
displayName : state.me.displayName
};
};
const mapDispatchToProps = (dispatch) =>
{
return {
onToggleChat : () =>
{
dispatch(stateActions.toggleChat());
},
onSendMessage : (event, displayName) =>
{
event.preventDefault();
const userInput = event.target.message.value;
if (userInput)
{
dispatch(stateActions.addUserMessage(userInput));
dispatch(requestActions.sendChatMessage(userInput, displayName));
}
event.target.message.value = '';
}
};
};
const ChatWidgetContainer = connect(
mapStateToProps,
mapDispatchToProps
)(ChatWidget);
export default ChatWidgetContainer;
+2
View File
@@ -10,6 +10,7 @@ import { Appear } from './transitions';
import Me from './Me';
import Peers from './Peers';
import Notifications from './Notifications';
import ChatWidget from './ChatWidget';
class Room extends React.Component
{
@@ -29,6 +30,7 @@ class Room extends React.Component
<Appear duration={300}>
<div data-component='Room'>
<Notifications />
<ChatWidget />
<div className='state' data-tip='Server status'>
<div className={classnames('icon', room.state)} />
+8
View File
@@ -69,3 +69,11 @@ export const Notification = PropTypes.shape(
type : PropTypes.oneOf([ 'info', 'error' ]).isRequired,
timeout : PropTypes.number
});
export const Message = PropTypes.shape(
{
type : PropTypes.string,
component : PropTypes.string,
text : PropTypes.string,
sender : PropTypes.string
});