Merge pull request #13104 from overleaf/td-history-compare-to-from

History migration: Add "compare to" and "compare from" options

GitOrigin-RevId: f550ad06b1e812011ecb32e6772354eb0abc2163
This commit is contained in:
Tim Down 2023-05-17 11:52:49 +01:00 committed by Copybot
parent 5d0ce8c4cd
commit c870bd5f53
12 changed files with 136 additions and 99 deletions

View file

@ -401,7 +401,9 @@
"history_add_label": "",
"history_adding_label": "",
"history_are_you_sure_delete_label": "",
"history_compare_with_this_version": "",
"history_compare_from_this_version": "",
"history_compare_to_selected_version": "",
"history_compare_to_this_version": "",
"history_delete_label": "",
"history_deleting_label": "",
"history_download_this_version": "",

View file

@ -99,6 +99,7 @@ function AllHistoryList() {
const faded =
updatesInfo.freeHistoryLimitHit &&
index === visibleUpdates.length - 1
const selectable = !faded && (selection.comparing || !selected)
return (
<HistoryVersion
@ -109,7 +110,7 @@ function AllHistoryList() {
setSelection={setSelection}
selected={selected}
currentUserId={currentUserId}
comparing={selection.comparing}
selectable={selectable}
projectId={projectId}
setActiveDropdownItem={setActiveDropdownItem}
closeDropdownForItem={closeDropdownForItem}

View file

@ -1,24 +1,22 @@
import Download from './menu-item/download'
import Compare from './menu-item/compare'
import { Version } from '../../../services/types/update'
import { ActiveDropdown } from '../../../hooks/use-dropdown-active-item'
import { useCallback } from 'react'
import CompareItems from './menu-item/compare-items'
type LabelDropdownContentProps = {
projectId: string
version: Version
updateMetaEndTimestamp: number
versionTimestamp: number
selected: boolean
comparing: boolean
closeDropdownForItem: ActiveDropdown['closeDropdownForItem']
}
function LabelDropdownContent({
projectId,
version,
updateMetaEndTimestamp,
versionTimestamp,
selected,
comparing,
closeDropdownForItem,
}: LabelDropdownContentProps) {
const closeDropdown = useCallback(() => {
@ -32,15 +30,16 @@ function LabelDropdownContent({
version={version}
closeDropdown={closeDropdown}
/>
{!comparing && !selected && (
<Compare
projectId={projectId}
fromV={version}
toV={version}
updateMetaEndTimestamp={updateMetaEndTimestamp}
<CompareItems
updateRange={{
fromV: version,
toV: version,
fromVTimestamp: versionTimestamp,
toVTimestamp: versionTimestamp,
}}
selected={selected}
closeDropdown={closeDropdown}
/>
)}
</>
)
}

View file

@ -0,0 +1,69 @@
import { useTranslation } from 'react-i18next'
import { useHistoryContext } from '../../../../context/history-context'
import { UpdateRange } from '../../../../services/types/update'
import Compare from './compare'
import { updateRangeUnion } from '../../../../utils/range'
type CompareItemsProps = {
updateRange: UpdateRange
selected: boolean
closeDropdown: () => void
}
function CompareItems({
updateRange,
selected,
closeDropdown,
}: CompareItemsProps) {
const { t } = useTranslation()
const { selection } = useHistoryContext()
const { updateRange: selRange, comparing } = selection
const notASelectionBoundary =
!!selRange &&
comparing &&
updateRange.toV !== selRange.toV &&
updateRange.fromV !== selRange.fromV
const showCompareWithSelected = !comparing && !!selRange && !selected
const showCompareToThis =
notASelectionBoundary && updateRange.toV > selRange.fromV
const showCompareFromThis =
notASelectionBoundary && updateRange.fromV < selRange.toV
return (
<>
{showCompareWithSelected ? (
<Compare
comparisonRange={updateRangeUnion(updateRange, selRange)}
closeDropdown={closeDropdown}
text={t('history_compare_to_selected_version')}
/>
) : null}
{showCompareToThis ? (
<Compare
comparisonRange={{
fromV: selRange.fromV,
toV: updateRange.toV,
fromVTimestamp: selRange.fromVTimestamp,
toVTimestamp: updateRange.toVTimestamp,
}}
closeDropdown={closeDropdown}
text={t('history_compare_to_this_version')}
/>
) : null}
{showCompareFromThis ? (
<Compare
comparisonRange={{
fromV: updateRange.fromV,
toV: selRange.toV,
fromVTimestamp: updateRange.fromVTimestamp,
toVTimestamp: selRange.toVTimestamp,
}}
closeDropdown={closeDropdown}
text={t('history_compare_from_this_version')}
/>
) : null}
</>
)
}
export default CompareItems

View file

@ -1,56 +1,36 @@
import { useTranslation } from 'react-i18next'
import { MenuItem, MenuItemProps } from 'react-bootstrap'
import Icon from '../../../../../../shared/components/icon'
import { useHistoryContext } from '../../../../context/history-context'
import { computeUpdateRange } from '../../../../utils/range'
import { UpdateRange } from '../../../../services/types/update'
type CompareProps = {
projectId: string
updateMetaEndTimestamp: number
comparisonRange: UpdateRange
text: string
closeDropdown: () => void
} & Pick<UpdateRange, 'fromV' | 'toV'>
}
function Compare({
projectId,
fromV,
toV,
updateMetaEndTimestamp,
comparisonRange,
text,
closeDropdown,
...props
}: CompareProps) {
const { t } = useTranslation()
const { setSelection } = useHistoryContext()
const handleCompareVersion = (e: React.MouseEvent<MenuItemProps>) => {
e.stopPropagation()
closeDropdown()
setSelection(prevSelection => {
const { updateRange } = prevSelection
if (updateRange) {
const range = computeUpdateRange(
updateRange,
fromV,
toV,
updateMetaEndTimestamp
)
return {
updateRange: range,
setSelection({
updateRange: comparisonRange,
comparing: true,
files: [],
}
}
return prevSelection
})
}
return (
<MenuItem onClick={handleCompareVersion} {...props}>
<Icon type="exchange" fw /> {t('history_compare_with_this_version')}
<Icon type="exchange" fw /> {text}
</MenuItem>
)
}

View file

@ -1,15 +1,15 @@
import AddLabel from './menu-item/add-label'
import Download from './menu-item/download'
import Compare from './menu-item/compare'
import { LoadedUpdate } from '../../../services/types/update'
import { useCallback } from 'react'
import { ActiveDropdown } from '../../../hooks/use-dropdown-active-item'
import CompareItems from './menu-item/compare-items'
import { updateRangeForUpdate } from '../../../utils/history-details'
type VersionDropdownContentProps = {
projectId: string
update: LoadedUpdate
selected: boolean
comparing: boolean
closeDropdownForItem: ActiveDropdown['closeDropdownForItem']
}
@ -17,9 +17,10 @@ function VersionDropdownContent({
projectId,
update,
selected,
comparing,
closeDropdownForItem,
}: VersionDropdownContentProps) {
const updateRange = updateRangeForUpdate(update)
const closeDropdown = useCallback(() => {
closeDropdownForItem(update)
}, [closeDropdownForItem, update])
@ -36,15 +37,11 @@ function VersionDropdownContent({
version={update.toV}
closeDropdown={closeDropdown}
/>
{!comparing && !selected && (
<Compare
projectId={projectId}
fromV={update.fromV}
toV={update.toV}
updateMetaEndTimestamp={update.meta.end_ts}
<CompareItems
updateRange={updateRange}
selected={selected}
closeDropdown={closeDropdown}
/>
)}
</>
)
}

View file

@ -18,7 +18,7 @@ type HistoryVersionProps = {
update: LoadedUpdate
currentUserId: string
projectId: string
comparing: boolean
selectable: boolean
faded: boolean
showDivider: boolean
selected: boolean
@ -33,7 +33,7 @@ function HistoryVersion({
update,
currentUserId,
projectId,
comparing,
selectable,
faded,
showDivider,
selected,
@ -44,7 +44,6 @@ function HistoryVersion({
closeDropdownForItem,
}: HistoryVersionProps) {
const orderedLabels = orderBy(update.labels, ['created_at'], ['desc'])
const selectable = !faded && (comparing || !selected)
return (
<>
@ -99,7 +98,6 @@ function HistoryVersion({
>
{dropdownActive ? (
<VersionDropdownContent
comparing={comparing}
selected={selected}
update={update}
projectId={projectId}

View file

@ -17,7 +17,6 @@ type LabelListItemProps = {
labels: LoadedLabel[]
currentUserId: string
projectId: string
comparing: boolean
selected: boolean
selectable: boolean
setSelection: HistoryContextValue['setSelection']
@ -32,7 +31,6 @@ function LabelListItem({
labels,
currentUserId,
projectId,
comparing,
selected,
selectable,
setSelection,
@ -105,10 +103,9 @@ function LabelListItem({
>
{dropdownActive ? (
<LabelDropdownContent
comparing={comparing}
selected={selected}
version={version}
updateMetaEndTimestamp={toVTimestamp}
versionTimestamp={toVTimestamp}
projectId={projectId}
closeDropdownForItem={closeDropdownForItem}
/>

View file

@ -39,7 +39,6 @@ function LabelsList() {
version={version}
currentUserId={currentUserId}
projectId={projectId}
comparing={selection.comparing}
selected={selected}
selectable={!(singleVersionSelected && selected)}
setSelection={setSelection}

View file

@ -1,9 +1,12 @@
import { useTranslation } from 'react-i18next'
import { useHistoryContext } from '../../context/history-context'
import { getUpdateForVersion } from '../../utils/history-details'
import { computeUpdateRange } from '../../utils/range'
import {
getUpdateForVersion,
updateRangeForUpdate,
} from '../../utils/history-details'
import { isAnyVersionMatchingSelection } from '../../utils/label'
import { HistoryContextValue } from '../../context/types/history-context-value'
import { updateRangeUnion } from '../../utils/range'
type ToggleSwitchProps = Pick<
HistoryContextValue,
@ -28,22 +31,19 @@ function ToggleSwitch({ labelsOnly, setLabelsOnly }: ToggleSwitchProps) {
// in labels only mode the `fromV` is equal to `toV` value
// switching to all history mode and triggering immediate comparison with
// an older version would cause a bug if the computation below is skipped.
const update = selection.updateRange?.toV
? getUpdateForVersion(selection.updateRange.toV, updatesInfo.updates)
: null
const { updateRange } = selection
const update = updateRange?.toV
? getUpdateForVersion(updateRange.toV, updatesInfo.updates)
: null
if (
updateRange &&
update &&
(update.fromV !== updateRange.fromV || update.toV !== updateRange.toV)
) {
const range = computeUpdateRange(
updateRange,
update.fromV,
update.toV,
update.meta.end_ts
const range = updateRangeUnion(
updateRangeForUpdate(update),
updateRange
)
setSelection({

View file

@ -1,26 +1,19 @@
import { UpdateRange } from '../services/types/update'
export const computeUpdateRange = (
updateRange: UpdateRange,
fromV: number,
toV: number,
updateMetaEndTimestamp: number
export const updateRangeUnion = (
updateRange1: UpdateRange,
updateRange2: UpdateRange
) => {
const fromVersion = Math.min(fromV, updateRange.fromV)
const toVersion = Math.max(toV, updateRange.toV)
const fromVTimestamp = Math.min(
updateMetaEndTimestamp,
updateRange.fromVTimestamp
)
const toVTimestamp = Math.max(
updateMetaEndTimestamp,
updateRange.toVTimestamp
)
return {
fromV: fromVersion,
toV: toVersion,
fromVTimestamp,
toVTimestamp,
fromV: Math.min(updateRange1.fromV, updateRange2.fromV),
toV: Math.max(updateRange1.toV, updateRange2.toV),
fromVTimestamp: Math.min(
updateRange1.fromVTimestamp,
updateRange2.fromVTimestamp
),
toVTimestamp: Math.max(
updateRange1.toVTimestamp,
updateRange2.toVTimestamp
),
}
}

View file

@ -682,7 +682,9 @@
"history_add_label": "Add label",
"history_adding_label": "Adding label",
"history_are_you_sure_delete_label": "Are you sure you want to delete the following label",
"history_compare_with_this_version": "Compare with this version",
"history_compare_from_this_version": "Compare from this version",
"history_compare_to_selected_version": "Compare to selected version",
"history_compare_to_this_version": "Compare to this version",
"history_delete_label": "Delete label",
"history_deleting_label": "Deleting label",
"history_download_this_version": "Download this version",