From c0d7badb7c0f061e7e02e9d2aec19ffcec441206 Mon Sep 17 00:00:00 2001 From: DorraJaouad Date: Wed, 31 Jan 2024 17:32:37 +0100 Subject: [PATCH] Add shortkey to edit message Signed-off-by: DorraJaouad --- .../MessagesGroup/Message/Message.vue | 18 +++------ src/components/NewMessage/NewMessage.vue | 33 +++++++++++++++- .../SettingsDialog/SettingsDialog.vue | 6 +++ src/stores/__tests__/chatExtras.spec.js | 38 +++++++++++++++++++ src/stores/chatExtras.js | 18 +++++++++ 5 files changed, 100 insertions(+), 13 deletions(-) diff --git a/src/components/MessagesList/MessagesGroup/Message/Message.vue b/src/components/MessagesList/MessagesGroup/Message/Message.vue index 93c4a1a1d52..9fa84bf1656 100644 --- a/src/components/MessagesList/MessagesGroup/Message/Message.vue +++ b/src/components/MessagesList/MessagesGroup/Message/Message.vue @@ -485,18 +485,12 @@ export default { }, handleEdit() { - this.chatExtrasStore.setMessageIdToEdit(this.token, this.id) - if (this.isFileShareOnly) { - this.chatExtrasStore.setChatEditInput({ token: this.token, text: '' }) - } else { - this.chatExtrasStore.setChatEditInput({ - token: this.token, - text: this.message, - parameters: this.messageParameters - }) - } - EventBus.$emit('editing-message') - EventBus.$emit('focus-chat-input') + this.chatExtrasStore.initiateEditingMessage({ + token: this.token, + id: this.id, + message: this.message, + messageParameters: this.messageParameters, + }) }, async handleDelete() { diff --git a/src/components/NewMessage/NewMessage.vue b/src/components/NewMessage/NewMessage.vue index c0fcfdc89d4..f321c98a19d 100644 --- a/src/components/NewMessage/NewMessage.vue +++ b/src/components/NewMessage/NewMessage.vue @@ -103,6 +103,7 @@ dir="auto" @shortkey="focusInput" @keydown.esc="handleInputEsc" + @keydown.ctrl.up="handleEditLastMessage" @tribute-active-true.native="isTributePickerActive = true" @tribute-active-false.native="isTributePickerActive = false" @input="handleTyping" @@ -230,6 +231,7 @@ import { parseSpecialSymbols } from '../../utils/textParse.js' const disableKeyboardShortcuts = OCP.Accessibility.disableKeyboardShortcuts() const supportTypingStatus = getCapabilities()?.spreed?.config?.chat?.['typing-privacy'] !== undefined +const canEditMessage = getCapabilities()?.spreed?.features?.includes('edit-messages') export default { name: 'NewMessage', @@ -641,7 +643,6 @@ export default { token: this.token, }) this.text = '' - this.resetTypingIndicator() this.userData = {} // Scrolls the message list to the last added message EventBus.$emit('smooth-scroll-chat-to-bottom') @@ -651,6 +652,7 @@ export default { this.broadcast ? await this.broadcastMessage(this.token, temporaryMessage.message) : await this.postMessage(this.token, temporaryMessage, options) + this.resetTypingIndicator() } }, @@ -696,6 +698,7 @@ export default { }) this.$store.dispatch('processMessage', { token: this.token, message: response.data.ocs.data }) this.chatExtrasStore.removeMessageIdToEdit(this.token) + this.resetTypingIndicator() } catch { this.$emit('failure') showError(t('spreed', 'The message could not be edited')) @@ -923,11 +926,39 @@ export default { }, handleInputEsc() { + if (this.messageToEdit && !this.isTributePickerActive) { + this.handleAbortEdit() + this.focusInput() + return + } // When the tribute picker (e.g. emoji picker or mentions) is open // ESC should only close the picker but not blur if (!this.isTributePickerActive) { this.blurInput() } + + }, + + handleEditLastMessage() { + if (!canEditMessage || this.upload || this.broadcast || this.isRecordingAudio) { + return + } + const lastMessageByCurrentUser = this.$store.getters.messagesList(this.token).findLast(message => { + return message.actorId === this.$store.getters.getUserId() + && message.actorType === this.$store.getters.getActorType() + && !message.isTemporary && !message.systemMessage + }) + + if (!lastMessageByCurrentUser) { + return + } + + this.chatExtrasStore.initiateEditingMessage({ + token: this.token, + id: lastMessageByCurrentUser.id, + message: lastMessageByCurrentUser.message, + messageParameters: lastMessageByCurrentUser.messageParameters, + }) }, async checkAbsenceStatus() { diff --git a/src/components/SettingsDialog/SettingsDialog.vue b/src/components/SettingsDialog/SettingsDialog.vue index 4905de1b209..83557c06423 100644 --- a/src/components/SettingsDialog/SettingsDialog.vue +++ b/src/components/SettingsDialog/SettingsDialog.vue @@ -118,6 +118,12 @@ {{ t('spreed', 'Unfocus the chat input to use shortcuts') }} +
+
Ctrl +
+
+ {{ t('spreed', 'Edit your last message') }} +
+
F
diff --git a/src/stores/__tests__/chatExtras.spec.js b/src/stores/__tests__/chatExtras.spec.js index c3ae0a162be..6596fc710a5 100644 --- a/src/stores/__tests__/chatExtras.spec.js +++ b/src/stores/__tests__/chatExtras.spec.js @@ -1,5 +1,6 @@ import { setActivePinia, createPinia } from 'pinia' +import { EventBus } from '../../services/EventBus.js' import { getUserAbsence } from '../../services/participantsService.js' import { generateOCSErrorResponse, generateOCSResponse } from '../../test-helpers.js' import { useChatExtrasStore } from '../chatExtras.js' @@ -196,4 +197,41 @@ describe('chatExtrasStore', () => { expect(chatExtrasStore.getChatInput('token-1')).toBe('Many whitespaces') }) }) + + describe('initiateEditingMessage', () => { + it('should set the message ID to edit, set the chat edit input, and emit an event', () => { + // Arrange + const payload = { + token: 'token-1', + id: 'id-1', + message: 'Hello, world!', + messageParameters: {} + } + const emitSpy = jest.spyOn(EventBus, '$emit') + + // Act + chatExtrasStore.initiateEditingMessage(payload) + + // Assert + expect(chatExtrasStore.getMessageIdToEdit('token-1')).toBe('id-1') + expect(chatExtrasStore.getChatEditInput('token-1')).toEqual('Hello, world!') + expect(emitSpy).toHaveBeenCalledWith('editing-message') + }) + + it('should set the chat edit input text to empty if the message is a file share only', () => { + // Arrange + const payload = { + token: 'token-1', + id: 'id-1', + message: '{file}', + messageParameters: { file0: 'file-path' } + } + + // Act + chatExtrasStore.initiateEditingMessage(payload) + + // Assert + expect(chatExtrasStore.getChatEditInput('token-1')).toEqual('') + }) + }) }) diff --git a/src/stores/chatExtras.js b/src/stores/chatExtras.js index d060b4ec56b..1d43b09688a 100644 --- a/src/stores/chatExtras.js +++ b/src/stores/chatExtras.js @@ -24,6 +24,7 @@ import { defineStore } from 'pinia' import Vue from 'vue' +import { EventBus } from '../services/EventBus.js' import { getUserAbsence } from '../services/participantsService.js' import { parseSpecialSymbols, parseMentions } from '../utils/textParse.js' @@ -188,6 +189,23 @@ export const useChatExtrasStore = defineStore('chatExtras', { Vue.delete(this.chatInput, token) }, + initiateEditingMessage({ token, id, message, messageParameters }) { + this.setMessageIdToEdit(token, id) + const isFileShareOnly = Object.keys(Object(messageParameters)).some(key => key.startsWith('file')) + && message === '{file}' + if (isFileShareOnly) { + this.setChatEditInput({ token, text: '' }) + } else { + this.setChatEditInput({ + token, + text: message, + parameters: messageParameters + }) + } + EventBus.$emit('editing-message') + EventBus.$emit('focus-chat-input') + }, + /** * Clears store for a deleted conversation *