~vpzom/hitide

2a708a587e1eb492ec3c1b1b74656d2c80b54438 — Colin Reeder 1 year, 2 months ago 62fd45d
User content listing
5 files changed, 133 insertions(+), 26 deletions(-)

M src/components/mod.rs
M src/resp_types.rs
M src/routes/communities.rs
M src/routes/mod.rs
M src/routes/posts.rs
M src/components/mod.rs => src/components/mod.rs +52 -6
@@ 2,7 2,8 @@ use std::borrow::{Borrow, Cow};
use std::collections::HashMap;

use crate::resp_types::{
    RespMinimalAuthorInfo, RespMinimalCommunityInfo, RespPostCommentInfo, RespPostListPost,
    RespMinimalAuthorInfo, RespMinimalCommunityInfo, RespPostCommentInfo, RespPostInfo,
    RespPostListPost, RespThingComment, RespThingInfo,
};
use crate::util::{abbreviate_link, author_is_me};
use crate::PageBaseData;


@@ 122,7 123,16 @@ impl<'a> HavingContent for RespPostCommentInfo<'a> {
    }
}

impl<'a> HavingContent for RespPostListPost<'a> {
impl<'a> HavingContent for RespThingComment<'a> {
    fn content_text(&self) -> Option<&str> {
        self.content_text.as_deref()
    }
    fn content_html(&self) -> Option<&str> {
        self.content_html.as_deref()
    }
}

impl<'a> HavingContent for RespPostInfo<'a> {
    fn content_text(&self) -> Option<&str> {
        self.content_text.as_deref()
    }


@@ 202,11 212,11 @@ pub fn HTPage<'a, Children: render::Render>(
}

#[render::component]
pub fn PostItem<'post>(post: &'post RespPostListPost<'post>, in_community: bool) {
pub fn PostItem<'post>(post: &'post RespPostListPost<'post>, in_community: bool, no_user: bool) {
    render::rsx! {
        <li>
            <a href={format!("/posts/{}", post.id)}>
                {post.title.as_ref()}
            <a href={format!("/posts/{}", post.as_ref().id)}>
                {post.as_ref().title.as_ref()}
            </a>
            {
                if let Some(href) = &post.href {


@@ 221,7 231,18 @@ pub fn PostItem<'post>(post: &'post RespPostListPost<'post>, in_community: bool)
                }
            }
            <br />
            {"Submitted by "}<UserLink user={post.author.as_ref()} />
            {"Submitted"}
            {
                if no_user {
                    None
                } else {
                    Some(render::rsx! {
                        <>
                            {" by "}<UserLink user={post.author.as_ref()} />
                        </>
                    })
                }
            }
            {
                if !in_community {
                    Some(render::rsx! {


@@ 235,6 256,31 @@ pub fn PostItem<'post>(post: &'post RespPostListPost<'post>, in_community: bool)
    }
}

pub struct ThingItem<'a> {
    pub thing: &'a RespThingInfo<'a>,
}

impl<'a> render::Render for ThingItem<'a> {
    fn render_into<W: std::fmt::Write>(self, writer: &mut W) -> std::fmt::Result {
        match self.thing {
            RespThingInfo::Post(post) => {
                (PostItem { post, in_community: false, no_user: true }).render_into(writer)
            },
            RespThingInfo::Comment(comment) => {
                (render::rsx! {
                    <li>
                        <small>
                            <a href={format!("/comments/{}", comment.id)}>{"Comment"}</a>
                            {" on "}<a href={format!("/posts/{}", comment.post.id)}>{comment.post.title.as_ref()}</a>{":"}
                        </small>
                        <Content src={comment} />
                    </li>
                }).render_into(writer)
            }
        }
    }
}

pub struct UserLink<'user> {
    pub user: Option<&'user RespMinimalAuthorInfo<'user>>,
}

M src/resp_types.rs => src/resp_types.rs +36 -3
@@ 10,12 10,16 @@ pub struct RespMinimalAuthorInfo<'a> {
}

#[derive(Deserialize, Debug)]
pub struct RespPostListPost<'a> {
pub struct RespMinimalPostInfo<'a> {
    pub id: i64,
    pub title: Cow<'a, str>,
}

