mirror of
https://github.com/nextcloud/server.git
synced 2026-06-07 15:53:04 -04:00
Merge pull request #46971 from nextcloud/backport/46963/stable28
[stable28] fix(files): cancel move-copy action should not be handled as an error
This commit is contained in:
commit
a9296ab5c2
5 changed files with 42 additions and 23 deletions
|
|
@ -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)
|
||||
|
|
|
|||
4
dist/files-init.js
vendored
4
dist/files-init.js
vendored
File diff suppressed because one or more lines are too long
2
dist/files-init.js.map
vendored
2
dist/files-init.js.map
vendored
File diff suppressed because one or more lines are too long
4
dist/files-main.js
vendored
4
dist/files-main.js
vendored
File diff suppressed because one or more lines are too long
2
dist/files-main.js.map
vendored
2
dist/files-main.js.map
vendored
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue