0.1.4 • Published 1 year ago

worthwhile-chat-app v0.1.4

Weekly downloads
-
License
-
Repository
-
Last release
1 year ago

Name

Worthwhile Chat App

Description

Worthwhile Chat App is a real-time Vue Chat Application that uses Socket.io, Tailwind, and Vite to allow communication between two users.

Features

  • Compatible with Vue framework
  • Customizable Real-time Chat Messaging
  • Editable Messages
  • Message Timestamps
  • Real-time typing indicator
  • Customizable text and background colors
  • Inbox Message List (List of the existing chats for the user)

Table of Contents

  • Installation
  • Usage
  • Props data structure
  • Required Methods

Installation

# Using npm
npm install --save worthwhile-chat-app

Usage

There are two components in this package:

  • InboxMessages
  • MessageList

You can import both as custom components in your Vue Project. Example Usage for InboxMessages:

<script>
import { useUser } from "@/stores/userStore";
import { useMessage } from "@/stores/messageStore";
import { useInvitation } from "@/stores/invitationStore";
import { InboxMessages } from "worthwhile-chat-app";

export default {
  name: "InboxMessagesChat",
  components: { InboxMessages },
  data() {
    return {
      messages: [],
      userStore: useUser(),
      invitationStore: useInvitation(),
      messageStore: useMessage(),
      sender: {},
      port: null,
    };
  },
  computed: {
    currentMessages() {
      return this.messageStore.messages;
    },
  },
  mounted() {
    if (this.messageStore?.selectedMessageInvitationInfo) {
      this.sender = {
        invitation_id: this.messageStore?.selectedMessageInvitationInfo.invitation_id,
        sender_name: this.userStore?.currentUser?.name,
        sender_image: "https://picsum.photos/200/300",
        userOne: {
          userOne_id: this.messageStore?.selectedMessageInvitationInfo?.userOne.userOne_id,
          name: this.messageStore?.selectedMessageInvitationInfo?.userOne.name,
          phone: this.messageStore?.selectedMessageInvitationInfo?.userOne.phone,
          email: this.messageStore?.selectedMessageInvitationInfo?.userOne.email,
        },
        userTwo: {
          userTwo_id: this.messageStore?.selectedMessageInvitationInfo?.userTwo.userTwo_id,
          name: this.messageStore?.selectedMessageInvitationInfo?.userTwo.name,
          phone: this.messageStore?.selectedMessageInvitationInfo?.userTwo.phone,
          email: this.messageStore?.selectedMessageInvitationInfo?.userTwo.email,
        },
        message: this.message,
        status: "active",
        read: false,
        edited: false,
      };
    }
    this.port = 4000;
  },
  methods: {
    createNewMessage(sender) {
      this.messageStore.createMessage(sender);
    },
    getAllMessages() {
      this.messageStore
        .readMessages(
          this.messageStore.selectedMessageInvitationInfo?.invitation_id,
          "invitation_id"
        )
        .then(() => {
          this.messageStore.messages.forEach((message) => this.messages.push(message));
        });
    },
    updateMessage(messageId, newMessage, messageItem) {
      const updatedMessage = {
        message: newMessage.message,
        edited: true,
      };

      this.messageStore.updateMessage(messageId, updatedMessage, messageItem).then(() => {
        this.getAllMessages();
        this.$nextTick(() => {
          document.getElementById(`${messageItem._id}`).scrollIntoView({
            behavior: "smooth",
            block: "center",
            inline: "center",
          });
        });
      });

      this.editedMessage = null;
      this.newMessage = null;
      this.hovered = null;
    },
  },
};
</script>
<template>
  <div class="overflow-y-scroll">
    <InboxMessages
      :port="port"
      :chatroom="messageStore.selectedMessageInvitationInfo?.invitation_id"
      :current-user="userStore.currentUser"
      :selected-message="messageStore.selectedMessageInvitationInfo"
      :sender-information="sender"
      :existing-messages="currentMessages"
      @get-all-messages="getAllMessages"
      @create-new-message="createNewMessage"
      @update-message="updateMessage"
    />
  </div>
</template>
<style lang="scss">
.message-height {
  height: 100vh;
  box-sizing: border-box;
  @media screen and (min-width: 768px) {
    height: calc(100vh - 300px);
  }
}
</style>

Example Usage of MessageList Component:

<script setup>
import { onMounted, computed } from "vue";
import { useView } from "@/stores/viewStore";
import { useMessage } from "@/stores/messageStore";
import { useUser } from "@/stores/userStore";
import _ from "lodash";
import { MessageList } from "worthwhile-chat-app";
const viewStore = useView();
const messageStore = useMessage();
const userStore = useUser();

onMounted(() => {
  messageStore.readMessages(userStore.currentUser._id, userStore.userType);
});

const messageList = computed(() => {
  return _.uniqBy(messageStore.messageList, "invitation_id");
});

