dappros/ethora

View on GitHub
client-web/src/xmppHandler.ts

Summary

Maintainability
D
1 day
Test Coverage
import {
  replaceMessageListItemProps,
  TActiveRoomFilter,
  TMessageHistory,
  TRoomRoles,
  TUserChatRooms,
  useStoreState,
} from "./store";
import { Element } from "ltx";
import { defaultChats } from "./config/config";
import { CONFERENCEDOMAIN } from "./constants";
import { xml } from "@xmpp/client";
import { sendBrowserNotification } from "./utils";
import { createMessage } from "./utils/createMessage";
import { history } from "./utils/history";
import { walletToUsername } from "./utils/walletManipulation";
import { XmppClass } from "./xmpp";

export class XmppHandler {
  public lastMsgId: string = "";
  public isGettingMessages: boolean = false;
  public isGettingFirstMessages: boolean = false;
  private temporaryMessages: TMessageHistory[] = [];
  private temporaryReplaceMessages: replaceMessageListItemProps[] = [];
  private lastRomJIDLoading: string = "";

  getRoomGroup = (jid: string, userCount: number): TActiveRoomFilter => {
    const splittedJid = jid.split("@")[0];
    if (defaultChats[splittedJid]) {
      return "official";
    }
    return "groups";
  };

  onRealtimeMessage = async (stanza: Element) => {
    if (stanza.attrs.id === "sendMessage") {
      const body = stanza?.getChild("body");
      const data = stanza?.getChild("data");
      const replace = stanza?.getChild("replace");
      const archived = stanza?.getChild("archived");

      const id = stanza.getChild("archived")?.attrs.id;
      if (!data || !body || !id) {
        return;
      }

      if (
        !data.attrs.senderFirstName ||
        !data.attrs.senderLastName ||
        !data.attrs.senderJID
      ) {
        return;
      }

      const msg = createMessage(data, body, id, stanza.attrs.from);
      const blackList = useStoreState
        .getState()
        .blackList.find((item) => item.user === msg.data.senderJID);

      if (blackList) {
        return;
      }

      if (replace) {
        const replaceMessageId = Number(replace.attrs.id);
        const messageString = body.getText();
        useStoreState
          .getState()
          .replaceMessage(replaceMessageId, messageString);
      }

      if (data.attrs.isReply) {
        const messageId = Number(msg.data?.mainMessage?.id);
        useStoreState.getState().setNumberOfReplies(messageId);
      }
      useStoreState.getState().updateCounterChatRoom(data.attrs.roomJid);
      useStoreState.getState().updateMessageHistory(msg);
      sendBrowserNotification(msg.body, () => {
        history.push("/chat/" + msg.roomJID.split("@")[0]);
      });
    }
  };

