summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMaksim Sukharev <antreesy.web@gmail.com>2023-10-23 15:11:15 +0200
committerGitHub <noreply@github.com>2023-10-23 15:11:15 +0200
commit701763ecb737834149af784f6303dc6ffbdca04a (patch)
tree7dceb863845ed189e5f92ea7e6b4a3e0a5517806
parentab03ae1f3f05f6c68e99a1db863fe44d33fa9635 (diff)
parent892c22b7e29ebf0248f143adcc07eb3ea71bf8ce (diff)
Merge pull request #10730 from nextcloud/feat/5354/captions-frontend
feat(NewMessageUploadEditor) - caption to file share
-rw-r--r--src/components/MessagesList/MessagesGroup/Message/Message.spec.js33
-rw-r--r--src/components/MessagesList/MessagesGroup/Message/Message.vue27
-rw-r--r--src/components/MessagesList/MessagesGroup/Message/MessagePart/FilePreview.vue16
-rw-r--r--src/components/NewMessage/NewMessage.vue31
-rw-r--r--src/components/NewMessage/NewMessageUploadEditor.vue67
-rw-r--r--src/store/fileUploadStore.js127
-rw-r--r--src/store/fileUploadStore.spec.js94
7 files changed, 254 insertions, 141 deletions
diff --git a/src/components/MessagesList/MessagesGroup/Message/Message.spec.js b/src/components/MessagesList/MessagesGroup/Message/Message.spec.js
index 232c7aeda..46bf01aa5 100644
--- a/src/components/MessagesList/MessagesGroup/Message/Message.spec.js
+++ b/src/components/MessagesList/MessagesGroup/Message/Message.spec.js
@@ -325,6 +325,7 @@ describe('Message.vue', () => {
const messageEl = wrapper.findComponent({ name: 'NcRichText' })
// note: indices as object keys are on purpose
expect(messageEl.props('arguments')).toMatchObject(expectedRichParameters)
+ return messageEl
}
test('renders mentions', () => {
@@ -364,7 +365,7 @@ describe('Message.vue', () => {
)
})
- test('renders file previews', () => {
+ test('renders single file preview', () => {
const params = {
actor: {
id: 'alice',
@@ -391,6 +392,36 @@ describe('Message.vue', () => {
)
})
+ test('renders single file preview with caption', () => {
+ const caption = 'text caption'
+ const params = {
+ actor: {
+ id: 'alice',
+ name: 'Alice',
+ type: 'user',
+ },
+ file: {
+ path: 'some/path',
+ type: 'file',
+ },
+ }
+ const messageEl = renderRichObject(
+ caption,
+ params, {
+ actor: {
+ component: Mention,
+ props: params.actor,
+ },
+ file: {
+ component: FilePreview,
+ props: params.file,
+ },
+ }
+ )
+
+ expect(messageEl.props('text')).toBe('{file}' + '\n\n' + caption)
+ })
+
test('renders deck cards', () => {
const params = {
actor: {
diff --git a/src/components/MessagesList/MessagesGroup/Message/Message.vue b/src/components/MessagesList/MessagesGroup/Message/Message.vue
index 1032bcab5..c282e823a 100644
--- a/src/components/MessagesList/MessagesGroup/Message/Message.vue
+++ b/src/components/MessagesList/MessagesGroup/Message/Message.vue
@@ -47,11 +47,11 @@ the main body of the message as well as a quote.
class="message-body__main__text">
<Quote v-if="parent" v-bind="parent" />
<div class="single-emoji">
- {{ message }}
+ {{ renderedMessage }}
</div>
</div>
<div v-else-if="showJoinCallButton" class="message-body__main__text call-started">
- <NcRichText :text="message"
+ <NcRichText :text="renderedMessage"
:arguments="richParameters"
autolink
dir="auto"
@@ -59,7 +59,7 @@ the main body of the message as well as a quote.
<CallButton />
</div>
<div v-else-if="showResultsButton || isSystemMessage" class="message-body__main__text system-message">
- <NcRichText :text="message"
+ <NcRichText :text="renderedMessage"
:arguments="richParameters"
autolink
dir="auto"
@@ -72,7 +72,7 @@ the main body of the message as well as a quote.
show-as-button />
</div>
<div v-else-if="isDeletedMessage" class="message-body__main__text deleted-message">
- <NcRichText :text="message"
+ <NcRichText :text="renderedMessage"
:arguments="richParameters"
autolink
dir="auto"
@@ -83,7 +83,7 @@ the main body of the message as well as a quote.
@mouseover="handleMarkdownMouseOver"
@mouseleave="handleMarkdownMouseLeave">
<Quote v-if="parent" v-bind="parent" />
- <NcRichText :text="message"
+ <NcRichText :text="renderedMessage"
:arguments="richParameters"
autolink
dir="auto"
@@ -457,6 +457,11 @@ export default {
type: Array,
default: () => { return [] },
},
+
+ referenceId: {
+ type: String,
+ default: '',
+ },
},
emits: ['toggle-combined-system-message'],
@@ -502,6 +507,15 @@ export default {
return !this.isLastMessage && this.id === this.$store.getters.getVisualLastReadMessageId(this.token)
},
+ renderedMessage() {
+ if (this.messageParameters?.file && this.message !== '{file}') {
+ // Add a new line after file to split content into different paragraphs
+ return '{file}' + '\n\n' + this.message
+ } else {
+ return this.message
+ }
+ },
+
messageObject() {
return this.$store.getters.message(this.token, this.id)
},
@@ -568,7 +582,7 @@ export default {
let match
let emojiStrings = ''
let emojiCount = 0
- const trimmedMessage = this.message.trim()
+ const trimmedMessage = this.renderedMessage.trim()
// eslint-disable-next-line no-cond-assign
while (match = regex.exec(trimmedMessage)) {
@@ -600,6 +614,7 @@ export default {
props: Object.assign({
token: this.token,
itemType,
+ referenceId: this.referenceId,
}, this.messageParameters[p]),
}
} else if (type === 'deck-card') {
diff --git a/src/components/MessagesList/MessagesGroup/Message/MessagePart/FilePreview.vue b/src/components/MessagesList/MessagesGroup/Message/MessagePart/FilePreview.vue
index b2e76a1fa..ced1222d7 100644
--- a/src/components/MessagesList/MessagesGroup/Message/MessagePart/FilePreview.vue
+++ b/src/components/MessagesList/MessagesGroup/Message/MessagePart/FilePreview.vue
@@ -32,7 +32,7 @@
'file-preview--row-layout': rowLayout }"
@click.exact="handleClick"
@keydown.enter="handleClick">
- <div v-if="!isLoading"
+ <div v-if="!isLoading || fallbackLocalUrl"
class="image-container"
:class="{'playable': isPlayable}">
<span v-if="isPlayable && !smallPreview" class="play-video-button">
@@ -122,6 +122,13 @@ export default {
required: true,
},
/**
+ * Reference id from the message
+ */
+ referenceId: {
+ type: String,
+ default: '',
+ },
+ /**
* File name
*/
name: {
@@ -283,6 +290,10 @@ export default {
return this.name
},
+ fallbackLocalUrl() {
+ return this.$store.getters.getLocalUrl(this.referenceId)
+ },
+
previewTooltip() {
if (this.shouldShowFileDetail) {
// no tooltip as the file name is already visible directly
@@ -363,6 +374,9 @@ export default {
if (this.previewType === PREVIEW_TYPE.TEMPORARY) {
return this.localUrl
}
+ if (this.fallbackLocalUrl) {
+ return this.fallbackLocalUrl
+ }
if (this.previewType === PREVIEW_TYPE.MIME_ICON || this.rowLayout) {
return OC.MimeType.getIconUrl(this.mimetype)
}
diff --git a/src/components/NewMessage/NewMessage.vue b/src/components/NewMessage/NewMessage.vue
index dd9ef6753..ab4fd1639 100644
--- a/src/components/NewMessage/NewMessage.vue
+++ b/src/components/NewMessage/NewMessage.vue
@@ -109,7 +109,7 @@
<!-- Send buttons -->
<template v-else>
- <NcActions v-if="!broadcast"
+ <NcActions v-if="!broadcast && !upload"
:container="container"
:force-menu="true">
<!-- Silent send -->
@@ -241,6 +241,14 @@ export default {
},
/**
+ * Upload files caption.
+ */
+ upload: {
+ type: Boolean,
+ default: false,
+ },
+
+ /**
* Show an indicator if someone is currently typing a message.
*/
hasTypingIndicator: {
@@ -359,11 +367,11 @@ export default {
},
showAttachmentsMenu() {
- return this.canShareFiles && !this.broadcast
+ return this.canShareFiles && !this.broadcast && !this.upload
},
showAudioRecorder() {
- return !this.hasText && this.canUploadFiles && !this.broadcast
+ return !this.hasText && this.canUploadFiles && !this.broadcast && !this.upload
},
showTypingStatus() {
return this.hasTypingIndicator && this.supportTypingStatus
@@ -446,8 +454,15 @@ export default {
},
handleUploadStart() {
- // refocus on upload start as the user might want to type again while the upload is running
- this.focusInput()
+ if (this.upload) {
+ return
+ }
+ this.$nextTick(() => {
+ // reset main input in chat view after upload file with caption
+ this.text = this.$store.getters.currentMessageInput(this.token)
+ // refocus on upload start as the user might want to type again while the upload is running
+ this.focusInput()
+ })
},
/**
@@ -466,6 +481,12 @@ export default {
}
}
+ if (this.upload) {
+ this.$emit('sent', this.text)
+ this.$store.dispatch('setCurrentMessageInput', { token: this.token, text: '' })
+ return
+ }
+
if (this.hasText) {
// FIXME upstream: https://github.com/nextcloud-libraries/nextcloud-vue/issues/4492
const temp = document.createElement('textarea')
diff --git a/src/components/NewMessage/NewMessageUploadEditor.vue b/src/components/NewMessage/NewMessageUploadEditor.vue
index 2837cb391..b0d687bba 100644
--- a/src/components/NewMessage/NewMessageUploadEditor.vue
+++ b/src/components/NewMessage/NewMessageUploadEditor.vue
@@ -21,6 +21,7 @@
<template>
<NcModal v-if="showModal"
+ ref="modal"
:size="isVoiceMessage ? 'small' : 'normal'"
class="upload-editor"
:container="container"
@@ -39,9 +40,9 @@
tag="div"
group>
<template v-for="file in files">
- <FilePreview :key="file.temporaryMessage.id"
+ <FilePreview :key="file[1].temporaryMessage.id"
:token="token"
- v-bind="file.temporaryMessage.messageParameters.file"
+ v-bind="file[1].temporaryMessage.messageParameters.file"
:is-upload-editor="true"
@remove-file="handleRemoveFileFromSelection" />
</template>
@@ -62,14 +63,15 @@
<AudioPlayer :name="voiceMessageName"
:local-url="voiceMessageLocalURL" />
</template>
- <div class="upload-editor__actions">
- <NcButton type="tertiary" @click="handleDismiss">
- {{ t('spreed', 'Dismiss') }}
- </NcButton>
- <NcButton ref="submitButton" type="primary" @click="handleUpload">
- {{ t('spreed', 'Send') }}
- </NcButton>
- </div>
+ <NewMessage v-if="modalContainerId"
+ ref="newMessage"
+ role="region"
+ upload
+ :token="token"
+ :container="modalContainerId"
+ :aria-label="t('spreed', 'Post message')"
+ @sent="handleUpload"
+ @failure="handleDismiss" />
</div>
</NcModal>
</template>
@@ -83,6 +85,7 @@ import NcModal from '@nextcloud/vue/dist/Components/NcModal.js'
import AudioPlayer from '../MessagesList/MessagesGroup/Message/MessagePart/AudioPlayer.vue'
import FilePreview from '../MessagesList/MessagesGroup/Message/MessagePart/FilePreview.vue'
+import NewMessage from './NewMessage.vue'
import TransitionWrapper from '../TransitionWrapper.vue'
export default {
@@ -94,9 +97,16 @@ export default {
Plus,
AudioPlayer,
NcButton,
+ NewMessage,
TransitionWrapper,
},
+ data() {
+ return {
+ modalContainerId: null,
+ }
+ },
+
computed: {
token() {
return this.$store.getters.getToken()
@@ -107,10 +117,7 @@ export default {
},
files() {
- if (this.currentUploadId) {
- return this.$store.getters.getInitialisedUploads(this.currentUploadId)
- }
- return []
+ return this.$store.getters.getInitialisedUploads(this.currentUploadId)
},
showModal() {
@@ -126,11 +133,10 @@ export default {
},
firstFile() {
- return this.files[Object.keys(this.files)[0]]
+ return this.files?.at(0)?.at(1)
},
- // Hide the plus button in case this editor is used while sending a voice
- // message
+ // Hide the plus button in case this editor is used while sending a voice message
isVoiceMessage() {
if (!this.firstFile) {
return false
@@ -154,17 +160,21 @@ export default {
},
watch: {
- showModal(show) {
+ async showModal(show) {
if (show) {
- this.focus()
+ await this.getContainerId()
+ this.$nextTick(() => {
+ this.$refs.newMessage?.focusInput()
+ })
}
},
},
methods: {
- focus() {
+ async getContainerId() {
this.$nextTick(() => {
- this.$refs.submitButton.$el.focus()
+ // Postpone render of NewMessage until modal container is mounted
+ this.modalContainerId = `#modal-description-${this.$refs.modal.randId}`
})
},
@@ -172,8 +182,8 @@ export default {
this.$store.dispatch('discardUpload', this.currentUploadId)
},
- handleUpload() {
- this.$store.dispatch('uploadFiles', this.currentUploadId)
+ handleUpload(caption) {
+ this.$store.dispatch('uploadFiles', { uploadId: this.currentUploadId, caption })
},
/**
* Clicks the hidden file input when clicking the correspondent NcActionButton,
@@ -204,21 +214,11 @@ export default {
padding: 16px;
&__previews {
- overflow-x: hidden !important;
display: flex;
position: relative;
overflow: auto;
flex-wrap: wrap;
}
- &__actions {
- display: flex;
- justify-content: space-between;
- margin-top: 16px;
- margin-bottom: 4px;
- button {
- margin: 0 4px 0 4px;
- }
- }
}
.add-more {
@@ -230,5 +230,4 @@ export default {
margin: auto;
}
}
-
</style>
diff --git a/src/store/fileUploadStore.js b/src/store/fileUploadStore.js
index fee6ec559..a167927bf 100644
--- a/src/store/fileUploadStore.js
+++ b/src/store/fileUploadStore.js
@@ -38,46 +38,33 @@ import { findUniquePath, getFileExtension } from '../utils/fileUpload.js'
const state = {
attachmentFolder: loadState('spreed', 'attachment_folder', ''),
attachmentFolderFreeSpace: loadState('spreed', 'attachment_folder_free_space', 0),
- uploads: {
- },
+ uploads: {},
currentUploadId: undefined,
-
+ localUrls: {},
fileTemplatesInitialised: false,
fileTemplates: [],
}
const getters = {
- getInitialisedUploads: (state) => (uploadId) => {
+ getUploadsArray: (state) => (uploadId) => {
if (state.uploads[uploadId]) {
- const initialisedUploads = {}
- for (const index in state.uploads[uploadId].files) {
- const currentFile = state.uploads[uploadId].files[index]
- if (currentFile.status === 'initialised') {
- initialisedUploads[index] = (currentFile)
- }
- }
- return initialisedUploads
+ return Object.entries(state.uploads[uploadId].files)
} else {
- return {}
+ return []
}
},
+ getInitialisedUploads: (state, getters) => (uploadId) => {
+ return getters.getUploadsArray(uploadId)
+ .filter(([_index, uploadedFile]) => uploadedFile.status === 'initialised')
+ },
+
// Returns all the files that have been successfully uploaded provided an
// upload id
- getShareableFiles: (state) => (uploadId) => {
- if (state.uploads[uploadId]) {
- const shareableFiles = {}
- for (const index in state.uploads[uploadId].files) {
- const currentFile = state.uploads[uploadId].files[index]
- if (currentFile.status === 'successUpload') {
- shareableFiles[index] = (currentFile)
- }
- }
- return shareableFiles
- } else {
- return {}
- }
+ getShareableFiles: (state, getters) => (uploadId) => {
+ return getters.getUploadsArray(uploadId)
+ .filter(([_index, uploadedFile]) => uploadedFile.status === 'successUpload')
},
// gets the current attachment folder
@@ -90,6 +77,11 @@ const getters = {
return state.attachmentFolderFreeSpace
},
+ // returns the local Url of uploaded image
+ getLocalUrl: (state) => (referenceId) => {
+ return state.localUrls[referenceId]
+ },
+
uploadProgress: (state) => (uploadId, index) => {
if (state.uploads[uploadId].files[index]) {
return state.uploads[uploadId].files[index].uploadedSize / state.uploads[uploadId].files[index].totalSize * 100
@@ -114,7 +106,7 @@ const getters = {
const mutations = {
// Adds a "file to be shared to the store"
- addFileToBeUploaded(state, { file, temporaryMessage }) {
+ addFileToBeUploaded(state, { file, temporaryMessage, localUrl }) {
const uploadId = temporaryMessage.messageParameters.file.uploadId
const token = temporaryMessage.messageParameters.file.token
const index = temporaryMessage.messageParameters.file.index
@@ -132,6 +124,7 @@ const mutations = {
uploadedSize: 0,
temporaryMessage,
})
+ Vue.set(state.localUrls, temporaryMessage.referenceId, localUrl)
},
// Marks a given file as failed upload
@@ -254,7 +247,7 @@ const actions = {
text: '{file}', token, uploadId, index, file, localUrl, isVoiceMessage,
})
console.debug('temporarymessage: ', temporaryMessage, 'uploadId', uploadId)
- commit('addFileToBeUploaded', { file, temporaryMessage })
+ commit('addFileToBeUploaded', { file, temporaryMessage, localUrl })
}
},
@@ -282,31 +275,38 @@ const actions = {
* @param {Function} context.dispatch the contexts dispatch function.
* @param {object} context.getters the contexts getters object.
* @param {object} context.state the contexts state object.
- * @param {string} uploadId The unique uploadId
+ * @param {object} data the wrapping object
+ * @param {string} data.uploadId The unique uploadId
+ * @param {string} [data.caption] The text caption to the media
*/
- async uploadFiles({ commit, dispatch, state, getters }, uploadId) {
+ async uploadFiles({ commit, dispatch, state, getters }, { uploadId, caption }) {
if (state.currentUploadId === uploadId) {
commit('setCurrentUploadId', undefined)
}
EventBus.$emit('upload-start')
- // Tag the previously indexed files and add the temporary messages to the
- // messages list
- for (const index in state.uploads[uploadId].files) {
+ // Tag previously indexed files and add temporary messages to the MessagesList
+ // If caption is provided, attach to the last temporary message
+ const lastIndex = getters.getUploadsArray(uploadId).at(-1).at(0)
+ for (const [index, uploadedFile] of getters.getUploadsArray(uploadId)) {
// mark all files as uploading
commit('markFileAsUploading', { uploadId, index })
// Store the previously created temporary message
- const temporaryMessage = state.uploads[uploadId].files[index].temporaryMessage
+ const temporaryMessage = {
+ ...uploadedFile.temporaryMessage,
+ message: index === lastIndex ? caption : '{file}',
+ }
// Add temporary messages (files) to the messages list
dispatch('addTemporaryMessage', temporaryMessage)
// Scroll the message list
- EventBus.$emit('scroll-chat-to-bottom')
+ EventBus.$emit('scroll-chat-to-bottom', { force: true })
}
+
// Iterate again and perform the uploads
- for (const index in state.uploads[uploadId].files) {
+ await Promise.allSettled(getters.getUploadsArray(uploadId).map(async ([index, uploadedFile]) => {
// currentFile to be uploaded
- const currentFile = state.uploads[uploadId].files[index].file
+ const currentFile = uploadedFile.file
// userRoot path
const userRoot = '/files/' + getters.getUserId()
const fileName = (currentFile.newName || currentFile.name)
@@ -343,41 +343,34 @@ const actions = {
showError(t('spreed', 'Error while uploading file "{fileName}"', { fileName }))
}
- const temporaryMessage = state.uploads[uploadId].files[index].temporaryMessage
// Mark the upload as failed in the store
commit('markFileAsFailedUpload', { uploadId, index })
- dispatch('markTemporaryMessageAsFailed', {
- message: temporaryMessage,
- reason,
- })
+ dispatch('markTemporaryMessageAsFailed', { message: uploadedFile.temporaryMessage, reason })
}
-
- // Get the files that have successfully been uploaded from the store
- const shareableFiles = getters.getShareableFiles(uploadId)
- // Share each of those files to the conversation
- for (const index in shareableFiles) {
- const path = shareableFiles[index].sharePath
- const temporaryMessage = shareableFiles[index].temporaryMessage
- const metadata = JSON.stringify({ messageType: temporaryMessage.messageType })
- try {
- const token = temporaryMessage.token
- dispatch('markFileAsSharing', { uploadId, index })
- await shareFile(path, token, temporaryMessage.referenceId, metadata)
- dispatch('markFileAsShared', { uploadId, index })
- } catch (error) {
- if (error?.response?.status === 403) {
- showError(t('spreed', 'You are not allowed to share files'))
- } else {
- showError(t('spreed', 'An error happened when trying to share your file'))
- }
- dispatch('markTemporaryMessageAsFailed', {
- message: temporaryMessage,
- reason: 'failed-share',
- })
- console.error('An error happened when trying to share your file: ', error)
+ }))
+
+ // Share the files, that have successfully been uploaded from the store, to the conversation
+ await Promise.all(getters.getShareableFiles(uploadId).map(async ([index, shareableFile]) => {
+ const path = shareableFile.sharePath
+ const temporaryMessage = shareableFile.temporaryMessage
+ const metadata = (caption && index === lastIndex)
+ ? JSON.stringify({ messageType: temporaryMessage.messageType, caption })
+ : JSON.stringify({ messageType: temporaryMessage.messageType })
+ try {
+ const token = temporaryMessage.token
+ dispatch('markFileAsSharing', { uploadId, index })
+ await shareFile(path, token, temporaryMessage.referenceId, metadata)
+ dispatch('markFileAsShared', { uploadId, index })
+ } catch (error) {
+ if (error?.response?.status === 403) {
+ showError(t('spreed', 'You are not allowed to share files'))
+ } else {
+ showError(t('spreed', 'An error happened when trying to share your file'))
}
+ dispatch('markTemporaryMessageAsFailed', { message: temporaryMessage, reason: 'failed-share' })
+ console.error('An error happened when trying to share your file: ', error)
}
- }
+ }))
EventBus.$emit('upload-finished')
},
/**
diff --git a/src/store/fileUploadStore.spec.js b/src/store/fileUploadStore.spec.js
index c35c99472..cf1762f6e 100644
--- a/src/store/fileUploadStore.spec.js
+++ b/src/store/fileUploadStore.spec.js
@@ -111,6 +111,8 @@ describe('fileUploadStore', () => {
lastModified: Date.UTC(2021, 3, 25, 15, 30, 0),
},
]
+ const localUrls = ['local-url:pngimage.png', 'local-url:jpgimage.jpg', 'icon-url:text/plain']
+
await store.dispatch('initialiseUpload', {
uploadId: 'upload-id1',
token: 'XXTOKENXX',
@@ -118,22 +120,58 @@ describe('fileUploadStore', () => {
})
const uploads = store.getters.getInitialisedUploads('upload-id1')
- expect(Object.keys(uploads).length).toBe(3)
-
- for (let i = 0; i < files.length; i++) {
- expect(mockedActions.createTemporaryMessage.mock.calls[i][1].text).toBe('{file}')
- expect(mockedActions.createTemporaryMessage.mock.calls[i][1].uploadId).toBe('upload-id1')
- expect(mockedActions.createTemporaryMessage.mock.calls[i][1].index).toBeDefined()
- expect(mockedActions.createTemporaryMessage.mock.calls[i][1].file).toBe(files[i])
- expect(mockedActions.createTemporaryMessage.mock.calls[i][1].token).toBe('XXTOKENXX')
+ expect(uploads).toHaveLength(files.length)
+
+ for (const index in files) {
+ expect(mockedActions.createTemporaryMessage.mock.calls[index][1]).toMatchObject({
+ text: '{file}',
+ token: 'XXTOKENXX',
+ uploadId: 'upload-id1',
+ index: expect.anything(),
+ file: files[index],
+ localUrl: localUrls[index],
+ })
+ }
+ })
+
+ test('performs upload and sharing of single file', async () => {
+ const file = {
+ name: 'pngimage.png',
+ type: 'image/png',
+ size: 123,
+ lastModified: Date.UTC(2021, 3, 27, 15, 30, 0),
}
+ const fileBuffer = await new Blob([file]).arrayBuffer()
+
+ await store.dispatch('initialiseUpload', {
+ uploadId: 'upload-id1',
+ token: 'XXTOKENXX',
+ files: [file],
+ })
+
+ expect(store.getters.currentUploadId).toBe('upload-id1')
- expect(mockedActions.createTemporaryMessage.mock.calls[0][1].localUrl).toBe('local-url:pngimage.png')
- expect(mockedActions.createTemporaryMessage.mock.calls[1][1].localUrl).toBe('local-url:jpgimage.jpg')
- expect(mockedActions.createTemporaryMessage.mock.calls[2][1].localUrl).toBe('icon-url:text/plain')
+ const uniqueFileName = '/Talk/' + file.name + 'uniq'
+ findUniquePath.mockResolvedValueOnce(uniqueFileName)
+ client.putFileContents.mockResolvedValue()
+ shareFile.mockResolvedValue()
+
+ await store.dispatch('uploadFiles', { uploadId: 'upload-id1', caption: 'text-caption' })
+
+ expect(findUniquePath).toHaveBeenCalledTimes(1)
+ expect(findUniquePath).toHaveBeenCalledWith(client, '/files/current-user', '/Talk/' + file.name)
+
+ expect(client.putFileContents).toHaveBeenCalledTimes(1)