overleaf/services/web/frontend/js/features/history/utils/auto-select-file.ts

144 lines
3.7 KiB
TypeScript
Raw Normal View History

import type { Nullable } from '../../../../../types/utils'
import type { FileDiff } from '../services/types/file'
import type { FileOperation } from '../services/types/file-operation'
import type { LoadedUpdate, Version } from '../services/types/update'
import type { Selection } from '../services/types/selection'
import { fileFinalPathname, isFileEditable } from './file-diff'
type FileWithOps = {
pathname: FileDiff['pathname']
editable: boolean
operation: FileOperation
}
function getFilesWithOps(
files: FileDiff[],
toV: Version,
comparing: boolean,
updateForToV: LoadedUpdate | undefined
): FileWithOps[] {
if (toV && !comparing) {
const filesWithOps: FileWithOps[] = []
if (updateForToV) {
const filesByPathname = new Map<string, FileDiff>()
for (const file of files) {
const pathname = fileFinalPathname(file)
filesByPathname.set(pathname, file)
}
const isEditable = (pathname: string) => {
const fileDiff = filesByPathname.get(pathname)
if (!fileDiff) {
return false
}
return isFileEditable(fileDiff)
}
for (const pathname of updateForToV.pathnames) {
filesWithOps.push({
pathname,
editable: isEditable(pathname),
operation: 'edited',
})
}
for (const op of updateForToV.project_ops) {
let pathAndOp: Nullable<Pick<FileWithOps, 'pathname' | 'operation'>> =
null
if (op.add) {
pathAndOp = {
pathname: op.add.pathname,
operation: 'added',
}
} else if (op.remove) {
pathAndOp = {
pathname: op.remove.pathname,
operation: 'removed',
}
} else if (op.rename) {
pathAndOp = {
pathname: op.rename.newPathname,
operation: 'renamed',
}
}
if (pathAndOp !== null) {
filesWithOps.push({
editable: isEditable(pathAndOp.pathname),
...pathAndOp,
})
}
}
}
return filesWithOps
} else {
const filesWithOps = files.reduce(
(curFilesWithOps, file) => {
if ('operation' in file) {
curFilesWithOps.push({
pathname: file.pathname,
editable: isFileEditable(file),
operation: file.operation,
})
}
return curFilesWithOps
},
<FileWithOps[]>[]
)
return filesWithOps
}
}
const orderedOpTypes: FileOperation[] = [
'edited',
'added',
'renamed',
'removed',
]
export function autoSelectFile(
files: FileDiff[],
toV: Version,
comparing: boolean,
updateForToV: LoadedUpdate | undefined,
previouslySelectedPathname: Selection['previouslySelectedPathname']
): FileDiff {
const filesWithOps = getFilesWithOps(files, toV, comparing, updateForToV)
const previouslySelectedFile = files.find(file => {
return file.pathname === previouslySelectedPathname
})
const previouslySelectedFileHasOp = filesWithOps.some(file => {
return file.pathname === previouslySelectedPathname
})
if (previouslySelectedFile && previouslySelectedFileHasOp) {
return previouslySelectedFile
}
for (const opType of orderedOpTypes) {
const fileWithMatchingOpType = filesWithOps.find(
file => file.operation === opType && file.editable
)
if (fileWithMatchingOpType) {
const fileToSelect = files.find(
file => fileFinalPathname(file) === fileWithMatchingOpType.pathname
)
if (fileToSelect) {
return fileToSelect
}
}
}
return (
previouslySelectedFile ||
files.find(file => /main\.tex$/.test(file.pathname)) ||
files.find(file => /\.tex$/.test(file.pathname)) ||
files[0]
)
}