diff --git a/src/components/Folder.vue b/src/components/Folder.vue index 7ac07aaf2..881a404b0 100644 --- a/src/components/Folder.vue +++ b/src/components/Folder.vue @@ -77,6 +77,7 @@ export default { const previewFiles = previewFolderContent ? previewFolderContent .map(id => this.files[id]) + .filter(file => !!file) // id might not exist in files .slice(0, 4) // only get the 4 first images : [] diff --git a/src/mixins/FetchFacesMixin.js b/src/mixins/FetchFacesMixin.js index c87c5b329..0b076f0b1 100644 --- a/src/mixins/FetchFacesMixin.js +++ b/src/mixins/FetchFacesMixin.js @@ -121,15 +121,17 @@ export default { .map(file => genFileInfo(file)) .map(file => ({ ...file, filename: file.realpath.replace(`/${getCurrentUser().uid}/files`, '') })) - const fileIds = fetchedFiles.map(file => '' + file.fileid) + const appendedFiles = await this.$store.dispatch('appendFiles', fetchedFiles) + const fileIds = appendedFiles + .map(file => file.fileid.toString()) - this.appendFiles(fetchedFiles) - - if (fetchedFiles.length > 0) { + if (fileIds.length > 0) { await this.$store.commit('addFilesToFace', { faceName, fileIdsToAdd: fileIds }) + } else { + await this.$store.commit('removeFaces', { faceNames: [faceName] }) } - logger.debug(`[FetchFacesMixin] Fetched ${fileIds.length} new files: `, fileIds) + logger.debug(`[FetchFacesMixin] Fetched ${fileIds.length} new files (from available ${fetchedFiles.length}): `, fileIds) } catch (error) { if (error.response && error.response.status) { if (error.response.status === 404) { diff --git a/src/mixins/FetchFilesMixin.js b/src/mixins/FetchFilesMixin.js index e07a6f969..09f1e8511 100644 --- a/src/mixins/FetchFilesMixin.js +++ b/src/mixins/FetchFilesMixin.js @@ -85,19 +85,21 @@ export default { this.doneFetchingFiles = true } - const fileIds = fetchedFiles - .map(file => file.fileid) + let fileIds = fetchedFiles + .map(file => file.fileid.toString()) .filter(fileId => !this.fetchedFileIds.includes(fileId)) // Filter to prevent duplicate fileIds. this.fetchedFileIds.push( ...fileIds - .map((fileId) => fileId.toString()) .filter((fileId) => !blacklist.includes(fileId)) ) - this.$store.dispatch('appendFiles', fetchedFiles) + const appendedFiles = await this.$store.dispatch('appendFiles', fetchedFiles) + fileIds = appendedFiles + .map(file => file.fileid.toString()) + .filter(fileId => fileIds.includes(fileId)) // Ensure fileIds had been selected before - logger.debug(`[FetchFilesMixin] Fetched ${fileIds.length} new files: `, fileIds) + logger.debug(`[FetchFilesMixin] Fetched ${fileIds.length} new files (from available ${fetchedFiles.length}): `, fileIds) return fileIds } catch (error) { diff --git a/src/store/faces.js b/src/store/faces.js index c49f1a184..b8eae6e3d 100644 --- a/src/store/faces.js +++ b/src/store/faces.js @@ -62,8 +62,8 @@ const mutations = { * @param {Array} data.faceNames list of faces ids */ removeFaces(state, { faceNames }) { - faceNames.forEach(faceName => delete state.faces[faceName]) - faceNames.forEach(faceName => delete state.facesFiles[faceName]) + faceNames.forEach(faceName => Vue.delete(state.faces, faceName)) + faceNames.forEach(faceName => Vue.delete(state.facesFiles, faceName)) }, /** diff --git a/src/store/files.js b/src/store/files.js index cbbd7aafd..4a0b9c5eb 100644 --- a/src/store/files.js +++ b/src/store/files.js @@ -148,11 +148,14 @@ const actions = { /** * Append or update given files * - * @param {object} context the store mutations + * @param {object.commit} commit the store mutation commit function + * @param {object.state} state the store state * @param {Array} files list of files + * @return {Array} the appended files */ - appendFiles(context, files = []) { - context.commit('updateFiles', files) + appendFiles({ commit, state }, files = []) { + commit('updateFiles', files) + return files.filter(file => !!state.files[file.fileid]) }, /** @@ -176,7 +179,7 @@ const actions = { const semaphore = new Semaphore(5) const files = fileIds - .map(fileId => state.files[fileId]) + .map(fileId => context.state.files[fileId]) .reduce((files, file) => ({ ...files, [file.fileid]: file }), {}) fileIds.forEach(fileId => context.commit('deleteFile', fileId)) diff --git a/src/store/folders.js b/src/store/folders.js index 198488241..8ba4222fe 100644 --- a/src/store/folders.js +++ b/src/store/folders.js @@ -82,6 +82,25 @@ const mutations = { Vue.set(state.folders, fileid, [...list, ...state.folders[fileid]]) } }, + + /** + * Remove files from a folder + * + * @param {object} state vuex state + * @param {object} data destructuring object + * @param {number} data.fileid id of this folder + * @param {Array} data.files list of files to remove + */ + removeFilesFromFolder(state, { fileid, files }) { + const removedIds = files + .filter(file => file && file.fileid) + .map(file => `${file.fileid}`) + + const folderFileIds = state.folders[fileid] + if (folderFileIds) { + Vue.set(state.folders, fileid, folderFileIds.filter(id => !removedIds.includes(`${id}`))) + } + }, } const getters = { @@ -130,6 +149,18 @@ const actions = { addFilesToFolder(context, { fileid, files }) { context.commit('addFilesToFolder', { fileid, files }) }, + + /** + * Remove files from a folder + * + * @param {object} context vuex context + * @param {object} data destructuring object + * @param {number} data.fileid id of this folder + * @param {Array} data.files list of files to remove + */ + removeFilesFromFolder(context, { fileid, files }) { + context.commit('removeFilesFromFolder', { fileid, files }) + }, } export default { state, mutations, getters, actions } diff --git a/src/store/systemtags.js b/src/store/systemtags.js index 94a2dfb7c..01b35a4f0 100644 --- a/src/store/systemtags.js +++ b/src/store/systemtags.js @@ -132,9 +132,9 @@ const actions = { async fetchTagFiles(context, { id, signal }) { try { // get data - const files = await getTaggedImages(id, { signal }) + let files = await getTaggedImages(id, { signal }) + files = await context.dispatch('appendFiles', files) await context.dispatch('updateTag', { id, files }) - await context.dispatch('appendFiles', files) } catch (error) { if (error.response && error.response.status) { console.error('Failed to get tag content', id, error.response) diff --git a/src/views/AlbumContent.vue b/src/views/AlbumContent.vue index 7570cfef6..fbc7bae34 100644 --- a/src/views/AlbumContent.vue +++ b/src/views/AlbumContent.vue @@ -350,12 +350,11 @@ export default { .map(file => genFileInfo(file)) .filter(file => file.fileid) - const fileIds = fetchedFiles + const appendedFiles = await this.$store.dispatch('appendFiles', fetchedFiles) + const fileIds = appendedFiles .map(file => file.fileid.toString()) - this.appendFiles(fetchedFiles) - - if (fetchedFiles.length > 0) { + if (appendedFiles.length > 0) { await this.$store.commit('setAlbumFiles', { albumName: this.albumName, fileIds }) } diff --git a/src/views/Folders.vue b/src/views/Folders.vue index a5f263742..55f2d6c20 100644 --- a/src/views/Folders.vue +++ b/src/views/Folders.vue @@ -65,6 +65,7 @@ import { mapGetters } from 'vuex' import { UploadPicker } from '@nextcloud/upload' import { NcEmptyContent } from '@nextcloud/vue' +import { subscribe, unsubscribe } from '@nextcloud/event-bus' import VirtualGrid from 'vue-virtual-grid' import FileLegacy from '../components/FileLegacy.vue' @@ -213,6 +214,14 @@ export default { this.fetchFolderContent() }, + created() { + subscribe('files:file:deleted', this.onDelete) // listen for delete in Viewer.vue + }, + + beforeDestroy() { + unsubscribe('files:file:deleted', this.onDelete) + }, + methods: { onRefresh() { this.fetchFolderContent() @@ -260,6 +269,12 @@ export default { } }, + onDelete(file) { + if (file && file.fileid) { + this.$store.dispatch('removeFilesFromFolder', { fileid: this.folderId, files: [file] }) + } + }, + /** * Fetch file Info and add them into the store * @@ -269,8 +284,8 @@ export default { uploads.forEach(async upload => { const relPath = upload.path.split(prefixPath).pop() const file = await getFileInfo(relPath) - this.$store.dispatch('appendFiles', [file]) - this.$store.dispatch('addFilesToFolder', { fileid: this.folderId, files: [file] }) + const files = await this.$store.dispatch('appendFiles', [file]) + this.$store.dispatch('addFilesToFolder', { fileid: this.folderId, files }) }) }, }, diff --git a/src/views/PublicAlbumContent.vue b/src/views/PublicAlbumContent.vue index 7511e5211..127736ab5 100644 --- a/src/views/PublicAlbumContent.vue +++ b/src/views/PublicAlbumContent.vue @@ -259,16 +259,15 @@ export default { this.publicClient, ) - const fileIds = fetchedFiles + const appendedFiles = await this.$store.dispatch('appendFiles', fetchedFiles) + const fileIds = appendedFiles .map(file => file.fileid.toString()) - this.appendFiles(fetchedFiles) - - if (fetchedFiles.length > 0) { + if (appendedFiles.length > 0) { await this.$store.commit('addFilesToPublicAlbum', { collectionId: this.albumName, fileIdsToAdd: fileIds }) } - return fetchedFiles + return appendedFiles } catch (error) { if (error.response?.status === 404) { this.errorFetchingFiles = 404 diff --git a/src/views/SharedAlbumContent.vue b/src/views/SharedAlbumContent.vue index d2ba59c2f..c7794ab99 100644 --- a/src/views/SharedAlbumContent.vue +++ b/src/views/SharedAlbumContent.vue @@ -242,17 +242,15 @@ export default { const fetchedFiles = response.data .map(file => genFileInfo(file)) - const fileIds = fetchedFiles - .map(file => file.fileid) - .map((fileId) => fileId.toString()) + const appendedFiles = await this.$store.dispatch('appendFiles', fetchedFiles) + const fileIds = appendedFiles + .map(file => file.fileid.toString()) - this.appendFiles(fetchedFiles) - - if (fetchedFiles.length > 0) { + if (appendedFiles.length > 0) { await this.$store.commit('addFilesToSharedAlbum', { albumName: this.albumName, fileIdsToAdd: fileIds }) } - logger.debug(`[SharedAlbumContent] Fetched ${fileIds.length} new files: `, fileIds) + logger.debug(`[SharedAlbumContent] Fetched ${fileIds.length} new files (from available ${fetchedFiles.length}): `, fileIds) } catch (error) { if (error.response?.status === 404) { this.errorFetchingFiles = 404 diff --git a/src/views/Timeline.vue b/src/views/Timeline.vue index 85fc96166..957d934d2 100644 --- a/src/views/Timeline.vue +++ b/src/views/Timeline.vue @@ -133,6 +133,7 @@ import PlusBoxMultiple from 'vue-material-design-icons/PlusBoxMultiple' import Download from 'vue-material-design-icons/Download' import { NcModal, NcActions, NcActionButton, NcButton, NcEmptyContent, isMobile } from '@nextcloud/vue' +import { subscribe, unsubscribe } from '@nextcloud/event-bus' import moment from '@nextcloud/moment' import { allMimes } from '../services/AllowedMimes.js' @@ -230,6 +231,14 @@ export default { ]), }, + created() { + subscribe('files:file:deleted', this.onDeleteFile) // listen for delete in Viewer.vue + }, + + beforeDestroy() { + unsubscribe('files:file:deleted', this.onDeleteFile) + }, + methods: { ...mapActions(['deleteFiles', 'addFilesToAlbum']), @@ -261,12 +270,23 @@ export default { }, async deleteSelection() { - // Need to store the file ids so it is not changed before the deleteFiles call. - const fileIds = this.selectedFileIds - this.onUncheckFiles(fileIds) - this.fetchedFileIds = this.fetchedFileIds.filter(fileid => !fileIds.includes(fileid)) + const fileIds = [...this.selectedFileIds] + this.removeFromFetchedFiles(fileIds) await this.deleteFiles(fileIds) }, + + onDeleteFile(file) { + const fileId = (file && file.fileid && file.fileid.toString()) + if (fileId) { + this.removeFromFetchedFiles([fileId]) + this.$store.commit('deleteFile', fileId) + } + }, + + removeFromFetchedFiles(fileIds) { + this.onUncheckFiles(fileIds) + this.fetchedFileIds = this.fetchedFileIds.filter(fileId => !fileIds.includes(fileId)) + }, }, }