  onMessageHistory = async (stanza: any) => {
    if (
      stanza.is("message") &&
      stanza.children[0].attrs.xmlns === "urn:xmpp:mam:2"
    ) {
      const body = stanza
        .getChild("result")
        ?.getChild("forwarded")
        ?.getChild("message")
        ?.getChild("body");
      const data = stanza
        .getChild("result")
        ?.getChild("forwarded")
        ?.getChild("message")
        ?.getChild("data");
      const delay = stanza
        .getChild("result")
        ?.getChild("forwarded")
        ?.getChild("delay");
      const replace = stanza
        .getChild("result")
        ?.getChild("forwarded")
        ?.getChild("message")
        ?.getChild("replace");

      const id = stanza.getChild("result")?.attrs.id;
      if (!data || !body || !delay || !id) {
        return;
      }

      if (
        !data.attrs.senderFirstName ||
        !data.attrs.senderLastName ||
        !data.attrs.senderJID
      ) {
        return;
      }

      const msg = createMessage(data, body, id, stanza.attrs.from);

      // console.log('TEST ', data.attrs)
      const blackList = useStoreState
        .getState()
        .blackList.find((item) => item.user === msg.data.senderJID);

      if (blackList) {
        return;
      }
      //if current stanza has replace tag
      if (replace) {
        //if message loading
        if (this.isGettingMessages) {
          const replaceItem: replaceMessageListItemProps = {
            replaceMessageId: Number(replace.attrs.id),
            replaceMessageText: body.getText(),
          };
          //add the replace item, which has the id of the main message to be edited, in a temporory array
          this.temporaryReplaceMessages.push(replaceItem);
        }
        //if message loading done
        if (!this.isGettingMessages) {
          const replaceMessageId = Number(replace.attrs.id);
          const messageString = body.getText();
          //replace body/text of message id in messageHistory array
          useStoreState
            .getState()
            .replaceMessage(replaceMessageId, messageString);
        }
      } else {
        this.temporaryMessages.push(msg);

        if (!this.isGettingMessages) {
          //check for messages in temp Replace message array agains the current stanza message id
          const replaceItem = this.temporaryReplaceMessages.find(
            (item) => item.replaceMessageId === msg.id
          );
          //if exists then replace the body with current stanza body
          if (replaceItem) {
            msg.body = replaceItem.replaceMessageText;
          }
          useStoreState.getState().setNewMessageHistory(msg);
          useStoreState.getState().sortMessageHistory();
        }

        const untrackedRoom = useStoreState.getState().currentUntrackedChatRoom;
        if (
          stanza.attrs.to.split("@")[0] !==
            data.attrs.senderJID.split("@")[0] &&
          stanza.attrs.from.split("@")[0] !== untrackedRoom.split("@")[0] &&
          !this.isGettingFirstMessages &&
          data.attrs.roomJid
        ) {
          useStoreState.getState().updateCounterChatRoom(data.attrs.roomJid);
        }
        if (data.attrs.isReply) {
          const messageid = msg.data.mainMessage?.id;
          useStoreState.getState().setNumberOfReplies(messageid);
          this.updateTemporaryMessagesRepliesCount(messageid);
        }
      }
    }
  };
  updateTemporaryMessagesRepliesCount = (messageId: number) => {
    const messageIndex = this.temporaryMessages.findIndex(
      (item) => item.id === messageId
    );
    if (!messageId || isNaN(messageId) || messageIndex === -1) {
      return;
    }
    const threadMessages = this.temporaryMessages.filter(
      (item) => item.data.mainMessage?.id === messageId
    );
    this.temporaryMessages[messageIndex].numberOfReplies =
      threadMessages.length;
  };

  onLastMessageArchive = (stanza: Element, xmpp: any) => {
    if (
      stanza.attrs.id === "paginatedArchive" ||
      stanza.attrs.id === "GetArchive"
    ) {
      this.lastMsgId = String(
        stanza.getChild("fin")?.getChild("set")?.getChild("last")?.children[0]
      );

      if (this.isGettingMessages) {
        useStoreState.getState().updateMessageHistory(this.temporaryMessages);
        this.isGettingMessages = false;

        this.temporaryMessages.forEach((item) => {
          if (item.data.isSystemMessage && item.data.tokenAmount > 0) {
            useStoreState
              .getState()
              .updateCoinsInMessageHistory(
                Number(item.data.receiverMessageId),
                String(item.data.senderJID),
                Number(item.data.tokenAmount)
              );
          }
        });

        this.temporaryReplaceMessages.forEach((item) => {
          useStoreState
            .getState()
            .replaceMessage(item.replaceMessageId, item.replaceMessageText);
        });

        useStoreState.getState().setLoaderArchive(false);
        this.temporaryMessages = [];
        this.isGettingFirstMessages = false;
      }

      if (
        this.lastRomJIDLoading &&
        this.lastRomJIDLoading === stanza.attrs.from
      ) {
        useStoreState.getState().setLoaderArchive(false);
        this.lastRomJIDLoading = "";
        this.isGettingFirstMessages = false;
      }
    }
  };

  onGetLastMessageArchive = (stanza: Element, xmpp: any) => {
    if (stanza.attrs.id === "sendMessage") {
      const data = stanza.getChild("stanza-id");
      if (data) {
        xmpp.getLastMessageArchive(data.attrs.by);
        return;
      }
      return this.onMessageHistory(stanza);
    }
  };

