fix(transcription): always show comment list, compose box on demand
Some checks failed
CI / Unit & Component Tests (push) Has been cancelled
CI / Backend Unit Tests (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
CI / Unit & Component Tests (pull_request) Has been cancelled
CI / Backend Unit Tests (pull_request) Has been cancelled
CI / E2E Tests (pull_request) Has been cancelled
Some checks failed
CI / Unit & Component Tests (push) Has been cancelled
CI / Backend Unit Tests (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
CI / Unit & Component Tests (pull_request) Has been cancelled
CI / Backend Unit Tests (pull_request) Has been cancelled
CI / E2E Tests (pull_request) Has been cancelled
Comments were only visible after clicking "Kommentieren". Now: - Comment list always renders (CommentThread with loadOnMount=true) - Compose box hidden by default (showCompose prop on CommentThread) - Clicking "Kommentieren" sets commentOpen=true → shows compose box - Closing hides compose box but comments remain visible This separates "viewing comments" (always) from "writing a comment" (on demand via Kommentieren button). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -17,6 +17,7 @@ type Props = {
|
|||||||
canAdmin: boolean;
|
canAdmin: boolean;
|
||||||
targetCommentId?: string | null;
|
targetCommentId?: string | null;
|
||||||
quotedText?: string | null;
|
quotedText?: string | null;
|
||||||
|
showCompose?: boolean;
|
||||||
onCountChange?: (count: number) => void;
|
onCountChange?: (count: number) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -31,6 +32,7 @@ let {
|
|||||||
canAdmin,
|
canAdmin,
|
||||||
targetCommentId = null,
|
targetCommentId = null,
|
||||||
quotedText = null,
|
quotedText = null,
|
||||||
|
showCompose = true,
|
||||||
onCountChange
|
onCountChange
|
||||||
}: Props = $props();
|
}: Props = $props();
|
||||||
|
|
||||||
@@ -357,7 +359,7 @@ onMount(async () => {
|
|||||||
{/each}
|
{/each}
|
||||||
|
|
||||||
<!-- New top-level comment -->
|
<!-- New top-level comment -->
|
||||||
{#if canComment}
|
{#if canComment && showCompose}
|
||||||
<div class={comments.length > 0 ? 'border-t border-line pt-4' : ''}>
|
<div class={comments.length > 0 ? 'border-t border-line pt-4' : ''}>
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
<MentionEditor
|
<MentionEditor
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ type Props = {
|
|||||||
saveState: SaveState;
|
saveState: SaveState;
|
||||||
canComment: boolean;
|
canComment: boolean;
|
||||||
currentUserId: string | null;
|
currentUserId: string | null;
|
||||||
commentCount: number;
|
|
||||||
onTextChange: (text: string) => void;
|
onTextChange: (text: string) => void;
|
||||||
onFocus: () => void;
|
onFocus: () => void;
|
||||||
onDeleteClick: () => void;
|
onDeleteClick: () => void;
|
||||||
@@ -31,7 +30,6 @@ let {
|
|||||||
saveState,
|
saveState,
|
||||||
canComment,
|
canComment,
|
||||||
currentUserId,
|
currentUserId,
|
||||||
commentCount,
|
|
||||||
onTextChange,
|
onTextChange,
|
||||||
onFocus,
|
onFocus,
|
||||||
onDeleteClick,
|
onDeleteClick,
|
||||||
@@ -39,7 +37,7 @@ let {
|
|||||||
}: Props = $props();
|
}: Props = $props();
|
||||||
|
|
||||||
let localText = $state(text);
|
let localText = $state(text);
|
||||||
let commentOpen = $state(commentCount > 0);
|
let commentOpen = $state(false);
|
||||||
let selectedQuote = $state<string | null>(null);
|
let selectedQuote = $state<string | null>(null);
|
||||||
let textareaEl = $state<HTMLTextAreaElement | null>(null);
|
let textareaEl = $state<HTMLTextAreaElement | null>(null);
|
||||||
|
|
||||||
@@ -199,34 +197,18 @@ function captureSelectionAndOpenComments() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Comment thread (expandable) -->
|
<!-- Comment thread — list always visible, compose toggled by Kommentieren -->
|
||||||
{#if commentOpen}
|
<div class="mt-3 border-t border-line pt-3">
|
||||||
<div class="mt-3 border-t border-line pt-3">
|
<CommentThread
|
||||||
<div class="mb-2 flex items-center justify-between">
|
documentId={documentId}
|
||||||
<span class="font-sans text-xs font-semibold text-ink-2">
|
blockId={blockId}
|
||||||
{m.comment_section_title()}
|
loadOnMount={true}
|
||||||
</span>
|
canComment={canComment}
|
||||||
<button
|
currentUserId={currentUserId}
|
||||||
type="button"
|
canAdmin={false}
|
||||||
class="font-sans text-xs text-ink-3 transition-colors hover:text-ink"
|
quotedText={selectedQuote}
|
||||||
onclick={() => {
|
showCompose={commentOpen}
|
||||||
commentOpen = false;
|
/>
|
||||||
selectedQuote = null;
|
</div>
|
||||||
}}
|
|
||||||
>
|
|
||||||
{m.comment_panel_close()}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<CommentThread
|
|
||||||
documentId={documentId}
|
|
||||||
blockId={blockId}
|
|
||||||
loadOnMount={true}
|
|
||||||
canComment={canComment}
|
|
||||||
currentUserId={currentUserId}
|
|
||||||
canAdmin={false}
|
|
||||||
quotedText={selectedQuote}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ function renderBlock(overrides: Record<string, unknown> = {}) {
|
|||||||
saveState: 'idle' as const,
|
saveState: 'idle' as const,
|
||||||
canComment: true,
|
canComment: true,
|
||||||
currentUserId: 'user-1',
|
currentUserId: 'user-1',
|
||||||
commentCount: 0,
|
|
||||||
onTextChange: vi.fn(),
|
onTextChange: vi.fn(),
|
||||||
onFocus: vi.fn(),
|
onFocus: vi.fn(),
|
||||||
onDeleteClick: vi.fn(),
|
onDeleteClick: vi.fn(),
|
||||||
|
|||||||
@@ -30,34 +30,9 @@ let activeBlockId: string | null = $state(null);
|
|||||||
let saveStates = new SvelteMap<string, SaveState>();
|
let saveStates = new SvelteMap<string, SaveState>();
|
||||||
let debounceTimers = new SvelteMap<string, ReturnType<typeof setTimeout>>();
|
let debounceTimers = new SvelteMap<string, ReturnType<typeof setTimeout>>();
|
||||||
let pendingTexts = new SvelteMap<string, string>();
|
let pendingTexts = new SvelteMap<string, string>();
|
||||||
let commentCounts = new SvelteMap<string, number>();
|
|
||||||
|
|
||||||
let sortedBlocks = $derived([...blocks].sort((a, b) => a.sortOrder - b.sortOrder));
|
let sortedBlocks = $derived([...blocks].sort((a, b) => a.sortOrder - b.sortOrder));
|
||||||
let hasBlocks = $derived(blocks.length > 0);
|
let hasBlocks = $derived(blocks.length > 0);
|
||||||
|
|
||||||
async function loadCommentCounts() {
|
|
||||||
for (const block of blocks) {
|
|
||||||
try {
|
|
||||||
const res = await fetch(
|
|
||||||
`/api/documents/${documentId}/transcription-blocks/${block.id}/comments`
|
|
||||||
);
|
|
||||||
if (res.ok) {
|
|
||||||
const comments = (await res.json()) as Array<{ replies: unknown[] }>;
|
|
||||||
const total = comments.reduce((s, c) => s + 1 + (c.replies?.length ?? 0), 0);
|
|
||||||
commentCounts.set(block.id, total);
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$effect(() => {
|
|
||||||
if (blocks.length > 0) {
|
|
||||||
loadCommentCounts();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function getSaveState(blockId: string): SaveState {
|
function getSaveState(blockId: string): SaveState {
|
||||||
return saveStates.get(blockId) ?? 'idle';
|
return saveStates.get(blockId) ?? 'idle';
|
||||||
}
|
}
|
||||||
@@ -191,7 +166,6 @@ $effect(() => {
|
|||||||
saveState={getSaveState(block.id)}
|
saveState={getSaveState(block.id)}
|
||||||
canComment={canComment}
|
canComment={canComment}
|
||||||
currentUserId={currentUserId}
|
currentUserId={currentUserId}
|
||||||
commentCount={commentCounts.get(block.id) ?? 0}
|
|
||||||
onTextChange={(text) => handleTextChange(block.id, text)}
|
onTextChange={(text) => handleTextChange(block.id, text)}
|
||||||
onFocus={() => handleFocus(block.id)}
|
onFocus={() => handleFocus(block.id)}
|
||||||
onDeleteClick={() => handleDelete(block.id)}
|
onDeleteClick={() => handleDelete(block.id)}
|
||||||
|
|||||||
Reference in New Issue
Block a user