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
|
// 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
|
// 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.
|
// 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>(
|
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)
|
.bind(upload_id)
|
||||||
.fetch_one(&state.pool)
|
.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
|
// 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
|
// 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.
|
// 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>(
|
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)
|
.bind(upload_id)
|
||||||
.fetch_one(&state.pool)
|
.fetch_one(&state.pool)
|
||||||
|
|||||||
Reference in New Issue
Block a user