import { ActionReducerMapBuilder , createAsyncThunk } from '@reduxjs/toolkit' ; import { StorageState } from '../storage.model' ; import { RootState } from '../../..' ; import { uploadItemsThunk } from './uploadItemsThunk' ; import { deleteItemsThunk } from './deleteItemsThunk' ; import storageThunks from '.' ; import i18n from '../../../../i18n/services/i18n.service' ; import tasksService from '../../../../tasks/services/tasks.service' ; import errorService from '../../../../core/services/error.service' ; import { TaskStatus , TaskType , UploadFolderTask } from '../../../../tasks/types' ; import { DriveFolderData , DriveItemData } from '../../../../drive/types' ; import notificationsService , { ToastType } from '../../../../notifications/services/notifications.service' ; export interface IRoot { name : string ; folderId : number | null ; childrenFiles : File [ ] ; childrenFolders : IRoot [ ] ; fullPathEdited : string ; } interface UploadFolderThunkPayload { root : IRoot ; currentFolderId : number ; options ? : { withNotification ? : boolean ; onSuccess ? : ( ) => void ; } ; } export const uploadFolderThunk = createAsyncThunk < void , UploadFolderThunkPayload , { state : RootState ; } > ( 'storage/createFolderStructure' , async ( { root , currentFolderId , options } , { dispatch , requestId } ) => { options = Object . assign ( { withNotification : true } , options || { } ) ; let alreadyUploaded = 0 ; let rootFolderItem ; const levels = [ root ] ; const itemsUnderRoot = countItemsUnderRoot ( root ) ; const taskId = tasksService . create < UploadFolderTask > ( { action : TaskType . UploadFolder , folderName : root . name , showNotification : ! ! options . withNotification , cancellable : true , stop : async ( ) => { const relatedTasks = tasksService . getTasks ( { relatedTaskId : requestId } ) ; const promises = [ ] ; promises . push ( ... ( relatedTasks . map ( ( task ) => task . stop ?. ( ) ) . filter ( ( promise ) => promise !== undefined ) as Promise < void > [ ] ) ) ; if ( rootFolderItem ) { promises . push ( dispatch ( deleteItemsThunk ( [ rootFolderItem as DriveItemData ] ) ) . unwrap ( ) ) ; } await Promise . all ( promises ) ; } , } ) ; try { root . folderId = currentFolderId ; while ( levels . length > 0 ) { const level = levels . shift ( ) as IRoot ; const createdFolder = await dispatch ( storageThunks . createFolderThunk ( { parentFolderId : level . folderId as number , folderName : level . name , options : { relatedTaskId : taskId , showErrors : false } , } ) ) . unwrap ( ) ; rootFolderItem = createdFolder ; if ( level . childrenFiles ) { for ( const childrenFile of level . childrenFiles ) { await dispatch ( uploadItemsThunk ( { files : [ childrenFile ] , parentFolderId : createdFolder . id , options : { relatedTaskId : taskId , showNotifications : false , showErrors : false } , } ) ) . unwrap ( ) ; tasksService . updateTask ( { taskId : taskId , merge : { status : TaskStatus . InProcess , progress : ++ alreadyUploaded / itemsUnderRoot , } , } ) ; } } for ( const child of level . childrenFolders ) { child . folderId = createdFolder . id ; } levels . push ( ... level . childrenFolders ) ; } tasksService . updateTask ( { taskId : taskId , merge : { status : TaskStatus . Success , } , } ) ; options . onSuccess ?. ( ) ; } catch ( err ) { const castedError = errorService . castError ( err ) ; const updatedTask = tasksService . findTask ( taskId ) ; if ( updatedTask ?. status !== TaskStatus . Cancelled ) { tasksService . updateTask ( { taskId : taskId , merge : { status : TaskStatus . Error , } , } ) ; throw castedError ; } } } ) ; function countItemsUnderRoot ( root ) { let count = 0 ; const queueOfFolders = [ root ] ; while ( queueOfFolders . length ) { const folder = queueOfFolders . shift ( ) as IRoot ; count += folder . childrenFiles ?. length ?? 0 ; queueOfFolders . push ( ... folder . childrenFolders ) ; } return count ; } export const uploadFolderThunkExtraReducers = ( builder ) : void => { builder . addCase ( uploadFolderThunk . pending , ( ) => undefined ) . addCase ( uploadFolderThunk . fulfilled , ( ) => undefined ) . addCase ( uploadFolderThunk . rejected , ( state , action ) => { let errorMessage = i18n . get ( 'error.uploadingFolder' ) ; if ( action . error . message ?. includes ( 'already exists' ) ) { errorMessage = i18n . get ( 'error.folderAlreadyExists' ) ; } else { errorMessage = action . error . message || action . error + '' ; } notificationsService . show ( errorMessage , ToastType . Error ) ; } ) ; } ;