33 * SPDX-License-Identifier: AGPL-3.0-or-later
44 */
55import type { AxiosResponse } from '@nextcloud/axios'
6- import type { Folder , View } from '@nextcloud/files '
6+ import type { OCSResponse } from '@nextcloud/typings/ocs '
77
8+ // eslint-disable-next-line n/no-extraneous-import
9+ import { AxiosError } from 'axios'
810import { emit } from '@nextcloud/event-bus'
911import { generateOcsUrl } from '@nextcloud/router'
1012import { showError , showLoading , showSuccess } from '@nextcloud/dialogs'
1113import { t } from '@nextcloud/l10n'
1214import axios from '@nextcloud/axios'
1315import PQueue from 'p-queue'
1416
17+ import { fetchNode } from '../services/WebdavClient.ts'
1518import logger from '../logger'
16- import { useFilesStore } from '../store/files'
17- import { getPinia } from '../store'
18- import { usePathsStore } from '../store/paths'
1919
20- const queue = new PQueue ( { concurrency : 5 } )
20+ type ConversionResponse = {
21+ path : string
22+ fileId : number
23+ }
24+
25+ interface PromiseRejectedResult < T > {
26+ status : 'rejected'
27+ reason : T
28+ }
29+
30+ type PromiseSettledResult < T , E > = PromiseFulfilledResult < T > | PromiseRejectedResult < E > ;
31+ type ConversionSuccess = AxiosResponse < OCSResponse < ConversionResponse > >
32+ type ConversionError = AxiosError < OCSResponse < ConversionResponse > >
2133
34+ const queue = new PQueue ( { concurrency : 5 } )
2235const requestConversion = function ( fileId : number , targetMimeType : string ) : Promise < AxiosResponse > {
2336 return axios . post ( generateOcsUrl ( '/apps/files/api/v1/convert' ) , {
2437 fileId,
2538 targetMimeType,
2639 } )
2740}
2841
29- export const convertFiles = async function ( fileIds : number [ ] , targetMimeType : string , parentFolder : Folder | null ) {
42+ export const convertFiles = async function ( fileIds : number [ ] , targetMimeType : string ) {
3043 const conversions = fileIds . map ( fileId => queue . add ( ( ) => requestConversion ( fileId , targetMimeType ) ) )
3144
3245 // Start conversion
3346 const toast = showLoading ( t ( 'files' , 'Converting files…' ) )
3447
3548 // Handle results
3649 try {
37- const results = await Promise . allSettled ( conversions )
38- const failed = results . filter ( result => result . status === 'rejected' )
50+ const results = await Promise . allSettled ( conversions ) as PromiseSettledResult < ConversionSuccess , ConversionError > [ ]
51+ const failed = results . filter ( result => result . status === 'rejected' ) as PromiseRejectedResult < ConversionError > [ ]
3952 if ( failed . length > 0 ) {
40- const messages = failed . map ( result => result . reason ?. response ?. data ?. ocs ?. meta ?. message ) as string [ ]
53+ const messages = failed . map ( result => result . reason ?. response ?. data ?. ocs ?. meta ?. message )
4154 logger . error ( 'Failed to convert files' , { fileIds, targetMimeType, messages } )
4255
4356 // If all failed files have the same error message, show it
44- if ( new Set ( messages ) . size === 1 ) {
57+ if ( new Set ( messages ) . size === 1 && typeof messages [ 0 ] === 'string' ) {
4558 showError ( t ( 'files' , 'Failed to convert files: {message}' , { message : messages [ 0 ] } ) )
4659 return
4760 }
@@ -74,15 +87,27 @@ export const convertFiles = async function(fileIds: number[], targetMimeType: st
7487 // All files converted
7588 showSuccess ( t ( 'files' , 'Files successfully converted' ) )
7689
77- // Trigger a reload of the file list
78- if ( parentFolder ) {
79- emit ( 'files:node:updated' , parentFolder )
80- }
90+ // Extract files that are within the current directory
91+ // in batch mode, you might have files from different directories
92+ // ⚠️, let's get the actual current dir, as the one from the action
93+ // might have changed as the user navigated away
94+ const currentDir = window . OCP . Files . Router . query . dir as string
95+ const newPaths = results
96+ . filter ( result => result . status === 'fulfilled' )
97+ . map ( result => result . value . data . ocs . data . path )
98+ . filter ( path => path . startsWith ( currentDir ) )
99+
100+ // Fetch the new files
101+ logger . debug ( 'Files to fetch' , { newPaths } )
102+ const newFiles = await Promise . all ( newPaths . map ( path => fetchNode ( path ) ) )
103+
104+ // Inform the file list about the new files
105+ newFiles . forEach ( file => emit ( 'files:node:created' , file ) )
81106
82107 // Switch to the new files
83- const firstSuccess = results [ 0 ] as PromiseFulfilledResult < AxiosResponse >
108+ const firstSuccess = results [ 0 ] as PromiseFulfilledResult < ConversionSuccess >
84109 const newFileId = firstSuccess . value . data . ocs . data . fileId
85- window . OCP . Files . Router . goToRoute ( null , { ...window . OCP . Files . Router . params , fileid : newFileId } , window . OCP . Files . Router . query )
110+ window . OCP . Files . Router . goToRoute ( null , { ...window . OCP . Files . Router . params , fileid : newFileId . toString ( ) } , window . OCP . Files . Router . query )
86111 } catch ( error ) {
87112 // Should not happen as we use allSettled and handle errors above
88113 showError ( t ( 'files' , 'Failed to convert files' ) )
@@ -93,24 +118,23 @@ export const convertFiles = async function(fileIds: number[], targetMimeType: st
93118 }
94119}
95120
96- export const convertFile = async function ( fileId : number , targetMimeType : string , parentFolder : Folder | null ) {
121+ export const convertFile = async function ( fileId : number , targetMimeType : string ) {
97122 const toast = showLoading ( t ( 'files' , 'Converting file…' ) )
98123
99124 try {
100- const result = await queue . add ( ( ) => requestConversion ( fileId , targetMimeType ) ) as AxiosResponse
125+ const result = await queue . add ( ( ) => requestConversion ( fileId , targetMimeType ) ) as AxiosResponse < OCSResponse < ConversionResponse > >
101126 showSuccess ( t ( 'files' , 'File successfully converted' ) )
102127
103- // Trigger a reload of the file list
104- if ( parentFolder ) {
105- emit ( 'files:node:updated' , parentFolder )
106- }
128+ // Inform the file list about the new file
129+ const newFile = await fetchNode ( result . data . ocs . data . path )
130+ emit ( 'files:node:created' , newFile )
107131
108132 // Switch to the new file
109133 const newFileId = result . data . ocs . data . fileId
110- window . OCP . Files . Router . goToRoute ( null , { ...window . OCP . Files . Router . params , fileid : newFileId } , window . OCP . Files . Router . query )
134+ window . OCP . Files . Router . goToRoute ( null , { ...window . OCP . Files . Router . params , fileid : newFileId . toString ( ) } , window . OCP . Files . Router . query )
111135 } catch ( error ) {
112136 // If the server returned an error message, show it
113- if ( error . response ?. data ?. ocs ?. meta ?. message ) {
137+ if ( error instanceof AxiosError && error ? .response ?. data ?. ocs ?. meta ?. message ) {
114138 showError ( t ( 'files' , 'Failed to convert file: {message}' , { message : error . response . data . ocs . meta . message } ) )
115139 return
116140 }
@@ -122,26 +146,3 @@ export const convertFile = async function(fileId: number, targetMimeType: string
122146 toast . hideToast ( )
123147 }
124148}
125-
126- /**
127- * Get the parent folder of a path
128- *
129- * TODO: replace by the parent node straight away when we
130- * update the Files actions api accordingly.
131- *
132- * @param view The current view
133- * @param path The path to the file
134- * @returns The parent folder
135- */
136- export const getParentFolder = function ( view : View , path : string ) : Folder | null {
137- const filesStore = useFilesStore ( getPinia ( ) )
138- const pathsStore = usePathsStore ( getPinia ( ) )
139-
140- const parentSource = pathsStore . getPath ( view . id , path )
141- if ( ! parentSource ) {
142- return null
143- }
144-
145- const parentFolder = filesStore . getNode ( parentSource ) as Folder | undefined
146- return parentFolder ?? null
147- }
0 commit comments