mirror of
https://github.com/overleaf/overleaf.git
synced 2024-12-02 18:50:19 -05:00
ba4300d9e1
Migrate "Add Files" modal to React GitOrigin-RevId: fc5235108ee65294e3176da9c327791c34aa5b3c
215 lines
4.4 KiB
JavaScript
215 lines
4.4 KiB
JavaScript
import React, {
|
|
createContext,
|
|
useCallback,
|
|
useReducer,
|
|
useContext
|
|
} from 'react'
|
|
import PropTypes from 'prop-types'
|
|
|
|
import {
|
|
renameInTree,
|
|
deleteInTree,
|
|
moveInTree,
|
|
createEntityInTree
|
|
} from '../util/mutate-in-tree'
|
|
|
|
const FileTreeMutableContext = createContext()
|
|
|
|
const ACTION_TYPES = {
|
|
RENAME: 'RENAME',
|
|
DELETE: 'DELETE',
|
|
MOVE: 'MOVE',
|
|
CREATE_ENTITY: 'CREATE_ENTITY'
|
|
}
|
|
|
|
function fileTreeMutableReducer({ fileTreeData }, action) {
|
|
switch (action.type) {
|
|
case ACTION_TYPES.RENAME: {
|
|
const newFileTreeData = renameInTree(fileTreeData, action.id, {
|
|
newName: action.newName
|
|
})
|
|
|
|
return {
|
|
fileTreeData: newFileTreeData,
|
|
fileCount: countFiles(newFileTreeData)
|
|
}
|
|
}
|
|
|
|
case ACTION_TYPES.DELETE: {
|
|
const newFileTreeData = deleteInTree(fileTreeData, action.id)
|
|
|
|
return {
|
|
fileTreeData: newFileTreeData,
|
|
fileCount: countFiles(newFileTreeData)
|
|
}
|
|
}
|
|
|
|
case ACTION_TYPES.MOVE: {
|
|
const newFileTreeData = moveInTree(
|
|
fileTreeData,
|
|
action.entityId,
|
|
action.toFolderId
|
|
)
|
|
|
|
return {
|
|
fileTreeData: newFileTreeData,
|
|
fileCount: countFiles(newFileTreeData)
|
|
}
|
|
}
|
|
|
|
case ACTION_TYPES.CREATE_ENTITY: {
|
|
const newFileTreeData = createEntityInTree(
|
|
fileTreeData,
|
|
action.parentFolderId,
|
|
action.entity
|
|
)
|
|
|
|
return {
|
|
fileTreeData: newFileTreeData,
|
|
fileCount: countFiles(newFileTreeData)
|
|
}
|
|
}
|
|
|
|
default: {
|
|
throw new Error(`Unknown mutable file tree action type: ${action.type}`)
|
|
}
|
|
}
|
|
}
|
|
|
|
export const FileTreeMutableProvider = function({ rootFolder, children }) {
|
|
const [{ fileTreeData, fileCount }, dispatch] = useReducer(
|
|
fileTreeMutableReducer,
|
|
{
|
|
fileTreeData: rootFolder[0],
|
|
fileCount: countFiles(rootFolder[0])
|
|
}
|
|
)
|
|
|
|
return (
|
|
<FileTreeMutableContext.Provider
|
|
value={{ fileTreeData, fileCount, dispatch }}
|
|
>
|
|
{children}
|
|
</FileTreeMutableContext.Provider>
|
|
)
|
|
}
|
|
|
|
FileTreeMutableProvider.propTypes = {
|
|
rootFolder: PropTypes.array.isRequired,
|
|
children: PropTypes.oneOfType([
|
|
PropTypes.arrayOf(PropTypes.node),
|
|
PropTypes.node
|
|
]).isRequired
|
|
}
|
|
|
|
export function useFileTreeMutable() {
|
|
const { fileTreeData, fileCount, dispatch } = useContext(
|
|
FileTreeMutableContext
|
|
)
|
|
|
|
const dispatchCreateFolder = useCallback(
|
|
(parentFolderId, entity) => {
|
|
entity.type = 'folder'
|
|
dispatch({
|
|
type: ACTION_TYPES.CREATE_ENTITY,
|
|
parentFolderId,
|
|
entity
|
|
})
|
|
},
|
|
[dispatch]
|
|
)
|
|
|
|
const dispatchCreateDoc = useCallback(
|
|
(parentFolderId, entity) => {
|
|
entity.type = 'doc'
|
|
dispatch({
|
|
type: ACTION_TYPES.CREATE_ENTITY,
|
|
parentFolderId,
|
|
entity
|
|
})
|
|
},
|
|
[dispatch]
|
|
)
|
|
|
|
const dispatchCreateFile = useCallback(
|
|
(parentFolderId, entity) => {
|
|
entity.type = 'fileRef'
|
|
dispatch({
|
|
type: ACTION_TYPES.CREATE_ENTITY,
|
|
parentFolderId,
|
|
entity
|
|
})
|
|
},
|
|
[dispatch]
|
|
)
|
|
|
|
const dispatchRename = useCallback(
|
|
(id, newName) => {
|
|
dispatch({
|
|
type: ACTION_TYPES.RENAME,
|
|
newName,
|
|
id
|
|
})
|
|
},
|
|
[dispatch]
|
|
)
|
|
|
|
const dispatchDelete = useCallback(
|
|
id => {
|
|
dispatch({ type: ACTION_TYPES.DELETE, id })
|
|
},
|
|
[dispatch]
|
|
)
|
|
|
|
const dispatchMove = useCallback(
|
|
(entityId, toFolderId) => {
|
|
dispatch({ type: ACTION_TYPES.MOVE, entityId, toFolderId })
|
|
},
|
|
[dispatch]
|
|
)
|
|
|
|
return {
|
|
fileTreeData,
|
|
fileCount,
|
|
dispatchRename,
|
|
dispatchDelete,
|
|
dispatchMove,
|
|
dispatchCreateFolder,
|
|
dispatchCreateDoc,
|
|
dispatchCreateFile
|
|
}
|
|
}
|
|
|
|
function filesInFolder({ docs, folders, fileRefs }) {
|
|
const files = [...docs, ...fileRefs]
|
|
|
|
for (const folder of folders) {
|
|
files.push(...filesInFolder(folder))
|
|
}
|
|
|
|
return files
|
|
}
|
|
|
|
function countFiles(fileTreeData) {
|
|
const files = filesInFolder(fileTreeData)
|
|
|
|
// count all the non-deleted entities
|
|
const value = files.filter(item => !item.deleted).length
|
|
|
|
const limit = window.ExposedSettings.maxEntitiesPerProject
|
|
const status = fileCountStatus(value, limit, Math.ceil(limit / 20))
|
|
|
|
return { value, status, limit }
|
|
}
|
|
|
|
function fileCountStatus(value, limit, range) {
|
|
if (value >= limit) {
|
|
return 'error'
|
|
}
|
|
|
|
if (value >= limit - range) {
|
|
return 'warning'
|
|
}
|
|
|
|
return 'success'
|
|
}
|