Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wip #76 begin work on ChatComponent #123

Merged
merged 8 commits into from
Mar 29, 2017
29 changes: 29 additions & 0 deletions client/modules/User/UserActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ export const LOGIN_USER = 'LOGIN_USER';
export const AUTHENTICATE_SESSION = 'AUTHENTICATE_SESSION';
export const FAILED_AUTHENTICATION = 'FAILED_AUTHENTICATION';
export const CREATE_GROUP = 'CREATE_GROUP';
export const SET_CURRENT_STUDY_GROUP = 'SET_CURRENT_STUDY_GROUP';
export const PREPARE_CHAT_MESSAGES = 'PREPARE_CHAT_MESSAGES';
export const PREPARE_CHAT_MESSAGE = 'PREPARE_CHAT_MESSAGE';

// Auth Pages
export const DASHBOARD_PAGE = 'DASHBOARD_PAGE';
Expand Down Expand Up @@ -153,3 +156,29 @@ export function createStudyGroupRequest(user,studyGroup) {
};
}

export function setCurrentStudyGroup(studyGroupIndex) {
return {
type: SET_CURRENT_STUDY_GROUP,
studyGroupIndex,
};
}

export function prepareChatMessages(messages) {
return {
type: PREPARE_CHAT_MESSAGES,
messages,
};
}

export function prepareChatMessage(message) {
return {
type: PREPARE_CHAT_MESSAGE,
message,
};
}

export function switchChat(studyGroupIndex, studyGroup) {
return (dispatch) => {
return callApi(`message/${studyGroup.guid}`).then(res => dispatch(prepareChatMessages(res.messages)));
};
}
45 changes: 43 additions & 2 deletions client/modules/User/UserReducer.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { ADD_USER, UPDATE_USER, LOGIN_USER, AUTHENTICATE_SESSION, FAILED_AUTHENTICATION, LOGOUT_USER } from './UserActions';
import { ADD_USER, UPDATE_USER, LOGIN_USER, AUTHENTICATE_SESSION, FAILED_AUTHENTICATION, LOGOUT_USER, SET_CURRENT_STUDY_GROUP, PREPARE_CHAT_MESSAGES, PREPARE_CHAT_MESSAGE } from './UserActions';

import { getColorFromUserIndex } from './components/ChatComponent/ChatComponent';
import React from 'react';

// Initial State
const initialState = { data: [], user: null };
const initialState = { data: [], user: null, currentStudyGroup: -1, chat: { messages: [] } };

