Skip to content

Commit

Permalink
docs(examples): 2nd part of the "private messaging" example
Browse files Browse the repository at this point in the history
  • Loading branch information
darrachequesne committed Feb 15, 2021
1 parent 8b404f4 commit 992c938
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 16 deletions.
68 changes: 58 additions & 10 deletions examples/private-messaging/server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,43 +5,91 @@ const io = require("socket.io")(httpServer, {
},
});

const crypto = require("crypto");
const randomId = () => crypto.randomBytes(8).toString("hex");

const { InMemorySessionStore } = require("./sessionStore");
const sessionStore = new InMemorySessionStore();

io.use((socket, next) => {
const sessionID = socket.handshake.auth.sessionID;
if (sessionID) {
const session = sessionStore.findSession(sessionID);
if (session) {
socket.sessionID = sessionID;
socket.userID = session.userID;
socket.username = session.username;
return next();
}
}
const username = socket.handshake.auth.username;
if (!username) {
return next(new Error("invalid username"));
}
socket.sessionID = randomId();
socket.userID = randomId();
socket.username = username;
next();
});

io.on("connection", (socket) => {
// persist session
sessionStore.saveSession(socket.sessionID, {
userID: socket.userID,
username: socket.username,
connected: true,
});

// emit session details
socket.emit("session", {
sessionID: socket.sessionID,
userID: socket.userID,
});

// join the "userID" room
socket.join(socket.userID);

// fetch existing users
const users = [];
for (let [id, socket] of io.of("/").sockets) {
sessionStore.findAllSessions().forEach((session) => {
users.push({
userID: id,
username: socket.username,
userID: session.userID,
username: session.username,
connected: session.connected,
});
}
});
socket.emit("users", users);

// notify existing users
socket.broadcast.emit("user connected", {
userID: socket.id,
userID: socket.userID,
username: socket.username,
connected: true,
});

// forward the private message to the right recipient
// forward the private message to the right recipient (and to other tabs of the sender)
socket.on("private message", ({ content, to }) => {
socket.to(to).emit("private message", {
socket.to(to).to(socket.userID).emit("private message", {
content,
from: socket.id,
from: socket.userID,
to,
});
});

// notify users upon disconnection
socket.on("disconnect", () => {
socket.broadcast.emit("user disconnected", socket.id);
socket.on("disconnect", async () => {
const matchingSockets = await io.in(socket.userID).allSockets();
const isDisconnected = matchingSockets.size === 0;
if (isDisconnected) {
// notify other users
socket.broadcast.emit("user disconnected", socket.userID);
// update the connection status of the session
sessionStore.saveSession(socket.sessionID, {
userID: socket.userID,
username: socket.username,
connected: false,
});
}
});
});

Expand Down
28 changes: 28 additions & 0 deletions examples/private-messaging/server/sessionStore.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/* abstract */ class SessionStore {
findSession(id) {}
saveSession(id, session) {}
findAllSessions() {}
}

class InMemorySessionStore extends SessionStore {
constructor() {
super();
this.sessions = new Map();
}

findSession(id) {
return this.sessions.get(id);
}

saveSession(id, session) {
this.sessions.set(id, session);
}

findAllSessions() {
return [...this.sessions.values()];
}
}

module.exports = {
InMemorySessionStore
};
17 changes: 17 additions & 0 deletions examples/private-messaging/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,23 @@ export default {
},
},
created() {
const sessionID = localStorage.getItem("sessionID");
if (sessionID) {
this.usernameAlreadySelected = true;
socket.auth = { sessionID };
socket.connect();
}
socket.on("session", ({ sessionID, userID }) => {
// attach the session ID to the next reconnection attempts
socket.auth = { sessionID };
// store it in the localStorage
localStorage.setItem("sessionID", sessionID);
// save the ID of the user
socket.userID = userID;
});
socket.on("connect_error", (err) => {
if (err.message === "invalid username") {
this.usernameAlreadySelected = false;
Expand Down
27 changes: 21 additions & 6 deletions examples/private-messaging/src/components/Chat.vue
Original file line number Diff line number Diff line change
Expand Up @@ -68,18 +68,25 @@ export default {
});
const initReactiveProperties = (user) => {
user.connected = true;
user.messages = [];
user.hasNewMessages = false;
};
socket.on("users", (users) => {
users.forEach((user) => {
user.self = user.userID === socket.id;
for (let i = 0; i < this.users.length; i++) {
const existingUser = this.users[i];
if (existingUser.userID === user.userID) {
existingUser.connected = user.connected;
return;
}
}
user.self = user.userID === socket.userID;
initReactiveProperties(user);
this.users.push(user);
});
// put the current user first, and sort by username
this.users = users.sort((a, b) => {
this.users.sort((a, b) => {
if (a.self) return -1;
if (b.self) return 1;
if (a.username < b.username) return -1;
Expand All @@ -88,6 +95,13 @@ export default {
});
socket.on("user connected", (user) => {
for (let i = 0; i < this.users.length; i++) {
const existingUser = this.users[i];
if (existingUser.userID === user.userID) {
existingUser.connected = true;
return;
}
}
initReactiveProperties(user);
this.users.push(user);
});
Expand All @@ -102,13 +116,14 @@ export default {
}
});
socket.on("private message", ({ content, from }) => {
socket.on("private message", ({ content, from, to }) => {
for (let i = 0; i < this.users.length; i++) {
const user = this.users[i];
if (user.userID === from) {
const fromSelf = socket.userID === from;
if (user.userID === (fromSelf ? to : from)) {
user.messages.push({
content,
fromSelf: false,
fromSelf,
});
if (user !== this.selectedUser) {
user.hasNewMessages = true;
Expand Down

0 comments on commit 992c938

Please sign in to comment.