feat: Improve email rendering, introduce a new layout for emails (#5039)
Co-authored-by: Fayaz Ahmed <15716057+fayazara@users.noreply.github.com>
This commit is contained in:
@@ -1,5 +1,11 @@
|
||||
<template>
|
||||
<div class="conversations-list-wrap">
|
||||
<div
|
||||
class="conversations-list-wrap"
|
||||
:class="{
|
||||
hide: !showConversationList,
|
||||
'list--full-width': isOnExpandedLayout,
|
||||
}"
|
||||
>
|
||||
<slot />
|
||||
<div
|
||||
class="chat-list__top"
|
||||
@@ -46,7 +52,7 @@
|
||||
|
||||
<woot-button
|
||||
v-else
|
||||
v-tooltip.top-end="$t('FILTER.TOOLTIP_LABEL')"
|
||||
v-tooltip.right="$t('FILTER.TOOLTIP_LABEL')"
|
||||
variant="clear"
|
||||
color-scheme="secondary"
|
||||
icon="filter"
|
||||
@@ -210,6 +216,14 @@ export default {
|
||||
type: [String, Number],
|
||||
default: 0,
|
||||
},
|
||||
showConversationList: {
|
||||
default: true,
|
||||
type: Boolean,
|
||||
},
|
||||
isOnExpandedLayout: {
|
||||
default: false,
|
||||
type: Boolean,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -696,6 +710,17 @@ export default {
|
||||
@include breakpoint(xxxlarge up) {
|
||||
flex-basis: 46rem;
|
||||
}
|
||||
|
||||
&.hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.list--full-width {
|
||||
width: 100%;
|
||||
@include breakpoint(xxxlarge up) {
|
||||
flex-basis: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
.filter--actions {
|
||||
display: flex;
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
<template>
|
||||
<div class="conversation-details-wrap">
|
||||
<div
|
||||
class="conversation-details-wrap"
|
||||
:class="{ 'with-border-left': !isOnExpandedLayout }"
|
||||
>
|
||||
<conversation-header
|
||||
v-if="currentChat.id"
|
||||
:chat="currentChat"
|
||||
:is-contact-panel-open="isContactPanelOpen"
|
||||
:show-back-button="isOnExpandedLayout"
|
||||
@contact-panel-toggle="onToggleContactPanel"
|
||||
/>
|
||||
<woot-tabs
|
||||
@@ -26,7 +30,7 @@
|
||||
:is-contact-panel-open="isContactPanelOpen"
|
||||
@contact-panel-toggle="onToggleContactPanel"
|
||||
/>
|
||||
<empty-state v-else />
|
||||
<empty-state v-else :is-on-expanded-layout="isOnExpandedLayout" />
|
||||
<div v-show="showContactPanel" class="conversation-sidebar-wrap">
|
||||
<contact-panel
|
||||
v-if="showContactPanel"
|
||||
@@ -71,6 +75,10 @@ export default {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
isOnExpandedLayout: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return { activeIndex: 0 };
|
||||
@@ -134,8 +142,11 @@ export default {
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
border-left: 1px solid var(--color-border);
|
||||
background: var(--color-background-light);
|
||||
|
||||
&.with-border-left {
|
||||
border-left: 1px solid var(--color-border);
|
||||
}
|
||||
}
|
||||
|
||||
.dashboard-app--tabs {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<template>
|
||||
<div class="conv-header">
|
||||
<div class="user">
|
||||
<back-button v-if="showBackButton" :back-url="backButtonUrl" />
|
||||
<Thumbnail
|
||||
:src="currentContact.thumbnail"
|
||||
size="40px"
|
||||
@@ -47,19 +48,21 @@
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { hasPressedAltAndOKey } from 'shared/helpers/KeyboardHelpers';
|
||||
import { mapGetters } from 'vuex';
|
||||
import MoreActions from './MoreActions';
|
||||
import Thumbnail from '../Thumbnail';
|
||||
import agentMixin from '../../../mixins/agentMixin.js';
|
||||
import BackButton from '../BackButton';
|
||||
import differenceInHours from 'date-fns/differenceInHours';
|
||||
import eventListenerMixins from 'shared/mixins/eventListenerMixins';
|
||||
import inboxMixin from 'shared/mixins/inboxMixin';
|
||||
import { hasPressedAltAndOKey } from 'shared/helpers/KeyboardHelpers';
|
||||
import wootConstants from '../../../constants';
|
||||
import differenceInHours from 'date-fns/differenceInHours';
|
||||
import InboxName from '../InboxName';
|
||||
|
||||
import MoreActions from './MoreActions';
|
||||
import Thumbnail from '../Thumbnail';
|
||||
import wootConstants from '../../../constants';
|
||||
import { conversationListPageURL } from 'dashboard/helper/URLHelper';
|
||||
export default {
|
||||
components: {
|
||||
BackButton,
|
||||
InboxName,
|
||||
MoreActions,
|
||||
Thumbnail,
|
||||
@@ -74,6 +77,10 @@ export default {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
showBackButton: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
@@ -83,6 +90,19 @@ export default {
|
||||
chatMetadata() {
|
||||
return this.chat.meta;
|
||||
},
|
||||
backButtonUrl() {
|
||||
const {
|
||||
params: { accountId, inbox_id: inboxId, label, teamId },
|
||||
name,
|
||||
} = this.$route;
|
||||
return conversationListPageURL({
|
||||
accountId,
|
||||
inboxId,
|
||||
label,
|
||||
teamId,
|
||||
conversationType: name === 'conversation_mentions' ? 'mention' : '',
|
||||
});
|
||||
},
|
||||
isHMACVerified() {
|
||||
if (!this.isAWebWidgetInbox) {
|
||||
return true;
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
<!-- No conversation selected -->
|
||||
<div v-else-if="allConversations.length && !currentChat.id">
|
||||
<img src="~dashboard/assets/images/chat.svg" alt="No Chat" />
|
||||
<span>{{ $t('CONVERSATION.404') }}</span>
|
||||
<span>{{ conversationMissingMessage }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -51,6 +51,12 @@ export default {
|
||||
OnboardingView,
|
||||
},
|
||||
mixins: [accountMixin, adminMixin],
|
||||
props: {
|
||||
isOnExpandedLayout: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
currentChat: 'getSelectedChat',
|
||||
@@ -65,6 +71,12 @@ export default {
|
||||
}
|
||||
return this.$t('CONVERSATION.LOADING_CONVERSATIONS');
|
||||
},
|
||||
conversationMissingMessage() {
|
||||
if (!this.isOnExpandedLayout) {
|
||||
return this.$t('CONVERSATION.SELECT_A_CONVERSATION');
|
||||
}
|
||||
return this.$t('CONVERSATION.404');
|
||||
},
|
||||
newInboxURL() {
|
||||
return this.addAccountScoping('settings/inboxes/new');
|
||||
},
|
||||
|
||||
@@ -170,7 +170,7 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
contentToBeParsed() {
|
||||
emailMessageContent() {
|
||||
const {
|
||||
html_content: { full: fullHTMLContent } = {},
|
||||
text_content: { full: fullTextContent } = {},
|
||||
@@ -182,13 +182,19 @@ export default {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.contentToBeParsed.includes('<blockquote')) {
|
||||
if (this.emailMessageContent.includes('<blockquote')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
message() {
|
||||
// If the message is an email, emailMessageContent would be present
|
||||
// In that case, we would use letter package to render the email
|
||||
if (this.emailMessageContent && this.isIncoming) {
|
||||
return this.emailMessageContent;
|
||||
}
|
||||
|
||||
const botMessageContent = generateBotMessageContent(
|
||||
this.contentType,
|
||||
this.contentAttributes,
|
||||
@@ -200,21 +206,6 @@ export default {
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const {
|
||||
email: { content_type: contentType = '' } = {},
|
||||
} = this.contentAttributes;
|
||||
if (this.contentToBeParsed && this.isIncoming) {
|
||||
const parsedContent = this.stripStyleCharacters(this.contentToBeParsed);
|
||||
if (parsedContent) {
|
||||
// This is a temporary fix for line-breaks in text/plain emails
|
||||
// Now, It is not rendered properly in the email preview.
|
||||
// FIXME: Remove this once we have a better solution for rendering text/plain emails
|
||||
return contentType.includes('text/plain')
|
||||
? parsedContent.replace(/\n/g, '<br />')
|
||||
: parsedContent;
|
||||
}
|
||||
}
|
||||
return (
|
||||
this.formatMessage(
|
||||
this.data.content,
|
||||
@@ -331,6 +322,7 @@ export default {
|
||||
'activity-wrap': !this.isBubble,
|
||||
'is-pending': this.isPending,
|
||||
'is-failed': this.isFailed,
|
||||
'is-email': this.isEmailContentType,
|
||||
};
|
||||
},
|
||||
bubbleClass() {
|
||||
@@ -342,6 +334,7 @@ export default {
|
||||
'is-text': this.hasText,
|
||||
'is-from-bot': this.isSentByBot,
|
||||
'is-failed': this.isFailed,
|
||||
'is-email': this.isEmailContentType,
|
||||
};
|
||||
},
|
||||
isPending() {
|
||||
@@ -518,6 +511,10 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
.wrap.is-email {
|
||||
--bubble-max-width: 84% !important;
|
||||
}
|
||||
|
||||
.sender--info {
|
||||
align-items: center;
|
||||
color: var(--b-700);
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
'hide--quoted': !showQuotedContent,
|
||||
}"
|
||||
>
|
||||
<div v-dompurify-html="message" class="text-content" />
|
||||
<div v-if="!isEmail" v-dompurify-html="message" class="text-content" />
|
||||
<letter v-else class="text-content" :html="message" />
|
||||
<button
|
||||
v-if="displayQuotedButton"
|
||||
class="quoted-text--button"
|
||||
@@ -25,7 +26,10 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Letter from 'vue-letter';
|
||||
|
||||
export default {
|
||||
components: { Letter },
|
||||
props: {
|
||||
message: {
|
||||
type: String,
|
||||
@@ -65,14 +69,16 @@ export default {
|
||||
padding-left: var(--space-two);
|
||||
}
|
||||
table {
|
||||
all: revert;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
|
||||
td {
|
||||
all: revert;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
tr {
|
||||
all: revert;
|
||||
border-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user