Compare commits

..
13 changed files with 144 additions and 50 deletions
@@ -144,6 +144,7 @@ import { FEATURE_FLAGS } from 'dashboard/featureFlags';
import {
ALLOWED_FILE_TYPES,
ALLOWED_FILE_TYPES_FOR_TWILIO_WHATSAPP,
ALLOWED_FILE_TYPES_FOR_LINE,
} from 'shared/constants/messages';
import VideoCallButton from '../VideoCallButton.vue';
import AIAssistanceButton from '../AIAssistanceButton.vue';
@@ -270,6 +271,9 @@ export default {
return this.showFileUpload || this.isNote;
},
showAudioRecorderButton() {
if (this.isALineChannel) {
return false;
}
// Disable audio recorder for safari browser as recording is not supported
const isSafari = /^((?!chrome|android|crios|fxios).)*safari/i.test(
navigator.userAgent
@@ -291,6 +295,9 @@ export default {
if (this.isATwilioWhatsAppChannel) {
return ALLOWED_FILE_TYPES_FOR_TWILIO_WHATSAPP;
}
if (this.isALineChannel) {
return ALLOWED_FILE_TYPES_FOR_LINE;
}
return ALLOWED_FILE_TYPES;
},
enableDragAndDrop() {
@@ -28,7 +28,6 @@
:message="inReplyTo"
:message-type="data.message_type"
:parent-has-attachments="hasAttachments"
@click="navigateToMessage"
/>
<bubble-text
v-if="data.content"
@@ -520,13 +519,6 @@ export default {
this.showBackgroundHighlight = false;
}, HIGHLIGHT_TIMER);
},
async navigateToMessage() {
this.$nextTick(() => {
bus.$emit(BUS_EVENTS.SCROLL_TO_MESSAGE, {
messageId: this.inReplyToMessageId,
});
});
},
},
};
</script>
@@ -377,19 +377,12 @@ export default {
removeBusListeners() {
bus.$off(BUS_EVENTS.SCROLL_TO_MESSAGE, this.onScrollToMessage);
},
async onScrollToMessage({ messageId }) {
// TODO: check if the message is already loaded
await this.$store.dispatch('setActiveChat', {
data: this.currentChat,
after: messageId,
force: true,
});
onScrollToMessage({ messageId = '' } = {}) {
this.$nextTick(() => {
const messageElement = document.getElementById('message' + messageId);
if (messageElement) {
this.isProgrammaticScroll = true;
messageElement.scrollIntoView();
messageElement.scrollIntoView({ behavior: 'smooth' });
this.fetchPreviousMessages();
} else {
this.scrollToBottom();
@@ -29,7 +29,6 @@
v-if="shouldShowReplyToMessage"
:message="inReplyTo"
@dismiss="resetReplyToMessage"
@navigate-to-message="navigateToMessage"
/>
<canned-response
v-if="showMentions && hasSlashCommand"
@@ -393,7 +392,8 @@ export default {
this.isAPIInbox ||
this.isAnEmailChannel ||
this.isASmsInbox ||
this.isATelegramChannel
this.isATelegramChannel ||
this.isALineChannel
);
},
replyButtonLabel() {
@@ -1159,12 +1159,6 @@ export default {
LocalStorage.deleteFromJsonStore(replyStorageKey, this.conversationId);
bus.$emit(BUS_EVENTS.TOGGLE_REPLY_TO_MESSAGE);
},
navigateToMessage(messageId) {
bus.$emit(BUS_EVENTS.SCROLL_TO_MESSAGE, {
messageId,
behavior: 'instant',
});
},
onNewConversationModalActive(isActive) {
// Issue is if the new conversation modal is open and we drag and drop the file
// then the file is not getting attached to the new conversation modal
@@ -1,7 +1,6 @@
<template>
<div
class="reply-editor bg-slate-50 dark:bg-slate-800 rounded-md py-1 pl-2 pr-1 text-xs tracking-wide mt-2 flex items-center gap-1.5 -mx-2 cursor-pointer"
@click="$emit('navigate-to-message', message.id)"
class="reply-editor bg-slate-50 dark:bg-slate-800 rounded-md py-1 pl-2 pr-1 text-xs tracking-wide mt-2 flex items-center gap-1.5 -mx-2"
>
<fluent-icon class="flex-shrink-0 icon" icon="arrow-reply" size="14" />
<div class="flex-grow gap-1 mt-px text-xs truncate">
@@ -1,13 +1,12 @@
<template>
<div
class="px-2 py-1.5 -mx-2 rounded-sm min-w-[15rem] mb-2 cursor-pointer"
class="px-2 py-1.5 rounded-sm min-w-[10rem] mb-2"
:class="{
'bg-slate-50 dark:bg-slate-600 dark:text-slate-50':
messageType === MESSAGE_TYPE.INCOMING,
'bg-woot-600 text-woot-50': messageType === MESSAGE_TYPE.OUTGOING,
'-mx-2': !parentHasAttachments,
}"
@click="$emit('click')"
>
<message-preview
:message="message"
@@ -163,7 +163,14 @@ export default {
return;
}
const { messageId } = this.$route.query;
bus.$emit(BUS_EVENTS.SCROLL_TO_MESSAGE, { messageId });
this.$store
.dispatch('setActiveChat', {
data: selectedConversation,
after: messageId,
})
.then(() => {
bus.$emit(BUS_EVENTS.SCROLL_TO_MESSAGE, { messageId });
});
} else {
this.$store.dispatch('clearSelectedState');
}
@@ -160,10 +160,10 @@ const actions = {
});
},
async setActiveChat({ commit, dispatch }, { data, after, force = false }) {
async setActiveChat({ commit, dispatch }, { data, after }) {
commit(types.SET_CURRENT_CHAT_WINDOW, data);
commit(types.CLEAR_ALL_MESSAGES_LOADED);
if (data.dataFetched === undefined || force) {
if (data.dataFetched === undefined) {
try {
await dispatch('fetchPreviousMessages', {
after,
@@ -55,6 +55,8 @@ export const ALLOWED_FILE_TYPES_FOR_TWILIO_WHATSAPP =
'audio/mpeg, audio/opus, audio/ogg, audio/amr,' +
'video/mp4,' +
'application/pdf,';
// https://developers.line.biz/en/reference/messaging-api/#image-message, https://developers.line.biz/en/reference/messaging-api/#video-message
export const ALLOWED_FILE_TYPES_FOR_LINE = 'image/png, image/jpeg,video/mp4';
export const CSAT_RATINGS = [
{
+33 -16
View File
@@ -42,25 +42,42 @@ class Line::IncomingMessageService
end
def attach_files(message)
return unless message_type_non_text?(message['type'])
return unless message_type_non_text?(message['type']) || message['type'] == 'sticker'
response = inbox.channel.client.get_message_content(message['id'])
if message['type'] == 'sticker'
attachment_file = Down.download(
"https://stickershop.line-scdn.net/stickershop/v1/sticker/#{message[:stickerId]}/iphone/sticker.png"
)
file_name = "media-#{message['id']}.#{response.content_type.split('/')[1]}"
temp_file = Tempfile.new(file_name)
temp_file.binmode
temp_file << response.body
temp_file.rewind
@message.attachments.new(
account_id: @message.account_id,
file_type: 'image',
file: {
io: attachment_file,
filename: "sticker-#{message['stickerId']}.png",
content_type: attachment_file.content_type
}
)
else
response = inbox.channel.client.get_message_content(message['id'])
@message.attachments.new(
account_id: @message.account_id,
file_type: file_content_type(response),
file: {
io: temp_file,
filename: file_name,
content_type: response.content_type
}
)
file_name = "media-#{message['id']}.#{response.content_type.split('/')[1]}"
temp_file = Tempfile.new(file_name)
temp_file.binmode
temp_file << response.body
temp_file.rewind
@message.attachments.new(
account_id: @message.account_id,
file_type: file_content_type(response),
file: {
io: temp_file,
filename: file_name,
content_type: response.content_type
}
)
end
@message.save!
end
+33 -1
View File
@@ -6,7 +6,8 @@ class Line::SendOnLineService < Base::SendOnChannelService
end
def perform_reply
response = channel.client.push_message(message.conversation.contact_inbox.source_id, [{ type: 'text', text: message.content }])
response = channel.client.push_message(message.conversation.contact_inbox.source_id, build_payload)
return if response.blank?
parsed_json = JSON.parse(response.body)
@@ -20,6 +21,37 @@ class Line::SendOnLineService < Base::SendOnChannelService
end
end
def build_payload
if message.content && message.attachments.any?
[text_message, *attachments]
elsif message.content.nil? && message.attachments.any?
attachments
else
text_message
end
end
def attachments
message.attachments.map do |attachment|
# Support only image and video for now, https://developers.line.biz/en/reference/messaging-api/#image-message
next unless attachment.file_type == 'image' || attachment.file_type == 'video'
{
type: attachment.file_type,
originalContentUrl: attachment.download_url,
previewImageUrl: attachment.download_url
}
end
end
# https://developers.line.biz/en/reference/messaging-api/#text-message
def text_message
{
type: 'text',
text: message.content
}
end
# https://developers.line.biz/en/reference/messaging-api/#error-responses
def external_error(error)
# Message containing information about the error. See https://developers.line.biz/en/reference/messaging-api/#error-messages
+1 -1
View File
@@ -104,7 +104,7 @@ RUN apk update && apk add --no-cache \
&& gem install bundler
RUN if [ "$RAILS_ENV" != "production" ]; then \
apk add --no-cache nodejs yarn; \
apk add --no-cache nodejs-current yarn; \
fi
COPY --from=pre-builder /gems/ /gems/
@@ -92,5 +92,57 @@ describe Line::SendOnLineService do
expect(message.status).to eq('delivered')
end
end
context 'with message attachments' do
it 'sends the message with text and attachments' do
attachment = message.attachments.new(account_id: message.account_id, file_type: :image)
attachment.file.attach(io: Rails.root.join('spec/assets/avatar.png').open, filename: 'avatar.png', content_type: 'image/png')
expected_url_regex = %r{rails/active_storage/disk/[a-zA-Z0-9=_\-+]+/avatar\.png}
expect(line_client).to receive(:push_message).with(
message.conversation.contact_inbox.source_id,
[
{ type: 'text', text: message.content },
{
type: 'image',
originalContentUrl: match(expected_url_regex),
previewImageUrl: match(expected_url_regex)
}
]
)
described_class.new(message: message).perform
end
it 'sends the message with attachments only' do
attachment = message.attachments.new(account_id: message.account_id, file_type: :image)
attachment.file.attach(io: Rails.root.join('spec/assets/avatar.png').open, filename: 'avatar.png', content_type: 'image/png')
message.update!(content: nil)
expected_url_regex = %r{rails/active_storage/disk/[a-zA-Z0-9=_\-+]+/avatar\.png}
expect(line_client).to receive(:push_message).with(
message.conversation.contact_inbox.source_id,
[
{
type: 'image',
originalContentUrl: match(expected_url_regex),
previewImageUrl: match(expected_url_regex)
}
]
)
described_class.new(message: message).perform
end
it 'sends the message with text only' do
message.attachments.destroy_all
expect(line_client).to receive(:push_message).with(
message.conversation.contact_inbox.source_id,
{ type: 'text', text: message.content }
)
described_class.new(message: message).perform
end
end
end
end