mirror of
https://github.com/LemmyNet/lemmy.git
synced 2025-03-13 15:02:44 +00:00
Extracting pagination cursor utils into a trait. (#5424)
* Extracting pagination cursor utils into a trait. - Fixes #5275 * Refactoring to avoid stack overflows. * Fixing api_common feature. * Rename the traits and paginationcursor::new * Using combined trait. * Removing empty files.
This commit is contained in:
parent
2f2f58da65
commit
a6507c169d
24 changed files with 615 additions and 509 deletions
|
@ -5,9 +5,10 @@ use lemmy_api_common::{
|
|||
person::{ListPersonSaved, ListPersonSavedResponse},
|
||||
utils::check_private_instance,
|
||||
};
|
||||
use lemmy_db_schema::traits::PaginationCursorBuilder;
|
||||
use lemmy_db_views::{
|
||||
combined::person_saved_combined_view::PersonSavedCombinedQuery,
|
||||
structs::{LocalUserView, SiteView},
|
||||
structs::{LocalUserView, PersonSavedCombinedView, SiteView},
|
||||
};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
|
@ -20,22 +21,21 @@ pub async fn list_person_saved(
|
|||
|
||||
check_private_instance(&Some(local_user_view.clone()), &local_site.local_site)?;
|
||||
|
||||
// parse pagination token
|
||||
let page_after = if let Some(pa) = &data.page_cursor {
|
||||
Some(pa.read(&mut context.pool()).await?)
|
||||
let cursor_data = if let Some(cursor) = &data.page_cursor {
|
||||
Some(PersonSavedCombinedView::from_cursor(cursor, &mut context.pool()).await?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let page_back = data.page_back;
|
||||
let type_ = data.type_;
|
||||
|
||||
let saved = PersonSavedCombinedQuery {
|
||||
type_,
|
||||
page_after,
|
||||
page_back,
|
||||
type_: data.type_,
|
||||
cursor_data,
|
||||
page_back: data.page_back,
|
||||
}
|
||||
.list(&mut context.pool(), &local_user_view)
|
||||
.await?;
|
||||
|
||||
Ok(Json(ListPersonSavedResponse { saved }))
|
||||
let next_page = saved.last().map(PaginationCursorBuilder::to_cursor);
|
||||
|
||||
Ok(Json(ListPersonSavedResponse { saved, next_page }))
|
||||
}
|
||||
|
|
|
@ -3,7 +3,11 @@ use lemmy_api_common::{
|
|||
context::LemmyContext,
|
||||
person::{ListInbox, ListInboxResponse},
|
||||
};
|
||||
use lemmy_db_views::{combined::inbox_combined_view::InboxCombinedQuery, structs::LocalUserView};
|
||||
use lemmy_db_schema::traits::PaginationCursorBuilder;
|
||||
use lemmy_db_views::{
|
||||
combined::inbox_combined_view::InboxCombinedQuery,
|
||||
structs::{InboxCombinedView, LocalUserView},
|
||||
};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
pub async fn list_inbox(
|
||||
|
@ -11,28 +15,25 @@ pub async fn list_inbox(
|
|||
context: Data<LemmyContext>,
|
||||
local_user_view: LocalUserView,
|
||||
) -> LemmyResult<Json<ListInboxResponse>> {
|
||||
let unread_only = data.unread_only;
|
||||
let type_ = data.type_;
|
||||
let person_id = local_user_view.person.id;
|
||||
let show_bot_accounts = Some(local_user_view.local_user.show_bot_accounts);
|
||||
|
||||
// parse pagination token
|
||||
let page_after = if let Some(pa) = &data.page_cursor {
|
||||
Some(pa.read(&mut context.pool()).await?)
|
||||
let cursor_data = if let Some(cursor) = &data.page_cursor {
|
||||
Some(InboxCombinedView::from_cursor(cursor, &mut context.pool()).await?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let page_back = data.page_back;
|
||||
|
||||
let inbox = InboxCombinedQuery {
|
||||
type_,
|
||||
unread_only,
|
||||
show_bot_accounts,
|
||||
page_after,
|
||||
page_back,
|
||||
type_: data.type_,
|
||||
unread_only: data.unread_only,
|
||||
show_bot_accounts: Some(local_user_view.local_user.show_bot_accounts),
|
||||
cursor_data,
|
||||
page_back: data.page_back,
|
||||
}
|
||||
.list(&mut context.pool(), person_id)
|
||||
.await?;
|
||||
|
||||
Ok(Json(ListInboxResponse { inbox }))
|
||||
let next_page = inbox.last().map(PaginationCursorBuilder::to_cursor);
|
||||
|
||||
Ok(Json(ListInboxResponse { inbox, next_page }))
|
||||
}
|
||||
|
|
|
@ -4,7 +4,11 @@ use lemmy_api_common::{
|
|||
reports::combined::{ListReports, ListReportsResponse},
|
||||
utils::check_community_mod_of_any_or_admin_action,
|
||||
};
|
||||
use lemmy_db_views::{combined::report_combined_view::ReportCombinedQuery, structs::LocalUserView};
|
||||
use lemmy_db_schema::traits::PaginationCursorBuilder;
|
||||
use lemmy_db_views::{
|
||||
combined::report_combined_view::ReportCombinedQuery,
|
||||
structs::{LocalUserView, ReportCombinedView},
|
||||
};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
/// Lists reports for a community if an id is supplied
|
||||
|
@ -21,26 +25,26 @@ pub async fn list_reports(
|
|||
check_community_mod_of_any_or_admin_action(&local_user_view, &mut context.pool()).await?;
|
||||
}
|
||||
|
||||
// parse pagination token
|
||||
let page_after = if let Some(pa) = &data.page_cursor {
|
||||
Some(pa.read(&mut context.pool()).await?)
|
||||
let cursor_data = if let Some(cursor) = &data.page_cursor {
|
||||
Some(ReportCombinedView::from_cursor(cursor, &mut context.pool()).await?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let page_back = data.page_back;
|
||||
|
||||
let reports = ReportCombinedQuery {
|
||||
community_id: data.community_id,
|
||||
post_id: data.post_id,
|
||||
type_: data.type_,
|
||||
unresolved_only: data.unresolved_only,
|
||||
page_after,
|
||||
page_back,
|
||||
cursor_data,
|
||||
page_back: data.page_back,
|
||||
show_community_rule_violations: data.show_community_rule_violations,
|
||||
my_reports_only,
|
||||
}
|
||||
.list(&mut context.pool(), &local_user_view)
|
||||
.await?;
|
||||
|
||||
Ok(Json(ListReportsResponse { reports }))
|
||||
let next_page = reports.last().map(PaginationCursorBuilder::to_cursor);
|
||||
|
||||
Ok(Json(ListReportsResponse { reports, next_page }))
|
||||
}
|
||||
|
|
|
@ -4,8 +4,11 @@ use lemmy_api_common::{
|
|||
site::{GetModlog, GetModlogResponse},
|
||||
utils::{check_community_mod_of_any_or_admin_action, check_private_instance},
|
||||
};
|
||||
use lemmy_db_schema::source::local_site::LocalSite;
|
||||
use lemmy_db_views::{combined::modlog_combined_view::ModlogCombinedQuery, structs::LocalUserView};
|
||||
use lemmy_db_schema::{source::local_site::LocalSite, traits::PaginationCursorBuilder};
|
||||
use lemmy_db_views::{
|
||||
combined::modlog_combined_view::ModlogCombinedQuery,
|
||||
structs::{LocalUserView, ModlogCombinedView},
|
||||
};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
pub async fn get_mod_log(
|
||||
|
@ -17,10 +20,6 @@ pub async fn get_mod_log(
|
|||
|
||||
check_private_instance(&local_user_view, &local_site)?;
|
||||
|
||||
let type_ = data.type_;
|
||||
let listing_type = data.listing_type;
|
||||
let community_id = data.community_id;
|
||||
|
||||
let is_mod_or_admin = if let Some(local_user_view) = &local_user_view {
|
||||
check_community_mod_of_any_or_admin_action(local_user_view, &mut context.pool())
|
||||
.await
|
||||
|
@ -35,34 +34,30 @@ pub async fn get_mod_log(
|
|||
} else {
|
||||
data.mod_person_id
|
||||
};
|
||||
let other_person_id = data.other_person_id;
|
||||
let post_id = data.post_id;
|
||||
let comment_id = data.comment_id;
|
||||
let local_user = local_user_view.as_ref().map(|u| &u.local_user);
|
||||
|
||||
// parse pagination token
|
||||
let page_after = if let Some(pa) = &data.page_cursor {
|
||||
Some(pa.read(&mut context.pool()).await?)
|
||||
let cursor_data = if let Some(cursor) = &data.page_cursor {
|
||||
Some(ModlogCombinedView::from_cursor(cursor, &mut context.pool()).await?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let page_back = data.page_back;
|
||||
|
||||
let modlog = ModlogCombinedQuery {
|
||||
type_,
|
||||
listing_type,
|
||||
community_id,
|
||||
type_: data.type_,
|
||||
listing_type: data.listing_type,
|
||||
community_id: data.community_id,
|
||||
mod_person_id,
|
||||
other_person_id,
|
||||
local_user,
|
||||
post_id,
|
||||
comment_id,
|
||||
other_person_id: data.other_person_id,
|
||||
local_user: local_user_view.as_ref().map(|u| &u.local_user),
|
||||
post_id: data.post_id,
|
||||
comment_id: data.comment_id,
|
||||
hide_modlog_names: Some(hide_modlog_names),
|
||||
page_after,
|
||||
page_back,
|
||||
cursor_data,
|
||||
page_back: data.page_back,
|
||||
}
|
||||
.list(&mut context.pool())
|
||||
.await?;
|
||||
|
||||
Ok(Json(GetModlogResponse { modlog }))
|
||||
let next_page = modlog.last().map(PaginationCursorBuilder::to_cursor);
|
||||
|
||||
Ok(Json(GetModlogResponse { modlog, next_page }))
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ use lemmy_db_schema::{
|
|||
CommentReplyId,
|
||||
CommunityId,
|
||||
LanguageId,
|
||||
PaginationCursor,
|
||||
PersonCommentMentionId,
|
||||
PersonId,
|
||||
PersonPostMentionId,
|
||||
|
@ -18,12 +19,10 @@ use lemmy_db_schema::{
|
|||
};
|
||||
use lemmy_db_views::structs::{
|
||||
CommunityModeratorView,
|
||||
InboxCombinedPaginationCursor,
|
||||
InboxCombinedView,
|
||||
LocalImageView,
|
||||
PersonContentCombinedPaginationCursor,
|
||||
PersonContentCombinedView,
|
||||
PersonSavedCombinedPaginationCursor,
|
||||
PersonSavedCombinedView,
|
||||
PersonView,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -264,7 +263,7 @@ pub struct ListPersonContent {
|
|||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub username: Option<String>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub page_cursor: Option<PersonContentCombinedPaginationCursor>,
|
||||
pub page_cursor: Option<PaginationCursor>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub page_back: Option<bool>,
|
||||
}
|
||||
|
@ -276,6 +275,9 @@ pub struct ListPersonContent {
|
|||
/// A person's content response.
|
||||
pub struct ListPersonContentResponse {
|
||||
pub content: Vec<PersonContentCombinedView>,
|
||||
/// the pagination cursor to use to fetch the next page
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub next_page: Option<PaginationCursor>,
|
||||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
|
@ -287,7 +289,7 @@ pub struct ListPersonSaved {
|
|||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub type_: Option<PersonContentType>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub page_cursor: Option<PersonSavedCombinedPaginationCursor>,
|
||||
pub page_cursor: Option<PaginationCursor>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub page_back: Option<bool>,
|
||||
}
|
||||
|
@ -298,7 +300,10 @@ pub struct ListPersonSaved {
|
|||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// A person's saved content response.
|
||||
pub struct ListPersonSavedResponse {
|
||||
pub saved: Vec<PersonContentCombinedView>,
|
||||
pub saved: Vec<PersonSavedCombinedView>,
|
||||
/// the pagination cursor to use to fetch the next page
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub next_page: Option<PaginationCursor>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Copy, Default, PartialEq, Eq)]
|
||||
|
@ -386,7 +391,7 @@ pub struct ListInbox {
|
|||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub unread_only: Option<bool>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub page_cursor: Option<InboxCombinedPaginationCursor>,
|
||||
pub page_cursor: Option<PaginationCursor>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub page_back: Option<bool>,
|
||||
}
|
||||
|
@ -397,6 +402,9 @@ pub struct ListInbox {
|
|||
/// Get your inbox (replies, comment mentions, post mentions, and messages)
|
||||
pub struct ListInboxResponse {
|
||||
pub inbox: Vec<InboxCombinedView>,
|
||||
/// the pagination cursor to use to fetch the next page
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub next_page: Option<PaginationCursor>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Copy, Default, PartialEq, Eq, Hash)]
|
||||
|
|
|
@ -4,7 +4,7 @@ use lemmy_db_schema::{
|
|||
PostFeatureType,
|
||||
PostSortType,
|
||||
};
|
||||
use lemmy_db_views::structs::{CommunityView, PaginationCursor, PostView, VoteView};
|
||||
use lemmy_db_views::structs::{CommunityView, PostPaginationCursor, PostView, VoteView};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::skip_serializing_none;
|
||||
#[cfg(feature = "full")]
|
||||
|
@ -125,7 +125,7 @@ pub struct GetPosts {
|
|||
/// If true, then only show posts with no comments
|
||||
pub no_comments_only: Option<bool>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub page_cursor: Option<PaginationCursor>,
|
||||
pub page_cursor: Option<PostPaginationCursor>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub page_back: Option<bool>,
|
||||
}
|
||||
|
@ -139,7 +139,7 @@ pub struct GetPostsResponse {
|
|||
pub posts: Vec<PostView>,
|
||||
/// the pagination cursor to use to fetch the next page
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub next_page: Option<PaginationCursor>,
|
||||
pub next_page: Option<PostPaginationCursor>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Copy, Default, PartialEq, Eq, Hash)]
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use lemmy_db_schema::{
|
||||
newtypes::{CommunityId, PostId},
|
||||
newtypes::{CommunityId, PaginationCursor, PostId},
|
||||
ReportType,
|
||||
};
|
||||
use lemmy_db_views::structs::{ReportCombinedPaginationCursor, ReportCombinedView};
|
||||
use lemmy_db_views::structs::ReportCombinedView;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::skip_serializing_none;
|
||||
#[cfg(feature = "full")]
|
||||
|
@ -27,7 +27,7 @@ pub struct ListReports {
|
|||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub community_id: Option<CommunityId>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub page_cursor: Option<ReportCombinedPaginationCursor>,
|
||||
pub page_cursor: Option<PaginationCursor>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub page_back: Option<bool>,
|
||||
/// Only for admins: also show reports with `violates_instance_rules=false`
|
||||
|
@ -44,4 +44,7 @@ pub struct ListReports {
|
|||
/// The post reports response.
|
||||
pub struct ListReportsResponse {
|
||||
pub reports: Vec<ReportCombinedView>,
|
||||
/// the pagination cursor to use to fetch the next page
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub next_page: Option<PaginationCursor>,
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ use lemmy_db_schema::{
|
|||
CommunityId,
|
||||
InstanceId,
|
||||
LanguageId,
|
||||
PaginationCursor,
|
||||
PersonId,
|
||||
PostId,
|
||||
RegistrationApplicationId,
|
||||
|
@ -36,12 +37,10 @@ use lemmy_db_views::structs::{
|
|||
CommunityModeratorView,
|
||||
CommunityView,
|
||||
LocalUserView,
|
||||
ModlogCombinedPaginationCursor,
|
||||
ModlogCombinedView,
|
||||
PersonView,
|
||||
PostView,
|
||||
RegistrationApplicationView,
|
||||
SearchCombinedPaginationCursor,
|
||||
SearchCombinedView,
|
||||
SiteView,
|
||||
};
|
||||
|
@ -83,7 +82,7 @@ pub struct Search {
|
|||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub disliked_only: Option<bool>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub page_cursor: Option<SearchCombinedPaginationCursor>,
|
||||
pub page_cursor: Option<PaginationCursor>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub page_back: Option<bool>,
|
||||
}
|
||||
|
@ -94,6 +93,9 @@ pub struct Search {
|
|||
/// The search response, containing lists of the return type possibilities
|
||||
pub struct SearchResponse {
|
||||
pub results: Vec<SearchCombinedView>,
|
||||
/// the pagination cursor to use to fetch the next page
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub next_page: Option<PaginationCursor>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq, Eq, Hash)]
|
||||
|
@ -151,7 +153,7 @@ pub struct GetModlog {
|
|||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub comment_id: Option<CommentId>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub page_cursor: Option<ModlogCombinedPaginationCursor>,
|
||||
pub page_cursor: Option<PaginationCursor>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub page_back: Option<bool>,
|
||||
}
|
||||
|
@ -162,6 +164,9 @@ pub struct GetModlog {
|
|||
/// The modlog fetch response.
|
||||
pub struct GetModlogResponse {
|
||||
pub modlog: Vec<ModlogCombinedView>,
|
||||
/// the pagination cursor to use to fetch the next page
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub next_page: Option<PaginationCursor>,
|
||||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
|
|
|
@ -6,9 +6,10 @@ use lemmy_api_common::{
|
|||
person::{ListPersonContent, ListPersonContentResponse},
|
||||
utils::check_private_instance,
|
||||
};
|
||||
use lemmy_db_schema::traits::PaginationCursorBuilder;
|
||||
use lemmy_db_views::{
|
||||
combined::person_content_combined_view::PersonContentCombinedQuery,
|
||||
structs::{LocalUserView, SiteView},
|
||||
structs::{LocalUserView, PersonContentCombinedView, SiteView},
|
||||
};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
|
@ -29,23 +30,22 @@ pub async fn list_person_content(
|
|||
)
|
||||
.await?;
|
||||
|
||||
// parse pagination token
|
||||
let page_after = if let Some(pa) = &data.page_cursor {
|
||||
Some(pa.read(&mut context.pool()).await?)
|
||||
let cursor_data = if let Some(cursor) = &data.page_cursor {
|
||||
Some(PersonContentCombinedView::from_cursor(cursor, &mut context.pool()).await?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let page_back = data.page_back;
|
||||
let type_ = data.type_;
|
||||
|
||||
let content = PersonContentCombinedQuery {
|
||||
creator_id: person_details_id,
|
||||
type_,
|
||||
page_after,
|
||||
page_back,
|
||||
type_: data.type_,
|
||||
cursor_data,
|
||||
page_back: data.page_back,
|
||||
}
|
||||
.list(&mut context.pool(), &local_user_view)
|
||||
.await?;
|
||||
|
||||
Ok(Json(ListPersonContentResponse { content }))
|
||||
let next_page = content.last().map(PaginationCursorBuilder::to_cursor);
|
||||
|
||||
Ok(Json(ListPersonContentResponse { content, next_page }))
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ use lemmy_db_schema::{
|
|||
};
|
||||
use lemmy_db_views::{
|
||||
post::post_view::PostQuery,
|
||||
structs::{LocalUserView, PaginationCursor, SiteView},
|
||||
structs::{LocalUserView, PostPaginationCursor, SiteView},
|
||||
};
|
||||
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||
|
||||
|
@ -116,6 +116,6 @@ pub async fn list_posts(
|
|||
}
|
||||
|
||||
// if this page wasn't empty, then there is a next page after the last post on this page
|
||||
let next_page = posts.last().map(PaginationCursor::after_post);
|
||||
let next_page = posts.last().map(PostPaginationCursor::after_post);
|
||||
Ok(Json(GetPostsResponse { posts, next_page }))
|
||||
}
|
||||
|
|
|
@ -6,10 +6,10 @@ use lemmy_api_common::{
|
|||
site::{Search, SearchResponse},
|
||||
utils::{check_conflicting_like_filters, check_private_instance},
|
||||
};
|
||||
use lemmy_db_schema::source::community::Community;
|
||||
use lemmy_db_schema::{source::community::Community, traits::PaginationCursorBuilder};
|
||||
use lemmy_db_views::{
|
||||
combined::search_combined_view::SearchCombinedQuery,
|
||||
structs::{LocalUserView, SiteView},
|
||||
structs::{LocalUserView, SearchCombinedView, SiteView},
|
||||
};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
|
@ -32,34 +32,32 @@ pub async fn search(
|
|||
} else {
|
||||
data.community_id
|
||||
};
|
||||
let search_term = data.search_term.clone();
|
||||
let time_range_seconds = data.time_range_seconds;
|
||||
|
||||
// parse pagination token
|
||||
let page_after = if let Some(pa) = &data.page_cursor {
|
||||
Some(pa.read(&mut context.pool()).await?)
|
||||
let cursor_data = if let Some(cursor) = &data.page_cursor {
|
||||
Some(SearchCombinedView::from_cursor(cursor, &mut context.pool()).await?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let page_back = data.page_back;
|
||||
|
||||
let results = SearchCombinedQuery {
|
||||
search_term,
|
||||
search_term: data.search_term.clone(),
|
||||
community_id,
|
||||
creator_id: data.creator_id,
|
||||
type_: data.type_,
|
||||
sort: data.sort,
|
||||
time_range_seconds,
|
||||
time_range_seconds: data.time_range_seconds,
|
||||
listing_type: data.listing_type,
|
||||
title_only: data.title_only,
|
||||
post_url_only: data.post_url_only,
|
||||
liked_only: data.liked_only,
|
||||
disliked_only: data.disliked_only,
|
||||
page_after,
|
||||
page_back,
|
||||
cursor_data,
|
||||
page_back: data.page_back,
|
||||
}
|
||||
.list(&mut context.pool(), &local_user_view)
|
||||
.await?;
|
||||
|
||||
Ok(Json(SearchResponse { results }))
|
||||
let next_page = results.last().map(PaginationCursorBuilder::to_cursor);
|
||||
|
||||
Ok(Json(SearchResponse { results, next_page }))
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ use lemmy_db_schema::{
|
|||
utils::{build_db_pool, get_conn, now},
|
||||
PostSortType,
|
||||
};
|
||||
use lemmy_db_views::{post::post_view::PostQuery, structs::PaginationCursor};
|
||||
use lemmy_db_views::{post::post_view::PostQuery, structs::PostPaginationCursor};
|
||||
use lemmy_utils::error::{LemmyErrorExt2, LemmyResult};
|
||||
use std::num::NonZeroU32;
|
||||
use url::Url;
|
||||
|
@ -162,7 +162,7 @@ async fn try_main() -> LemmyResult<()> {
|
|||
|
||||
if let Some(post_view) = post_views.into_iter().next_back() {
|
||||
println!("👀 getting pagination cursor data for next page");
|
||||
let cursor_data = PaginationCursor::after_post(&post_view)
|
||||
let cursor_data = PostPaginationCursor::after_post(&post_view)
|
||||
.read(&mut conn.into(), None)
|
||||
.await?;
|
||||
page_after = Some(cursor_data);
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use crate::{
|
||||
diesel::{BoolExpressionMethods, NullableExpressionMethods, OptionalExtension},
|
||||
newtypes::{CommunityId, DbUrl, PersonId, PostId},
|
||||
schema::{community, person, post, post_actions},
|
||||
source::post::{
|
||||
|
@ -22,6 +21,7 @@ use crate::{
|
|||
get_conn,
|
||||
now,
|
||||
uplete,
|
||||
DbConn,
|
||||
DbPool,
|
||||
DELETED_REPLACEMENT_TEXT,
|
||||
FETCH_LIMIT_MAX,
|
||||
|
@ -35,8 +35,11 @@ use diesel::{
|
|||
dsl::{count, insert_into, not},
|
||||
expression::SelectableHelper,
|
||||
result::Error,
|
||||
BoolExpressionMethods,
|
||||
DecoratableTarget,
|
||||
ExpressionMethods,
|
||||
NullableExpressionMethods,
|
||||
OptionalExtension,
|
||||
QueryDsl,
|
||||
TextExpressionMethods,
|
||||
};
|
||||
|
@ -436,6 +439,14 @@ impl PostActionsCursor {
|
|||
) -> Result<Self, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
|
||||
Self::read_conn(conn, post_id, person_id).await
|
||||
}
|
||||
|
||||
pub async fn read_conn(
|
||||
conn: &mut DbConn<'_>,
|
||||
post_id: PostId,
|
||||
person_id: Option<PersonId>,
|
||||
) -> Result<Self, Error> {
|
||||
Ok(if let Some(person_id) = person_id {
|
||||
post_actions::table
|
||||
.find((person_id, post_id))
|
||||
|
|
|
@ -15,6 +15,8 @@ use diesel::{
|
|||
};
|
||||
#[cfg(feature = "full")]
|
||||
use diesel_ltree::Ltree;
|
||||
#[cfg(feature = "full")]
|
||||
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
fmt,
|
||||
|
@ -420,3 +422,31 @@ impl InstanceId {
|
|||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// The internal tag id.
|
||||
pub struct TagId(pub i32);
|
||||
|
||||
/// A pagination cursor
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "full", derive(TS))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
pub struct PaginationCursor(pub String);
|
||||
|
||||
#[cfg(feature = "full")]
|
||||
impl PaginationCursor {
|
||||
pub fn new(prefix: char, id: i32) -> Self {
|
||||
// hex encoding to prevent ossification
|
||||
Self(format!("{prefix}{id:x}"))
|
||||
}
|
||||
|
||||
pub fn prefix_and_id(&self) -> LemmyResult<(char, i32)> {
|
||||
let (prefix_str, id_str) = self
|
||||
.0
|
||||
.split_at_checked(1)
|
||||
.ok_or(LemmyErrorType::CouldntParsePaginationToken)?;
|
||||
let prefix = prefix_str
|
||||
.chars()
|
||||
.next()
|
||||
.ok_or(LemmyErrorType::CouldntParsePaginationToken)?;
|
||||
let id = i32::from_str_radix(id_str, 16)?;
|
||||
|
||||
Ok((prefix, id))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
newtypes::{CommunityId, DbUrl, PersonId},
|
||||
newtypes::{CommunityId, DbUrl, PaginationCursor, PersonId},
|
||||
utils::{get_conn, uplete, DbPool},
|
||||
};
|
||||
use diesel::{
|
||||
|
@ -257,3 +257,18 @@ pub trait InternalToCombinedView {
|
|||
/// Maps the combined DB row to an enum
|
||||
fn map_to_enum(self) -> Option<Self::CombinedView>;
|
||||
}
|
||||
|
||||
pub trait PaginationCursorBuilder {
|
||||
type CursorData;
|
||||
|
||||
/// Builds a pagination cursor for the given query result.
|
||||
fn to_cursor(&self) -> PaginationCursor;
|
||||
|
||||
/// Reads a database row from a given pagination cursor.
|
||||
fn from_cursor(
|
||||
cursor: &PaginationCursor,
|
||||
conn: &mut DbPool<'_>,
|
||||
) -> impl Future<Output = LemmyResult<Self::CursorData>> + Send
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use crate::structs::{
|
||||
CommentReplyView,
|
||||
InboxCombinedPaginationCursor,
|
||||
InboxCombinedView,
|
||||
InboxCombinedViewInternal,
|
||||
PersonCommentMentionView,
|
||||
|
@ -22,7 +21,7 @@ use i_love_jesus::PaginatedQueryBuilder;
|
|||
use lemmy_db_schema::{
|
||||
aliases::{self, creator_community_actions, creator_local_user},
|
||||
impls::{community::community_follower_select_subscribed_type, local_user::local_user_can_mod},
|
||||
newtypes::PersonId,
|
||||
newtypes::{PaginationCursor, PersonId},
|
||||
schema::{
|
||||
comment,
|
||||
comment_actions,
|
||||
|
@ -46,11 +45,11 @@ use lemmy_db_schema::{
|
|||
tag,
|
||||
},
|
||||
source::combined::inbox::{inbox_combined_keys as key, InboxCombined},
|
||||
traits::InternalToCombinedView,
|
||||
traits::{InternalToCombinedView, PaginationCursorBuilder},
|
||||
utils::{functions::coalesce, get_conn, DbPool},
|
||||
InboxDataType,
|
||||
};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
|
||||
|
||||
impl InboxCombinedViewInternal {
|
||||
#[diesel::dsl::auto_type(no_type_alias)]
|
||||
|
@ -218,48 +217,49 @@ impl InboxCombinedViewInternal {
|
|||
}
|
||||
}
|
||||
|
||||
impl InboxCombinedPaginationCursor {
|
||||
// get cursor for page that starts immediately after the given post
|
||||
pub fn after_post(view: &InboxCombinedView) -> InboxCombinedPaginationCursor {
|
||||
let (prefix, id) = match view {
|
||||
impl PaginationCursorBuilder for InboxCombinedView {
|
||||
type CursorData = InboxCombined;
|
||||
|
||||
fn to_cursor(&self) -> PaginationCursor {
|
||||
let (prefix, id) = match &self {
|
||||
InboxCombinedView::CommentReply(v) => ('R', v.comment_reply.id.0),
|
||||
InboxCombinedView::CommentMention(v) => ('C', v.person_comment_mention.id.0),
|
||||
InboxCombinedView::PostMention(v) => ('P', v.person_post_mention.id.0),
|
||||
InboxCombinedView::PrivateMessage(v) => ('M', v.private_message.id.0),
|
||||
};
|
||||
// hex encoding to prevent ossification
|
||||
InboxCombinedPaginationCursor(format!("{prefix}{id:x}"))
|
||||
PaginationCursor::new(prefix, id)
|
||||
}
|
||||
|
||||
pub async fn read(&self, pool: &mut DbPool<'_>) -> Result<PaginationCursorData, Error> {
|
||||
let err_msg = || Error::QueryBuilderError("Could not parse pagination token".into());
|
||||
let mut query = inbox_combined::table
|
||||
.select(InboxCombined::as_select())
|
||||
.into_boxed();
|
||||
let (prefix, id_str) = self.0.split_at_checked(1).ok_or_else(err_msg)?;
|
||||
let id = i32::from_str_radix(id_str, 16).map_err(|_err| err_msg())?;
|
||||
query = match prefix {
|
||||
"R" => query.filter(inbox_combined::comment_reply_id.eq(id)),
|
||||
"C" => query.filter(inbox_combined::person_comment_mention_id.eq(id)),
|
||||
"P" => query.filter(inbox_combined::person_post_mention_id.eq(id)),
|
||||
"M" => query.filter(inbox_combined::private_message_id.eq(id)),
|
||||
_ => return Err(err_msg()),
|
||||
};
|
||||
let token = query.first(&mut get_conn(pool).await?).await?;
|
||||
async fn from_cursor(
|
||||
cursor: &PaginationCursor,
|
||||
pool: &mut DbPool<'_>,
|
||||
) -> LemmyResult<Self::CursorData> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
let (prefix, id) = cursor.prefix_and_id()?;
|
||||
|
||||
Ok(PaginationCursorData(token))
|
||||
let mut query = inbox_combined::table
|
||||
.select(Self::CursorData::as_select())
|
||||
.into_boxed();
|
||||
|
||||
query = match prefix {
|
||||
'R' => query.filter(inbox_combined::comment_reply_id.eq(id)),
|
||||
'C' => query.filter(inbox_combined::person_comment_mention_id.eq(id)),
|
||||
'P' => query.filter(inbox_combined::person_post_mention_id.eq(id)),
|
||||
'M' => query.filter(inbox_combined::private_message_id.eq(id)),
|
||||
_ => return Err(LemmyErrorType::CouldntParsePaginationToken.into()),
|
||||
};
|
||||
let token = query.first(conn).await?;
|
||||
|
||||
Ok(token)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PaginationCursorData(InboxCombined);
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct InboxCombinedQuery {
|
||||
pub type_: Option<InboxDataType>,
|
||||
pub unread_only: Option<bool>,
|
||||
pub show_bot_accounts: Option<bool>,
|
||||
pub page_after: Option<PaginationCursorData>,
|
||||
pub cursor_data: Option<InboxCombined>,
|
||||
pub page_back: Option<bool>,
|
||||
}
|
||||
|
||||
|
@ -397,12 +397,10 @@ impl InboxCombinedQuery {
|
|||
|
||||
let mut query = PaginatedQueryBuilder::new(query);
|
||||
|
||||
let page_after = self.page_after.map(|c| c.0);
|
||||
|
||||
if self.page_back.unwrap_or_default() {
|
||||
query = query.before(page_after).limit_and_offset_from_end();
|
||||
query = query.before(self.cursor_data).limit_and_offset_from_end();
|
||||
} else {
|
||||
query = query.after(page_after);
|
||||
query = query.after(self.cursor_data);
|
||||
}
|
||||
|
||||
// Sorting by published
|
||||
|
|
|
@ -16,12 +16,10 @@ use crate::structs::{
|
|||
ModRemoveCommunityView,
|
||||
ModRemovePostView,
|
||||
ModTransferCommunityView,
|
||||
ModlogCombinedPaginationCursor,
|
||||
ModlogCombinedView,
|
||||
ModlogCombinedViewInternal,
|
||||
};
|
||||
use diesel::{
|
||||
result::Error,
|
||||
BoolExpressionMethods,
|
||||
ExpressionMethods,
|
||||
IntoSql,
|
||||
|
@ -35,7 +33,7 @@ use i_love_jesus::PaginatedQueryBuilder;
|
|||
use lemmy_db_schema::{
|
||||
aliases,
|
||||
impls::local_user::LocalUserOptionHelper,
|
||||
newtypes::{CommentId, CommunityId, PersonId, PostId},
|
||||
newtypes::{CommentId, CommunityId, PaginationCursor, PersonId, PostId},
|
||||
schema::{
|
||||
admin_allow_instance,
|
||||
admin_block_instance,
|
||||
|
@ -66,12 +64,13 @@ use lemmy_db_schema::{
|
|||
combined::modlog::{modlog_combined_keys as key, ModlogCombined},
|
||||
local_user::LocalUser,
|
||||
},
|
||||
traits::InternalToCombinedView,
|
||||
traits::{InternalToCombinedView, PaginationCursorBuilder},
|
||||
utils::{get_conn, DbPool},
|
||||
ListingType,
|
||||
ModlogActionType,
|
||||
};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
|
||||
|
||||
impl ModlogCombinedViewInternal {
|
||||
#[diesel::dsl::auto_type(no_type_alias)]
|
||||
fn joins(
|
||||
|
@ -228,81 +227,69 @@ impl ModlogCombinedViewInternal {
|
|||
}
|
||||
}
|
||||
|
||||
impl ModlogCombinedPaginationCursor {
|
||||
// get cursor for page that starts immediately after the given post
|
||||
pub fn after_post(view: &ModlogCombinedView) -> ModlogCombinedPaginationCursor {
|
||||
let (prefix, id) = match view {
|
||||
ModlogCombinedView::AdminAllowInstance(v) => {
|
||||
("AdminAllowInstance", v.admin_allow_instance.id.0)
|
||||
}
|
||||
ModlogCombinedView::AdminBlockInstance(v) => {
|
||||
("AdminBlockInstance", v.admin_block_instance.id.0)
|
||||
}
|
||||
ModlogCombinedView::AdminPurgeComment(v) => ("AdminPurgeComment", v.admin_purge_comment.id.0),
|
||||
ModlogCombinedView::AdminPurgeCommunity(v) => {
|
||||
("AdminPurgeCommunity", v.admin_purge_community.id.0)
|
||||
}
|
||||
ModlogCombinedView::AdminPurgePerson(v) => ("AdminPurgePerson", v.admin_purge_person.id.0),
|
||||
ModlogCombinedView::AdminPurgePost(v) => ("AdminPurgePost", v.admin_purge_post.id.0),
|
||||
ModlogCombinedView::ModAdd(v) => ("ModAdd", v.mod_add.id.0),
|
||||
ModlogCombinedView::ModAddCommunity(v) => ("ModAddCommunity", v.mod_add_community.id.0),
|
||||
ModlogCombinedView::ModBan(v) => ("ModBan", v.mod_ban.id.0),
|
||||
ModlogCombinedView::ModBanFromCommunity(v) => {
|
||||
("ModBanFromCommunity", v.mod_ban_from_community.id.0)
|
||||
}
|
||||
ModlogCombinedView::ModFeaturePost(v) => ("ModFeaturePost", v.mod_feature_post.id.0),
|
||||
ModlogCombinedView::ModHideCommunity(v) => ("ModHideCommunity", v.mod_hide_community.id.0),
|
||||
ModlogCombinedView::ModLockPost(v) => ("ModLockPost", v.mod_lock_post.id.0),
|
||||
ModlogCombinedView::ModRemoveComment(v) => ("ModRemoveComment", v.mod_remove_comment.id.0),
|
||||
ModlogCombinedView::ModRemoveCommunity(v) => {
|
||||
("ModRemoveCommunity", v.mod_remove_community.id.0)
|
||||
}
|
||||
ModlogCombinedView::ModRemovePost(v) => ("ModRemovePost", v.mod_remove_post.id.0),
|
||||
ModlogCombinedView::ModTransferCommunity(v) => {
|
||||
("ModTransferCommunity", v.mod_transfer_community.id.0)
|
||||
}
|
||||
impl PaginationCursorBuilder for ModlogCombinedView {
|
||||
type CursorData = ModlogCombined;
|
||||
fn to_cursor(&self) -> PaginationCursor {
|
||||
let (prefix, id) = match &self {
|
||||
ModlogCombinedView::AdminAllowInstance(v) => ('A', v.admin_allow_instance.id.0),
|
||||
ModlogCombinedView::AdminBlockInstance(v) => ('B', v.admin_block_instance.id.0),
|
||||
ModlogCombinedView::AdminPurgeComment(v) => ('C', v.admin_purge_comment.id.0),
|
||||
ModlogCombinedView::AdminPurgeCommunity(v) => ('D', v.admin_purge_community.id.0),
|
||||
ModlogCombinedView::AdminPurgePerson(v) => ('E', v.admin_purge_person.id.0),
|
||||
ModlogCombinedView::AdminPurgePost(v) => ('F', v.admin_purge_post.id.0),
|
||||
ModlogCombinedView::ModAdd(v) => ('G', v.mod_add.id.0),
|
||||
ModlogCombinedView::ModAddCommunity(v) => ('H', v.mod_add_community.id.0),
|
||||
ModlogCombinedView::ModBan(v) => ('I', v.mod_ban.id.0),
|
||||
ModlogCombinedView::ModBanFromCommunity(v) => ('J', v.mod_ban_from_community.id.0),
|
||||
ModlogCombinedView::ModFeaturePost(v) => ('K', v.mod_feature_post.id.0),
|
||||
ModlogCombinedView::ModHideCommunity(v) => ('L', v.mod_hide_community.id.0),
|
||||
ModlogCombinedView::ModLockPost(v) => ('M', v.mod_lock_post.id.0),
|
||||
ModlogCombinedView::ModRemoveComment(v) => ('N', v.mod_remove_comment.id.0),
|
||||
ModlogCombinedView::ModRemoveCommunity(v) => ('O', v.mod_remove_community.id.0),
|
||||
ModlogCombinedView::ModRemovePost(v) => ('P', v.mod_remove_post.id.0),
|
||||
ModlogCombinedView::ModTransferCommunity(v) => ('Q', v.mod_transfer_community.id.0),
|
||||
};
|
||||
// hex encoding to prevent ossification
|
||||
ModlogCombinedPaginationCursor(format!("{prefix}-{id:x}"))
|
||||
PaginationCursor::new(prefix, id)
|
||||
}
|
||||
|
||||
pub async fn read(&self, pool: &mut DbPool<'_>) -> Result<PaginationCursorData, Error> {
|
||||
let err_msg = || Error::QueryBuilderError("Could not parse pagination token".into());
|
||||
async fn from_cursor(
|
||||
cursor: &PaginationCursor,
|
||||
pool: &mut DbPool<'_>,
|
||||
) -> LemmyResult<Self::CursorData> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
let (prefix, id) = cursor.prefix_and_id()?;
|
||||
|
||||
let mut query = modlog_combined::table
|
||||
.select(ModlogCombined::as_select())
|
||||
.select(Self::CursorData::as_select())
|
||||
.into_boxed();
|
||||
let (prefix, id_str) = self.0.split_once('-').ok_or_else(err_msg)?;
|
||||
let id = i32::from_str_radix(id_str, 16).map_err(|_err| err_msg())?;
|
||||
|
||||
query = match prefix {
|
||||
"AdminAllowInstance" => query.filter(modlog_combined::admin_allow_instance_id.eq(id)),
|
||||
"AdminBlockInstance" => query.filter(modlog_combined::admin_block_instance_id.eq(id)),
|
||||
"AdminPurgeComment" => query.filter(modlog_combined::admin_purge_comment_id.eq(id)),
|
||||
"AdminPurgeCommunity" => query.filter(modlog_combined::admin_purge_community_id.eq(id)),
|
||||
"AdminPurgePerson" => query.filter(modlog_combined::admin_purge_person_id.eq(id)),
|
||||
"AdminPurgePost" => query.filter(modlog_combined::admin_purge_post_id.eq(id)),
|
||||
"ModAdd" => query.filter(modlog_combined::mod_add_id.eq(id)),
|
||||
"ModAddCommunity" => query.filter(modlog_combined::mod_add_community_id.eq(id)),
|
||||
"ModBan" => query.filter(modlog_combined::mod_ban_id.eq(id)),
|
||||
"ModBanFromCommunity" => query.filter(modlog_combined::mod_ban_from_community_id.eq(id)),
|
||||
"ModFeaturePost" => query.filter(modlog_combined::mod_feature_post_id.eq(id)),
|
||||
"ModHideCommunity" => query.filter(modlog_combined::mod_hide_community_id.eq(id)),
|
||||
"ModLockPost" => query.filter(modlog_combined::mod_lock_post_id.eq(id)),
|
||||
"ModRemoveComment" => query.filter(modlog_combined::mod_remove_comment_id.eq(id)),
|
||||
"ModRemoveCommunity" => query.filter(modlog_combined::mod_remove_community_id.eq(id)),
|
||||
"ModRemovePost" => query.filter(modlog_combined::mod_remove_post_id.eq(id)),
|
||||
"ModTransferCommunity" => query.filter(modlog_combined::mod_transfer_community_id.eq(id)),
|
||||
|
||||
_ => return Err(err_msg()),
|
||||
'A' => query.filter(modlog_combined::admin_allow_instance_id.eq(id)),
|
||||
'B' => query.filter(modlog_combined::admin_block_instance_id.eq(id)),
|
||||
'C' => query.filter(modlog_combined::admin_purge_comment_id.eq(id)),
|
||||
'D' => query.filter(modlog_combined::admin_purge_community_id.eq(id)),
|
||||
'E' => query.filter(modlog_combined::admin_purge_person_id.eq(id)),
|
||||
'F' => query.filter(modlog_combined::admin_purge_post_id.eq(id)),
|
||||
'G' => query.filter(modlog_combined::mod_add_id.eq(id)),
|
||||
'H' => query.filter(modlog_combined::mod_add_community_id.eq(id)),
|
||||
'I' => query.filter(modlog_combined::mod_ban_id.eq(id)),
|
||||
'J' => query.filter(modlog_combined::mod_ban_from_community_id.eq(id)),
|
||||
'K' => query.filter(modlog_combined::mod_feature_post_id.eq(id)),
|
||||
'L' => query.filter(modlog_combined::mod_hide_community_id.eq(id)),
|
||||
'M' => query.filter(modlog_combined::mod_lock_post_id.eq(id)),
|
||||
'N' => query.filter(modlog_combined::mod_remove_comment_id.eq(id)),
|
||||
'O' => query.filter(modlog_combined::mod_remove_community_id.eq(id)),
|
||||
'P' => query.filter(modlog_combined::mod_remove_post_id.eq(id)),
|
||||
'Q' => query.filter(modlog_combined::mod_transfer_community_id.eq(id)),
|
||||
_ => return Err(LemmyErrorType::CouldntParsePaginationToken.into()),
|
||||
};
|
||||
let token = query.first(&mut get_conn(pool).await?).await?;
|
||||
|
||||
Ok(PaginationCursorData(token))
|
||||
let token = query.first(conn).await?;
|
||||
|
||||
Ok(token)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PaginationCursorData(ModlogCombined);
|
||||
|
||||
#[derive(Default)]
|
||||
/// Querying / filtering the modlog.
|
||||
pub struct ModlogCombinedQuery<'a> {
|
||||
|
@ -315,7 +302,7 @@ pub struct ModlogCombinedQuery<'a> {
|
|||
pub local_user: Option<&'a LocalUser>,
|
||||
pub mod_person_id: Option<PersonId>,
|
||||
pub other_person_id: Option<PersonId>,
|
||||
pub page_after: Option<PaginationCursorData>,
|
||||
pub cursor_data: Option<ModlogCombined>,
|
||||
pub page_back: Option<bool>,
|
||||
}
|
||||
|
||||
|
@ -392,12 +379,10 @@ impl ModlogCombinedQuery<'_> {
|
|||
|
||||
let mut query = PaginatedQueryBuilder::new(query);
|
||||
|
||||
let page_after = self.page_after.map(|c| c.0);
|
||||
|
||||
if self.page_back.unwrap_or_default() {
|
||||
query = query.before(page_after).limit_and_offset_from_end();
|
||||
query = query.before(self.cursor_data).limit_and_offset_from_end();
|
||||
} else {
|
||||
query = query.after(page_after);
|
||||
query = query.after(self.cursor_data);
|
||||
}
|
||||
|
||||
query = query
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
use crate::structs::{
|
||||
CommentView,
|
||||
LocalUserView,
|
||||
PersonContentCombinedPaginationCursor,
|
||||
PersonContentCombinedView,
|
||||
PersonContentCombinedViewInternal,
|
||||
PostView,
|
||||
};
|
||||
use diesel::{
|
||||
result::Error,
|
||||
BoolExpressionMethods,
|
||||
ExpressionMethods,
|
||||
JoinOnDsl,
|
||||
|
@ -20,7 +18,7 @@ use i_love_jesus::PaginatedQueryBuilder;
|
|||
use lemmy_db_schema::{
|
||||
aliases::{creator_community_actions, creator_local_user},
|
||||
impls::{community::community_follower_select_subscribed_type, local_user::local_user_can_mod},
|
||||
newtypes::PersonId,
|
||||
newtypes::{PaginationCursor, PersonId},
|
||||
schema::{
|
||||
comment,
|
||||
comment_actions,
|
||||
|
@ -32,7 +30,6 @@ use lemmy_db_schema::{
|
|||
person,
|
||||
person_actions,
|
||||
person_content_combined,
|
||||
person_saved_combined,
|
||||
post,
|
||||
post_actions,
|
||||
post_aggregates,
|
||||
|
@ -40,11 +37,11 @@ use lemmy_db_schema::{
|
|||
tag,
|
||||
},
|
||||
source::combined::person_content::{person_content_combined_keys as key, PersonContentCombined},
|
||||
traits::InternalToCombinedView,
|
||||
traits::{InternalToCombinedView, PaginationCursorBuilder},
|
||||
utils::{functions::coalesce, get_conn, DbPool},
|
||||
PersonContentType,
|
||||
};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
|
||||
|
||||
impl PersonContentCombinedViewInternal {
|
||||
#[diesel::dsl::auto_type(no_type_alias)]
|
||||
|
@ -140,142 +137,48 @@ impl PersonContentCombinedViewInternal {
|
|||
.left_join(comment_actions_join)
|
||||
.left_join(image_details_join)
|
||||
}
|
||||
|
||||
#[diesel::dsl::auto_type(no_type_alias)]
|
||||
pub(crate) fn joins_saved(my_person_id: PersonId) -> _ {
|
||||
let item_creator = person::id;
|
||||
|
||||
let comment_join =
|
||||
comment::table.on(person_saved_combined::comment_id.eq(comment::id.nullable()));
|
||||
|
||||
let post_join = post::table.on(
|
||||
person_saved_combined::post_id
|
||||
.eq(post::id.nullable())
|
||||
.or(comment::post_id.eq(post::id)),
|
||||
);
|
||||
|
||||
let item_creator_join = person::table.on(
|
||||
comment::creator_id
|
||||
.eq(item_creator)
|
||||
// Need to filter out the post rows where the post_id given is null
|
||||
// Otherwise you'll get duped post rows
|
||||
.or(
|
||||
post::creator_id
|
||||
.eq(item_creator)
|
||||
.and(person_saved_combined::post_id.is_not_null()),
|
||||
),
|
||||
);
|
||||
|
||||
let community_join = community::table.on(post::community_id.eq(community::id));
|
||||
|
||||
let creator_community_actions_join = creator_community_actions.on(
|
||||
creator_community_actions
|
||||
.field(community_actions::community_id)
|
||||
.eq(post::community_id)
|
||||
.and(
|
||||
creator_community_actions
|
||||
.field(community_actions::person_id)
|
||||
.eq(item_creator),
|
||||
),
|
||||
);
|
||||
|
||||
let local_user_join = local_user::table.on(local_user::person_id.nullable().eq(my_person_id));
|
||||
|
||||
let creator_local_user_join = creator_local_user.on(
|
||||
item_creator
|
||||
.eq(creator_local_user.field(local_user::person_id))
|
||||
.and(creator_local_user.field(local_user::admin).eq(true)),
|
||||
);
|
||||
|
||||
let community_actions_join = community_actions::table.on(
|
||||
community_actions::community_id
|
||||
.eq(post::community_id)
|
||||
.and(community_actions::person_id.eq(my_person_id)),
|
||||
);
|
||||
|
||||
let post_actions_join = post_actions::table.on(
|
||||
post_actions::post_id
|
||||
.eq(post::id)
|
||||
.and(post_actions::person_id.eq(my_person_id)),
|
||||
);
|
||||
|
||||
let person_actions_join = person_actions::table.on(
|
||||
person_actions::target_id
|
||||
.eq(item_creator)
|
||||
.and(person_actions::person_id.eq(my_person_id)),
|
||||
);
|
||||
|
||||
let comment_actions_join = comment_actions::table.on(
|
||||
comment_actions::comment_id
|
||||
.eq(comment::id)
|
||||
.and(comment_actions::person_id.eq(my_person_id)),
|
||||
);
|
||||
|
||||
let post_aggregates_join = post_aggregates::table.on(post::id.eq(post_aggregates::post_id));
|
||||
|
||||
let comment_aggregates_join = comment_aggregates::table
|
||||
.on(person_saved_combined::comment_id.eq(comment_aggregates::comment_id.nullable()));
|
||||
|
||||
let image_details_join =
|
||||
image_details::table.on(post::thumbnail_url.eq(image_details::link.nullable()));
|
||||
|
||||
person_saved_combined::table
|
||||
.left_join(comment_join)
|
||||
.inner_join(post_join)
|
||||
.inner_join(item_creator_join)
|
||||
.inner_join(community_join)
|
||||
.left_join(creator_community_actions_join)
|
||||
.left_join(local_user_join)
|
||||
.left_join(creator_local_user_join)
|
||||
.left_join(community_actions_join)
|
||||
.left_join(post_actions_join)
|
||||
.left_join(person_actions_join)
|
||||
.inner_join(post_aggregates_join)
|
||||
.left_join(comment_aggregates_join)
|
||||
.left_join(comment_actions_join)
|
||||
.left_join(image_details_join)
|
||||
}
|
||||
}
|
||||
|
||||
impl PersonContentCombinedPaginationCursor {
|
||||
// get cursor for page that starts immediately after the given post
|
||||
pub fn after_post(view: &PersonContentCombinedView) -> PersonContentCombinedPaginationCursor {
|
||||
let (prefix, id) = match view {
|
||||
impl PaginationCursorBuilder for PersonContentCombinedView {
|
||||
type CursorData = PersonContentCombined;
|
||||
|
||||
fn to_cursor(&self) -> PaginationCursor {
|
||||
let (prefix, id) = match &self {
|
||||
PersonContentCombinedView::Comment(v) => ('C', v.comment.id.0),
|
||||
PersonContentCombinedView::Post(v) => ('P', v.post.id.0),
|
||||
};
|
||||
// hex encoding to prevent ossification
|
||||
PersonContentCombinedPaginationCursor(format!("{prefix}{id:x}"))
|
||||
PaginationCursor::new(prefix, id)
|
||||
}
|
||||
|
||||
pub async fn read(&self, pool: &mut DbPool<'_>) -> Result<PaginationCursorData, Error> {
|
||||
let err_msg = || Error::QueryBuilderError("Could not parse pagination token".into());
|
||||
let mut query = person_content_combined::table
|
||||
.select(PersonContentCombined::as_select())
|
||||
.into_boxed();
|
||||
let (prefix, id_str) = self.0.split_at_checked(1).ok_or_else(err_msg)?;
|
||||
let id = i32::from_str_radix(id_str, 16).map_err(|_err| err_msg())?;
|
||||
query = match prefix {
|
||||
"C" => query.filter(person_content_combined::comment_id.eq(id)),
|
||||
"P" => query.filter(person_content_combined::post_id.eq(id)),
|
||||
_ => return Err(err_msg()),
|
||||
};
|
||||
let token = query.first(&mut get_conn(pool).await?).await?;
|
||||
async fn from_cursor(
|
||||
cursor: &PaginationCursor,
|
||||
pool: &mut DbPool<'_>,
|
||||
) -> LemmyResult<Self::CursorData> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
let (prefix, id) = cursor.prefix_and_id()?;
|
||||
|
||||
Ok(PaginationCursorData(token))
|
||||
let mut query = person_content_combined::table
|
||||
.select(Self::CursorData::as_select())
|
||||
.into_boxed();
|
||||
|
||||
query = match prefix {
|
||||
'C' => query.filter(person_content_combined::comment_id.eq(id)),
|
||||
'P' => query.filter(person_content_combined::post_id.eq(id)),
|
||||
_ => return Err(LemmyErrorType::CouldntParsePaginationToken.into()),
|
||||
};
|
||||
let token = query.first(conn).await?;
|
||||
|
||||
Ok(token)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PaginationCursorData(PersonContentCombined);
|
||||
|
||||
#[derive(derive_new::new)]
|
||||
pub struct PersonContentCombinedQuery {
|
||||
pub creator_id: PersonId,
|
||||
#[new(default)]
|
||||
pub type_: Option<PersonContentType>,
|
||||
#[new(default)]
|
||||
pub page_after: Option<PaginationCursorData>,
|
||||
pub cursor_data: Option<PersonContentCombined>,
|
||||
#[new(default)]
|
||||
pub page_back: Option<bool>,
|
||||
}
|
||||
|
@ -361,12 +264,10 @@ impl PersonContentCombinedQuery {
|
|||
|
||||
let mut query = PaginatedQueryBuilder::new(query);
|
||||
|
||||
let page_after = self.page_after.map(|c| c.0);
|
||||
|
||||
if self.page_back.unwrap_or_default() {
|
||||
query = query.before(page_after).limit_and_offset_from_end();
|
||||
query = query.before(self.cursor_data).limit_and_offset_from_end();
|
||||
} else {
|
||||
query = query.after(page_after);
|
||||
query = query.after(self.cursor_data);
|
||||
}
|
||||
|
||||
// Sorting by published
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
use crate::structs::{
|
||||
CommentView,
|
||||
LocalUserView,
|
||||
PersonContentCombinedView,
|
||||
PersonContentCombinedViewInternal,
|
||||
PersonSavedCombinedPaginationCursor,
|
||||
PersonSavedCombinedView,
|
||||
PersonSavedCombinedViewInternal,
|
||||
PostView,
|
||||
};
|
||||
use diesel::{
|
||||
result::Error,
|
||||
BoolExpressionMethods,
|
||||
ExpressionMethods,
|
||||
JoinOnDsl,
|
||||
NullableExpressionMethods,
|
||||
QueryDsl,
|
||||
SelectableHelper,
|
||||
|
@ -16,6 +18,7 @@ use i_love_jesus::PaginatedQueryBuilder;
|
|||
use lemmy_db_schema::{
|
||||
aliases::{creator_community_actions, creator_local_user},
|
||||
impls::{community::community_follower_select_subscribed_type, local_user::local_user_can_mod},
|
||||
newtypes::{PaginationCursor, PersonId},
|
||||
schema::{
|
||||
comment,
|
||||
comment_actions,
|
||||
|
@ -34,57 +37,155 @@ use lemmy_db_schema::{
|
|||
tag,
|
||||
},
|
||||
source::combined::person_saved::{person_saved_combined_keys as key, PersonSavedCombined},
|
||||
traits::InternalToCombinedView,
|
||||
traits::{InternalToCombinedView, PaginationCursorBuilder},
|
||||
utils::{functions::coalesce, get_conn, DbPool},
|
||||
PersonContentType,
|
||||
};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
impl PersonSavedCombinedPaginationCursor {
|
||||
// get cursor for page that starts immediately after the given post
|
||||
pub fn after_post(view: &PersonContentCombinedView) -> PersonSavedCombinedPaginationCursor {
|
||||
let (prefix, id) = match view {
|
||||
PersonContentCombinedView::Comment(v) => ('C', v.comment.id.0),
|
||||
PersonContentCombinedView::Post(v) => ('P', v.post.id.0),
|
||||
};
|
||||
// hex encoding to prevent ossification
|
||||
PersonSavedCombinedPaginationCursor(format!("{prefix}{id:x}"))
|
||||
}
|
||||
|
||||
pub async fn read(&self, pool: &mut DbPool<'_>) -> Result<PaginationCursorData, Error> {
|
||||
let err_msg = || Error::QueryBuilderError("Could not parse pagination token".into());
|
||||
let mut query = person_saved_combined::table
|
||||
.select(PersonSavedCombined::as_select())
|
||||
.into_boxed();
|
||||
let (prefix, id_str) = self.0.split_at_checked(1).ok_or_else(err_msg)?;
|
||||
let id = i32::from_str_radix(id_str, 16).map_err(|_err| err_msg())?;
|
||||
query = match prefix {
|
||||
"C" => query.filter(person_saved_combined::comment_id.eq(id)),
|
||||
"P" => query.filter(person_saved_combined::post_id.eq(id)),
|
||||
_ => return Err(err_msg()),
|
||||
};
|
||||
let token = query.first(&mut get_conn(pool).await?).await?;
|
||||
|
||||
Ok(PaginationCursorData(token))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PaginationCursorData(PersonSavedCombined);
|
||||
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct PersonSavedCombinedQuery {
|
||||
pub type_: Option<PersonContentType>,
|
||||
pub page_after: Option<PaginationCursorData>,
|
||||
pub cursor_data: Option<PersonSavedCombined>,
|
||||
pub page_back: Option<bool>,
|
||||
}
|
||||
|
||||
impl PaginationCursorBuilder for PersonSavedCombinedView {
|
||||
type CursorData = PersonSavedCombined;
|
||||
|
||||
fn to_cursor(&self) -> PaginationCursor {
|
||||
let (prefix, id) = match &self {
|
||||
PersonSavedCombinedView::Comment(v) => ('C', v.comment.id.0),
|
||||
PersonSavedCombinedView::Post(v) => ('P', v.post.id.0),
|
||||
};
|
||||
PaginationCursor::new(prefix, id)
|
||||
}
|
||||
|
||||
async fn from_cursor(
|
||||
cursor: &PaginationCursor,
|
||||
pool: &mut DbPool<'_>,
|
||||
) -> LemmyResult<Self::CursorData> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
let (prefix, id) = cursor.prefix_and_id()?;
|
||||
|
||||
let mut query = person_saved_combined::table
|
||||
.select(Self::CursorData::as_select())
|
||||
.into_boxed();
|
||||
|
||||
query = match prefix {
|
||||
'C' => query.filter(person_saved_combined::comment_id.eq(id)),
|
||||
'P' => query.filter(person_saved_combined::post_id.eq(id)),
|
||||
_ => return Err(LemmyErrorType::CouldntParsePaginationToken.into()),
|
||||
};
|
||||
let token = query.first(conn).await?;
|
||||
|
||||
Ok(token)
|
||||
}
|
||||
}
|
||||
|
||||
impl PersonSavedCombinedViewInternal {
|
||||
#[diesel::dsl::auto_type(no_type_alias)]
|
||||
pub(crate) fn joins(my_person_id: PersonId) -> _ {
|
||||
let item_creator = person::id;
|
||||
|
||||
let comment_join =
|
||||
comment::table.on(person_saved_combined::comment_id.eq(comment::id.nullable()));
|
||||
|
||||
let post_join = post::table.on(
|
||||
person_saved_combined::post_id
|
||||
.eq(post::id.nullable())
|
||||
.or(comment::post_id.eq(post::id)),
|
||||
);
|
||||
|
||||
let item_creator_join = person::table.on(
|
||||
comment::creator_id
|
||||
.eq(item_creator)
|
||||
// Need to filter out the post rows where the post_id given is null
|
||||
// Otherwise you'll get duped post rows
|
||||
.or(
|
||||
post::creator_id
|
||||
.eq(item_creator)
|
||||
.and(person_saved_combined::post_id.is_not_null()),
|
||||
),
|
||||
);
|
||||
|
||||
let community_join = community::table.on(post::community_id.eq(community::id));
|
||||
|
||||
let creator_community_actions_join = creator_community_actions.on(
|
||||
creator_community_actions
|
||||
.field(community_actions::community_id)
|
||||
.eq(post::community_id)
|
||||
.and(
|
||||
creator_community_actions
|
||||
.field(community_actions::person_id)
|
||||
.eq(item_creator),
|
||||
),
|
||||
);
|
||||
|
||||
let local_user_join = local_user::table.on(local_user::person_id.nullable().eq(my_person_id));
|
||||
|
||||
let creator_local_user_join = creator_local_user.on(
|
||||
item_creator
|
||||
.eq(creator_local_user.field(local_user::person_id))
|
||||
.and(creator_local_user.field(local_user::admin).eq(true)),
|
||||
);
|
||||
|
||||
let community_actions_join = community_actions::table.on(
|
||||
community_actions::community_id
|
||||
.eq(post::community_id)
|
||||
.and(community_actions::person_id.eq(my_person_id)),
|
||||
);
|
||||
|
||||
let post_actions_join = post_actions::table.on(
|
||||
post_actions::post_id
|
||||
.eq(post::id)
|
||||
.and(post_actions::person_id.eq(my_person_id)),
|
||||
);
|
||||
|
||||
let person_actions_join = person_actions::table.on(
|
||||
person_actions::target_id
|
||||
.eq(item_creator)
|
||||
.and(person_actions::person_id.eq(my_person_id)),
|
||||
);
|
||||
|
||||
let comment_actions_join = comment_actions::table.on(
|
||||
comment_actions::comment_id
|
||||
.eq(comment::id)
|
||||
.and(comment_actions::person_id.eq(my_person_id)),
|
||||
);
|
||||
|
||||
let post_aggregates_join = post_aggregates::table.on(post::id.eq(post_aggregates::post_id));
|
||||
|
||||
let comment_aggregates_join = comment_aggregates::table
|
||||
.on(person_saved_combined::comment_id.eq(comment_aggregates::comment_id.nullable()));
|
||||
|
||||
let image_details_join =
|
||||
image_details::table.on(post::thumbnail_url.eq(image_details::link.nullable()));
|
||||
|
||||
person_saved_combined::table
|
||||
.left_join(comment_join)
|
||||
.inner_join(post_join)
|
||||
.inner_join(item_creator_join)
|
||||
.inner_join(community_join)
|
||||
.left_join(creator_community_actions_join)
|
||||
.left_join(local_user_join)
|
||||
.left_join(creator_local_user_join)
|
||||
.left_join(community_actions_join)
|
||||
.left_join(post_actions_join)
|
||||
.left_join(person_actions_join)
|
||||
.inner_join(post_aggregates_join)
|
||||
.left_join(comment_aggregates_join)
|
||||
.left_join(comment_actions_join)
|
||||
.left_join(image_details_join)
|
||||
}
|
||||
}
|
||||
|
||||
impl PersonSavedCombinedQuery {
|
||||
pub async fn list(
|
||||
self,
|
||||
pool: &mut DbPool<'_>,
|
||||
user: &LocalUserView,
|
||||
) -> LemmyResult<Vec<PersonContentCombinedView>> {
|
||||
) -> LemmyResult<Vec<PersonSavedCombinedView>> {
|
||||
let my_person_id = user.local_user.person_id;
|
||||
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
|
@ -98,7 +199,7 @@ impl PersonSavedCombinedQuery {
|
|||
.filter(tag::deleted.eq(false))
|
||||
.single_value();
|
||||
|
||||
let mut query = PersonContentCombinedViewInternal::joins_saved(my_person_id)
|
||||
let mut query = PersonSavedCombinedViewInternal::joins(my_person_id)
|
||||
.filter(person_saved_combined::person_id.eq(my_person_id))
|
||||
.select((
|
||||
// Post-specific
|
||||
|
@ -153,12 +254,10 @@ impl PersonSavedCombinedQuery {
|
|||
|
||||
let mut query = PaginatedQueryBuilder::new(query);
|
||||
|
||||
let page_after = self.page_after.map(|c| c.0);
|
||||
|
||||
if self.page_back.unwrap_or_default() {
|
||||
query = query.before(page_after).limit_and_offset_from_end();
|
||||
query = query.before(self.cursor_data).limit_and_offset_from_end();
|
||||
} else {
|
||||
query = query.after(page_after);
|
||||
query = query.after(self.cursor_data);
|
||||
}
|
||||
|
||||
// Sorting by saved desc
|
||||
|
@ -167,9 +266,7 @@ impl PersonSavedCombinedQuery {
|
|||
// Tie breaker
|
||||
.then_desc(key::id);
|
||||
|
||||
let res = query
|
||||
.load::<PersonContentCombinedViewInternal>(conn)
|
||||
.await?;
|
||||
let res = query.load::<PersonSavedCombinedViewInternal>(conn).await?;
|
||||
|
||||
// Map the query results to the enum
|
||||
let out = res
|
||||
|
@ -181,13 +278,62 @@ impl PersonSavedCombinedQuery {
|
|||
}
|
||||
}
|
||||
|
||||
impl InternalToCombinedView for PersonSavedCombinedViewInternal {
|
||||
type CombinedView = PersonSavedCombinedView;
|
||||
|
||||
fn map_to_enum(self) -> Option<Self::CombinedView> {
|
||||
// Use for a short alias
|
||||
let v = self;
|
||||
|
||||
if let (Some(comment), Some(counts)) = (v.comment, v.comment_counts) {
|
||||
Some(PersonSavedCombinedView::Comment(CommentView {
|
||||
comment,
|
||||
counts,
|
||||
post: v.post,
|
||||
community: v.community,
|
||||
creator: v.item_creator,
|
||||
creator_banned_from_community: v.item_creator_banned_from_community,
|
||||
creator_is_moderator: v.item_creator_is_moderator,
|
||||
creator_is_admin: v.item_creator_is_admin,
|
||||
creator_blocked: v.item_creator_blocked,
|
||||
subscribed: v.subscribed,
|
||||
saved: v.comment_saved,
|
||||
my_vote: v.my_comment_vote,
|
||||
banned_from_community: v.banned_from_community,
|
||||
can_mod: v.can_mod,
|
||||
}))
|
||||
} else {
|
||||
Some(PersonSavedCombinedView::Post(PostView {
|
||||
post: v.post,
|
||||
community: v.community,
|
||||
unread_comments: v.post_unread_comments,
|
||||
counts: v.post_counts,
|
||||
creator: v.item_creator,
|
||||
creator_banned_from_community: v.item_creator_banned_from_community,
|
||||
creator_is_moderator: v.item_creator_is_moderator,
|
||||
creator_is_admin: v.item_creator_is_admin,
|
||||
creator_blocked: v.item_creator_blocked,
|
||||
subscribed: v.subscribed,
|
||||
saved: v.post_saved,
|
||||
read: v.post_read,
|
||||
hidden: v.post_hidden,
|
||||
my_vote: v.my_post_vote,
|
||||
image_details: v.image_details,
|
||||
banned_from_community: v.banned_from_community,
|
||||
tags: v.post_tags,
|
||||
can_mod: v.can_mod,
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[expect(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
|
||||
use crate::{
|
||||
combined::person_saved_combined_view::PersonSavedCombinedQuery,
|
||||
structs::{LocalUserView, PersonContentCombinedView},
|
||||
structs::{LocalUserView, PersonSavedCombinedView},
|
||||
};
|
||||
use lemmy_db_schema::{
|
||||
source::{
|
||||
|
@ -309,19 +455,19 @@ mod tests {
|
|||
assert_eq!(3, timmy_saved.len());
|
||||
|
||||
// Make sure the types and order are correct
|
||||
if let PersonContentCombinedView::Post(v) = &timmy_saved[0] {
|
||||
if let PersonSavedCombinedView::Post(v) = &timmy_saved[0] {
|
||||
assert_eq!(data.timmy_post.id, v.post.id);
|
||||
assert_eq!(data.timmy.id, v.post.creator_id);
|
||||
} else {
|
||||
panic!("wrong type");
|
||||
}
|
||||
if let PersonContentCombinedView::Comment(v) = &timmy_saved[1] {
|
||||
if let PersonSavedCombinedView::Comment(v) = &timmy_saved[1] {
|
||||
assert_eq!(data.sara_comment.id, v.comment.id);
|
||||
assert_eq!(data.sara.id, v.comment.creator_id);
|
||||
} else {
|
||||
panic!("wrong type");
|
||||
}
|
||||
if let PersonContentCombinedView::Comment(v) = &timmy_saved[2] {
|
||||
if let PersonSavedCombinedView::Comment(v) = &timmy_saved[2] {
|
||||
assert_eq!(data.sara_comment_2.id, v.comment.id);
|
||||
assert_eq!(data.sara.id, v.comment.creator_id);
|
||||
} else {
|
||||
|
@ -337,7 +483,7 @@ mod tests {
|
|||
.await?;
|
||||
assert_eq!(1, timmy_saved.len());
|
||||
|
||||
if let PersonContentCombinedView::Comment(v) = &timmy_saved[0] {
|
||||
if let PersonSavedCombinedView::Comment(v) = &timmy_saved[0] {
|
||||
assert_eq!(data.sara_comment_2.id, v.comment.id);
|
||||
assert_eq!(data.sara.id, v.comment.creator_id);
|
||||
} else {
|
||||
|
|
|
@ -4,7 +4,6 @@ use crate::structs::{
|
|||
LocalUserView,
|
||||
PostReportView,
|
||||
PrivateMessageReportView,
|
||||
ReportCombinedPaginationCursor,
|
||||
ReportCombinedView,
|
||||
ReportCombinedViewInternal,
|
||||
};
|
||||
|
@ -24,7 +23,7 @@ use i_love_jesus::PaginatedQueryBuilder;
|
|||
use lemmy_db_schema::{
|
||||
aliases::{self, creator_community_actions},
|
||||
impls::community::community_follower_select_subscribed_type,
|
||||
newtypes::{CommunityId, PersonId, PostId},
|
||||
newtypes::{CommunityId, PaginationCursor, PersonId, PostId},
|
||||
schema::{
|
||||
comment,
|
||||
comment_actions,
|
||||
|
@ -46,11 +45,11 @@ use lemmy_db_schema::{
|
|||
report_combined,
|
||||
},
|
||||
source::combined::report::{report_combined_keys as key, ReportCombined},
|
||||
traits::InternalToCombinedView,
|
||||
traits::{InternalToCombinedView, PaginationCursorBuilder},
|
||||
utils::{functions::coalesce, get_conn, DbPool, ReverseTimestampKey},
|
||||
ReportType,
|
||||
};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
|
||||
|
||||
impl ReportCombinedViewInternal {
|
||||
#[diesel::dsl::auto_type(no_type_alias)]
|
||||
|
@ -211,42 +210,43 @@ impl ReportCombinedViewInternal {
|
|||
}
|
||||
}
|
||||
|
||||
impl ReportCombinedPaginationCursor {
|
||||
// get cursor for page that starts immediately after the given post
|
||||
pub fn after_post(view: &ReportCombinedView) -> ReportCombinedPaginationCursor {
|
||||
let (prefix, id) = match view {
|
||||
impl PaginationCursorBuilder for ReportCombinedView {
|
||||
type CursorData = ReportCombined;
|
||||
|
||||
fn to_cursor(&self) -> PaginationCursor {
|
||||
let (prefix, id) = match &self {
|
||||
ReportCombinedView::Comment(v) => ('C', v.comment_report.id.0),
|
||||
ReportCombinedView::Post(v) => ('P', v.post_report.id.0),
|
||||
ReportCombinedView::PrivateMessage(v) => ('M', v.private_message_report.id.0),
|
||||
ReportCombinedView::Community(v) => ('Y', v.community_report.id.0),
|
||||
};
|
||||
// hex encoding to prevent ossification
|
||||
ReportCombinedPaginationCursor(format!("{prefix}{id:x}"))
|
||||
PaginationCursor::new(prefix, id)
|
||||
}
|
||||
|
||||
pub async fn read(&self, pool: &mut DbPool<'_>) -> Result<PaginationCursorData, Error> {
|
||||
let err_msg = || Error::QueryBuilderError("Could not parse pagination token".into());
|
||||
let mut query = report_combined::table
|
||||
.select(ReportCombined::as_select())
|
||||
.into_boxed();
|
||||
let (prefix, id_str) = self.0.split_at_checked(1).ok_or_else(err_msg)?;
|
||||
let id = i32::from_str_radix(id_str, 16).map_err(|_err| err_msg())?;
|
||||
query = match prefix {
|
||||
"C" => query.filter(report_combined::comment_report_id.eq(id)),
|
||||
"P" => query.filter(report_combined::post_report_id.eq(id)),
|
||||
"M" => query.filter(report_combined::private_message_report_id.eq(id)),
|
||||
"Y" => query.filter(report_combined::community_report_id.eq(id)),
|
||||
_ => return Err(err_msg()),
|
||||
};
|
||||
let token = query.first(&mut get_conn(pool).await?).await?;
|
||||
async fn from_cursor(
|
||||
cursor: &PaginationCursor,
|
||||
pool: &mut DbPool<'_>,
|
||||
) -> LemmyResult<Self::CursorData> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
let (prefix, id) = cursor.prefix_and_id()?;
|
||||
|
||||
Ok(PaginationCursorData(token))
|
||||
let mut query = report_combined::table
|
||||
.select(Self::CursorData::as_select())
|
||||
.into_boxed();
|
||||
|
||||
query = match prefix {
|
||||
'C' => query.filter(report_combined::comment_report_id.eq(id)),
|
||||
'P' => query.filter(report_combined::post_report_id.eq(id)),
|
||||
'M' => query.filter(report_combined::private_message_report_id.eq(id)),
|
||||
'Y' => query.filter(report_combined::community_report_id.eq(id)),
|
||||
_ => return Err(LemmyErrorType::CouldntParsePaginationToken.into()),
|
||||
};
|
||||
let token = query.first(conn).await?;
|
||||
|
||||
Ok(token)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PaginationCursorData(ReportCombined);
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ReportCombinedQuery {
|
||||
pub type_: Option<ReportType>,
|
||||
|
@ -255,8 +255,8 @@ pub struct ReportCombinedQuery {
|
|||
pub unresolved_only: Option<bool>,
|
||||
/// For admins, also show reports with `violates_instance_rules=false`
|
||||
pub show_community_rule_violations: Option<bool>,
|
||||
pub cursor_data: Option<ReportCombined>,
|
||||
pub my_reports_only: Option<bool>,
|
||||
pub page_after: Option<PaginationCursorData>,
|
||||
pub page_back: Option<bool>,
|
||||
}
|
||||
|
||||
|
@ -342,12 +342,10 @@ impl ReportCombinedQuery {
|
|||
|
||||
let mut query = PaginatedQueryBuilder::new(query);
|
||||
|
||||
let page_after = self.page_after.map(|c| c.0);
|
||||
|
||||
if self.page_back.unwrap_or_default() {
|
||||
query = query.before(page_after).limit_and_offset_from_end();
|
||||
query = query.before(self.cursor_data).limit_and_offset_from_end();
|
||||
} else {
|
||||
query = query.after(page_after);
|
||||
query = query.after(self.cursor_data);
|
||||
}
|
||||
|
||||
if let Some(type_) = self.type_ {
|
||||
|
|
|
@ -4,13 +4,11 @@ use crate::structs::{
|
|||
LocalUserView,
|
||||
PersonView,
|
||||
PostView,
|
||||
SearchCombinedPaginationCursor,
|
||||
SearchCombinedView,
|
||||
SearchCombinedViewInternal,
|
||||
};
|
||||
use diesel::{
|
||||
dsl::not,
|
||||
result::Error,
|
||||
BoolExpressionMethods,
|
||||
ExpressionMethods,
|
||||
JoinOnDsl,
|
||||
|
@ -24,7 +22,7 @@ use i_love_jesus::PaginatedQueryBuilder;
|
|||
use lemmy_db_schema::{
|
||||
aliases::{creator_community_actions, creator_local_user},
|
||||
impls::{community::community_follower_select_subscribed_type, local_user::local_user_can_mod},
|
||||
newtypes::{CommunityId, PersonId},
|
||||
newtypes::{CommunityId, PaginationCursor, PersonId},
|
||||
schema::{
|
||||
comment,
|
||||
comment_actions,
|
||||
|
@ -45,7 +43,7 @@ use lemmy_db_schema::{
|
|||
tag,
|
||||
},
|
||||
source::combined::search::{search_combined_keys as key, SearchCombined},
|
||||
traits::InternalToCombinedView,
|
||||
traits::{InternalToCombinedView, PaginationCursorBuilder},
|
||||
utils::{
|
||||
functions::coalesce,
|
||||
fuzzy_search,
|
||||
|
@ -59,7 +57,7 @@ use lemmy_db_schema::{
|
|||
SearchSortType,
|
||||
SearchType,
|
||||
};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
|
||||
use SearchSortType::*;
|
||||
|
||||
impl SearchCombinedViewInternal {
|
||||
|
@ -183,42 +181,43 @@ impl SearchCombinedViewInternal {
|
|||
}
|
||||
}
|
||||
|
||||
impl SearchCombinedPaginationCursor {
|
||||
// get cursor for page that starts immediately after the given post
|
||||
pub fn after_post(view: &SearchCombinedView) -> SearchCombinedPaginationCursor {
|
||||
let (prefix, id) = match view {
|
||||
impl PaginationCursorBuilder for SearchCombinedView {
|
||||
type CursorData = SearchCombined;
|
||||
|
||||
fn to_cursor(&self) -> PaginationCursor {
|
||||
let (prefix, id) = match &self {
|
||||
SearchCombinedView::Post(v) => ('P', v.post.id.0),
|
||||
SearchCombinedView::Comment(v) => ('C', v.comment.id.0),
|
||||
SearchCombinedView::Community(v) => ('O', v.community.id.0),
|
||||
SearchCombinedView::Person(v) => ('E', v.person.id.0),
|
||||
};
|
||||
// hex encoding to prevent ossification
|
||||
SearchCombinedPaginationCursor(format!("{prefix}{id:x}"))
|
||||
PaginationCursor::new(prefix, id)
|
||||
}
|
||||
|
||||
pub async fn read(&self, pool: &mut DbPool<'_>) -> Result<PaginationCursorData, Error> {
|
||||
let err_msg = || Error::QueryBuilderError("Could not parse pagination token".into());
|
||||
let mut query = search_combined::table
|
||||
.select(SearchCombined::as_select())
|
||||
.into_boxed();
|
||||
let (prefix, id_str) = self.0.split_at_checked(1).ok_or_else(err_msg)?;
|
||||
let id = i32::from_str_radix(id_str, 16).map_err(|_err| err_msg())?;
|
||||
query = match prefix {
|
||||
"P" => query.filter(search_combined::post_id.eq(id)),
|
||||
"C" => query.filter(search_combined::comment_id.eq(id)),
|
||||
"O" => query.filter(search_combined::community_id.eq(id)),
|
||||
"E" => query.filter(search_combined::person_id.eq(id)),
|
||||
_ => return Err(err_msg()),
|
||||
};
|
||||
let token = query.first(&mut get_conn(pool).await?).await?;
|
||||
async fn from_cursor(
|
||||
cursor: &PaginationCursor,
|
||||
pool: &mut DbPool<'_>,
|
||||
) -> LemmyResult<Self::CursorData> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
let (prefix, id) = cursor.prefix_and_id()?;
|
||||
|
||||
Ok(PaginationCursorData(token))
|
||||
let mut query = search_combined::table
|
||||
.select(Self::CursorData::as_select())
|
||||
.into_boxed();
|
||||
|
||||
query = match prefix {
|
||||
'P' => query.filter(search_combined::post_id.eq(id)),
|
||||
'C' => query.filter(search_combined::comment_id.eq(id)),
|
||||
'O' => query.filter(search_combined::community_id.eq(id)),
|
||||
'E' => query.filter(search_combined::person_id.eq(id)),
|
||||
_ => return Err(LemmyErrorType::CouldntParsePaginationToken.into()),
|
||||
};
|
||||
let token = query.first(conn).await?;
|
||||
|
||||
Ok(token)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PaginationCursorData(SearchCombined);
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct SearchCombinedQuery {
|
||||
pub search_term: Option<String>,
|
||||
|
@ -232,7 +231,7 @@ pub struct SearchCombinedQuery {
|
|||
pub post_url_only: Option<bool>,
|
||||
pub liked_only: Option<bool>,
|
||||
pub disliked_only: Option<bool>,
|
||||
pub page_after: Option<PaginationCursorData>,
|
||||
pub cursor_data: Option<SearchCombined>,
|
||||
pub page_back: Option<bool>,
|
||||
}
|
||||
|
||||
|
@ -399,14 +398,18 @@ impl SearchCombinedQuery {
|
|||
}
|
||||
}
|
||||
|
||||
// Filter by the time range
|
||||
if let Some(time_range_seconds) = self.time_range_seconds {
|
||||
query = query
|
||||
.filter(search_combined::published.gt(now() - seconds_to_pg_interval(time_range_seconds)));
|
||||
}
|
||||
|
||||
let mut query = PaginatedQueryBuilder::new(query);
|
||||
|
||||
let page_after = self.page_after.map(|c| c.0);
|
||||
|
||||
if self.page_back.unwrap_or_default() {
|
||||
query = query.before(page_after).limit_and_offset_from_end();
|
||||
query = query.before(self.cursor_data).limit_and_offset_from_end();
|
||||
} else {
|
||||
query = query.after(page_after);
|
||||
query = query.after(self.cursor_data);
|
||||
}
|
||||
|
||||
query = match self.sort.unwrap_or_default() {
|
||||
|
@ -415,12 +418,6 @@ impl SearchCombinedQuery {
|
|||
Top => query.then_desc(key::score),
|
||||
};
|
||||
|
||||
// Filter by the time range
|
||||
if let Some(time_range_seconds) = self.time_range_seconds {
|
||||
query = query
|
||||
.filter(search_combined::published.gt(now() - seconds_to_pg_interval(time_range_seconds)));
|
||||
}
|
||||
|
||||
// finally use unique id as tie breaker
|
||||
query = query.then_desc(key::id);
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
structs::{PaginationCursor, PostView},
|
||||
structs::{PostPaginationCursor, PostView},
|
||||
utils::filter_blocked,
|
||||
};
|
||||
use diesel::{
|
||||
|
@ -229,11 +229,12 @@ impl PostView {
|
|||
}
|
||||
}
|
||||
|
||||
impl PaginationCursor {
|
||||
// TODO This pagination cursor is a mess, get rid of it and have it match the others
|
||||
impl PostPaginationCursor {
|
||||
// get cursor for page that starts immediately after the given post
|
||||
pub fn after_post(view: &PostView) -> PaginationCursor {
|
||||
pub fn after_post(view: &PostView) -> PostPaginationCursor {
|
||||
// hex encoding to prevent ossification
|
||||
PaginationCursor(format!("P{:x}", view.counts.post_id.0))
|
||||
PostPaginationCursor(format!("P{:x}", view.counts.post_id.0))
|
||||
}
|
||||
pub async fn read(
|
||||
&self,
|
||||
|
@ -277,6 +278,7 @@ pub struct PostQuery<'a> {
|
|||
// literal filter
|
||||
pub community_id_just_for_prefetch: bool,
|
||||
pub local_user: Option<&'a LocalUser>,
|
||||
// TODO get rid of this
|
||||
pub search_term: Option<String>,
|
||||
pub url_only: Option<bool>,
|
||||
pub read_only: Option<bool>,
|
||||
|
@ -285,6 +287,7 @@ pub struct PostQuery<'a> {
|
|||
pub title_only: Option<bool>,
|
||||
pub page: Option<i64>,
|
||||
pub limit: Option<i64>,
|
||||
// TODO these should be simple cursors like the others, not data
|
||||
pub page_after: Option<PaginationCursorData>,
|
||||
pub page_before_or_equal: Option<PostAggregates>,
|
||||
pub page_back: Option<bool>,
|
||||
|
@ -296,6 +299,7 @@ pub struct PostQuery<'a> {
|
|||
}
|
||||
|
||||
impl<'a> PostQuery<'a> {
|
||||
// TODO this should not be doing recursive fetching, get rid of it.
|
||||
#[allow(clippy::expect_used)]
|
||||
async fn prefetch_upper_bound_for_page_before(
|
||||
&self,
|
||||
|
|
|
@ -303,28 +303,11 @@ pub struct PostReportView {
|
|||
/// perspective. stringified since we might want to use arbitrary info later, with a P prepended to
|
||||
/// prevent ossification (api users love to make assumptions (e.g. parse stuff that looks like
|
||||
/// numbers as numbers) about apis that aren't part of the spec
|
||||
// TODO this is a mess, get rid of it and prefer the one in db_schema
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "full", derive(TS))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
pub struct PaginationCursor(pub String);
|
||||
|
||||
/// like PaginationCursor but for the report_combined table
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "full", derive(TS))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
pub struct ReportCombinedPaginationCursor(pub String);
|
||||
|
||||
/// like PaginationCursor but for the person_content_combined table
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "full", derive(TS))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
pub struct PersonContentCombinedPaginationCursor(pub String);
|
||||
|
||||
/// like PaginationCursor but for the person_saved_combined table
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "full", derive(TS))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
pub struct PersonSavedCombinedPaginationCursor(pub String);
|
||||
pub struct PostPaginationCursor(pub String);
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
|
||||
|
@ -538,6 +521,48 @@ pub enum PersonContentCombinedView {
|
|||
Comment(CommentView),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
/// A combined person_saved view
|
||||
pub(crate) struct PersonSavedCombinedViewInternal {
|
||||
// Post-specific
|
||||
pub post_counts: PostAggregates,
|
||||
pub post_unread_comments: i64,
|
||||
pub post_saved: Option<DateTime<Utc>>,
|
||||
pub post_read: bool,
|
||||
pub post_hidden: bool,
|
||||
pub my_post_vote: Option<i16>,
|
||||
pub image_details: Option<ImageDetails>,
|
||||
pub post_tags: PostTags,
|
||||
// Comment-specific
|
||||
pub comment: Option<Comment>,
|
||||
pub comment_counts: Option<CommentAggregates>,
|
||||
pub comment_saved: Option<DateTime<Utc>>,
|
||||
pub my_comment_vote: Option<i16>,
|
||||
// Shared
|
||||
pub post: Post,
|
||||
pub community: Community,
|
||||
pub item_creator: Person,
|
||||
pub subscribed: SubscribedType,
|
||||
pub item_creator_is_admin: bool,
|
||||
pub item_creator_is_moderator: bool,
|
||||
pub item_creator_banned_from_community: bool,
|
||||
pub item_creator_blocked: bool,
|
||||
pub banned_from_community: bool,
|
||||
pub can_mod: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(TS))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
// Use serde's internal tagging, to work easier with javascript libraries
|
||||
#[serde(tag = "type_")]
|
||||
pub enum PersonSavedCombinedView {
|
||||
Post(PostView),
|
||||
Comment(CommentView),
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(TS, Queryable, Selectable))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
|
@ -770,12 +795,6 @@ pub struct PrivateMessageView {
|
|||
pub recipient: Person,
|
||||
}
|
||||
|
||||
/// like PaginationCursor but for the report_combined table
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "full", derive(TS))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
pub struct InboxCombinedPaginationCursor(pub String);
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
|
@ -1057,12 +1076,6 @@ pub struct AdminAllowInstanceView {
|
|||
pub admin: Option<Person>,
|
||||
}
|
||||
|
||||
/// like PaginationCursor but for the modlog_combined
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "full", derive(TS))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
pub struct ModlogCombinedPaginationCursor(pub String);
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Selectable))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
|
@ -1150,13 +1163,6 @@ pub enum ModlogCombinedView {
|
|||
ModTransferCommunity(ModTransferCommunityView),
|
||||
}
|
||||
|
||||
/// like PaginationCursor but for the modlog_combined
|
||||
// TODO get rid of all these pagination cursors
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "full", derive(TS))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
pub struct SearchCombinedPaginationCursor(pub String);
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
|
|
|
@ -158,6 +158,7 @@ pub enum LemmyErrorType {
|
|||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
error: Option<FederationError>,
|
||||
},
|
||||
CouldntParsePaginationToken,
|
||||
}
|
||||
|
||||
/// Federation related errors, these dont need to be translated.
|
||||
|
|
Loading…
Reference in a new issue