Co-authored-by: Kaj Oudshoorn <kaj@milvum.com> Co-authored-by: Pranav Raj S <pranav@chatwoot.com> Co-authored-by: Nithin David Thomas <1277421+nithindavid@users.noreply.github.com>
This commit is contained in:
parent
2ddd508aee
commit
af1d8c0ee5
11 changed files with 109 additions and 23 deletions
|
@ -43,7 +43,7 @@ $woot-logo-padding: $space-large $space-two;
|
|||
// Colors
|
||||
$color-woot: #1f93ff;
|
||||
$color-gray: #6e6f73;
|
||||
$color-light-gray: #999a9b;
|
||||
$color-light-gray: #747677;
|
||||
$color-border: #e0e6ed;
|
||||
$color-border-light: #f0f4f5;
|
||||
$color-background: #f4f6fb;
|
||||
|
|
|
@ -142,6 +142,7 @@ export const IFrameHelper = {
|
|||
},
|
||||
|
||||
onBubbleToggle: isOpen => {
|
||||
IFrameHelper.sendMessage('toggle-open', { isOpen });
|
||||
if (!isOpen) {
|
||||
IFrameHelper.events.resetUnreadMode();
|
||||
} else {
|
||||
|
@ -194,6 +195,10 @@ export const IFrameHelper = {
|
|||
const holderEl = document.querySelector('.woot-widget-holder');
|
||||
removeClass(holderEl, 'has-unread-view');
|
||||
},
|
||||
|
||||
closeChat: () => {
|
||||
onBubbleClick({ toggleValue: false });
|
||||
},
|
||||
},
|
||||
pushEvent: eventName => {
|
||||
IFrameHelper.sendMessage('push-event', { eventName });
|
||||
|
|
|
@ -9,8 +9,8 @@ export const body = document.getElementsByTagName('body')[0];
|
|||
export const widgetHolder = document.createElement('div');
|
||||
|
||||
export const bubbleHolder = document.createElement('div');
|
||||
export const chatBubble = document.createElement('div');
|
||||
export const closeBubble = document.createElement('div');
|
||||
export const chatBubble = document.createElement('button');
|
||||
export const closeBubble = document.createElement('button');
|
||||
export const notificationBubble = document.createElement('span');
|
||||
|
||||
export const getBubbleView = type =>
|
||||
|
@ -64,6 +64,10 @@ export const onBubbleClick = (props = {}) => {
|
|||
toggleClass(closeBubble, 'woot--hide');
|
||||
toggleClass(widgetHolder, 'woot--hide');
|
||||
IFrameHelper.events.onBubbleToggle(newIsOpen);
|
||||
|
||||
if (!newIsOpen) {
|
||||
chatBubble.focus();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -25,7 +25,9 @@ export const SDK_CSS = `.woot-widget-holder {
|
|||
.woot-widget-bubble {
|
||||
background: #1f93ff;
|
||||
border-radius: 100px !important;
|
||||
border-width: 0px;
|
||||
bottom: 20px;
|
||||
padding: 0px;
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, .16) !important;
|
||||
cursor: pointer;
|
||||
height: 64px !important;
|
||||
|
@ -40,6 +42,7 @@ export const SDK_CSS = `.woot-widget-holder {
|
|||
display: flex;
|
||||
height: 48px !important;
|
||||
width: auto !important;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.woot-widget-bubble.woot-widget--expanded div {
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters, mapActions } from 'vuex';
|
||||
import { mapGetters, mapActions, mapMutations } from 'vuex';
|
||||
import { setHeader } from 'widget/helpers/axios';
|
||||
import { IFrameHelper, RNHelper } from 'widget/helpers/utils';
|
||||
import Router from './views/Router';
|
||||
|
@ -97,6 +97,7 @@ export default {
|
|||
...mapActions('conversation', ['fetchOldConversations', 'setUserLastSeen']),
|
||||
...mapActions('campaign', ['initCampaigns', 'executeCampaign']),
|
||||
...mapActions('agent', ['fetchAvailableAgents']),
|
||||
...mapMutations('events', ['toggleOpen']),
|
||||
scrollConversationToBottom() {
|
||||
const container = this.$el.querySelector('.conversation-wrap');
|
||||
container.scrollTop = container.scrollHeight;
|
||||
|
@ -249,6 +250,8 @@ export default {
|
|||
} else if (message.event === 'unset-unread-view') {
|
||||
this.showUnreadView = false;
|
||||
this.showCampaignView = false;
|
||||
} else if (message.event === 'toggle-open') {
|
||||
this.toggleOpen();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
|
|
@ -74,3 +74,8 @@ $color-shadow-outline: rgba(66, 153, 225, 0.5);
|
|||
@mixin shadow-none {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
@mixin button-size {
|
||||
min-height: $space-large;
|
||||
min-width: $space-large;
|
||||
}
|
||||
|
|
|
@ -68,13 +68,19 @@ export default {
|
|||
</script>
|
||||
<style scoped lang="scss">
|
||||
@import '~widget/assets/scss/variables.scss';
|
||||
@import '~widget/assets/scss/mixins.scss';
|
||||
|
||||
.attachment-button {
|
||||
@include button-size;
|
||||
|
||||
background: transparent;
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
width: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
i {
|
||||
font-size: $font-size-large;
|
||||
|
|
|
@ -124,7 +124,6 @@ export default {
|
|||
.footer {
|
||||
background: $color-white;
|
||||
box-sizing: border-box;
|
||||
padding: $space-small $space-slab;
|
||||
width: 100%;
|
||||
border-radius: 7px;
|
||||
@include shadow-big;
|
||||
|
|
|
@ -1,27 +1,42 @@
|
|||
<template>
|
||||
<div class="chat-message--input">
|
||||
<div
|
||||
class="chat-message--input"
|
||||
:class="{ 'is-focused': isFocused }"
|
||||
@keydown.esc="hideEmojiPicker"
|
||||
>
|
||||
<resizable-text-area
|
||||
id="chat-input"
|
||||
ref="chatInput"
|
||||
v-model="userInput"
|
||||
:aria-label="$t('CHAT_PLACEHOLDER')"
|
||||
:placeholder="$t('CHAT_PLACEHOLDER')"
|
||||
class="form-input user-message-input"
|
||||
@typing-off="onTypingOff"
|
||||
@typing-on="onTypingOn"
|
||||
@focus="onFocus"
|
||||
@blur="onBlur"
|
||||
/>
|
||||
<div class="button-wrap">
|
||||
<chat-attachment-button
|
||||
v-if="showAttachment"
|
||||
:on-attach="onSendAttachment"
|
||||
/>
|
||||
<button
|
||||
v-if="hasEmojiPickerEnabled"
|
||||
class="emoji-toggle"
|
||||
aria-label="Emoji picker"
|
||||
@click="toggleEmojiPicker()"
|
||||
>
|
||||
<i
|
||||
class="icon ion-happy-outline"
|
||||
:class="{ active: showEmojiPicker }"
|
||||
/>
|
||||
</button>
|
||||
<emoji-input
|
||||
v-if="showEmojiPicker"
|
||||
v-on-clickaway="hideEmojiPicker"
|
||||
:on-click="emojiOnClick"
|
||||
/>
|
||||
<i
|
||||
v-if="hasEmojiPickerEnabled"
|
||||
class="emoji-toggle icon ion-happy-outline"
|
||||
:class="{ active: showEmojiPicker }"
|
||||
@click="toggleEmojiPicker()"
|
||||
@keydown.esc="hideEmojiPicker"
|
||||
/>
|
||||
<chat-send-button
|
||||
v-if="showSendButton"
|
||||
|
@ -65,6 +80,7 @@ export default {
|
|||
return {
|
||||
userInput: '',
|
||||
showEmojiPicker: false,
|
||||
isFocused: false,
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -78,21 +94,40 @@ export default {
|
|||
showSendButton() {
|
||||
return this.userInput.length > 0;
|
||||
},
|
||||
isOpen() {
|
||||
return this.$store.state.events.isOpen;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
isOpen(isOpen) {
|
||||
if (isOpen) {
|
||||
this.focusInput();
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
destroyed() {
|
||||
document.removeEventListener('keypress', this.handleEnterKeyPress);
|
||||
},
|
||||
mounted() {
|
||||
document.addEventListener('keypress', this.handleEnterKeyPress);
|
||||
if (this.isOpen) {
|
||||
this.focusInput();
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
onBlur() {
|
||||
this.isFocused = false;
|
||||
},
|
||||
onFocus() {
|
||||
this.isFocused = true;
|
||||
},
|
||||
handleButtonClick() {
|
||||
if (this.userInput && this.userInput.trim()) {
|
||||
this.onSendMessage(this.userInput);
|
||||
}
|
||||
this.userInput = '';
|
||||
this.focusInput();
|
||||
},
|
||||
handleEnterKeyPress(e) {
|
||||
if (e.keyCode === 13 && !e.shiftKey) {
|
||||
|
@ -103,8 +138,9 @@ export default {
|
|||
toggleEmojiPicker() {
|
||||
this.showEmojiPicker = !this.showEmojiPicker;
|
||||
},
|
||||
hideEmojiPicker() {
|
||||
hideEmojiPicker(e) {
|
||||
if (this.showEmojiPicker) {
|
||||
e.stopPropagation();
|
||||
this.toggleEmojiPicker();
|
||||
}
|
||||
},
|
||||
|
@ -120,22 +156,33 @@ export default {
|
|||
toggleTyping(typingStatus) {
|
||||
this.$store.dispatch('conversation/toggleUserTyping', { typingStatus });
|
||||
},
|
||||
focusInput() {
|
||||
this.$refs.chatInput.focus();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '~widget/assets/scss/variables.scss';
|
||||
@import '~widget/assets/scss/mixins.scss';
|
||||
|
||||
.chat-message--input {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
padding: 0 $space-small 0 $space-slab;
|
||||
border-radius: 7px;
|
||||
|
||||
&.is-focused {
|
||||
box-shadow: 0 0 0 1px $color-woot, 0 0 2px 3px $color-primary-light;
|
||||
}
|
||||
}
|
||||
|
||||
.emoji-toggle {
|
||||
@include button-size;
|
||||
|
||||
font-size: $font-size-large;
|
||||
color: $color-gray;
|
||||
padding-right: $space-smaller;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
@ -143,13 +190,10 @@ export default {
|
|||
right: $space-one;
|
||||
}
|
||||
|
||||
.file-uploads {
|
||||
margin-right: $space-small;
|
||||
}
|
||||
|
||||
.button-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: $space-small;
|
||||
}
|
||||
|
||||
.user-message-input {
|
||||
|
@ -158,6 +202,9 @@ export default {
|
|||
min-height: $space-large;
|
||||
max-height: 2.4 * $space-mega;
|
||||
resize: none;
|
||||
padding: 0;
|
||||
padding-top: $space-small;
|
||||
margin-top: $space-small;
|
||||
margin-bottom: $space-small;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
import events from 'widget/api/events';
|
||||
|
||||
const state = {
|
||||
isOpen: false,
|
||||
}
|
||||
|
||||
const actions = {
|
||||
create: async (_, { name }) => {
|
||||
try {
|
||||
|
@ -10,10 +14,16 @@ const actions = {
|
|||
},
|
||||
};
|
||||
|
||||
const mutations = {
|
||||
toggleOpen($state) {
|
||||
$state.isOpen = !$state.isOpen;
|
||||
}
|
||||
};
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state: {},
|
||||
state,
|
||||
getters: {},
|
||||
actions,
|
||||
mutations: {},
|
||||
mutations,
|
||||
};
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
>
|
||||
<spinner size="" />
|
||||
</div>
|
||||
<div v-else class="home">
|
||||
<div v-else class="home" @keydown.esc="closeChat">
|
||||
<div
|
||||
class="header-wrap bg-white"
|
||||
:class="{ expanded: !isHeaderCollapsed, collapsed: isHeaderCollapsed }"
|
||||
|
@ -74,6 +74,7 @@ import ChatFooter from 'widget/components/ChatFooter.vue';
|
|||
import ChatHeaderExpanded from 'widget/components/ChatHeaderExpanded.vue';
|
||||
import ChatHeader from 'widget/components/ChatHeader.vue';
|
||||
import ConversationWrap from 'widget/components/ConversationWrap.vue';
|
||||
import { IFrameHelper } from 'widget/helpers/utils';
|
||||
import configMixin from '../mixins/configMixin';
|
||||
import TeamAvailability from 'widget/components/TeamAvailability';
|
||||
import Spinner from 'shared/components/Spinner.vue';
|
||||
|
@ -166,6 +167,9 @@ export default {
|
|||
startConversation() {
|
||||
this.isOnCollapsedView = !this.isOnCollapsedView;
|
||||
},
|
||||
closeChat() {
|
||||
IFrameHelper.sendMessage({ event: 'closeChat' });
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -227,7 +231,7 @@ export default {
|
|||
}
|
||||
|
||||
.input-wrap {
|
||||
padding: 0 $space-normal;
|
||||
padding: 0 $space-two;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
Loading…
Reference in a new issue