diff options
author | Ferdinand Thiessen <opensource@fthiessen.de> | 2024-08-02 23:40:10 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-08-02 23:40:10 +0200 |
commit | a9296ab5c25a8f34c48b407352a9760b9767ca9b (patch) | |
tree | 1cdf736878a76a7e1f5720aa54a563d40b1efd37 /apps | |
parent | 2c1883f152b883608e61e8f88f0eb1fd2283006b (diff) | |
parent | ba14e7b3f219507d3bbb740482f2f8908d7e6307 (diff) | |
download | nextcloud-server-a9296ab5c25a8f34c48b407352a9760b9767ca9b.tar.gz nextcloud-server-a9296ab5c25a8f34c48b407352a9760b9767ca9b.zip |
Merge pull request #46971 from nextcloud/backport/46963/stable28
[stable28] fix(files): cancel move-copy action should not be handled as an error
Diffstat (limited to 'apps')
-rw-r--r-- | apps/files/src/actions/moveOrCopyAction.ts | 53 |
1 files changed, 36 insertions, 17 deletions
diff --git a/apps/files/src/actions/moveOrCopyAction.ts b/apps/files/src/actions/moveOrCopyAction.ts index cd051916f32..74193fa0f41 100644 --- a/apps/files/src/actions/moveOrCopyAction.ts +++ b/apps/files/src/actions/moveOrCopyAction.ts @@ -25,14 +25,14 @@ import type { IFilePickerButton } from '@nextcloud/dialogs' import type { FileStat, ResponseDataDetailed } from 'webdav' import type { MoveCopyResult } from './moveOrCopyActionUtils' -// eslint-disable-next-line n/no-extraneous-import -import { AxiosError } from 'axios' -import { basename, join } from 'path' +import { FilePickerClosed, getFilePickerBuilder, showError, showInfo } from '@nextcloud/dialogs' import { emit } from '@nextcloud/event-bus' -import { FilePickerClosed, getFilePickerBuilder, showError } from '@nextcloud/dialogs' import { FileAction, FileType, NodeStatus, davGetClient, davRootPath, davResultToNode, davGetDefaultPropfind, getUniqueName } from '@nextcloud/files' import { translate as t } from '@nextcloud/l10n' import { openConflictPicker, hasConflict } from '@nextcloud/upload' +// eslint-disable-next-line n/no-extraneous-import +import { AxiosError } from 'axios' +import { basename, join } from 'path' import Vue from 'vue' import CopyIconSvg from '@mdi/svg/svg/folder-multiple.svg?raw' @@ -106,7 +106,7 @@ export const handleCopyMoveNodeTo = async (node: Node, destination: Folder, meth if (index === 1) { return t('files', '(copy)') // TRANSLATORS: Mark a file as a copy of another file } - return t('files', '(copy %n)', undefined, index) // TRANSLATORS: Meaning it is the n'th copy of a file + return t('files', '(copy %n)', undefined, index) // TRANSLATORS: Meaning it is the nth copy of a file } try { @@ -186,12 +186,17 @@ export const handleCopyMoveNodeTo = async (node: Node, destination: Folder, meth /** * Open a file picker for the given action - * @param {MoveCopyAction} action The action to open the file picker for - * @param {string} dir The directory to start the file picker in - * @param {Node[]} nodes The nodes to move/copy - * @return {Promise<MoveCopyResult>} The picked destination + * @param action The action to open the file picker for + * @param dir The directory to start the file picker in + * @param nodes The nodes to move/copy + * @return The picked destination or false if cancelled by user */ -const openFilePickerForAction = async (action: MoveCopyAction, dir = '/', nodes: Node[]): Promise<MoveCopyResult> => { +async function openFilePickerForAction( + action: MoveCopyAction, + dir = '/', + nodes: Node[], +): Promise<MoveCopyResult | false> { + const { resolve, reject, promise } = Promise.withResolvers<MoveCopyResult | false>() const fileIDs = nodes.map(node => node.fileid).filter(Boolean) const filePicker = getFilePickerBuilder(t('files', 'Choose destination')) .allowDirectories(true) @@ -202,9 +207,7 @@ const openFilePickerForAction = async (action: MoveCopyAction, dir = '/', nodes: .setMimeTypeFilter([]) .setMultiSelect(false) .startAt(dir) - - return new Promise((resolve, reject) => { - filePicker.setButtonFactory((selection: Node[], path: string) => { + .setButtonFactory((selection: Node[], path: string) => { const buttons: IFilePickerButton[] = [] const target = basename(path) @@ -252,17 +255,19 @@ const openFilePickerForAction = async (action: MoveCopyAction, dir = '/', nodes: return buttons }) + .build() - const picker = filePicker.build() - picker.pick().catch((error) => { + filePicker.pick() + .catch((error: Error) => { logger.debug(error as Error) if (error instanceof FilePickerClosed) { - reject(new Error(t('files', 'Cancelled move or copy operation'))) + resolve(false) } else { reject(new Error(t('files', 'Move or copy operation failed'))) } }) - }) + + return promise } export const action = new FileAction({ @@ -295,6 +300,11 @@ export const action = new FileAction({ logger.error(e as Error) return false } + if (result === false) { + showInfo(t('files', 'Cancelled move or copy of "{filename}".', { filename: node.displayname })) + return null + } + try { await handleCopyMoveNodeTo(node, result.destination, result.action) return true @@ -311,6 +321,15 @@ export const action = new FileAction({ async execBatch(nodes: Node[], view: View, dir: string) { const action = getActionForNodes(nodes) const result = await openFilePickerForAction(action, dir, nodes) + // Handle cancellation silently + if (result === false) { + showInfo(nodes.length === 1 + ? t('files', 'Cancelled move or copy of "{filename}".', { filename: nodes[0].displayname }) + : t('files', 'Cancelled move or copy operation'), + ) + return nodes.map(() => null) + } + const promises = nodes.map(async node => { try { await handleCopyMoveNodeTo(node, result.destination, result.action) |