import { v4 } from 'uuid' ; import { ThunkDispatch } from 'redux-thunk' ; import { AnyAction , Store } from 'redux' ; import { toast } from 'react-toastify' ; import { DriveMetadata } from './googleDriveUtils' ; import * as constants from './constants' ; import { addFilesAction , removeFileAction , replaceFileAction } from '../redux/fileIndexReducer' ; import { getAllFilesFromStore , getUploadPlaceholdersFromStore , ReduxStoreType } from '../redux/mainReducer' ; import { addUploadPlaceholderAction , incrementUploadProgressAction , incrementUploadTargetProgressAction , removeUploadPlaceholderAction , setUploadProgressAction , UploadPlaceholderType } from '../redux/uploadPlaceholderReducer' ; import { FileAPI , OnProgressParams } from './fileUtils' ; export type UploadType = { name : string ; files : File [ ] ; metadataId ? : string ; subdirectories ? : UploadType [ ] ; } ; function getAncestorMetadata ( metadata , store , rootId , result = [ ] ) { const uploadPlaceholders = getUploadPlaceholdersFromStore ( store . getState ( ) ) ; const { driveMetadata } = getAllFilesFromStore ( store . getState ( ) ) ; for ( let parentId of metadata . parents ) { if ( parentId !== rootId ) { const parentMetadata = uploadPlaceholders . entities [ parentId ] ?. metadata || driveMetadata [ parentId ] ; if ( parentMetadata ) { result . push ( parentMetadata ) ; getAncestorMetadata ( parentMetadata , store , rootId , result ) ; } } } return result ; } export function createUploadPlaceholder ( store , rootFolder , name , parents , file ? , directoryDepth = 0 , upload = false ) { const metadata = { id : v4 ( ) , name , parents , trashed : false , appProperties : undefined , properties : undefined , mimeType : directoryDepth ? constants . MIME_TYPE_DRIVE_FOLDER : '' } ; store . dispatch ( addUploadPlaceholderAction ( metadata , rootFolder , file , directoryDepth , upload ) ) ; store . dispatch ( addFilesAction ( [ metadata ] ) ) ; if ( upload ) { const rootId = getAllFilesFromStore ( store . getState ( ) ) . roots [ rootFolder ] ; const ancestorMetadata = getAncestorMetadata ( metadata , store , rootId ) ; if ( ancestorMetadata . length > 0 ) { store . dispatch ( incrementUploadTargetProgressAction ( ancestorMetadata , rootFolder ) ) ; } } return metadata ; } export function replaceUploadPlaceholder ( store , rootFolder , oldMetadata , newMetadata ) { if ( newMetadata ) { store . dispatch ( replaceFileAction ( oldMetadata , newMetadata , rootFolder ) ) ; } else { store . dispatch ( removeUploadPlaceholderAction ( oldMetadata . id ) ) ; store . dispatch ( removeFileAction ( oldMetadata ) ) ; } const rootId = getAllFilesFromStore ( store . getState ( ) ) . roots [ rootFolder ] ; const ancestorMetadata = getAncestorMetadata ( oldMetadata , store , rootId ) ; if ( ancestorMetadata . length > 0 ) { store . dispatch ( incrementUploadProgressAction ( ancestorMetadata ) ) ; } } export async function createMultipleUploadPlaceholders ( store , rootFolder , fileAPI , upload , parents , depth = 1 , parentExists = true ) { let siblingMetadata = [ ] ; if ( parentExists ) { const parentMetadataId = parents [ 0 ] ; const files = getAllFilesFromStore ( store . getState ( ) ) ; if ( files . children [ parentMetadataId ] ) { siblingMetadata = files . children [ parentMetadataId ] . map ( ( childId ) => ( files . driveMetadata [ childId ] ) ) ; } else { await fileAPI . loadFilesInFolder ( parentMetadataId , ( files ) => { store . dispatch ( addFilesAction ( files ) ) ; siblingMetadata . push ( ... files ) ; } ) ; } } for ( let file of upload . files ) { const match = siblingMetadata . find ( ( sibling ) => ( sibling ?. name === file . name ) ) ; if ( match ) { toast ( `Skipping existing file "${ match . name }".` ) ; } else { createUploadPlaceholder ( store , rootFolder , file . name , parents , file , 0 , true ) ; } } if ( upload . subdirectories ) { for ( let subdir of upload . subdirectories ) { let subdirExists ; const match = siblingMetadata . find ( ( sibling ) => ( sibling ?. name === subdir . name ) ) ; if ( match ) { subdir . metadataId = match . id ; subdirExists = true ; } else { const metadata = createUploadPlaceholder ( store , rootFolder , subdir . name , parents , undefined , depth , true ) ; subdir . metadataId = metadata . id ; subdirExists = false ; } await createMultipleUploadPlaceholders ( store , rootFolder , fileAPI , subdir , [ subdir . metadataId ] , depth + 1 , subdirExists ) ; } } } async function uploadFileFromPlaceholder ( fileAPI , dispatch , placeholder ) { const { file , metadata } = placeholder ; if ( ! file ) { return null ; } const { parents , id } = metadata ; try { const driveMetadata = await fileAPI . uploadFile ( { name : file . name , parents } , file , ( progress ) => { dispatch ( setUploadProgressAction ( id , progress . loaded , progress . total ) ) ; } ) ; await fileAPI . makeFileReadableToAll ( driveMetadata ) ; return driveMetadata ; } catch ( e ) { const message = `Failed to upload file ${ file . name }` ; toast ( message ) ; console . error ( message , e ) ; return null ; } } export async function uploadFromPlaceholder ( store , fileAPI , placeholder , continueUpload ) { let metadata = null ; if ( continueUpload && ! placeholder . deleted ) { if ( placeholder . file ) { metadata = await uploadFileFromPlaceholder ( fileAPI , store . dispatch , placeholder ) ; } else if ( placeholder . directoryDepth ) { metadata = await fileAPI . createFolder ( placeholder . metadata . name , { parents : placeholder . metadata . parents } ) ; } } await replaceUploadPlaceholder ( store , placeholder . rootFolder , placeholder . metadata , metadata ) ; }