diff --git a/docs/openapi.yaml b/docs/openapi.yaml index 2f64ad5..f97ea00 100644 --- a/docs/openapi.yaml +++ b/docs/openapi.yaml @@ -555,6 +555,34 @@ paths: description: Post not found 422: description: Transaction already registered + /api/v1/timelines/public: + get: + summary: View local public posts. + parameters: + - name: max_id + in: query + description: Return results older than this ID. + required: false + schema: + type: string + format: uuid + - name: limit + in: query + description: Maximum number of results to return. + required: false + schema: + type: integer + default: 20 + responses: + 200: + description: Successful operation + content: + application/json: + schema: + description: Post list + type: array + items: + $ref: '#/components/schemas/Status' /api/v1/timelines/tag/{hashtag}: get: summary: View public posts containing the given hashtag diff --git a/src/mastodon_api/timelines/views.rs b/src/mastodon_api/timelines/views.rs index 4654ede..618046a 100644 --- a/src/mastodon_api/timelines/views.rs +++ b/src/mastodon_api/timelines/views.rs @@ -7,7 +7,11 @@ use crate::database::{Pool, get_database_client}; use crate::errors::HttpError; use crate::mastodon_api::oauth::auth::get_current_user; use crate::mastodon_api::statuses::helpers::build_status_list; -use crate::models::posts::queries::{get_home_timeline, get_posts_by_tag}; +use crate::models::posts::queries::{ + get_home_timeline, + get_local_timeline, + get_posts_by_tag, +}; use super::types::TimelineQueryParams; #[get("/home")] @@ -34,6 +38,31 @@ async fn home_timeline( Ok(HttpResponse::Ok().json(statuses)) } +/// Local timeline ("local" parameter is ignored) +#[get("/public")] +async fn public_timeline( + auth: BearerAuth, + config: web::Data, + db_pool: web::Data, + query_params: web::Query, +) -> Result { + let db_client = &**get_database_client(&db_pool).await?; + let current_user = get_current_user(db_client, auth.token()).await?; + let posts = get_local_timeline( + db_client, + ¤t_user.id, + query_params.max_id, + query_params.limit, + ).await?; + let statuses = build_status_list( + db_client, + &config.instance_url(), + Some(¤t_user), + posts, + ).await?; + Ok(HttpResponse::Ok().json(statuses)) +} + #[get("/tag/{hashtag}")] async fn hashtag_timeline( auth: Option, @@ -66,5 +95,6 @@ async fn hashtag_timeline( pub fn timeline_api_scope() -> Scope { web::scope("/api/v1/timelines") .service(home_timeline) + .service(public_timeline) .service(hashtag_timeline) } diff --git a/src/models/posts/queries.rs b/src/models/posts/queries.rs index 9197c03..ae90135 100644 --- a/src/models/posts/queries.rs +++ b/src/models/posts/queries.rs @@ -344,6 +344,46 @@ pub async fn get_home_timeline( Ok(posts) } +pub async fn get_local_timeline( + db_client: &impl GenericClient, + current_user_id: &Uuid, + max_post_id: Option, + limit: i64, +) -> Result, DatabaseError> { + let statement = format!( + " + SELECT + post, actor_profile, + {related_attachments}, + {related_mentions}, + {related_tags} + FROM post + JOIN actor_profile ON post.author_id = actor_profile.id + WHERE + actor_profile.actor_json IS NULL + AND {visibility_filter} + AND ($max_post_id::uuid IS NULL OR post.id < $max_post_id) + ORDER BY post.id DESC + LIMIT $limit + ", + related_attachments=RELATED_ATTACHMENTS, + related_mentions=RELATED_MENTIONS, + related_tags=RELATED_TAGS, + visibility_filter=build_visibility_filter(), + ); + let query = query!( + &statement, + current_user_id=current_user_id, + max_post_id=max_post_id, + limit=limit, + )?; + let rows = db_client.query(query.sql(), query.parameters()).await?; + let posts: Vec = rows.iter() + .map(Post::try_from) + .collect::>()?; + Ok(posts) +} + pub async fn get_posts( db_client: &impl GenericClient, posts_ids: Vec,