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

completed project #13

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
# project_superchat
Build a realtime multi-room chat application. Make it super.

By Tyler Ketron.
15 changes: 15 additions & 0 deletions helpers/handlebars-helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module.exports = {
chatTable: messages => {
var str = '';
messages.forEach(message => {
str +=
'<tr><td><strong>' +
message.author +
': </strong>' +
message.body +
'</td></tr>';
});

return str;
}
};
106 changes: 106 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
const express = require('express');
const app = express();
const server = require('http').createServer(app);
const io = require('socket.io')(server);

const expressHandlebars = require('express-handlebars');
const hbsHelpers = require('./helpers/handlebars-helpers');
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');
const {
storeMessage,
getMessages,
addRoom,
getRoomNames
} = require('./message-store');

app.engine(
'handlebars',
expressHandlebars({ defaultLayout: 'main', helpers: hbsHelpers })
);
app.set('view engine', 'handlebars');

app.use(
'/socket.io',
express.static(__dirname + '/node_modules/socket.io-client/dist/')
);
app.use(express.static(__dirname + '/public'));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(cookieParser());

const roomName = 'BASIC';

app.get('/', (req, res) => {
if (req.cookies.username) {
res.redirect('/rooms');
} else {
res.redirect('/login');
}
});

app.get('/rooms', (req, res) => {
getRoomNames()
.then(rooms => {
res.render('rooms', { rooms: rooms, username: req.cookies.username });
})
.catch(err => console.log(err));
});

app.get('/rooms/:room', (req, res) => {
let roomName = req.params.room;
getRoomNames().then(rooms => {
getMessages(roomName).then(values => {
Promise.all(values)
.then(messages => {
console.log(messages);
res.render('index', {
rooms: rooms,
roomName: roomName,
messages: messages,
username: req.cookies.username
});
})
.catch(err => {
console.log(err);
});
});
});
});

app.post('/rooms', (req, res) => {
let newRoomName = req.body.roomName;
addRoom(newRoomName);
res.redirect('back');
});

app.post('/rooms/:room', (req, res) => {
let messageBody = req.body.message;
let username = req.cookies.username;
let roomName = req.params.room;
storeMessage(messageBody, username, roomName);
io.sockets.emit('new message', {
body: messageBody,
author: username,
room: roomName
});
res.redirect('back');
});

app.get('/login', (req, res) => {
res.render('login');
});

app.post('/login', (req, res) => {
let username = req.body.username;
res.cookie('username', username);
res.redirect('/rooms');
});

app.get('/logout', (req, res) => {
res.clearCookie('username');
res.redirect('/');
});

server.listen(3000, () => {
console.log('Listening on port 3000');
});
98 changes: 98 additions & 0 deletions message-store.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
const redis = require('redis');
const redisClient = redis.createClient();

const storeMessage = (messageBody, authorName, roomName) => {
let messageID = Date.now();
let messageHashName = 'message:' + messageID;
let roomListName = 'room:' + roomName;
console.log(
'Message hash name: ' +
messageHashName +
', Message: ' +
messageBody +
', Author: ' +
authorName +
', Room: ' +
roomName
);
redisClient.hmset(
messageHashName,
'body',
messageBody,
'author',
authorName,
'room',
roomName
);
redisClient.lpush(roomListName, messageHashName);
};

const getMessageIDs = roomName => {
let roomListName = 'room:' + roomName;
return new Promise((resolve, reject) => {
redisClient.lrange(roomListName, 0, 20, (err, messageIDs) => {
if (err) {
reject(err);
} else {
// console.log(messageIDs);
resolve(messageIDs);
}
});
});
};

const getMessageByID = messageID => {
return new Promise((resolve, reject) => {
redisClient.hgetall(messageID, (err, messageObject) => {
if (err) {
reject(err);
} else {
// console.log(messageObject);
resolve(messageObject);
}
});
});
};

