[Enhancement] Create EmptyState component (#209)
This commit is contained in:
parent
88ac20efb5
commit
1ad36f164f
4 changed files with 316 additions and 240 deletions
|
@ -1,263 +1,51 @@
|
||||||
<template>
|
<template>
|
||||||
<div :class="conversationClass">
|
<div :class="conversationClass">
|
||||||
<div v-if="currentChat.id !== null" class="view-box columns">
|
<messages-view
|
||||||
<conversation-header
|
v-if="currentChat.id"
|
||||||
:chat="currentChat"
|
:inbox-id="inboxId"
|
||||||
:is-contact-panel-open="isContactPanelOpen"
|
:is-contact-panel-open="isContactPanelOpen"
|
||||||
@contactPanelToggle="onToggleContactPanel"
|
@contactPanelToggle="onToggleContactPanel"
|
||||||
/>
|
>
|
||||||
<ul class="conversation-panel">
|
</messages-view>
|
||||||
<transition name="slide-up">
|
<empty-state v-else></empty-state>
|
||||||
<li>
|
|
||||||
<span v-if="shouldShowSpinner" class="spinner message" />
|
|
||||||
</li>
|
|
||||||
</transition>
|
|
||||||
<conversation
|
|
||||||
v-for="message in getReadMessages"
|
|
||||||
:key="message.id"
|
|
||||||
:data="message"
|
|
||||||
/>
|
|
||||||
<li v-show="getUnreadCount != 0" class="unread--toast">
|
|
||||||
<span>
|
|
||||||
{{ getUnreadCount }} UNREAD MESSAGE{{
|
|
||||||
getUnreadCount > 1 ? 'S' : ''
|
|
||||||
}}
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
<conversation
|
|
||||||
v-for="message in getUnReadMessages"
|
|
||||||
:key="message.id"
|
|
||||||
:data="message"
|
|
||||||
/>
|
|
||||||
</ul>
|
|
||||||
<ReplyBox
|
|
||||||
:conversation-id="currentChat.id"
|
|
||||||
@scrollToMessage="focusLastMessage"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<!-- No Conversation Selected -->
|
|
||||||
<div v-else class="columns full-height conv-empty-state">
|
|
||||||
<!-- Loading status -->
|
|
||||||
<woot-loading-state
|
|
||||||
v-if="fetchingInboxes || loadingChatList"
|
|
||||||
:message="loadingIndicatorMessage"
|
|
||||||
/>
|
|
||||||
<!-- Show empty state images if not loading -->
|
|
||||||
<div v-if="!fetchingInboxes && !loadingChatList" class="current-chat">
|
|
||||||
<!-- No inboxes attached -->
|
|
||||||
<div v-if="!inboxesList.length">
|
|
||||||
<img src="~dashboard/assets/images/inboxes.svg" alt="No Inboxes" />
|
|
||||||
<span v-if="isAdmin()">
|
|
||||||
{{ $t('CONVERSATION.NO_INBOX_1') }}
|
|
||||||
<br />
|
|
||||||
<router-link :to="newInboxURL">
|
|
||||||
{{ $t('CONVERSATION.CLICK_HERE') }}
|
|
||||||
</router-link>
|
|
||||||
{{ $t('CONVERSATION.NO_INBOX_2') }}
|
|
||||||
</span>
|
|
||||||
<span v-if="!isAdmin()">
|
|
||||||
{{ $t('CONVERSATION.NO_INBOX_AGENT') }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<!-- No conversations available -->
|
|
||||||
<div v-else-if="!allConversations.length">
|
|
||||||
<img src="~dashboard/assets/images/chat.svg" alt="No Chat" />
|
|
||||||
<span>
|
|
||||||
{{ $t('CONVERSATION.NO_MESSAGE_1') }}
|
|
||||||
<br />
|
|
||||||
<a :href="linkToMessage" target="_blank">
|
|
||||||
{{ $t('CONVERSATION.CLICK_HERE') }}
|
|
||||||
</a>
|
|
||||||
{{ $t('CONVERSATION.NO_MESSAGE_2') }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<!-- No conversation selected -->
|
|
||||||
<div v-else-if="allConversations.length && currentChat.id === null">
|
|
||||||
<img src="~dashboard/assets/images/chat.svg" alt="No Chat" />
|
|
||||||
<span>{{ $t('CONVERSATION.404') }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
/* eslint no-console: 0 */
|
|
||||||
/* eslint no-extra-boolean-cast: 0 */
|
|
||||||
/* global bus */
|
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
|
import EmptyState from './EmptyState';
|
||||||
import ConversationHeader from './ConversationHeader';
|
import MessagesView from './MessagesView';
|
||||||
import ReplyBox from './ReplyBox';
|
|
||||||
import Conversation from './Conversation';
|
|
||||||
import conversationMixin from '../../../mixins/conversations';
|
|
||||||
import adminMixin from '../../../mixins/isAdmin';
|
|
||||||
import { frontendURL } from '../../../helper/URLHelper';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
ConversationHeader,
|
EmptyState,
|
||||||
Conversation,
|
MessagesView,
|
||||||
ReplyBox,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [conversationMixin, adminMixin],
|
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
inboxId: {
|
inboxId: {
|
||||||
type: [Number, String],
|
type: [Number, String],
|
||||||
required: true,
|
default: '',
|
||||||
|
required: false,
|
||||||
},
|
},
|
||||||
isContactPanelOpen: {
|
isContactPanelOpen: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
isLoadingPrevious: true,
|
|
||||||
heightBeforeLoad: null,
|
|
||||||
conversationPanel: null,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
currentChat: 'getSelectedChat',
|
currentChat: 'getSelectedChat',
|
||||||
allConversations: 'getAllConversations',
|
|
||||||
inboxesList: 'getInboxesList',
|
|
||||||
listLoadingStatus: 'getAllMessagesLoaded',
|
|
||||||
getUnreadCount: 'getUnreadCount',
|
|
||||||
fetchingInboxes: 'getInboxLoadingStatus',
|
|
||||||
loadingChatList: 'getChatListLoadingStatus',
|
|
||||||
}),
|
}),
|
||||||
conversationClass() {
|
conversationClass() {
|
||||||
return `medium-${
|
return `medium-${
|
||||||
this.isContactPanelOpen ? '5' : '8'
|
this.isContactPanelOpen ? '5' : '8'
|
||||||
} columns conversation-wrap`;
|
} columns conversation-wrap`;
|
||||||
},
|
},
|
||||||
// Loading indicator
|
|
||||||
// Returns corresponding loading message
|
|
||||||
loadingIndicatorMessage() {
|
|
||||||
if (this.fetchingInboxes) {
|
|
||||||
return this.$t('CONVERSATION.LOADING_INBOXES');
|
|
||||||
}
|
|
||||||
return this.$t('CONVERSATION.LOADING_CONVERSATIONS');
|
|
||||||
},
|
},
|
||||||
getMessages() {
|
|
||||||
const [chat] = this.allConversations.filter(
|
|
||||||
c => c.id === this.currentChat.id
|
|
||||||
);
|
|
||||||
return chat;
|
|
||||||
},
|
|
||||||
// Get current FB Page ID
|
|
||||||
getPageId() {
|
|
||||||
let stateInbox;
|
|
||||||
if (this.inboxId) {
|
|
||||||
const inboxId = Number(this.inboxId);
|
|
||||||
[stateInbox] = this.inboxesList.filter(
|
|
||||||
inbox => inbox.channel_id === inboxId
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
[stateInbox] = this.inboxesList;
|
|
||||||
}
|
|
||||||
return !stateInbox ? 0 : stateInbox.pageId;
|
|
||||||
},
|
|
||||||
// Get current FB Page ID link
|
|
||||||
linkToMessage() {
|
|
||||||
return `https://m.me/${this.getPageId}`;
|
|
||||||
},
|
|
||||||
getReadMessages() {
|
|
||||||
const chat = this.getMessages;
|
|
||||||
return chat === undefined ? null : this.readMessages(chat);
|
|
||||||
},
|
|
||||||
getUnReadMessages() {
|
|
||||||
const chat = this.getMessages;
|
|
||||||
return chat === undefined ? null : this.unReadMessages(chat);
|
|
||||||
},
|
|
||||||
shouldShowSpinner() {
|
|
||||||
return (
|
|
||||||
this.getMessages.dataFetched === undefined ||
|
|
||||||
(!this.listLoadingStatus && this.isLoadingPrevious)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
newInboxURL() {
|
|
||||||
return frontendURL('settings/inboxes/new');
|
|
||||||
},
|
|
||||||
|
|
||||||
shouldLoadMoreChats() {
|
|
||||||
return !this.listLoadingStatus && !this.isLoadingPrevious;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
|
||||||
bus.$on('scrollToMessage', () => {
|
|
||||||
this.focusLastMessage();
|
|
||||||
this.makeMessagesRead();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
focusLastMessage() {
|
|
||||||
setTimeout(() => {
|
|
||||||
this.attachListner();
|
|
||||||
}, 0);
|
|
||||||
},
|
|
||||||
|
|
||||||
onToggleContactPanel() {
|
onToggleContactPanel() {
|
||||||
this.$emit('contactPanelToggle');
|
this.$emit('contactPanelToggle');
|
||||||
},
|
},
|
||||||
|
|
||||||
attachListner() {
|
|
||||||
this.conversationPanel = this.$el.querySelector('.conversation-panel');
|
|
||||||
this.heightBeforeLoad =
|
|
||||||
this.getUnreadCount === 0
|
|
||||||
? this.conversationPanel.scrollHeight
|
|
||||||
: this.$el.querySelector('.conversation-panel .unread--toast')
|
|
||||||
.offsetTop - 56;
|
|
||||||
this.conversationPanel.scrollTop = this.heightBeforeLoad;
|
|
||||||
this.conversationPanel.addEventListener('scroll', this.handleScroll);
|
|
||||||
this.isLoadingPrevious = false;
|
|
||||||
},
|
|
||||||
|
|
||||||
handleScroll(e) {
|
|
||||||
const dataFetchCheck =
|
|
||||||
this.getMessages.dataFetched === true && this.shouldLoadMoreChats;
|
|
||||||
if (
|
|
||||||
e.target.scrollTop < 100 &&
|
|
||||||
!this.isLoadingPrevious &&
|
|
||||||
dataFetchCheck
|
|
||||||
) {
|
|
||||||
this.isLoadingPrevious = true;
|
|
||||||
this.$store
|
|
||||||
.dispatch('fetchPreviousMessages', {
|
|
||||||
conversationId: this.currentChat.id,
|
|
||||||
before: this.getMessages.messages[0].id,
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
this.conversationPanel.scrollTop =
|
|
||||||
this.conversationPanel.scrollHeight -
|
|
||||||
(this.heightBeforeLoad - this.conversationPanel.scrollTop);
|
|
||||||
this.isLoadingPrevious = false;
|
|
||||||
this.heightBeforeLoad =
|
|
||||||
this.getUnreadCount === 0
|
|
||||||
? this.conversationPanel.scrollHeight
|
|
||||||
: this.$el.querySelector('.conversation-panel .unread--toast')
|
|
||||||
.offsetTop - 56;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
makeMessagesRead() {
|
|
||||||
if (this.getUnreadCount !== 0 && this.getMessages !== undefined) {
|
|
||||||
this.$store.dispatch('markMessagesRead', {
|
|
||||||
id: this.currentChat.id,
|
|
||||||
lastSeen: this.getMessages.messages.last().created_at,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
<template>
|
||||||
|
<div class="columns full-height conv-empty-state">
|
||||||
|
<woot-loading-state
|
||||||
|
v-if="fetchingInboxes || loadingChatList"
|
||||||
|
:message="loadingIndicatorMessage"
|
||||||
|
/>
|
||||||
|
<!-- Show empty state images if not loading -->
|
||||||
|
<div v-if="!fetchingInboxes && !loadingChatList" class="current-chat">
|
||||||
|
<!-- No inboxes attached -->
|
||||||
|
<div v-if="!inboxesList.length">
|
||||||
|
<img src="~dashboard/assets/images/inboxes.svg" alt="No Inboxes" />
|
||||||
|
<span v-if="isAdmin()">
|
||||||
|
{{ $t('CONVERSATION.NO_INBOX_1') }}
|
||||||
|
<br />
|
||||||
|
<router-link :to="newInboxURL">
|
||||||
|
{{ $t('CONVERSATION.CLICK_HERE') }}
|
||||||
|
</router-link>
|
||||||
|
{{ $t('CONVERSATION.NO_INBOX_2') }}
|
||||||
|
</span>
|
||||||
|
<span v-if="!isAdmin()">
|
||||||
|
{{ $t('CONVERSATION.NO_INBOX_AGENT') }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<!-- No conversations available -->
|
||||||
|
<div v-else-if="!allConversations.length">
|
||||||
|
<img src="~dashboard/assets/images/chat.svg" alt="No Chat" />
|
||||||
|
<span>
|
||||||
|
{{ $t('CONVERSATION.NO_MESSAGE_1') }}
|
||||||
|
<br />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<!-- No conversation selected -->
|
||||||
|
<div v-else-if="allConversations.length && currentChat.id === null">
|
||||||
|
<img src="~dashboard/assets/images/chat.svg" alt="No Chat" />
|
||||||
|
<span>{{ $t('CONVERSATION.404') }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { mapGetters } from 'vuex';
|
||||||
|
import adminMixin from '../../../mixins/isAdmin';
|
||||||
|
import { frontendURL } from '../../../helper/URLHelper';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [adminMixin],
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
currentChat: 'getSelectedChat',
|
||||||
|
allConversations: 'getAllConversations',
|
||||||
|
inboxesList: 'getInboxesList',
|
||||||
|
fetchingInboxes: 'getInboxLoadingStatus',
|
||||||
|
loadingChatList: 'getChatListLoadingStatus',
|
||||||
|
}),
|
||||||
|
loadingIndicatorMessage() {
|
||||||
|
if (this.fetchingInboxes) {
|
||||||
|
return this.$t('CONVERSATION.LOADING_INBOXES');
|
||||||
|
}
|
||||||
|
return this.$t('CONVERSATION.LOADING_CONVERSATIONS');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
newInboxURL() {
|
||||||
|
return frontendURL('settings/inboxes/new');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -1,30 +1,40 @@
|
||||||
<template>
|
<template>
|
||||||
<li :class="alignBubble" v-if="data.attachment || data.content">
|
<li v-if="data.attachment || data.content" :class="alignBubble">
|
||||||
<div :class="wrapClass">
|
<div :class="wrapClass">
|
||||||
<p
|
<p
|
||||||
:class="{ bubble: isBubble, 'is-private': isPrivate }"
|
|
||||||
v-tooltip.top-start="sentByMessage"
|
v-tooltip.top-start="sentByMessage"
|
||||||
|
:class="{ bubble: isBubble, 'is-private': isPrivate }"
|
||||||
>
|
>
|
||||||
<bubble-image
|
<bubble-image
|
||||||
|
v-if="data.attachment && data.attachment.file_type === 'image'"
|
||||||
:url="data.attachment.data_url"
|
:url="data.attachment.data_url"
|
||||||
:readable-time="readableTime"
|
:readable-time="readableTime"
|
||||||
v-if="data.attachment && data.attachment.file_type==='image'"
|
|
||||||
/>
|
/>
|
||||||
<bubble-audio
|
<bubble-audio
|
||||||
|
v-if="data.attachment && data.attachment.file_type === 'audio'"
|
||||||
:url="data.attachment.data_url"
|
:url="data.attachment.data_url"
|
||||||
:readable-time="readableTime"
|
:readable-time="readableTime"
|
||||||
v-if="data.attachment && data.attachment.file_type==='audio'"
|
|
||||||
/>
|
/>
|
||||||
<bubble-map
|
<bubble-map
|
||||||
|
v-if="data.attachment && data.attachment.file_type === 'location'"
|
||||||
:lat="data.attachment.coordinates_lat"
|
:lat="data.attachment.coordinates_lat"
|
||||||
:lng="data.attachment.coordinates_long"
|
:lng="data.attachment.coordinates_long"
|
||||||
:label="data.attachment.fallback_title"
|
:label="data.attachment.fallback_title"
|
||||||
:readable-time="readableTime"
|
:readable-time="readableTime"
|
||||||
v-if="data.attachment && data.attachment.file_type==='location'"
|
|
||||||
/>
|
/>
|
||||||
<i class="icon ion-person" v-if="data.message_type === 2"></i>
|
<i v-if="data.message_type === 2" class="icon ion-person"></i>
|
||||||
<bubble-text v-if="data.content" :message="message" :readable-time="readableTime"/>
|
<bubble-text
|
||||||
<i class="icon ion-android-lock" v-if="isPrivate" v-tooltip.top-start="toolTipMessage" @mouseenter="isHovered = true" @mouseleave="isHovered = false"></i>
|
v-if="data.content"
|
||||||
|
:message="message"
|
||||||
|
:readable-time="readableTime"
|
||||||
|
/>
|
||||||
|
<i
|
||||||
|
v-if="isPrivate"
|
||||||
|
v-tooltip.top-start="toolTipMessage"
|
||||||
|
class="icon ion-android-lock"
|
||||||
|
@mouseenter="isHovered = true"
|
||||||
|
@mouseleave="isHovered = false"
|
||||||
|
></i>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<!-- <img v-if="showSenderData" src="https://chatwoot-staging.s3-us-west-2.amazonaws.com/uploads/avatar/contact/3415/thumb_10418362_10201264050880840_6087258728802054624_n.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAI3KBM2ES3VRHQHPQ%2F20170422%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20170422T075421Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=host&X-Amz-Signature=8d5ff60e41415515f59ff682b9a4e4c0574d9d9aabfeff1dc5a51087a9b49e03" class="sender--thumbnail"> -->
|
<!-- <img v-if="showSenderData" src="https://chatwoot-staging.s3-us-west-2.amazonaws.com/uploads/avatar/contact/3415/thumb_10418362_10201264050880840_6087258728802054624_n.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAI3KBM2ES3VRHQHPQ%2F20170422%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20170422T075421Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=host&X-Amz-Signature=8d5ff60e41415515f59ff682b9a4e4c0574d9d9aabfeff1dc5a51087a9b49e03" class="sender--thumbnail"> -->
|
||||||
|
@ -46,8 +56,13 @@ export default {
|
||||||
BubbleMap,
|
BubbleMap,
|
||||||
BubbleAudio,
|
BubbleAudio,
|
||||||
},
|
},
|
||||||
props: ['data'],
|
|
||||||
mixins: [timeMixin],
|
mixins: [timeMixin],
|
||||||
|
props: {
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
isHovered: false,
|
isHovered: false,
|
||||||
|
@ -71,11 +86,16 @@ export default {
|
||||||
return this.data.private;
|
return this.data.private;
|
||||||
},
|
},
|
||||||
toolTipMessage() {
|
toolTipMessage() {
|
||||||
return this.data.private ? { content: this.$t('CONVERSATION.VISIBLE_TO_AGENTS'), classes: 'top' } : false;
|
return this.data.private
|
||||||
|
? { content: this.$t('CONVERSATION.VISIBLE_TO_AGENTS'), classes: 'top' }
|
||||||
|
: false;
|
||||||
},
|
},
|
||||||
sentByMessage() {
|
sentByMessage() {
|
||||||
return this.data.message_type === 1 && !this.isHovered && this.data.sender !== undefined ?
|
return this.data.message_type === 1 &&
|
||||||
{ content: `Sent by: ${this.data.sender.name}`, classes: 'top' } : false;
|
!this.isHovered &&
|
||||||
|
this.data.sender !== undefined
|
||||||
|
? { content: `Sent by: ${this.data.sender.name}`, classes: 'top' }
|
||||||
|
: false;
|
||||||
},
|
},
|
||||||
wrapClass() {
|
wrapClass() {
|
||||||
return {
|
return {
|
||||||
|
@ -83,14 +103,16 @@ export default {
|
||||||
'activity-wrap': !this.isBubble,
|
'activity-wrap': !this.isBubble,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getEmojiSVG,
|
getEmojiSVG,
|
||||||
linkify(text) {
|
linkify(text) {
|
||||||
if (!text) return text;
|
if (!text) return text;
|
||||||
const urlRegex = /(https?:\/\/[^\s]+)/g;
|
const urlRegex = /(https?:\/\/[^\s]+)/g;
|
||||||
return text.replace(urlRegex, url => `<a href="${url}" target="_blank">${url}</a>`);
|
return text.replace(
|
||||||
|
urlRegex,
|
||||||
|
url => `<a href="${url}" target="_blank">${url}</a>`
|
||||||
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
|
@ -0,0 +1,196 @@
|
||||||
|
<template>
|
||||||
|
<div class="view-box columns">
|
||||||
|
<conversation-header
|
||||||
|
:chat="currentChat"
|
||||||
|
:is-contact-panel-open="isContactPanelOpen"
|
||||||
|
@contactPanelToggle="onToggleContactPanel"
|
||||||
|
/>
|
||||||
|
<ul class="conversation-panel">
|
||||||
|
<transition name="slide-up">
|
||||||
|
<li>
|
||||||
|
<span v-if="shouldShowSpinner" class="spinner message" />
|
||||||
|
</li>
|
||||||
|
</transition>
|
||||||
|
<message
|
||||||
|
v-for="message in getReadMessages"
|
||||||
|
:key="message.id"
|
||||||
|
:data="message"
|
||||||
|
/>
|
||||||
|
<li v-show="getUnreadCount != 0" class="unread--toast">
|
||||||
|
<span>
|
||||||
|
{{ getUnreadCount }} UNREAD MESSAGE{{ getUnreadCount > 1 ? 'S' : '' }}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<message
|
||||||
|
v-for="message in getUnReadMessages"
|
||||||
|
:key="message.id"
|
||||||
|
:data="message"
|
||||||
|
/>
|
||||||
|
</ul>
|
||||||
|
<ReplyBox
|
||||||
|
:conversation-id="currentChat.id"
|
||||||
|
@scrollToMessage="focusLastMessage"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
/* global bus */
|
||||||
|
import { mapGetters } from 'vuex';
|
||||||
|
|
||||||
|
import ConversationHeader from './ConversationHeader';
|
||||||
|
import ReplyBox from './ReplyBox';
|
||||||
|
import Message from './Message';
|
||||||
|
import conversationMixin from '../../../mixins/conversations';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
ConversationHeader,
|
||||||
|
Message,
|
||||||
|
ReplyBox,
|
||||||
|
},
|
||||||
|
|
||||||
|
mixins: [conversationMixin],
|
||||||
|
|
||||||
|
props: {
|
||||||
|
inboxId: {
|
||||||
|
type: [Number, String],
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
isContactPanelOpen: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isLoadingPrevious: true,
|
||||||
|
heightBeforeLoad: null,
|
||||||
|
conversationPanel: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
currentChat: 'getSelectedChat',
|
||||||
|
allConversations: 'getAllConversations',
|
||||||
|
inboxesList: 'getInboxesList',
|
||||||
|
listLoadingStatus: 'getAllMessagesLoaded',
|
||||||
|
getUnreadCount: 'getUnreadCount',
|
||||||
|
fetchingInboxes: 'getInboxLoadingStatus',
|
||||||
|
loadingChatList: 'getChatListLoadingStatus',
|
||||||
|
}),
|
||||||
|
|
||||||
|
getMessages() {
|
||||||
|
const [chat] = this.allConversations.filter(
|
||||||
|
c => c.id === this.currentChat.id
|
||||||
|
);
|
||||||
|
return chat;
|
||||||
|
},
|
||||||
|
// Get current FB Page ID
|
||||||
|
getPageId() {
|
||||||
|
let stateInbox;
|
||||||
|
if (this.inboxId) {
|
||||||
|
const inboxId = Number(this.inboxId);
|
||||||
|
[stateInbox] = this.inboxesList.filter(
|
||||||
|
inbox => inbox.channel_id === inboxId
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
[stateInbox] = this.inboxesList;
|
||||||
|
}
|
||||||
|
return !stateInbox ? 0 : stateInbox.pageId;
|
||||||
|
},
|
||||||
|
// Get current FB Page ID link
|
||||||
|
linkToMessage() {
|
||||||
|
return `https://m.me/${this.getPageId}`;
|
||||||
|
},
|
||||||
|
getReadMessages() {
|
||||||
|
const chat = this.getMessages;
|
||||||
|
return chat === undefined ? null : this.readMessages(chat);
|
||||||
|
},
|
||||||
|
getUnReadMessages() {
|
||||||
|
const chat = this.getMessages;
|
||||||
|
return chat === undefined ? null : this.unReadMessages(chat);
|
||||||
|
},
|
||||||
|
shouldShowSpinner() {
|
||||||
|
return (
|
||||||
|
this.getMessages.dataFetched === undefined ||
|
||||||
|
(!this.listLoadingStatus && this.isLoadingPrevious)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
shouldLoadMoreChats() {
|
||||||
|
return !this.listLoadingStatus && !this.isLoadingPrevious;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
created() {
|
||||||
|
bus.$on('scrollToMessage', () => {
|
||||||
|
this.focusLastMessage();
|
||||||
|
this.makeMessagesRead();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
focusLastMessage() {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.attachListner();
|
||||||
|
}, 0);
|
||||||
|
},
|
||||||
|
|
||||||
|
onToggleContactPanel() {
|
||||||
|
this.$emit('contactPanelToggle');
|
||||||
|
},
|
||||||
|
|
||||||
|
attachListner() {
|
||||||
|
this.conversationPanel = this.$el.querySelector('.conversation-panel');
|
||||||
|
this.heightBeforeLoad =
|
||||||
|
this.getUnreadCount === 0
|
||||||
|
? this.conversationPanel.scrollHeight
|
||||||
|
: this.$el.querySelector('.conversation-panel .unread--toast')
|
||||||
|
.offsetTop - 56;
|
||||||
|
this.conversationPanel.scrollTop = this.heightBeforeLoad;
|
||||||
|
this.conversationPanel.addEventListener('scroll', this.handleScroll);
|
||||||
|
this.isLoadingPrevious = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
handleScroll(e) {
|
||||||
|
const dataFetchCheck =
|
||||||
|
this.getMessages.dataFetched === true && this.shouldLoadMoreChats;
|
||||||
|
if (
|
||||||
|
e.target.scrollTop < 100 &&
|
||||||
|
!this.isLoadingPrevious &&
|
||||||
|
dataFetchCheck
|
||||||
|
) {
|
||||||
|
this.isLoadingPrevious = true;
|
||||||
|
this.$store
|
||||||
|
.dispatch('fetchPreviousMessages', {
|
||||||
|
conversationId: this.currentChat.id,
|
||||||
|
before: this.getMessages.messages[0].id,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
this.conversationPanel.scrollTop =
|
||||||
|
this.conversationPanel.scrollHeight -
|
||||||
|
(this.heightBeforeLoad - this.conversationPanel.scrollTop);
|
||||||
|
this.isLoadingPrevious = false;
|
||||||
|
this.heightBeforeLoad =
|
||||||
|
this.getUnreadCount === 0
|
||||||
|
? this.conversationPanel.scrollHeight
|
||||||
|
: this.$el.querySelector('.conversation-panel .unread--toast')
|
||||||
|
.offsetTop - 56;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
makeMessagesRead() {
|
||||||
|
if (this.getUnreadCount !== 0 && this.getMessages !== undefined) {
|
||||||
|
this.$store.dispatch('markMessagesRead', {
|
||||||
|
id: this.currentChat.id,
|
||||||
|
lastSeen: this.getMessages.messages.last().created_at,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
Loading…
Reference in a new issue