From f36eeb0602b27cc828795394b1aa6696fc7f4567 Mon Sep 17 00:00:00 2001 From: saschrad <saschrad@stud.ntnu.no> Date: Tue, 3 May 2022 15:50:24 +0200 Subject: [PATCH] Rewritten the whole chat --- .../ChatComponents/ChatComponent.vue | 229 ++++++++++++++++++ src/components/ChatComponents/ChatMessage.vue | 32 ++- src/components/ChatComponents/ChatProfile.vue | 7 +- .../ChatComponents/ChatsComponent.vue | 181 ++++++++++++++ src/views/TestView.vue | 7 +- 5 files changed, 445 insertions(+), 11 deletions(-) create mode 100644 src/components/ChatComponents/ChatComponent.vue create mode 100644 src/components/ChatComponents/ChatsComponent.vue diff --git a/src/components/ChatComponents/ChatComponent.vue b/src/components/ChatComponents/ChatComponent.vue new file mode 100644 index 0000000..9e89948 --- /dev/null +++ b/src/components/ChatComponents/ChatComponent.vue @@ -0,0 +1,229 @@ +<template> + <div class="chat-container"> + <div class="header"> + <div + v-on:click="openHamburgerMethod" + class="hamburger grid space-y-2 content-center m-3" + > + <div class="w-8 h-0.5 bg-gray-600"></div> + <div class="w-8 h-0.5 bg-gray-600"></div> + <div class="w-8 h-0.5 bg-gray-600"></div> + </div> + <div class="flex"> + <img class="pfp" :src="this.src" alt="Profile Picture" /> + <h1>{{ name }}</h1> + </div> + <div></div> + </div> + <div class="conversation"> + <ChatMessage + v-for="(message, i) in messages" + v-bind:key="i" + :message="message" + ></ChatMessage> + </div> + <div + class=" + flex + items-center + justify-between + w-full + p-3 + border-t border-gray-300 + " + > + <input + v-on:keyup.enter="sendMessage" + type="text" + placeholder="Message" + class=" + block + w-full + py-2 + pl-4 + mx-3 + bg-gray-100 + rounded-full + outline-none + focus:text-gray-700 + " + name="message" + v-model="message" + /> + <button v-on:click="sendMessage" style="padding: 10px; color: red"> + <svg + class="w-5 h-5 text-gray-500 origin-center transform rotate-90" + xmlns="http://www.w3.org/2000/svg" + viewBox="0 0 20 20" + fill="currentColor" + > + <path + d="M10.894 2.553a1 1 0 00-1.788 0l-7 14a1 1 0 001.169 1.409l5-1.429A1 1 0 009 15.571V11a1 1 0 112 0v4.571a1 1 0 00.725.962l5 1.428a1 1 0 001.17-1.408l-7-14z" + /> + </svg> + </button> + </div> + </div> +</template> + +<script> +import ChatMessage from "./ChatMessage.vue"; +import axios from "axios"; +import ws from "@/services/ws"; +export default { + props: { + openHamburger: { type: Function }, + recipientID: { + type: Number, + }, + }, + data() { + return { + src: "https://pbs.twimg.com/media/FEaFK4OWUAAlgiV?format=jpg&name=900x900", + messages: [], + canScroll: true, + scrollBehavior: "", + recipient: null, + }; + }, + components: { + ChatMessage, + }, + computed: { + name() { + console.log(this.recipient); + return this.recipient + ? this.recipient.firstName + " " + this.recipient.lastName + : "N/A"; + }, + }, + methods: { + openHamburgerMethod() { + this.$emit("openHamburger"); + }, + scroll() { + let container = this.$el.querySelector(".conversation"); + container.scrollTop = container.scrollHeight; + }, + async sendMessage() { + if (this.message == null || this.message == "") return; + this.canScroll = true; + const token = this.$store.state.user.token; + await axios.post( + process.env.VUE_APP_BASEURL + + `chats/users/${this.recipientID}/messages`, + { + message: this.message, + }, + { + headers: { + Authorization: `Bearer ${token}`, + }, + } + ); + this.message = ""; + ws.sendMessage({ + sender: parseInt(this.userid), + recipient: this.recipientID, + }); + this.reloadMessages(); + }, + async reloadMessages() { + const token = this.$store.state.user.token; + const response = await fetch( + `${process.env.VUE_APP_BASEURL}chats/users/${this.recipientID}/messages`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + }, + } + ); + this.messages = await response.json(); + }, + async getRecipient() { + const token = this.$store.state.user.token; + const response = await fetch( + `${process.env.VUE_APP_BASEURL}users/${this.recipientID}/profile`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + }, + } + ); + + this.recipient = await response.json(); + }, + }, + watch: { + async recipientID() { + await this.reloadMessages(); + await this.getRecipient(); + }, + }, + async created() { + await this.reloadMessages(); + await this.getRecipient(); + }, + updated() { + if (this.canScroll) this.scroll(); + this.canScroll = false; + this.scrollBehavior = "smooth"; + }, +}; +</script> + +<style scoped> +.hamburger { + display: none; +} + +.chat-container { + display: flex; + flex-direction: column; + height: 100%; + width: auto; +} + +.conversation { + height: 100%; + width: 100%; + padding: 1.25rem; + overflow-y: scroll; + scroll-behavior: v-bind(scrollBehavior); +} + +.header { + display: flex; + flex-direction: row; + padding: 0.75rem; + border-bottom: 1px solid black; +} + +.header h1 { + align-self: center; + margin-left: 10px; + color: #4b5563; + font-weight: bold; +} + +.pfp { + object-fit: cover; + width: 2.5rem; + height: 2.5rem; + border-radius: 50%; +} + +@media screen and (max-width: 600px) { + .header { + justify-content: space-between; + } + + .hamburger { + display: block; + } +} +</style> \ No newline at end of file diff --git a/src/components/ChatComponents/ChatMessage.vue b/src/components/ChatComponents/ChatMessage.vue index d699677..c0a900f 100644 --- a/src/components/ChatComponents/ChatMessage.vue +++ b/src/components/ChatComponents/ChatMessage.vue @@ -1,22 +1,40 @@ <template> - <div v-bind:class="'w-full break-words flex ' + side()"> + <div v-bind:class="'blob-container ' + this.side()"> <div - style="max-width: 70%" v-bind:class=" - this.color() + ' rounded px-5 py-2 my-2 relative ' + this.textColor() + this.color() + ' message-container ' +this.textColor() " > - <span class="block" - >{{ this.message.content }} {{ this.message.from }}</span + <span class="message" + >{{ this.message.content }}</span > - <span class="block text-xs text-right">{{ this.calculateTime() }}</span> + <span class="">{{ this.calculateTime() }}</span> </div> </div> </template> +<style scoped> + .blob-container { + display: flex; + max-width: 100%; + } + + .message { + word-break: break-word; + display: block; + } + + .message-container { + border-radius: 10px; + max-width: 70%; + padding: 0.75rem; + margin-top: 0.25rem; + margin-bottom: 0.25rem; + } +</style> <script> import { parseCurrentUser } from "@/utils/token-utils"; - +//block text-xs text-right export default { props: { message: Object, diff --git a/src/components/ChatComponents/ChatProfile.vue b/src/components/ChatComponents/ChatProfile.vue index 7e1e9aa..8d5c5bf 100644 --- a/src/components/ChatComponents/ChatProfile.vue +++ b/src/components/ChatComponents/ChatProfile.vue @@ -52,9 +52,12 @@ export default { }, methods: { selectUser() { - console.log(this.conversation.recipient.userId); - this.$emit("recipient", this.conversation.recipient.userId); + console.log(this.conversation?.recipient.userId); + this.$emit("recipient", this.conversation?.recipient.userId); }, }, + created() { + console.log("convesation", this.conversation); + } }; </script> diff --git a/src/components/ChatComponents/ChatsComponent.vue b/src/components/ChatComponents/ChatsComponent.vue new file mode 100644 index 0000000..936b95c --- /dev/null +++ b/src/components/ChatComponents/ChatsComponent.vue @@ -0,0 +1,181 @@ +<template> + <div class="chat"> + <div class="conversations"> + <h1 >Samtaler:</h1> + <hr/> + <ChatProfile + v-for="(conversation, i) in conversations" + :conversation="conversation" + :key="i" + @recipient="selectUser" + ></ChatProfile> + <div class="button"> + <colored-button text="Ny Samtale"></colored-button> + </div> + </div> + <div class="current-chat"> + <ChatComponent @openHamburger="openHamburger" v-if="recipient" :recipientID="recipient"></ChatComponent> + <div v-else><p>NOTHING HERE :)</p></div> + </div> + </div> +</template> + +<script> +import ChatProfile from './ChatProfile.vue'; +import ChatComponent from './ChatComponent.vue'; +//import ChatMessage from "./ChatMessage.vue"; +import axios from "axios"; +import { parseCurrentUser } from "@/utils/token-utils"; +import ws from "@/services/ws"; +import ColoredButton from '../BaseComponents/ColoredButton.vue'; + +export default { + props: { + + }, + data: () => { + return { + messages: [], + message: "", + recipient: null, + hambuger: "none", + conversations: [ + + ], + hambugerDisplay: "none" + }; + }, + components: { ChatProfile, ChatComponent, ColoredButton}, + computed: { + userid() { + return parseCurrentUser().accountId; + }, + recipientID() { + return this.recipient.userId; + }, + name() { + return this.recipient.firstName + " " + this.recipient.lastName; + }, + }, + methods: { + selectUser(recipientID) { + this.hambugerDisplay = "none" + this.recipient = this.conversations.find( + (conversation) => conversation.recipient.userId == recipientID + )?.recipient.userId; + + console.log("New recipient", this.recipient) + }, + openHamburger() { + this.hambugerDisplay = "block" + }, + calculateSide(from) { + return from == this.userid ? "end" : "start"; + }, + async sendMessage() { + const token = this.$store.state.user.token; + await axios.post( + process.env.VUE_APP_BASEURL + + `chats/users/${this.recipientID}/messages`, + { + message: this.message, + }, + { + headers: { + Authorization: `Bearer ${token}`, + }, + } + ); + this.message = ""; + ws.sendMessage({ + sender: parseInt(this.userid), + recipient: this.recipientID, + }); + this.reloadMessages(); + }, + async reloadMessages() { + const token = this.$store.state.user.token; + const response = await fetch( + `${process.env.VUE_APP_BASEURL}chats/users/${this.recipientID}/messages`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + }, + } + ); + + this.messages = await response.json(); + }, + }, + async created() { + const token = this.$store.state.user.token; + + // Get all conversations from api with /chats/users + const conResponse = await fetch(`${process.env.VUE_APP_BASEURL}chats/users`, { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + }, + }); // add error handling + this.conversations = await conResponse.json(); + + ws.on("NEW_MESSAGE", () => { + this.reloadMessages(); + }); + }, +}; +</script> +<style scoped> + .chat { + display: flex; + flex-direction: row; + width: 100%; + height: min(100vh - 3.5rem); + } + + .current-chat { + width: 100%; + height: 100%; + } + + .conversations { + min-width: 300px; + border-right-width: 1px; + border-color: black; + height: 100%; + } + + .conversations h1 { + padding: 0.5rem; + padding-left: 0; + align-self: center; + margin-left: 10px; + color: #4b5563; + font-weight: bold; + font-size: large; +} + + @media screen and (max-width: 600px) { + .conversations { + display: v-bind(hambugerDisplay); + z-index: 99; + width: 100%; + position: absolute; + background-color: white; + } + + .conversations h1 { + text-align: center; + } + + } + + .button { + display: flex; + justify-content: center; + padding: 0.75rem; + } +</style> \ No newline at end of file diff --git a/src/views/TestView.vue b/src/views/TestView.vue index 7537e2d..60d0d1c 100644 --- a/src/views/TestView.vue +++ b/src/views/TestView.vue @@ -1,15 +1,18 @@ <template> - <div /> + <chats-component></chats-component> </template> <script> +import ChatsComponent from '@/components/ChatComponents/ChatsComponent.vue'; export default { + components: { + ChatsComponent, + }, data() { return { show: false, }; }, - components: {}, methods: { toggleModal() { this.show = !this.show; -- GitLab