diff --git a/src/App.vue b/src/App.vue index 655be16cd52..d2efc9e5fb1 100644 --- a/src/App.vue +++ b/src/App.vue @@ -32,6 +32,7 @@ + @@ -61,6 +62,7 @@ import duplicateSessionHandler from './mixins/duplicateSessionHandler' import isInCall from './mixins/isInCall' import talkHashCheck from './mixins/talkHashCheck' import { generateUrl } from '@nextcloud/router' +import UploadEditor from './components/UploadEditor' export default { name: 'App', @@ -70,6 +72,7 @@ export default { LeftSidebar, PreventUnload, RightSidebar, + UploadEditor, }, mixins: [ diff --git a/src/components/MessagesList/MessagesGroup/Message/MessagePart/FilePreview.vue b/src/components/MessagesList/MessagesGroup/Message/MessagePart/FilePreview.vue index fc0c4a74328..3696adda75d 100644 --- a/src/components/MessagesList/MessagesGroup/Message/MessagePart/FilePreview.vue +++ b/src/components/MessagesList/MessagesGroup/Message/MessagePart/FilePreview.vue @@ -1,7 +1,9 @@ - - + {{ name }} - - + + + + + diff --git a/src/components/UploadEditor.vue b/src/components/UploadEditor.vue new file mode 100644 index 00000000000..2f3bfc34045 --- /dev/null +++ b/src/components/UploadEditor.vue @@ -0,0 +1,199 @@ + + + + + + + + + + + + + + + + + + + Dismiss + + + Upload + + + + + + + + diff --git a/src/store/fileUploadStore.js b/src/store/fileUploadStore.js index b7c7d498451..ed6773d2352 100644 --- a/src/store/fileUploadStore.js +++ b/src/store/fileUploadStore.js @@ -27,14 +27,33 @@ import { loadState } from '@nextcloud/initial-state' import { findUniquePath } from '../utils/fileUpload' import createTemporaryMessage from '../utils/temporaryMessage' import { EventBus } from '../services/EventBus' +import { shareFile } from '../services/filesSharingServices' const state = { attachmentFolder: loadState('talk', 'attachment_folder'), uploads: { }, + currentUploadId: undefined, + showUploadEditor: false, } const getters = { + + getInitialisedUploads: (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 + } else { + return {} + } + }, + // Returns all the files that have been successfully uploaded provided an // upload id getShareableFiles: (state) => (uploadId) => { @@ -64,17 +83,23 @@ const getters = { return 0 } }, + + currentUploadId: (state) => { + return state.currentUploadId + }, + + showUploadEditor: (state) => { + return state.showUploadEditor + }, } const mutations = { - /** - * Adds a "file to be shared to the store" - * @param {object} state the state object - * @param {object} file the file to be added to the store - * @param {number} uploadId The unique identifier of the upload operation - * @param {string} token the conversation's token - */ - addFileToBeUploaded(state, { uploadId, token, file }) { + + // Adds a "file to be shared to the store" + addFileToBeUploaded(state, { file, temporaryMessage }) { + const uploadId = temporaryMessage.messageParameters.file.uploadId + const token = temporaryMessage.messageParameters.file.token + const index = temporaryMessage.messageParameters.file.index // Create upload id if not present if (!state.uploads[uploadId]) { Vue.set(state.uploads, uploadId, { @@ -82,7 +107,13 @@ const mutations = { files: {}, }) } - Vue.set(state.uploads[uploadId].files, Object.keys(state.uploads[uploadId].files).length, { file, status: 'toBeUploaded', totalSize: file.size, uploadedSize: 0 }) + Vue.set(state.uploads[uploadId].files, index, { + file, + status: 'initialised', + totalSize: file.size, + uploadedSize: 0, + temporaryMessage, + }) }, // Marks a given file as failed upload @@ -131,36 +162,68 @@ const mutations = { console.debug('uploadId: ' + uploadId + ' index: ' + index) Vue.set(state.uploads[uploadId].files[index], 'temporaryMessage', temporaryMessage) }, + + // Sets the id of the current upload operation + setCurrentUploadId(state, currentUploadId) { + state.currentUploadId = currentUploadId + }, + + // Shows hides the upload editor + showUploadEditor(state, show) { + state.showUploadEditor = show + }, + + removeFileFromSelection(state, fileId) { + const uploadId = state.currentUploadId + for (const key in state.uploads[uploadId].files) { + if (state.uploads[uploadId].files[key].temporaryMessage.id === fileId) { + Vue.delete(state.uploads[uploadId].files, key) + } + } + }, } const actions = { + + initialiseUpload({ commit, dispatch }, { uploadId, token, files }) { + // Set last upload id + commit('setCurrentUploadId', uploadId) + // Show upload editor + commit('showUploadEditor', true) + + files.forEach(file => { + // Get localurl for previews + const localUrl = URL.createObjectURL(file) + // Create a unique index for each file + 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 = createTemporaryMessage('{file}', token, uploadId, index, file, localUrl) + console.debug('temporarymessage: ', temporaryMessage, 'uploadId', uploadId) + commit('addFileToBeUploaded', { file, temporaryMessage }) + }) + }, + /** * Uploads the files to the root directory of the user * @param {object} param0 Commit, state and getters - * @param {object} param1 The unique uploadId, the conversation token and the - * files array + * @param {object} uploadId The unique uploadId */ - async uploadFiles({ commit, dispatch, state, getters }, { uploadId, token, files }) { - files.forEach(file => { - commit('addFileToBeUploaded', { uploadId, token, file }) - }) + async uploadFiles({ commit, dispatch, state, getters }, uploadId) { - // Add temporary messages + // Tag the previously indexed files and add the temporary messages to the + // messages list for (const index in state.uploads[uploadId].files) { - // Mark file as uploading to prevent a second function call to start a - // second upload for the same file + // mark all files as uploading commit('markFileAsUploading', { uploadId, index }) - // currentFile to be uploaded - const currentFile = state.uploads[uploadId].files[index].file - // Create temporary message for the file and add it to the message list - const temporaryMessage = createTemporaryMessage('{file}', token, uploadId, index, currentFile) + // Store the previously created temporary message + const temporaryMessage = state.uploads[uploadId].files[index].temporaryMessage + // Add temporary messages (files) to the messages list dispatch('addTemporaryMessage', temporaryMessage) // Scroll the message list EventBus.$emit('scrollChatToBottom') - commit('setTemporaryMessageForFile', { uploadId, index, temporaryMessage }) } - - // Iterate through the previously indexed files for a given conversation (token) + // Iterate again and perform the uploads for (const index in state.uploads[uploadId].files) { // currentFile to be uploaded const currentFile = state.uploads[uploadId].files[index].file @@ -186,6 +249,22 @@ const actions = { // Mark the upload as failed in the store commit('markFileAsFailedUpload', { uploadId, index }) } + + // 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 + try { + const temporaryMessage = shareableFiles[index].temporaryMessage + const token = temporaryMessage.token + dispatch('markFileAsSharing', { uploadId, index }) + await shareFile(path, token, temporaryMessage.referenceId) + dispatch('markFileAsShared', { uploadId, index }) + } catch (exception) { + console.debug('An error happened when trying to share your file: ', exception) + } + } } }, /** @@ -220,6 +299,10 @@ const actions = { context.commit('markFileAsShared', { uploadId, index }) }, + removeFileFromSelection({ commit }, fileId) { + commit('removeFileFromSelection', fileId) + }, + } export default { state, mutations, getters, actions } diff --git a/src/utils/fileUpload.js b/src/utils/fileUpload.js index 5488e44f195..2ad6128e415 100644 --- a/src/utils/fileUpload.js +++ b/src/utils/fileUpload.js @@ -29,7 +29,6 @@ * @returns {string} The unique path */ -import { shareFile } from '../services/filesSharingServices' import store from '../store/index' const findUniquePath = async function(client, userRoot, path) { @@ -69,23 +68,8 @@ const findUniquePath = async function(client, userRoot, path) { */ const processFiles = async function(files, token, uploadId) { // Process these files in the store - await store.dispatch('uploadFiles', { uploadId, token, files }) - // Get the files that have successfully been uploaded from the store - const shareableFiles = store.getters.getShareableFiles(uploadId) + await store.dispatch('initialiseUpload', { uploadId, token, files }) - // Share each of those files in the conversation - for (const index in shareableFiles) { - const path = shareableFiles[index].sharePath - try { - const temporaryMessage = shareableFiles[index].temporaryMessage - - store.dispatch('markFileAsSharing', { uploadId, index }) - await shareFile(path, token, temporaryMessage.referenceId) - store.dispatch('markFileAsShared', { uploadId, index }) - } catch (exception) { - console.debug('An error happened when triying to share your file: ', exception) - } - } } export { diff --git a/src/utils/temporaryMessage.js b/src/utils/temporaryMessage.js index d7e04c6c8ca..39a54624de0 100644 --- a/src/utils/temporaryMessage.js +++ b/src/utils/temporaryMessage.js @@ -24,21 +24,23 @@ import store from '../store/index' import SHA1 from 'crypto-js/sha1' import Hex from 'crypto-js/enc-hex' -const createTemporaryMessage = (text, token, uploadId, index, file) => { +const createTemporaryMessage = (text, token, uploadId, index, file, localUrl) => { const messageToBeReplied = store.getters.getMessageToBeReplied(token) const date = new Date() let tempId = 'temp-' + date.getTime() const messageParameters = {} if (file) { - tempId += '-' + uploadId + '-' + index + tempId += '-' + uploadId + '-' + Math.random() messageParameters.file = { 'type': 'file', 'file': file, 'mimetype': file.type, 'id': tempId, 'name': file.name, - index, + // index, will be the id from now on uploadId, + localUrl, + index, } } const message = Object.assign({}, {