const UserReducer = (state = initialState, action) => {
switch (action.type) {
Expand All @@ -12,29 +15,67 @@ const UserReducer = (state = initialState, action) => {
case UPDATE_USER :
return {
user: action.user,
currentStudyGroup: state.currentStudyGroup,
chat: state.chat,
};
case LOGIN_USER: {
const user = (action.response.statusCode === 200) ? action.response.user : null;

return {
user,
currentStudyGroup: state.currentStudyGroup,
chat: state.chat,
};
}
case AUTHENTICATE_SESSION: {
const user = (action.response.statusCode === 200) ? action.response.user : null;

return {
user,
currentStudyGroup: state.currentStudyGroup,
chat: state.chat,
};
}
case FAILED_AUTHENTICATION: {
return {
user: null,
currentStudyGroup: -1,
chat: initialState.chat,
};
}
case LOGOUT_USER: {
return {
user: null,
currentStudyGroup: -1,
chat: initialState.chat,
};
}
case SET_CURRENT_STUDY_GROUP: {
return {
user: state.user, // Not sure if this is necessary
currentStudyGroup: action.studyGroupIndex,
chat: state.chat,
};
}
case PREPARE_CHAT_MESSAGES: {
const messages = [];

for (let i = 0; i < action.messages.length; i++) {
messages.push(<div key={i} style={{ color: getColorFromUserIndex(i) }}>{`${action.messages[i].author}: ${action.messages[i].messageContent}`}</div>);
}

return {
user: state.user,
currentStudyGroup: state.currentStudyGroup,
chat: { messages },
};
}
case PREPARE_CHAT_MESSAGE: {
state.chat.messages.push(<div key={state.chat.messages.length + 1} style={{ color: getColorFromUserIndex(0) }}>{`${action.message.user}: ${action.message.message}`}</div>);
return {
user: state.user,
currentStudyGroup: state.currentStudyGroup,
chat: state.chat,
};
}
default:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#message-area {
background-color: white;
overflow-y:scroll;
height: 90%;
}
107 changes: 107 additions & 0 deletions client/modules/User/components/ChatComponent/ChatComponent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import React, { Component, PropTypes } from 'react';
import { injectIntl } from 'react-intl';
import io from 'socket.io-client';

// Import Style
import styles from './ChatComponent.css';

const COLORS = [
'Red',
// 'Maroon', ugly color
'Yellow',
// 'Olive', also ugly color
'Lime',
'Green',
'Aqua',
'Teal',
'Blue',
'Navy',
'Fuchsia',
'Purple',
];

// Ensure we have a color for every user, if we are out of colors just wrap back around.
export function getColorFromUserIndex(index) {
return COLORS[index % COLORS.length];
}

export class ChatComponent extends Component {
constructor(props) {
super(props);
this.state = { value: '' };
this.socket = null;
this.sendMessage = this.sendMessage.bind(this);
this.handleChange = this.handleChange.bind(this);
this.handleKeyDown = this.handleKeyDown.bind(this);
this.onMessageReceive = this.onMessageReceive.bind(this);
}

componentWillMount() {
console.log('Will Mount');
this.socket = io.connect();
this.socket.emit('UserSignedIn', `${this.props.users.user.firstName} ${this.props.users.user.lastName}`);
this.socket.on('UpdateMessages', this.onMessageReceive);

//this.props.setChat(0);
}

componentWillUpdate(nextProps, nextState) {

}

componentWillUnmount() {
console.log('Will Unmount');
this.socket.disconnect();
}

onMessageReceive(data) {
this.props.prepareChatMessage(data);
}

handleChange(event) {
this.setState({ value: event.target.value });
}

handleKeyDown(event) {
if (event.keyCode === 13 && !event.shiftKey) {
this.sendMessage();
event.preventDefault();
}
}

sendMessage() {
if (this.state.value !== '') {
this.socket.emit('SaveMessage', {
message: this.state.value,
studyGroup: this.props.users.user.studyGroups[this.props.users.currentStudyGroup].guid,
});
this.state.value = '';
}
}

// Display form
render() {
if (this.props.users.currentStudyGroup <= -1) {
return null;
}

return (
<div className="col-md-9">
<div className="row-md-3 border rounded" id={styles['message-area']}>
{this.props.users.chat.messages}
</div>
<div className="row">
<textarea className="col-md-11 form-control" rows="3" value={this.state.value} onKeyDown={this.handleKeyDown} onChange={this.handleChange} ></textarea>
<button className="col-md-1 btn btn-primary" onClick={this.sendMessage}>Send</button>
</div>
</div>
);
}
}

// Warning issued if prop not provided
ChatComponent.propTypes = {
users: PropTypes.object.isRequired,
};

export default injectIntl(ChatComponent);
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import UserStudyGroupComponent from '../UserStudyGroupComponent/UserStudyGroupCo
import styles from './UserInfoComponent.css';

function UserInfoComponent(props) {
if (Object.keys(props.users).length === 1) { // wait props.users not to be null
if (props.users.user !== null) { // wait props.users not to be null
return (
<div className="col-md-3" id={styles.sidebar}>
<img className="img-circle" id={styles['circle-image']}src="/static/images/user.png" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,19 @@ import styles from './UserStudyGroupComponent.css';

function UserStudyGroupComponent(props){
const arr = [
{'name': "Dream Team Study"}, // Mock data (study groups)
{'name': "SOEN 341 Study Group"},
{'name': "COMP 346 Exam Study"},
{'name': "Team \"Mandy's Salad\" Discussion"}
{ name: 'Dream Team Study' }, // Mock data (study groups)
{ name: 'SOEN 341 Study Group' },
{ name: 'COMP 346 Exam Study' },
{ name: 'Team "Mandy\'s Salad" Discussion' },
];
if((props.users.user.studyGroups).length !== 0){

if (props.users.user !== null) {
return (
<div className={styles.studyGroup}>
<h4>Study Groups</h4>
<ul>
{(arr).map(group => { // Displays group name in array of user study groups. Tested using mock data.
return <li><Link href="">{group.name}</Link></li>; // Missing routes to chat.
{(arr).map((group, i) => { // Displays group name in array of user study groups. Tested using mock data.
return <li key={i}><Link href="">{group.name}</Link></li>; // Missing routes to chat.
})}
</ul>
</div>
Expand Down
22 changes: 18 additions & 4 deletions client/modules/User/pages/UserDashboardPage/UserDashboardPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,35 @@ import { bindActionCreators } from 'redux';

// Import Components
import UserInfoComponent from '../../components/UserInfoComponent/UserInfoComponent';
import ChatComponent from '../../components/ChatComponent/ChatComponent';

import { authenticateSessionRequest } from '../../UserActions';
import { authenticateSessionRequest, switchChat, setCurrentStudyGroup, prepareChatMessage } from '../../UserActions';

import styles from './UserDashboardPage.css';

class UserDashboardPage extends Component {
constructor(props) {
super(props);
this.setChat = this.setChat.bind(this);
}
componentWillMount() {
// If I do not have any user data attempt to authenticate using cookie
if (this.props.users.user == null) {
this.props.authenticateSessionRequest();
}
}

setChat(studyGroupIndex) {
this.props.setCurrentStudyGroup(studyGroupIndex);
this.props.switchChat(studyGroupIndex, this.props.users.user.studyGroups[studyGroupIndex]);
}

render() {
if (this.props.users.user != null) {
return (
<div className={styles.dashboardContainer}>
<UserInfoComponent users={this.props.users} />
<div className={`row ${styles.dashboardContainer}`}>
<UserInfoComponent users={this.props.users} setChat={this.setChat} />
<ChatComponent users={this.props.users} setChat={this.setChat} prepareChatMessage={this.props.prepareChatMessage} />
</div>
);
}
Expand All @@ -32,7 +43,7 @@ class UserDashboardPage extends Component {

// Bind actions to props
function mapDispatchToProps(dispatch) {
return bindActionCreators({ authenticateSessionRequest }, dispatch);
return bindActionCreators({ authenticateSessionRequest, switchChat, setCurrentStudyGroup, prepareChatMessage }, dispatch);
}

// map Users from store to Props
Expand All @@ -43,6 +54,9 @@ function mapStateToProps({ users }) {
// Warning issued if prop not provided
UserDashboardPage.propTypes = {
authenticateSessionRequest: PropTypes.func.isRequired,
setCurrentStudyGroup: PropTypes.func.isRequired,
switchChat: PropTypes.func.isRequired,
prepareChatMessage: PropTypes.func.isRequired,
users: PropTypes.object,
};

Expand Down
Loading