Part 1. Getting Connected
WebSockets are used for various interactions.
To start: the WebSocket is a AWS lambda WebSocket, and as with everything in the development of Weave, uses typescript.
first here is thee WebSocket side code:
import { APIGatewayProxyEvent } from "aws-lambda";
import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient();
export const connect = async (event: APIGatewayProxyEvent) => {
const connectionId = event.requestContext.connectionId;
await prisma.wSConnection.create({
data: {
connectionID: connectionId,
},
});
return { statusCode: 200, body: JSON.stringify({ connectionId }) };
};All that is accomplished here is the creation of an entry in a database and returns the connectionID (was simply used for debugging), AWS holds open the connection by default, and the reason for the entry is for propagation of messages to specific channels which we will get to later.
Here is the client side code for the interaction:
useEffect(() => {
if (!socketRef.current) {
const newSocket = new WebSocket(process.env.NEXT_PUBLIC_WEBSOCKET as string);
socketRef.current = newSocket;
setSocket(newSocket);
}
if (socket) {
socket.onopen = () => {
console.log("Socket opened");
};
socket.onclose = () => {
if (socket?.readyState !== WebSocket.OPEN) {
socketRef.current = null;
setSocket(null);
}
};
return () => {
socket?.close();
};
}
}, [socket]);Very simple and straightforward, it connects when the page loads (see here) and it manages reconnects if a disconnect happens.
So at this point we have a connection to the Weave WebSocket, and an entry of a connectionID in a database. The user is also not in a specific server or channel.
When the WebSocket state changes to connected - stored like so -
const [socket, setSocket] = useState<WebSocket | null>(null);
- this useEffect is fired to update the WebSocket with the users id (and potentially the server channel if the user is viewing one - this is used during reconnections )
const socketUserUpdate = () => {
socket?.send(
JSON.stringify({
senderID: currentUser?.id,
updateType: "user",
})
);
};
useEffect(() => {
if (socket && socket.readyState === WebSocket.OPEN && currentUser) {
if (selectedChannel) {
socketChannelUpdate();
} else {
socketUserUpdate();
}
}
}, [socket, currentUser]);Next, if the user clicks on a server channel, the following code is executed.
const socketChannelUpdate = async () => {
socket?.send(
JSON.stringify({
senderID: currentUser?.id,
channelID: selectedChannel?.id,
updateType: "channel",
})
);
};And for the previous two functions this is how it is handled on the WebSocket side of things
import { APIGatewayProxyEvent } from "aws-lambda";
import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient();
type payloadType = {
senderID: string;
channelID?: number;
updateType: string;
conversationID?: number;
};
export async function handler(event: APIGatewayProxyEvent) {
let payload: payloadType = JSON.parse(event.body);
const updateType = payload.updateType;
const senderID = payload.senderID;
const connectionId = event.requestContext.connectionId;
switch (updateType) {
case "channel":
if (payload.conversationID) {
await prisma.wSConnection.update({
where: {
connectionID: connectionId,
},
data: {
conversationID: payload.conversationID,
userId: senderID,
channelID: null,
},
});
} else {
const channelID = payload.channelID;
await prisma.wSConnection.update({
where: {
connectionID: connectionId,
},
data: {
conversationID: null,
channelID: channelID,
userId: senderID,
},
});
}
break;
case "user":
await prisma.wSConnection.update({
where: {
connectionID: connectionId,
},
data: {
userId: senderID,
},
});
}
return { statusCode: 200, body: "Connection updated." };
}And thats all for the connections! 🎉
Now the user is subscribed to a channel and any messages sent by other users in the same channel will be pushed to them!
