Real time data - Steinberg99/DogeMeet GitHub Wiki

Real time data door Stein

Voor back-end heb ik gekozen voor de volgende topic:

#15 - Implement real-time data with socket.io
Socket.IO enables real-time, bidirectional and event-based communication. Very useful if you want to enable users to communicate (chat) with each other.

Zoals in de topic ook staat vermeld heb ik er voor gekozen om een chat functie uit te bouwen met socket.io. Het uiteindelijke eindresultaat ziet er als volgt uit:

Chat pagina

Wat is socket.io?

Socket.io is een npm package die een website in staat zet om real time data weer te geven. Dit wordt bereikt door een directe communicatie tussen de server en de client side javascript te faciliteren. Socket.io voert deze communicatie uit door zogenaamde events. Een event kan hier van alles zijn, maar in mijn geval is het het sturen van een chat bericht. Zodra er een bericht wordt gestuurd wordt deze vanuit de client naar de server gestuurd. De server kan dit bericht vervolgens weer doorsturen naar al de andere gebruikers die op dit moment verbonden zijn met de server. Een andere client kan bijvoorbeeld een bericht ontvangen waarna hij zijn DOM veranderd zodat dit nieuwe bericht wordt weergegeven. Op deze manier ontstaat er real time data die tussen meerdere clients wordt gedeeld.

Hoe heb ik het geïmplementeerd?

Zoals ik net vertelde moet er gebruik worden gemaakt van zowel de client (browser) als de server. Er is dus een onderscheid tussen de code die in de client draait en op de server draait. Ze zijn echter wel beide nodig om de chatberichten live weer te geven.

Stap 1: Het opzetten van de server voor Socket.IO

Voordat de client met de server kan praten moet hij eerst kunnen luisteren naar binnenkomende berichten. Hiervoor is het volgende stuk code nodig.

io.on("connection", (socket) => {
  socket.on("join", (chatId) => {
    socket.join(chatId);
  });

  socket.on("message-sent", async (data) => {
    let message = {
      sender_id: data.sender_id,
      content: data.content,
      date: data.date,
    };

    // Save message in the database
    await database
      .collection("chat_logs")
      .updateOne(
        { _id: ObjectId(data.chatId) },
        { $push: { messages: message } }
      );

    // Emit the message in the room
    io.sockets.in(data.chatId).emit("message-sent", data);
  });
});

In dit stuk code gebeuren vier dingen. Eerst wordt er een socket (de client) verbonden met de socket.io server. Vanaf dit moment wordt er vanuit de server naar twee events geluisterd. Het joinen van een room en het sturen van een bericht. Zodra de client een room joint (na het laden van de chatpagina) wordt hij in een aparte room gegooid. Deze room wordt aangemaakt met het gedeelde chatId van de chatlogs van de gebruikers. Op deze manier is het mogelijk om berichten alleen naar gebruikers te sturen die met elkaar in een chat zitten.

Zodra er een bericht binnenkomt wordt dit bericht opgeslagen in de database. Hiervoor wordt het chatId, senderId, de content en de datum van het bericht gebruikt. Er wordt een message object aangemaakt die vervolgens met het chatId wordt opgeslagen. Na het opslaan van het bericht wordt er vanuit de server een bericht naar de gebruikers gestuurd die verbonden zijn met de kamer (dit kan dus alleen de andere gebruiker zijn omdat hij de enige is die ook bij dit chatId kan). Deze "message-sent" event wordt nu dus doorgestuurd naar een andere client.

Stap 2: Het laden van de chat pagina zelf

Deze stap is redelijk voor zichzelf sprekend. Zodra de chat pagina wordt opgevraagd wordt er naar de url gekeken met welke hond de chat geopend moet worden. Vervolgens wordt er met het id uit de url en het id van de hond van de gebruiker de chat logs opgehaald. Deze worden vervolgens weergegeven in het chatscherm. Bij het laden van de chatpagina wordt er door wat inline JavaScript te gebruiken wat informatie naar de client gestuurd.

   const chatId = !{JSON.stringify(chatId)}
   const senderId = !{JSON.stringify(senderId)};
   const recieverId = !{JSON.stringify(recieverDoggo._id)};

