From 6c4e1fdaac45cb91eefc7342580a08069f05ccb6 Mon Sep 17 00:00:00 2001 From: Nithin David Thomas Date: Mon, 30 Mar 2020 12:15:06 +0530 Subject: [PATCH] Feature: Send images from widget --- app/javascript/widget/api/conversation.js | 8 ++- app/javascript/widget/api/endPoints.js | 17 +++++ .../widget/assets/images/paperclip.svg | 1 + .../widget/components/AgentMessage.vue | 11 +++- .../widget/components/ChatAttachment.vue | 66 +++++++++++++++++++ .../widget/components/ChatFooter.vue | 19 ++++-- .../widget/components/ChatInputWrap.vue | 7 ++ .../widget/components/ChatMessage.vue | 6 +- .../widget/components/ImageBubble.vue | 50 +++++++------- .../widget/components/UserMessage.vue | 58 +++++++++++++--- .../widget/components/UserMessageBubble.vue | 20 +++--- .../widget/store/modules/conversation.js | 43 +++++++++++- .../specs/conversation/actions.spec.js | 26 ++++++++ .../specs/conversation/mutations.spec.js | 25 +++++++ .../modules/specs/conversation/utils.spec.js | 2 +- app/javascript/widget/views/Home.vue | 13 +--- 16 files changed, 305 insertions(+), 67 deletions(-) create mode 100644 app/javascript/widget/assets/images/paperclip.svg create mode 100755 app/javascript/widget/components/ChatAttachment.vue diff --git a/app/javascript/widget/api/conversation.js b/app/javascript/widget/api/conversation.js index b7a243f67..542d8a213 100755 --- a/app/javascript/widget/api/conversation.js +++ b/app/javascript/widget/api/conversation.js @@ -7,10 +7,16 @@ const sendMessageAPI = async content => { return result; }; +const sendAttachmentAPI = async attachment => { + const urlData = endPoints.sendAttachmnet(attachment); + const result = await API.post(urlData.url, urlData.params); + return result; +}; + const getConversationAPI = async ({ before }) => { const urlData = endPoints.getConversation({ before }); const result = await API.get(urlData.url, { params: urlData.params }); return result; }; -export { sendMessageAPI, getConversationAPI }; +export { sendMessageAPI, getConversationAPI, sendAttachmentAPI }; diff --git a/app/javascript/widget/api/endPoints.js b/app/javascript/widget/api/endPoints.js index 2a36bc793..36cae18ba 100755 --- a/app/javascript/widget/api/endPoints.js +++ b/app/javascript/widget/api/endPoints.js @@ -9,6 +9,22 @@ const sendMessage = content => ({ }, }); +const sendAttachmnet = ({ attachment }) => { + const { refererURL = '' } = window; + const timestamp = new Date().toString(); + const { file, file_type: fileType } = attachment; + + const formData = new FormData(); + formData.append('message[attachment][file]', file); + formData.append('message[attachment][file_type]', fileType); + formData.append('message[referer_url]', refererURL); + formData.append('message[timestamp]', timestamp); + return { + url: `/api/v1/widget/messages${window.location.search}`, + params: formData, + }; +}; + const getConversation = ({ before }) => ({ url: `/api/v1/widget/messages${window.location.search}`, params: { before }, @@ -27,6 +43,7 @@ const getAvailableAgents = token => ({ export default { sendMessage, + sendAttachmnet, getConversation, updateContact, getAvailableAgents, diff --git a/app/javascript/widget/assets/images/paperclip.svg b/app/javascript/widget/assets/images/paperclip.svg new file mode 100644 index 000000000..b1f69b7a7 --- /dev/null +++ b/app/javascript/widget/assets/images/paperclip.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/javascript/widget/components/AgentMessage.vue b/app/javascript/widget/components/AgentMessage.vue index 62cc64641..d0ffd1f3c 100755 --- a/app/javascript/widget/components/AgentMessage.vue +++ b/app/javascript/widget/components/AgentMessage.vue @@ -17,10 +17,10 @@ :message-type="messageType" :message="message.content" /> -
+
@@ -53,9 +53,14 @@ export default { }, }, computed: { + hasImage() { + const { attachment = {} } = this.message; + const { file_type: fileType } = attachment; + return fileType === 'image'; + }, showTextBubble() { const { message } = this; - return !!message.content && !message.attachment; + return !!message.content; }, readableTime() { const { created_at: createdAt = '' } = this.message; diff --git a/app/javascript/widget/components/ChatAttachment.vue b/app/javascript/widget/components/ChatAttachment.vue new file mode 100755 index 000000000..88169d341 --- /dev/null +++ b/app/javascript/widget/components/ChatAttachment.vue @@ -0,0 +1,66 @@ + + + + diff --git a/app/javascript/widget/components/ChatFooter.vue b/app/javascript/widget/components/ChatFooter.vue index 01dbb16ac..dbfbb5633 100755 --- a/app/javascript/widget/components/ChatFooter.vue +++ b/app/javascript/widget/components/ChatFooter.vue @@ -1,10 +1,14 @@ diff --git a/app/javascript/widget/components/UserMessage.vue b/app/javascript/widget/components/UserMessage.vue index 3a15fa518..8dea5f14b 100755 --- a/app/javascript/widget/components/UserMessage.vue +++ b/app/javascript/widget/components/UserMessage.vue @@ -1,25 +1,58 @@ diff --git a/app/javascript/widget/store/modules/conversation.js b/app/javascript/widget/store/modules/conversation.js index 47ed94656..09d172ab9 100755 --- a/app/javascript/widget/store/modules/conversation.js +++ b/app/javascript/widget/store/modules/conversation.js @@ -1,6 +1,10 @@ /* eslint-disable no-param-reassign */ import Vue from 'vue'; -import { sendMessageAPI, getConversationAPI } from 'widget/api/conversation'; +import { + sendMessageAPI, + getConversationAPI, + sendAttachmentAPI, +} from 'widget/api/conversation'; import { MESSAGE_TYPE } from 'widget/helpers/constants'; import { playNotificationAudio } from 'shared/helpers/AudioNotificationHelper'; import getUuid from '../../helpers/uuid'; @@ -8,11 +12,12 @@ import DateHelper from '../../../shared/helpers/DateHelper'; const groupBy = require('lodash.groupby'); -export const createTemporaryMessage = content => { +export const createTemporaryMessage = ({ attachment, content }) => { const timestamp = new Date().getTime() / 1000; return { id: getUuid(), content, + attachment, status: 'in_progress', created_at: timestamp, message_type: MESSAGE_TYPE.INCOMING, @@ -78,10 +83,29 @@ export const getters = { export const actions = { sendMessage: async ({ commit }, params) => { const { content } = params; - commit('pushMessageToConversation', createTemporaryMessage(content)); + commit('pushMessageToConversation', createTemporaryMessage({ content })); await sendMessageAPI(content); }, + sendAttachment: async ({ commit }, params) => { + const { attachment } = params; + const { thumbUrl } = attachment; + const attachmentBlob = { + thumb_url: thumbUrl, + data_url: thumbUrl, + file_type: 'image', + status: 'in_progress', + }; + const tempMessage = createTemporaryMessage({ attachment: attachmentBlob }); + commit('pushMessageToConversation', tempMessage); + try { + const { data } = await sendAttachmentAPI(params); + commit('setMessageStatus', { message: data, tempId: tempMessage.id }); + } catch (error) { + // Show error + } + }, + fetchOldConversations: async ({ commit }, { before } = {}) => { try { commit('setConversationListLoading', true); @@ -126,6 +150,19 @@ export const mutations = { } }, + setMessageStatus($state, { message, tempId }) { + const { status, id } = message; + const messagesInbox = $state.conversations; + + const messageInConversation = messagesInbox[tempId]; + + if (messageInConversation) { + Vue.delete(messagesInbox, tempId); + const newMessage = { ...messageInConversation }; + Vue.set(messagesInbox, id, { ...newMessage, id, status }); + } + }, + setConversationListLoading($state, status) { $state.uiFlags.isFetchingList = status; }, diff --git a/app/javascript/widget/store/modules/specs/conversation/actions.spec.js b/app/javascript/widget/store/modules/specs/conversation/actions.spec.js index 1482d276c..c2f8c9a90 100644 --- a/app/javascript/widget/store/modules/specs/conversation/actions.spec.js +++ b/app/javascript/widget/store/modules/specs/conversation/actions.spec.js @@ -42,4 +42,30 @@ describe('#actions', () => { }); }); }); + + describe('#sendAttachment', () => { + it('sends correct mutations', () => { + const mockDate = new Date(1466424490000); + getUuid.mockImplementationOnce(() => '1111'); + const spy = jest.spyOn(global, 'Date').mockImplementation(() => mockDate); + const thumbUrl = ''; + const attachment = { thumbUrl }; + + actions.sendAttachment({ commit }, { attachment }); + spy.mockRestore(); + expect(commit).toBeCalledWith('pushMessageToConversation', { + id: '1111', + content: undefined, + status: 'in_progress', + created_at: 1466424490, + message_type: 0, + attachment: { + thumb_url: '', + data_url: '', + file_type: 'image', + status: 'in_progress', + }, + }); + }); + }); }); diff --git a/app/javascript/widget/store/modules/specs/conversation/mutations.spec.js b/app/javascript/widget/store/modules/specs/conversation/mutations.spec.js index b465ac92e..4c3c7ae18 100644 --- a/app/javascript/widget/store/modules/specs/conversation/mutations.spec.js +++ b/app/javascript/widget/store/modules/specs/conversation/mutations.spec.js @@ -92,4 +92,29 @@ describe('#mutations', () => { expect(state.uiFlags.allMessagesLoaded).toEqual(false); }); }); + + describe('#setMessageStatus', () => { + it('Updates status of loading messages if payload is not empty', () => { + const state = { + conversations: { + rand_id_123: { + content: '', + id: 'rand_id_123', + message_type: 0, + status: 'in_progress', + }, + }, + }; + const message = { + id: '1', + content: '', + status: 'sent', + }; + mutations.setMessageStatus(state, { message, tempId: 'rand_id_123' }); + + expect(state.conversations).toEqual({ + 1: { id: '1', content: '', message_type: 0, status: 'sent' }, + }); + }); + }); }); diff --git a/app/javascript/widget/store/modules/specs/conversation/utils.spec.js b/app/javascript/widget/store/modules/specs/conversation/utils.spec.js index f7843de5c..1a09f3f64 100644 --- a/app/javascript/widget/store/modules/specs/conversation/utils.spec.js +++ b/app/javascript/widget/store/modules/specs/conversation/utils.spec.js @@ -30,7 +30,7 @@ describe('#findUndeliveredMessage', () => { describe('#createTemporaryMessage', () => { it('returns message object', () => { - const message = createTemporaryMessage('hello'); + const message = createTemporaryMessage({ content: 'hello' }); expect(message.content).toBe('hello'); expect(message.status).toBe('in_progress'); }); diff --git a/app/javascript/widget/views/Home.vue b/app/javascript/widget/views/Home.vue index 20d86fdd7..c523a1c6c 100755 --- a/app/javascript/widget/views/Home.vue +++ b/app/javascript/widget/views/Home.vue @@ -8,7 +8,7 @@ @@ -16,7 +16,7 @@