diff --git a/crates/presentation/src/handlers/thoughts.rs b/crates/presentation/src/handlers/thoughts.rs index 1ff33ba..1e804e8 100644 --- a/crates/presentation/src/handlers/thoughts.rs +++ b/crates/presentation/src/handlers/thoughts.rs @@ -1,15 +1,16 @@ use crate::{ + deps_struct, errors::ApiError, - extractors::{AuthUser, Deps, FromAppState, OptionalAuthUser}, - handlers::auth::to_user_response, - state::AppState, + extractors::{AuthUser, Deps, OptionalAuthUser}, + handlers::feed::to_thought_response, }; use api_types::{ requests::{CreateThoughtRequest, EditThoughtRequest}, responses::ErrorResponse, }; use application::use_cases::thoughts::{ - create_thought, delete_thought, edit_thought, get_thought, get_thread, CreateThoughtInput, + create_thought, delete_thought, edit_thought, get_thread_views, get_thought_view, + CreateThoughtInput, }; use axum::{ extract::Path, @@ -18,56 +19,20 @@ use axum::{ Json, }; use domain::{ - ports::{EventPublisher, OutboxWriter, TagRepository, ThoughtRepository, UserRepository}, + models::feed::{EngagementStats, FeedEntry, ViewerContext}, + ports::{EngagementRepository, EventPublisher, OutboxWriter, TagRepository, ThoughtRepository, UserRepository}, value_objects::ThoughtId, }; -use std::sync::Arc; use uuid::Uuid; -pub struct ThoughtsDeps { - pub thoughts: Arc, - pub users: Arc, - pub tags: Arc, - pub events: Arc, - pub outbox: Arc, -} - -impl FromAppState for ThoughtsDeps { - fn from_state(s: &AppState) -> Self { - Self { - thoughts: s.thoughts.clone(), - users: s.users.clone(), - tags: s.tags.clone(), - events: s.events.clone(), - outbox: s.outbox.clone(), - } - } -} - -fn thought_to_json( - t: &domain::models::thought::Thought, - author: &domain::models::user::User, - like_count: i64, - boost_count: i64, - reply_count: i64, -) -> serde_json::Value { - serde_json::json!({ - "id": t.id.as_uuid(), - "content": t.content.as_str(), - "author": to_user_response(author), - "replyToId": t.in_reply_to_id.as_ref().map(|x| x.as_uuid()), - "visibility": t.visibility.as_str(), - "contentWarning": t.content_warning, - "sensitive": t.sensitive, - "likeCount": like_count, - "boostCount": boost_count, - "replyCount": reply_count, - "likedByViewer": false, - "boostedByViewer": false, - "createdAt": t.created_at, - "updatedAt": t.updated_at, - }) -} +deps_struct!(ThoughtsDeps { + thoughts: ThoughtRepository, + users: UserRepository, + tags: TagRepository, + events: EventPublisher, + outbox: OutboxWriter, + engagement: EngagementRepository, +}); #[utoipa::path( post, path = "/thoughts", @@ -106,10 +71,13 @@ pub async fn post_thought( .find_by_id(&uid) .await? .ok_or(domain::errors::DomainError::NotFound)?; - Ok(( - StatusCode::CREATED, - Json(thought_to_json(&out.thought, &author, 0, 0, 0)), - )) + let entry = FeedEntry { + thought: out.thought, + author, + stats: EngagementStats { like_count: 0, boost_count: 0, reply_count: 0 }, + viewer: Some(ViewerContext { liked: false, boosted: false }), + }; + Ok((StatusCode::CREATED, Json(to_thought_response(&entry)))) } #[utoipa::path( @@ -123,15 +91,17 @@ pub async fn post_thought( pub async fn get_thought_handler( Deps(d): Deps, Path(id): Path, - OptionalAuthUser(_viewer): OptionalAuthUser, + OptionalAuthUser(viewer): OptionalAuthUser, ) -> Result, ApiError> { - let thought = get_thought(&*d.thoughts, &ThoughtId::from_uuid(id)).await?; - let author = d - .users - .find_by_id(&thought.user_id) - .await? - .ok_or(domain::errors::DomainError::NotFound)?; - Ok(Json(thought_to_json(&thought, &author, 0, 0, 0))) + let entry = get_thought_view( + &*d.thoughts, + &*d.users, + &*d.engagement, + &ThoughtId::from_uuid(id), + viewer.as_ref(), + ) + .await?; + Ok(Json(serde_json::to_value(to_thought_response(&entry)).unwrap())) } #[utoipa::path( @@ -191,13 +161,19 @@ pub async fn patch_thought( pub async fn get_thread_handler( Deps(d): Deps, Path(id): Path, + OptionalAuthUser(viewer): OptionalAuthUser, ) -> Result>, ApiError> { - let thoughts = get_thread(&*d.thoughts, &ThoughtId::from_uuid(id)).await?; - let mut items = Vec::new(); - for t in &thoughts { - if let Ok(Some(author)) = d.users.find_by_id(&t.user_id).await { - items.push(thought_to_json(t, &author, 0, 0, 0)); - } - } + let entries = get_thread_views( + &*d.thoughts, + &*d.users, + &*d.engagement, + &ThoughtId::from_uuid(id), + viewer.as_ref(), + ) + .await?; + let items: Vec<_> = entries + .iter() + .map(|e| serde_json::to_value(to_thought_response(e)).unwrap()) + .collect(); Ok(Json(items)) }