overleaf/services/web/frontend/js/features/history/utils/auto-select-file.ts
M Fahru 4dec157e08 Implement history react toolbar UI (#12530)
There are two different UI in this PR: `comparing` and `viewing` mode.

- For `comparing`, the user would be shown two separate date. It uses the `UpdateRange` object and this PR adds a timestamp to both `fromV` and `toV` of the object type.
- For `viewing`, the user would only be shown one date since `viewing` mode means viewing a specific update version.

Some other notable changes:

- Move `diff` state to `diff-view.tsx`, which contains `main.tsx` (main editor history view) and `toolbar.tsx` as its children
- refactor `autoSelectFile` by passing `updateRange.toV` directly
- refactor `updateIsSelected` by passing an object that contains `fromV` and `toV` instead of passing `update

There's also a cypress test for both the `viewing` mode and `comparing` mode in this PR.

GitOrigin-RevId: ba54f073f3479c55a39eb6b2932ea7faff78dddc
2023-04-21 08:03:39 +00:00

136 lines
3.2 KiB
TypeScript

import _ from 'lodash'
import type { Nullable } from '../../../../../types/utils'
import type { HistoryContextValue } from '../context/types/history-context-value'
import type { FileDiff } from '../services/types/file'
import type { DiffOperation } from '../services/types/diff-operation'
import type { LoadedUpdate, Version } from '../services/types/update'
function getUpdateForVersion(
version: Version,
updates: HistoryContextValue['updates']
): Nullable<LoadedUpdate> {
return updates.filter(update => update.toV === version)?.[0] ?? null
}
type FileWithOps = {
pathname: FileDiff['pathname']
operation: DiffOperation
}
function getFilesWithOps(
files: FileDiff[],
toV: Version,
comparing: boolean,
updates: HistoryContextValue['updates']
): FileWithOps[] {
if (toV && !comparing) {
const filesWithOps: FileWithOps[] = []
const currentUpdate = getUpdateForVersion(toV, updates)
if (currentUpdate !== null) {
for (const pathname of currentUpdate.pathnames) {
filesWithOps.push({
pathname,
operation: 'edited',
})
}
for (const op of currentUpdate.project_ops) {
let fileWithOps: Nullable<FileWithOps> = null
if (op.add) {
fileWithOps = {
pathname: op.add.pathname,
operation: 'added',
}
} else if (op.remove) {
fileWithOps = {
pathname: op.remove.pathname,
operation: 'removed',
}
} else if (op.rename) {
fileWithOps = {
pathname: op.rename.newPathname,
operation: 'renamed',
}
}
if (fileWithOps !== null) {
filesWithOps.push(fileWithOps)
}
}
}
return filesWithOps
} else {
const filesWithOps = _.reduce(
files,
(curFilesWithOps, file) => {
if ('operation' in file) {
curFilesWithOps.push({
pathname: file.pathname,
operation: file.operation,
})
}
return curFilesWithOps
},
<FileWithOps[]>[]
)
return filesWithOps
}
}
const orderedOpTypes: DiffOperation[] = [
'edited',
'added',
'renamed',
'removed',
]
export function autoSelectFile(
files: FileDiff[],
toV: Version,
comparing: boolean,
updates: HistoryContextValue['updates']
) {
let fileToSelect: Nullable<FileDiff> = null
const filesWithOps = getFilesWithOps(files, toV, comparing, updates)
for (const opType of orderedOpTypes) {
const fileWithMatchingOpType = _.find(filesWithOps, {
operation: opType,
})
if (fileWithMatchingOpType != null) {
fileToSelect =
_.find(files, {
pathname: fileWithMatchingOpType.pathname,
}) ?? null
break
}
}
if (!fileToSelect) {
const mainFile = _.find(files, function (file) {
return /main\.tex$/.test(file.pathname)
})
if (mainFile) {
fileToSelect = mainFile
} else {
const anyTeXFile = _.find(files, function (file) {
return /\.tex$/.test(file.pathname)
})
if (anyTeXFile) {
fileToSelect = anyTeXFile
} else {
fileToSelect = files[0]
}
}
}
return fileToSelect.pathname
}