From c26490e9c14606e7594c0cc5390f5efdd3a940ae Mon Sep 17 00:00:00 2001 From: Fayaz Ahmed Date: Tue, 13 Aug 2024 09:21:54 +0530 Subject: [PATCH] chore: Replace messageMixing with useMessage composable [CW-3475] (#9942) # Pull Request Template ## Description Replaces the messageMixin with the new useMessage composable Fixes https://linear.app/chatwoot/issue/CW-3475/rewrite-messagemixin-mixin-to-a-composable ## Type of change Please delete options that are not relevant. - [ ] Bug fix (non-breaking change which fixes an issue) - [x] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality not to work as expected) - [ ] This change requires a documentation update --- .../widget/components/AgentMessage.vue | 13 +++- .../widget/components/UserMessage.vue | 9 ++- .../composables/specs/useMessage.spec.js | 71 +++++++++++++++++++ .../widget/composables/useMessage.js | 22 ++++++ app/javascript/widget/mixins/messageMixin.js | 13 ---- .../widget/mixins/specs/messageMixin.spec.js | 37 ---------- 6 files changed, 111 insertions(+), 54 deletions(-) create mode 100644 app/javascript/widget/composables/specs/useMessage.spec.js create mode 100644 app/javascript/widget/composables/useMessage.js delete mode 100644 app/javascript/widget/mixins/messageMixin.js delete mode 100644 app/javascript/widget/mixins/specs/messageMixin.spec.js diff --git a/app/javascript/widget/components/AgentMessage.vue b/app/javascript/widget/components/AgentMessage.vue index 3296d5128..e7d592abd 100755 --- a/app/javascript/widget/components/AgentMessage.vue +++ b/app/javascript/widget/components/AgentMessage.vue @@ -9,7 +9,7 @@ import FileBubble from 'widget/components/FileBubble.vue'; import Thumbnail from 'dashboard/components/widgets/Thumbnail.vue'; import { MESSAGE_TYPE } from 'widget/helpers/constants'; import configMixin from '../mixins/configMixin'; -import messageMixin from '../mixins/messageMixin'; +import { useMessage } from '../composables/useMessage'; import { isASubmittedFormMessage } from 'shared/helpers/MessageTypeHelper'; import darkModeMixin from 'widget/mixins/darkModeMixin.js'; import ReplyToChip from 'widget/components/ReplyToChip.vue'; @@ -28,7 +28,7 @@ export default { MessageReplyButton, ReplyToChip, }, - mixins: [configMixin, messageMixin, darkModeMixin], + mixins: [configMixin, darkModeMixin], props: { message: { type: Object, @@ -39,6 +39,15 @@ export default { default: () => {}, }, }, + setup(props) { + const { messageContentAttributes, hasAttachments } = useMessage( + props.message + ); + return { + messageContentAttributes, + hasAttachments, + }; + }, data() { return { hasImageError: false, diff --git a/app/javascript/widget/components/UserMessage.vue b/app/javascript/widget/components/UserMessage.vue index 7f8528e08..7c7d844e9 100755 --- a/app/javascript/widget/components/UserMessage.vue +++ b/app/javascript/widget/components/UserMessage.vue @@ -6,7 +6,7 @@ import VideoBubble from 'widget/components/VideoBubble.vue'; import FluentIcon from 'shared/components/FluentIcon/Index.vue'; import FileBubble from 'widget/components/FileBubble.vue'; import { messageStamp } from 'shared/helpers/timeHelper'; -import messageMixin from '../mixins/messageMixin'; +import { useMessage } from '../composables/useMessage'; import ReplyToChip from 'widget/components/ReplyToChip.vue'; import DragWrapper from 'widget/components/DragWrapper.vue'; import { BUS_EVENTS } from 'shared/constants/busEvents'; @@ -25,7 +25,6 @@ export default { ReplyToChip, DragWrapper, }, - mixins: [messageMixin], props: { message: { type: Object, @@ -36,6 +35,12 @@ export default { default: () => {}, }, }, + setup(props) { + const { hasAttachments } = useMessage(props.message); + return { + hasAttachments, + }; + }, data() { return { hasImageError: false, diff --git a/app/javascript/widget/composables/specs/useMessage.spec.js b/app/javascript/widget/composables/specs/useMessage.spec.js new file mode 100644 index 000000000..1498c87af --- /dev/null +++ b/app/javascript/widget/composables/specs/useMessage.spec.js @@ -0,0 +1,71 @@ +import { describe, it, expect } from 'vitest'; +import { reactive, nextTick } from 'vue'; +import { useMessage } from '../useMessage'; + +describe('useMessage', () => { + it('should handle deleted messages', () => { + const message = reactive({ + content_attributes: { deleted: true }, + attachments: [], + }); + + const { messageContentAttributes, hasAttachments } = useMessage(message); + + expect(messageContentAttributes.value).toEqual({ deleted: true }); + expect(hasAttachments.value).toBe(false); + }); + + it('should handle non-deleted messages with attachments', () => { + const message = reactive({ + content_attributes: {}, + attachments: ['attachment1', 'attachment2'], + }); + + const { messageContentAttributes, hasAttachments } = useMessage(message); + + expect(messageContentAttributes.value).toEqual({}); + expect(hasAttachments.value).toBe(true); + }); + + it('should handle messages without content_attributes', () => { + const message = reactive({ + attachments: [], + }); + + const { messageContentAttributes, hasAttachments } = useMessage(message); + + expect(messageContentAttributes.value).toEqual({}); + expect(hasAttachments.value).toBe(false); + }); + + it('should handle messages with empty attachments array', () => { + const message = reactive({ + content_attributes: { someAttribute: 'value' }, + attachments: [], + }); + + const { messageContentAttributes, hasAttachments } = useMessage(message); + + expect(messageContentAttributes.value).toEqual({ someAttribute: 'value' }); + expect(hasAttachments.value).toBe(false); + }); + + it('should update reactive properties when message changes', async () => { + const message = reactive({ + content_attributes: {}, + attachments: [], + }); + + const { messageContentAttributes, hasAttachments } = useMessage(message); + + expect(messageContentAttributes.value).toEqual({}); + expect(hasAttachments.value).toBe(false); + + message.content_attributes = { updated: true }; + message.attachments.push('newAttachment'); + await nextTick(); + + expect(messageContentAttributes.value).toEqual({ updated: true }); + expect(hasAttachments.value).toBe(true); + }); +}); diff --git a/app/javascript/widget/composables/useMessage.js b/app/javascript/widget/composables/useMessage.js new file mode 100644 index 000000000..97b7a7800 --- /dev/null +++ b/app/javascript/widget/composables/useMessage.js @@ -0,0 +1,22 @@ +import { computed } from 'vue'; + +/** + * Composable for handling message-related computations. + * @param {Object} message - The message object to be processed. + * @returns {Object} An object containing computed properties for message content and attachments. + */ +export function useMessage(message) { + const messageContentAttributes = computed(() => { + const { content_attributes: attribute = {} } = message; + return attribute; + }); + + const hasAttachments = computed(() => { + return !!(message.attachments && message.attachments.length > 0); + }); + + return { + messageContentAttributes, + hasAttachments, + }; +} diff --git a/app/javascript/widget/mixins/messageMixin.js b/app/javascript/widget/mixins/messageMixin.js deleted file mode 100644 index dfa1e0e6a..000000000 --- a/app/javascript/widget/mixins/messageMixin.js +++ /dev/null @@ -1,13 +0,0 @@ -export default { - computed: { - messageContentAttributes() { - const { content_attributes: attribute = {} } = this.message; - return attribute; - }, - hasAttachments() { - return !!( - this.message.attachments && this.message.attachments.length > 0 - ); - }, - }, -}; diff --git a/app/javascript/widget/mixins/specs/messageMixin.spec.js b/app/javascript/widget/mixins/specs/messageMixin.spec.js deleted file mode 100644 index a6443ccd6..000000000 --- a/app/javascript/widget/mixins/specs/messageMixin.spec.js +++ /dev/null @@ -1,37 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import messageMixin from '../messageMixin'; -import messages from './messageFixture'; - -describe('messageMixin', () => { - let Component = { - render() {}, - title: 'TestComponent', - mixins: [messageMixin], - }; - - it('deleted messages', async () => { - const wrapper = shallowMount(Component, { - data() { - return { - message: messages.deletedMessage, - }; - }, - }); - expect(wrapper.vm.messageContentAttributes).toEqual({ - deleted: true, - }); - expect(wrapper.vm.hasAttachments).toBe(false); - }); - it('non-deleted messages', async () => { - const wrapper = shallowMount(Component, { - data() { - return { - message: messages.nonDeletedMessage, - }; - }, - }); - expect(wrapper.vm.deletedMessage).toBe(undefined); - expect(wrapper.vm.messageContentAttributes).toEqual({}); - expect(wrapper.vm.hasAttachments).toBe(true); - }); -});