mirror of
				https://github.com/lingble/chatwoot.git
				synced 2025-10-31 19:17:48 +00:00 
			
		
		
		
	Chore: Cleanup attachment handling for Facebook & Whatsapp (#1051)
* Chore: Enable file upload for facebook messenger * Chore: Fix attachments * Chore: Fix Specs * Fix ReplyBox file attachment logic * Set default value for message Co-authored-by: Pranav Raj Sreepuram <pranavrajs@gmail.com>
This commit is contained in:
		| @@ -15,9 +15,9 @@ | ||||
|       /> | ||||
|       <resizable-text-area | ||||
|         ref="messageInput" | ||||
|         v-model="message" | ||||
|         v-model.trim="message" | ||||
|         class="input" | ||||
|         :placeholder="$t(messagePlaceHolder())" | ||||
|         :placeholder="messagePlaceHolder" | ||||
|         :min-height="4" | ||||
|         @focus="onFocus" | ||||
|         @blur="onBlur" | ||||
| @@ -25,46 +25,43 @@ | ||||
|       <file-upload | ||||
|         v-if="showFileUpload" | ||||
|         :size="4096 * 4096" | ||||
|         accept="jpg,jpeg,png,mp3,ogg,amr,pdf,mp4" | ||||
|         accept="image/*, application/pdf, audio/mpeg, video/mp4, audio/ogg" | ||||
|         @input-file="onFileUpload" | ||||
|       > | ||||
|         <i | ||||
|           v-if="!isUploading.image" | ||||
|           class="icon ion-android-attach attachment" | ||||
|         /> | ||||
|         <woot-spinner v-if="isUploading.image" /> | ||||
|         <i v-if="!isUploading" class="icon ion-android-attach attachment" /> | ||||
|         <woot-spinner v-if="isUploading" /> | ||||
|       </file-upload> | ||||
|       <i | ||||
|         class="icon ion-happy-outline" | ||||
|         :class="{ active: showEmojiPicker }" | ||||
|         @click="toggleEmojiPicker()" | ||||
|         @click="toggleEmojiPicker" | ||||
|       /> | ||||
|     </div> | ||||
|  | ||||
|     <div class="reply-box__bottom"> | ||||
|       <ul class="tabs"> | ||||
|         <li class="tabs-title" :class="{ 'is-active': !isPrivate }"> | ||||
|           <a href="#" @click="makeReply">{{ | ||||
|           <a href="#" @click="setReplyMode">{{ | ||||
|             $t('CONVERSATION.REPLYBOX.REPLY') | ||||
|           }}</a> | ||||
|         </li> | ||||
|         <li class="tabs-title is-private" :class="{ 'is-active': isPrivate }"> | ||||
|           <a href="#" @click="makePrivate">{{ | ||||
|           <a href="#" @click="setPrivateReplyMode">{{ | ||||
|             $t('CONVERSATION.REPLYBOX.PRIVATE_NOTE') | ||||
|           }}</a> | ||||
|         </li> | ||||
|         <li v-if="message.length" class="tabs-title message-length"> | ||||
|           <a :class="{ 'message-error': message.length > maxLength - 40 }"> | ||||
|             {{ message.length }} / {{ maxLength }} | ||||
|           </a> | ||||
|           <a :class="{ 'message-error': isMessageLengthReachingThreshold }">{{ | ||||
|             characterCountIndicator | ||||
|           }}</a> | ||||
|         </li> | ||||
|       </ul> | ||||
|       <button | ||||
|         type="button" | ||||
|         class="button send-button" | ||||
|         :disabled="disableButton()" | ||||
|         :disabled="isReplyButtonDisabled" | ||||
|         :class="{ | ||||
|           disabled: message.length === 0 || message.length > maxLength, | ||||
|           disabled: isReplyButtonDisabled, | ||||
|           warning: isPrivate, | ||||
|         }" | ||||
|         @click="sendMessage" | ||||
| @@ -93,6 +90,12 @@ import FileUpload from 'vue-upload-component'; | ||||
| import EmojiInput from '../emoji/EmojiInput'; | ||||
| import CannedResponse from './CannedResponse'; | ||||
| import ResizableTextArea from 'shared/components/ResizableTextArea'; | ||||
| import { | ||||
|   isEscape, | ||||
|   isEnter, | ||||
|   hasPressedShift, | ||||
| } from 'shared/helpers/KeyboardHelpers'; | ||||
| import { MESSAGE_MAX_LENGTH } from 'shared/helpers/MessageTypeHelper'; | ||||
|  | ||||
| export default { | ||||
|   components: { | ||||
| @@ -109,19 +112,38 @@ export default { | ||||
|       isFocused: false, | ||||
|       showEmojiPicker: false, | ||||
|       showCannedResponsesList: false, | ||||
|       isUploading: { | ||||
|         audio: false, | ||||
|         video: false, | ||||
|         image: false, | ||||
|       }, | ||||
|       isUploading: false, | ||||
|     }; | ||||
|   }, | ||||
|   computed: { | ||||
|     ...mapGetters({ | ||||
|       currentChat: 'getSelectedChat', | ||||
|     }), | ||||
|     ...mapGetters({ currentChat: 'getSelectedChat' }), | ||||
|     inboxId() { | ||||
|       return this.currentChat.inbox_id; | ||||
|     }, | ||||
|     inbox() { | ||||
|       return this.$store.getters['inboxes/getInbox'](this.inboxId); | ||||
|     }, | ||||
|     messagePlaceHolder() { | ||||
|       return this.isPrivate | ||||
|         ? this.$t('CONVERSATION.FOOTER.PRIVATE_MSG_INPUT') | ||||
|         : this.$t('CONVERSATION.FOOTER.MSG_INPUT'); | ||||
|     }, | ||||
|     isMessageLengthReachingThreshold() { | ||||
|       return this.message.length > this.maxLength - 40; | ||||
|     }, | ||||
|     characterCountIndicator() { | ||||
|       return `${this.message.length} / ${this.maxLength}`; | ||||
|     }, | ||||
|     isReplyButtonDisabled() { | ||||
|       const isMessageEmpty = !this.message.replace(/\n/g, '').length; | ||||
|       return ( | ||||
|         isMessageEmpty || | ||||
|         this.message.length === 0 || | ||||
|         this.message.length > this.maxLength | ||||
|       ); | ||||
|     }, | ||||
|     channelType() { | ||||
|       return this.currentChat.meta.channel; | ||||
|       return this.inbox.channel_type; | ||||
|     }, | ||||
|     conversationType() { | ||||
|       const { additional_attributes: additionalAttributes } = this.currentChat; | ||||
| @@ -129,18 +151,52 @@ export default { | ||||
|       return type || ''; | ||||
|     }, | ||||
|     maxLength() { | ||||
|       if (this.channelType === 'Channel::FacebookPage') { | ||||
|         return 640; | ||||
|       if (this.isPrivate) { | ||||
|         return MESSAGE_MAX_LENGTH.GENERAL; | ||||
|       } | ||||
|       if (this.channelType === 'Channel::TwitterProfile') { | ||||
|  | ||||
|       if (this.isAFacebookInbox) { | ||||
|         return MESSAGE_MAX_LENGTH.FACEBOOK; | ||||
|       } | ||||
|       if (this.isATwilioSMSChannel) { | ||||
|         return MESSAGE_MAX_LENGTH.TWILIO_SMS; | ||||
|       } | ||||
|       if (this.isATwitterInbox) { | ||||
|         if (this.conversationType === 'tweet') { | ||||
|           return 280; | ||||
|           return MESSAGE_MAX_LENGTH.TWEET; | ||||
|         } | ||||
|       } | ||||
|       return 10000; | ||||
|       return MESSAGE_MAX_LENGTH.GENERAL; | ||||
|     }, | ||||
|     isATwitterInbox() { | ||||
|       return this.channelType === 'Channel::TwitterProfile'; | ||||
|     }, | ||||
|     isAFacebookInbox() { | ||||
|       return this.channelType === 'Channel::FacebookPage'; | ||||
|     }, | ||||
|     isAWebWidgetInbox() { | ||||
|       return this.channelType === 'Channel::WebWidget'; | ||||
|     }, | ||||
|     isATwilioSMSChannel() { | ||||
|       const { phone_number: phoneNumber = '' } = this.inbox; | ||||
|       return ( | ||||
|         this.channelType === 'Channel::TwilioSms' && | ||||
|         !phoneNumber.startsWith('whatsapp') | ||||
|       ); | ||||
|     }, | ||||
|     isATwilioWhatsappChannel() { | ||||
|       const { phone_number: phoneNumber = '' } = this.inbox; | ||||
|       return ( | ||||
|         this.channelType === 'Channel::TwilioSms' && | ||||
|         phoneNumber.startsWith('whatsapp') | ||||
|       ); | ||||
|     }, | ||||
|     showFileUpload() { | ||||
|       return this.channelType === 'Channel::WebWidget'; | ||||
|       return ( | ||||
|         this.isAWebWidgetInbox || | ||||
|         this.isAFacebookInbox || | ||||
|         this.isATwilioWhatsappChannel | ||||
|       ); | ||||
|     }, | ||||
|     replyButtonLabel() { | ||||
|       if (this.isPrivate) { | ||||
| @@ -158,20 +214,18 @@ export default { | ||||
|     }, | ||||
|   }, | ||||
|   watch: { | ||||
|     message(val) { | ||||
|     message(updatedMessage) { | ||||
|       if (this.isPrivate) { | ||||
|         return; | ||||
|       } | ||||
|       const isSlashCommand = val[0] === '/'; | ||||
|       const hasNextWord = val.includes(' '); | ||||
|       const isSlashCommand = updatedMessage[0] === '/'; | ||||
|       const hasNextWord = updatedMessage.includes(' '); | ||||
|       const isShortCodeActive = isSlashCommand && !hasNextWord; | ||||
|       if (isShortCodeActive) { | ||||
|         this.showCannedResponsesList = true; | ||||
|         if (val.length > 1) { | ||||
|           const searchKey = val.substr(1, val.length); | ||||
|           this.$store.dispatch('getCannedResponse', { | ||||
|             searchKey, | ||||
|           }); | ||||
|         if (updatedMessage.length > 1) { | ||||
|           const searchKey = updatedMessage.substr(1, updatedMessage.length); | ||||
|           this.$store.dispatch('getCannedResponse', { searchKey }); | ||||
|         } else { | ||||
|           this.$store.dispatch('getCannedResponse'); | ||||
|         } | ||||
| @@ -188,26 +242,18 @@ export default { | ||||
|   }, | ||||
|   methods: { | ||||
|     handleKeyEvents(e) { | ||||
|       if (this.isEscape(e)) { | ||||
|       if (isEscape(e)) { | ||||
|         this.hideEmojiPicker(); | ||||
|         this.hideCannedResponse(); | ||||
|       } else if (this.isEnter(e)) { | ||||
|         if (!e.shiftKey) { | ||||
|       } else if (isEnter(e)) { | ||||
|         if (!hasPressedShift(e)) { | ||||
|           e.preventDefault(); | ||||
|           this.sendMessage(); | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     isEnter(e) { | ||||
|       return e.keyCode === 13; | ||||
|     }, | ||||
|     isEscape(e) { | ||||
|       return e.keyCode === 27; // ESCAPE | ||||
|     }, | ||||
|     async sendMessage() { | ||||
|       const isMessageEmpty = !this.message.replace(/\n/g, '').length; | ||||
|       if (isMessageEmpty) return; | ||||
|       if (this.message.length > this.maxLength) { | ||||
|       if (this.isReplyButtonDisabled) { | ||||
|         return; | ||||
|       } | ||||
|       const newMessage = this.message; | ||||
| @@ -231,11 +277,11 @@ export default { | ||||
|         this.message = message; | ||||
|       }, 100); | ||||
|     }, | ||||
|     makePrivate() { | ||||
|     setPrivateReplyMode() { | ||||
|       this.isPrivate = true; | ||||
|       this.$refs.messageInput.focus(); | ||||
|     }, | ||||
|     makeReply() { | ||||
|     setReplyMode() { | ||||
|       this.isPrivate = false; | ||||
|       this.$refs.messageInput.focus(); | ||||
|     }, | ||||
| @@ -258,7 +304,6 @@ export default { | ||||
|     hideCannedResponse() { | ||||
|       this.showCannedResponsesList = false; | ||||
|     }, | ||||
|  | ||||
|     onBlur() { | ||||
|       this.isFocused = false; | ||||
|       this.toggleTyping('off'); | ||||
| @@ -267,9 +312,8 @@ export default { | ||||
|       this.isFocused = true; | ||||
|       this.toggleTyping('on'); | ||||
|     }, | ||||
|  | ||||
|     toggleTyping(status) { | ||||
|       if (this.channelType === 'Channel::WebWidget' && !this.isPrivate) { | ||||
|       if (this.isAWebWidgetInbox && !this.isPrivate) { | ||||
|         const conversationId = this.currentChat.id; | ||||
|         this.$store.dispatch('conversationTypingStatus/toggleTyping', { | ||||
|           status, | ||||
| @@ -277,35 +321,19 @@ export default { | ||||
|         }); | ||||
|       } | ||||
|     }, | ||||
|     disableButton() { | ||||
|       const messageHasOnlyNewLines = !this.message.replace(/\n/g, '').length; | ||||
|       return ( | ||||
|         this.message.length === 0 || | ||||
|         this.message.length > 640 || | ||||
|         messageHasOnlyNewLines | ||||
|       ); | ||||
|     }, | ||||
|  | ||||
|     messagePlaceHolder() { | ||||
|       const placeHolder = this.isPrivate | ||||
|         ? 'CONVERSATION.FOOTER.PRIVATE_MSG_INPUT' | ||||
|         : 'CONVERSATION.FOOTER.MSG_INPUT'; | ||||
|       return placeHolder; | ||||
|     }, | ||||
|  | ||||
|     onFileUpload(file) { | ||||
|       if (!file) { | ||||
|         return; | ||||
|       } | ||||
|       this.isUploading.image = true; | ||||
|       this.isUploading = true; | ||||
|       this.$store | ||||
|         .dispatch('sendAttachment', [this.currentChat.id, { file: file.file }]) | ||||
|         .then(() => { | ||||
|           this.isUploading.image = false; | ||||
|           this.isUploading = false; | ||||
|           this.$emit('scrollToMessage'); | ||||
|         }) | ||||
|         .catch(() => { | ||||
|           this.isUploading.image = false; | ||||
|           this.isUploading = false; | ||||
|           this.$emit('scrollToMessage'); | ||||
|         }); | ||||
|     }, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Sojan Jose
					Sojan Jose