diff options
-rw-r--r-- | src/components/NewMessageForm/NewMessageForm.vue | 61 | ||||
-rw-r--r-- | src/services/filesSharingServices.js | 37 | ||||
-rw-r--r-- | src/store/fileUploadStore.js | 132 | ||||
-rw-r--r-- | src/store/index.js | 2 |
4 files changed, 178 insertions, 54 deletions
diff --git a/src/components/NewMessageForm/NewMessageForm.vue b/src/components/NewMessageForm/NewMessageForm.vue index d7f8695af..f24854147 100644 --- a/src/components/NewMessageForm/NewMessageForm.vue +++ b/src/components/NewMessageForm/NewMessageForm.vue @@ -76,13 +76,12 @@ <script> import AdvancedInput from './AdvancedInput/AdvancedInput' -import { getFilePickerBuilder, showError } from '@nextcloud/dialogs' +import { getFilePickerBuilder } from '@nextcloud/dialogs' import { postNewMessage } from '../../services/messagesService' import Quote from '../Quote' import Actions from '@nextcloud/vue/dist/Components/Actions' import ActionButton from '@nextcloud/vue/dist/Components/ActionButton' -import client from '../../services/DavClient' -import { shareFileToRoom } from '../../services/filesSharingServices' +import { shareFile } from '../../services/filesSharingServices' const picker = getFilePickerBuilder(t('spreed', 'File to share')) .setMultiSelect(false) @@ -243,29 +242,6 @@ export default { } }, - /** - * Appends a file as a message to the messagelist. - * @param {string} path The file path from the user's root directory - * e.g. `/myfile.txt` - */ - async sendFile(path) { - try { - await shareFileToRoom(path, this.token) - } catch (error) { - if (error.response - && error.response.data - && error.response.data.ocs - && error.response.data.ocs.meta - && error.response.data.ocs.meta.message) { - console.error(`Error while sharing file: ${error.response.data.ocs.meta.message || 'Unknown error'}`) - OCP.Toast.error(error.response.data.ocs.meta.message) - } else { - console.error(`Error while sharing file: Unknown error`) - OCP.Toast.error(t('files', 'Error while sharing file')) - } - } - }, - async handleFileShare() { picker.pick() .then(async(path) => { @@ -273,7 +249,7 @@ export default { if (!path.startsWith('/')) { throw new Error(t('files', 'Invalid path selected')) } - this.sendFile(path) + shareFile(path, this.token) }) }, @@ -286,25 +262,26 @@ export default { }, /** - * Uploads the files to the root files directory + * Uploads and shares the selected files * @param {object} event the file input event object */ async processFiles(event) { - // The selected files array + // Store the token in a variable to prevent changes when changing conversation + // when the upload is still running + const token = this.token + // The selected files array coming from the input const files = Object.values(event.target.files) - // process each file in the array - for (let i = 0; i < files.length; i++) { - const userId = this.$store.getters.getUserId() - const path = `/files/${userId}/` + files[i].name - try { - // Upload the file - await client.putFileContents(path, files[i]) - // Share the file to the talk room - this.sendFile('/' + files[i].name) - } catch (exception) { - console.debug('Error while uploading file:' + exception) - showError(t('spreed', 'Error while uploading file')) - } + // Process these files in the store + await this.$store.dispatch('addFilesToBeUploaded', { token, files }) + // Dispatch the upload action (see uploadStore) + await this.$store.dispatch('uploadFiles', token) + // Get the files that have successfully been uploaded from the store + const shareableFiles = this.$store.getters.getShareableFiles(token) + // Share each of those files in the conversation + for (const index in shareableFiles) { + // The pat of the file to share to the conversation + const path = '/' + shareableFiles[index].name + shareFile(path, token) } }, }, diff --git a/src/services/filesSharingServices.js b/src/services/filesSharingServices.js index 011570920..3aa1b35fa 100644 --- a/src/services/filesSharingServices.js +++ b/src/services/filesSharingServices.js @@ -21,25 +21,38 @@ */ import axios from '@nextcloud/axios' import { generateOcsUrl } from '@nextcloud/router' +import { showError } from '@nextcloud/dialogs' /** * Appends a file as a message to the messagelist. * @param {string} path The file path from the user's root directory - * e.g. `/myfile.txt` * @param {string} token The conversation's token - * @returns {object} the response object + * e.g. `/myfile.txt` */ -const shareFileToRoom = (path, token) => { - const response = axios.post( - generateOcsUrl('apps/files_sharing/api/v1', 2) + 'shares', - { - shareType: 10, // OC.Share.SHARE_TYPE_ROOM, - path: path, - shareWith: token, - }) - return response +const shareFile = async function(path, token) { + try { + return axios.post( + generateOcsUrl('apps/files_sharing/api/v1', 2) + 'shares', + { + shareType: 10, // OC.Share.SHARE_TYPE_ROOM, + path: path, + shareWith: token, + }) + } catch (error) { + if (error.response + && error.response.data + && error.response.data.ocs + && error.response.data.ocs.meta + && error.response.data.ocs.meta.message) { + console.error(`Error while sharing file: ${error.response.data.ocs.meta.message || 'Unknown error'}`) + showError(error.response.data.ocs.meta.message) + } else { + console.error(`Error while sharing file: Unknown error`) + showError(t('spreed', 'Error while sharing file')) + } + } } export { - shareFileToRoom + shareFile, } diff --git a/src/store/fileUploadStore.js b/src/store/fileUploadStore.js new file mode 100644 index 000000000..845bf2441 --- /dev/null +++ b/src/store/fileUploadStore.js @@ -0,0 +1,132 @@ +/** + * @copyright Copyright (c) 2019 Marco Ambrosini <marcoambrosini@pm.me> + * + * @author Marco Ambrosini <marcoambrosini@pm.me> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +import Vue from 'vue' +import client from '../services/DavClient' +import { showError } from '@nextcloud/dialogs' + +const state = { + files: { + }, +} + +const getters = { + // Returns all the files that have been marked for upload in a given conversation, + // regardless of their upload state + getFiles: (state) => (token) => { + return state.files[token] + }, + // Returns all the files that have been successfully uploaded + getShareableFiles: (state) => (token) => { + if (state.files[token]) { + const shareableFiles = [] + for (const index in state.files[token]) { + const currentFile = state.files[token][index] + if (currentFile.status === 'successUpload') { + shareableFiles.push(currentFile.file) + } + } + return shareableFiles + } return undefined + }, +} + +const mutations = { + /** + * Adds a "file to be shared to the store" + * @param {*} state the state object + * @param {*} file the file to be added to the store + * @param {*} token the conversation's token + */ + addFileToBeUploaded(state, { token, file }) { + if (!state.files[token]) { + Vue.set(state.files, token, {}) + } + Vue.set(state.files[token], Object.keys(state.files[token]).length, { file, status: 'toBeUploaded' }) + }, + + // Marks a given file as failed uplosd + markFileAsFailedUpload(state, { token, index }) { + state.files[token][index].status = 'failedUpload' + }, + + // Marks a given file as uploaded + markFileAsSuccessUpload(state, { token, index }) { + state.files[token][index].status = 'successUpload' + }, + + // Marks a given file as uploading + markFileAsUploading(state, { token, index }) { + state.files[token][index].status = 'uploading' + }, +} + +const actions = { + + /** + * + * @param {object} context The context object + * @param {string} token The conversation's token + * @param {array} files The files to be processed and added to the store + */ + addFilesToBeUploaded(context, { token, files }) { + files.forEach(file => { + context.commit('addFileToBeUploaded', { token, file }) + }) + }, + + /** + * Uploads the files to the root directory of the user + * @param {object} param0 Commit, state and getters + * @param {*} token The conversation's token + */ + async uploadFiles({ commit, state, getters }, token) { + // Iterate through the previously indexed files for a given conversation (token) + for (const index in state.files[token]) { + if (state.files[token][index].status !== 'toBeUploaded') { + continue + } + // Mark file as uploading to prevent a second function call to start a + // second upload for the same file + commit('markFileAsUploading', { token, index }) + // Get the current user id + const userId = getters.getUserId() + // currentFile to be uploaded + const currentFile = state.files[token][index].file + // Destination path on the server + const path = `/files/${userId}/` + currentFile.name + try { + // Upload the file + await client.putFileContents(path, currentFile) + // Mark the file as uploaded in the store + commit('markFileAsSuccessUpload', { token, index }) + } catch (exception) { + console.debug('Error while uploading file:' + exception) + showError(t('spreed', 'Error while uploading file')) + // Mark the upload as failed in the store + commit('markFileAsFailedUpload', { token, index }) + } + } + }, +} + +export default { state, mutations, getters, actions } diff --git a/src/store/index.js b/src/store/index.js index 18e9fb17d..88b93c5f1 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -31,6 +31,7 @@ import quoteReplyStore from './quoteReplyStore' import sidebarStore from './sidebarStore' import tokenStore from './tokenStore' import windowVisibilityStore from './windowVisibilityStore' +import fileUploadStore from './fileUploadStore' Vue.use(Vuex) @@ -47,6 +48,7 @@ export default new Store({ sidebarStore, tokenStore, windowVisibilityStore, + fileUploadStore, }, mutations, |