function getMessages(message) {
  if (viewStore.isMobile) {
    userStore.showFilterPanel = !userStore.showFilterPanel;
  }
  console.log("get messages", message);

  messageStore.selectedMessageInvitationInfo = {
    invitation_id: message.invitation_id,
    userOne: {
      userOne_id: message.userOne.userOne_id,
      name: message.userOne.name,
      phone: message.userOne.phone,
      email: message.userOne.email,
    },
    userTwo: {
      userTwo_id: message.userTwo.userTwo_id,
      name: message.userTwo.name,
      phone: message.userTwo.phone,
      email: message.userTwo.email,
    },
    sender_name: message.sender_name,
    sender_image: message.sender_image,
  };
  messageStore.readMessages(message.invitation_id, "invitation_id");
  viewStore.search = "messages";
}

function getAllUserMessages() {
  messageStore.readMessages(userStore.currentUser._id, userStore.userType);
}
</script>
<template>
  <MessageList
    :message-list="messageList"
    identifier-id="invitation_id"
    user-one="userOne"
    user-two="userTwo"
    message-background-color="bg-gray-100"
    max-width="max-width: 75px"
    name-color="text-gray-900"
    identifier-color="text-gray-900"
    :user-one-identifier="userStore.isUserOne"
    :current-user="userStore.currentUser"
    @get-all-user-messages="getAllUserMessages"
    @get-messages="getMessages"
  />
</template>

Required Props and Data Structures

InboxMessages Component

The following props are required: | Props | Type | Required |--------------------|--------|----------| | port | String | true | | chatroom | String | true | | current-user | Object | true | | selected-chat | Object, String | true | | sender-information | Object | true | | token | String | true | | roles | Array | false | | userId | String | true | | existing-messages | Array | true |

