bug: notification deep-link does not scroll to comment on document detail page #299

Merged
marcel merged 8 commits from feat/issue-276-notification-deep-link-scroll into main 2026-04-21 15:06:02 +02:00
2 changed files with 134 additions and 0 deletions
Showing only changes of commit 13732ab96b - Show all commits

View File

@@ -0,0 +1,24 @@
-- Backfill annotation_id on block comments and their notifications.
--
-- Before the upstream fix, CommentService.postBlockComment did not set
-- DocumentComment.annotationId, so block comments were stored with
-- annotation_id = NULL and every notification built from them inherited
-- that NULL (see NotificationService.notifyMentions/notifyReply).
--
-- The frontend deep-link flow needs annotationId in the URL query string
-- to open the correct annotation panel and scroll to the comment.
-- Without this backfill, previously issued notifications would still
-- carry annotation_id = NULL even after the code fix lands.
UPDATE document_comments dc
SET annotation_id = tb.annotation_id
FROM transcription_blocks tb
WHERE dc.block_id = tb.id
AND dc.annotation_id IS NULL;
UPDATE notifications n
SET annotation_id = dc.annotation_id
FROM document_comments dc
WHERE n.reference_id = dc.id
AND n.annotation_id IS NULL
AND dc.annotation_id IS NOT NULL;

View File

@@ -302,6 +302,57 @@ class MigrationIntegrationTest {
).isInstanceOf(DataIntegrityViolationException.class);
}
// ─── V51: backfill annotation_id on block comments and notifications ─────
@Test
void v51_backfillsAnnotationIdOnBlockCommentsFromTheirBlocks() {
UUID docId = createDocument();
UUID annotationId = insertAnnotation(docId);
UUID blockId = insertBlock(docId, annotationId);
UUID commentId = insertBlockCommentWithNullAnnotationId(docId, blockId);
jdbc.update(V51_BACKFILL_COMMENTS_SQL);
UUID stored = jdbc.queryForObject(
"SELECT annotation_id FROM document_comments WHERE id = ?",
UUID.class, commentId);
assertThat(stored).isEqualTo(annotationId);
}
@Test
void v51_backfillsAnnotationIdOnNotificationsFromTheirReferencedComment() {
UUID docId = createDocument();
UUID userId = insertUser("recipient-" + UUID.randomUUID() + "@example.com");
UUID annotationId = insertAnnotation(docId);
UUID blockId = insertBlock(docId, annotationId);
UUID commentId = insertBlockCommentWithAnnotationId(docId, blockId, annotationId);
UUID notificationId = insertNotificationWithNullAnnotationId(docId, commentId, userId);
jdbc.update(V51_BACKFILL_NOTIFICATIONS_SQL);
UUID stored = jdbc.queryForObject(
"SELECT annotation_id FROM notifications WHERE id = ?",
UUID.class, notificationId);
assertThat(stored).isEqualTo(annotationId);
}
private static final String V51_BACKFILL_COMMENTS_SQL = """
UPDATE document_comments dc
SET annotation_id = tb.annotation_id
FROM transcription_blocks tb
WHERE dc.block_id = tb.id
AND dc.annotation_id IS NULL
""";
private static final String V51_BACKFILL_NOTIFICATIONS_SQL = """
UPDATE notifications n
SET annotation_id = dc.annotation_id
FROM document_comments dc
WHERE n.reference_id = dc.id
AND n.annotation_id IS NULL
AND dc.annotation_id IS NOT NULL
""";
// ─── helpers ─────────────────────────────────────────────────────────────
private UUID createPerson(String firstName, String lastName) {
@@ -326,4 +377,63 @@ class MigrationIntegrationTest {
em.flush();
return doc.getId();
}
private UUID insertAnnotation(UUID docId) {
UUID id = UUID.randomUUID();
jdbc.update("""
INSERT INTO document_annotations
(id, document_id, page_number, x, y, width, height, color)
VALUES (?, ?, 1, 0.1, 0.1, 0.3, 0.1, '#00C7B1')
""", id, docId);
return id;
}
private UUID insertBlock(UUID docId, UUID annotationId) {
UUID id = UUID.randomUUID();
jdbc.update("""
INSERT INTO transcription_blocks
(id, annotation_id, document_id, text, sort_order)
VALUES (?, ?, ?, '', 0)
""", id, annotationId, docId);
return id;
}
private UUID insertUser(String email) {
UUID id = UUID.randomUUID();
jdbc.update("""
INSERT INTO users (id, email, password, enabled, notify_on_reply, notify_on_mention)
VALUES (?, ?, 'hash', true, false, false)
""", id, email);
return id;
}
private UUID insertBlockCommentWithNullAnnotationId(UUID docId, UUID blockId) {
UUID id = UUID.randomUUID();
jdbc.update("""
INSERT INTO document_comments
(id, document_id, block_id, annotation_id, author_name, content)
VALUES (?, ?, ?, NULL, 'Tester', 'Hi')
""", id, docId, blockId);
return id;
}
private UUID insertBlockCommentWithAnnotationId(UUID docId, UUID blockId, UUID annotationId) {
UUID id = UUID.randomUUID();
jdbc.update("""
INSERT INTO document_comments
(id, document_id, block_id, annotation_id, author_name, content)
VALUES (?, ?, ?, ?, 'Tester', 'Hi')
""", id, docId, blockId, annotationId);
return id;
}
private UUID insertNotificationWithNullAnnotationId(UUID docId, UUID commentId, UUID recipientId) {
UUID id = UUID.randomUUID();
jdbc.update("""
INSERT INTO notifications
(id, recipient_id, type, document_id, reference_id, annotation_id, read, actor_name)
VALUES (?, ?, 'MENTION', ?, ?, NULL, false, 'Tester')
""", id, recipientId, docId, commentId);
return id;
}
}