|
@@ -3,9 +3,9 @@ import {
|
|
|
memo,
|
|
memo,
|
|
|
useCallback,
|
|
useCallback,
|
|
|
useEffect,
|
|
useEffect,
|
|
|
- useMemo,
|
|
|
|
|
useRef,
|
|
useRef,
|
|
|
useState,
|
|
useState,
|
|
|
|
|
+ useMemo,
|
|
|
} from 'react'
|
|
} from 'react'
|
|
|
import { ReviewPanelAddComment } from './review-panel-add-comment'
|
|
import { ReviewPanelAddComment } from './review-panel-add-comment'
|
|
|
import { ReviewPanelChange } from './review-panel-change'
|
|
import { ReviewPanelChange } from './review-panel-change'
|
|
@@ -23,12 +23,12 @@ import {
|
|
|
import { useRangesContext } from '../context/ranges-context'
|
|
import { useRangesContext } from '../context/ranges-context'
|
|
|
import { useThreadsContext } from '../context/threads-context'
|
|
import { useThreadsContext } from '../context/threads-context'
|
|
|
import { isDeleteChange, isInsertChange } from '@/utils/operations'
|
|
import { isDeleteChange, isInsertChange } from '@/utils/operations'
|
|
|
-import Icon from '@/shared/components/icon'
|
|
|
|
|
import { positionItems } from '../utils/position-items'
|
|
import { positionItems } from '../utils/position-items'
|
|
|
import { canAggregate } from '../utils/can-aggregate'
|
|
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'
|
|
|
|
|
|
|
|
type AggregatedRanges = {
|
|
type AggregatedRanges = {
|
|
|
changes: Change<EditOperation>[]
|
|
changes: Change<EditOperation>[]
|
|
@@ -44,14 +44,6 @@ const ReviewPanelCurrentFile: FC = () => {
|
|
|
|
|
|
|
|
const [aggregatedRanges, setAggregatedRanges] = useState<AggregatedRanges>()
|
|
const [aggregatedRanges, setAggregatedRanges] = useState<AggregatedRanges>()
|
|
|
|
|
|
|
|
- const selectionCoords = useMemo(
|
|
|
|
|
- () =>
|
|
|
|
|
- state.selection.main.empty
|
|
|
|
|
- ? null
|
|
|
|
|
- : view.coordsAtPos(state.selection.main.head),
|
|
|
|
|
- [view, state]
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
const containerRef = useRef<HTMLDivElement | null>(null)
|
|
const containerRef = useRef<HTMLDivElement | null>(null)
|
|
|
const previousFocusedItem = useRef(0)
|
|
const previousFocusedItem = useRef(0)
|
|
|
|
|
|
|
@@ -149,6 +141,8 @@ const ReviewPanelCurrentFile: FC = () => {
|
|
|
|
|
|
|
|
const positionsRef = useRef<Map<string, number>>(new Map())
|
|
const positionsRef = useRef<Map<string, number>>(new Map())
|
|
|
|
|
|
|
|
|
|
+ const addCommentRanges = state.field(addCommentStateField).ranges
|
|
|
|
|
+
|
|
|
useEffect(() => {
|
|
useEffect(() => {
|
|
|
if (aggregatedRanges) {
|
|
if (aggregatedRanges) {
|
|
|
view.requestMeasure({
|
|
view.requestMeasure({
|
|
@@ -157,29 +151,45 @@ const ReviewPanelCurrentFile: FC = () => {
|
|
|
const contentRect = view.contentDOM.getBoundingClientRect()
|
|
const contentRect = view.contentDOM.getBoundingClientRect()
|
|
|
const docLength = view.state.doc.length
|
|
const docLength = view.state.doc.length
|
|
|
|
|
|
|
|
- const screenPosition = (change: Change): number | undefined => {
|
|
|
|
|
- const pos = Math.min(change.op.p, docLength) // TODO: needed?
|
|
|
|
|
|
|
+ const screenPosition = (position: number): number | undefined => {
|
|
|
|
|
+ const pos = Math.min(position, docLength) // TODO: needed?
|
|
|
const coords = view.coordsAtPos(pos)
|
|
const coords = view.coordsAtPos(pos)
|
|
|
|
|
|
|
|
return coords ? Math.round(coords.top - contentRect.top) : undefined
|
|
return coords ? Math.round(coords.top - contentRect.top) : undefined
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
for (const change of aggregatedRanges.changes) {
|
|
for (const change of aggregatedRanges.changes) {
|
|
|
- const position = screenPosition(change)
|
|
|
|
|
|
|
+ const position = screenPosition(change.op.p)
|
|
|
if (position) {
|
|
if (position) {
|
|
|
positionsRef.current.set(change.id, position)
|
|
positionsRef.current.set(change.id, position)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
for (const comment of aggregatedRanges.comments) {
|
|
for (const comment of aggregatedRanges.comments) {
|
|
|
- const position = screenPosition(comment)
|
|
|
|
|
|
|
+ const position = screenPosition(comment.op.p)
|
|
|
if (position) {
|
|
if (position) {
|
|
|
positionsRef.current.set(comment.id, position)
|
|
positionsRef.current.set(comment.id, position)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ const cursor = addCommentRanges.iter()
|
|
|
|
|
+
|
|
|
|
|
+ while (cursor.value) {
|
|
|
|
|
+ const { from } = cursor
|
|
|
|
|
+ const position = screenPosition(from)
|
|
|
|
|
+
|
|
|
|
|
+ if (position) {
|
|
|
|
|
+ positionsRef.current.set(
|
|
|
|
|
+ `new-comment-${cursor.value.spec.id}`,
|
|
|
|
|
+ position
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ cursor.next()
|
|
|
|
|
+ }
|
|
|
},
|
|
},
|
|
|
write() {
|
|
write() {
|
|
|
- setPositions(positionsRef.current)
|
|
|
|
|
|
|
+ setPositions(new Map(positionsRef.current))
|
|
|
window.setTimeout(() => {
|
|
window.setTimeout(() => {
|
|
|
containerRef.current?.dispatchEvent(
|
|
containerRef.current?.dispatchEvent(
|
|
|
new Event('review-panel:position')
|
|
new Event('review-panel:position')
|
|
@@ -188,34 +198,58 @@ const ReviewPanelCurrentFile: FC = () => {
|
|
|
},
|
|
},
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
- }, [view, aggregatedRanges])
|
|
|
|
|
|
|
+ }, [view, aggregatedRanges, addCommentRanges])
|
|
|
|
|
|
|
|
const showEmptyState = useMemo(
|
|
const showEmptyState = useMemo(
|
|
|
() => hasActiveRange(ranges, threads) === false,
|
|
() => hasActiveRange(ranges, threads) === false,
|
|
|
[ranges, threads]
|
|
[ranges, threads]
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
|
|
+ const addCommentEntries = useMemo(() => {
|
|
|
|
|
+ const cursor = addCommentRanges.iter()
|
|
|
|
|
+
|
|
|
|
|
+ const entries = []
|
|
|
|
|
+
|
|
|
|
|
+ while (cursor.value) {
|
|
|
|
|
+ const id = `new-comment-${cursor.value.spec.id}`
|
|
|
|
|
+ if (!positions.has(id)) {
|
|
|
|
|
+ cursor.next()
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const { from, to } = cursor
|
|
|
|
|
+
|
|
|
|
|
+ entries.push({
|
|
|
|
|
+ id,
|
|
|
|
|
+ from,
|
|
|
|
|
+ to,
|
|
|
|
|
+ value: cursor.value,
|
|
|
|
|
+ top: positions.get(id),
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ cursor.next()
|
|
|
|
|
+ }
|
|
|
|
|
+ return entries
|
|
|
|
|
+ }, [addCommentRanges, positions])
|
|
|
|
|
+
|
|
|
if (!aggregatedRanges) {
|
|
if (!aggregatedRanges) {
|
|
|
return null
|
|
return null
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
return (
|
|
|
<div ref={handleContainer}>
|
|
<div ref={handleContainer}>
|
|
|
- {selectionCoords && (
|
|
|
|
|
- <div
|
|
|
|
|
- className="review-panel-entry review-panel-entry-action"
|
|
|
|
|
- style={{ position: 'absolute' }}
|
|
|
|
|
- data-top={selectionCoords.top + view.scrollDOM.scrollTop - 70}
|
|
|
|
|
- data-pos={state.selection.main.head}
|
|
|
|
|
- >
|
|
|
|
|
- <div className="review-panel-entry-indicator">
|
|
|
|
|
- <Icon type="pencil" fw />
|
|
|
|
|
- </div>
|
|
|
|
|
- <div className="review-panel-entry-content">
|
|
|
|
|
- <ReviewPanelAddComment />
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- )}
|
|
|
|
|
|
|
+ {addCommentEntries.map(entry => {
|
|
|
|
|
+ const { id, from, to, value, top } = entry
|
|
|
|
|
+ return (
|
|
|
|
|
+ <ReviewPanelAddComment
|
|
|
|
|
+ key={id}
|
|
|
|
|
+ from={from}
|
|
|
|
|
+ to={to}
|
|
|
|
|
+ value={value}
|
|
|
|
|
+ top={top}
|
|
|
|
|
+ />
|
|
|
|
|
+ )
|
|
|
|
|
+ })}
|
|
|
|
|
|
|
|
{showEmptyState && <ReviewPanelEmptyState />}
|
|
{showEmptyState && <ReviewPanelEmptyState />}
|
|
|
|
|
|