diff --git a/backend/api/src/api_doc.rs b/backend/api/src/api_doc.rs index 7dbbfda4..8264e909 100644 --- a/backend/api/src/api_doc.rs +++ b/backend/api/src/api_doc.rs @@ -1,5 +1,5 @@ -use arcadia_storage::models::collage::SearchCollagesQuery; use arcadia_storage::models::series::SearchSeriesQuery; +use arcadia_storage::models::{collage::SearchCollagesQuery, forum::GetForumThreadPostsQuery}; use utoipa::{ openapi::security::{HttpAuthScheme, HttpBuilder, SecurityScheme}, Modify, OpenApi, @@ -92,7 +92,8 @@ use crate::handlers::{ GetUserApplicationsQuery, SearchTorrentRequestsQuery, SearchCollagesQuery, - SearchSeriesQuery + SearchSeriesQuery, + GetForumThreadPostsQuery ),) )] pub struct ApiDoc; diff --git a/backend/api/src/handlers/forum/get_forum_thread.rs b/backend/api/src/handlers/forum/get_forum_thread.rs index 1f3a2de2..e9773c43 100644 --- a/backend/api/src/handlers/forum/get_forum_thread.rs +++ b/backend/api/src/handlers/forum/get_forum_thread.rs @@ -8,13 +8,6 @@ use arcadia_storage::{models::forum::ForumThreadEnriched, redis::RedisPoolInterf use serde::Deserialize; use utoipa::IntoParams; -#[derive(Debug, Deserialize, IntoParams)] -pub struct GetForumThreadQuery { - pub title: String, - pub offset: Option, - pub limit: Option, -} - #[derive(Debug, Deserialize, IntoParams)] pub struct GetForumThreadQueryId { pub id: i64, diff --git a/backend/api/src/handlers/forum/get_forum_thread_posts.rs b/backend/api/src/handlers/forum/get_forum_thread_posts.rs index 06e6daad..f3ebc4de 100644 --- a/backend/api/src/handlers/forum/get_forum_thread_posts.rs +++ b/backend/api/src/handlers/forum/get_forum_thread_posts.rs @@ -5,25 +5,12 @@ use actix_web::{ }; use arcadia_common::error::Result; use arcadia_storage::{ - models::{common::PaginatedResults, forum::ForumPostHierarchy}, + models::{ + common::PaginatedResults, + forum::{ForumPostHierarchy, GetForumThreadPostsQuery}, + }, redis::RedisPoolInterface, }; -use serde::Deserialize; -use utoipa::IntoParams; - -#[derive(Debug, Deserialize, IntoParams)] -pub struct GetForumThreadQuery { - pub title: String, - pub offset: Option, - pub limit: Option, -} - -#[derive(Debug, Deserialize, IntoParams)] -pub struct GetForumThreadPostsQuery { - pub thread_id: i64, - pub page: u32, - pub page_size: u32, -} #[utoipa::path( get, @@ -41,10 +28,7 @@ pub async fn exec( ) -> Result { //TODO: restrict access to some sub_categories based on forbidden_classes - let thread = arc - .pool - .find_forum_thread_posts(query.thread_id, query.page, query.page_size) - .await?; + let thread = arc.pool.find_forum_thread_posts(query.into_inner()).await?; Ok(HttpResponse::Ok().json(thread)) } diff --git a/backend/storage/.sqlx/query-0a8b1361933b00d93f861dee93762b059f94b0ca603ad4201a9a96143eaed957.json b/backend/storage/.sqlx/query-0a8b1361933b00d93f861dee93762b059f94b0ca603ad4201a9a96143eaed957.json new file mode 100644 index 00000000..42e86daa --- /dev/null +++ b/backend/storage/.sqlx/query-0a8b1361933b00d93f861dee93762b059f94b0ca603ad4201a9a96143eaed957.json @@ -0,0 +1,23 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT COUNT(*)::BIGINT FROM forum_posts\n WHERE forum_thread_id = $1 AND id < $2\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Int8", + "Int8" + ] + }, + "nullable": [ + null + ] + }, + "hash": "0a8b1361933b00d93f861dee93762b059f94b0ca603ad4201a9a96143eaed957" +} diff --git a/backend/storage/src/models/forum.rs b/backend/storage/src/models/forum.rs index 8c2fd4e5..1457a94e 100644 --- a/backend/storage/src/models/forum.rs +++ b/backend/storage/src/models/forum.rs @@ -1,7 +1,7 @@ use chrono::{DateTime, Local}; use serde::{Deserialize, Serialize}; use sqlx::prelude::FromRow; -use utoipa::ToSchema; +use utoipa::{IntoParams, ToSchema}; use super::user::{UserLite, UserLiteAvatar}; @@ -160,3 +160,11 @@ pub struct ForumPostAndThreadName { pub sticky: bool, pub forum_thread_name: String, } + +#[derive(Debug, Deserialize, IntoParams, ToSchema)] +pub struct GetForumThreadPostsQuery { + pub thread_id: i64, + pub page: Option, + pub page_size: u32, + pub post_id: Option, +} diff --git a/backend/storage/src/repositories/forum_repository.rs b/backend/storage/src/repositories/forum_repository.rs index 48330ea6..4be3cad7 100644 --- a/backend/storage/src/repositories/forum_repository.rs +++ b/backend/storage/src/repositories/forum_repository.rs @@ -4,7 +4,8 @@ use crate::{ common::PaginatedResults, forum::{ ForumPost, ForumPostAndThreadName, ForumPostHierarchy, ForumThread, - ForumThreadEnriched, UserCreatedForumPost, UserCreatedForumThread, + ForumThreadEnriched, GetForumThreadPostsQuery, UserCreatedForumPost, + UserCreatedForumThread, }, }, }; @@ -305,11 +306,30 @@ impl ConnectionPool { pub async fn find_forum_thread_posts( &self, - forum_thread_id: i64, - page: u32, - page_size: u32, + form: GetForumThreadPostsQuery, ) -> Result> { - let offset = (page - 1) * page_size; + let page_size = form.page_size as i64; + let mut current_page = form.page.unwrap_or(1); + + let offset = if let Some(post_id) = form.post_id { + let position = sqlx::query_scalar!( + r#" + SELECT COUNT(*)::BIGINT FROM forum_posts + WHERE forum_thread_id = $1 AND id < $2 + "#, + form.thread_id, + post_id + ) + .fetch_one(self.borrow()) + .await? + .unwrap_or(0); + + // i64 ceil division is unstable as of now + current_page = ((position + 1) as u64).div_ceil(form.page_size as u64) as u32; + ((position / page_size) * page_size) as i64 + } else { + ((form.page.unwrap_or(1) - 1) as i64) * page_size + }; let forum_thread_data = sqlx::query!( r#" @@ -354,9 +374,9 @@ impl ConnectionPool { LIMIT $3 ) p; "#, - forum_thread_id, - offset as i64, - page_size as i64 + form.thread_id, + offset, + page_size ) .fetch_one(self.borrow()) .await @@ -372,8 +392,8 @@ impl ConnectionPool { let paginated_results = PaginatedResults { results: posts, - page, - page_size, + page: current_page, + page_size: form.page_size, total_items: forum_thread_data.total_items.unwrap_or(0), }; diff --git a/frontend/src/api-schema/schema.d.ts b/frontend/src/api-schema/schema.d.ts index fda7e57a..874171bd 100644 --- a/frontend/src/api-schema/schema.d.ts +++ b/frontend/src/api-schema/schema.d.ts @@ -315,7 +315,7 @@ export interface paths { path?: never; cookie?: never; }; - get: operations["Get forum thread"]; + get: operations["Get forum thread's posts"]; put?: never; post?: never; delete?: never; @@ -1415,6 +1415,16 @@ export interface components { id: number; name: string; }; + GetForumThreadPostsQuery: { + /** Format: int32 */ + page?: number | null; + /** Format: int32 */ + page_size: number; + /** Format: int64 */ + post_id?: number | null; + /** Format: int64 */ + thread_id: number; + }; GetUserApplicationsQuery: { /** Format: int64 */ limit?: number | null; @@ -3163,12 +3173,13 @@ export interface operations { }; }; }; - "Get forum thread": { + "Get forum thread's posts": { parameters: { query: { thread_id: number; - page: number; + page?: number | null; page_size: number; + post_id?: number | null; }; header?: never; path?: never; diff --git a/frontend/src/components/PaginatedResults.vue b/frontend/src/components/PaginatedResults.vue index a9e7e70b..6d4cbe5b 100644 --- a/frontend/src/components/PaginatedResults.vue +++ b/frontend/src/components/PaginatedResults.vue @@ -23,10 +23,16 @@