#[derive(Deserialize, Debug)]
pub struct RespPostListPost<'a> {
    #[serde(flatten)]
    pub base: RespMinimalPostInfo<'a>,
    pub href: Option<Cow<'a, str>>,
    pub content_text: Option<Cow<'a, str>>,
    pub content_html: Option<Cow<'a, str>>,
    #[serde(borrow)]
    pub author: Option<RespMinimalAuthorInfo<'a>>,
    pub created: Cow<'a, str>,


@@ 23,6 27,32 @@ pub struct RespPostListPost<'a> {
    pub community: RespMinimalCommunityInfo<'a>,
}

impl<'a> AsRef<RespMinimalPostInfo<'a>> for RespPostListPost<'a> {
    fn as_ref(&self) -> &RespMinimalPostInfo<'a> {
        &self.base
    }
}

#[derive(Deserialize, Debug)]
#[serde(tag = "type")]
pub enum RespThingInfo<'a> {
    #[serde(rename = "post")]
    Post(RespPostListPost<'a>),
    #[serde(rename = "comment")]
    #[serde(borrow)]
    Comment(RespThingComment<'a>),
}

#[derive(Deserialize, Debug)]
pub struct RespThingComment<'a> {
    pub id: i64,
    pub created: Cow<'a, str>,
    pub content_text: Option<Cow<'a, str>>,
    pub content_html: Option<Cow<'a, str>>,
    #[serde(borrow)]
    pub post: RespMinimalPostInfo<'a>,
}

#[derive(Deserialize, Debug)]
pub struct RespPostCommentInfo<'a> {
    pub id: i64,


@@ 41,6 71,9 @@ pub struct RespPostCommentInfo<'a> {
pub struct RespPostInfo<'a> {
    #[serde(flatten, borrow)]
    pub base: RespPostListPost<'a>,

    pub content_text: Option<Cow<'a, str>>,
    pub content_html: Option<Cow<'a, str>>,
    pub score: i64,
    #[serde(borrow)]
    pub comments: Vec<RespPostCommentInfo<'a>>,

M src/routes/communities.rs => src/routes/communities.rs +1 -1
@@ 201,7 201,7 @@ async fn page_community(
            }
            <ul>
                {posts.iter().map(|post| {
                    PostItem { post, in_community: true }
                    PostItem { post, in_community: true, no_user: false }
                }).collect::<Vec<_>>()}
            </ul>
        </HTPage>

M src/routes/mod.rs => src/routes/mod.rs +38 -10
@@ 3,9 3,11 @@ use std::borrow::Cow;
use std::sync::Arc;

use crate::components::{
    Comment, Content, HTPage, MaybeFillInput, MaybeFillTextArea, PostItem, UserLink,
    Comment, Content, HTPage, MaybeFillInput, MaybeFillTextArea, PostItem, ThingItem, UserLink,
};
use crate::resp_types::{
    RespMinimalAuthorInfo, RespPostCommentInfo, RespPostListPost, RespThingInfo,
};
use crate::resp_types::{RespMinimalAuthorInfo, RespPostCommentInfo, RespPostListPost};
use crate::util::author_is_me;
use crate::PageBaseData;