  onGetRoomInfo = (stanza: Element | any) => {
    const userChatRooms = useStoreState.getState().userChatRooms;
    const currentRoomData = userChatRooms.filter(
      (e) => e.jid === stanza.attrs.from
    )[0];
    if (stanza.attrs.id === "roomInfo") {
      if (stanza.children[0] && currentRoomData) {
        const featureList = stanza.children[0].children.find(
          (item) => item.attrs.xmlns === "jabber:x:data"
        );
        const roomDescription = featureList.children.find(
          (item) => item.attrs.var === "muc#roominfo_description"
        ).children[0]?.children[0];
        const roomName = featureList.children.find(
          (item) => item.attrs.var === "muc#roomconfig_roomname"
        ).children[0]?.children[0];

        const roomData = {
          jid: currentRoomData.jid,
          name: roomName,
          room_background: currentRoomData.room_background,
          room_thumbnail: currentRoomData.room_thumbnail,
          users_cnt: currentRoomData.users_cnt,
          unreadMessages: currentRoomData.unreadMessages,
          composing: currentRoomData.composing,
          toUpdate: currentRoomData.toUpdate,
          description: roomDescription,
        };
        useStoreState.getState().updateUserChatRoom(roomData);
      }
    }
  };

  onGetRoomMemberInfo = (stanza: Element | any) => {
    if (stanza.attrs.id === "roomMemberInfo") {
      if (stanza.children.length) {
        const info = stanza.children[0].children.map((item) => item.attrs);
        useStoreState.getState().setRoomMemberInfo(info);
      }
    }
  };

  onChangeDescription = (stanza: Element, xmpp: any) => {
    if (stanza.attrs.id === "changeRoomDescription") {
      // console.log(stanza)
      xmpp.getRoomInfo(stanza.attrs.from);
    }
  };

  onChangeRoomName = (stanza: Element, xmpp: any) => {
    if (stanza.attrs.id === "changeRoomName") {
      xmpp.getRoomInfo(stanza.attrs.from);
    }
  };

  onPresenceInRoom = (stanza: Element | any) => {
    if (stanza.attrs.id === "presenceInRoom") {
      const roomJID: string = stanza.attrs.from.split("/")[0];
      const role: string = stanza?.children[1]?.children[0]?.attrs.role;
      const elementObject: TRoomRoles = { roomJID: roomJID, role: role };
      useStoreState.getState().setRoomRoles(elementObject);
    }
  };

  connectToUserRooms = (stanza: Element, xmpp: any) => {
    if (stanza.attrs.id === "getUserRooms") {
      if (stanza.getChild("query")?.children) {
        this.isGettingFirstMessages = true;
        useStoreState.getState().setLoaderArchive(true);
        let roomJID: string = "";
        stanza.getChild("query")?.children.forEach((result: any) => {
          const currentChatRooms = useStoreState.getState().userChatRooms;

          if (result?.attrs.name) {
            const currentSavedChatRoom = currentChatRooms.filter(
              (el) => el.jid === result?.attrs.jid
            );
            if (
              currentSavedChatRoom.length === 0 ||
              currentSavedChatRoom[0].toUpdate
            ) {
              roomJID = result.attrs.jid;
              xmpp.presenceInRoom(roomJID);
              const roomData: TUserChatRooms = {
                jid: roomJID,
                name: result?.attrs.name,
                room_background: result?.attrs.room_background,
                room_thumbnail: result?.attrs.room_thumbnail,
                users_cnt: result?.attrs.users_cnt,
                unreadMessages: 0,
                composing: "",
                toUpdate: false,
                description: "",
                group: this.getRoomGroup(roomJID, +result?.attrs.users_cnt),
              };
              if (
                currentSavedChatRoom.length > 0 &&
                currentSavedChatRoom[0].toUpdate
              ) {
                useStoreState.getState().updateUserChatRoom(roomData);
              } else {
                useStoreState.getState().setNewUserChatRoom(roomData);
              }
              //get message history in the room
              xmpp.getRoomArchiveStanza(roomJID, 1);
              this.lastRomJIDLoading = roomJID;
            }
          }
        });
      }
    }
  };

