commit 7b5699384059ded1d87419a226c3ab577988bc30
parent e0e3d681a922eb0b6d85130a144e150eae226f34
Author: Silas Brack <silasbrack@gmail.com>
Date: Wed, 10 Jun 2026 22:40:30 +0200
refactor: consolidate comments fragment into story handler
Sub-route via ?fragment=comments query param instead of a separate
/story/{id}/comments endpoint, keeping the story page as one logical unit.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Diffstat:
3 files changed, 19 insertions(+), 25 deletions(-)
diff --git a/src/handlers/story.rs b/src/handlers/story.rs
@@ -1,6 +1,6 @@
use askama::Template;
use axum::{
- extract::{Path, State},
+ extract::{Path, Query, State},
response::{Html, Redirect},
Form,
};
@@ -68,10 +68,16 @@ async fn fetch_enriched_comments(
Ok(enriched)
}
+#[derive(Debug, Deserialize)]
+pub struct StoryQuery {
+ pub fragment: Option<String>,
+}
+
pub async fn show_story(
State(state): State<AppState>,
jar: CookieJar,
Path(id): Path<i64>,
+ Query(query): Query<StoryQuery>,
) -> Result<Html<String>, AppError> {
let tokens = get_tokens_from_cookies(&jar);
let client = match &tokens {
@@ -79,6 +85,17 @@ pub async fn show_story(
None => trailbase::new_client(&state.trailbase_url)?,
};
+ // Return just the comments fragment if requested
+ if query.fragment.as_deref() == Some("comments") {
+ let enriched_comments = fetch_enriched_comments(&state, &tokens, &client, id).await?;
+ let is_logged_in = tokens.is_some();
+ let template = CommentsTemplate {
+ comments: enriched_comments,
+ is_logged_in,
+ };
+ return Ok(Html(template.render()?));
+ }
+
// Fetch the story
let story = trailbase::get_story(&client, id)
.await?
@@ -135,28 +152,6 @@ pub async fn show_story(
Ok(Html(app.render()?))
}
-/// Returns just the comments HTML fragment for a story (used by SSE refresh)
-pub async fn story_comments_fragment(
- State(state): State<AppState>,
- jar: CookieJar,
- Path(id): Path<i64>,
-) -> Result<Html<String>, AppError> {
- let tokens = get_tokens_from_cookies(&jar);
- let client = match &tokens {
- Some(t) => trailbase::client_with_tokens(&state.trailbase_url, t)?,
- None => trailbase::new_client(&state.trailbase_url)?,
- };
-
- let enriched_comments = fetch_enriched_comments(&state, &tokens, &client, id).await?;
- let is_logged_in = tokens.is_some();
-
- let template = CommentsTemplate {
- comments: enriched_comments,
- is_logged_in,
- };
- Ok(Html(template.render()?))
-}
-
pub async fn show_submit(
State(state): State<AppState>,
jar: CookieJar,
diff --git a/src/routes.rs b/src/routes.rs
@@ -71,7 +71,6 @@ pub fn create_router(state: AppState) -> Router {
.route("/story/{id}/edit", post(handlers::edit_story))
// Comments
- .route("/story/{id}/comments", get(handlers::story_comments_fragment))
.route("/story/{id}/comment", post(handlers::create_comment))
.route("/comment/{id}/reply", get(handlers::show_reply_form))
.route("/comment/{id}/reply", post(handlers::submit_reply))
diff --git a/templates/story.html b/templates/story.html
@@ -88,7 +88,7 @@
(function() {
var evtSource = new EventSource('/api/records/v1/comment/subscribe/*?filter[story_id]={{ story.id }}');
evtSource.onmessage = function(event) {
- fetch('/story/{{ story.id }}/comments')
+ fetch('/story/{{ story.id }}?fragment=comments')
.then(function(r) { return r.text(); })
.then(function(html) {
document.getElementById('comments-section').innerHTML = html;