fix(social): make the counts clients patch with ban-aware, like the view
Migration 028 added `NOT is_banned` to v_feed.like_count and v_feed.comment_count, but not to the two scalar counts in social.rs — which are returned in the response AND broadcast over SSE, and which clients use to patch a card in place rather than refetching. So the two disagreed the moment anyone was banned: the host bans a guest, the feed correctly drops to the lower number, and the very next like on that photo pushes the unfiltered count back to every open client — including the host's, who is watching that number to confirm the ban took. It stayed wrong until a full page-1 refetch. Both call sites carried comments asserting they mirror the view. 028 made those comments false without touching them; this makes them true again.
This commit is contained in:
@@ -104,8 +104,17 @@ pub async fn toggle_like(
|
||||
// itself is already committed, so a failed count must not fail the request — but we
|
||||
// also must NOT broadcast/return a bogus 0 (that would push like_count: 0 to every
|
||||
// client until the next event). On error we skip the broadcast and return null.
|
||||
// The `NOT u.is_banned` join is what makes "mirrors v_feed.like_count" true. Migration 028
|
||||
// added it to the view and not here, so the two disagreed the moment anyone was banned: the
|
||||
// host bans a guest, the feed correctly drops to the lower number, and then the very next like
|
||||
// on that photo broadcasts the UNFILTERED count back to every open client — including the
|
||||
// host's, who is watching that number to confirm the ban took. It stayed wrong until a full
|
||||
// page-1 refetch. `like.user_id` is NOT NULL REFERENCES "user"(id), so the inner join can
|
||||
// neither drop nor duplicate a row.
|
||||
let like_count = sqlx::query_scalar::<_, i64>(
|
||||
"SELECT COUNT(DISTINCT user_id) FROM \"like\" WHERE upload_id = $1",
|
||||
"SELECT COUNT(DISTINCT l.user_id) FROM \"like\" l \
|
||||
JOIN \"user\" u ON u.id = l.user_id \
|
||||
WHERE l.upload_id = $1 AND NOT u.is_banned",
|
||||
)
|
||||
.bind(upload_id)
|
||||
.fetch_one(&state.pool)
|
||||
@@ -222,8 +231,13 @@ pub async fn add_comment(
|
||||
// over the same deleted_at filter is identical since comment.id is the PK). The
|
||||
// count + broadcast are a UI optimisation — the comment is already committed, so a
|
||||
// failure here must not fail the request. Swallow the error and skip the broadcast.
|
||||
// `NOT u.is_banned` for the same reason as `like_count` above — see that comment. Migration
|
||||
// 028 put this filter in `v_feed.comment_count` and `Comment::list_for_upload`, but not here,
|
||||
// so posting a comment pushed the pre-ban total back to every client.
|
||||
if let Ok(comment_count) = sqlx::query_scalar::<_, i64>(
|
||||
"SELECT COUNT(*) FROM comment WHERE upload_id = $1 AND deleted_at IS NULL",
|
||||
"SELECT COUNT(*) FROM comment c \
|
||||
JOIN \"user\" u ON u.id = c.user_id \
|
||||
WHERE c.upload_id = $1 AND c.deleted_at IS NULL AND NOT u.is_banned",
|
||||
)
|
||||
.bind(upload_id)
|
||||
.fetch_one(&state.pool)
|
||||
|
||||
Reference in New Issue
Block a user