Просмотр исходного кода

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

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

GitOrigin-RevId: f550ad06b1e812011ecb32e6772354eb0abc2163
Tim Down 3 лет назад
Родитель
Сommit
c870bd5f53

+ 3 - 1
services/web/frontend/extracted-translations.json

@@ -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": "",

+ 2 - 1
services/web/frontend/js/features/history/components/change-list/all-history-list.tsx

@@ -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}

+ 13 - 14
services/web/frontend/js/features/history/components/change-list/dropdown/label-dropdown-content.tsx

@@ -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}
-          closeDropdown={closeDropdown}
-        />
-      )}
+      <CompareItems
+        updateRange={{
+          fromV: version,
+          toV: version,
+          fromVTimestamp: versionTimestamp,
+          toVTimestamp: versionTimestamp,
+        }}
+        selected={selected}
+        closeDropdown={closeDropdown}
+      />
     </>
   )
 }

+ 69 - 0
services/web/frontend/js/features/history/components/change-list/dropdown/menu-item/compare-items.tsx

@@ -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

+ 10 - 30
services/web/frontend/js/features/history/components/change-list/dropdown/menu-item/compare.tsx

@@ -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,
-          comparing: true,
-          files: [],
-        }
-      }
-
-      return prevSelection
+    setSelection({
+      updateRange: comparisonRange,
+      comparing: true,
+      files: [],
     })
   }
 
   return (
     <MenuItem onClick={handleCompareVersion} {...props}>
-      <Icon type="exchange" fw /> {t('history_compare_with_this_version')}
+      <Icon type="exchange" fw /> {text}
     </MenuItem>
   )
 }

+ 9 - 12
services/web/frontend/js/features/history/components/change-list/dropdown/version-dropdown-content.tsx

@@ -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}
-          closeDropdown={closeDropdown}
-        />
-      )}
+      <CompareItems
+        updateRange={updateRange}
+        selected={selected}
+        closeDropdown={closeDropdown}
+      />
     </>
   )
 }

+ 2 - 4
services/web/frontend/js/features/history/components/change-list/history-version.tsx

@@ -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}

+ 1 - 4
services/web/frontend/js/features/history/components/change-list/label-list-item.tsx

@@ -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}
           />

+ 0 - 1
services/web/frontend/js/features/history/components/change-list/labels-list.tsx

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

+ 11 - 11
services/web/frontend/js/features/history/components/change-list/toggle-switch.tsx

@@ -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({

+ 13 - 20
services/web/frontend/js/features/history/utils/range.ts

@@ -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
+    ),
   }
 }

+ 3 - 1
services/web/locales/en.json

@@ -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",