import '@nextcloud/dialogs/style.css'
import type { Folder, Node, View } from '@nextcloud/files'
import type { IFilePickerButton } from '@nextcloud/dialogs'
+import type { MoveCopyResult } from './moveOrCopyActionUtils'
// eslint-disable-next-line n/no-extraneous-import
import { AxiosError } from 'axios'
const relativePath = join(destination.path, node.basename)
const destinationUrl = generateRemoteUrl(`dav/files/${getCurrentUser()?.uid}${relativePath}`)
- logger.debug(`${method} ${node.basename} to ${destinationUrl}`)
// Set loading state
Vue.set(node, 'status', NodeStatus.LOADING)
* 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} node The node to move/copy
- * @return {Promise<boolean>} A promise that resolves to true if the action was successful
+ * @param {Node[]} nodes The nodes to move/copy
+ * @return {Promise<MoveCopyResult>} The picked destination
*/
-const openFilePickerForAction = async (action: MoveCopyAction, dir = '/', node: Node): Promise<boolean> => {
+const openFilePickerForAction = async (action: MoveCopyAction, dir = '/', nodes: Node[]): Promise<MoveCopyResult> => {
+ const fileIDs = nodes.map(node => node.fileid).filter(Boolean)
const filePicker = getFilePickerBuilder(t('files', 'Chose destination'))
.allowDirectories(true)
.setFilter((n: Node) => {
// We only want to show folders that we can create nodes in
return (n.permissions & Permission.CREATE) !== 0
- // We don't want to show the current node in the file picker
- && node.fileid !== n.fileid
+ // We don't want to show the current nodes in the file picker
+ && !fileIDs.includes(n.fileid)
})
.setMimeTypeFilter([])
.setMultiSelect(false)
.startAt(dir)
return new Promise((resolve, reject) => {
- filePicker.setButtonFactory((nodes: Node[], path: string) => {
+ filePicker.setButtonFactory((_selection, path: string) => {
const buttons: IFilePickerButton[] = []
const target = basename(path)
- if (node.dirname === path) {
+ const dirnames = nodes.map(node => node.dirname)
+ const paths = nodes.map(node => node.path)
+
+ if (dirnames.includes(path)) {
// This file/folder is already in that directory
return buttons
}
- if (node.path === path) {
+ if (paths.includes(path)) {
// You cannot move a file/folder onto itself
return buttons
}
type: 'primary',
icon: CopyIconSvg,
async callback(destination: Node[]) {
- try {
- await handleCopyMoveNodeTo(node, destination[0], MoveCopyAction.COPY)
- resolve(true)
- } catch (error) {
- reject(error)
- }
+ resolve({
+ destination: destination[0] as Folder,
+ action: MoveCopyAction.COPY,
+ } as MoveCopyResult)
},
})
}
type: action === MoveCopyAction.MOVE ? 'primary' : 'secondary',
icon: FolderMoveSvg,
async callback(destination: Node[]) {
- try {
- await handleCopyMoveNodeTo(node, destination[0], MoveCopyAction.MOVE)
- resolve(true)
- } catch (error) {
- console.warn('got error', error)
- reject(error)
- }
+ resolve({
+ destination: destination[0] as Folder,
+ action: MoveCopyAction.MOVE,
+ } as MoveCopyResult)
},
})
}
async exec(node: Node, view: View, dir: string) {
const action = getActionForNodes([node])
+ const result = await openFilePickerForAction(action, dir, [node])
try {
- await openFilePickerForAction(action, dir, node)
+ await handleCopyMoveNodeTo(node, result.destination, result.action)
return true
} catch (error) {
if (error instanceof Error && !!error.message) {
}
},
+ async execBatch(nodes: Node[], view: View, dir: string) {
+ const action = getActionForNodes(nodes)
+ const result = await openFilePickerForAction(action, dir, nodes)
+ const promises = nodes.map(async node => {
+ try {
+ await handleCopyMoveNodeTo(node, result.destination, result.action)
+ return true
+ } catch (error) {
+ logger.error(`Failed to ${result.action} node`, { node, error })
+ return false
+ }
+ })
+
+ // We need to keep the selection on error!
+ // So we do not return null, and for batch action
+ // we let the front handle the error.
+ return await Promise.all(promises)
+ },
+
order: 15,
})