use crate::routes::api::{
MaybeIncludeYour, RespAvatarInfo, RespMinimalAuthorInfo, RespMinimalCommunityInfo,
RespPostListPost,
};
use crate::{CommunityLocalID, PostLocalID, UserLocalID};
use serde_derive::{Deserialize, Serialize};
use std::borrow::Cow;
use std::sync::Arc;
#[derive(Serialize)]
struct RespCommunityInfo<'a> {
#[serde(flatten)]
base: RespMinimalCommunityInfo<'a>,
description: &'a str,
description_html: Option<String>,
description_text: Option<&'a str>,
#[serde(skip_serializing_if = "Option::is_none")]
you_are_moderator: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
your_follow: Option<Option<RespYourFollowInfo>>,
}
#[derive(Serialize)]
struct RespYourFollowInfo {
accepted: bool,
}
fn get_community_description_fields<'a>(
description_text: &'a str,
description_html: Option<&'a str>,
) -> (&'a str, Option<&'a str>, Option<String>) {
match description_html {
Some(description_html) => (
description_html,
None,
Some(ammonia::clean(description_html)),
),
None => (description_text, Some(description_text), None),
}
}
async fn route_unstable_communities_list(
_: (),
ctx: Arc<crate::RouteContext>,
req: hyper::Request<hyper::Body>,
) -> Result<hyper::Response<hyper::Body>, crate::Error> {
use std::fmt::Write;
#[derive(Deserialize)]
struct CommunitiesListQuery<'a> {
#[serde(default)]
search: Option<Cow<'a, str>>,
#[serde(default)]
include_your: bool,
}
let query: CommunitiesListQuery = serde_urlencoded::from_str(req.uri().query().unwrap_or(""))?;
let mut sql = String::from("SELECT id, name, local, ap_id, description, description_html");
let mut values: Vec<&(dyn tokio_postgres::types::ToSql + Sync)> = Vec::new();
let db = ctx.db_pool.get().await?;
let include_your_for = if query.include_your {
let user = crate::require_login(&req, &db).await?;
Some(user)
} else {
None
};
if let Some(user) = &include_your_for {
values.push(user);
sql.push_str(", (SELECT accepted FROM community_follow WHERE community=community.id AND follower=$1), EXISTS(SELECT 1 FROM community_moderator WHERE community=community.id AND person=$1)");
}
sql.push_str(" FROM community");
if let Some(search) = &query.search {
values.push(search);
write!(sql, " WHERE community_fts(community) @@ plainto_tsquery('english', ${0}) ORDER BY ts_rank_cd(community_fts(community), plainto_tsquery('english', ${0})) DESC", values.len()).unwrap();
}
let sql: &str = &sql;
let rows = db.query(sql, &values).await?;
let output: Vec<_> = rows
.iter()
.map(|row| {
let id = CommunityLocalID(row.get(0));
let name = row.get(1);
let local = row.get(2);
let ap_id = row.get(3);
let (description, description_text, description_html) =
get_community_description_fields(row.get(4), row.get(5));
let host = crate::get_actor_host_or_unknown(local, ap_id, &ctx.local_hostname);
RespCommunityInfo {
base: RespMinimalCommunityInfo {
id,
name,
local,
host,
remote_url: ap_id,
},
description,
description_html,
description_text,
you_are_moderator: if query.include_your {
Some(row.get(7))
} else {
None
},
your_follow: if query.include_your {
Some(match row.get(6) {
Some(accepted) => Some(RespYourFollowInfo { accepted }),
None => None,
})
} else {
None
},
}
})
.collect();
crate::json_response(&output)
}
async fn route_unstable_communities_create(
_: (),
ctx: Arc<crate::RouteContext>,
req: hyper::Request<hyper::Body>,
) -> Result<hyper::Response<hyper::Body>, crate::Error> {
let lang = crate::get_lang_for_req(&req);
let mut db = ctx.db_pool.get().await?;
let user = crate::require_login(&req, &db).await?;
#[derive(Deserialize)]
struct CommunitiesCreateBody<'a> {
name: &'