summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMaksim Sukharev <antreesy.web@gmail.com>2024-11-17 15:34:46 +0100
committerGitHub <noreply@github.com>2024-11-17 15:34:46 +0100
commit71f9d6c06d3c51429da60271ba610e2307acce1a (patch)
treeb0ca61813b64999b3388e67efbd6d5394b448b30
parentaade00d5b4ec870696486e6174c001607e6c57d0 (diff)
parent3071d4dab62ebc974b296197bc68e84a0ed5100f (diff)
Merge pull request #13784 from nextcloud/fix/noid/temp-message-utilmain
-rw-r--r--src/components/NewMessage/NewMessage.vue11
-rw-r--r--src/components/NewMessage/NewMessageAudioRecorder.vue2
-rw-r--r--src/components/NewMessage/NewMessageUploadEditor.vue4
-rw-r--r--src/composables/useTemporaryMessage.ts37
-rw-r--r--src/store/fileUploadStore.js21
-rw-r--r--src/store/fileUploadStore.spec.js71
-rw-r--r--src/store/messagesStore.js60
-rw-r--r--src/store/messagesStore.spec.js243
-rw-r--r--src/utils/__tests__/prepareTemporaryMessage.spec.js124
-rw-r--r--src/utils/prepareTemporaryMessage.ts95
10 files changed, 343 insertions, 325 deletions
diff --git a/src/components/NewMessage/NewMessage.vue b/src/components/NewMessage/NewMessage.vue
index a8b85b48f9..4268937afc 100644
--- a/src/components/NewMessage/NewMessage.vue
+++ b/src/components/NewMessage/NewMessage.vue
@@ -205,6 +205,7 @@ import PollDraftHandler from '../PollViewer/PollDraftHandler.vue'
import Quote from '../Quote.vue'
import { useChatMentions } from '../../composables/useChatMentions.ts'
+import { useTemporaryMessage } from '../../composables/useTemporaryMessage.ts'
import { CONVERSATION, PARTICIPANT, PRIVACY } from '../../constants.js'
import BrowserStorage from '../../services/BrowserStorage.js'
import { getTalkConfig, hasTalkFeature } from '../../services/CapabilitiesManager.ts'
@@ -293,6 +294,7 @@ export default {
const { token } = toRefs(props)
const supportTypingStatus = getTalkConfig(token.value, 'chat', 'typing-privacy') !== undefined
const { autoComplete, userData } = useChatMentions(token)
+ const { createTemporaryMessage } = useTemporaryMessage()
return {
breakoutRoomsStore: useBreakoutRoomsStore(),
@@ -301,6 +303,7 @@ export default {
supportTypingStatus,
autoComplete,
userData,
+ createTemporaryMessage,
}
},
@@ -676,8 +679,8 @@ export default {
}
if (this.hasText) {
- const temporaryMessage = await this.$store.dispatch('createTemporaryMessage', {
- text: this.text.trim(),
+ const temporaryMessage = this.createTemporaryMessage({
+ message: this.text.trim(),
token: this.token,
})
this.text = ''
@@ -835,7 +838,7 @@ export default {
* @param {boolean} rename whether to rename the files
* @param {boolean} isVoiceMessage indicates whether the file is a voice message
*/
- async handleFiles(files, rename = false, isVoiceMessage = false) {
+ handleFiles(files, rename = false, isVoiceMessage = false) {
if (!this.canUploadFiles) {
showWarning(t('spreed', 'File upload is not available in this conversation'))
return
@@ -843,7 +846,7 @@ export default {
// Create a unique id for the upload operation
const uploadId = this.currentUploadId ?? new Date().getTime()
// Uploads and shares the files
- await this.$store.dispatch('initialiseUpload', { files, token: this.token, uploadId, rename, isVoiceMessage })
+ this.$store.dispatch('initialiseUpload', { files, token: this.token, uploadId, rename, isVoiceMessage })
},
/**
diff --git a/src/components/NewMessage/NewMessageAudioRecorder.vue b/src/components/NewMessage/NewMessageAudioRecorder.vue
index b78f5f4400..c13eb0e7c9 100644
--- a/src/components/NewMessage/NewMessageAudioRecorder.vue
+++ b/src/components/NewMessage/NewMessageAudioRecorder.vue
@@ -246,7 +246,7 @@ export default {
// Generate file name
const fileName = this.generateFileName()
// Convert blob to file
- const audioFile = new File([this.blob], fileName)
+ const audioFile = new File([this.blob], fileName, { type: 'audio/wav' })
this.$emit('audio-file', audioFile)
this.$emit('recording', false)
}
diff --git a/src/components/NewMessage/NewMessageUploadEditor.vue b/src/components/NewMessage/NewMessageUploadEditor.vue
index 2da4e82a4a..295e92ec87 100644
--- a/src/components/NewMessage/NewMessageUploadEditor.vue
+++ b/src/components/NewMessage/NewMessageUploadEditor.vue
@@ -201,9 +201,9 @@ export default {
this.$refs.fileUploadInput.click()
},
- async handleFileInput(event) {
+ handleFileInput(event) {
const files = Object.values(event.target.files)
- await this.$store.dispatch('initialiseUpload', { files, token: this.token, uploadId: this.currentUploadId })
+ this.$store.dispatch('initialiseUpload', { files, token: this.token, uploadId: this.currentUploadId })
this.$refs.fileUploadInput.value = null
},
diff --git a/src/composables/useTemporaryMessage.ts b/src/composables/useTemporaryMessage.ts
new file mode 100644
index 0000000000..26260e6fc7
--- /dev/null
+++ b/src/composables/useTemporaryMessage.ts
@@ -0,0 +1,37 @@
+/*
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+import { useStore } from './useStore.js'
+import { useChatExtrasStore } from '../stores/chatExtras.js'
+import { prepareTemporaryMessage } from '../utils/prepareTemporaryMessage.ts'
+import type { PrepareTemporaryMessagePayload } from '../utils/prepareTemporaryMessage.ts'
+
+/**
+ * Composable to generate temporary messages using defined in store information
+ * @param context Vuex Store (to be used inside Vuex modules)
+ */
+export function useTemporaryMessage(context: unknown) {
+ const store = context ?? useStore()
+ const chatExtrasStore = useChatExtrasStore()
+
+ /**
+ * @param payload payload for generating a temporary message
+ */
+ function createTemporaryMessage(payload: PrepareTemporaryMessagePayload) {
+ const parentId = chatExtrasStore.getParentIdToReply(payload.token)
+
+ return prepareTemporaryMessage({
+ ...payload,
+ actorId: store.getters.getActorId(),
+ actorType: store.getters.getActorType(),
+ actorDisplayName: store.getters.getDisplayName(),
+ parent: parentId && store.getters.message(payload.token, parentId),
+ })
+ }
+
+ return {
+ createTemporaryMessage,
+ }
+}
diff --git a/src/store/fileUploadStore.js b/src/store/fileUploadStore.js
index 2eee36f9ea..754d65cd37 100644
--- a/src/store/fileUploadStore.js
+++ b/src/store/fileUploadStore.js
@@ -11,6 +11,7 @@ import { t } from '@nextcloud/l10n'
import moment from '@nextcloud/moment'
import { getUploader } from '@nextcloud/upload'
+import { useTemporaryMessage } from '../composables/useTemporaryMessage.ts'
import { SHARED_ITEM } from '../constants.js'
import { getDavClient } from '../services/DavClient.js'
import { EventBus } from '../services/EventBus.ts'
@@ -227,9 +228,10 @@ const actions = {
* @param {boolean} data.rename whether to rename the files (usually after pasting)
* @param {boolean} data.isVoiceMessage whether the file is a voice recording
*/
- async initialiseUpload({ commit, dispatch }, { uploadId, token, files, rename = false, isVoiceMessage }) {
+ initialiseUpload(context, { uploadId, token, files, rename = false, isVoiceMessage }) {
// Set last upload id
- commit('setCurrentUploadId', uploadId)
+ context.commit('setCurrentUploadId', uploadId)
+ const { createTemporaryMessage } = useTemporaryMessage(context)
for (let i = 0; i < files.length; i++) {
const file = files[i]
@@ -249,11 +251,17 @@ const actions = {
const date = new Date()
const index = 'temp_' + date.getTime() + Math.random()
// Create temporary message for the file and add it to the message list
- const temporaryMessage = await dispatch('createTemporaryMessage', {
- text: '{file}', token, uploadId, index, file, localUrl, isVoiceMessage,
+ const temporaryMessage = createTemporaryMessage({
+ message: '{file}',
+ token,
+ uploadId,
+ index,
+ file,
+ localUrl,
+ messageType: isVoiceMessage ? 'voice-message' : 'comment',
})
console.debug('temporarymessage: ', temporaryMessage, 'uploadId', uploadId)
- commit('addFileToBeUploaded', { file, temporaryMessage, localUrl, token })
+ context.commit('addFileToBeUploaded', { file, temporaryMessage, localUrl, token })
}
},
@@ -442,7 +450,8 @@ const actions = {
const [index, shareableFile] = share
const { id, messageType, parent, referenceId } = shareableFile.temporaryMessage || {}
- const metadata = JSON.stringify(Object.assign({ messageType },
+ const metadata = JSON.stringify(Object.assign(
+ messageType !== 'comment' ? { messageType } : {},
caption && index === lastIndex ? { caption } : {},
options?.silent ? { silent: options.silent } : {},
parent ? { replyTo: parent.id } : {},
diff --git a/src/store/fileUploadStore.spec.js b/src/store/fileUploadStore.spec.js
index 6dcf8fb192..37e75ab833 100644
--- a/src/store/fileUploadStore.spec.js
+++ b/src/store/fileUploadStore.spec.js
@@ -44,31 +44,11 @@ describe('fileUploadStore', () => {
let mockedActions = null
beforeEach(() => {
- let temporaryMessageCount = 0
-
localVue = createLocalVue()
localVue.use(Vuex)
setActivePinia(createPinia())
mockedActions = {
- createTemporaryMessage: jest.fn()
- .mockImplementation((context, { file, index, uploadId, localUrl, token }) => {
- temporaryMessageCount += 1
- return {
- id: temporaryMessageCount,
- referenceId: 'reference-id-' + temporaryMessageCount,
- token,
- messageParameters: {
- file: {
- uploadId,
- index,
- token,
- localUrl,
- file,
- },
- },
- }
- }),
addTemporaryMessage: jest.fn(),
markTemporaryMessageAsFailed: jest.fn(),
}
@@ -78,6 +58,9 @@ describe('fileUploadStore', () => {
storeConfig = cloneDeep(fileUploadStore)
storeConfig.actions = Object.assign(storeConfig.actions, mockedActions)
storeConfig.getters.getUserId = jest.fn().mockReturnValue(() => 'current-user')
+ storeConfig.getters.getActorId = jest.fn().mockReturnValue(() => 'current-user')
+ storeConfig.getters.getActorType = jest.fn().mockReturnValue(() => 'users')
+ storeConfig.getters.getDisplayName = jest.fn().mockReturnValue(() => 'Current User')
})
afterEach(() => {
@@ -103,7 +86,7 @@ describe('fileUploadStore', () => {
restoreConsole()
})
- test('initialises upload for given files', async () => {
+ test('initialises upload for given files', () => {
const files = [
{
name: 'pngimage.png',
@@ -126,7 +109,7 @@ describe('fileUploadStore', () => {
]
const localUrls = ['local-url:pngimage.png', 'local-url:jpgimage.jpg', undefined]
- await store.dispatch('initialiseUpload', {
+ store.dispatch('initialiseUpload', {
uploadId: 'upload-id1',
token: 'XXTOKENXX',
files,
@@ -135,10 +118,16 @@ describe('fileUploadStore', () => {
const uploads = store.getters.getInitialisedUploads('upload-id1')
expect(uploads).toHaveLength(files.length)
- for (const index in files) {
- expect(mockedActions.createTemporaryMessage.mock.calls[index][1]).toMatchObject({
- text: '{file}',
+ for (const index in uploads) {
+ expect(uploads[index][1].temporaryMessage).toMatchObject({
+ message: '{file}',
token: 'XXTOKENXX',
+ })
+ expect(uploads[index][1].temporaryMessage.messageParameters.file).toMatchObject({
+ type: 'file',
+ mimetype: files[index].type,
+ id: uploads[index][1].temporaryMessage.id,
+ name: files[index].name,
uploadId: 'upload-id1',
index: expect.anything(),
file: files[index],
@@ -155,7 +144,7 @@ describe('fileUploadStore', () => {
lastModified: Date.UTC(2021, 3, 27, 15, 30, 0),
}
- await store.dispatch('initialiseUpload', {
+ store.dispatch('initialiseUpload', {
uploadId: 'upload-id1',
token: 'XXTOKENXX',
files: [file],
@@ -164,6 +153,7 @@ describe('fileUploadStore', () => {
expect(store.getters.currentUploadId).toBe('upload-id1')
const uniqueFileName = '/Talk/' + file.name + 'uniq'
+ const referenceId = store.getters.getUploadsArray('upload-id1')[0][1].temporaryMessage.referenceId
findUniquePath.mockResolvedValueOnce({ uniquePath: uniqueFileName, suffix: 1 })
uploadMock.mockResolvedValue()
shareFile.mockResolvedValue()
@@ -177,7 +167,7 @@ describe('fileUploadStore', () => {
expect(uploadMock).toHaveBeenCalledWith(uniqueFileName, file)
expect(shareFile).toHaveBeenCalledTimes(1)
- expect(shareFile).toHaveBeenCalledWith(uniqueFileName, 'XXTOKENXX', 'reference-id-1', '{"caption":"text-caption","silent":true}')
+ expect(shareFile).toHaveBeenCalledWith(uniqueFileName, 'XXTOKENXX', referenceId, '{"caption":"text-caption","silent":true}')
expect(mockedActions.addTemporaryMessage).toHaveBeenCalledTimes(1)
expect(store.getters.currentUploadId).not.toBeDefined()
@@ -198,7 +188,7 @@ describe('fileUploadStore', () => {
}
const files = [file1, file2]
- await store.dispatch('initialiseUpload', {
+ store.dispatch('initialiseUpload', {
uploadId: 'upload-id1',
token: 'XXTOKENXX',
files,
@@ -221,10 +211,11 @@ describe('fileUploadStore', () => {
expect(findUniquePath).toHaveBeenNthCalledWith(+index + 1, client, '/files/current-user', '/Talk/' + files[index].name, undefined)
expect(uploadMock).toHaveBeenNthCalledWith(+index + 1, `/Talk/${files[index].name}uniq`, files[index])
}
+ const referenceIds = store.getters.getUploadsArray('upload-id1').map(entry => entry[1].temporaryMessage.referenceId)
expect(shareFile).toHaveBeenCalledTimes(2)
- expect(shareFile).toHaveBeenNthCalledWith(1, '/Talk/' + files[0].name + 'uniq', 'XXTOKENXX', 'reference-id-1', '{}')
- expect(shareFile).toHaveBeenNthCalledWith(2, '/Talk/' + files[1].name + 'uniq', 'XXTOKENXX', 'reference-id-2', '{"caption":"text-caption"}')
+ expect(shareFile).toHaveBeenNthCalledWith(1, '/Talk/' + files[0].name + 'uniq', 'XXTOKENXX', referenceIds[0], '{}')
+ expect(shareFile).toHaveBeenNthCalledWith(2, '/Talk/' + files[1].name + 'uniq', 'XXTOKENXX', referenceIds[1], '{"caption":"text-caption"}')
expect(mockedActions.addTemporaryMessage).toHaveBeenCalledTimes(2)
expect(store.getters.currentUploadId).not.toBeDefined()
@@ -240,7 +231,7 @@ describe('fileUploadStore', () => {
},
]
- await store.dispatch('initialiseUpload', {
+ store.dispatch('initialiseUpload', {
uploadId: 'upload-id1',
token: 'XXTOKENXX',
files,
@@ -262,7 +253,7 @@ describe('fileUploadStore', () => {
expect(mockedActions.markTemporaryMessageAsFailed).toHaveBeenCalledTimes(1)
expect(mockedActions.markTemporaryMessageAsFailed).toHaveBeenCalledWith(expect.anything(), {
token: 'XXTOKENXX',
- id: 1,
+ id: store.getters.getUploadsArray('upload-id1')[0][1].temporaryMessage.id,
uploadId: 'upload-id1',
reason: 'failed-upload'
})
@@ -280,7 +271,7 @@ describe('fileUploadStore', () => {
},
]
- await store.dispatch('initialiseUpload', {
+ store.dispatch('initialiseUpload', {
uploadId: 'upload-id1',
token: 'XXTOKENXX',
files,
@@ -303,7 +294,7 @@ describe('fileUploadStore', () => {
expect(mockedActions.markTemporaryMessageAsFailed).toHaveBeenCalledTimes(1)
expect(mockedActions.markTemporaryMessageAsFailed).toHaveBeenCalledWith(expect.anything(), {
token: 'XXTOKENXX',
- id: 1,
+ id: store.getters.getUploadsArray('upload-id1')[0][1].temporaryMessage.id,
uploadId: 'upload-id1',
reason: 'failed-share'
})
@@ -327,14 +318,14 @@ describe('fileUploadStore', () => {
},
]
- await store.dispatch('initialiseUpload', {
+ store.dispatch('initialiseUpload', {
uploadId: 'upload-id1',
token: 'XXTOKENXX',
files,
})
- // temporary message mock uses incremental id
- await store.dispatch('removeFileFromSelection', 2)</