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

Finished Project #4

Open
wants to merge 33 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
0c32d63
general file structure built
William-Charles Apr 5, 2017
0758b10
Add notes to README.md
rttomlinson Apr 5, 2017
1bb187f
building html of login
William-Charles Apr 5, 2017
df65d2b
Add websocket connection
rttomlinson Apr 5, 2017
ac31402
more layout html
William-Charles Apr 5, 2017
c6dcf05
Add socket listeners on client
rttomlinson Apr 5, 2017
a2d145b
Move redisOps from app.js back to ioOps
rttomlinson Apr 5, 2017
71966fb
Add connects to sockets.js
rttomlinson Apr 5, 2017
e3bf2c3
Add io dependency injection in index.js
rttomlinson Apr 5, 2017
5bc1fd4
debugging
William-Charles Apr 5, 2017
1c7d4d8
Sending data for new message from client to server
rttomlinson Apr 5, 2017
d76994c
built message storing with redis
William-Charles Apr 5, 2017
ff61e41
Add event emitter for websockets for newMessages
rttomlinson Apr 5, 2017
abe6ed7
debugging messages
William-Charles Apr 5, 2017
db38085
Try to get makeNewMessage functions to work
rttomlinson Apr 5, 2017
3e62a9d
Add initizing data and bluebird
rttomlinson Apr 6, 2017
991d82a
Merge conflicts
rttomlinson Apr 6, 2017
0683e82
workign on makiing async await work
William-Charles Apr 6, 2017
3194ae3
Rework createChatroom functions
rttomlinson Apr 6, 2017
a232799
Rework createMessage function of redisOps.js
rttomlinson Apr 6, 2017
0032852
storing messages in different data structure
William-Charles Apr 6, 2017
deecefb
Add socket listener for room-change event
rttomlinson Apr 6, 2017
2ff054d
Fix bug sending data to room-change event from app.js
rttomlinson Apr 6, 2017
7a06608
Send data as object for room-change event for websockets
rttomlinson Apr 6, 2017
99877b8
Rework delegation and add class to room-names div
rttomlinson Apr 6, 2017
ec2247c
change checkIfRoomExists return
rttomlinson Apr 6, 2017
34b210c
Move currentRoom value to browser environment
rttomlinson Apr 6, 2017
78fd920
BOOM did some cool stuff
William-Charles Apr 6, 2017
fd786aa
Add signout button hTML
rttomlinson Apr 6, 2017
1613be7
a function version of super chat
William-Charles Apr 6, 2017
a87ac84
Add BS classes to index.handlebars and main.handlebars
rttomlinson Apr 6, 2017
31889e5
Make redisClient depenedency and reorganize redisOps.js
rttomlinson Apr 6, 2017
6de234f
updated required version of node in package.json
William-Charles Apr 6, 2017
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules/
dump.rdb
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,37 @@
# project_superchat
Build a realtime multi-room chat application. Make it super.

Renzo Tomlinson and Will Whitworth



Notes:

If user has username cookie,
Send them to the chat page
If not, then require them to submit a username which sets the cookie
and redirects them to the chatpage



They get a default chatroom
--Get a list of chatrooms that they can click on
Only one chatroom displayed at a time


They can submit posts to the chatroom
Posts submitted through chatroom are handled with websockets
New room submissions are handled with websocks

Handle chatroom changes with a post request (partials? Possible to just use websockets)







It should update in realtime on every browser that is connected to the server



81 changes: 81 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
const express = require("express");
const app = express();
const server = require("http").createServer(app);
const io = require("socket.io")(server);
const path = require("path");
const cookieParser = require("cookie-parser");
const chatOps = require("./lib/chatOps");

//Require routes
const index = require("./routes/index.js")(io);

// Set up handlebars
const exphbs = require("express-handlebars");
app.engine("handlebars", exphbs({ defaultLayout: "main" }));
app.set("view engine", "handlebars");

// Set up body-parser
app.use(cookieParser());
const bodyParser = require("body-parser");
app.use(bodyParser.urlencoded({ extended: false }));

app.use(express.static(path.join(__dirname, "public")));

app.use("/", index);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error("Not Found");
err.status = 404;
next(err);
});

// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get("env") === "development" ? err : {};
// render the error page
res.status(err.status || 500);
res.render("error");
});

/////////////////////
io.on("connection", client => {
console.log("New connection!");
//send the client the data
//display all message of the default chatroom
let pro = Promise.all([
chatOps.buildMessageTable("default"),
chatOps.buildRoomsTable()
]);
pro.then(function onFulfilled(infoObj) {
client.emit("connection", { messages: infoObj[0], rooms: infoObj[1] });
});

client.on("new room", room => {
//io.emit(new room) tells all the clients to update their rooms
let pro = chatOps.makeNewRoom(room);
pro.then(htmlString => {
if (htmlString) {
io.emit("new room", htmlString);
}
});
});
client.on("new message", data => {
//io.emit(new room) tells all the clients to update their rooms
let htmlString = chatOps.makeNewMessage(data);
if (htmlString) {
let room = data.room;
io.emit("new message", { htmlString, room });
}
});

client.on("room-change", room => {
chatOps.buildMessageTable(room).then(function onFulfilled(messages) {
client.emit("room-change", { messages });
});
});
});

server.listen(process.env.PORT || 3000);
54 changes: 54 additions & 0 deletions lib/chatOps.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
const redisOps = require("./redisOps");

let chatOps = {};

chatOps.makeNewRoom = async function makeNewRoom(roomName) {
//Unknown if it makes it into the list
if (await redisOps.createChatRoom(roomName)) {
let newRoom = chatOps.makeRoomHTML(roomName);
return newRoom;
} else {
return false;
}
};

