mirror of
https://github.com/overleaf/overleaf.git
synced 2024-10-24 21:12:38 -04:00
Merge pull request #21208 from overleaf/dp-accept-reject-all-changes
Add options to accept/reject all changes to review tooltip GitOrigin-RevId: 1cea76926d59d0354c8abaa6ba69b7e99c02fbe0
This commit is contained in:
parent
c4b23b4d39
commit
1c5f5950fa
7 changed files with 84 additions and 20 deletions
|
@ -6,7 +6,7 @@ import {
|
||||||
import { EditorSelection } from '@codemirror/state'
|
import { EditorSelection } from '@codemirror/state'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useThreadsActionsContext } from '../context/threads-context'
|
import { useThreadsActionsContext } from '../context/threads-context'
|
||||||
import { removeNewCommentRangeEffect } from '@/features/source-editor/extensions/add-comment'
|
import { removeNewCommentRangeEffect } from '@/features/source-editor/extensions/review-tooltip'
|
||||||
import useSubmittableTextInput from '../hooks/use-submittable-text-input'
|
import useSubmittableTextInput from '../hooks/use-submittable-text-input'
|
||||||
import AutoExpandingTextArea from '@/shared/components/auto-expanding-text-area'
|
import AutoExpandingTextArea from '@/shared/components/auto-expanding-text-area'
|
||||||
import { ReviewPanelEntry } from './review-panel-entry'
|
import { ReviewPanelEntry } from './review-panel-entry'
|
||||||
|
|
|
@ -28,7 +28,7 @@ import { canAggregate } from '../utils/can-aggregate'
|
||||||
import ReviewPanelEmptyState from './review-panel-empty-state'
|
import ReviewPanelEmptyState from './review-panel-empty-state'
|
||||||
import useEventListener from '@/shared/hooks/use-event-listener'
|
import useEventListener from '@/shared/hooks/use-event-listener'
|
||||||
import { hasActiveRange } from '@/features/review-panel-new/utils/has-active-range'
|
import { hasActiveRange } from '@/features/review-panel-new/utils/has-active-range'
|
||||||
import { addCommentStateField } from '@/features/source-editor/extensions/add-comment'
|
import { reviewTooltipStateField } from '@/features/source-editor/extensions/review-tooltip'
|
||||||
import ReviewPanelMoreCommentsButton from './review-panel-more-comments-button'
|
import ReviewPanelMoreCommentsButton from './review-panel-more-comments-button'
|
||||||
import useMoreCommments from '../hooks/use-more-comments'
|
import useMoreCommments from '../hooks/use-more-comments'
|
||||||
import { Decoration } from '@codemirror/view'
|
import { Decoration } from '@codemirror/view'
|
||||||
|
@ -111,7 +111,10 @@ const ReviewPanelCurrentFile: FC = () => {
|
||||||
|
|
||||||
const positionsRef = useRef<Map<string, number>>(new Map())
|
const positionsRef = useRef<Map<string, number>>(new Map())
|
||||||
|
|
||||||
const addCommentRanges = state.field(addCommentStateField, false)?.ranges
|
const addCommentRanges = state.field(
|
||||||
|
reviewTooltipStateField,
|
||||||
|
false
|
||||||
|
)?.addCommentRanges
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (aggregatedRanges) {
|
if (aggregatedRanges) {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import {
|
||||||
SetStateAction,
|
SetStateAction,
|
||||||
useCallback,
|
useCallback,
|
||||||
useEffect,
|
useEffect,
|
||||||
|
useMemo,
|
||||||
useState,
|
useState,
|
||||||
} from 'react'
|
} from 'react'
|
||||||
import ReactDOM from 'react-dom'
|
import ReactDOM from 'react-dom'
|
||||||
|
@ -14,14 +15,19 @@ import {
|
||||||
useCodeMirrorViewContext,
|
useCodeMirrorViewContext,
|
||||||
} from '@/features/source-editor/components/codemirror-context'
|
} from '@/features/source-editor/components/codemirror-context'
|
||||||
import {
|
import {
|
||||||
addCommentStateField,
|
|
||||||
buildAddNewCommentRangeEffect,
|
buildAddNewCommentRangeEffect,
|
||||||
} from '@/features/source-editor/extensions/add-comment'
|
reviewTooltipStateField,
|
||||||
|
} from '@/features/source-editor/extensions/review-tooltip'
|
||||||
import { getTooltip } from '@codemirror/view'
|
import { getTooltip } from '@codemirror/view'
|
||||||
import useViewerPermissions from '@/shared/hooks/use-viewer-permissions'
|
import useViewerPermissions from '@/shared/hooks/use-viewer-permissions'
|
||||||
import usePreviousValue from '@/shared/hooks/use-previous-value'
|
import usePreviousValue from '@/shared/hooks/use-previous-value'
|
||||||
import { useLayoutContext } from '@/shared/context/layout-context'
|
import { useLayoutContext } from '@/shared/context/layout-context'
|
||||||
import { useReviewPanelViewActionsContext } from '../context/review-panel-view-context'
|
import { useReviewPanelViewActionsContext } from '../context/review-panel-view-context'
|
||||||
|
import {
|
||||||
|
useRangesActionsContext,
|
||||||
|
useRangesContext,
|
||||||
|
} from '../context/ranges-context'
|
||||||
|
import { isInsertOperation } from '@/utils/operations'
|
||||||
|
|
||||||
const ReviewTooltipMenu: FC = () => {
|
const ReviewTooltipMenu: FC = () => {
|
||||||
const state = useCodeMirrorStateContext()
|
const state = useCodeMirrorStateContext()
|
||||||
|
@ -29,7 +35,7 @@ const ReviewTooltipMenu: FC = () => {
|
||||||
const isViewer = useViewerPermissions()
|
const isViewer = useViewerPermissions()
|
||||||
const [show, setShow] = useState(true)
|
const [show, setShow] = useState(true)
|
||||||
|
|
||||||
const tooltipState = state.field(addCommentStateField, false)?.tooltip
|
const tooltipState = state.field(reviewTooltipStateField, false)?.tooltip
|
||||||
const previousTooltipState = usePreviousValue(tooltipState)
|
const previousTooltipState = usePreviousValue(tooltipState)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -62,6 +68,8 @@ const ReviewTooltipMenuContent: FC<{
|
||||||
const state = useCodeMirrorStateContext()
|
const state = useCodeMirrorStateContext()
|
||||||
const { setReviewPanelOpen } = useLayoutContext()
|
const { setReviewPanelOpen } = useLayoutContext()
|
||||||
const { setView } = useReviewPanelViewActionsContext()
|
const { setView } = useReviewPanelViewActionsContext()
|
||||||
|
const ranges = useRangesContext()
|
||||||
|
const { acceptChanges, rejectChanges } = useRangesActionsContext()
|
||||||
|
|
||||||
const addComment = useCallback(() => {
|
const addComment = useCallback(() => {
|
||||||
setReviewPanelOpen(true)
|
setReviewPanelOpen(true)
|
||||||
|
@ -80,6 +88,28 @@ const ReviewTooltipMenuContent: FC<{
|
||||||
}
|
}
|
||||||
}, [addComment])
|
}, [addComment])
|
||||||
|
|
||||||
|
const changeIdsInSelection = useMemo(() => {
|
||||||
|
return (ranges?.changes ?? [])
|
||||||
|
.filter(({ op }) => {
|
||||||
|
const opFrom = op.p
|
||||||
|
const opLength = isInsertOperation(op) ? op.i.length : 0
|
||||||
|
const opTo = opFrom + opLength
|
||||||
|
const selection = state.selection.main
|
||||||
|
return opFrom >= selection.from && opTo <= selection.to
|
||||||
|
})
|
||||||
|
.map(({ id }) => id)
|
||||||
|
}, [ranges, state.selection.main])
|
||||||
|
|
||||||
|
const acceptChangesHandler = useCallback(() => {
|
||||||
|
acceptChanges(...changeIdsInSelection)
|
||||||
|
}, [acceptChanges, changeIdsInSelection])
|
||||||
|
|
||||||
|
const rejectChangesHandler = useCallback(() => {
|
||||||
|
rejectChanges(...changeIdsInSelection)
|
||||||
|
}, [rejectChanges, changeIdsInSelection])
|
||||||
|
|
||||||
|
const showChangesButtons = changeIdsInSelection.length > 0
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="review-tooltip-menu">
|
<div className="review-tooltip-menu">
|
||||||
<button
|
<button
|
||||||
|
@ -89,6 +119,23 @@ const ReviewTooltipMenuContent: FC<{
|
||||||
<MaterialIcon type="chat" />
|
<MaterialIcon type="chat" />
|
||||||
{t('add_comment')}
|
{t('add_comment')}
|
||||||
</button>
|
</button>
|
||||||
|
{showChangesButtons && (
|
||||||
|
<>
|
||||||
|
<div className="review-tooltip-menu-divider" />
|
||||||
|
<button
|
||||||
|
className="review-tooltip-menu-button"
|
||||||
|
onClick={acceptChangesHandler}
|
||||||
|
>
|
||||||
|
<MaterialIcon type="check" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="review-tooltip-menu-button"
|
||||||
|
onClick={rejectChangesHandler}
|
||||||
|
>
|
||||||
|
<MaterialIcon type="clear" />
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ import { mathPreview } from './math-preview'
|
||||||
import { isSplitTestEnabled } from '@/utils/splitTestUtils'
|
import { isSplitTestEnabled } from '@/utils/splitTestUtils'
|
||||||
import { ranges } from './ranges'
|
import { ranges } from './ranges'
|
||||||
import { trackDetachedComments } from './track-detached-comments'
|
import { trackDetachedComments } from './track-detached-comments'
|
||||||
import { addComment } from './add-comment'
|
import { reviewTooltip } from './review-tooltip'
|
||||||
|
|
||||||
const moduleExtensions: Array<(options: Record<string, any>) => Extension> =
|
const moduleExtensions: Array<(options: Record<string, any>) => Extension> =
|
||||||
importOverleafModules('sourceEditorExtensions').map(
|
importOverleafModules('sourceEditorExtensions').map(
|
||||||
|
@ -138,7 +138,7 @@ export const createExtensions = (options: Record<string, any>): Extension[] => [
|
||||||
trackDetachedComments(options.currentDoc),
|
trackDetachedComments(options.currentDoc),
|
||||||
visual(options.visual),
|
visual(options.visual),
|
||||||
mathPreview(options.settings.mathPreview),
|
mathPreview(options.settings.mathPreview),
|
||||||
addComment(),
|
reviewTooltip(),
|
||||||
toolbarPanel(),
|
toolbarPanel(),
|
||||||
verticalOverflow(),
|
verticalOverflow(),
|
||||||
highlightActiveLine(options.visual.visual),
|
highlightActiveLine(options.visual.visual),
|
||||||
|
|
|
@ -31,31 +31,31 @@ export const buildAddNewCommentRangeEffect = (range: SelectionRange) => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const addComment = (): Extension => {
|
export const reviewTooltip = (): Extension => {
|
||||||
if (!isSplitTestEnabled('review-panel-redesign')) {
|
if (!isSplitTestEnabled('review-panel-redesign')) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
return [addCommentTheme, addCommentStateField, textSelected]
|
return [reviewTooltipTheme, reviewTooltipStateField, textSelected]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const addCommentStateField = StateField.define<{
|
export const reviewTooltipStateField = StateField.define<{
|
||||||
tooltip: Tooltip | null
|
tooltip: Tooltip | null
|
||||||
ranges: DecorationSet
|
addCommentRanges: DecorationSet
|
||||||
}>({
|
}>({
|
||||||
create() {
|
create() {
|
||||||
return { tooltip: null, ranges: Decoration.none }
|
return { tooltip: null, addCommentRanges: Decoration.none }
|
||||||
},
|
},
|
||||||
|
|
||||||
update(field, tr) {
|
update(field, tr) {
|
||||||
let { tooltip, ranges } = field
|
let { tooltip, addCommentRanges } = field
|
||||||
|
|
||||||
ranges = ranges.map(tr.changes)
|
addCommentRanges = addCommentRanges.map(tr.changes)
|
||||||
|
|
||||||
for (const effect of tr.effects) {
|
for (const effect of tr.effects) {
|
||||||
if (effect.is(removeNewCommentRangeEffect)) {
|
if (effect.is(removeNewCommentRangeEffect)) {
|
||||||
const rangeToRemove = effect.value
|
const rangeToRemove = effect.value
|
||||||
ranges = ranges.update({
|
addCommentRanges = addCommentRanges.update({
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
filter: (from, to, value) => {
|
filter: (from, to, value) => {
|
||||||
return value.spec.id !== rangeToRemove.spec.id
|
return value.spec.id !== rangeToRemove.spec.id
|
||||||
|
@ -65,7 +65,7 @@ export const addCommentStateField = StateField.define<{
|
||||||
|
|
||||||
if (effect.is(addNewCommentRangeEffect)) {
|
if (effect.is(addNewCommentRangeEffect)) {
|
||||||
const rangeToAdd = effect.value
|
const rangeToAdd = effect.value
|
||||||
ranges = ranges.update({
|
addCommentRanges = addCommentRanges.update({
|
||||||
add: [rangeToAdd],
|
add: [rangeToAdd],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -79,11 +79,11 @@ export const addCommentStateField = StateField.define<{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { tooltip, ranges }
|
return { tooltip, addCommentRanges }
|
||||||
},
|
},
|
||||||
|
|
||||||
provide: field => [
|
provide: field => [
|
||||||
EditorView.decorations.from(field, field => field.ranges),
|
EditorView.decorations.from(field, field => field.addCommentRanges),
|
||||||
showTooltip.compute([field], state => state.field(field).tooltip),
|
showTooltip.compute([field], state => state.field(field).tooltip),
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
@ -109,7 +109,7 @@ function buildTooltip(range: SelectionRange): Tooltip | null {
|
||||||
/**
|
/**
|
||||||
* Styles for the tooltip
|
* Styles for the tooltip
|
||||||
*/
|
*/
|
||||||
const addCommentTheme = EditorView.baseTheme({
|
const reviewTooltipTheme = EditorView.baseTheme({
|
||||||
'.review-tooltip-menu-container.cm-tooltip': {
|
'.review-tooltip-menu-container.cm-tooltip': {
|
||||||
backgroundColor: 'transparent',
|
backgroundColor: 'transparent',
|
||||||
border: 'none',
|
border: 'none',
|
|
@ -619,10 +619,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.review-tooltip-menu {
|
.review-tooltip-menu {
|
||||||
|
display: flex;
|
||||||
box-shadow: 0px 2px 4px 0px #1e253029;
|
box-shadow: 0px 2px 4px 0px #1e253029;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
|
gap: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.review-tooltip-menu-button {
|
.review-tooltip-menu-button {
|
||||||
|
@ -635,6 +637,11 @@
|
||||||
border-radius: @border-radius-base;
|
border-radius: @border-radius-base;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.review-tooltip-menu-divider {
|
||||||
|
width: 1px;
|
||||||
|
background-color: #e7e9ee;
|
||||||
|
}
|
||||||
|
|
||||||
.review-tooltip-add-comment-button {
|
.review-tooltip-add-comment-button {
|
||||||
padding: 2px 8px;
|
padding: 2px 8px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -623,10 +623,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.review-tooltip-menu {
|
.review-tooltip-menu {
|
||||||
|
display: flex;
|
||||||
box-shadow: 0 2px 4px 0 #1e253029;
|
box-shadow: 0 2px 4px 0 #1e253029;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: var(--border-radius-base);
|
border-radius: var(--border-radius-base);
|
||||||
padding: var(--spacing-02);
|
padding: var(--spacing-02);
|
||||||
|
gap: var(--spacing-02);
|
||||||
}
|
}
|
||||||
|
|
||||||
.review-tooltip-menu-button {
|
.review-tooltip-menu-button {
|
||||||
|
@ -644,6 +646,11 @@
|
||||||
padding: var(--spacing-01) var(--spacing-04);
|
padding: var(--spacing-01) var(--spacing-04);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.review-tooltip-menu-divider {
|
||||||
|
width: 1px;
|
||||||
|
background-color: #e7e9ee;
|
||||||
|
}
|
||||||
|
|
||||||
.review-panel-tooltip {
|
.review-panel-tooltip {
|
||||||
pointer-events: none; // this is to prevent mouseLeave event from firing when hovering over the tooltip
|
pointer-events: none; // this is to prevent mouseLeave event from firing when hovering over the tooltip
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue