feat(comments): add CommentThread, annotation panel, Diskussion section, and i18n keys
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,16 +1,24 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { SvelteMap } from 'svelte/reactivity';
|
||||
import type { PDFDocumentProxy, PDFPageProxy, RenderTask } from 'pdfjs-dist';
|
||||
import AnnotationLayer from './AnnotationLayer.svelte';
|
||||
import AnnotationCommentPanel from './AnnotationCommentPanel.svelte';
|
||||
|
||||
let {
|
||||
url,
|
||||
documentId = '',
|
||||
canAnnotate = false
|
||||
canAnnotate = false,
|
||||
canComment,
|
||||
currentUserId,
|
||||
canAdmin
|
||||
}: {
|
||||
url: string;
|
||||
documentId?: string;
|
||||
canAnnotate?: boolean;
|
||||
canComment?: boolean;
|
||||
currentUserId?: string | null;
|
||||
canAdmin?: boolean;
|
||||
} = $props();
|
||||
|
||||
let pdfDoc = $state<PDFDocumentProxy | null>(null);
|
||||
@@ -48,6 +56,8 @@ type Annotation = {
|
||||
let annotations = $state<Annotation[]>([]);
|
||||
let annotateMode = $state(false);
|
||||
let annotateColor = $state('#ffff00');
|
||||
let commentCounts = new SvelteMap<string, number>();
|
||||
let activeAnnotationId = $state<string | null>(null);
|
||||
|
||||
onMount(async () => {
|
||||
// Dynamic import keeps pdfjs out of the SSR bundle entirely
|
||||
@@ -159,11 +169,31 @@ async function prerender(doc: PDFDocumentProxy, pageNum: number) {
|
||||
}
|
||||
}
|
||||
|
||||
async function loadCommentCounts(docId: string, anns: Annotation[]) {
|
||||
await Promise.all(
|
||||
anns.map(async (a) => {
|
||||
try {
|
||||
const res = await fetch(`/api/documents/${docId}/annotations/${a.id}/comments`);
|
||||
if (res.ok) {
|
||||
const threads = (await res.json()) as Array<{ replies: unknown[] }>;
|
||||
const total = threads.reduce((sum, t) => sum + 1 + t.replies.length, 0);
|
||||
commentCounts.set(a.id, total);
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
async function loadAnnotations(docId: string) {
|
||||
if (!docId) return;
|
||||
try {
|
||||
const res = await fetch(`/api/documents/${docId}/annotations`);
|
||||
if (res.ok) annotations = await res.json();
|
||||
if (res.ok) {
|
||||
annotations = await res.json();
|
||||
await loadCommentCounts(docId, annotations);
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
@@ -413,10 +443,26 @@ function zoomOut() {
|
||||
color={annotateColor}
|
||||
onDraw={handleAnnotationDraw}
|
||||
onDelete={handleAnnotationDelete}
|
||||
commentCounts={Object.fromEntries(commentCounts)}
|
||||
onAnnotationClick={(id) => (activeAnnotationId = id)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if activeAnnotationId}
|
||||
<AnnotationCommentPanel
|
||||
documentId={documentId}
|
||||
annotationId={activeAnnotationId}
|
||||
canComment={canComment ?? false}
|
||||
currentUserId={currentUserId ?? null}
|
||||
canAdmin={canAdmin ?? false}
|
||||
onClose={() => (activeAnnotationId = null)}
|
||||
onCountChange={(count) => {
|
||||
if (activeAnnotationId) commentCounts.set(activeAnnotationId, count);
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
Reference in New Issue
Block a user