From d6369c35b552fa5ae13772cde246f80e56857db0 Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Thu, 22 Aug 2024 21:49:06 +0200 Subject: [PATCH 1/2] fix(files): Ensure renaming state is correctly reset Problem: Is a node is renamed and the new name is out of the current visible list of nodes the component will be recycled, this means the props will change, so when the `onRename` functions is about to reset the state the `this.source` will point to a different node. To fix this, but also to separate business logic from visual representation, the logic is moved into the renaming store and the component is only responsible for rendering. Signed-off-by: Ferdinand Thiessen --- .../components/FileEntry/FileEntryName.vue | 11 +-- apps/files/src/store/renaming.ts | 83 ++++++++++++++++++- 2 files changed, 83 insertions(+), 11 deletions(-) diff --git a/apps/files/src/components/FileEntry/FileEntryName.vue b/apps/files/src/components/FileEntry/FileEntryName.vue index abc3baeaf161e..31852a256c7c3 100644 --- a/apps/files/src/components/FileEntry/FileEntryName.vue +++ b/apps/files/src/components/FileEntry/FileEntryName.vue @@ -59,7 +59,6 @@ import type { PropType } from 'vue' import axios from '@nextcloud/axios' import { showError, showSuccess } from '@nextcloud/dialogs' -import { emit } from '@nextcloud/event-bus' import { FileType, NodeStatus } from '@nextcloud/files' import { translate as t } from '@nextcloud/l10n' import { defineComponent, inject } from 'vue' @@ -260,11 +259,6 @@ export default defineComponent({ } const oldName = this.source.basename - const oldEncodedSource = this.source.encodedSource - if (oldName === newName) { - this.stopRenaming() - return - } // Set loading state this.loading = 'renaming' @@ -296,9 +290,8 @@ export default defineComponent({ nameContainter?.focus() }) } catch (error) { - logger.error('Error while renaming file', { error }) - // Rename back as it failed - this.source.rename(oldName) + logger.error(error as Error) + showError((error as Error).message) // And ensure we reset to the renaming state this.startRenaming() diff --git a/apps/files/src/store/renaming.ts b/apps/files/src/store/renaming.ts index 6ba024f18b090..27e305c7b3e35 100644 --- a/apps/files/src/store/renaming.ts +++ b/apps/files/src/store/renaming.ts @@ -19,17 +19,96 @@ * along with this program. If not, see . * */ -import { defineStore } from 'pinia' -import { subscribe } from '@nextcloud/event-bus' import type { Node } from '@nextcloud/files' import type { RenamingStore } from '../types' +import axios, { isAxiosError } from '@nextcloud/axios' +import { emit, subscribe } from '@nextcloud/event-bus' +import { NodeStatus } from '@nextcloud/files' +import { t } from '@nextcloud/l10n' +import { basename, dirname } from 'path' +import { defineStore } from 'pinia' +import logger from '../logger' +import Vue from 'vue' + export const useRenamingStore = function(...args) { const store = defineStore('renaming', { state: () => ({ renamingNode: undefined, newName: '', } as RenamingStore), + + actions: { + /** + * Execute the renaming. + * This will rename the node set as `renamingNode` to the configured new name `newName`. + * @return true if success, false if skipped (e.g. new and old name are the same) + * @throws Error if renaming fails, details are set in the error message + */ + async rename(): Promise { + if (this.renamingNode === undefined) { + throw new Error('No node is currently being renamed') + } + + const newName = this.newName.trim?.() || '' + const oldName = this.renamingNode.basename + const oldEncodedSource = this.renamingNode.encodedSource + if (oldName === newName) { + return false + } + + const node = this.renamingNode + Vue.set(node, 'status', NodeStatus.LOADING) + + try { + // rename the node + this.renamingNode.rename(newName) + logger.debug('Moving file to', { destination: this.renamingNode.encodedSource, oldEncodedSource }) + // create MOVE request + await axios({ + method: 'MOVE', + url: oldEncodedSource, + headers: { + Destination: this.renamingNode.encodedSource, + Overwrite: 'F', + }, + }) + + // Success 🎉 + emit('files:node:updated', this.renamingNode as Node) + emit('files:node:renamed', this.renamingNode as Node) + emit('files:node:moved', { + node: this.renamingNode as Node, + oldSource: `${dirname(this.renamingNode.source)}/${oldName}`, + }) + this.$reset() + return true + } catch (error) { + logger.error('Error while renaming file', { error }) + // Rename back as it failed + this.renamingNode.rename(oldName) + if (isAxiosError(error)) { + // TODO: 409 means current folder does not exist, redirect ? + if (error?.response?.status === 404) { + throw new Error(t('files', 'Could not rename "{oldName}", it does not exist any more', { oldName })) + } else if (error?.response?.status === 412) { + throw new Error(t( + 'files', + 'The name "{newName}" is already used in the folder "{dir}". Please choose a different name.', + { + newName, + dir: basename(this.renamingNode.dirname), + }, + )) + } + } + // Unknown error + throw new Error(t('files', 'Could not rename "{oldName}"', { oldName })) + } finally { + Vue.set(node, 'status', undefined) + } + }, + }, }) const renamingStore = store(...args) From ab0f13d3875535d8346cc30217da8903c42e4b15 Mon Sep 17 00:00:00 2001 From: nextcloud-command Date: Tue, 15 Oct 2024 16:30:25 +0000 Subject: [PATCH 2/2] chore(assets): Recompile assets Signed-off-by: nextcloud-command