@@ 884,7 886,7 @@ async fn page_user(

    let base_data = fetch_base_data(&ctx.backend_host, &ctx.http_client, &cookies).await?;

    let api_res = res_to_error(
    let user = res_to_error(
        ctx.http_client
            .request(
                hyper::Request::get(format!(


@@ 896,18 898,44 @@ async fn page_user(
            .await?,
    )
    .await?;
    let user = hyper::body::to_bytes(user.into_body()).await?;
    let user: RespMinimalAuthorInfo<'_> = serde_json::from_slice(&user)?;

    let api_res = hyper::body::to_bytes(api_res.into_body()).await?;
    let user: RespMinimalAuthorInfo<'_> = serde_json::from_slice(&api_res)?;
    let things = res_to_error(
        ctx.http_client
            .request(
                hyper::Request::get(format!(
                    "{}/api/unstable/users/{}/things",
                    ctx.backend_host, user_id,
                ))
                .body(Default::default())?,
            )
            .await?,
    )
    .await?;
    let things = hyper::body::to_bytes(things.into_body()).await?;
    let things: Vec<RespThingInfo> = serde_json::from_slice(&things)?;

    let title = user.username.as_ref();

    Ok(html_response(render::html! {
        <HTPage base_data={&base_data} title>
            <h1>{title}</h1>
            <p>
                <em>{"User post listing is not currently implemented."}</em>
            </p>
            {
                if things.is_empty() {
                    Some(render::rsx! { <p>{"Looks like there's nothing here."}</p> })
                } else {
                    None
                }
            }
            <ul>
                {
                    things.iter().map(|thing| {
                        ThingItem { thing }
                    })
                    .collect::<Vec<_>>()
                }
            </ul>
        </HTPage>
    }))
}


@@ 959,7 987,7 @@ async fn page_home(
            }
            <ul>
                {api_res.iter().map(|post| {
                    PostItem { post, in_community: false }
                    PostItem { post, in_community: false, no_user: false }
                }).collect::<Vec<_>>()}
            </ul>
        </HTPage>


@@ 1013,7 1041,7 @@ async fn page_all_inner(
            }
            <ul>
                {api_res.iter().map(|post| {
                    PostItem { post, in_community: false }
                    PostItem { post, in_community: false, no_user: false }
                }).collect::<Vec<_>>()}
            </ul>
        </HTPage>

M src/routes/posts.rs => src/routes/posts.rs +6 -6
@@ 41,7 41,7 @@ async fn page_post(

    let post: RespPostInfo = serde_json::from_slice(&api_res)?;

    let title = post.as_ref().title.as_ref();
    let title = post.as_ref().as_ref().title.as_ref();

    Ok(html_response(render::html! {
        <HTPage base_data={&base_data} title={title}>


@@ 83,7 83,7 @@ async fn page_post(
                    }
                }
            }
            <Content src={post.as_ref()} />
            <Content src={&post} />
            {
                if author_is_me(&post.as_ref().author, &base_data.login) {
                    Some(render::rsx! {


@@ 100,7 100,7 @@ async fn page_post(
                {
                    if base_data.login.is_some() {
                        Some(render::rsx! {
                            <form method={"POST"} action={format!("/posts/{}/submit_reply", post.as_ref().id)}>
                            <form method={"POST"} action={format!("/posts/{}/submit_reply", post.as_ref().as_ref().id)}>
                                <div>
                                    <textarea name={"content_text"}>{()}</textarea>
                                </div>


@@ 155,10 155,10 @@ async fn page_post_delete(

    Ok(html_response(render::html! {
        <HTPage base_data={&base_data} title={"Delete Post"}>
            <h1>{post.as_ref().title.as_ref()}</h1>
            <h1>{post.as_ref().as_ref().title.as_ref()}</h1>
            <h2>{"Delete this post?"}</h2>
            <form method={"POST"} action={format!("/posts/{}/delete/confirm", post.as_ref().id)}>
                <a href={format!("/posts/{}/", post.as_ref().id)}>{"No, cancel"}</a>
            <form method={"POST"} action={format!("/posts/{}/delete/confirm", post.as_ref().as_ref().id)}>
                <a href={format!("/posts/{}/", post.as_ref().as_ref().id)}>{"No, cancel"}</a>
                {" "}
                <button r#type={"submit"}>{"Yes, delete"}</button>
            </form>