fix(document-detail): force edit panel on notification deep-link
Comments only render inside TranscriptionEditView, so a deep-link into a document with existing reviewed transcriptions landed the user in read mode with no comment element in the DOM — the scroll target silently missed. scrollToCommentFromQuery now takes a setPanelMode callback and calls it with 'edit' whenever both query params are present. The page's own transcribe-mode $effect checks a skipInitialPanelMode flag the deep-link flow sets, so its default-panel-mode logic doesn't race against the explicit override. Two new helper tests pin the contract: panel mode is forced to 'edit' both when transcribe mode is off (entering fresh) and when it is already on (same-page notification click). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -18,6 +18,7 @@ function buildOpts(overrides: Overrides = {}): DeepLinkScrollOptions {
|
|||||||
return {
|
return {
|
||||||
transcribeMode: true,
|
transcribeMode: true,
|
||||||
setTranscribeMode: vi.fn(),
|
setTranscribeMode: vi.fn(),
|
||||||
|
setPanelMode: vi.fn(),
|
||||||
loadBlocks: vi.fn().mockResolvedValue(undefined),
|
loadBlocks: vi.fn().mockResolvedValue(undefined),
|
||||||
setActiveAnnotationId: vi.fn(),
|
setActiveAnnotationId: vi.fn(),
|
||||||
flashAnnotation: vi.fn(),
|
flashAnnotation: vi.fn(),
|
||||||
@@ -126,4 +127,26 @@ describe('scrollToCommentFromQuery', () => {
|
|||||||
|
|
||||||
expect(opts.onStripUrl).toHaveBeenCalled();
|
expect(opts.onStripUrl).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('forces panel mode to "edit" so the comment DOM exists on reviewed documents', async () => {
|
||||||
|
const url = new URL(
|
||||||
|
`https://app/documents/doc-1?commentId=${COMMENT_ID}&annotationId=${ANNOTATION_ID}`
|
||||||
|
);
|
||||||
|
const opts = buildOpts();
|
||||||
|
|
||||||
|
await scrollToCommentFromQuery(url, opts);
|
||||||
|
|
||||||
|
expect(opts.setPanelMode).toHaveBeenCalledWith('edit');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('forces panel mode to "edit" even when transcribe mode is already on', async () => {
|
||||||
|
const url = new URL(
|
||||||
|
`https://app/documents/doc-1?commentId=${COMMENT_ID}&annotationId=${ANNOTATION_ID}`
|
||||||
|
);
|
||||||
|
const opts = buildOpts({ transcribeMode: true });
|
||||||
|
|
||||||
|
await scrollToCommentFromQuery(url, opts);
|
||||||
|
|
||||||
|
expect(opts.setPanelMode).toHaveBeenCalledWith('edit');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
export type DeepLinkScrollOptions = {
|
export type DeepLinkScrollOptions = {
|
||||||
transcribeMode: boolean;
|
transcribeMode: boolean;
|
||||||
setTranscribeMode: (value: boolean) => void;
|
setTranscribeMode: (value: boolean) => void;
|
||||||
|
setPanelMode: (mode: 'read' | 'edit') => void;
|
||||||
loadBlocks: () => Promise<void>;
|
loadBlocks: () => Promise<void>;
|
||||||
setActiveAnnotationId: (id: string) => void;
|
setActiveAnnotationId: (id: string) => void;
|
||||||
flashAnnotation: (annotationId: string) => void;
|
flashAnnotation: (annotationId: string) => void;
|
||||||
@@ -25,6 +26,11 @@ export async function scrollToCommentFromQuery(
|
|||||||
await opts.loadBlocks();
|
await opts.loadBlocks();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Comments only render in edit mode — force it so the deep-link target
|
||||||
|
// exists in the DOM even if the document already has reviewed transcriptions
|
||||||
|
// (which default the panel to read mode).
|
||||||
|
opts.setPanelMode('edit');
|
||||||
|
|
||||||
opts.setActiveAnnotationId(annotationId);
|
opts.setActiveAnnotationId(annotationId);
|
||||||
await opts.afterTick();
|
await opts.afterTick();
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,10 @@ let activeAnnotationId = $state<string | null>(null);
|
|||||||
let highlightBlockId = $state<string | null>(null);
|
let highlightBlockId = $state<string | null>(null);
|
||||||
let flashAnnotationId = $state<string | null>(null);
|
let flashAnnotationId = $state<string | null>(null);
|
||||||
let pdfStripExpanded = $state(false);
|
let pdfStripExpanded = $state(false);
|
||||||
|
// Flag set by the deep-link helper so the transcribe-mode $effect does not
|
||||||
|
// overwrite the panelMode it picked (e.g. forcing 'edit' on notification
|
||||||
|
// click-through). One-shot: consumed after the effect's loadBlocks resolves.
|
||||||
|
let skipInitialPanelMode = $state(false);
|
||||||
|
|
||||||
const prefersReducedMotion = $derived(
|
const prefersReducedMotion = $derived(
|
||||||
typeof window !== 'undefined' && window.matchMedia('(prefers-reduced-motion: reduce)').matches
|
typeof window !== 'undefined' && window.matchMedia('(prefers-reduced-motion: reduce)').matches
|
||||||
@@ -281,7 +285,11 @@ async function checkOcrStatus() {
|
|||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (transcribeMode) {
|
if (transcribeMode) {
|
||||||
loadTranscriptionBlocks().then(() => {
|
loadTranscriptionBlocks().then(() => {
|
||||||
panelMode = transcriptionBlocks.length > 0 ? 'read' : 'edit';
|
if (skipInitialPanelMode) {
|
||||||
|
skipInitialPanelMode = false;
|
||||||
|
} else {
|
||||||
|
panelMode = transcriptionBlocks.length > 0 ? 'read' : 'edit';
|
||||||
|
}
|
||||||
});
|
});
|
||||||
checkOcrStatus();
|
checkOcrStatus();
|
||||||
}
|
}
|
||||||
@@ -309,6 +317,10 @@ onMount(() => {
|
|||||||
scrollToCommentFromQuery(new URL(page.url), {
|
scrollToCommentFromQuery(new URL(page.url), {
|
||||||
transcribeMode,
|
transcribeMode,
|
||||||
setTranscribeMode: (v) => (transcribeMode = v),
|
setTranscribeMode: (v) => (transcribeMode = v),
|
||||||
|
setPanelMode: (m) => {
|
||||||
|
skipInitialPanelMode = true;
|
||||||
|
panelMode = m;
|
||||||
|
},
|
||||||
loadBlocks: loadTranscriptionBlocks,
|
loadBlocks: loadTranscriptionBlocks,
|
||||||
setActiveAnnotationId: (id) => (activeAnnotationId = id),
|
setActiveAnnotationId: (id) => (activeAnnotationId = id),
|
||||||
flashAnnotation: (annotationId) => {
|
flashAnnotation: (annotationId) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user