async function buildMessageTable(roomName) {
let messageList = await redisOps.getNamesOfMessagesByChatRoom(roomName);
let listOfObjects = await redisOps.lookupMessagesAndJSONParse(messageList);
let HTMLstring = "";
listOfObjects.forEach(obj => {
HTMLstring += chatOps.makeMessageHTML(obj);
});
return HTMLstring;
}

chatOps.buildRoomsTable = async function buildRoomsTable() {
let listOfNames = await redisOps.getAllChatRooms();
console.log(`listOfNames is a ${listOfNames}`);
let HTMLstring = "";
listOfNames.forEach(name => {
HTMLstring += chatOps.makeRoomHTML(name);
});

return HTMLstring;
};

chatOps.makeRoomHTML = function makeRoomHTML(roomName) {
let htmlString = `<tr><td><a><div class="room-name" id="${roomName}">${roomName}</div></a></td></tr>`;
return htmlString;
};

chatOps.makeNewMessage = function makeNewMessage(messageObj) {
redisOps.createMessage(messageObj);
return chatOps.makeMessageHTML(messageObj);
};
chatOps.makeMessageHTML = function makeMessageHTML(messageObj) {
let htmlString = `<tr><td>Username ${messageObj.user}<br>${messageObj.message}</td></tr>`;
return htmlString;
};
//stageing
//redisstuff
//buildHTML
chatOps.buildMessageTable = buildMessageTable;

module.exports = chatOps;
7 changes: 7 additions & 0 deletions lib/redisClient.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const redis = require('redis');
const bluebird = require("bluebird");
bluebird.promisifyAll(redis.RedisClient.prototype);
var redisClient = (process.env.REDIS_URL) ? redis.createClient(process.env.REDIS_URL) : redis.createClient();


module.exports = redisClient;
130 changes: 130 additions & 0 deletions lib/redisOps.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
const redisClient = require("./redisClient");
const shortid = require("shortid");



//************ Initialize Database ****************//////
redisClient.flushall();
redisClient.lpush("rooms", "default");
//Grab the default room and pull from it?

let messageID = "message" + Date.now();
redisClient.setnx(
messageID,
JSON.stringify({
user: "welcome",
message: "welcome to default",
room: "default"
})
);
//Add this newly created message to the default's field in the rooms key
redisClient.lpush("default", messageID);
///////********* End database Initialization *********/////////


async function createChatRoom(roomName) {
//check if roomName already exists
//if so, we should tell user somewhere
//if not, add it
if (roomName === "") {
console.log("Can't be an empty string");
}
if (await checkIfRoomExists(roomName)) {
return false;
} else {
//call lrange and count the entries
await redisClient.lpushAsync("rooms", roomName);
// check if the value returned from lpush is 1 greater than lrange count
return true;
}
}

async function checkIfRoomExists(roomName) {
let namesOfRooms = await redisClient.lrangeAsync("rooms", 0, -1);
//see if roomNames is in namesOfRooms
return namesOfRooms.some(element => element == roomName);
}

function getChatRoomMessages(roomName) {
getChatRoom(roomName).then(
data => {
return data;
},
err => {
return err;
}
);
}

function getChatRoom(roomName) {
return new Promise((resolve, reject) => {
redisClient.hget("rooms", roomName, (err, data) => {
if (err) {
reject(err);
}
resolve(data);
});
});
}

function getAllChatRooms() {
return redisClient.lrangeAsync("rooms", 0, -1);
}

/* Returns an array of strings corresponding to the unique ID
of the messages
@params roomName {String} name of room
*/
function getNamesOfMessagesByChatRoom(roomName) {
return new Promise((resolve, reject) => {
redisClient.lrange(roomName, 0, -1, (err, arr) => {
if (err) {
reject(err);
}
resolve(arr);
});
});
}

//loop through the array
//redisClient.get(messageName)
//JSON parse the data we get back
//data will be an object with properties user, room, message
//Make an array of these objects and pass it back
function lookupMessagesAndJSONParse(arrOfMessageNames) {
return Promise.all(
arrOfMessageNames.map(item => {
return redisClient.getAsync(item).then(result => {
return JSON.parse(result);
});
})
);
}

function _getAllOfHash(hash) {
return new Promise((resolve, reject) => {
redisClient.hgetall(hash, (err, data) => {
if (err) {
reject(err);
}
resolve(data);
});
});
}

function createMessage(messageObj) {
let messageID = "message" + shortid.generate();
redisClient.lpushAsync(messageObj.room, messageID);
messageObj = JSON.stringify(messageObj);
redisClient.setAsync(messageID, messageObj);
}

module.exports = {
createChatRoom,
getChatRoom,
getChatRoomMessages,
getAllChatRooms,
createMessage,
getNamesOfMessagesByChatRoom,
lookupMessagesAndJSONParse
};
37 changes: 37 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"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",
"start": "node app.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/William-Charles/project_superchat.git"
},
"engines": {
"node": "7.8.0"
},
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/William-Charles/project_superchat/issues"
},
"homepage": "https://github.com/William-Charles/project_superchat#readme",
"dependencies": {
"bluebird": "^3.5.0",
"body-parser": "^1.17.1",
"cookie-parser": "^1.4.3",
"express": "^4.15.2",
"express-handlebars": "^3.0.0",
"http": "0.0.0",
"path": "^0.12.7",
"redis": "^2.7.1",
"shorthash": "0.0.2",
"shortid": "^2.2.8",
"socket.io": "^1.7.3",
"socket.io-client": "^1.7.3"
}
}
Loading