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
This commit is contained in:
@@ -9,7 +9,7 @@ import FileBubble from 'widget/components/FileBubble.vue';
|
|||||||
import Thumbnail from 'dashboard/components/widgets/Thumbnail.vue';
|
import Thumbnail from 'dashboard/components/widgets/Thumbnail.vue';
|
||||||
import { MESSAGE_TYPE } from 'widget/helpers/constants';
|
import { MESSAGE_TYPE } from 'widget/helpers/constants';
|
||||||
import configMixin from '../mixins/configMixin';
|
import configMixin from '../mixins/configMixin';
|
||||||
import messageMixin from '../mixins/messageMixin';
|
import { useMessage } from '../composables/useMessage';
|
||||||
import { isASubmittedFormMessage } from 'shared/helpers/MessageTypeHelper';
|
import { isASubmittedFormMessage } from 'shared/helpers/MessageTypeHelper';
|
||||||
import darkModeMixin from 'widget/mixins/darkModeMixin.js';
|
import darkModeMixin from 'widget/mixins/darkModeMixin.js';
|
||||||
import ReplyToChip from 'widget/components/ReplyToChip.vue';
|
import ReplyToChip from 'widget/components/ReplyToChip.vue';
|
||||||
@@ -28,7 +28,7 @@ export default {
|
|||||||
MessageReplyButton,
|
MessageReplyButton,
|
||||||
ReplyToChip,
|
ReplyToChip,
|
||||||
},
|
},
|
||||||
mixins: [configMixin, messageMixin, darkModeMixin],
|
mixins: [configMixin, darkModeMixin],
|
||||||
props: {
|
props: {
|
||||||
message: {
|
message: {
|
||||||
type: Object,
|
type: Object,
|
||||||
@@ -39,6 +39,15 @@ export default {
|
|||||||
default: () => {},
|
default: () => {},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
setup(props) {
|
||||||
|
const { messageContentAttributes, hasAttachments } = useMessage(
|
||||||
|
props.message
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
messageContentAttributes,
|
||||||
|
hasAttachments,
|
||||||
|
};
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
hasImageError: false,
|
hasImageError: false,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import VideoBubble from 'widget/components/VideoBubble.vue';
|
|||||||
import FluentIcon from 'shared/components/FluentIcon/Index.vue';
|
import FluentIcon from 'shared/components/FluentIcon/Index.vue';
|
||||||
import FileBubble from 'widget/components/FileBubble.vue';
|
import FileBubble from 'widget/components/FileBubble.vue';
|
||||||
import { messageStamp } from 'shared/helpers/timeHelper';
|
import { messageStamp } from 'shared/helpers/timeHelper';
|
||||||
import messageMixin from '../mixins/messageMixin';
|
import { useMessage } from '../composables/useMessage';
|
||||||
import ReplyToChip from 'widget/components/ReplyToChip.vue';
|
import ReplyToChip from 'widget/components/ReplyToChip.vue';
|
||||||
import DragWrapper from 'widget/components/DragWrapper.vue';
|
import DragWrapper from 'widget/components/DragWrapper.vue';
|
||||||
import { BUS_EVENTS } from 'shared/constants/busEvents';
|
import { BUS_EVENTS } from 'shared/constants/busEvents';
|
||||||
@@ -25,7 +25,6 @@ export default {
|
|||||||
ReplyToChip,
|
ReplyToChip,
|
||||||
DragWrapper,
|
DragWrapper,
|
||||||
},
|
},
|
||||||
mixins: [messageMixin],
|
|
||||||
props: {
|
props: {
|
||||||
message: {
|
message: {
|
||||||
type: Object,
|
type: Object,
|
||||||
@@ -36,6 +35,12 @@ export default {
|
|||||||
default: () => {},
|
default: () => {},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
setup(props) {
|
||||||
|
const { hasAttachments } = useMessage(props.message);
|
||||||
|
return {
|
||||||
|
hasAttachments,
|
||||||
|
};
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
hasImageError: false,
|
hasImageError: false,
|
||||||
|
|||||||
71
app/javascript/widget/composables/specs/useMessage.spec.js
Normal file
71
app/javascript/widget/composables/specs/useMessage.spec.js
Normal file
@@ -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);
|
||||||
|
});
|
||||||
|
});
|
||||||
22
app/javascript/widget/composables/useMessage.js
Normal file
22
app/javascript/widget/composables/useMessage.js
Normal file
@@ -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,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -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
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -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);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
Reference in New Issue
Block a user