simple-web-app

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

commit a2f3e8c23e385234e7173baef4e8fc443a107c4b
parent 4f6a4d78d497583e07040969a4e3b1cf47b92539
Author: Silas Brack <silasbrack@gmail.com>
Date:   Sun, 14 Jun 2026 14:50:11 +0200

fix: update score display when voting

Vote SSE response now sends two patch events — one for the vote
button and one for the score span. Previously only the button
updated, leaving the score unchanged.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

Diffstat:
Msrc/handlers/vote.rs | 16+++++++++++-----
Msrc/sse.rs | 22++++++++++++++++++++++
2 files changed, 33 insertions(+), 5 deletions(-)

diff --git a/src/handlers/vote.rs b/src/handlers/vote.rs @@ -11,7 +11,7 @@ use crate::sse; use crate::state::AppState; use crate::templates::VoteButtonTemplate; -fn render_vote_button( +fn render_vote_response( target_type: &str, target_id: i64, score: i64, @@ -23,9 +23,15 @@ fn render_vote_button( score, voted, }; + let button_html = template.render()?; - let html = template.render()?; - Ok(sse::patch_elements(&html)) + let score_class = if target_type == "comment" { "comment-score" } else { "story-score" }; + let score_html = format!( + "<span class=\"{score_class}\" id=\"score-{target_type}-{target_id}\">{score} point{}</span>", + if score == 1 { "" } else { "s" } + ); + + Ok(sse::patch_multiple(&[&button_html, &score_html])) } /// Vote on a story (Datastar endpoint) @@ -39,7 +45,7 @@ pub async fn vote_story( let (voted, new_score) = state.db().toggle_vote(user_id, TARGET_TYPE_STORY, story_id).await?; - render_vote_button("story", story_id, new_score, voted) + render_vote_response("story", story_id, new_score, voted) } /// Vote on a comment (Datastar endpoint) @@ -53,5 +59,5 @@ pub async fn vote_comment( let (voted, new_score) = state.db().toggle_vote(user_id, TARGET_TYPE_COMMENT, comment_id).await?; - render_vote_button("comment", comment_id, new_score, voted) + render_vote_response("comment", comment_id, new_score, voted) } diff --git a/src/sse.rs b/src/sse.rs @@ -4,6 +4,28 @@ pub fn patch_elements(html: &str) -> Response { patch_elements_with_options(html, None, None) } +/// Send multiple patch events in a single SSE response. +pub fn patch_multiple(fragments: &[&str]) -> Response { + let mut events = Vec::new(); + for html in fragments { + let mut data_lines = Vec::new(); + for line in html.lines() { + data_lines.push(format!("data: elements {line}")); + } + events.push(format!("event: datastar-patch-elements\n{}", data_lines.join("\n"))); + } + + let body = format!("{}\n\n", events.join("\n\n")); + + Response::builder() + .status(StatusCode::OK) + .header(header::CONTENT_TYPE, "text/event-stream") + .header(header::CACHE_CONTROL, "no-cache") + .header("X-Accel-Buffering", "no") + .body(Body::from(body)) + .unwrap() +} + fn patch_elements_with_options( html: &str, selector: Option<&str>,