From 3fb1b5d80db40e1d81e8486370547cab5f3c94fb Mon Sep 17 00:00:00 2001 From: MechaCat02 Date: Sun, 19 Jul 2026 18:26:59 +0200 Subject: [PATCH] feat(comments): hide every comment mention when COMMENTS_ENABLED=false MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The kill-switch previously left comment UI/text visible in several places. Sweep the whole surface so a comments-off instance shows no trace: Frontend (main app): - VirtualFeed grid tile: gate the comment button/count on $commentsEnabled (was ungated — the only feed surface still showing it). - admin stats: hide the "Kommentare" count card. - UploadSheet "Uploads geschlossen" notice, host ban-modal description, and host/admin unban confirmations: drop the "…und kommentieren" wording. - export page: drop "Kommentaren" from the keepsake description. Keepsake export (had no concept of the flag): - export.rs: thread comments_enabled into the exported data (ViewerEvent), wired through spawn_export_jobs/recover_exports and their call sites (host.rs, main.rs). - export-viewer: gate comment counts (list + grid) and the lightbox comments section; older exports without the field default to enabled (?? true). Backend still 403s comment writes when disabled (unchanged) — this is the UI half so stale clients and archives match. Verified on the running stack (COMMENTS_ENABLED=false): /event reports comments_enabled=false, a regenerated keepsake embeds "comments_enabled": false with no comment UI, and the uploads-closed notice renders "…ansehen und liken." Co-Authored-By: Claude Opus 4.8 --- backend/src/handlers/host.rs | 3 + backend/src/main.rs | 1 + backend/src/services/export.rs | 13 +++ backend/static/export-viewer/index.html | 4 +- frontend/export-viewer/src/lib/types.ts | 3 + .../export-viewer/src/routes/+page.svelte | 100 ++++++++++-------- .../src/lib/components/UploadSheet.svelte | 6 +- .../src/lib/components/VirtualFeed.svelte | 44 ++++---- frontend/src/routes/admin/+page.svelte | 23 ++-- frontend/src/routes/export/+page.svelte | 4 +- frontend/src/routes/host/+page.svelte | 10 +- 11 files changed, 133 insertions(+), 78 deletions(-) diff --git a/backend/src/handlers/host.rs b/backend/src/handlers/host.rs index fcdc48a..f47f474 100644 --- a/backend/src/handlers/host.rs +++ b/backend/src/handlers/host.rs @@ -302,6 +302,7 @@ pub async fn rebuild_export( r.event_id, r.event_name, r.epoch, + state.config.comments_enabled, std::time::Duration::ZERO, state.pool.clone(), state.config.media_path.clone(), @@ -549,6 +550,7 @@ pub fn start_regen(state: &AppState, regen: crate::services::export::PendingRege regen.event_id, regen.event_name, regen.epoch, + state.config.comments_enabled, // Debounced: a takedown pass is a burst, and each request retires the last generation. The // delay lets superseded workers fail their claim and do zero work instead of each building // a full archive. See export::REGEN_DEBOUNCE. @@ -753,6 +755,7 @@ pub async fn release_gallery( event_id, event_name, epoch, + state.config.comments_enabled, std::time::Duration::ZERO, state.pool.clone(), state.config.media_path.clone(), diff --git a/backend/src/main.rs b/backend/src/main.rs index 7fad4ca..fd5b3a8 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -58,6 +58,7 @@ async fn main() -> Result<()> { pool.clone(), config.media_path.clone(), config.export_path.clone(), + config.comments_enabled, state.sse_tx.clone(), ) .await; diff --git a/backend/src/services/export.rs b/backend/src/services/export.rs index db8a0ca..850658f 100644 --- a/backend/src/services/export.rs +++ b/backend/src/services/export.rs @@ -53,6 +53,9 @@ struct ViewerData { struct ViewerEvent { name: String, exported_at: String, + // Mirrors the live COMMENTS_ENABLED flag so the offline keepsake hides all comment + // UI (buttons, counts, sections) when the feature was off for the event. + comments_enabled: bool, } #[derive(Serialize)] @@ -238,6 +241,7 @@ pub async fn recover_exports( pool: PgPool, media_path: PathBuf, export_path: PathBuf, + comments_enabled: bool, sse_tx: broadcast::Sender, ) { let rows = match sqlx::query_as::<_, (Uuid, String, i64)>( @@ -297,6 +301,7 @@ pub async fn recover_exports( event_id, event_name, epoch, + comments_enabled, Duration::ZERO, pool.clone(), media_path.clone(), @@ -403,6 +408,7 @@ pub fn spawn_export_jobs( event_id: Uuid, event_name: String, epoch: i64, + comments_enabled: bool, delay: Duration, pool: PgPool, media_path: PathBuf, @@ -439,6 +445,7 @@ pub fn spawn_export_jobs( event_id, epoch, &event_name2, + comments_enabled, &pool2, &media_path2, &export_path2, @@ -635,10 +642,12 @@ impl MediaSource { } } +#[allow(clippy::too_many_arguments)] async fn run_html_export( event_id: Uuid, epoch: i64, event_name: &str, + comments_enabled: bool, pool: &PgPool, media_path: &Path, export_path: &Path, @@ -653,6 +662,7 @@ async fn run_html_export( epoch, event_id, event_name, + comments_enabled, pool, media_path, export_path, @@ -672,10 +682,12 @@ async fn run_html_export( abandon_if_superseded("HTML", event_id, epoch, res) } +#[allow(clippy::too_many_arguments)] async fn run_html_export_inner( epoch: i64, event_id: Uuid, event_name: &str, + comments_enabled: bool, pool: &PgPool, media_path: &Path, export_path: &Path, @@ -882,6 +894,7 @@ async fn run_html_export_inner( event: ViewerEvent { name: event_name.to_string(), exported_at: Utc::now().to_rfc3339(), + comments_enabled, }, posts: viewer_posts, }; diff --git a/backend/static/export-viewer/index.html b/backend/static/export-viewer/index.html index 3208cbd..9251a67 100644 --- a/backend/static/export-viewer/index.html +++ b/backend/static/export-viewer/index.html @@ -4,8 +4,8 @@ EventSnap — Galerie - + diff --git a/frontend/export-viewer/src/lib/types.ts b/frontend/export-viewer/src/lib/types.ts index e7f4071..2728d2d 100644 --- a/frontend/export-viewer/src/lib/types.ts +++ b/frontend/export-viewer/src/lib/types.ts @@ -2,6 +2,9 @@ export interface ViewerData { event: { name: string; exported_at: string; + // Mirrors the live COMMENTS_ENABLED flag. Older exports predate this field, so + // treat a missing value as enabled (`?? true`) to preserve their comment UI. + comments_enabled?: boolean; }; posts: ViewerPost[]; } diff --git a/frontend/export-viewer/src/routes/+page.svelte b/frontend/export-viewer/src/routes/+page.svelte index 5450fc4..4ab0769 100644 --- a/frontend/export-viewer/src/routes/+page.svelte +++ b/frontend/export-viewer/src/routes/+page.svelte @@ -31,6 +31,9 @@ let posts = $derived(data?.posts ?? []); + // Mirror the live COMMENTS_ENABLED flag. Missing on older exports → treat as enabled. + let commentsEnabled = $derived(data?.event.comments_enabled ?? true); + let allTags = $derived.by(() => { // eslint-disable-next-line svelte/prefer-svelte-reactivity -- local throwaway counter inside a $derived.by; never stored in $state. const freq = new Map(); @@ -550,22 +553,24 @@ {post.likes} - - - - - {post.comments.length} - + {#if commentsEnabled} + + + + + {post.comments.length} + + {/if} @@ -641,17 +646,24 @@ {post.likes} - - - - - {post.comments.length} - + {#if commentsEnabled} + + + + + {post.comments.length} + + {/if} @@ -784,23 +796,25 @@ -
- {#if selectedPost.comments.length === 0} -

Keine Kommentare.

- {:else} -
- {#each selectedPost.comments as comment, i (i)} -
- {comment.author} - {comment.text} -
- {formatShortDate(comment.timestamp)} + {#if commentsEnabled} +
+ {#if selectedPost.comments.length === 0} +

Keine Kommentare.

+ {:else} +
+ {#each selectedPost.comments as comment, i (i)} +
+ {comment.author} + {comment.text} +
+ {formatShortDate(comment.timestamp)} +
-
- {/each} -
- {/if} -
+ {/each} +
+ {/if} +
+ {/if}
diff --git a/frontend/src/lib/components/UploadSheet.svelte b/frontend/src/lib/components/UploadSheet.svelte index 1624d39..17acc9e 100644 --- a/frontend/src/lib/components/UploadSheet.svelte +++ b/frontend/src/lib/components/UploadSheet.svelte @@ -6,6 +6,7 @@ import CameraCapture from '$lib/components/CameraCapture.svelte'; import type { PendingFile } from '$lib/pending-upload-store'; import { eventState, uploadsClosed } from '$lib/event-state-store'; + import { commentsEnabled } from '$lib/event-config-store'; // Uploads closed (event locked or gallery released) — show a lock notice instead of // the capture options, so a guest can't stage a photo that would just be rejected. @@ -163,8 +164,9 @@

Uploads geschlossen

- Der Host hat die Uploads für dieses Event beendet. Du kannst weiterhin Fotos ansehen, - liken und kommentieren. + Der Host hat die Uploads für dieses Event beendet. Du kannst weiterhin Fotos ansehen{$commentsEnabled + ? ', liken und kommentieren' + : ' und liken'}.

diff --git a/frontend/src/lib/components/VirtualFeed.svelte b/frontend/src/lib/components/VirtualFeed.svelte index 3616156..0c64199 100644 --- a/frontend/src/lib/components/VirtualFeed.svelte +++ b/frontend/src/lib/components/VirtualFeed.svelte @@ -22,6 +22,7 @@ import { browser } from '$app/environment'; import type { FeedUpload } from '$lib/types'; import { dataMode } from '$lib/data-mode-store'; + import { commentsEnabled } from '$lib/event-config-store'; import { longpress } from '$lib/actions/longpress'; import FeedListCard from './FeedListCard.svelte'; @@ -277,24 +278,31 @@ {upload.like_count} - + {#if $commentsEnabled} + + {/if} diff --git a/frontend/src/routes/admin/+page.svelte b/frontend/src/routes/admin/+page.svelte index 3808a0a..50b70be 100644 --- a/frontend/src/routes/admin/+page.svelte +++ b/frontend/src/routes/admin/+page.svelte @@ -8,7 +8,12 @@ import Modal from '$lib/components/Modal.svelte'; import IconButton from '$lib/components/IconButton.svelte'; import { PRESETS, DEFAULT_SEED, buildPaletteCss, type ThemeConfig } from '$lib/theme/palette'; - import { previewTheme, PALETTE_CACHE_KEY, loadEventConfig } from '$lib/event-config-store'; + import { + previewTheme, + PALETTE_CACHE_KEY, + loadEventConfig, + commentsEnabled + } from '$lib/event-config-store'; import { onDestroy } from 'svelte'; interface StatsDto { @@ -644,12 +649,14 @@

Uploads

-
-

- {stats.comment_count} -

-

Kommentare

-
+ {#if $commentsEnabled} +
+

+ {stats.comment_count} +

+

Kommentare

+
+ {/if}

{diskPct(stats)} % @@ -1053,7 +1060,7 @@ onclick={() => (confirmAction = { title: 'Sperre aufheben?', - message: `${user.display_name} kann danach wieder hochladen, liken und kommentieren.`, + message: `${user.display_name} kann danach wieder hochladen${$commentsEnabled ? ', liken und kommentieren' : ' und liken'}.`, confirmLabel: 'Entsperren', tone: 'default', run: () => unban(user) diff --git a/frontend/src/routes/export/+page.svelte b/frontend/src/routes/export/+page.svelte index 4d3b628..fa40682 100644 --- a/frontend/src/routes/export/+page.svelte +++ b/frontend/src/routes/export/+page.svelte @@ -7,6 +7,7 @@ import { toastError } from '$lib/toast-store'; import { focusTrap } from '$lib/actions/focus-trap'; import IconButton from '$lib/components/IconButton.svelte'; + import { commentsEnabled } from '$lib/event-config-store'; interface JobStatus { status: 'locked' | 'pending' | 'running' | 'done' | 'failed'; @@ -266,7 +267,8 @@

HTML-Viewer

- Schöne Offline-Galerie mit Filterung, Kommentaren und Likes — kein Internet nötig. + Schöne Offline-Galerie mit Filterung{$commentsEnabled ? ', Kommentaren' : ''} und Likes + — kein Internet nötig.