import { createEntityAdapter , createSlice , PayloadAction } from '@reduxjs/toolkit' ; import { DriveMetadata } from '../util/googleDriveUtils' ; import { FileIndexActionTypes , RemoveFileActionType , ReplaceFileAction } from './fileIndexReducer' ; /**
 * It's not recommended to store non-serializable objects like File in the redux store, but these are short-lived
 * objects which are just used to maintain the state of an ongoing multi-file upload while the user does other things.
 */ export type UploadPlaceholderType = { metadata : DriveMetadata ; rootFolder : string ; file ? : File ; directoryDepth : number ; progress : number ; targetProgress : number ; upload : boolean ; deleted ? : boolean ; } ; const uploadPlaceholderAdaptor = createEntityAdapter < UploadPlaceholderType > ( { selectId : ( entity ) => ( entity . metadata . id ) } ) ; const initialState = uploadPlaceholderAdaptor . getInitialState ( { uploading : false , singleMetadata : undefined as null | undefined | DriveMetadata } ) ; const uploadPlaceholderSlice = createSlice ( { name : 'uploadPlaceholder' , initialState , reducers : { addUploadPlaceholderAction : { prepare : ( metadata , rootFolder , file , directoryDepth , upload = true ) => ( { payload : { metadata , rootFolder , file , directoryDepth , upload , progress : 0 , targetProgress : 0 } } ) , reducer : ( state , action ) => { uploadPlaceholderAdaptor . addOne ( state , action . payload ) ; if ( action . payload . upload && ! state . uploading ) { state . uploading = true ; state . singleMetadata = undefined ; } } } , removeUploadPlaceholderAction : { prepare : ( id ) => ( { payload : id } ) , reducer : ( state , action ) => { uploadPlaceholderAdaptor . removeOne ( state , action . payload ) ; if ( state . ids . length === 0 ) { state . uploading = false ; } } } , setUploadProgressAction : { prepare : ( id , progress , targetProgress ) => ( { payload : { id , progress , targetProgress } } ) , reducer : ( state , action ) => { const { id , progress , targetProgress } = action . payload ; const entity = state . entities [ id ] ; if ( entity ) { entity . progress = progress ; entity . targetProgress = targetProgress ; } } } , incrementUploadProgressAction : ( state , action ) => { for ( let metadata of action . payload ) { const entity = state . entities [ metadata . id ] ; if ( entity && ++ entity . progress >= entity . targetProgress && ! entity . upload ) { uploadPlaceholderAdaptor . removeOne ( state , metadata . id ) ; } if ( state . ids . length === 0 ) { state . uploading = false ; } } } , incrementUploadTargetProgressAction : { prepare : ( metadataList , rootFolder ) => ( { payload : { metadataList , rootFolder } } ) , reducer : ( state , action ) => { const { metadataList , rootFolder } = action . payload ; for ( let metadata of metadataList ) { if ( state . entities [ metadata . id ] ) { state . entities [ metadata . id ] ! . targetProgress ++ ; } else { uploadPlaceholderAdaptor . addOne ( state , { metadata , rootFolder , directoryDepth : 1 , upload : false , progress : 0 , targetProgress : 1 } ) ; } } } } , cancelUploadPlaceholderUploadingAction : ( state ) => { state . uploading = false ; state . singleMetadata = null ; state . ids . sort ( ( id1 , id2 ) => { const p1 = state . entities [ id1 ] ! ; const p2 = state . entities [ id2 ] ! ; const diff = p2 . directoryDepth - p1 . directoryDepth ; return ( p1 . directoryDepth === 0 || p2 . directoryDepth === 0 ) ? - diff : diff ; } ) ; } , clearSingleMetadata : ( state ) => { state . singleMetadata = undefined ; } , clearUploadingPlaceholderDataAction : ( state ) => { state . singleMetadata = undefined ; state . uploading = false ; uploadPlaceholderAdaptor . removeAll ( state ) ; } } , extraReducers : ( builder ) => { builder . addCase ( FileIndexActionTypes . REPLACE_FILE_ACTION , ( state , action ) => { const oldId = action . metadata . id ; const newId = action . newMetadata . id ; const placeholder = state . entities [ oldId ] ; uploadPlaceholderAdaptor . removeOne ( state , oldId ) ; if ( placeholder && placeholder . progress < placeholder . targetProgress ) { const newPlaceholder = { ... placeholder , upload : false , metadata : { ... placeholder . metadata , id : newId } } ; uploadPlaceholderAdaptor . addOne ( state , newPlaceholder ) ; } if ( state . ids . length === 0 ) { state . uploading = false ; } else { for ( let id of state . ids ) { const placeholder = state . entities [ id ] ; if ( placeholder && placeholder . metadata . parents . indexOf ( oldId ) >= 0 ) { placeholder . metadata . parents = placeholder . metadata . parents . map ( ( id ) => ( id === oldId ? newId : id ) ) ; } } } if ( placeholder ?. file && ! placeholder . deleted ) { if ( state . singleMetadata ) { state . singleMetadata = null ; } else if ( state . singleMetadata === undefined ) { state . singleMetadata = action . newMetadata ; } } } ) . addCase ( FileIndexActionTypes . REMOVE_FILE_ACTION , ( state , action ) => { const placeholder = state . entities [ action . file . id ] ; if ( placeholder ) { placeholder . deleted = true ; if ( placeholder . directoryDepth ) { markPlaceholderDescendentsDeleted ( state , action . file . id ) ; } state . ids . sort ( ( id1 , id2 ) => { const p1 = state . entities [ id1 ] ! ; const p2 = state . entities [ id2 ] ! ; if ( p1 . deleted && p2 . deleted ) { const diff = p2 . directoryDepth - p1 . directoryDepth ; return ( p1 . directoryDepth === 0 || p2 . directoryDepth === 0 ) ? - diff : diff ; } else if ( p1 . deleted ) { return 1 ; } else { return p2 . deleted ? - 1 : 0 ; } } ) ; } } ) ; } } ) ; function markPlaceholderDescendentsDeleted ( state , targetId ) { for ( let id of state . ids ) { const placeholder = state . entities [ id ] ; if ( placeholder && placeholder . metadata . parents . indexOf ( targetId ) >= 0 ) { placeholder . deleted = true ; if ( placeholder . directoryDepth ) { markPlaceholderDescendentsDeleted ( state , id as string ) ; } } } } export type UploadPlaceholderReducerType = ReturnType < typeof uploadPlaceholderSlice . reducer > ; export const { addUploadPlaceholderAction , cancelUploadPlaceholderUploadingAction , clearSingleMetadata , clearUploadingPlaceholderDataAction , incrementUploadProgressAction , incrementUploadTargetProgressAction , removeUploadPlaceholderAction , setUploadProgressAction } = uploadPlaceholderSlice . actions ; export default uploadPlaceholderSlice . reducer ;