  onComposing = (stanza: Element) => {
    if (
      stanza.attrs.id === "isComposing" ||
      stanza.attrs.id === "pausedComposing"
    ) {
      const requestType = stanza.attrs.id;
      const recipientID = String(stanza.attrs.to).split("@")[0];
      const senderID = stanza.getChild("data").attrs.manipulatedWalletAddress;

      if (recipientID === walletToUsername(senderID)) {
        return;
      }

      if (requestType === "isComposing") {
        useStoreState
          .getState()
          .updateComposingChatRoom(
            stanza.attrs.from.toString().split("/")[0],
            true,
            stanza.getChild("data").attrs.fullName
          );

        setTimeout(
          () =>
            useStoreState
              .getState()
              .updateComposingChatRoom(
                stanza.attrs.from.toString().split("/")[0],
                false
              ),
          1500
        );
      }

      if (stanza.attrs.id === "pausedComposing") {
        useStoreState
          .getState()
          .updateComposingChatRoom(
            stanza.attrs.from.toString().split("/")[0],
            false
          );
      }
    }
  };

  getListOfRooms = (xmpp: any) => {
    useStoreState.getState().clearUserChatRooms();
    useStoreState.getState().setCurrentUntrackedChatRoom("");
    useStoreState.getState().clearBlackList();

    xmpp.client.send(xml("presence"));
    xmpp.getArchive(xmpp.client?.jid?.toString());
    Object.keys(defaultChats).forEach((roomJID) => {
      xmpp.presenceInRoom(roomJID + CONFERENCEDOMAIN);
    });
    xmpp.getRooms();
    xmpp.getBlackList();
    useStoreState.getState().clearMessageHistory();
  };

  onInvite = (stanza: Element, xmpp: any) => {
    if (stanza.is("message")) {
      const isArchiveInvite = stanza
        .getChild("result")
        ?.getChild("forwarded")
        ?.getChild("message")
        ?.getChild("x")
        ?.getChild("invite");
      if (stanza.getChild("x")?.getChild("invite") || isArchiveInvite) {
        let jid = stanza.attrs.from;
        if (isArchiveInvite) {
          jid = stanza
            .getChild("result")
            ?.getChild("forwarded")
            ?.getChild("message").attrs.from;
        }
        const currentChatRooms = useStoreState.getState().userChatRooms;
        if (currentChatRooms.filter((el) => el.jid === jid).length === 0) {
          xmpp.subsribe(jid);
          xmpp.presenceInRoom(jid);
          xmpp.getRooms();
        }
      } else {
      }
    } else {
    }
  };

  onBlackList = (stanza: Element, xmpp: any) => {
    if (stanza.attrs.id === "blackList") {
      const blackList = stanza?.getChild("query").children.map((item: any) => ({
        date: Number(item.attrs.date),
        fullName: item.attrs.fullname,
        user: item.attrs.user,
      }));
      useStoreState.getState().saveInBlackList(blackList);
    }
  };

  onRemoveFromBlackList = (stanza: Element, xmpp: any) => {
    if (stanza.attrs.id === "removeFromBlackList") {
      console.log(stanza);
    }
  };
  onNewSubscription = (stanza: Element, xmpp: XmppClass) => {
    if (stanza.attrs.id === "newSubscription") {
      xmpp.getRooms();
    }
  };
  onRoomDesignChange = (stanza: Element, xmpp: XmppClass) => {
    if (stanza.attrs.id === "unsubscribe") {
      xmpp.getRooms();
    }
    if (
      stanza.attrs.id === "setRoomImage" ||
      stanza.attrs.id === "setRoomBackground"
    ) {
      xmpp.getRoomInfo(stanza.attrs.from);
    }
  };

  onBan = (stanza: Element) => {
    if (stanza.attrs.id === "ban") {
      console.log(stanza, "ban stanza");
    }
  };

  //when messages are edited in realtime then capture broadcast with id "replaceMessage" and replace the text.
  onSendReplaceMessageStanza = (stanza: any) => {
    if (stanza.attrs.id === "replaceMessage") {
      const replaceMessageId = Number(
        stanza.children.find((item) => item.name === "replace").attrs.id
      );
      const messageString = stanza.children.find((item) => item.name === "body")
        .children[0];
      useStoreState.getState().replaceMessage(replaceMessageId, messageString);
    }
  };

  //when delete request is sent this listener will capture
  onDeleteMessageStanza = (stanza: any) => {
    if (stanza.attrs.id === "deleteMessageStanza") {
      console.log(stanza, "delete response");
    }
  };
}