Skip to main content

nexus_common/models/post/
view.rs

1use serde::{Deserialize, Serialize};
2use utoipa::ToSchema;
3
4use super::{Bookmark, PostCounts, PostDetails, PostRelationships};
5use crate::models::tag::post::TagPost;
6use crate::models::tag::traits::TagCollection;
7use crate::models::tag::TagDetails;
8use crate::types::DynError;
9
10/// Represents a Pubky user with relational data including tags, counts, and relationship with a viewer.
11#[derive(Serialize, Deserialize, ToSchema, Default, Debug)]
12pub struct PostView {
13    pub details: PostDetails,
14    pub counts: PostCounts,
15    pub tags: Vec<TagDetails>,
16    pub relationships: PostRelationships,
17    pub bookmark: Option<Bookmark>,
18}
19
20impl PostView {
21    /// Retrieves a user ID, checking the cache first and then the graph database.
22    pub async fn get_by_id(
23        author_id: &str,
24        post_id: &str,
25        viewer_id: Option<&str>,
26        limit_tags: Option<usize>,
27        limit_taggers: Option<usize>,
28    ) -> Result<Option<Self>, DynError> {
29        // Perform all operations concurrently
30        let (details, counts, bookmark, relationships) = tokio::try_join!(
31            PostDetails::get_by_id(author_id, post_id),
32            PostCounts::get_by_id(author_id, post_id),
33            Bookmark::get_by_id(author_id, post_id, viewer_id),
34            PostRelationships::get_by_id(author_id, post_id),
35        )?;
36
37        let details = match details {
38            None => return Ok(None),
39            Some(details) => details,
40        };
41
42        let counts = counts.unwrap_or_default();
43        let relationships = relationships.unwrap_or_default();
44
45        // Before fetching post tags, check if the post has any tags
46        // Without this check, the index search will return a NONE because the tag index
47        // doesn't exist, leading us to query the graph unnecessarily, assuming the data wasn't indexed
48        let tags = match counts.tags {
49            0 => Vec::new(),
50            _ => {
51                TagPost::get_by_id(
52                    author_id,
53                    Some(post_id),
54                    None,
55                    limit_tags,
56                    limit_taggers,
57                    viewer_id,
58                    None, // Avoid by default WoT tags in a Post
59                )
60                .await?
61                .unwrap_or_default()
62            }
63        };
64
65        Ok(Some(Self {
66            details,
67            counts,
68            bookmark,
69            relationships,
70            tags,
71        }))
72    }
73}