[web/document-updater] display preview for accepted/rejected tracked changes (#36240)

* [web] add preview payload to track-change notification types

* [doc-updater] pass accepted/rejected change data through to web for previews

* [web] snapshot change preview onto accepted/rejected notifications

* [web] render resolved change previews instead of accepted/rejected count lines

* [web] add unit coverage for accepted/rejected change preview flow

* fix: notification carries multiple previews per accept/reject batch; extract shared group helper

* fix: one preview per accept/reject action, drop adjacency grouping

* fix: move buildResolvedChangePreviews out of the email builder class

* fix: type error, make tests more explicit

* fix: render resolver below preview

* fix: overflow text, alignment

* fix: test error

* fix: resolved count

* [document-updater/web] send sparse change preview instead of full doc lines

* fix: store sparse preview, hydrate at render, drop CE gate

* fix: advance slice cursor past tracked-delete text in sparse preview

* fix: inline buildChangeGroupPreview

* fix: changePreviewBuilder -> sparseChangePreview

* fix: rm change_contributors

* fix: copilot flagged testing issues

* fix: hydrate and truncate change preview at write time

* fix: avoid materializing full doc text when slicing preview window

* fix: update reject preview test to hydrated shape

* fix: accepted delete removing context

* [document-updater/web] split resolved change previews into one per change cluster

* fix: narrow cluster userIds to string[] for typecheck

* feat: updating to use zod schema for previews

---------

Co-authored-by: Jimmy Domagala-Tang <jimmy.domagala-tang@overleaf.com>
GitOrigin-RevId: 0740713c715a61a220180f353e497b27da6a5b0f
This commit is contained in:
Kristina
2026-09-17 11:58:14 +00:00
committed by Copybot
co-authored by Jimmy Domagala-Tang
parent 3e0a54c917
commit 9c005e4e54
14 changed files with 699 additions and 41 deletions
@@ -208,7 +208,7 @@ async function clearProjectState(projectId) {
* @param {string} userId
*/
async function acceptChanges(projectId, docId, changeIds, userId) {
const { changeContributors } = await fetchJson(
const { changeContributors, previews } = await fetchJson(
`${BASE_URL}/project/${projectId}/doc/${docId}/change/accept`,
{
method: 'POST',
@@ -221,7 +221,8 @@ async function acceptChanges(projectId, docId, changeIds, userId) {
projectId,
docId,
userId,
changeContributors
changeContributors,
previews
)
}
@@ -214,6 +214,21 @@ async function setDocument(req, res) {
res.json(result)
}
const changePreview = z.strictObject({
sectionPath: z.array(z.string()),
startLine: z.number().int().min(1),
changes: z.array(
z.strictObject({
i: z.string().optional(),
d: z.string().optional(),
p: z.number().int().min(0),
})
),
slice: z.string(),
sliceStart: z.number().int().min(0),
userIds: z.array(zz.objectId()),
})
const trackChangesRejectedSchema = z.object({
params: z.strictObject({
Project_id: zz.objectId(),
@@ -222,6 +237,7 @@ const trackChangesRejectedSchema = z.object({
body: z.strictObject({
rejectedChangeAuthorIds: z.array(zz.objectId()),
userId: zz.objectId().optional(),
previews: z.array(changePreview).optional(),
}),
})
@@ -230,13 +246,14 @@ async function trackChangesRejected(req, res) {
logOnly: true,
})
const { Project_id: projectId, doc_id: docId } = params
const { rejectedChangeAuthorIds, userId } = body
const { rejectedChangeAuthorIds, userId, previews } = body
await Modules.promises.hooks.fire(
'trackChangesRejected',
projectId,
docId,
userId,
rejectedChangeAuthorIds
rejectedChangeAuthorIds,
previews
)
res.sendStatus(204)
}