|
|
@@ -0,0 +1,114 @@
|
|
|
+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
|
|
|
+}
|
|
|
+
|
|
|
+type HistoryFileTree = {
|
|
|
+ docs?: Doc[]
|
|
|
+ folders: HistoryFileTree[]
|
|
|
+ name: string
|
|
|
+ // `id` and `fileRefs` are both required from react file tree.
|
|
|
+ // TODO: update react file tree to make the data optional so we can delete these keys
|
|
|
+ id: ''
|
|
|
+ fileRefs: []
|
|
|
+}
|
|
|
+
|
|
|
+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: '',
|
|
|
+ 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: '',
|
|
|
+ fileRefs: [],
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 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'
|
|
|
+}
|