const getMessages = roomName => {
//array of promises containing message bodies
return new Promise((resolve, reject) => {
var messages = [];
getMessageIDs(roomName)
.then(messageIDsList => {
messageIDsList.forEach(messageID => {
var p = getMessageByID(messageID);
messages.push(p);
});
// console.log(messages);
resolve(messages);
})
.catch(error => {
// console.error(error);
reject(error);
});
});
};

const addRoom = roomName => {
redisClient.sadd('rooms', roomName);
};

const getRoomNames = () => {
return new Promise((resolve, reject) => {
redisClient.smembers('rooms', (err, roomNames) => {
if (err) {
reject(err);
} else {
resolve(roomNames);
}
});
});
};

module.exports = {
storeMessage,
getMessages,
addRoom,
getRoomNames
};
28 changes: 28 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"name": "project_superchat",
"version": "1.0.0",
"description": "Build a realtime multi-room chat application. Make it super.",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/tketron/project_superchat.git"
},
"author": "Tyler Ketron",
"license": "ISC",
"bugs": {
"url": "https://github.com/tketron/project_superchat/issues"
},
"homepage": "https://github.com/tketron/project_superchat#readme",
"dependencies": {
"body-parser": "^1.17.2",
"cookie-parser": "^1.4.3",
"express": "^4.15.3",
"express-handlebars": "^3.0.0",
"redis": "^2.7.1",
"socket.io": "^2.0.3",
"socket.io-client": "^2.0.3"
}
}
Binary file added views/.DS_Store
Binary file not shown.
85 changes: 85 additions & 0 deletions views/index.handlebars
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<nav class="navbar navbar-default">
<div class="container-fluid">

<div class="navbar-header">

<h2 id="heading" class="navbar-text">SUPER CHAT</h2>
</div>

<p class="navbar-text navbar-right">
{{username}}
<a href="/logout" class="navbar-link">Logout</a>
</p>
</div>
</div>
</nav>

<div class="container">
<div class="row">

<div id="room-list" class="col-sm-4">
<table class="table">
<thead>
<tr>
<th>Chat Rooms</th>
</tr>
</thead>
<tbody id="rooms-body">
<tr>
<td>
<a href='/rooms'>Create New Room</a>
</td>
</tr>
{{#each rooms}}
<tr>
<td>
<a href='/rooms/{{this}}'>{{this}}</a>
</td>
</tr>
{{/each}}
</tbody>
</table>
</div>

<div id="active-room" class="col-sm-4">
<table class="table">
<thead>
<tr>
<th>{{roomName}}</th>
</tr>
</thead>
<tbody id="{{roomName}}">
{{{chatTable messages}}}
</tbody>
</table>
</div>

<div class="col-sm-4">
<form action="/rooms/{{roomName}}" method="post">
<div class="form-group">
<label for="message">New Message</label>
<textarea class="form-control" placeholder="Message" id="message" name="message"></textarea>
</div>

<button type="submit" class="btn btn-default">Send</button>
</form>
</div>

</div>
</div>


</div>

<script>
var socket = io.connect('http://localhost:3000');

socket.on('new message', function(message) {
var $newMessage = $('<tr><td><strong>' +
message.author +
': </strong>' +
message.body +
'</td></tr>');
$('#'+message.room).prepend($newMessage);
});
</script>
20 changes: 20 additions & 0 deletions views/layouts/main.handlebars
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>SUPERCHAT</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">

<!-- Optional theme -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">

<script src="https://code.jquery.com/jquery-1.10.2.js"></script>
<script src="/socket.io/socket.io.js"></script>

<!-- Latest compiled and minified JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
</head>
<body>
{{{body}}}
</body>
</html>
21 changes: 21 additions & 0 deletions views/login.handlebars
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<div class="container">
<div class="row">
<div class="col-sm-12">

<h1 id="heading">SUPER CHAT</h1>
</div>
</div>

<div class="row">

<div class="col-sm-4 col-sm-offset-4">
<form action="/login" method="post">
<div class="form-group">
<label for="message">Login</label>
<input type="text" class="form-control" placeholder="Username" id="username" name="username">
</div>

<button type="submit" class="btn btn-default">Submit</button>
</form>
</div>
</div>
Loading