Skip to content

Commit 992c938

Browse files
docs(examples): 2nd part of the "private messaging" example
See also: https://socket.io/get-started/private-messaging-part-2/
1 parent 8b404f4 commit 992c938

File tree

4 files changed

+124
-16
lines changed

4 files changed

+124
-16
lines changed

examples/private-messaging/server/index.js

Lines changed: 58 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,43 +5,91 @@ const io = require("socket.io")(httpServer, {
55
},
66
});
77

8+
const crypto = require("crypto");
9+
const randomId = () => crypto.randomBytes(8).toString("hex");
10+
11+
const { InMemorySessionStore } = require("./sessionStore");
12+
const sessionStore = new InMemorySessionStore();
13+
814
io.use((socket, next) => {
15+
const sessionID = socket.handshake.auth.sessionID;
16+
if (sessionID) {
17+
const session = sessionStore.findSession(sessionID);
18+
if (session) {
19+
socket.sessionID = sessionID;
20+
socket.userID = session.userID;
21+
socket.username = session.username;
22+
return next();
23+
}
24+
}
925
const username = socket.handshake.auth.username;
1026
if (!username) {
1127
return next(new Error("invalid username"));
1228
}
29+
socket.sessionID = randomId();
30+
socket.userID = randomId();
1331
socket.username = username;
1432
next();
1533
});
1634

1735
io.on("connection", (socket) => {
36+
// persist session
37+
sessionStore.saveSession(socket.sessionID, {
38+
userID: socket.userID,
39+
username: socket.username,
40+
connected: true,
41+
});
42+
43+
// emit session details
44+
socket.emit("session", {
45+
sessionID: socket.sessionID,
46+
userID: socket.userID,
47+
});
48+
49+
// join the "userID" room
50+
socket.join(socket.userID);
51+
1852
// fetch existing users
1953
const users = [];
20-
for (let [id, socket] of io.of("/").sockets) {
54+
sessionStore.findAllSessions().forEach((session) => {
2155
users.push({
22-
userID: id,
23-
username: socket.username,
56+
userID: session.userID,
57+
username: session.username,
58+
connected: session.connected,
2459
});
25-
}
60+
});
2661
socket.emit("users", users);
2762

2863
// notify existing users
2964
socket.broadcast.emit("user connected", {
30-
userID: socket.id,
65+
userID: socket.userID,
3166
username: socket.username,
67+
connected: true,
3268
});
3369

34-
// forward the private message to the right recipient
70+
// forward the private message to the right recipient (and to other tabs of the sender)
3571
socket.on("private message", ({ content, to }) => {
36-
socket.to(to).emit("private message", {
72+
socket.to(to).to(socket.userID).emit("private message", {
3773
content,
38-
from: socket.id,
74+
from: socket.userID,
75+
to,
3976
});
4077
});
4178

4279
// notify users upon disconnection
43-
socket.on("disconnect", () => {
44-
socket.broadcast.emit("user disconnected", socket.id);
80+
socket.on("disconnect", async () => {
81+
const matchingSockets = await io.in(socket.userID).allSockets();
82+
const isDisconnected = matchingSockets.size === 0;
83+
if (isDisconnected) {
84+
// notify other users
85+
socket.broadcast.emit("user disconnected", socket.userID);
86+
// update the connection status of the session
87+
sessionStore.saveSession(socket.sessionID, {
88+
userID: socket.userID,
89+
username: socket.username,
90+
connected: false,
91+
});
92+
}
4593
});
4694
});
4795

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/* abstract */ class SessionStore {
2+
findSession(id) {}
3+
saveSession(id, session) {}
4+
findAllSessions() {}
5+
}
6+
7+
class InMemorySessionStore extends SessionStore {
8+
constructor() {
9+
super();
10+
this.sessions = new Map();
11+
}
12+
13+
findSession(id) {
14+
return this.sessions.get(id);
15+
}
16+
17+
saveSession(id, session) {
18+
this.sessions.set(id, session);
19+
}
20+
21+
findAllSessions() {
22+
return [...this.sessions.values()];
23+
}
24+
}
25+
26+
module.exports = {
27+
InMemorySessionStore
28+
};

examples/private-messaging/src/App.vue

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,23 @@ export default {
3232
},
3333
},
3434
created() {
35+
const sessionID = localStorage.getItem("sessionID");
36+
37+
if (sessionID) {
38+
this.usernameAlreadySelected = true;
39+
socket.auth = { sessionID };
40+
socket.connect();
41+
}
42+
43+
socket.on("session", ({ sessionID, userID }) => {
44+
// attach the session ID to the next reconnection attempts
45+
socket.auth = { sessionID };
46+
// store it in the localStorage
47+
localStorage.setItem("sessionID", sessionID);
48+
// save the ID of the user
49+
socket.userID = userID;
50+
});
51+
3552
socket.on("connect_error", (err) => {
3653
if (err.message === "invalid username") {
3754
this.usernameAlreadySelected = false;

examples/private-messaging/src/components/Chat.vue

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,18 +68,25 @@ export default {
6868
});
6969
7070
const initReactiveProperties = (user) => {
71-
user.connected = true;
7271
user.messages = [];
7372
user.hasNewMessages = false;
7473
};
7574
7675
socket.on("users", (users) => {
7776
users.forEach((user) => {
78-
user.self = user.userID === socket.id;
77+
for (let i = 0; i < this.users.length; i++) {
78+
const existingUser = this.users[i];
79+
if (existingUser.userID === user.userID) {
80+
existingUser.connected = user.connected;
81+
return;
82+
}
83+
}
84+
user.self = user.userID === socket.userID;
7985
initReactiveProperties(user);
86+
this.users.push(user);
8087
});
8188
// put the current user first, and sort by username
82-
this.users = users.sort((a, b) => {
89+
this.users.sort((a, b) => {
8390
if (a.self) return -1;
8491
if (b.self) return 1;
8592
if (a.username < b.username) return -1;
@@ -88,6 +95,13 @@ export default {
8895
});
8996
9097
socket.on("user connected", (user) => {
98+
for (let i = 0; i < this.users.length; i++) {
99+
const existingUser = this.users[i];
100+
if (existingUser.userID === user.userID) {
101+
existingUser.connected = true;
102+
return;
103+
}
104+
}
91105
initReactiveProperties(user);
92106
this.users.push(user);
93107
});
@@ -102,13 +116,14 @@ export default {
102116
}
103117
});
104118
105-
socket.on("private message", ({ content, from }) => {
119+
socket.on("private message", ({ content, from, to }) => {
106120
for (let i = 0; i < this.users.length; i++) {
107121
const user = this.users[i];
108-
if (user.userID === from) {
122+
const fromSelf = socket.userID === from;
123+
if (user.userID === (fromSelf ? to : from)) {
109124
user.messages.push({
110125
content,
111-
fromSelf: false,
126+
fromSelf,
112127
});
113128
if (user !== this.selectedUser) {
114129
user.hasNewMessages = true;

0 commit comments

Comments
 (0)