post-cover
Weave pt.2
Websockets
Written Tue Jul 25 2023
By Michael Freno
39 Hits
Weave pt.2
Websockets

Part 2. Sending Messages

The most straightforward and obvious use of a WebSocket used by Weave is to send and push messages, here is the code executed on the sender side.

const sendMessage = async (e: React.FormEvent) => {
  e.preventDefault();
  if (messageInputRef.current && socket) {
    const input = messageInputRef.current.value;
    if (input.length > 0) {
      setIconClass("move-fade");
      const data = {
        message: input,
        senderID: currentUser.id,
        channelID: selectedChannel.id,
        action: "message",
      };
      socket.send(JSON.stringify(data));
      messageInputRef.current.value = "";
      setTimeout(() => {
        setIconClass("");
      }, 500);
    }
  }
};

The important bit is

const data = {
  message: input,
  senderID: currentUser.id,
  channelID: selectedChannel.id,
  action: "message",
};

The way this is consumed by the WebSocket is as follows: keep in mind, this is not optimized it simply works

if (payload.conversationID) {
//first check for a conversationID, this indicates a Direct Message,
//not what is specifically being covered in this article
}else if (payload.channelID) {
    const channelID = payload.channelID;
    let comment: Comment;
    if (!payload.reaction) {
      //negative check for a reaction
      comment = await prisma.comment.create({
        data: {
          message: message,
          channelID: channelID,
          userId: senderID,
        },
      });
    } else {
     //this is where reactions are handled, not going over it yet
    }
    //find other connections to the same channel
    const connections = await prisma.wSConnection.findMany({
      where: {
        channelID: channelID,
      },
    });
    //error check
    if (connections.length > 0 && (comment || payload.reaction)) {
      await Promise.all(
        connections.map(async (connection) => {
          try {
            const output = {
              ConnectionId: connection.connectionID,
              Data: JSON.stringify(payload.reaction ? "refresh" : comment),
            };
            await client.postToConnection(output).promise();
          } catch (e) {
            if (e.statusCode === 410) {
              // If a connection is no longer available, delete it from the database.
              await prisma.wSConnection.delete({
                where: { connectionID: connection.connectionID },
              });
            }
          }
        })
      );
    }
  }

await Promise.all(x) wraps the contents in an async await pattern so that all the contained code is executed before moving on. The code here being wrapped is the pushing of the comment to each other connection in the same channel.

That was the simplest implementation of using the WebSocket. As all that happens is:

  1. user 1 sends message

  2. lambda WebSocket check for channel message or direct message

  3. check if the message is a reaction or comment

  4. create new comment entry using prisma

  5. find all connections (includes user 1) written in database

  6. map over each connection (user 1...n) and push each connection a message "refresh" string.

The reason for not sending the actual message, was due to not knowing at the time how to handle (sending/parsing) all the data needed client side. Instead, once the message is received, a new request to the db is made to get the new message.

  useEffect(() => {
    if (socket) {
      socket.onmessage = async (event) => {
        console.log(event.data);
        if (event.data.message !== "Internal server error") {
          getMessagesQuery.refetch();
        }
      };
    }
  }, [socket]);

getMessagesQuery.refetch() is a trpc query function. I really enjoyed working with trpc, however I have not used it on another site since Weave as I've been working with Next13 (app dir) since and trpc (and other bits, NextAuth, prisma etc. used to make Weave) don't play great with Next13 yet, but also some, prisma for instance, have other performance costs that I wanted to ditch.

Comments
No Comments Yet