-
Notifications
You must be signed in to change notification settings - Fork 445
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added ability to have multiple chats #194
base: master
Are you sure you want to change the base?
Changes from all commits
c394c26
f2a49eb
d28f795
dbb7cb1
c45b282
6e5aa75
8825474
10bd969
6c7bf40
ee8aa9c
c08382c
9b93095
c7ed93e
c573bdb
38cd562
821ec66
fbe2cac
3d3531a
f70de30
158450a
a060084
931640f
fb59708
16aa042
968f543
00454fe
c92b3c1
a6da716
c7e3acc
ffe8608
28869d5
9eacbc4
92bc47e
1e89a12
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,9 +26,22 @@ | |
:confirmationDeletionMessage="'Are you sure? (you can customize this message)'" | ||
:titleImageUrl="titleImageUrl" | ||
:disableUserListToggle="false" | ||
:chatListImageUrl="chatListImageUrl" | ||
:placeholder="placeholder" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. placeholder was already implemented. I just added it to the demo app. |
||
:multipleChatsEnabled="multipleChatsEnabled" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. multipleChatsEnabled is needed mostly for backward compatibility. We could've gotten this information from chatList prop (for example, if chatList returns just one room, work in single chat room mode), but we can't require this new prop (chatList) to be specified for backward compatibility. |
||
:chatList="chatList" | ||
:chatListTitle="chatListTitle" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Text in the header for chat list screen. |
||
:messageListHeaderTitleClickable="false" | ||
:chatListHeaderTitleClickable="true" | ||
:messageIconClickable="true" | ||
@onType="handleOnType" | ||
@edit="editMessage" | ||
@remove="removeMessage" | ||
@changeCurrentChat="handleChangeCurrentChat" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Developer needs to handle this event to put new chat content into messageList prop. |
||
@messageListMountedUpdated="handleMessageListMountedUpdated" | ||
@messageListHeaderTitleClicked="messageListHeaderTitleClicked" | ||
@chatListHeaderTitleClicked="chatListHeaderTitleClicked" | ||
@messageIconClicked="messageIconClicked" | ||
> | ||
<template v-slot:text-message-toolbox="scopedProps"> | ||
<button v-if="!scopedProps.me && scopedProps.message.type==='text'" @click.prevent="like(scopedProps.message.id)"> | ||
|
@@ -103,6 +116,9 @@ | |
:messageStyling="messageStyling" | ||
:onMessage="sendMessage" | ||
:onTyping="handleTyping" | ||
:multipleChatsEnabled="multipleChatsEnabled" | ||
:chatList="chatList" | ||
@multipleChatsEnabledChange="handleMultipleChatsEnabledChange" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This event is mostly needed for the demo app. I don't expect this to be used in real applications. I think developers should just choose they either use single chat or multi chat mode once and keep it a constant. |
||
/> | ||
<Footer | ||
:chosenColor="chosenColor" | ||
|
@@ -112,8 +128,7 @@ | |
</template> | ||
|
||
<script> | ||
import messageHistory from './messageHistory' | ||
import chatParticipants from './chatProfiles' | ||
import chatHistory from './messageHistory' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Chat content format has changed in the demo app to include chat room info. |
||
import Header from './Header.vue' | ||
import Footer from './Footer.vue' | ||
import TestArea from './TestArea.vue' | ||
|
@@ -128,50 +143,53 @@ export default { | |
}, | ||
data() { | ||
return { | ||
participants: chatParticipants, | ||
titleImageUrl: | ||
'https://a.slack-edge.com/66f9/img/avatars-teams/ava_0001-34.png', | ||
messageList: messageHistory, | ||
newMessagesCount: 0, | ||
titleImageUrl: "https://a.slack-edge.com/66f9/img/avatars-teams/ava_0001-34.png", | ||
chatListImageUrl: "https://a.slack-edge.com/66f9/img/avatars-teams/ava_0001-34.png", | ||
chatHistory: chatHistory, | ||
isChatOpen: false, | ||
showTypingIndicator: '', | ||
colors: null, | ||
availableColors, | ||
chosenColor: null, | ||
alwaysScrollToBottom: true, | ||
messageStyling: true, | ||
userIsTyping: false | ||
userIsTyping: false, | ||
placeholder: 'Write something...', | ||
multipleChatsEnabled: true, | ||
currentChatID: Object.keys(chatHistory)[0], | ||
chatListTitle: 'My chats' | ||
} | ||
}, | ||
created() { | ||
this.setColor('blue') | ||
}, | ||
methods: { | ||
sendMessage(text) { | ||
sendMessage(text, chatID) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We now need to know which chat room the message goes to, so I added chatID. |
||
if (text.length > 0) { | ||
this.newMessagesCount = this.isChatOpen | ||
? this.newMessagesCount | ||
: this.newMessagesCount + 1 | ||
this.onMessageWasSent({ | ||
author: 'support', | ||
type: 'text', | ||
id: Math.random(), | ||
data: { text } | ||
}) | ||
}, chatID) | ||
} | ||
}, | ||
handleTyping(text) { | ||
handleTyping(text, chatID) { | ||
this.showTypingIndicator = | ||
text.length > 0 | ||
text.length > 0 && (chatID === undefined || this.currentChatID == chatID) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If I remember right chatID is undefined in single chat mode. |
||
? this.participants[this.participants.length - 1].id | ||
: '' | ||
}, | ||
onMessageWasSent(message) { | ||
this.messageList = [...this.messageList, Object.assign({}, message, {id: Math.random()})] | ||
onMessageWasSent(message, chatID) { | ||
const msg = Object.assign({}, message, {id: Math.random(), read: (message.author === this.myId)}) | ||
if (chatID !== undefined) { | ||
this.chatMessageList(chatID).push(msg) | ||
} else { | ||
this.messageList.push(msg) | ||
} | ||
}, | ||
openChat() { | ||
this.isChatOpen = true | ||
this.newMessagesCount = 0 | ||
}, | ||
closeChat() { | ||
this.isChatOpen = false | ||
|
@@ -204,12 +222,52 @@ export default { | |
m.type = 'system'; | ||
m.data.text = 'This message has been removed'; | ||
}, | ||
handleChangeCurrentChat(newCurrentChatID) { | ||
this.showTypingIndicator = '' | ||
this.currentChatID = newCurrentChatID | ||
const chat = this.chatHistory[this.currentChatID] | ||
this.titleImageUrl = chat.imageUrl | ||
this.title = chat.name | ||
}, | ||
handleMultipleChatsEnabledChange(newMultipleChatsEnabled) { | ||
this.multipleChatsEnabled = newMultipleChatsEnabled | ||
this.currentChatID = Object.keys(this.chatHistory)[0] | ||
}, | ||
handleMessageListMountedUpdated() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Experiment showed the 2 events, Mounted and Updated need to be listened to to mark messages as read. |
||
if (this.isChatOpen) { | ||
this.markCurrentChatRead() | ||
} | ||
}, | ||
messageListHeaderTitleClicked() { | ||
console.log("messageListHeaderTitleClicked") | ||
}, | ||
chatListHeaderTitleClicked() { | ||
console.log("chatListHeaderTitleClicked") | ||
}, | ||
messageIconClicked(user) { | ||
console.log("messageIconClicked user:") | ||
console.log(user) | ||
}, | ||
like(id){ | ||
const m = this.messageList.findIndex(m => m.id === id); | ||
var msg = this.messageList[m]; | ||
msg.liked = !msg.liked; | ||
this.$set(this.messageList, m, msg); | ||
} | ||
}, | ||
markCurrentChatRead() { | ||
this.messageList.forEach((msg) => { | ||
if (!msg.read) { | ||
msg.read = true | ||
} | ||
}) | ||
}, | ||
chatMessageList(chatID) { | ||
const chat = this.chatHistory[chatID]; | ||
if (chat === undefined) { | ||
return undefined; | ||
} | ||
return chat.messages; | ||
}, | ||
}, | ||
computed: { | ||
linkColor() { | ||
|
@@ -219,10 +277,53 @@ export default { | |
}, | ||
backgroundColor() { | ||
return this.chosenColor === 'dark' ? this.colors.messageList.bg : '#fff' | ||
}, | ||
messageList() { | ||
const chat = this.chatHistory[this.currentChatID]; | ||
if (chat === undefined) { | ||
return undefined; | ||
} | ||
return chat.messages; | ||
}, | ||
participants() { | ||
const chat = this.chatHistory[this.currentChatID]; | ||
if (chat === undefined) { | ||
return []; | ||
} | ||
return chat.participants; | ||
}, | ||
chatList() { | ||
var chats = [] | ||
Object.entries(this.chatHistory).forEach(([chatKey, chat]) => { | ||
var unreadCount = 0 | ||
chat.messages.forEach((msg) => { | ||
if (!msg.read) { | ||
unreadCount++ | ||
} | ||
}) | ||
chats.push( | ||
{ | ||
id: chatKey, | ||
name: chat.name, | ||
imageUrl: chat.imageUrl, | ||
unreadCount: unreadCount | ||
} | ||
) | ||
}) | ||
return chats | ||
}, | ||
newMessagesCount() { | ||
var totalUnreadCount = 0 | ||
this.chatList.forEach((chat) => { | ||
totalUnreadCount += chat.unreadCount | ||
}) | ||
return totalUnreadCount | ||
} | ||
}, | ||
mounted(){ | ||
this.messageList.forEach(x=>x.liked = false); | ||
Object.entries(this.chatHistory).forEach(([_, chat]) => { | ||
chat.messages.forEach(msg => msg.liked = false) | ||
}) | ||
} | ||
} | ||
</script> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,16 @@ | |
<form class="demo-test-area" @submit.prevent="_handleSubmit" @keyup="_handleTyping"> | ||
<div class="demo-test-area--preamble"> | ||
<p>Test the chat window by sending a message:</p> | ||
<p class="text-center messageStyling"> | ||
Multiple chat groups? | ||
<input :checked="multipleChatsEnabled" type="checkbox" @change.prevent="_multipleChatsEnabledChanged"> | ||
</p> | ||
<p v-if="multipleChatsEnabled"> | ||
To: | ||
<select name="chat" id="id" v-model="chatID"> | ||
<option v-for="chat in chatList" :key="chat.id" :value="chat.id">{{ chat.name }}</option> | ||
</select> | ||
</p> | ||
<p v-if="userIsTyping">User is typing...</p> | ||
</div> | ||
<textarea ref="textArea" class="demo-test-area--text" placeholder="Write a test message...." :style="textareaStyle" /> | ||
|
@@ -46,21 +56,34 @@ export default { | |
messageStyling: { | ||
type: Boolean, | ||
required: true | ||
}, | ||
chatList: { | ||
type: Array, | ||
required: true | ||
}, | ||
multipleChatsEnabled: { | ||
type: Boolean, | ||
required: true | ||
Comment on lines
+64
to
+66
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this a prop? Is there any difference when this is off? I would expect a single instance of the component to work just as it has, and multiple instances to also work out-of-the-box. I can't see why it needs to be configured. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In multi-room mode, when chat window is opened, I show list of chat rooms (as opposite to chat right away). Also, in the header for chat window I show go-back-to-chat-list button. I use multipleChatsEnabled prop to determine if those 2 things need to be done. I don't see any other good way to do it. The main reason for that is the demo app is controlling the chat content and there is only one property (messageList) with chat content it passes launcher. So, the demo app puts correct chat content when chat room changes. I considered to use chatList prop instead of multipleChatsEnabled (if it's empty or undefined than assume single chat mode), but it's possible to have 0 chat rooms in multi-chat mode. |
||
} | ||
}, | ||
data() { | ||
return { | ||
userIsTyping: false | ||
userIsTyping: false, | ||
chatID: this.chatList[0].id | ||
} | ||
}, | ||
methods: { | ||
_handleSubmit() { | ||
this.onMessage(this.$refs.textArea.value) | ||
this.onMessage(this.$refs.textArea.value, this.chatID) | ||
this.$refs.textArea.value = '' | ||
this.onTyping('') | ||
this.onTyping('', this.chatID) | ||
}, | ||
_handleTyping() { | ||
this.onTyping(this.$refs.textArea.value) | ||
this.onTyping(this.$refs.textArea.value, this.chatID) | ||
}, | ||
_multipleChatsEnabledChanged(e) { | ||
this.chatID = this.chatList[0].id | ||
this.$emit("multipleChatsEnabledChange", e.srcElement.checked) | ||
} | ||
}, | ||
computed: { | ||
|
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This icon appears in the header when the chat is in chat list mode. If it's an empty string, then no icon appears, but it still looks good.