mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-07 20:31:06 -05:00
9a55bbf325
This new history file tree is mostly copied from the editor file tree, with some of the features stripped away: 1. Remove multiple selections 2. Remove drag and drop ability 3. Remove the ability to rename files & folders 4. No more right-click hijacking (context menu) 5. No more triple dots menu on a file tree item shown 6. No file references, since history doesn't have the data to differentiate between real files and linked file 7. etc (some other small changes that are not too important to be listed) Other notable changes: 1. Simplify the selectable provider (the only context provider being copied from react file tree) 2. Convert to typescript GitOrigin-RevId: 1017e545b2bd99775e01307a9b7eac2daf454014
110 lines
3.5 KiB
TypeScript
110 lines
3.5 KiB
TypeScript
import _ from 'lodash'
|
|
import type { Doc } from '../../../../../types/doc'
|
|
import type { FileDiff, FileRenamed } from '../services/types/file'
|
|
|
|
// `Partial` because the `reducePathsToTree` function was copied directly
|
|
// from a javascript file without proper type system and the logic is not typescript-friendly.
|
|
// TODO: refactor the function to have a proper type system
|
|
type FileTreeEntity = Partial<{
|
|
name: string
|
|
type: 'file' | 'folder'
|
|
oldPathname: string
|
|
newPathname: string
|
|
pathname: string
|
|
children: FileTreeEntity[]
|
|
operation: 'edited' | 'added' | 'renamed' | 'removed'
|
|
}>
|
|
|
|
export function reducePathsToTree(
|
|
currentFileTree: FileTreeEntity[],
|
|
fileObject: FileTreeEntity
|
|
) {
|
|
const filePathParts = fileObject?.pathname?.split('/') ?? ''
|
|
let currentFileTreeLocation = currentFileTree
|
|
for (let index = 0; index < filePathParts.length; index++) {
|
|
let fileTreeEntity: FileTreeEntity | null = {}
|
|
const pathPart = filePathParts[index]
|
|
const isFile = index === filePathParts.length - 1
|
|
if (isFile) {
|
|
fileTreeEntity = _.clone(fileObject)
|
|
fileTreeEntity.name = pathPart
|
|
fileTreeEntity.type = 'file'
|
|
currentFileTreeLocation.push(fileTreeEntity)
|
|
} else {
|
|
fileTreeEntity =
|
|
_.find(currentFileTreeLocation, entity => entity.name === pathPart) ??
|
|
null
|
|
if (fileTreeEntity == null) {
|
|
fileTreeEntity = {
|
|
name: pathPart,
|
|
type: 'folder',
|
|
children: [],
|
|
}
|
|
currentFileTreeLocation.push(fileTreeEntity)
|
|
}
|
|
currentFileTreeLocation = fileTreeEntity.children ?? []
|
|
}
|
|
}
|
|
return currentFileTree
|
|
}
|
|
|
|
export type HistoryFileTree = {
|
|
docs?: Doc[]
|
|
folders: HistoryFileTree[]
|
|
name: string
|
|
_id: string
|
|
}
|
|
|
|
export function fileTreeDiffToFileTreeData(
|
|
fileTreeDiff: FileTreeEntity[],
|
|
currentFolderName = 'rootFolder' // default value from angular version
|
|
): HistoryFileTree {
|
|
const folders: HistoryFileTree[] = []
|
|
const docs: Doc[] = []
|
|
|
|
for (const file of fileTreeDiff) {
|
|
if (file.type === 'file') {
|
|
docs.push({
|
|
_id: file.pathname as string,
|
|
name: file.name ?? '',
|
|
})
|
|
} else if (file.type === 'folder') {
|
|
if (file.children) {
|
|
const folder = fileTreeDiffToFileTreeData(file.children, file.name)
|
|
folders.push(folder)
|
|
}
|
|
}
|
|
}
|
|
|
|
return {
|
|
docs,
|
|
folders,
|
|
name: currentFolderName,
|
|
_id: currentFolderName,
|
|
}
|
|
}
|
|
|
|
// TODO: refactor the oldPathname/newPathname data
|
|
// It's an artifact from the angular version.
|
|
// Our API returns `pathname` and `newPathname` for `renamed` operation
|
|
// In the angular version, we change the key of the data:
|
|
// 1. `pathname` -> `oldPathname`
|
|
// 2. `newPathname` -> `pathname`
|
|
// 3. Delete the `newPathname` key from the object
|
|
// This is because the angular version wants to generalize the API usage
|
|
// In the operation other than the `renamed` operation, the diff API (/project/:id/diff) consumes the `pathname`
|
|
// But the `renamed` operation consumes the `newPathname` instead of the `pathname` data
|
|
//
|
|
// This behaviour can be refactored by introducing a conditional when calling the API
|
|
// i.e if `renamed` -> use `newPathname`, else -> use `pathname`
|
|
export function renamePathnameKey(file: FileRenamed): FileRenamed {
|
|
return {
|
|
oldPathname: file.pathname,
|
|
pathname: file.newPathname as string,
|
|
operation: file.operation,
|
|
}
|
|
}
|
|
|
|
export function isFileRenamed(fileDiff: FileDiff): fileDiff is FileRenamed {
|
|
return (fileDiff as FileRenamed).operation === 'renamed'
|
|
}
|