diff options
author | Maksim Sukharev <antreesy.web@gmail.com> | 2024-01-15 11:07:50 +0100 |
---|---|---|
committer | Maksim Sukharev <antreesy.web@gmail.com> | 2024-01-18 11:21:48 +0100 |
commit | 20b3e34950b00d2981362df459820918b8d1bf52 (patch) | |
tree | b37434c0a2c9fe44360fd640395eebbcb9503bc2 | |
parent | 086728d6c0156ec41ac26506304ec0f3d59f8b10 (diff) |
enh(message): pass destructured message object as $props to children
Signed-off-by: Maksim Sukharev <antreesy.web@gmail.com>
8 files changed, 111 insertions, 173 deletions
diff --git a/src/components/MessagesList/MessagesGroup/Message/Message.vue b/src/components/MessagesList/MessagesGroup/Message/Message.vue index b6ab4f9fb..dd6075371 100644 --- a/src/components/MessagesList/MessagesGroup/Message/Message.vue +++ b/src/components/MessagesList/MessagesGroup/Message/Message.vue @@ -36,24 +36,10 @@ 'system' : isSystemMessage, 'combined-system': isCombinedSystemMessage}" class="message-body"> - <MessageBody :id="id" - :token="token" - :parent="parent" - :markdown="markdown" - :message="message" - :message-type="messageType" - :system-message="systemMessage" - :message-parameters="messageParameters" + <MessageBody v-bind="{...$props, ...readInfoProps}" :rich-parameters="richParameters" - :timestamp="timestamp" :is-deleting="isDeleting" - :is-temporary="isTemporary" - :has-call="conversation.hasCall" - :sending-failure="sendingFailure" - :show-common-read-icon="showCommonReadIcon" - :common-read-icon-tooltip="commonReadIconTooltip" - :show-sent-icon="showSentIcon" - :sent-icon-tooltip="sentIconTooltip" /> + :has-call="conversation.hasCall" /> <!-- reactions buttons and popover with details --> <Reactions v-if="Object.keys(reactions).length" @@ -68,20 +54,13 @@ <MessageButtonsBar v-if="showMessageButtonsBar" ref="messageButtonsBar" class="message-buttons-bar" + v-bind="{...$props, ...readInfoProps}" :is-translation-available="isTranslationAvailable" :is-action-menu-open.sync="isActionMenuOpen" :is-emoji-picker-open.sync="isEmojiPickerOpen" :is-reactions-menu-open.sync="isReactionsMenuOpen" :is-forwarder-open.sync="isForwarderOpen" - :message-api-data="messageApiData" - :message-object="messageObject" :can-react="canReact" - v-bind="$props" - :previous-message-id="previousMessageId" - :show-common-read-icon="showCommonReadIcon" - :common-read-icon-tooltip="commonReadIconTooltip" - :show-sent-icon="showSentIcon" - :sent-icon-tooltip="sentIconTooltip" @show-translate-dialog="isTranslateDialogOpen = true" @reply="handleReply" @edit="handleEdit" @@ -100,6 +79,11 @@ </div> </div> + <MessageForwarder v-if="isForwarderOpen" + :id="id" + :token="token" + @close="isForwarderOpen = false" /> + <MessageTranslateDialog v-if="isTranslationAvailable && isTranslateDialogOpen" :message="message" :rich-parameters="richParameters" @@ -123,6 +107,7 @@ import { showError, showSuccess, showWarning, TOAST_DEFAULT_TIMEOUT } from '@nex import NcButton from '@nextcloud/vue/dist/Components/NcButton.js' import MessageButtonsBar from './MessageButtonsBar/MessageButtonsBar.vue' +import MessageForwarder from './MessageButtonsBar/MessageForwarder.vue' import MessageTranslateDialog from './MessageButtonsBar/MessageTranslateDialog.vue' import Contact from './MessagePart/Contact.vue' import DeckCard from './MessagePart/DeckCard.vue' @@ -148,8 +133,9 @@ export default { components: { MessageBody, - MessageTranslateDialog, MessageButtonsBar, + MessageForwarder, + MessageTranslateDialog, NcButton, Reactions, // Icons @@ -301,16 +287,6 @@ export default { default: '', }, - lastEditActorId: { - type: String, - default: '', - }, - - lastEditActorType: { - type: String, - default: '', - }, - lastEditTimestamp: { type: Number, default: 0, @@ -356,10 +332,6 @@ export default { return !this.isLastMessage && this.id === this.$store.getters.getVisualLastReadMessageId(this.token) }, - messageObject() { - return this.$store.getters.message(this.token, this.id) - }, - isSystemMessage() { return this.systemMessage !== '' }, @@ -391,7 +363,10 @@ export default { Object.keys(this.messageParameters).forEach(function(p) { const type = this.messageParameters[p].type const mimetype = this.messageParameters[p].mimetype - const itemType = getItemTypeFromMessage(this.messageObject) + const itemType = getItemTypeFromMessage({ + messageParameters: this.messageParameters, + messageType: this.messageType + }) if (type === 'user' || type === 'call' || type === 'guest' || type === 'user-group' || type === 'group') { richParameters[p] = { component: Mention, @@ -450,27 +425,20 @@ export default { && this.isCombinedSystemMessage && (this.isHovered || !this.isCombinedSystemMessageCollapsed) }, - sentIconTooltip() { - return t('spreed', 'Message sent') - }, - - commonReadIconTooltip() { - return t('spreed', 'Message read by everyone who shares their reading status') - }, - - messageApiData() { + readInfoProps() { return { - message: this.messageObject, - metadata: this.conversation, - apiVersion: 'v3', + showCommonReadIcon: this.showCommonReadIcon, + commonReadIconTooltip: t('spreed', 'Message read by everyone who shares their reading status'), + showSentIcon: this.showSentIcon, + sentIconTooltip: t('spreed', 'Message sent'), } }, canReact() { return this.conversation.readOnly !== CONVERSATION.STATE.READ_ONLY && (this.conversation.permissions & PARTICIPANT.PERMISSIONS.CHAT) !== 0 - && this.messageObject.messageType !== 'command' - && this.messageObject.messageType !== 'comment_deleted' + && this.messageType !== 'command' + && this.messageType !== 'comment_deleted' }, isFileShareOnly() { diff --git a/src/components/MessagesList/MessagesGroup/Message/MessageButtonsBar/MessageButtonsBar.spec.js b/src/components/MessagesList/MessagesGroup/Message/MessageButtonsBar/MessageButtonsBar.spec.js index 8c2e23259..97e888bca 100644 --- a/src/components/MessagesList/MessagesGroup/Message/MessageButtonsBar/MessageButtonsBar.spec.js +++ b/src/components/MessagesList/MessagesGroup/Message/MessageButtonsBar/MessageButtonsBar.spec.js @@ -60,6 +60,8 @@ describe('MessageButtonsBar.vue', () => { isTranslationAvailable: false, canReact: true, isReactionsMenuOpen: false, + isActionMenuOpen: false, + isEmojiPickerOpen: false, isLastRead: false, isForwarderOpen: false, timestamp: new Date('2020-05-07 09:23:00').getTime() / 1000, @@ -67,10 +69,6 @@ describe('MessageButtonsBar.vue', () => { systemMessage: '', messageType: 'comment', previousMessageId: 100, - messageObject: {}, - messageApiData: { - apiDummyData: 1, - }, participant: { actorId: 'user-id-1', actorType: ATTENDEE.ACTOR_TYPE.USERS, @@ -333,7 +331,7 @@ describe('MessageButtonsBar.vue', () => { test('marks message as unread', async () => { const updateLastReadMessageAction = jest.fn().mockResolvedValueOnce() const fetchConversationAction = jest.fn().mockResolvedValueOnce() - testStoreConfig.modules.conversationsStore.actions.updateLastReadMessage = updateLastReadMessageAction + testStoreConfig.modules.messagesStore.actions.updateLastReadMessage = updateLastReadMessageAction testStoreConfig.modules.conversationsStore.actions.fetchConversation = fetchConversationAction store = new Store(testStoreConfig) @@ -437,19 +435,23 @@ describe('MessageButtonsBar.vue', () => { }) const actionButton = findNcActionButton(wrapper, 'first action') - expect(actionButton.exists()).toBe(true) + expect(actionButton.exists()).toBeTruthy() await actionButton.find('button').trigger('click') expect(handler).toHaveBeenCalledWith({ - apiDummyData: 1, + apiVersion: 'v3', + message: messageProps, + metadata: conversationProps, },) const actionButton2 = findNcActionButton(wrapper, 'second action') - expect(actionButton2.exists()).toBe(true) + expect(actionButton2.exists()).toBeTruthy() await actionButton2.find('button').trigger('click') expect(handler2).toHaveBeenCalledWith({ - apiDummyData: 1, + apiVersion: 'v3', + message: messageProps, + metadata: conversationProps, }) }) }) diff --git a/src/components/MessagesList/MessagesGroup/Message/MessageButtonsBar/MessageButtonsBar.vue b/src/components/MessagesList/MessagesGroup/Message/MessageButtonsBar/MessageButtonsBar.vue index 699756087..5a0a75147 100644 --- a/src/components/MessagesList/MessagesGroup/Message/MessageButtonsBar/MessageButtonsBar.vue +++ b/src/components/MessagesList/MessagesGroup/Message/MessageButtonsBar/MessageButtonsBar.vue @@ -3,7 +3,7 @@ - - @author Marco Ambrosini <marcoambrosini@icloud.com> - - - @license GNU AGPL version 3 or any later version + - @license AGPL-3.0-or-later - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as @@ -66,12 +66,12 @@ {{ messageDateTime }} </NcActionText> <!-- Edited message timestamp --> - <NcActionButtonGroup v-if="messageObject.lastEditTimestamp"> + <NcActionButtonGroup v-if="lastEditTimestamp"> <NcActionText> <template #icon> <ClockEditOutline :size="16" /> </template> - {{ messageObject.lastEditActorDisplayName }} + {{ lastEditActorDisplayName }} </NcActionText> <NcActionText> {{ editedDateTime }} @@ -263,9 +263,6 @@ </NcButton> </NcEmojiPicker> </template> - <MessageForwarder v-if="isForwarderOpen" - :message-object="messageObject" - @close="closeForwarder" /> </div> </template> @@ -309,8 +306,6 @@ import NcActionText from '@nextcloud/vue/dist/Components/NcActionText.js' import NcButton from '@nextcloud/vue/dist/Components/NcButton.js' import NcEmojiPicker from '@nextcloud/vue/dist/Components/NcEmojiPicker.js' -import MessageForwarder from './MessageForwarder.vue' - import { PARTICIPANT, CONVERSATION, ATTENDEE } from '../../../../../constants.js' import { getMessageReminder, removeMessageReminder, setMessageReminder } from '../../../../../services/remindersService.js' import { copyConversationLinkToClipboard } from '../../../../../services/urlService.js' @@ -324,7 +319,6 @@ export default { name: 'MessageButtonsBar', components: { - MessageForwarder, NcActionButtonGroup, NcActionButton, NcActionInput, @@ -360,6 +354,8 @@ export default { inject: ['getMessagesListScroller'], + inheritAttrs: false, + props: { token: { type: String, @@ -376,11 +372,6 @@ export default { required: true, }, - messageObject: { - type: Object, - required: true, - }, - actorId: { type: String, required: true, @@ -431,16 +422,27 @@ export default { required: true, }, - messageApiData: { - type: Object, - required: true, + lastEditActorDisplayName: { + type: String, + default: '', + }, + lastEditTimestamp: { + type: Number, + default: 0, }, + isActionMenuOpen: { + type: Boolean, + required: true, + }, + isEmojiPickerOpen: { + type: Boolean, + required: true, + }, isReactionsMenuOpen: { type: Boolean, required: true, }, - isForwarderOpen: { type: Boolean, required: true, @@ -607,7 +609,7 @@ export default { }, editedDateTime() { - return moment(this.messageObject.lastEditTimestamp * 1000).format('lll') + return moment(this.lastEditTimestamp * 1000).format('lll') }, reminderOptions() { @@ -660,8 +662,19 @@ export default { }, clearReminderLabel() { + if (!this.currentReminder) { + return '' + } return t('spreed', 'Clear reminder – {timeLocale}', { timeLocale: moment(this.currentReminder.timestamp * 1000).format('ddd LT') }) }, + + messageApiData() { + return { + message: this.$store.getters.message(this.token, this.id), + metadata: this.$store.getters.conversation(this.token), + apiVersion: 'v3', + } + }, }, watch: { @@ -684,9 +697,9 @@ export default { }, async handleCopyMessageText() { - let parsedText = this.messageObject.message + let parsedText = this.message - for (const [key, value] of Object.entries(this.messageObject.messageParameters)) { + for (const [key, value] of Object.entries(this.messageParameters)) { if (value?.type === 'call') { parsedText = parsedText.replace(new RegExp(`{${key}}`, 'g'), '@all') } else if (value?.type === 'user') { @@ -720,10 +733,10 @@ export default { handleReactionClick(selectedEmoji) { // Add reaction only if user hasn't reacted yet - if (!this.messageObject.reactionsSelf?.includes(selectedEmoji)) { + if (!this.reactionsSelf?.includes(selectedEmoji)) { this.reactionsStore.addReactionToMessage({ token: this.token, - messageId: this.messageObject.id, + messageId: this.id, selectedEmoji, }) } else { @@ -765,7 +778,9 @@ export default { async forwardToNote() { try { - await this.$store.dispatch('forwardMessage', { messageToBeForwarded: this.messageObject }) + await this.$store.dispatch('forwardMessage', { + messageToBeForwarded: this.$store.getters.message(this.token, this.id) + }) showSuccess(t('spreed', 'Message forwarded to "Note to self"')) } catch (error) { console.error('Error while forwarding message to "Note to self"', error) @@ -777,10 +792,6 @@ export default { this.$emit('update:isForwarderOpen', true) }, - closeForwarder() { - this.$emit('update:isForwarderOpen', false) - }, - // Making sure that the click is outside the MessageButtonsBar handleClickOutside(event) { if (event.composedPath().includes(this.$el)) { diff --git a/src/components/MessagesList/MessagesGroup/Message/MessageButtonsBar/MessageForwarder.vue b/src/components/MessagesList/MessagesGroup/Message/MessageButtonsBar/MessageForwarder.vue index d7e625c58..09ebdbd28 100644 --- a/src/components/MessagesList/MessagesGroup/Message/MessageButtonsBar/MessageForwarder.vue +++ b/src/components/MessagesList/MessagesGroup/Message/MessageButtonsBar/MessageForwarder.vue @@ -3,7 +3,7 @@ - - @author Marco Ambrosini <marcoambrosini@icloud.com> - - - @license GNU AGPL version 3 or any later version + - @license AGPL-3.0-or-later - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as @@ -78,11 +78,12 @@ export default { }, props: { - /** - * The message to be forwarded - */ - messageObject: { - type: Object, + token: { + type: String, + required: true, + }, + id: { + type: [String, Number], required: true, }, }, @@ -122,7 +123,7 @@ export default { try { const response = await this.$store.dispatch('forwardMessage', { targetToken: this.selectedConversationToken, - messageToBeForwarded: this.messageObject, + messageToBeForwarded: this.$store.getters.message(this.token, this.id), }) this.forwardedMessageID = response.data.ocs.data.id this.showForwardedConfirmation = true diff --git a/src/components/MessagesList/MessagesGroup/Message/MessagePart/MessageBody.vue b/src/components/MessagesList/MessagesGroup/Message/MessagePart/MessageBody.vue index 57dcd5c62..fbdc9e91a 100644 --- a/src/components/MessagesList/MessagesGroup/Message/MessagePart/MessageBody.vue +++ b/src/components/MessagesList/MessagesGroup/Message/MessagePart/MessageBody.vue @@ -166,6 +166,8 @@ export default { ReloadIcon, }, + inheritAttrs: false, + props: { id: { type: [String, Number], diff --git a/src/components/MessagesList/MessagesGroup/MessagesGroup.vue b/src/components/MessagesList/MessagesGroup/MessagesGroup.vue index e1f683560..1a37bcd85 100644 --- a/src/components/MessagesList/MessagesGroup/MessagesGroup.vue +++ b/src/components/MessagesList/MessagesGroup/MessagesGroup.vue @@ -32,7 +32,7 @@ <ul class="messages"> <li class="messages__author" aria-level="4"> {{ actorDisplayName }} - <div v-if="lastEditActorDisplayName"> + <div v-if="lastEditTimestamp"> {{ getLastEditor }} </div> </li> @@ -64,6 +64,7 @@ export default { AvatarWrapper, Message, }, + inheritAttrs: false, props: { @@ -91,55 +92,30 @@ export default { type: [String, Number], default: 0, }, - - lastEditTimestamp: { - type: Number, - default: 0, - }, - lastEditActorId: { - type: String, - default: '', - }, - lastEditActorType: { - type: String, - default: '', - }, - - lastEditActorDisplayName: { - type: String, - default: '' - }, }, setup() { - const guestNameStore = useGuestNameStore() - return { AVATAR, guestNameStore } + return { + AVATAR, + guestNameStore: useGuestNameStore() + } }, expose: ['highlightMessage'], computed: { - /** - * The message actor type. - * - * @return {string} - */ + actorId() { + return this.messages[0].actorId + }, + actorType() { return this.messages[0].actorType }, - /** - * The message actor id. - * - * @return {string} - */ - actorId() { - return this.messages[0].actorId + + lastEditTimestamp() { + return this.messages[0].lastEditTimestamp }, - /** - * The message actor display name. - * - * @return {string} - */ + actorDisplayName() { const displayName = this.messages[0].actorDisplayName.trim() @@ -155,16 +131,18 @@ export default { }, getLastEditor() { - if (this.lastEditActorId === this.actorId && this.lastEditActorType === this.actorType) { + if (!this.lastEditTimestamp) { + return '' + } else if (this.messages[0].lastEditActorId === this.actorId + && this.messages[0].lastEditActorType === this.actorType) { // TRANSLATORS Edited by the author of the message themselves return t('spreed', '(edited)') - } else if (this.lastEditActorId === this.$store.getters.getActorId() - && this.lastEditActorType === this.$store.getters.getActorType()) { + } else if (this.messages[0].lastEditActorId === this.$store.getters.getActorId() + && this.messages[0].lastEditActorType === this.$store.getters.getActorType()) { return t('spreed', '(edited by you)') } else { - return t('spreed', '(edited by {moderator})', { moderator: this.lastEditActorDisplayName }) + return t('spreed', '(edited by {moderator})', { moderator: this.messages[0].lastEditActorDisplayName }) } - }, disableMenu() { diff --git a/src/components/MessagesList/MessagesList.spec.js b/src/components/MessagesList/MessagesList.spec.js index fe0f64024..ac5211064 100644 --- a/src/components/MessagesList/MessagesList.spec.js +++ b/src/components/MessagesList/MessagesList.spec.js @@ -129,23 +129,16 @@ describe('MessagesList.vue', () => { expect(group.props('messages')).toStrictEqual(messagesGroup1) expect(group.props('previousMessageId')).toBe(0) expect(group.props('nextMessageId')).toBe(200) - // using attributes to access v-bind props - expect(group.attributes('actorid')).toBe('alice') - expect(group.attributes('actortype')).toBe(ATTENDEE.ACTOR_TYPE.USERS) group = groups.at(1) expect(group.props('messages')).toStrictEqual(messagesGroup2) expect(group.props('previousMessageId')).toBe(110) expect(group.props('nextMessageId')).toBe(300) - expect(group.attributes('actorid')).toBe('bob') - expect(group.attributes('actortype')).toBe(ATTENDEE.ACTOR_TYPE.USERS) group = groups.at(2) expect(group.props('messages')).toStrictEqual(messagesGroup3) expect(group.props('previousMessageId')).toBe(210) expect(group.props('nextMessageId')).toBe(0) - expect(group.attributes('actorid')).toBe('alice') - expect(group.attributes('actortype')).toBe(ATTENDEE.ACTOR_TYPE.USERS) expect(messagesListMock).toHaveBeenCalledWith(TOKEN) @@ -208,23 +201,11 @@ describe('MessagesList.vue', () => { const groups = wrapper.findAllComponents({ name: 'MessagesGroup' }) - expect(groups.exists()).toBe(true) - - let group = groups.at(0) - expect(group.props('messages')).toStrictEqual([messages[0]]) - // using attributes to access v-bind props - expect(group.attributes('actorid')).toBe('alice') - expect(group.attributes('actortype')).toBe(ATTENDEE.ACTOR_TYPE.USERS) + expect(groups.exists()).toBeTruthy() - group = groups.at(1) - expect(group.props('messages')).toStrictEqual([messages[1]]) - expect(group.attributes('actorid')).toBe('alice') - expect(group.attributes('actortype')).toBe(ATTENDEE.ACTOR_TYPE.USERS) - - group = groups.at(2) - expect(group.props('messages')).toStrictEqual([messages[2]]) - expect(group.attributes('actorid')).toBe('alice') - expect(group.attributes('actortype')).toBe(ATTENDEE.ACTOR_TYPE.USERS) + groups.wrappers.forEach((group, index) => { + expect(group.props('messages')).toStrictEqual([messages[index]]) + }) const dateSeparators = wrapper.findAll('.messages-group__date') expect(dateSeparators).toHaveLength(3) @@ -279,9 +260,6 @@ describe('MessagesList.vue', () => { const group = groups.at(0) expect(group.props('messages')).toStrictEqual(messages) - // using attributes to access v-bind props - expect(group.attributes('actorid')).toBe('alice') - expect(group.attributes('actortype')).toBe(ATTENDEE.ACTOR_TYPE.USERS) expect(messagesListMock).toHaveBeenCalledWith(TOKEN) }) @@ -303,12 +281,11 @@ describe('MessagesList.vue', () => { const groups = wrapper.findAllComponents({ ref: 'messagesGroup' }) - expect(groups.exists()).toBe(true) + expect(groups.exists()).toBeTruthy() - let group = groups.at(0) - expect(group.props('messages')).toStrictEqual([messages[0]]) - group = groups.at(1) - expect(group.props('messages')).toStrictE |