Deze variabelen zijn namelijk nodig in de client side JavaScript om te kijken of een inkomend bericht van de client zelf is of van iemand anders. Ook zijn ze nodig bij het versturen van een bericht. Deze informatie wordt dus vanuit de server aan de client side JavaScript doorgegeven.

Stap 3: Het opzetten van de client side JavaScript code

Voor het ontvangen van berichten moet het volgende stuk client side JavaScript worden geïmplementeerd. Eerst wordt hier het "join" event afgevuurd waarna de gebruiker in de server verbonden zal worden met een privé kamer. Vervolgens gaat de client luisteren naar binnenkomende berichten. Zodra er een bericht binnenkomt wordt het "message-sent" event uitgevoerd. Dit event voegt een nieuw bericht toe aan de messages list. Er wordt hier ook gekeken of het bericht door de gebruiker zelf of door de andere gebruiker is verstuurd.

socket.emit("join", chatId);

socket.on("message-sent", (data) => {
  let message = document.createElement("li");
  if (data.sender_id == senderId) {
    message.classList.add("sent-message");
  } else {
    message.classList.add("recieved-message");
  }

  message.innerHTML = `
   <span>${getDateString(new Date(data.date))}</span>
   <p>${data.content}</p>
 `;

  messages.appendChild(message);
  window.scrollTo(0, document.body.scrollHeight);
});

Het andere wat de client side code moet verrichten is het versturen van de berichten. Hiervoor wordt het volgende stuk code toegepast. Zodra de gebruiker een bericht stuurt wordt er een data object aangemaakt. Vervolgens wordt het "message-sent" event afgevuurd. Dit event wordt door de server herkend. De server kan het bericht nu doorsturen naar de client en de andere gebruiker.

form.addEventListener("submit", (e) => {
  e.preventDefault();
  if (input.value) {
    let data = {
      chatId: chatId,
      sender_id: senderId,
      reciever_id: recieverId,
      content: input.value,
      date: new Date(),
    };
    socket.emit("message-sent", data);
    input.value = "";
  }
});

Mogelijke verbeteringen

Mijn chat feature is natuurlijk nog niet helemaal perfect. Er zijn namelijk nog een aantal verbeteringen die hem nog beter zouden kunnen maken.

  • Een notificatie zodra iemand aan het typen is. Hiervoor zou ik ook een socket.io event kunnen aanmaken die afgaat zodra de gebruiker een teken neerzet in het input veld. Op deze manier zou ik aan de andere gebruiker kunnen doorgeven dat de gebruiker aan het typen is.
  • Het versturen van foto's of andere bestanden. Hiervoor zou ik iets moeten doen met een file input button in combinatie met een socket.io event.
  • Het verwijderen van een chat bericht. Hiervoor moet zoals ik net aangaf wederom een socket.io event worden afgevuurd die direct een bericht verwijdert bij zowel de ontvanger als de verstuurder. Dit bericht zou dan ook uit de database verwijderd moeten worden.
  • Het blokkeren en rapporteren van een andere gebruiker. Dit zou buiten socket.io om geregeld kunnen worden door een blocked_users array aan te maken in het user bestand.
  • Het voorkomen dat gebruikers chats kunnen starten met gebruikers waarmee ze niet zijn gematched. Op dit moment kan een gebruiker in de url een id zetten van een hond waarmee hij niet is gematched. Omdat er voor deze honden nog geen chat log is aangemaakt zal de code vastlopen. Dit zou voorkomen kunnen worden door eerst te controleren of de gebruiker wel gematched is met de hond uit de url.

Bronnen

⚠️ **GitHub.com Fallback** ⚠️