(1) port (ex: http://localhost:4000)

(2) chatroom Identifies which room the users will join. You will want this to be something in common between the users, such as a request ID that joins the two users.

(3) current-user This is an object with the currentUser information. See data structure below.

(4) selected-chat This is the indicator for which specific chat is selected. It can be either an Object or String. The component simply checks if it exists.

(5)sender-information This is an object with the sender information. See data structure below.

(6) existing-messages This is an array of all the messages that have already been persisted to the database.

Data Structure for InboxMessages Props

The following only illustrates what is required for the component to work correctly. Including more properties in the object will not break the code.

Current User

{
  name: {
    first: "First",
    last: "Last"
  }
}

Sender Information

{
  invitation_id: "68asdfasdf6842",
  sender_name: this.userStore?.currentUser?.name,
  sender_image: "https://picsum.photos/200/300",
  userOne: {
    userOne_id: this.messageStore?.selectedMessageInvitationInfo?.userOne.userOne_id,
    name: {
      first: "First",
      last: "Last"
    },
    phone: this.messageStore?.selectedMessageInvitationInfo?.userOne.phone,
    email: this.messageStore?.selectedMessageInvitationInfo?.userOne.email,
  },
  userTwo: {
    userTwo_id: this.messageStore?.selectedMessageInvitationInfo?.userTwo.userTwo_id,
    name: {
      first: "First",
      last: "Last"
    },
    phone: this.messageStore?.selectedMessageInvitationInfo?.userTwo.phone,
    email: this.messageStore?.selectedMessageInvitationInfo?.userTwo.email,
  },
}

** Object should have _id and updatedAt properties as well, this should be automatically assigned in Mongo. If not, you will need to also include those.

(1) invitation_id This can be named anything, this is what identifies the common identifier between the two parties. This property will need to be the same name as the identifierId prop for MessageList component and a String value. (2) userOne This can be named anything, this property will be the identifier for User One (will need to be the name as userOne prop for MessageList component) (3) userTwo This can be named anything, this property will be the identifier for User Two (will need to be the name as userTwo prop for MessageList component)

InboxMessages Component

The following props are required:

PropsTypeRequired
chat-listArraytrue
identifier-idStringtrue
user-one-identifierBooleantrue
current-userObjecttrue
user-oneStringtrue
user-twoStringtrue
message-background-color*Stringfalse
hover-message-background-color*Stringfalse
max-widthStringfalse
name-color*Stringfalse
identifier-color*Stringfalse

**must be Tailwind values (ex. text-gray-100)

(1) chat-list is the filtered list of chats that for the current user.

(2) identifier-id is a String value and must equal the name of the property that was assigned in the SenderInformation object in the InboxMessages component. This would be the 'invitation_id' in the SenderInformation data structure example.

(3) user-one-identifier is a Boolean value that identifies userOne from userTwo. For example, pass a computed property that checks if userOne is the userOne versus the professional.

(4) current-user This is an object with the currentUser information. This should be the same current-user value that was passed to InboxMessages Component. See data structure below.

(5) userOne This must match the property name that was used in the SenderInformation Object in the InboxMessages Component. For example, 'userOne'.

(6) userTwo This must match the property name that was used in the SenderInformation Object in the InboxMessages Component. For example, 'userTwo'.

(7) message-background-color This is an optional background color for each item in the chat-list

(8) hover-message-background-color This is an optional hover background color for each item in the chat-list

(9) max-width Sets the max-width for each chat list item. For example, pass in "max-width: 75px"

(10) name-color Sets the color of the Sender name.

(10) identifier-color Sets the color of the identifierId property.

Data Structure for MessageList Props

Current User

{
  name: {
    first: "First",
    last: "Last"
  }
}

Required Methods to pass to Components

InboxMessages

You must pass the following methods to InboxMessages:

@get-all-messages="YourMethod" *This function should send a GET request for all the existing messages from the database. Then you will want to update the existing-messages property that you're passing to InboxMessages Component with this response data.

@create-new-message="YourMethod" @update-message="YourMethod"

*See comments below in Example Usage for descriptions

Example Usage:

<script>
   getAllMessages() {
      this.messageStore
        .readMessages(
          this.messageStore.selectedMessageInvitationInfo?.invitation_id,
          "invitation_id"
        )
        .then(() => {
          this.messageStore.messages.forEach((message) => this.messages.push(message));
        });
    },

    //*_This function should send a POST request 
    //to create a new message in the database._ 
    createNewMessage(sender) {
      this.messageStore.createMessage(sender);
    },

    //This function should send a PUT request 
    //to update a message in the database.
    updateMessage(messageId, newMessage, messageItem) {
      const updatedMessage = {
        message: newMessage.message,
        edited: true,
      };

      this.messageStore.updateMessage(messageId, updatedMessage, messageItem).then(() => {
        this.getAllMessages();
        this.$nextTick(() => {
          document.getElementById(`${messageItem._id}`).scrollIntoView({
            behavior: "smooth",
            block: "center",
            inline: "center",
          });
        });
      });
    }
</script>
<template>
  <div class="overflow-y-scroll">
    <InboxMessages
      :port="port"
      :chatroom="messageStore.selectedMessageInvitationInfo?.invitation_id"
      :current-user="userStore.currentUser"
      :selected-chat="messageStore.selectedMessageInvitationInfo"
      :sender-information="sender"
      :token="userStore.token"
      :roles="userStore.currentUser.roles"
      :user-id="userStore.currentUser._id"
      :existing-messages="currentMessages"
      @get-all-messages="getAllMessages"
      @create-new-message="createNewMessage"
      @update-message="updateMessage"
    />
  </div>
</template>   

MessageList

You must pass the following methods to MessageList:

@get-all-user-messages="YourMethod" @get-messages="YourMethod"

*See comments in example usage below for descriptions

Important The messageList prop needs to be filtered prior to passing it as a prop. You will want to filter it so that the messageList does not include all messages, but only surfaces one message per chat. See messageList function below for an example.

Example Usage:

<template>
  import _ from "lodash";

  //This returns only one message per unique invitation_id
  const messageList = computed(() => {
    return _.uniqBy(messageStore.messageList, "invitation_id");
  });

  //This function should send a GET request to the 
  //database that queries for all messages that are
  //tied to the selected chat list item

  function getMessages(message) {
    messageStore.selectedMessageInvitationInfo = {
      invitation_id: message.invitation_id,
      userOne: {
        userOne_id: message.userOne.userOne_id,
        name: message.userOne.name,
        phone: message.userOne.phone,
        email: message.userOne.email,
      },
      userTwo: {
        userTwo_id: message.userTwo.userTwo_id,
        name: message.userTwo.name,
        phone: message.userTwo.phone,
        email: message.userTwo.email,
      },
      sender_name: message.sender_name,
      sender_image: message.sender_image,
    };
    messageStore.readMessages(message.invitation_id, "invitation_id");
    viewStore.search = "messages";
  }

  //This function should search for all 
  //messages in the database that includes the current user

  function getAllUserMessages() {
    messageStore.readMessages(userStore.currentUser._id, userStore.userType);
  }

  <MessageList
    :chat-list="messageList"
    identifier-id="invitation_id"
    user-one="userOne"
    user-two="userTwo"
    message-background-color="bg-gray-100"
    max-width="max-width: 75px"
    name-color="text-gray-900"
    identifier-color="text-gray-900"
    :user-one-identifier="userStore.isuserOne"
    :current-user="userStore.currentUser"
    @get-all-user-messages="getAllUserMessages"
    @get-messages="getMessages"
  />
</template>
0.1.4

1 year ago

0.1.3

1 year ago

0.1.2

1 year ago

0.1.1

1 year ago

0.1.0

1 year ago

0.0.9

1 year ago

0.0.8

1 year ago

0.0.7

1 year ago

0.0.6

1 year ago

0.0.5

1 year ago

0.0.4

1 year ago

0.0.3

1 year ago

0.0.2

1 year ago

0.0.1

1 year ago