feat(document): add server-computed hasTranscription to detail payload (#697)
getDocumentById now populates a transient hasTranscription boolean so the document detail page can gate the transcription entry control at first paint (no client store, no full block fetch, no layout shift). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -177,6 +177,13 @@ public class Document {
|
|||||||
@Builder.Default
|
@Builder.Default
|
||||||
private Set<TrainingLabel> trainingLabels = new HashSet<>();
|
private Set<TrainingLabel> trainingLabels = new HashSet<>();
|
||||||
|
|
||||||
|
// Not persisted — computed per detail fetch so read-only users can tell at first
|
||||||
|
// paint whether there is a transcription to read (DocumentService.getDocumentById).
|
||||||
|
@Transient
|
||||||
|
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
|
@Builder.Default
|
||||||
|
private boolean hasTranscription = false;
|
||||||
|
|
||||||
// The `?v={thumbnailGeneratedAt}` cache-buster is load-bearing: the thumbnail
|
// The `?v={thumbnailGeneratedAt}` cache-buster is load-bearing: the thumbnail
|
||||||
// endpoint sends `Cache-Control: private, max-age=31536000, immutable`
|
// endpoint sends `Cache-Control: private, max-age=31536000, immutable`
|
||||||
// (DocumentController.getDocumentThumbnail). `immutable` is only safe because
|
// (DocumentController.getDocumentThumbnail). `immutable` is only safe because
|
||||||
|
|||||||
@@ -943,6 +943,7 @@ public class DocumentService {
|
|||||||
Document doc = documentRepository.findById(id)
|
Document doc = documentRepository.findById(id)
|
||||||
.orElseThrow(() -> DomainException.notFound(ErrorCode.DOCUMENT_NOT_FOUND, "Document not found: " + id));
|
.orElseThrow(() -> DomainException.notFound(ErrorCode.DOCUMENT_NOT_FOUND, "Document not found: " + id));
|
||||||
tagService.resolveEffectiveColors(doc.getTags());
|
tagService.resolveEffectiveColors(doc.getTags());
|
||||||
|
doc.setHasTranscription(transcriptionBlockQueryService.hasBlocks(id));
|
||||||
return doc;
|
return doc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -118,6 +118,26 @@ class DocumentServiceTest {
|
|||||||
assertThat(documentService.getDocumentById(id)).isEqualTo(doc);
|
assertThat(documentService.getDocumentById(id)).isEqualTo(doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getDocumentById_setsHasTranscriptionTrue_whenBlocksExist() {
|
||||||
|
UUID id = UUID.randomUUID();
|
||||||
|
Document doc = Document.builder().id(id).title("Test").build();
|
||||||
|
when(documentRepository.findById(id)).thenReturn(Optional.of(doc));
|
||||||
|
when(transcriptionBlockQueryService.hasBlocks(id)).thenReturn(true);
|
||||||
|
|
||||||
|
assertThat(documentService.getDocumentById(id).isHasTranscription()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getDocumentById_setsHasTranscriptionFalse_whenNoBlocksExist() {
|
||||||
|
UUID id = UUID.randomUUID();
|
||||||
|
Document doc = Document.builder().id(id).title("Test").build();
|
||||||
|
when(documentRepository.findById(id)).thenReturn(Optional.of(doc));
|
||||||
|
when(transcriptionBlockQueryService.hasBlocks(id)).thenReturn(false);
|
||||||
|
|
||||||
|
assertThat(documentService.getDocumentById(id).isHasTranscription()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
// ─── updateDocument ───────────────────────────────────────────────────────
|
// ─── updateDocument ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user