diff --git a/app/builders/messages/outgoing/normal_builder.rb b/app/builders/messages/outgoing/normal_builder.rb index 5d64608a3..a3c744341 100644 --- a/app/builders/messages/outgoing/normal_builder.rb +++ b/app/builders/messages/outgoing/normal_builder.rb @@ -7,10 +7,17 @@ class Messages::Outgoing::NormalBuilder @conversation = conversation @user = user @fb_id = params[:fb_id] + @attachment = params[:attachment] end def perform - @message = @conversation.messages.create!(message_params) + @message = @conversation.messages.build(message_params) + if @attachment + @message.attachment = Attachment.new(account_id: message.account_id) + @message.attachment.file.attach(@attachment[:file]) + end + @message.save + @message end private diff --git a/app/controllers/api/v1/widget/messages_controller.rb b/app/controllers/api/v1/widget/messages_controller.rb index 6e1eae9fe..2fe76f0eb 100644 --- a/app/controllers/api/v1/widget/messages_controller.rb +++ b/app/controllers/api/v1/widget/messages_controller.rb @@ -11,6 +11,10 @@ class Api::V1::Widget::MessagesController < Api::V1::Widget::BaseController def create @message = conversation.messages.new(message_params) @message.save! + if params[:message][:attachment].present? + @message.attachment = Attachment.new(account_id: @message.account_id) + @message.attachment.file.attach(params[:message][:attachment][:file]) + end render json: @message end diff --git a/app/javascript/dashboard/api/inbox/message.js b/app/javascript/dashboard/api/inbox/message.js index 3cea5f7a8..2e1669ef6 100644 --- a/app/javascript/dashboard/api/inbox/message.js +++ b/app/javascript/dashboard/api/inbox/message.js @@ -19,6 +19,17 @@ class MessageApi extends ApiClient { params: { before }, }); } + + sendAttachment([conversationId, { file, file_type }]) { + const formData = new FormData(); + formData.append('attachment[file]', file); + formData.append('attachment[file_type]', file_type); + return axios({ + method: 'post', + url: `${this.url}/${conversationId}/messages`, + data: formData, + }); + } } export default new MessageApi(); diff --git a/app/javascript/dashboard/assets/scss/widgets/_conversation-view.scss b/app/javascript/dashboard/assets/scss/widgets/_conversation-view.scss index e179d156d..ecf682b2d 100644 --- a/app/javascript/dashboard/assets/scss/widgets/_conversation-view.scss +++ b/app/javascript/dashboard/assets/scss/widgets/_conversation-view.scss @@ -31,26 +31,36 @@ } .image { - @include flex; - align-items: flex-end; - justify-content: center; - text-align: center; - - img { - @include padding($space-small); - max-height: 30rem; - max-width: 20rem; - } + cursor: pointer; + position: relative; .time { - margin-left: -$space-large; + bottom: $space-smaller; + color: $color-white; + position: absolute; + right: $space-small; white-space: nowrap; } + .modal-container { + text-align: center; + } + .modal-image { - max-height: 80%; max-width: 80%; } + + &::before { + $color-black: #000; + background-image: linear-gradient(-180deg, transparent 3%, $color-black 70%); + bottom: 0; + content: ''; + height: 20%; + left: 0; + opacity: .8; + position: absolute; + width: 100%; + } } .map { diff --git a/app/javascript/dashboard/assets/scss/widgets/_reply-box.scss b/app/javascript/dashboard/assets/scss/widgets/_reply-box.scss index 15dde2966..d0bab2074 100644 --- a/app/javascript/dashboard/assets/scss/widgets/_reply-box.scss +++ b/app/javascript/dashboard/assets/scss/widgets/_reply-box.scss @@ -47,7 +47,7 @@ } } - >.icon { + .icon { color: $medium-gray; cursor: pointer; font-size: $font-size-medium; @@ -58,6 +58,16 @@ } } + .file-uploads>label { + cursor: pointer; + } + + .attachment { + cursor: pointer; + margin-right: $space-one; + padding: 0 $space-small; + } + >textarea { @include ghost-input(); @include margin(0); diff --git a/app/javascript/dashboard/components/widgets/conversation/Message.vue b/app/javascript/dashboard/components/widgets/conversation/Message.vue index b36e70d51..a398ab52a 100644 --- a/app/javascript/dashboard/components/widgets/conversation/Message.vue +++ b/app/javascript/dashboard/components/widgets/conversation/Message.vue @@ -1,10 +1,7 @@ - + diff --git a/app/javascript/widget/components/ChatMessage.vue b/app/javascript/widget/components/ChatMessage.vue index 8c25fc36a..999064a83 100755 --- a/app/javascript/widget/components/ChatMessage.vue +++ b/app/javascript/widget/components/ChatMessage.vue @@ -4,17 +4,7 @@ :message="message.content" :status="message.status" /> - + diff --git a/app/javascript/widget/components/ImageBubble.vue b/app/javascript/widget/components/ImageBubble.vue new file mode 100644 index 000000000..380a93cb6 --- /dev/null +++ b/app/javascript/widget/components/ImageBubble.vue @@ -0,0 +1,50 @@ + + + + diff --git a/app/models/attachment.rb b/app/models/attachment.rb index 1c7ac75b6..79ec890cf 100644 --- a/app/models/attachment.rb +++ b/app/models/attachment.rb @@ -15,8 +15,6 @@ # message_id :integer not null # -require 'uri' -require 'open-uri' class Attachment < ApplicationRecord include Rails.application.routes.url_helpers belongs_to :account @@ -38,7 +36,7 @@ class Attachment < ApplicationRecord { extension: extension, data_url: file_url, - thumb_url: file.try(:thumb).try(:url) # will exist only for images + thumb_url: thumb_url } end @@ -70,4 +68,12 @@ class Attachment < ApplicationRecord def file_url file.attached? ? url_for(file) : '' end + + def thumb_url + if file.attached? && file.representable? + url_for(file.representation(resize: '250x250')) + else + '' + end + end end diff --git a/package.json b/package.json index 1cd6b0e6e..88dd2a49b 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "vue-router": "~2.2.0", "vue-select": "~2.0.0", "vue-template-compiler": "^2.6.10", + "vue-upload-component": "^2.8.20", "vuelidate": "~0.7.5", "vuex": "~2.1.1", "vuex-router-sync": "~4.1.2" diff --git a/spec/controllers/api/v1/accounts/conversations/messages_controller_spec.rb b/spec/controllers/api/v1/accounts/conversations/messages_controller_spec.rb index a92731eec..226f4b14c 100644 --- a/spec/controllers/api/v1/accounts/conversations/messages_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/conversations/messages_controller_spec.rb @@ -30,6 +30,18 @@ RSpec.describe 'Conversation Messages API', type: :request do expect(conversation.messages.count).to eq(1) expect(conversation.messages.first.content).to eq(params[:message]) end + + it 'creates a new outgoing message with attachment' do + file = fixture_file_upload(Rails.root.join('spec/assets/avatar.png'), 'image/png') + params = { message: 'test-message', attachment: { file: file } } + + post api_v1_account_conversation_messages_url(account_id: account.id, conversation_id: conversation.display_id), + params: params, + headers: agent.create_new_auth_token + + expect(response).to have_http_status(:success) + expect(conversation.messages.last.attachment.file.present?).to eq(true) + end end context 'when it is an authenticated agent bot' do diff --git a/spec/controllers/api/v1/widget/messages_controller_spec.rb b/spec/controllers/api/v1/widget/messages_controller_spec.rb index ed838c2d0..109bc2ccc 100644 --- a/spec/controllers/api/v1/widget/messages_controller_spec.rb +++ b/spec/controllers/api/v1/widget/messages_controller_spec.rb @@ -44,6 +44,20 @@ RSpec.describe '/api/v1/widget/messages', type: :request do json_response = JSON.parse(response.body) expect(json_response['content']).to eq(message_params[:content]) end + + it 'creates attachment message in conversation' do + file = fixture_file_upload(Rails.root.join('spec/assets/avatar.png'), 'image/png') + message_params = { content: 'hello world', timestamp: Time.current, attachment: { file: file } } + post api_v1_widget_messages_url, + params: { website_token: web_widget.website_token, message: message_params }, + headers: { 'X-Auth-Token' => token } + + expect(response).to have_http_status(:success) + json_response = JSON.parse(response.body) + expect(json_response['content']).to eq(message_params[:content]) + + expect(conversation.messages.last.attachment.file.present?).to eq(true) + end end end diff --git a/yarn.lock b/yarn.lock index e095413a6..5482bda1b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10630,6 +10630,11 @@ vue-template-es2015-compiler@^1.6.0, vue-template-es2015-compiler@^1.9.0: resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825" integrity sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw== +vue-upload-component@^2.8.20: + version "2.8.20" + resolved "https://registry.yarnpkg.com/vue-upload-component/-/vue-upload-component-2.8.20.tgz#60824d3f20f3216dca90d8c86a5c980851b04ea0" + integrity sha512-zrnJvULu4rnZe36Ib2/AZrI/h/mmNbUJZ+acZD652PyumzbvjCOQeYHe00sGifTdYjzzS66CwhTT+ubZ2D0Aow== + vue@^2.5.8, vue@^2.6.0: version "2.6.11" resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.11.tgz#76594d877d4b12234406e84e35275c6d514125c5"