worthwhile-chat-app v0.1.4
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:
Props | Type | Required |
---|---|---|
chat-list | Array | true |
identifier-id | String | true |
user-one-identifier | Boolean | true |
current-user | Object | true |
user-one | String | true |
user-two | String | true |
message-background-color* | String | false |
hover-message-background-color* | String | false |
max-width | String | false |
name-color* | String | false |
identifier-color* | String | false |
**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>