M openapi/openapi.json => openapi/openapi.json +26 -7
@@ 102,7 102,7 @@
},
"PostListPost": {
"type": "object",
- "required": ["id", "title", "created", "community"],
+ "required": ["id", "title", "created", "community", "score"],
"properties": {
"id": {"type": "integer"},
"title": {"type": "string"},
@@ 129,7 129,9 @@
"host": {"type": "string"},
"remote_url": {"type": "string", "nullable": true}
}
- }
+ },
+ "score": {"type": "integer"},
+ "your_vote": {"$ref": "#/components/schemas/YourVote"}
}
},
"PostCommentInfo": {
@@ 687,6 689,12 @@
"in": "query",
"required": false,
"schema": {"$ref": "#/components/schemas/SortType"}
+ },
+ {
+ "name": "include_your",
+ "in": "query",
+ "required": false,
+ "schema": {"type": "boolean"}
}
],
"responses": {
@@ 1024,6 1032,14 @@
"/api/unstable/posts": {
"get": {
"summary": "List posts",
+ "parameters": [
+ {
+ "name": "include_your",
+ "in": "query",
+ "required": false,
+ "schema": {"type": "boolean"}
+ }
+ ],
"responses": {
"200": {
"description": "",
@@ 1105,15 1121,13 @@
"schema": {
"allOf": [{"$ref": "#/components/schemas/PostListPost"}],
"type": "object",
- "required": ["approved", "score", "replies"],
+ "required": ["approved", "replies"],
"properties": {
"approved": {"type": "boolean"},
- "score": {"type": "integer"},
"replies": {
"type": "array",
"items": {"$ref": "#/components/schemas/PostCommentInfo"}
- },
- "your_vote": {"$ref": "#/components/schemas/YourVote"}
+ }
}
}
}
@@ 1479,7 1493,12 @@
"schema": {
"type": "array",
"items": {
- "$ref": "#/components/schemas/PostListPost"
+ "allOf": [{"$ref": "#/components/schemas/PostListPost"}],
+ "type": "object",
+ "required": ["your_vote"],
+ "properties": {
+ "your_vote": {"$ref": "#/components/schemas/YourVote"}
+ }
}
}
}
M src/routes/api/communities.rs => src/routes/api/communities.rs +27 -2
@@ 536,6 536,8 @@ async fn route_unstable_communities_posts_list(
struct Query {
#[serde(default = "default_sort")]
sort: super::SortType,
+ #[serde(default)]
+ include_your: bool,
}
let query: Query = serde_urlencoded::from_str(req.uri().query().unwrap_or(""))?;
@@ 545,6 547,13 @@ async fn route_unstable_communities_posts_list(
let lang = crate::get_lang_for_req(&req);
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
+ };
+
let community_row = db
.query_opt(
"SELECT name, local, ap_id FROM community WHERE id=$1",
@@ 581,9 590,15 @@ async fn route_unstable_communities_posts_list(
let limit: i64 = 30; // TODO make configurable
- let values: &[&(dyn tokio_postgres::types::ToSql + Sync)] = &[&community_id, &limit];
+ let mut values: Vec<&(dyn tokio_postgres::types::ToSql + Sync)> = vec![&community_id, &limit];
let sql: &str = &format!(
- "SELECT post.id, post.author, post.href, post.content_text, post.title, post.created, post.content_html, person.username, person.local, person.ap_id, person.avatar FROM post LEFT OUTER JOIN person ON (person.id = post.author) WHERE post.community = $1 AND post.approved=TRUE AND post.deleted=FALSE ORDER BY {} LIMIT $2",
+ "SELECT post.id, post.author, post.href, post.content_text, post.title, post.created, post.content_html, person.username, person.local, person.ap_id, person.avatar, (SELECT COUNT(*) FROM post_like WHERE post_like.post = post.id){} FROM post LEFT OUTER JOIN person ON (person.id = post.author) WHERE post.community = $1 AND post.approved=TRUE AND post.deleted=FALSE ORDER BY {} LIMIT $2",
+ if let Some(user) = &include_your_for {
+ values.push(user);
+ ", EXISTS(SELECT 1 FROM post_like WHERE post=post.id AND person=$3)"
+ } else {
+ ""
+ },
query.sort.post_sort_sql(),
);
@@ 631,6 646,16 @@ async fn route_unstable_communities_posts_list(
author: author.as_ref(),
created: &created.to_rfc3339(),
community: &community,
+ score: row.get(11),
+ your_vote: if include_your_for.is_some() {
+ Some(if row.get(12) {
+ Some(crate::Empty {})
+ } else {
+ None
+ })
+ } else {
+ None
+ },
};
futures::future::ready(serde_json::to_value(&post).map_err(Into::into))
M src/routes/api/mod.rs => src/routes/api/mod.rs +14 -0
@@ 102,6 102,9 @@ struct RespPostListPost<'a> {
author: Option<&'a RespMinimalAuthorInfo<'a>>,
created: &'a str,
community: &'a RespMinimalCommunityInfo<'a>,
+ score: i64,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ your_vote: Option<Option<crate::Empty>>,
}
#[derive(Serialize)]
@@ 727,6 730,7 @@ async fn handle_common_posts_list(
stream: impl futures::stream::TryStream<Ok = tokio_postgres::Row, Error = tokio_postgres::Error>
+ Send,
ctx: &crate::RouteContext,
+ include_your: bool,
) -> Result<Vec<serde_json::Value>, crate::Error> {
use futures::stream::TryStreamExt;
@@ 787,6 791,16 @@ async fn handle_common_posts_list(
author: author.as_ref(),
created: &created.to_rfc3339(),
community: &community,
+ score: row.get(15),
+ your_vote: if include_your {
+ Some(if row.get(16) {
+ Some(crate::Empty {})
+ } else {
+ None
+ })
+ } else {
+ None
+ },
};
futures::future::ready(serde_json::to_value(&post).map_err(Into::into))
M src/routes/api/posts.rs => src/routes/api/posts.rs +26 -11
@@ 99,18 99,36 @@ async fn get_post_comments<'a>(
async fn route_unstable_posts_list(
_: (),
ctx: Arc<crate::RouteContext>,
- _req: hyper::Request<hyper::Body>,
+ req: hyper::Request<hyper::Body>,
) -> Result<hyper::Response<hyper::Body>, crate::Error> {
+ let query: MaybeIncludeYour = serde_urlencoded::from_str(req.uri().query().unwrap_or(""))?;
+
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
+ };
+
let limit: i64 = 30;
- let stream = db.query_raw(
- "SELECT post.id, post.author, post.href, post.content_text, post.title, post.created, post.content_html, community.id, community.name, community.local, community.ap_id, person.username, person.local, person.ap_id, person.avatar FROM community, post LEFT OUTER JOIN person ON (person.id = post.author) WHERE post.community = community.id AND deleted=FALSE ORDER BY hot_rank((SELECT COUNT(*) FROM post_like WHERE post = post.id AND person != post.author), post.created) DESC LIMIT $1",
- ([limit]).iter().map(|x| x as _),
- ).await?;
+ let mut values: Vec<&(dyn tokio_postgres::types::ToSql + Sync)> = vec![&limit];
+
+ let sql: &str = &format!(
+ "SELECT post.id, post.author, post.href, post.content_text, post.title, post.created, post.content_html, community.id, community.name, community.local, community.ap_id, person.username, person.local, person.ap_id, person.avatar, (SELECT COUNT(*) FROM post_like WHERE post_like.post = post.id){} FROM community, post LEFT OUTER JOIN person ON (person.id = post.author) WHERE post.community = community.id AND deleted=FALSE ORDER BY hot_rank((SELECT COUNT(*) FROM post_like WHERE post = post.id AND person != post.author), post.created) DESC LIMIT $1",
+ if let Some(user) = &include_your_for {
+ values.push(user);
+ ", EXISTS(SELECT 1 FROM post_like WHERE post=post.id AND person=$2)"
+ } else {
+ ""
+ },
+ );
+
+ let stream = crate::query_stream(&db, sql, &values).await?;
- let posts = super::handle_common_posts_list(stream, &ctx).await?;
+ let posts = super::handle_common_posts_list(stream, &ctx, include_your_for.is_some()).await?;
crate::json_response(&posts)
}
@@ 253,10 271,7 @@ async fn route_unstable_posts_get(
#[serde(flatten)]
post: &'a RespPostListPost<'a>,
approved: bool,
- score: i64,
replies: Vec<RespPostCommentInfo<'a>>,
- #[serde(skip_serializing_if = "Option::is_none")]
- your_vote: Option<Option<crate::Empty>>,
}
let (post_id,) = params;
@@ 340,14 355,14 @@ async fn route_unstable_posts_get(
author: author.as_ref(),
created: &created.to_rfc3339(),
community: &community,
+ score: row.get(13),
+ your_vote,
};
let output = RespPostInfo {
post: &post,
replies: comments,
approved: row.get(14),
- score: row.get(13),
- your_vote,
};
crate::json_response(&output)
M src/routes/api/users.rs => src/routes/api/users.rs +2 -2
@@ 264,11 264,11 @@ async fn route_unstable_users_following_posts_list(
let values: &[&(dyn tokio_postgres::types::ToSql + Sync)] = &[&user, &limit];
let stream = db.query_raw(
- "SELECT post.id, post.author, post.href, post.content_text, post.title, post.created, post.content_html, community.id, community.name, community.local, community.ap_id, person.username, person.local, person.ap_id, person.avatar FROM community, post LEFT OUTER JOIN person ON (person.id = post.author) WHERE post.community = community.id AND post.approved AND post.deleted=FALSE AND community.id IN (SELECT community FROM community_follow WHERE follower=$1 AND accepted) ORDER BY hot_rank((SELECT COUNT(*) FROM post_like WHERE post = post.id AND person != post.author), post.created) DESC LIMIT $2",
+ "SELECT post.id, post.author, post.href, post.content_text, post.title, post.created, post.content_html, community.id, community.name, community.local, community.ap_id, person.username, person.local, person.ap_id, person.avatar, (SELECT COUNT(*) FROM post_like WHERE post_like.post = post.id), EXISTS(SELECT 1 FROM post_like WHERE post = post.id AND person = $1) FROM community, post LEFT OUTER JOIN person ON (person.id = post.author) WHERE post.community = community.id AND post.approved AND post.deleted=FALSE AND community.id IN (SELECT community FROM community_follow WHERE follower=$1 AND accepted) ORDER BY hot_rank((SELECT COUNT(*) FROM post_like WHERE post = post.id AND person != post.author), post.created) DESC LIMIT $2",
values.iter().map(|s| *s as _)
).await?;
- let posts = handle_common_posts_list(stream, &ctx).await?;
+ let posts = handle_common_posts_list(stream, &ctx, true).await?;
crate::json_response(&posts)
}