Merge pull request #28274 from overleaf/dp-review-panel-events

Add review panel tracking events back to editor

GitOrigin-RevId: 3e9b1dafd5e90909b75d7b082f0e407724dfc61a
This commit is contained in:
David
2025-09-09 11:19:19 +01:00
committed by Copybot
parent 52213330f3
commit 3feecee06f
3 changed files with 63 additions and 7 deletions

View File

@@ -32,6 +32,8 @@ import {
TransactionSpec,
} from '@codemirror/state'
import { buildRangesFromSnapshot } from '@/features/review-panel/utils/snapshot-ranges'
import { useEditorAnalytics } from '@/shared/hooks/use-editor-analytics'
import { useReviewPanelViewContext } from './review-panel-view-context'
export type Ranges = {
docId: string
@@ -106,9 +108,11 @@ export const RangesProvider: FC<React.PropsWithChildren> = ({ children }) => {
const { projectId } = useIdeReactContext()
const { currentDocument } = useEditorOpenDocContext()
const { socket } = useConnectionContext()
const { sendEvent } = useEditorAnalytics()
const [ranges, setRanges] = useState<Ranges | undefined>(() =>
buildRanges(currentDocument)
)
const reviewPanelView = useReviewPanelViewContext()
// rebuild the ranges when the current doc changes
useEffect(() => {
@@ -247,6 +251,10 @@ export const RangesProvider: FC<React.PropsWithChildren> = ({ children }) => {
}
shareDoc.submitOp([op])
sendEvent('rp-changes-accepted', {
count: changes.length,
view: reviewPanelView,
})
// dispatch an effect as the editor's doc doesn't change when tracked changes are accepted
view.dispatch({
@@ -317,6 +325,10 @@ export const RangesProvider: FC<React.PropsWithChildren> = ({ children }) => {
}
shareDoc.submitOp([op])
sendEvent('rp-changes-rejected', {
count: changes.length,
view: reviewPanelView,
})
// in case the doc didn't change
view.dispatch(...specs, {
@@ -333,6 +345,10 @@ export const RangesProvider: FC<React.PropsWithChildren> = ({ children }) => {
await postJSON(url, { body: { change_ids: ids } })
currentDocument.ranges.removeChangeIds(ids)
setRanges(buildRanges(currentDocument))
sendEvent('rp-changes-accepted', {
count: ids.length,
view: reviewPanelView,
})
}
},
async rejectChanges(...changes) {
@@ -341,11 +357,15 @@ export const RangesProvider: FC<React.PropsWithChildren> = ({ children }) => {
view.dispatch(
rejectChanges(view.state, currentDocument.ranges, ids)
)
sendEvent('rp-changes-rejected', {
count: ids.length,
view: reviewPanelView,
})
}
},
} satisfies RangesActions
}
}, [currentDocument, projectId, view])
}, [currentDocument, projectId, view, sendEvent, reviewPanelView])
if (!actions) {
return null

View File

@@ -1,8 +1,8 @@
import { useEditorAnalytics } from '@/shared/hooks/use-editor-analytics'
import {
createContext,
Dispatch,
FC,
SetStateAction,
useCallback,
useContext,
useMemo,
useState,
@@ -13,7 +13,7 @@ export type View = 'cur_file' | 'overview'
export const ReviewPanelViewContext = createContext<View>('cur_file')
type ViewActions = {
setView: Dispatch<SetStateAction<View>>
setView: (newView: View) => void
}
const ReviewPanelViewActionsContext = createContext<ViewActions | undefined>(
@@ -24,12 +24,21 @@ export const ReviewPanelViewProvider: FC<React.PropsWithChildren> = ({
children,
}) => {
const [view, setView] = useState<View>('cur_file')
const { sendEvent } = useEditorAnalytics()
const handleSetView = useCallback(
(newView: View) => {
sendEvent('rp-subview-change', { subView: newView })
setView(newView)
},
[sendEvent]
)
const actions = useMemo(
() => ({
setView,
setView: handleSetView,
}),
[setView]
[handleSetView]
)
return (

View File

@@ -33,6 +33,8 @@ import Range from 'overleaf-editor-core/lib/range'
import { trackedDeletesFromState } from '@/features/source-editor/utils/tracked-deletes'
import { useCodeMirrorViewContext } from '@/features/source-editor/components/codemirror-context'
import { rangesUpdatedEffect } from '@/features/source-editor/extensions/history-ot'
import { useEditorAnalytics } from '@/shared/hooks/use-editor-analytics'
import { useReviewPanelViewContext } from './review-panel-view-context'
export type Threads = Record<ThreadId, ReviewPanelCommentThread>
@@ -62,6 +64,8 @@ export const ThreadsProvider: FC<React.PropsWithChildren> = ({ children }) => {
const { currentDocument } = useEditorOpenDocContext()
const { isRestrictedTokenMember } = useEditorContext()
const view = useCodeMirrorViewContext()
const { sendEvent } = useEditorAnalytics()
const reviewPanelView = useReviewPanelViewContext()
// const [error, setError] = useState<Error>()
const [data, setData] = useState<Threads>()
@@ -275,6 +279,8 @@ export const ThreadsProvider: FC<React.PropsWithChildren> = ({ children }) => {
body: { content },
})
sendEvent('rp-new-comment', { size: content.length })
const op: CommentOperation = {
c: text,
p: pos,
@@ -287,22 +293,31 @@ export const ThreadsProvider: FC<React.PropsWithChildren> = ({ children }) => {
await postJSON(
`/project/${projectId}/doc/${currentDocument.doc_id}/thread/${threadId}/resolve`
)
sendEvent('rp-comment-resolve', { view: reviewPanelView })
},
async reopenThread(threadId: string) {
await postJSON(
`/project/${projectId}/doc/${currentDocument.doc_id}/thread/${threadId}/reopen`
)
sendEvent('rp-comment-reopen')
},
async deleteThread(threadId: string) {
await deleteJSON(
`/project/${projectId}/doc/${currentDocument.doc_id}/thread/${threadId}`
)
currentDocument.ranges?.removeCommentId(threadId)
sendEvent('rp-comment-delete')
},
async addMessage(threadId: ThreadId, content: string) {
await postJSON(`/project/${projectId}/thread/${threadId}/messages`, {
body: { content },
})
sendEvent('rp-comment-reply', {
view,
size: content.length,
thread: threadId,
})
},
async editMessage(
threadId: ThreadId,
@@ -336,6 +351,8 @@ export const ThreadsProvider: FC<React.PropsWithChildren> = ({ children }) => {
body: { content },
})
sendEvent('rp-new-comment', { size: content.length })
const trackedDeletes = trackedDeletesFromState(view.state)
pos = trackedDeletes.toSnapshot(pos)
const ranges = [new Range(pos, text.length)]
@@ -348,6 +365,7 @@ export const ThreadsProvider: FC<React.PropsWithChildren> = ({ children }) => {
async resolveThread(threadId: string) {
const op = new SetCommentStateOperation(threadId, true)
currentDocument.historyOTShareDoc.submitOp([op])
sendEvent('rp-comment-resolve', { view: reviewPanelView })
view.dispatch({
effects: rangesUpdatedEffect.of(null),
})
@@ -355,6 +373,7 @@ export const ThreadsProvider: FC<React.PropsWithChildren> = ({ children }) => {
async reopenThread(threadId: string) {
const op = new SetCommentStateOperation(threadId, false)
currentDocument.historyOTShareDoc.submitOp([op])
sendEvent('rp-comment-reopen')
view.dispatch({
effects: rangesUpdatedEffect.of(null),
})
@@ -362,6 +381,7 @@ export const ThreadsProvider: FC<React.PropsWithChildren> = ({ children }) => {
async deleteThread(threadId: string) {
const op = new DeleteCommentOperation(threadId)
currentDocument.historyOTShareDoc.submitOp([op])
sendEvent('rp-comment-delete')
view.dispatch({
effects: rangesUpdatedEffect.of(null),
})
@@ -370,7 +390,14 @@ export const ThreadsProvider: FC<React.PropsWithChildren> = ({ children }) => {
}
return actions
}, [view, currentDocument, projectId, isHistoryOT])
}, [
view,
reviewPanelView,
currentDocument,
projectId,
isHistoryOT,
sendEvent,
])
if (!actions) {
return null