From 7e70c40db7cfbbb079806feaeed7a70a01bd6c04 Mon Sep 17 00:00:00 2001 From: Louis Chemineau Date: Tue, 28 Feb 2023 19:04:58 +0100 Subject: [PATCH] Switch publicAlbums to collections helpers and improve typing Signed-off-by: Louis Chemineau --- src/mixins/FetchCollectionContentMixin.js | 17 +- src/mixins/FetchCollectionsMixin.js | 7 +- src/store/collectionStoreFactory.js | 211 ---------------------- src/store/index.js | 4 +- src/store/publicAlbums.js | 41 +++++ src/views/PublicAlbumContent.vue | 151 +++++----------- 6 files changed, 104 insertions(+), 327 deletions(-) delete mode 100644 src/store/collectionStoreFactory.js create mode 100644 src/store/publicAlbums.js diff --git a/src/mixins/FetchCollectionContentMixin.js b/src/mixins/FetchCollectionContentMixin.js index d6712902c..866b6c9a8 100644 --- a/src/mixins/FetchCollectionContentMixin.js +++ b/src/mixins/FetchCollectionContentMixin.js @@ -54,10 +54,11 @@ export default { ]), /** * @param {string} collectionFileName - * @param {string[]} extraProps - Extra properties to add to the DAV request. + * @param {string[]} [extraProps] - Extra properties to add to the DAV request. + * @param {import('webdav').WebDAVClient} [client] - The DAV client to use. * @return {Promise} */ - async fetchCollection(collectionFileName, extraProps) { + async fetchCollection(collectionFileName, extraProps, client) { if (this.loadingCollection) { return null } @@ -66,7 +67,7 @@ export default { this.loadingCollection = true this.errorFetchingCollection = null - const collection = await fetchCollection(collectionFileName, { signal: this.abortController.signal }, extraProps) + const collection = await fetchCollection(collectionFileName, { signal: this.abortController.signal }, extraProps, client) this.addCollections({ collections: [collection] }) return collection } catch (error) { @@ -87,10 +88,12 @@ export default { /** * @param {string} collectionFileName - * @param {string[]} extraProps - Extra properties to add to the DAV request. + * @param {string[]} [extraProps] - Extra properties to add to the DAV request. + * @param {import('webdav').WebDAVClient} [client] - The DAV client to use. + * @param {((value: import('../services/collectionFetcher.js').CollectionFile, index: number, array: import('../services/collectionFetcher.js').CollectionFile[]) => any)[]} [mappers] - Callback that can transform files before they are appended. * @return {Promise} */ - async fetchCollectionFiles(collectionFileName, extraProps = []) { + async fetchCollectionFiles(collectionFileName, extraProps, client, mappers = []) { if (this.loadingCollectionFiles) { return [] } @@ -101,9 +104,11 @@ export default { this.errorFetchingCollectionFiles = null this.loadingCollectionFiles = true - const fetchedFiles = await fetchCollectionFiles(collectionFileName, { signal: this.abortController.signal }, extraProps) + let fetchedFiles = await fetchCollectionFiles(collectionFileName, { signal: this.abortController.signal }, extraProps, client) const fileIds = fetchedFiles.map(file => file.fileid.toString()) + mappers.forEach(mapper => (fetchedFiles = fetchedFiles.map(mapper))) + this.appendFiles(fetchedFiles) if (fetchedFiles.length > 0) { diff --git a/src/mixins/FetchCollectionsMixin.js b/src/mixins/FetchCollectionsMixin.js index 8d914201f..f31a8fd32 100644 --- a/src/mixins/FetchCollectionsMixin.js +++ b/src/mixins/FetchCollectionsMixin.js @@ -46,10 +46,11 @@ export default { /** * @param {string} collectionHome - * @param {string[]} extraProps - Extra properties to add to the DAV request. + * @param {string[]} [extraProps] - Extra properties to add to the DAV request. + * @param {import('webdav').WebDAVClient} [client] - The DAV client to use. * @return {Promise} */ - async fetchCollections(collectionHome, extraProps = []) { + async fetchCollections(collectionHome, extraProps, client) { if (this.loadingCollections) { return [] } @@ -58,7 +59,7 @@ export default { this.loadingCollections = true this.errorFetchingCollections = null - const collections = await fetchCollections(collectionHome, { signal: this.abortController.signal }, extraProps) + const collections = await fetchCollections(collectionHome, { signal: this.abortController.signal }, extraProps, client) this.addCollections({ collections }) diff --git a/src/store/collectionStoreFactory.js b/src/store/collectionStoreFactory.js deleted file mode 100644 index 638e7bcc8..000000000 --- a/src/store/collectionStoreFactory.js +++ /dev/null @@ -1,211 +0,0 @@ -/** - * @copyright Copyright (c) 2022 Louis Chemineau - * - * @author Louis Chemineau - * - * @license AGPL-3.0-or-later - * - * 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 . - * - */ - -import { showError } from '@nextcloud/dialogs' - -import client from '../services/DavClient.js' -import logger from '../services/logger.js' -import Semaphore from '../utils/semaphoreWithPriority.js' -import { translate } from '@nextcloud/l10n' - -/** - * @param {string} collectionName - The name of the collection/ - */ -export default function collectionStoreFactory(collectionName) { - const capitalizedCollectionName = collectionName[0].toUpperCase() + collectionName.substr(1) - - const state = { - [`${collectionName}s`]: {}, - [`${collectionName}sFiles`]: {}, - } - - const mutations = { - /** - * Add a list of collections. - * - * @param {object} state vuex state - * @param {object} data destructuring object - * @param {Array} data.collections list of collections - */ - [`add${capitalizedCollectionName}s`](state, { collections }) { - state[`${collectionName}s`] = { - ...state[`${collectionName}s`], - ...collections.reduce((collections, collection) => ({ ...collections, [collection.basename]: collection }), {}), - } - }, - - /** - * Remove a list of collections. - * - * @param {object} state vuex state - * @param {object} data destructuring object - * @param {Array} data.collectionIds list of collection ids - */ - [`remove${capitalizedCollectionName}s`](state, { collectionIds }) { - collectionIds.forEach(collectionId => delete state[`${collectionName}s`][collectionId]) - collectionIds.forEach(collectionId => delete state[`${collectionName}sFiles`][collectionId]) - }, - - /** - * Add files to a collection. - * - * @param {object} state vuex state - * @param {object} data destructuring object - * @param {string} data.collectionId the collection id - * @param {string[]} data.fileIdsToAdd list of files - */ - [`addFilesTo${capitalizedCollectionName}`](state, { collectionId, fileIdsToAdd }) { - const collectionFiles = state[`${collectionName}sFiles`][collectionId] || [] - state[`${collectionName}sFiles`] = { - ...state[`${collectionName}sFiles`], - [collectionId]: [...new Set([...collectionFiles, ...fileIdsToAdd])], - } - state[`${collectionName}s`][collectionId].nbItems += fileIdsToAdd.length - }, - - /** - * Remove files to an collection. - * - * @param {object} state vuex state - * @param {object} data destructuring object - * @param {string} data.collectionId the collection id - * @param {string[]} data.fileIdsToRemove list of files - */ - [`removeFilesFrom${capitalizedCollectionName}`](state, { collectionId, fileIdsToRemove }) { - state[`${collectionName}sFiles`] = { - ...state[`${collectionName}sFiles`], - [collectionId]: state[`${collectionName}sFiles`][collectionId].filter(fileId => !fileIdsToRemove.includes(fileId)), - } - state[`${collectionName}s`][collectionId].nbItems -= fileIdsToRemove.length - }, - } - - const getters = { - [`${collectionName}s`]: state => state[`${collectionName}s`], - [`${collectionName}sFiles`]: state => state[`${collectionName}sFiles`], - } - - const actions = { - /** - * Update files and collections - * - * @param {object} context vuex context - * @param {object} data destructuring object - * @param {Array} data.collections list of collections - */ - [`add${capitalizedCollectionName}s`](context, { collections }) { - context.commit(`add${capitalizedCollectionName}s`, { collections }) - }, - - /** - * Add files to an collection. - * - * @param {object} context vuex context - * @param {object} data destructuring object - * @param {string} data.collectionId the collection name - * @param {string[]} data.fileIdsToAdd list of files ids to add - */ - async [`addFilesTo${capitalizedCollectionName}`](context, { collectionId, fileIdsToAdd }) { - const semaphore = new Semaphore(5) - - context.commit(`addFilesTo${capitalizedCollectionName}`, { collectionId, fileIdsToAdd }) - - const promises = fileIdsToAdd - .map(async (fileId) => { - const file = context.getters.files[fileId] - const collection = context.getters[`${collectionName}s`][collectionId] - const symbol = await semaphore.acquire() - - try { - await client.copyFile( - file.filename, - `${collection.filename}/${file.basename}`, - ) - } catch (error) { - if (error.response.status !== 409) { // Already in the collection. - context.commit(`removeFilesFrom${capitalizedCollectionName}`, { collectionId, fileIdsToRemove: [fileId] }) - - logger.error(translate('photos', 'Failed to add {fileBaseName} to {collectionId}.', { fileBaseName: file.basename, collectionId }), { error }) - showError(translate('photos', 'Failed to add {fileBaseName} to {collectionId}.', { fileBaseName: file.basename, collectionId })) - } - } finally { - semaphore.release(symbol) - } - }) - - return Promise.all(promises) - }, - - /** - * Remove files to an collection. - * - * @param {object} context vuex context - * @param {object} data destructuring object - * @param {string} data.collectionId the collection name - * @param {string[]} data.fileIdsToRemove list of files ids to remove - */ - async [`removeFilesFrom${capitalizedCollectionName}`](context, { collectionId, fileIdsToRemove }) { - const semaphore = new Semaphore(5) - - context.commit(`removeFilesFrom${capitalizedCollectionName}`, { collectionId, fileIdsToRemove }) - - const promises = fileIdsToRemove - .map(async (fileId) => { - const file = context.getters.files[fileId] - const symbol = await semaphore.acquire() - - try { - await client.deleteFile(file.filename) - } catch (error) { - context.commit(`addFilesTo${capitalizedCollectionName}`, { collectionId, fileIdsToAdd: [fileId] }) - - logger.error(translate('photos', 'Failed to delete {fileBaseName}.', { fileBaseName: file.basename }), { error }) - showError(translate('photos', 'Failed to delete {fileBaseName}.', { fileBaseName: file.basename })) - } finally { - semaphore.release(symbol) - } - }) - - return Promise.all(promises) - }, - - /** - * Delete a collection. - * - * @param {object} context vuex context - * @param {object} data destructuring object - * @param {string} data.collectionId the id of the collection - */ - async [`delete${capitalizedCollectionName}`](context, { collectionId }) { - try { - const collection = context.getters[`${collectionName}s`][collectionId] - await client.deleteFile(collection.filename) - context.commit(`remove${capitalizedCollectionName}s`, { collectionIds: [collectionId] }) - } catch (error) { - logger.error(translate('photos', 'Failed to delete {collectionId}.', { collectionId }), { error }) - showError(translate('photos', 'Failed to delete {collectionId}.', { collectionId })) - } - }, - } - - return { state, mutations, getters, actions } -} diff --git a/src/store/index.js b/src/store/index.js index 464728f76..f9bb78411 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -25,13 +25,13 @@ import Vuex, { Store } from 'vuex' import files from './files.js' import albums from './albums.js' +import publicAlbums from './publicAlbums.js' import sharedAlbums from './sharedAlbums.js' import collections from './collections.js' import places from './places.js' import faces from './faces.js' import folders from './folders.js' import systemtags from './systemtags.js' -import collectionStoreFactory from './collectionStoreFactory.js' Vue.use(Vuex) export default new Store({ @@ -40,9 +40,9 @@ export default new Store({ folders, albums, sharedAlbums, + publicAlbums, faces, systemtags, - publicAlbums: collectionStoreFactory('publicAlbum'), collections, places, }, diff --git a/src/store/publicAlbums.js b/src/store/publicAlbums.js new file mode 100644 index 000000000..c1fe14415 --- /dev/null +++ b/src/store/publicAlbums.js @@ -0,0 +1,41 @@ +/** + * @copyright Copyright (c) 2022 Louis Chemineau + * + * @author Louis Chemineau + * + * @license AGPL-3.0-or-later + * + * 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 . + * + */ + +/** + * @typedef {object} _PublicAlbum + * @property {string} originalName - The original name of the album. + * @property {string} location - The user set location of the album. + * + * @typedef {import("../services/collectionFetcher").Collection&_PublicAlbum} PublicAlbum + * + * @typedef {Object} IndexedPublicAlbums + */ + +const publicAlbumsPrefix = '/photospublic/' + +const getters = { + publicAlbums: (_, __, ___, rootGetters) => rootGetters.collectionsWithPrefix(publicAlbumsPrefix), + getPublicAlbum: (_, __, rootState) => publicAlbumName => rootState.collections.collections[`${publicAlbumsPrefix}${publicAlbumName}`] || null, + getPublicAlbumFiles: (_, __, rootState) => publicAlbumName => rootState.collections.collectionsFiles[`${publicAlbumsPrefix}${publicAlbumName}`] || [], + getPublicAlbumName: (_, __, ___) => publicAlbumName => `${publicAlbumsPrefix}${publicAlbumName}`, +} +export default { getters } diff --git a/src/views/PublicAlbumContent.vue b/src/views/PublicAlbumContent.vue index ed8471cf5..94dca89bb 100644 --- a/src/views/PublicAlbumContent.vue +++ b/src/views/PublicAlbumContent.vue @@ -25,14 +25,14 @@ + :loading="loadingCollection || loadingCollectionFiles" + :error="errorFetchingCollection || errorFetchingCollectionFiles"> --> - + */ --> @@ -89,7 +89,7 @@