diff --git a/app/javascript/dashboard/components/widgets/conversation/components/GalleryView.vue b/app/javascript/dashboard/components/widgets/conversation/components/GalleryView.vue index aa17dabf9..6b356f4d3 100644 --- a/app/javascript/dashboard/components/widgets/conversation/components/GalleryView.vue +++ b/app/javascript/dashboard/components/widgets/conversation/components/GalleryView.vue @@ -22,6 +22,10 @@ const props = defineProps({ type: Array, required: true, }, + autoPlay: { + type: Boolean, + default: false, + }, }); const emit = defineEmits(['close']); @@ -309,6 +313,7 @@ onMounted(() => { :src="activeAttachment.data_url" controls playsInline + :autoplay="autoPlay" class="max-h-full max-w-full object-contain" @click.stop /> @@ -317,6 +322,7 @@ onMounted(() => { v-if="isAudio" :key="activeAttachment.message_id" controls + :autoplay="autoPlay" class="w-full max-w-md" @click.stop > diff --git a/app/javascript/dashboard/composables/useUISettings.js b/app/javascript/dashboard/composables/useUISettings.js index 9f1943414..4f015e1d8 100644 --- a/app/javascript/dashboard/composables/useUISettings.js +++ b/app/javascript/dashboard/composables/useUISettings.js @@ -7,6 +7,7 @@ export const DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER = Object.freeze([ { name: 'conversation_info' }, { name: 'contact_attributes' }, { name: 'contact_notes' }, + { name: 'shared_files' }, { name: 'previous_conversation' }, { name: 'conversation_participants' }, { name: 'linear_issues' }, diff --git a/app/javascript/dashboard/i18n/locale/en/conversation.json b/app/javascript/dashboard/i18n/locale/en/conversation.json index 88e66ebe3..2dc3ed70e 100644 --- a/app/javascript/dashboard/i18n/locale/en/conversation.json +++ b/app/javascript/dashboard/i18n/locale/en/conversation.json @@ -365,7 +365,19 @@ "PREVIOUS_CONVERSATION": "Previous Conversations", "MACROS": "Macros", "LINEAR_ISSUES": "Linked Linear Issues", - "SHOPIFY_ORDERS": "Shopify Orders" + "SHOPIFY_ORDERS": "Shopify Orders", + "SHARED_FILES": "Attachments" + }, + "SHARED_FILES": { + "EMPTY": "No attachments yet", + "DOWNLOAD": "Download file", + "DOWNLOAD_ERROR": "Could not download the file. Please try again.", + "MEDIA_HEADING": "Media", + "FILES_HEADING": "Files", + "VIEW_ALL": "View all", + "SHOW_LESS": "Show less", + "MORE_COUNT": "+{count}", + "UNTITLED_FILE": "Untitled file" }, "SHOPIFY": { "ORDER_ID": "Order #{id}", diff --git a/app/javascript/dashboard/routes/dashboard/conversation/ContactPanel.vue b/app/javascript/dashboard/routes/dashboard/conversation/ContactPanel.vue index 653b43840..fe95f9e99 100644 --- a/app/javascript/dashboard/routes/dashboard/conversation/ContactPanel.vue +++ b/app/javascript/dashboard/routes/dashboard/conversation/ContactPanel.vue @@ -17,6 +17,7 @@ import ContactInfo from './contact/ContactInfo.vue'; import ContactNotes from './contact/ContactNotes.vue'; import ConversationInfo from './ConversationInfo.vue'; import CustomAttributes from './customAttributes/CustomAttributes.vue'; +import SharedFiles from './SharedFiles.vue'; import Draggable from 'vuedraggable'; import MacrosList from './Macros/List.vue'; import ShopifyOrdersList from 'dashboard/components/widgets/conversation/ShopifyOrdersList.vue'; @@ -297,6 +298,18 @@ onMounted(() => { +
+ + + +
diff --git a/app/javascript/dashboard/routes/dashboard/conversation/SharedFiles.vue b/app/javascript/dashboard/routes/dashboard/conversation/SharedFiles.vue new file mode 100644 index 000000000..272407a53 --- /dev/null +++ b/app/javascript/dashboard/routes/dashboard/conversation/SharedFiles.vue @@ -0,0 +1,421 @@ + + + diff --git a/app/javascript/dashboard/store/modules/conversations/getters.js b/app/javascript/dashboard/store/modules/conversations/getters.js index 333009707..5e85c423c 100644 --- a/app/javascript/dashboard/store/modules/conversations/getters.js +++ b/app/javascript/dashboard/store/modules/conversations/getters.js @@ -57,6 +57,8 @@ const getters = { getSelectedChatAttachments: ({ selectedChatId, attachments }) => { return attachments[selectedChatId] || []; }, + getSelectedChatAttachmentsLoaded: ({ selectedChatId, attachments }) => + selectedChatId !== null && attachments[selectedChatId] !== undefined, getChatListFilters: ({ conversationFilters }) => conversationFilters, getLastEmailInSelectedChat: (stage, _getters) => { const selectedChat = _getters.getSelectedChat; diff --git a/app/javascript/dashboard/store/modules/specs/conversations/getters.spec.js b/app/javascript/dashboard/store/modules/specs/conversations/getters.spec.js index 69bf9c2ac..a7ee3833e 100644 --- a/app/javascript/dashboard/store/modules/specs/conversations/getters.spec.js +++ b/app/javascript/dashboard/store/modules/specs/conversations/getters.spec.js @@ -328,6 +328,31 @@ describe('#getters', () => { }); }); + describe('#getSelectedChatAttachmentsLoaded', () => { + it('returns true when attachments have been fetched for the selected chat', () => { + const state = { selectedChatId: 1, attachments: { 1: [] } }; + expect(getters.getSelectedChatAttachmentsLoaded(state)).toBe(true); + }); + + it('returns true when the fetched attachment list is non-empty', () => { + const state = { + selectedChatId: 1, + attachments: { 1: [{ id: 1, file_name: 'test' }] }, + }; + expect(getters.getSelectedChatAttachmentsLoaded(state)).toBe(true); + }); + + it('returns false when attachments have not been fetched yet', () => { + const state = { selectedChatId: 1, attachments: {} }; + expect(getters.getSelectedChatAttachmentsLoaded(state)).toBe(false); + }); + + it('returns false when no chat is selected', () => { + const state = { selectedChatId: null, attachments: {} }; + expect(getters.getSelectedChatAttachmentsLoaded(state)).toBe(false); + }); + }); + describe('#getContextMenuChatId', () => { it('returns the context menu chat id', () => { const state = { contextMenuChatId: 1 }; diff --git a/app/views/api/v1/accounts/conversations/attachments.json.jbuilder b/app/views/api/v1/accounts/conversations/attachments.json.jbuilder index 167b18390..8bd647f27 100644 --- a/app/views/api/v1/accounts/conversations/attachments.json.jbuilder +++ b/app/views/api/v1/accounts/conversations/attachments.json.jbuilder @@ -3,6 +3,7 @@ json.meta do end json.payload @attachments do |attachment| + json.id attachment.push_event_data[:id] json.message_id attachment.push_event_data[:message_id] json.thumb_url attachment.push_event_data[:thumb_url] json.data_url attachment.push_event_data[:data_url] diff --git a/spec/controllers/api/v1/accounts/conversations_controller_spec.rb b/spec/controllers/api/v1/accounts/conversations_controller_spec.rb index e007f4900..19d080b47 100644 --- a/spec/controllers/api/v1/accounts/conversations_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/conversations_controller_spec.rb @@ -1039,6 +1039,8 @@ RSpec.describe 'Conversations API', type: :request do expect(response).to have_http_status(:success) response_body = response.parsed_body + attachment = conversation.messages.last.attachments.first + expect(response_body['payload'].first['id']).to eq(attachment.id) expect(response_body['payload'].first['file_type']).to eq('image') expect(response_body['payload'].first['sender']['id']).to eq(conversation.messages.last.sender.id) end