{"release_id":"01a1c502-4ad1-0000-ec62-adc54d3c69aa","chunk_id":"01a1c502-4c2e-7813-95d8-04e585b1264e","file":"D9tq_KxS.mjs","mappings":"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","names":["entryId","endpoint","base","$.derived","MAX","comments","$.state","$.proxy","$$props","hasMore","count","body","posting","loading","error","remaining","$.get","canPost","fmtAge","d","t","name","c","profileHref","nick","post","$.set","res","json","loadMore","remove","id","prev","section","root","h2","span","root_1","$$render","consequent","div","root_2","textarea","$.child","$.remove_textarea_child","$.set_attribute","div_1","$.sibling","span_1","button","text_2","$.reset","p","root_3","consequent_1","$$value","$.delegated","p_1","root_4","p_2","root_5","div_2","$.first_child","fragment","$$anchor","div_3","root_7","$0","getAvatarUrl","$2","$3","DuelAvatar","node_4","div_4","div_5","a","root_8","text_4","$.append","d_1","span_2","root_9","$.template_effect","$.set_text","text_5","span_3","node_5","button_1","root_10","consequent_5","p_3","root_11","p_4","root_12","text_7","consequent_6","alternate_2","button_2","root_13","text_8","consequent_7","consequent_3","alternate_3"],"sources":["../../../../../../src/lib/components/contest/ContestComments.svelte"],"sourcesContent":["<script lang=\"ts\">\n\timport DuelAvatar from '$lib/components/duel/DuelAvatar.svelte';\n\timport { getAvatarUrl } from '$lib/avatar';\n\timport type { ContestComment } from '$lib/types/contest';\n\n\tinterface Props {\n\t\t/** photo entry id — resolves the default endpoint */\n\t\tentryId?: string;\n\t\t/** comments api base (GET/POST list, DELETE `${endpoint}/${id}`). Defaults\n\t\t *  to the photo entry's; the rater landing passes its own. */\n\t\tendpoint?: string;\n\t\tisLoggedIn: boolean;\n\t\tcurrentUserId: string | null;\n\t\tinitialComments: ContestComment[];\n\t\tinitialHasMore: boolean;\n\t\tcommentCount: number;\n\t}\n\n\tlet {\n\t\tentryId = '',\n\t\tendpoint = '',\n\t\tisLoggedIn,\n\t\tcurrentUserId,\n\t\tinitialComments,\n\t\tinitialHasMore,\n\t\tcommentCount\n\t}: Props = $props();\n\tconst base = $derived(endpoint || `/api/contest/e/${entryId}/comments`);\n\n\tconst MAX = 500;\n\tlet comments = $state<ContestComment[]>([...initialComments]);\n\tlet hasMore = $state(initialHasMore);\n\tlet count = $state(commentCount);\n\tlet body = $state('');\n\tlet posting = $state(false);\n\tlet loading = $state(false);\n\tlet error = $state<string | null>(null);\n\n\tconst remaining = $derived(MAX - body.length);\n\tconst canPost = $derived(isLoggedIn && body.trim().length > 0 && !posting);\n\n\tfunction fmtAge(d: string | Date): string {\n\t\tconst t = new Date(d).getTime();\n\t\tconst s = Math.floor((Date.now() - t) / 1000);\n\t\tif (s < 60) return 'now';\n\t\tif (s < 3600) return `${Math.floor(s / 60)}m`;\n\t\tif (s < 86400) return `${Math.floor(s / 3600)}h`;\n\t\treturn `${Math.floor(s / 86400)}d`;\n\t}\n\n\tconst name = (c: ContestComment) => c.liveNickname || c.nickname || '[deleted]';\n\t// Commenter's profile link — null when there's no usable nickname (deleted/anon).\n\tconst profileHref = (c: ContestComment) => {\n\t\tconst nick = c.liveNickname || c.nickname;\n\t\treturn nick ? `/u/${encodeURIComponent(nick)}` : null;\n\t};\n\n\tasync function post() {\n\t\tif (!canPost) return;\n\t\tposting = true;\n\t\terror = null;\n\t\ttry {\n\t\t\tconst res = await fetch(`${base}`, {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: { 'Content-Type': 'application/json' },\n\t\t\t\tbody: JSON.stringify({ body: body.trim() })\n\t\t\t});\n\t\t\tconst json = await res.json();\n\t\t\tif (!res.ok) {\n\t\t\t\terror =\n\t\t\t\t\tjson.error === 'rate_limited'\n\t\t\t\t\t\t? 'slow down a sec.'\n\t\t\t\t\t\t: json.error === 'too_long'\n\t\t\t\t\t\t\t? 'too long.'\n\t\t\t\t\t\t\t: 'could not post.';\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcomments = [json as ContestComment, ...comments];\n\t\t\tcount += 1;\n\t\t\tbody = '';\n\t\t} catch {\n\t\t\terror = 'network error.';\n\t\t} finally {\n\t\t\tposting = false;\n\t\t}\n\t}\n\n\tasync function loadMore() {\n\t\tif (loading || !hasMore) return;\n\t\tloading = true;\n\t\ttry {\n\t\t\tconst res = await fetch(`${base}?offset=${comments.length}&limit=20`);\n\t\t\tconst json = await res.json();\n\t\t\tcomments = [...comments, ...(json.items as ContestComment[])];\n\t\t\thasMore = json.hasMore;\n\t\t} catch {\n\t\t\thasMore = false;\n\t\t} finally {\n\t\t\tloading = false;\n\t\t}\n\t}\n\n\tasync function remove(id: string) {\n\t\tconst prev = comments;\n\t\tcomments = comments.filter((c) => c.id !== id);\n\t\tcount = Math.max(0, count - 1);\n\t\tconst res = await fetch(`${base}/${id}`, { method: 'DELETE' });\n\t\tif (!res.ok) {\n\t\t\tcomments = prev; // rollback\n\t\t\tcount += 1;\n\t\t}\n\t}\n</script>\n\n<section>\n\t<h2 class=\"mb-4 text-[11px] font-semibold tracking-[0.22em] text-text uppercase\">\n\t\tcomments {#if count > 0}<span class=\"text-muted\">· {count}</span>{/if}\n\t</h2>\n\n\t{#if isLoggedIn}\n\t\t<div class=\"mb-6\">\n\t\t\t<textarea\n\t\t\t\tbind:value={body}\n\t\t\t\tmaxlength={MAX}\n\t\t\t\trows=\"2\"\n\t\t\t\tplaceholder=\"weigh in. be cruel. be funny. be honest.\"\n\t\t\t\tclass=\"w-full resize-none rounded-lg border border-border bg-surface px-3 py-2.5 text-sm text-text placeholder:text-muted/40 focus:border-muted focus:outline-none\"\n\t\t\t></textarea>\n\t\t\t<div class=\"mt-2 flex items-center justify-between\">\n\t\t\t\t<span class=\"text-[10px] text-muted/40\">{remaining} left</span>\n\t\t\t\t<button\n\t\t\t\t\tonclick={post}\n\t\t\t\t\tdisabled={!canPost}\n\t\t\t\t\tclass=\"rounded-md bg-coral px-4 py-1.5 text-[13px] font-semibold text-white transition-colors hover:bg-coral-dark disabled:opacity-40\"\n\t\t\t\t>\n\t\t\t\t\t{posting ? 'posting…' : 'post'}\n\t\t\t\t</button>\n\t\t\t</div>\n\t\t\t{#if error}<p class=\"mt-1 text-[12px] text-coral\">{error}</p>{/if}\n\t\t</div>\n\t{:else}\n\t\t<p class=\"mb-6 text-[13px] text-muted\">\n\t\t\t<a href=\"/auth/sign-in\" class=\"font-semibold text-coral underline\">log in</a> to comment.\n\t\t</p>\n\t{/if}\n\n\t{#if comments.length === 0}\n\t\t<p class=\"py-6 text-[13px] text-muted/50\">no comments yet. first one's free.</p>\n\t{:else}\n\t\t<div class=\"flex flex-col gap-4\">\n\t\t\t{#each comments as c (c.id)}\n\t\t\t\t<div class=\"flex gap-3\">\n\t\t\t\t\t<DuelAvatar\n\t\t\t\t\t\tnickname={c.liveNickname || c.nickname}\n\t\t\t\t\t\tavatarUrl={getAvatarUrl(c.avatarPath)}\n\t\t\t\t\t\tsize={32}\n\t\t\t\t\t\thref={profileHref(c)}\n\t\t\t\t\t\tlink={!!profileHref(c)}\n\t\t\t\t\t/>\n\t\t\t\t\t<div class=\"min-w-0 flex-1\">\n\t\t\t\t\t\t<div class=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t{#if profileHref(c)}\n\t\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\t\thref={profileHref(c)}\n\t\t\t\t\t\t\t\t\tclass=\"font-display text-[13px] font-semibold text-text no-underline transition-colors hover:text-coral\"\n\t\t\t\t\t\t\t\t\t>{name(c)}</a\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t<span class=\"font-display text-[13px] font-semibold text-text\">{name(c)}</span>\n\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t<span class=\"text-[10px] text-muted/40\">{fmtAge(c.createdAt)}</span>\n\t\t\t\t\t\t\t{#if currentUserId && c.userId === currentUserId}\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\tonclick={() => remove(c.id)}\n\t\t\t\t\t\t\t\t\tclass=\"ml-auto text-[10px] text-muted/40 transition-colors hover:text-coral\"\n\t\t\t\t\t\t\t\t\t>delete</button\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t{#if c.moderationStatus === 'hidden'}\n\t\t\t\t\t\t\t<p class=\"mt-0.5 text-[13px] text-muted/40 italic\">[message removed by moderation]</p>\n\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t<p class=\"mt-0.5 text-[13px] break-words whitespace-pre-wrap text-muted\">{c.body}</p>\n\t\t\t\t\t\t{/if}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t{/each}\n\t\t</div>\n\n\t\t{#if hasMore}\n\t\t\t<button\n\t\t\t\tonclick={loadMore}\n\t\t\t\tdisabled={loading}\n\t\t\t\tclass=\"mt-5 w-full rounded-lg border border-border/50 bg-surface/30 py-2.5 text-[12px] font-semibold text-muted transition-all hover:border-border hover:text-text disabled:opacity-50\"\n\t\t\t>\n\t\t\t\t{loading ? 'loading…' : 'show more'}\n\t\t\t</button>\n\t\t{/if}\n\t{/if}\n</section>\n"],"version":3}