mirror of
https://github.com/LemmyNet/lemmy.git
synced 2025-03-28 06:05:29 +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},
|
person::{ListPersonSaved, ListPersonSavedResponse},
|
||||||
utils::check_private_instance,
|
utils::check_private_instance,
|
||||||
};
|
};
|
||||||
|
use lemmy_db_schema::traits::PaginationCursorBuilder;
|
||||||
use lemmy_db_views::{
|
use lemmy_db_views::{
|
||||||
combined::person_saved_combined_view::PersonSavedCombinedQuery,
|
combined::person_saved_combined_view::PersonSavedCombinedQuery,
|
||||||
structs::{LocalUserView, SiteView},
|
structs::{LocalUserView, PersonSavedCombinedView, SiteView},
|
||||||
};
|
};
|
||||||
use lemmy_utils::error::LemmyResult;
|
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)?;
|
check_private_instance(&Some(local_user_view.clone()), &local_site.local_site)?;
|
||||||
|
|
||||||
// parse pagination token
|
let cursor_data = if let Some(cursor) = &data.page_cursor {
|
||||||
let page_after = if let Some(pa) = &data.page_cursor {
|
Some(PersonSavedCombinedView::from_cursor(cursor, &mut context.pool()).await?)
|
||||||
Some(pa.read(&mut context.pool()).await?)
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
let page_back = data.page_back;
|
|
||||||
let type_ = data.type_;
|
|
||||||
|
|
||||||
let saved = PersonSavedCombinedQuery {
|
let saved = PersonSavedCombinedQuery {
|
||||||
type_,
|
type_: data.type_,
|
||||||
page_after,
|
cursor_data,
|
||||||
page_back,
|
page_back: data.page_back,
|
||||||
}
|
}
|
||||||
.list(&mut context.pool(), &local_user_view)
|
.list(&mut context.pool(), &local_user_view)
|
||||||
.await?;
|
.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,
|
context::LemmyContext,
|
||||||
person::{ListInbox, ListInboxResponse},
|
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;
|
use lemmy_utils::error::LemmyResult;
|
||||||
|
|
||||||
pub async fn list_inbox(
|
pub async fn list_inbox(
|
||||||
|
@ -11,28 +15,25 @@ pub async fn list_inbox(
|
||||||
context: Data<LemmyContext>,
|
context: Data<LemmyContext>,
|
||||||
local_user_view: LocalUserView,
|
local_user_view: LocalUserView,
|
||||||
) -> LemmyResult<Json<ListInboxResponse>> {
|
) -> LemmyResult<Json<ListInboxResponse>> {
|
||||||
let unread_only = data.unread_only;
|
|
||||||
let type_ = data.type_;
|
|
||||||
let person_id = local_user_view.person.id;
|
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 cursor_data = if let Some(cursor) = &data.page_cursor {
|
||||||
let page_after = if let Some(pa) = &data.page_cursor {
|
Some(InboxCombinedView::from_cursor(cursor, &mut context.pool()).await?)
|
||||||
Some(pa.read(&mut context.pool()).await?)
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
let page_back = data.page_back;
|
|
||||||
|
|
||||||
let inbox = InboxCombinedQuery {
|
let inbox = InboxCombinedQuery {
|
||||||
type_,
|
type_: data.type_,
|
||||||
unread_only,
|
unread_only: data.unread_only,
|
||||||
show_bot_accounts,
|
show_bot_accounts: Some(local_user_view.local_user.show_bot_accounts),
|
||||||
page_after,
|
cursor_data,
|
||||||
page_back,
|
page_back: data.page_back,
|
||||||
}
|
}
|
||||||
.list(&mut context.pool(), person_id)
|
.list(&mut context.pool(), person_id)
|
||||||
.await?;
|
.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},
|
reports::combined::{ListReports, ListReportsResponse},
|
||||||
utils::check_community_mod_of_any_or_admin_action,
|
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;
|
use lemmy_utils::error::LemmyResult;
|
||||||
|
|
||||||
/// Lists reports for a community if an id is supplied
|
/// 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?;
|
check_community_mod_of_any_or_admin_action(&local_user_view, &mut context.pool()).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse pagination token
|
let cursor_data = if let Some(cursor) = &data.page_cursor {
|
||||||
let page_after = if let Some(pa) = &data.page_cursor {
|
Some(ReportCombinedView::from_cursor(cursor, &mut context.pool()).await?)
|
||||||
Some(pa.read(&mut context.pool()).await?)
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
let page_back = data.page_back;
|
|
||||||
|
|
||||||
let reports = ReportCombinedQuery {
|
let reports = ReportCombinedQuery {
|
||||||
community_id: data.community_id,
|
community_id: data.community_id,
|
||||||
post_id: data.post_id,
|
post_id: data.post_id,
|
||||||
type_: data.type_,
|
type_: data.type_,
|
||||||
unresolved_only: data.unresolved_only,
|
unresolved_only: data.unresolved_only,
|
||||||
page_after,
|
cursor_data,
|
||||||
page_back,
|
page_back: data.page_back,
|
||||||
show_community_rule_violations: data.show_community_rule_violations,
|
show_community_rule_violations: data.show_community_rule_violations,
|
||||||
my_reports_only,
|
my_reports_only,
|
||||||
}
|
}
|
||||||
.list(&mut context.pool(), &local_user_view)
|
.list(&mut context.pool(), &local_user_view)
|
||||||
.await?;
|
.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},
|
site::{GetModlog, GetModlogResponse},
|
||||||
utils::{check_community_mod_of_any_or_admin_action, check_private_instance},
|
utils::{check_community_mod_of_any_or_admin_action, check_private_instance},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::source::local_site::LocalSite;
|
use lemmy_db_schema::{source::local_site::LocalSite, traits::PaginationCursorBuilder};
|
||||||
use lemmy_db_views::{combined::modlog_combined_view::ModlogCombinedQuery, structs::LocalUserView};
|
use lemmy_db_views::{
|
||||||
|
combined::modlog_combined_view::ModlogCombinedQuery,
|
||||||
|
structs::{LocalUserView, ModlogCombinedView},
|
||||||
|
};
|
||||||
use lemmy_utils::error::LemmyResult;
|
use lemmy_utils::error::LemmyResult;
|
||||||
|
|
||||||
pub async fn get_mod_log(
|
pub async fn get_mod_log(
|
||||||
|
@ -17,10 +20,6 @@ pub async fn get_mod_log(
|
||||||
|
|
||||||
check_private_instance(&local_user_view, &local_site)?;
|
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 {
|
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())
|
check_community_mod_of_any_or_admin_action(local_user_view, &mut context.pool())
|
||||||
.await
|
.await
|
||||||
|
@ -35,34 +34,30 @@ pub async fn get_mod_log(
|
||||||
} else {
|
} else {
|
||||||
data.mod_person_id
|
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 cursor_data = if let Some(cursor) = &data.page_cursor {
|
||||||
let page_after = if let Some(pa) = &data.page_cursor {
|
Some(ModlogCombinedView::from_cursor(cursor, &mut context.pool()).await?)
|
||||||
Some(pa.read(&mut context.pool()).await?)
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
let page_back = data.page_back;
|
|
||||||
|
|
||||||
let modlog = ModlogCombinedQuery {
|
let modlog = ModlogCombinedQuery {
|
||||||
type_,
|
type_: data.type_,
|
||||||
listing_type,
|
listing_type: data.listing_type,
|
||||||
community_id,
|
community_id: data.community_id,
|
||||||
mod_person_id,
|
mod_person_id,
|
||||||
other_person_id,
|
other_person_id: data.other_person_id,
|
||||||
local_user,
|
local_user: local_user_view.as_ref().map(|u| &u.local_user),
|
||||||
post_id,
|
post_id: data.post_id,
|
||||||
comment_id,
|
comment_id: data.comment_id,
|
||||||
hide_modlog_names: Some(hide_modlog_names),
|
hide_modlog_names: Some(hide_modlog_names),
|
||||||
page_after,
|
cursor_data,
|
||||||
page_back,
|
page_back: data.page_back,
|
||||||
}
|
}
|
||||||
.list(&mut context.pool())
|
.list(&mut context.pool())
|
||||||
.await?;
|
.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,
|
CommentReplyId,
|
||||||
CommunityId,
|
CommunityId,
|
||||||
LanguageId,
|
LanguageId,
|
||||||
|
PaginationCursor,
|
||||||
PersonCommentMentionId,
|
PersonCommentMentionId,
|
||||||
PersonId,
|
PersonId,
|
||||||
PersonPostMentionId,
|
PersonPostMentionId,
|
||||||
|
@ -18,12 +19,10 @@ use lemmy_db_schema::{
|
||||||
};
|
};
|
||||||
use lemmy_db_views::structs::{
|
use lemmy_db_views::structs::{
|
||||||
CommunityModeratorView,
|
CommunityModeratorView,
|
||||||
InboxCombinedPaginationCursor,
|
|
||||||
InboxCombinedView,
|
InboxCombinedView,
|
||||||
LocalImageView,
|
LocalImageView,
|
||||||
PersonContentCombinedPaginationCursor,
|
|
||||||
PersonContentCombinedView,
|
PersonContentCombinedView,
|
||||||
PersonSavedCombinedPaginationCursor,
|
PersonSavedCombinedView,
|
||||||
PersonView,
|
PersonView,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -264,7 +263,7 @@ pub struct ListPersonContent {
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub username: Option<String>,
|
pub username: Option<String>,
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub page_cursor: Option<PersonContentCombinedPaginationCursor>,
|
pub page_cursor: Option<PaginationCursor>,
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub page_back: Option<bool>,
|
pub page_back: Option<bool>,
|
||||||
}
|
}
|
||||||
|
@ -276,6 +275,9 @@ pub struct ListPersonContent {
|
||||||
/// A person's content response.
|
/// A person's content response.
|
||||||
pub struct ListPersonContentResponse {
|
pub struct ListPersonContentResponse {
|
||||||
pub content: Vec<PersonContentCombinedView>,
|
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]
|
#[skip_serializing_none]
|
||||||
|
@ -287,7 +289,7 @@ pub struct ListPersonSaved {
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub type_: Option<PersonContentType>,
|
pub type_: Option<PersonContentType>,
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub page_cursor: Option<PersonSavedCombinedPaginationCursor>,
|
pub page_cursor: Option<PaginationCursor>,
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub page_back: Option<bool>,
|
pub page_back: Option<bool>,
|
||||||
}
|
}
|
||||||
|
@ -298,7 +300,10 @@ pub struct ListPersonSaved {
|
||||||
#[cfg_attr(feature = "full", ts(export))]
|
#[cfg_attr(feature = "full", ts(export))]
|
||||||
/// A person's saved content response.
|
/// A person's saved content response.
|
||||||
pub struct ListPersonSavedResponse {
|
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)]
|
#[derive(Debug, Serialize, Deserialize, Clone, Copy, Default, PartialEq, Eq)]
|
||||||
|
@ -386,7 +391,7 @@ pub struct ListInbox {
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub unread_only: Option<bool>,
|
pub unread_only: Option<bool>,
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub page_cursor: Option<InboxCombinedPaginationCursor>,
|
pub page_cursor: Option<PaginationCursor>,
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub page_back: Option<bool>,
|
pub page_back: Option<bool>,
|
||||||
}
|
}
|
||||||
|
@ -397,6 +402,9 @@ pub struct ListInbox {
|
||||||
/// Get your inbox (replies, comment mentions, post mentions, and messages)
|
/// Get your inbox (replies, comment mentions, post mentions, and messages)
|
||||||
pub struct ListInboxResponse {
|
pub struct ListInboxResponse {
|
||||||
pub inbox: Vec<InboxCombinedView>,
|
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)]
|
#[derive(Debug, Serialize, Deserialize, Clone, Copy, Default, PartialEq, Eq, Hash)]
|
||||||
|
|
|
@ -4,7 +4,7 @@ use lemmy_db_schema::{
|
||||||
PostFeatureType,
|
PostFeatureType,
|
||||||
PostSortType,
|
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::{Deserialize, Serialize};
|
||||||
use serde_with::skip_serializing_none;
|
use serde_with::skip_serializing_none;
|
||||||
#[cfg(feature = "full")]
|
#[cfg(feature = "full")]
|
||||||
|
@ -125,7 +125,7 @@ pub struct GetPosts {
|
||||||
/// If true, then only show posts with no comments
|
/// If true, then only show posts with no comments
|
||||||
pub no_comments_only: Option<bool>,
|
pub no_comments_only: Option<bool>,
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub page_cursor: Option<PaginationCursor>,
|
pub page_cursor: Option<PostPaginationCursor>,
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub page_back: Option<bool>,
|
pub page_back: Option<bool>,
|
||||||
}
|
}
|
||||||
|
@ -139,7 +139,7 @@ pub struct GetPostsResponse {
|
||||||
pub posts: Vec<PostView>,
|
pub posts: Vec<PostView>,
|
||||||
/// the pagination cursor to use to fetch the next page
|
/// the pagination cursor to use to fetch the next page
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[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)]
|
#[derive(Debug, Serialize, Deserialize, Clone, Copy, Default, PartialEq, Eq, Hash)]
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
newtypes::{CommunityId, PostId},
|
newtypes::{CommunityId, PaginationCursor, PostId},
|
||||||
ReportType,
|
ReportType,
|
||||||
};
|
};
|
||||||
use lemmy_db_views::structs::{ReportCombinedPaginationCursor, ReportCombinedView};
|
use lemmy_db_views::structs::ReportCombinedView;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_with::skip_serializing_none;
|
use serde_with::skip_serializing_none;
|
||||||
#[cfg(feature = "full")]
|
#[cfg(feature = "full")]
|
||||||
|
@ -27,7 +27,7 @@ pub struct ListReports {
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub community_id: Option<CommunityId>,
|
pub community_id: Option<CommunityId>,
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub page_cursor: Option<ReportCombinedPaginationCursor>,
|
pub page_cursor: Option<PaginationCursor>,
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub page_back: Option<bool>,
|
pub page_back: Option<bool>,
|
||||||
/// Only for admins: also show reports with `violates_instance_rules=false`
|
/// Only for admins: also show reports with `violates_instance_rules=false`
|
||||||
|
@ -44,4 +44,7 @@ pub struct ListReports {
|
||||||
/// The post reports response.
|
/// The post reports response.
|
||||||
pub struct ListReportsResponse {
|
pub struct ListReportsResponse {
|
||||||
pub reports: Vec<ReportCombinedView>,
|
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,
|
CommunityId,
|
||||||
InstanceId,
|
InstanceId,
|
||||||
LanguageId,
|
LanguageId,
|
||||||
|
PaginationCursor,
|
||||||
PersonId,
|
PersonId,
|
||||||
PostId,
|
PostId,
|
||||||
RegistrationApplicationId,
|
RegistrationApplicationId,
|
||||||
|
@ -36,12 +37,10 @@ use lemmy_db_views::structs::{
|
||||||
CommunityModeratorView,
|
CommunityModeratorView,
|
||||||
CommunityView,
|
CommunityView,
|
||||||
LocalUserView,
|
LocalUserView,
|
||||||
ModlogCombinedPaginationCursor,
|
|
||||||
ModlogCombinedView,
|
ModlogCombinedView,
|
||||||
PersonView,
|
PersonView,
|
||||||
PostView,
|
PostView,
|
||||||
RegistrationApplicationView,
|
RegistrationApplicationView,
|
||||||
SearchCombinedPaginationCursor,
|
|
||||||
SearchCombinedView,
|
SearchCombinedView,
|
||||||
SiteView,
|
SiteView,
|
||||||
};
|
};
|
||||||
|
@ -83,7 +82,7 @@ pub struct Search {
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub disliked_only: Option<bool>,
|
pub disliked_only: Option<bool>,
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub page_cursor: Option<SearchCombinedPaginationCursor>,
|
pub page_cursor: Option<PaginationCursor>,
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub page_back: Option<bool>,
|
pub page_back: Option<bool>,
|
||||||
}
|
}
|
||||||
|
@ -94,6 +93,9 @@ pub struct Search {
|
||||||
/// The search response, containing lists of the return type possibilities
|
/// The search response, containing lists of the return type possibilities
|
||||||
pub struct SearchResponse {
|
pub struct SearchResponse {
|
||||||
pub results: Vec<SearchCombinedView>,
|
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)]
|
#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq, Eq, Hash)]
|
||||||
|
@ -151,7 +153,7 @@ pub struct GetModlog {
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub comment_id: Option<CommentId>,
|
pub comment_id: Option<CommentId>,
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub page_cursor: Option<ModlogCombinedPaginationCursor>,
|
pub page_cursor: Option<PaginationCursor>,
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub page_back: Option<bool>,
|
pub page_back: Option<bool>,
|
||||||
}
|
}
|
||||||
|
@ -162,6 +164,9 @@ pub struct GetModlog {
|
||||||
/// The modlog fetch response.
|
/// The modlog fetch response.
|
||||||
pub struct GetModlogResponse {
|
pub struct GetModlogResponse {
|
||||||
pub modlog: Vec<ModlogCombinedView>,
|
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]
|
#[skip_serializing_none]
|
||||||
|
|
|
@ -6,9 +6,10 @@ use lemmy_api_common::{
|
||||||
person::{ListPersonContent, ListPersonContentResponse},
|
person::{ListPersonContent, ListPersonContentResponse},
|
||||||
utils::check_private_instance,
|
utils::check_private_instance,
|
||||||
};
|
};
|
||||||
|
use lemmy_db_schema::traits::PaginationCursorBuilder;
|
||||||
use lemmy_db_views::{
|
use lemmy_db_views::{
|
||||||
combined::person_content_combined_view::PersonContentCombinedQuery,
|
combined::person_content_combined_view::PersonContentCombinedQuery,
|
||||||
structs::{LocalUserView, SiteView},
|
structs::{LocalUserView, PersonContentCombinedView, SiteView},
|
||||||
};
|
};
|
||||||
use lemmy_utils::error::LemmyResult;
|
use lemmy_utils::error::LemmyResult;
|
||||||
|
|
||||||
|
@ -29,23 +30,22 @@ pub async fn list_person_content(
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// parse pagination token
|
let cursor_data = if let Some(cursor) = &data.page_cursor {
|
||||||
let page_after = if let Some(pa) = &data.page_cursor {
|
Some(PersonContentCombinedView::from_cursor(cursor, &mut context.pool()).await?)
|
||||||
Some(pa.read(&mut context.pool()).await?)
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
let page_back = data.page_back;
|
|
||||||
let type_ = data.type_;
|
|
||||||
|
|
||||||
let content = PersonContentCombinedQuery {
|
let content = PersonContentCombinedQuery {
|
||||||
creator_id: person_details_id,
|
creator_id: person_details_id,
|
||||||
type_,
|
type_: data.type_,
|
||||||
page_after,
|
cursor_data,
|
||||||
page_back,
|
page_back: data.page_back,
|
||||||
}
|
}
|
||||||
.list(&mut context.pool(), &local_user_view)
|
.list(&mut context.pool(), &local_user_view)
|
||||||
.await?;
|
.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::{
|
use lemmy_db_views::{
|
||||||
post::post_view::PostQuery,
|
post::post_view::PostQuery,
|
||||||
structs::{LocalUserView, PaginationCursor, SiteView},
|
structs::{LocalUserView, PostPaginationCursor, SiteView},
|
||||||
};
|
};
|
||||||
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
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
|
// 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 }))
|
Ok(Json(GetPostsResponse { posts, next_page }))
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,10 +6,10 @@ use lemmy_api_common::{
|
||||||
site::{Search, SearchResponse},
|
site::{Search, SearchResponse},
|
||||||
utils::{check_conflicting_like_filters, check_private_instance},
|
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::{
|
use lemmy_db_views::{
|
||||||
combined::search_combined_view::SearchCombinedQuery,
|
combined::search_combined_view::SearchCombinedQuery,
|
||||||
structs::{LocalUserView, SiteView},
|
structs::{LocalUserView, SearchCombinedView, SiteView},
|
||||||
};
|
};
|
||||||
use lemmy_utils::error::LemmyResult;
|
use lemmy_utils::error::LemmyResult;
|
||||||
|
|
||||||
|
@ -32,34 +32,32 @@ pub async fn search(
|
||||||
} else {
|
} else {
|
||||||
data.community_id
|
data.community_id
|
||||||
};
|
};
|
||||||
let search_term = data.search_term.clone();
|
|
||||||
let time_range_seconds = data.time_range_seconds;
|
|
||||||
|
|
||||||
// parse pagination token
|
let cursor_data = if let Some(cursor) = &data.page_cursor {
|
||||||
let page_after = if let Some(pa) = &data.page_cursor {
|
Some(SearchCombinedView::from_cursor(cursor, &mut context.pool()).await?)
|
||||||
Some(pa.read(&mut context.pool()).await?)
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
let page_back = data.page_back;
|
|
||||||
|
|
||||||
let results = SearchCombinedQuery {
|
let results = SearchCombinedQuery {
|
||||||
search_term,
|
search_term: data.search_term.clone(),
|
||||||
community_id,
|
community_id,
|
||||||
creator_id: data.creator_id,
|
creator_id: data.creator_id,
|
||||||
type_: data.type_,
|
type_: data.type_,
|
||||||
sort: data.sort,
|
sort: data.sort,
|
||||||
time_range_seconds,
|
time_range_seconds: data.time_range_seconds,
|
||||||
listing_type: data.listing_type,
|
listing_type: data.listing_type,
|
||||||
title_only: data.title_only,
|
title_only: data.title_only,
|
||||||
post_url_only: data.post_url_only,
|
post_url_only: data.post_url_only,
|
||||||
liked_only: data.liked_only,
|
liked_only: data.liked_only,
|
||||||
disliked_only: data.disliked_only,
|
disliked_only: data.disliked_only,
|
||||||
page_after,
|
cursor_data,
|
||||||
page_back,
|
page_back: data.page_back,
|
||||||
}
|
}
|
||||||
.list(&mut context.pool(), &local_user_view)
|
.list(&mut context.pool(), &local_user_view)
|
||||||
.await?;
|
.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},
|
utils::{build_db_pool, get_conn, now},
|
||||||
PostSortType,
|
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 lemmy_utils::error::{LemmyErrorExt2, LemmyResult};
|
||||||
use std::num::NonZeroU32;
|
use std::num::NonZeroU32;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
@ -162,7 +162,7 @@ async fn try_main() -> LemmyResult<()> {
|
||||||
|
|
||||||
if let Some(post_view) = post_views.into_iter().next_back() {
|
if let Some(post_view) = post_views.into_iter().next_back() {
|
||||||
println!("👀 getting pagination cursor data for next page");
|
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)
|
.read(&mut conn.into(), None)
|
||||||
.await?;
|
.await?;
|
||||||
page_after = Some(cursor_data);
|
page_after = Some(cursor_data);
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
diesel::{BoolExpressionMethods, NullableExpressionMethods, OptionalExtension},
|
|
||||||
newtypes::{CommunityId, DbUrl, PersonId, PostId},
|
newtypes::{CommunityId, DbUrl, PersonId, PostId},
|
||||||
schema::{community, person, post, post_actions},
|
schema::{community, person, post, post_actions},
|
||||||
source::post::{
|
source::post::{
|
||||||
|
@ -22,6 +21,7 @@ use crate::{
|
||||||
get_conn,
|
get_conn,
|
||||||
now,
|
now,
|
||||||
uplete,
|
uplete,
|
||||||
|
DbConn,
|
||||||
DbPool,
|
DbPool,
|
||||||
DELETED_REPLACEMENT_TEXT,
|
DELETED_REPLACEMENT_TEXT,
|
||||||
FETCH_LIMIT_MAX,
|
FETCH_LIMIT_MAX,
|
||||||
|
@ -35,8 +35,11 @@ use diesel::{
|
||||||
dsl::{count, insert_into, not},
|
dsl::{count, insert_into, not},
|
||||||
expression::SelectableHelper,
|
expression::SelectableHelper,
|
||||||
result::Error,
|
result::Error,
|
||||||
|
BoolExpressionMethods,
|
||||||
DecoratableTarget,
|
DecoratableTarget,
|
||||||
ExpressionMethods,
|
ExpressionMethods,
|
||||||
|
NullableExpressionMethods,
|
||||||
|
OptionalExtension,
|
||||||
QueryDsl,
|
QueryDsl,
|
||||||
TextExpressionMethods,
|
TextExpressionMethods,
|
||||||
};
|
};
|
||||||
|
@ -436,6 +439,14 @@ impl PostActionsCursor {
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
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 {
|
Ok(if let Some(person_id) = person_id {
|
||||||
post_actions::table
|
post_actions::table
|
||||||
.find((person_id, post_id))
|
.find((person_id, post_id))
|
||||||
|
|
|
@ -15,6 +15,8 @@ use diesel::{
|
||||||
};
|
};
|
||||||
#[cfg(feature = "full")]
|
#[cfg(feature = "full")]
|
||||||
use diesel_ltree::Ltree;
|
use diesel_ltree::Ltree;
|
||||||
|
#[cfg(feature = "full")]
|
||||||
|
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{
|
use std::{
|
||||||
fmt,
|
fmt,
|
||||||
|
@ -420,3 +422,31 @@ impl InstanceId {
|
||||||
#[cfg_attr(feature = "full", ts(export))]
|
#[cfg_attr(feature = "full", ts(export))]
|
||||||
/// The internal tag id.
|
/// The internal tag id.
|
||||||
pub struct TagId(pub i32);
|
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::{
|
use crate::{
|
||||||
newtypes::{CommunityId, DbUrl, PersonId},
|
newtypes::{CommunityId, DbUrl, PaginationCursor, PersonId},
|
||||||
utils::{get_conn, uplete, DbPool},
|
utils::{get_conn, uplete, DbPool},
|
||||||
};
|
};
|
||||||
use diesel::{
|
use diesel::{
|
||||||
|
@ -257,3 +257,18 @@ pub trait InternalToCombinedView {
|
||||||
/// Maps the combined DB row to an enum
|
/// Maps the combined DB row to an enum
|
||||||
fn map_to_enum(self) -> Option<Self::CombinedView>;
|
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::{
|
use crate::structs::{
|
||||||
CommentReplyView,
|
CommentReplyView,
|
||||||
InboxCombinedPaginationCursor,
|
|
||||||
InboxCombinedView,
|
InboxCombinedView,
|
||||||
InboxCombinedViewInternal,
|
InboxCombinedViewInternal,
|
||||||
PersonCommentMentionView,
|
PersonCommentMentionView,
|
||||||
|
@ -22,7 +21,7 @@ use i_love_jesus::PaginatedQueryBuilder;
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
aliases::{self, creator_community_actions, creator_local_user},
|
aliases::{self, creator_community_actions, creator_local_user},
|
||||||
impls::{community::community_follower_select_subscribed_type, local_user::local_user_can_mod},
|
impls::{community::community_follower_select_subscribed_type, local_user::local_user_can_mod},
|
||||||
newtypes::PersonId,
|
newtypes::{PaginationCursor, PersonId},
|
||||||
schema::{
|
schema::{
|
||||||
comment,
|
comment,
|
||||||
comment_actions,
|
comment_actions,
|
||||||
|
@ -46,11 +45,11 @@ use lemmy_db_schema::{
|
||||||
tag,
|
tag,
|
||||||
},
|
},
|
||||||
source::combined::inbox::{inbox_combined_keys as key, InboxCombined},
|
source::combined::inbox::{inbox_combined_keys as key, InboxCombined},
|
||||||
traits::InternalToCombinedView,
|
traits::{InternalToCombinedView, PaginationCursorBuilder},
|
||||||
utils::{functions::coalesce, get_conn, DbPool},
|
utils::{functions::coalesce, get_conn, DbPool},
|
||||||
InboxDataType,
|
InboxDataType,
|
||||||
};
|
};
|
||||||
use lemmy_utils::error::LemmyResult;
|
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
|
||||||
|
|
||||||
impl InboxCombinedViewInternal {
|
impl InboxCombinedViewInternal {
|
||||||
#[diesel::dsl::auto_type(no_type_alias)]
|
#[diesel::dsl::auto_type(no_type_alias)]
|
||||||
|
@ -218,48 +217,49 @@ impl InboxCombinedViewInternal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InboxCombinedPaginationCursor {
|
impl PaginationCursorBuilder for InboxCombinedView {
|
||||||
// get cursor for page that starts immediately after the given post
|
type CursorData = InboxCombined;
|
||||||
pub fn after_post(view: &InboxCombinedView) -> InboxCombinedPaginationCursor {
|
|
||||||
let (prefix, id) = match view {
|
fn to_cursor(&self) -> PaginationCursor {
|
||||||
|
let (prefix, id) = match &self {
|
||||||
InboxCombinedView::CommentReply(v) => ('R', v.comment_reply.id.0),
|
InboxCombinedView::CommentReply(v) => ('R', v.comment_reply.id.0),
|
||||||
InboxCombinedView::CommentMention(v) => ('C', v.person_comment_mention.id.0),
|
InboxCombinedView::CommentMention(v) => ('C', v.person_comment_mention.id.0),
|
||||||
InboxCombinedView::PostMention(v) => ('P', v.person_post_mention.id.0),
|
InboxCombinedView::PostMention(v) => ('P', v.person_post_mention.id.0),
|
||||||
InboxCombinedView::PrivateMessage(v) => ('M', v.private_message.id.0),
|
InboxCombinedView::PrivateMessage(v) => ('M', v.private_message.id.0),
|
||||||
};
|
};
|
||||||
// hex encoding to prevent ossification
|
PaginationCursor::new(prefix, id)
|
||||||
InboxCombinedPaginationCursor(format!("{prefix}{id:x}"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn read(&self, pool: &mut DbPool<'_>) -> Result<PaginationCursorData, Error> {
|
async fn from_cursor(
|
||||||
let err_msg = || Error::QueryBuilderError("Could not parse pagination token".into());
|
cursor: &PaginationCursor,
|
||||||
let mut query = inbox_combined::table
|
pool: &mut DbPool<'_>,
|
||||||
.select(InboxCombined::as_select())
|
) -> LemmyResult<Self::CursorData> {
|
||||||
.into_boxed();
|
let conn = &mut get_conn(pool).await?;
|
||||||
let (prefix, id_str) = self.0.split_at_checked(1).ok_or_else(err_msg)?;
|
let (prefix, id) = cursor.prefix_and_id()?;
|
||||||
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?;
|
|
||||||
|
|
||||||
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)]
|
#[derive(Default)]
|
||||||
pub struct InboxCombinedQuery {
|
pub struct InboxCombinedQuery {
|
||||||
pub type_: Option<InboxDataType>,
|
pub type_: Option<InboxDataType>,
|
||||||
pub unread_only: Option<bool>,
|
pub unread_only: Option<bool>,
|
||||||
pub show_bot_accounts: Option<bool>,
|
pub show_bot_accounts: Option<bool>,
|
||||||
pub page_after: Option<PaginationCursorData>,
|
pub cursor_data: Option<InboxCombined>,
|
||||||
pub page_back: Option<bool>,
|
pub page_back: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -397,12 +397,10 @@ impl InboxCombinedQuery {
|
||||||
|
|
||||||
let mut query = PaginatedQueryBuilder::new(query);
|
let mut query = PaginatedQueryBuilder::new(query);
|
||||||
|
|
||||||
let page_after = self.page_after.map(|c| c.0);
|
|
||||||
|
|
||||||
if self.page_back.unwrap_or_default() {
|
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 {
|
} else {
|
||||||
query = query.after(page_after);
|
query = query.after(self.cursor_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sorting by published
|
// Sorting by published
|
||||||
|
|
|
@ -16,12 +16,10 @@ use crate::structs::{
|
||||||
ModRemoveCommunityView,
|
ModRemoveCommunityView,
|
||||||
ModRemovePostView,
|
ModRemovePostView,
|
||||||
ModTransferCommunityView,
|
ModTransferCommunityView,
|
||||||
ModlogCombinedPaginationCursor,
|
|
||||||
ModlogCombinedView,
|
ModlogCombinedView,
|
||||||
ModlogCombinedViewInternal,
|
ModlogCombinedViewInternal,
|
||||||
};
|
};
|
||||||
use diesel::{
|
use diesel::{
|
||||||
result::Error,
|
|
||||||
BoolExpressionMethods,
|
BoolExpressionMethods,
|
||||||
ExpressionMethods,
|
ExpressionMethods,
|
||||||
IntoSql,
|
IntoSql,
|
||||||
|
@ -35,7 +33,7 @@ use i_love_jesus::PaginatedQueryBuilder;
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
aliases,
|
aliases,
|
||||||
impls::local_user::LocalUserOptionHelper,
|
impls::local_user::LocalUserOptionHelper,
|
||||||
newtypes::{CommentId, CommunityId, PersonId, PostId},
|
newtypes::{CommentId, CommunityId, PaginationCursor, PersonId, PostId},
|
||||||
schema::{
|
schema::{
|
||||||
admin_allow_instance,
|
admin_allow_instance,
|
||||||
admin_block_instance,
|
admin_block_instance,
|
||||||
|
@ -66,12 +64,13 @@ use lemmy_db_schema::{
|
||||||
combined::modlog::{modlog_combined_keys as key, ModlogCombined},
|
combined::modlog::{modlog_combined_keys as key, ModlogCombined},
|
||||||
local_user::LocalUser,
|
local_user::LocalUser,
|
||||||
},
|
},
|
||||||
traits::InternalToCombinedView,
|
traits::{InternalToCombinedView, PaginationCursorBuilder},
|
||||||
utils::{get_conn, DbPool},
|
utils::{get_conn, DbPool},
|
||||||
ListingType,
|
ListingType,
|
||||||
ModlogActionType,
|
ModlogActionType,
|
||||||
};
|
};
|
||||||
use lemmy_utils::error::LemmyResult;
|
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
|
||||||
|
|
||||||
impl ModlogCombinedViewInternal {
|
impl ModlogCombinedViewInternal {
|
||||||
#[diesel::dsl::auto_type(no_type_alias)]
|
#[diesel::dsl::auto_type(no_type_alias)]
|
||||||
fn joins(
|
fn joins(
|
||||||
|
@ -228,81 +227,69 @@ impl ModlogCombinedViewInternal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ModlogCombinedPaginationCursor {
|
impl PaginationCursorBuilder for ModlogCombinedView {
|
||||||
// get cursor for page that starts immediately after the given post
|
type CursorData = ModlogCombined;
|
||||||
pub fn after_post(view: &ModlogCombinedView) -> ModlogCombinedPaginationCursor {
|
fn to_cursor(&self) -> PaginationCursor {
|
||||||
let (prefix, id) = match view {
|
let (prefix, id) = match &self {
|
||||||
ModlogCombinedView::AdminAllowInstance(v) => {
|
ModlogCombinedView::AdminAllowInstance(v) => ('A', v.admin_allow_instance.id.0),
|
||||||
("AdminAllowInstance", 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::AdminBlockInstance(v) => {
|
ModlogCombinedView::AdminPurgeCommunity(v) => ('D', v.admin_purge_community.id.0),
|
||||||
("AdminBlockInstance", v.admin_block_instance.id.0)
|
ModlogCombinedView::AdminPurgePerson(v) => ('E', v.admin_purge_person.id.0),
|
||||||
}
|
ModlogCombinedView::AdminPurgePost(v) => ('F', v.admin_purge_post.id.0),
|
||||||
ModlogCombinedView::AdminPurgeComment(v) => ("AdminPurgeComment", v.admin_purge_comment.id.0),
|
ModlogCombinedView::ModAdd(v) => ('G', v.mod_add.id.0),
|
||||||
ModlogCombinedView::AdminPurgeCommunity(v) => {
|
ModlogCombinedView::ModAddCommunity(v) => ('H', v.mod_add_community.id.0),
|
||||||
("AdminPurgeCommunity", v.admin_purge_community.id.0)
|
ModlogCombinedView::ModBan(v) => ('I', v.mod_ban.id.0),
|
||||||
}
|
ModlogCombinedView::ModBanFromCommunity(v) => ('J', v.mod_ban_from_community.id.0),
|
||||||
ModlogCombinedView::AdminPurgePerson(v) => ("AdminPurgePerson", v.admin_purge_person.id.0),
|
ModlogCombinedView::ModFeaturePost(v) => ('K', v.mod_feature_post.id.0),
|
||||||
ModlogCombinedView::AdminPurgePost(v) => ("AdminPurgePost", v.admin_purge_post.id.0),
|
ModlogCombinedView::ModHideCommunity(v) => ('L', v.mod_hide_community.id.0),
|
||||||
ModlogCombinedView::ModAdd(v) => ("ModAdd", v.mod_add.id.0),
|
ModlogCombinedView::ModLockPost(v) => ('M', v.mod_lock_post.id.0),
|
||||||
ModlogCombinedView::ModAddCommunity(v) => ("ModAddCommunity", v.mod_add_community.id.0),
|
ModlogCombinedView::ModRemoveComment(v) => ('N', v.mod_remove_comment.id.0),
|
||||||
ModlogCombinedView::ModBan(v) => ("ModBan", v.mod_ban.id.0),
|
ModlogCombinedView::ModRemoveCommunity(v) => ('O', v.mod_remove_community.id.0),
|
||||||
ModlogCombinedView::ModBanFromCommunity(v) => {
|
ModlogCombinedView::ModRemovePost(v) => ('P', v.mod_remove_post.id.0),
|
||||||
("ModBanFromCommunity", v.mod_ban_from_community.id.0)
|
ModlogCombinedView::ModTransferCommunity(v) => ('Q', v.mod_transfer_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)
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
// hex encoding to prevent ossification
|
PaginationCursor::new(prefix, id)
|
||||||
ModlogCombinedPaginationCursor(format!("{prefix}-{id:x}"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn read(&self, pool: &mut DbPool<'_>) -> Result<PaginationCursorData, Error> {
|
async fn from_cursor(
|
||||||
let err_msg = || Error::QueryBuilderError("Could not parse pagination token".into());
|
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
|
let mut query = modlog_combined::table
|
||||||
.select(ModlogCombined::as_select())
|
.select(Self::CursorData::as_select())
|
||||||
.into_boxed();
|
.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 {
|
query = match prefix {
|
||||||
"AdminAllowInstance" => query.filter(modlog_combined::admin_allow_instance_id.eq(id)),
|
'A' => query.filter(modlog_combined::admin_allow_instance_id.eq(id)),
|
||||||
"AdminBlockInstance" => query.filter(modlog_combined::admin_block_instance_id.eq(id)),
|
'B' => query.filter(modlog_combined::admin_block_instance_id.eq(id)),
|
||||||
"AdminPurgeComment" => query.filter(modlog_combined::admin_purge_comment_id.eq(id)),
|
'C' => query.filter(modlog_combined::admin_purge_comment_id.eq(id)),
|
||||||
"AdminPurgeCommunity" => query.filter(modlog_combined::admin_purge_community_id.eq(id)),
|
'D' => query.filter(modlog_combined::admin_purge_community_id.eq(id)),
|
||||||
"AdminPurgePerson" => query.filter(modlog_combined::admin_purge_person_id.eq(id)),
|
'E' => query.filter(modlog_combined::admin_purge_person_id.eq(id)),
|
||||||
"AdminPurgePost" => query.filter(modlog_combined::admin_purge_post_id.eq(id)),
|
'F' => query.filter(modlog_combined::admin_purge_post_id.eq(id)),
|
||||||
"ModAdd" => query.filter(modlog_combined::mod_add_id.eq(id)),
|
'G' => query.filter(modlog_combined::mod_add_id.eq(id)),
|
||||||
"ModAddCommunity" => query.filter(modlog_combined::mod_add_community_id.eq(id)),
|
'H' => query.filter(modlog_combined::mod_add_community_id.eq(id)),
|
||||||
"ModBan" => query.filter(modlog_combined::mod_ban_id.eq(id)),
|
'I' => query.filter(modlog_combined::mod_ban_id.eq(id)),
|
||||||
"ModBanFromCommunity" => query.filter(modlog_combined::mod_ban_from_community_id.eq(id)),
|
'J' => query.filter(modlog_combined::mod_ban_from_community_id.eq(id)),
|
||||||
"ModFeaturePost" => query.filter(modlog_combined::mod_feature_post_id.eq(id)),
|
'K' => query.filter(modlog_combined::mod_feature_post_id.eq(id)),
|
||||||
"ModHideCommunity" => query.filter(modlog_combined::mod_hide_community_id.eq(id)),
|
'L' => query.filter(modlog_combined::mod_hide_community_id.eq(id)),
|
||||||
"ModLockPost" => query.filter(modlog_combined::mod_lock_post_id.eq(id)),
|
'M' => query.filter(modlog_combined::mod_lock_post_id.eq(id)),
|
||||||
"ModRemoveComment" => query.filter(modlog_combined::mod_remove_comment_id.eq(id)),
|
'N' => query.filter(modlog_combined::mod_remove_comment_id.eq(id)),
|
||||||
"ModRemoveCommunity" => query.filter(modlog_combined::mod_remove_community_id.eq(id)),
|
'O' => query.filter(modlog_combined::mod_remove_community_id.eq(id)),
|
||||||
"ModRemovePost" => query.filter(modlog_combined::mod_remove_post_id.eq(id)),
|
'P' => query.filter(modlog_combined::mod_remove_post_id.eq(id)),
|
||||||
"ModTransferCommunity" => query.filter(modlog_combined::mod_transfer_community_id.eq(id)),
|
'Q' => query.filter(modlog_combined::mod_transfer_community_id.eq(id)),
|
||||||
|
_ => return Err(LemmyErrorType::CouldntParsePaginationToken.into()),
|
||||||
_ => return Err(err_msg()),
|
|
||||||
};
|
};
|
||||||
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)]
|
#[derive(Default)]
|
||||||
/// Querying / filtering the modlog.
|
/// Querying / filtering the modlog.
|
||||||
pub struct ModlogCombinedQuery<'a> {
|
pub struct ModlogCombinedQuery<'a> {
|
||||||
|
@ -315,7 +302,7 @@ pub struct ModlogCombinedQuery<'a> {
|
||||||
pub local_user: Option<&'a LocalUser>,
|
pub local_user: Option<&'a LocalUser>,
|
||||||
pub mod_person_id: Option<PersonId>,
|
pub mod_person_id: Option<PersonId>,
|
||||||
pub other_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>,
|
pub page_back: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -392,12 +379,10 @@ impl ModlogCombinedQuery<'_> {
|
||||||
|
|
||||||
let mut query = PaginatedQueryBuilder::new(query);
|
let mut query = PaginatedQueryBuilder::new(query);
|
||||||
|
|
||||||
let page_after = self.page_after.map(|c| c.0);
|
|
||||||
|
|
||||||
if self.page_back.unwrap_or_default() {
|
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 {
|
} else {
|
||||||
query = query.after(page_after);
|
query = query.after(self.cursor_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
query = query
|
query = query
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
use crate::structs::{
|
use crate::structs::{
|
||||||
CommentView,
|
CommentView,
|
||||||
LocalUserView,
|
LocalUserView,
|
||||||
PersonContentCombinedPaginationCursor,
|
|
||||||
PersonContentCombinedView,
|
PersonContentCombinedView,
|
||||||
PersonContentCombinedViewInternal,
|
PersonContentCombinedViewInternal,
|
||||||
PostView,
|
PostView,
|
||||||
};
|
};
|
||||||
use diesel::{
|
use diesel::{
|
||||||
result::Error,
|
|
||||||
BoolExpressionMethods,
|
BoolExpressionMethods,
|
||||||
ExpressionMethods,
|
ExpressionMethods,
|
||||||
JoinOnDsl,
|
JoinOnDsl,
|
||||||
|
@ -20,7 +18,7 @@ use i_love_jesus::PaginatedQueryBuilder;
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
aliases::{creator_community_actions, creator_local_user},
|
aliases::{creator_community_actions, creator_local_user},
|
||||||
impls::{community::community_follower_select_subscribed_type, local_user::local_user_can_mod},
|
impls::{community::community_follower_select_subscribed_type, local_user::local_user_can_mod},
|
||||||
newtypes::PersonId,
|
newtypes::{PaginationCursor, PersonId},
|
||||||
schema::{
|
schema::{
|
||||||
comment,
|
comment,
|
||||||
comment_actions,
|
comment_actions,
|
||||||
|
@ -32,7 +30,6 @@ use lemmy_db_schema::{
|
||||||
person,
|
person,
|
||||||
person_actions,
|
person_actions,
|
||||||
person_content_combined,
|
person_content_combined,
|
||||||
person_saved_combined,
|
|
||||||
post,
|
post,
|
||||||
post_actions,
|
post_actions,
|
||||||
post_aggregates,
|
post_aggregates,
|
||||||
|
@ -40,11 +37,11 @@ use lemmy_db_schema::{
|
||||||
tag,
|
tag,
|
||||||
},
|
},
|
||||||
source::combined::person_content::{person_content_combined_keys as key, PersonContentCombined},
|
source::combined::person_content::{person_content_combined_keys as key, PersonContentCombined},
|
||||||
traits::InternalToCombinedView,
|
traits::{InternalToCombinedView, PaginationCursorBuilder},
|
||||||
utils::{functions::coalesce, get_conn, DbPool},
|
utils::{functions::coalesce, get_conn, DbPool},
|
||||||
PersonContentType,
|
PersonContentType,
|
||||||
};
|
};
|
||||||
use lemmy_utils::error::LemmyResult;
|
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
|
||||||
|
|
||||||
impl PersonContentCombinedViewInternal {
|
impl PersonContentCombinedViewInternal {
|
||||||
#[diesel::dsl::auto_type(no_type_alias)]
|
#[diesel::dsl::auto_type(no_type_alias)]
|
||||||
|
@ -140,142 +137,48 @@ impl PersonContentCombinedViewInternal {
|
||||||
.left_join(comment_actions_join)
|
.left_join(comment_actions_join)
|
||||||
.left_join(image_details_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 {
|
impl PaginationCursorBuilder for PersonContentCombinedView {
|
||||||
// get cursor for page that starts immediately after the given post
|
type CursorData = PersonContentCombined;
|
||||||
pub fn after_post(view: &PersonContentCombinedView) -> PersonContentCombinedPaginationCursor {
|
|
||||||
let (prefix, id) = match view {
|
fn to_cursor(&self) -> PaginationCursor {
|
||||||
|
let (prefix, id) = match &self {
|
||||||
PersonContentCombinedView::Comment(v) => ('C', v.comment.id.0),
|
PersonContentCombinedView::Comment(v) => ('C', v.comment.id.0),
|
||||||
PersonContentCombinedView::Post(v) => ('P', v.post.id.0),
|
PersonContentCombinedView::Post(v) => ('P', v.post.id.0),
|
||||||
};
|
};
|
||||||
// hex encoding to prevent ossification
|
PaginationCursor::new(prefix, id)
|
||||||
PersonContentCombinedPaginationCursor(format!("{prefix}{id:x}"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn read(&self, pool: &mut DbPool<'_>) -> Result<PaginationCursorData, Error> {
|
async fn from_cursor(
|
||||||
let err_msg = || Error::QueryBuilderError("Could not parse pagination token".into());
|
cursor: &PaginationCursor,
|
||||||
let mut query = person_content_combined::table
|
pool: &mut DbPool<'_>,
|
||||||
.select(PersonContentCombined::as_select())
|
) -> LemmyResult<Self::CursorData> {
|
||||||
.into_boxed();
|
let conn = &mut get_conn(pool).await?;
|
||||||
let (prefix, id_str) = self.0.split_at_checked(1).ok_or_else(err_msg)?;
|
let (prefix, id) = cursor.prefix_and_id()?;
|
||||||
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?;
|
|
||||||
|
|
||||||
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)]
|
#[derive(derive_new::new)]
|
||||||
pub struct PersonContentCombinedQuery {
|
pub struct PersonContentCombinedQuery {
|
||||||
pub creator_id: PersonId,
|
pub creator_id: PersonId,
|
||||||
#[new(default)]
|
#[new(default)]
|
||||||
pub type_: Option<PersonContentType>,
|
pub type_: Option<PersonContentType>,
|
||||||
#[new(default)]
|
#[new(default)]
|
||||||
pub page_after: Option<PaginationCursorData>,
|
pub cursor_data: Option<PersonContentCombined>,
|
||||||
#[new(default)]
|
#[new(default)]
|
||||||
pub page_back: Option<bool>,
|
pub page_back: Option<bool>,
|
||||||
}
|
}
|
||||||
|
@ -361,12 +264,10 @@ impl PersonContentCombinedQuery {
|
||||||
|
|
||||||
let mut query = PaginatedQueryBuilder::new(query);
|
let mut query = PaginatedQueryBuilder::new(query);
|
||||||
|
|
||||||
let page_after = self.page_after.map(|c| c.0);
|
|
||||||
|
|
||||||
if self.page_back.unwrap_or_default() {
|
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 {
|
} else {
|
||||||
query = query.after(page_after);
|
query = query.after(self.cursor_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sorting by published
|
// Sorting by published
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
use crate::structs::{
|
use crate::structs::{
|
||||||
|
CommentView,
|
||||||
LocalUserView,
|
LocalUserView,
|
||||||
PersonContentCombinedView,
|
PersonSavedCombinedView,
|
||||||
PersonContentCombinedViewInternal,
|
PersonSavedCombinedViewInternal,
|
||||||
PersonSavedCombinedPaginationCursor,
|
PostView,
|
||||||
};
|
};
|
||||||
use diesel::{
|
use diesel::{
|
||||||
result::Error,
|
BoolExpressionMethods,
|
||||||
ExpressionMethods,
|
ExpressionMethods,
|
||||||
|
JoinOnDsl,
|
||||||
NullableExpressionMethods,
|
NullableExpressionMethods,
|
||||||
QueryDsl,
|
QueryDsl,
|
||||||
SelectableHelper,
|
SelectableHelper,
|
||||||
|
@ -16,6 +18,7 @@ use i_love_jesus::PaginatedQueryBuilder;
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
aliases::{creator_community_actions, creator_local_user},
|
aliases::{creator_community_actions, creator_local_user},
|
||||||
impls::{community::community_follower_select_subscribed_type, local_user::local_user_can_mod},
|
impls::{community::community_follower_select_subscribed_type, local_user::local_user_can_mod},
|
||||||
|
newtypes::{PaginationCursor, PersonId},
|
||||||
schema::{
|
schema::{
|
||||||
comment,
|
comment,
|
||||||
comment_actions,
|
comment_actions,
|
||||||
|
@ -34,57 +37,155 @@ use lemmy_db_schema::{
|
||||||
tag,
|
tag,
|
||||||
},
|
},
|
||||||
source::combined::person_saved::{person_saved_combined_keys as key, PersonSavedCombined},
|
source::combined::person_saved::{person_saved_combined_keys as key, PersonSavedCombined},
|
||||||
traits::InternalToCombinedView,
|
traits::{InternalToCombinedView, PaginationCursorBuilder},
|
||||||
utils::{functions::coalesce, get_conn, DbPool},
|
utils::{functions::coalesce, get_conn, DbPool},
|
||||||
PersonContentType,
|
PersonContentType,
|
||||||
};
|
};
|
||||||
use lemmy_utils::error::LemmyResult;
|
use lemmy_utils::error::{LemmyErrorType, 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);
|
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct PersonSavedCombinedQuery {
|
pub struct PersonSavedCombinedQuery {
|
||||||
pub type_: Option<PersonContentType>,
|
pub type_: Option<PersonContentType>,
|
||||||
pub page_after: Option<PaginationCursorData>,
|
pub cursor_data: Option<PersonSavedCombined>,
|
||||||
pub page_back: Option<bool>,
|
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 {
|
impl PersonSavedCombinedQuery {
|
||||||
pub async fn list(
|
pub async fn list(
|
||||||
self,
|
self,
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
user: &LocalUserView,
|
user: &LocalUserView,
|
||||||
) -> LemmyResult<Vec<PersonContentCombinedView>> {
|
) -> LemmyResult<Vec<PersonSavedCombinedView>> {
|
||||||
let my_person_id = user.local_user.person_id;
|
let my_person_id = user.local_user.person_id;
|
||||||
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
|
@ -98,7 +199,7 @@ impl PersonSavedCombinedQuery {
|
||||||
.filter(tag::deleted.eq(false))
|
.filter(tag::deleted.eq(false))
|
||||||
.single_value();
|
.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))
|
.filter(person_saved_combined::person_id.eq(my_person_id))
|
||||||
.select((
|
.select((
|
||||||
// Post-specific
|
// Post-specific
|
||||||
|
@ -153,12 +254,10 @@ impl PersonSavedCombinedQuery {
|
||||||
|
|
||||||
let mut query = PaginatedQueryBuilder::new(query);
|
let mut query = PaginatedQueryBuilder::new(query);
|
||||||
|
|
||||||
let page_after = self.page_after.map(|c| c.0);
|
|
||||||
|
|
||||||
if self.page_back.unwrap_or_default() {
|
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 {
|
} else {
|
||||||
query = query.after(page_after);
|
query = query.after(self.cursor_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sorting by saved desc
|
// Sorting by saved desc
|
||||||
|
@ -167,9 +266,7 @@ impl PersonSavedCombinedQuery {
|
||||||
// Tie breaker
|
// Tie breaker
|
||||||
.then_desc(key::id);
|
.then_desc(key::id);
|
||||||
|
|
||||||
let res = query
|
let res = query.load::<PersonSavedCombinedViewInternal>(conn).await?;
|
||||||
.load::<PersonContentCombinedViewInternal>(conn)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
// Map the query results to the enum
|
// Map the query results to the enum
|
||||||
let out = res
|
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)]
|
#[cfg(test)]
|
||||||
#[expect(clippy::indexing_slicing)]
|
#[expect(clippy::indexing_slicing)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
combined::person_saved_combined_view::PersonSavedCombinedQuery,
|
combined::person_saved_combined_view::PersonSavedCombinedQuery,
|
||||||
structs::{LocalUserView, PersonContentCombinedView},
|
structs::{LocalUserView, PersonSavedCombinedView},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::{
|
source::{
|
||||||
|
@ -309,19 +455,19 @@ mod tests {
|
||||||
assert_eq!(3, timmy_saved.len());
|
assert_eq!(3, timmy_saved.len());
|
||||||
|
|
||||||
// Make sure the types and order are correct
|
// 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_post.id, v.post.id);
|
||||||
assert_eq!(data.timmy.id, v.post.creator_id);
|
assert_eq!(data.timmy.id, v.post.creator_id);
|
||||||
} else {
|
} else {
|
||||||
panic!("wrong type");
|
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_comment.id, v.comment.id);
|
||||||
assert_eq!(data.sara.id, v.comment.creator_id);
|
assert_eq!(data.sara.id, v.comment.creator_id);
|
||||||
} else {
|
} else {
|
||||||
panic!("wrong type");
|
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_comment_2.id, v.comment.id);
|
||||||
assert_eq!(data.sara.id, v.comment.creator_id);
|
assert_eq!(data.sara.id, v.comment.creator_id);
|
||||||
} else {
|
} else {
|
||||||
|
@ -337,7 +483,7 @@ mod tests {
|
||||||
.await?;
|
.await?;
|
||||||
assert_eq!(1, timmy_saved.len());
|
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_comment_2.id, v.comment.id);
|
||||||
assert_eq!(data.sara.id, v.comment.creator_id);
|
assert_eq!(data.sara.id, v.comment.creator_id);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -4,7 +4,6 @@ use crate::structs::{
|
||||||
LocalUserView,
|
LocalUserView,
|
||||||
PostReportView,
|
PostReportView,
|
||||||
PrivateMessageReportView,
|
PrivateMessageReportView,
|
||||||
ReportCombinedPaginationCursor,
|
|
||||||
ReportCombinedView,
|
ReportCombinedView,
|
||||||
ReportCombinedViewInternal,
|
ReportCombinedViewInternal,
|
||||||
};
|
};
|
||||||
|
@ -24,7 +23,7 @@ use i_love_jesus::PaginatedQueryBuilder;
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
aliases::{self, creator_community_actions},
|
aliases::{self, creator_community_actions},
|
||||||
impls::community::community_follower_select_subscribed_type,
|
impls::community::community_follower_select_subscribed_type,
|
||||||
newtypes::{CommunityId, PersonId, PostId},
|
newtypes::{CommunityId, PaginationCursor, PersonId, PostId},
|
||||||
schema::{
|
schema::{
|
||||||
comment,
|
comment,
|
||||||
comment_actions,
|
comment_actions,
|
||||||
|
@ -46,11 +45,11 @@ use lemmy_db_schema::{
|
||||||
report_combined,
|
report_combined,
|
||||||
},
|
},
|
||||||
source::combined::report::{report_combined_keys as key, ReportCombined},
|
source::combined::report::{report_combined_keys as key, ReportCombined},
|
||||||
traits::InternalToCombinedView,
|
traits::{InternalToCombinedView, PaginationCursorBuilder},
|
||||||
utils::{functions::coalesce, get_conn, DbPool, ReverseTimestampKey},
|
utils::{functions::coalesce, get_conn, DbPool, ReverseTimestampKey},
|
||||||
ReportType,
|
ReportType,
|
||||||
};
|
};
|
||||||
use lemmy_utils::error::LemmyResult;
|
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
|
||||||
|
|
||||||
impl ReportCombinedViewInternal {
|
impl ReportCombinedViewInternal {
|
||||||
#[diesel::dsl::auto_type(no_type_alias)]
|
#[diesel::dsl::auto_type(no_type_alias)]
|
||||||
|
@ -211,42 +210,43 @@ impl ReportCombinedViewInternal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ReportCombinedPaginationCursor {
|
impl PaginationCursorBuilder for ReportCombinedView {
|
||||||
// get cursor for page that starts immediately after the given post
|
type CursorData = ReportCombined;
|
||||||
pub fn after_post(view: &ReportCombinedView) -> ReportCombinedPaginationCursor {
|
|
||||||
let (prefix, id) = match view {
|
fn to_cursor(&self) -> PaginationCursor {
|
||||||
|
let (prefix, id) = match &self {
|
||||||
ReportCombinedView::Comment(v) => ('C', v.comment_report.id.0),
|
ReportCombinedView::Comment(v) => ('C', v.comment_report.id.0),
|
||||||
ReportCombinedView::Post(v) => ('P', v.post_report.id.0),
|
ReportCombinedView::Post(v) => ('P', v.post_report.id.0),
|
||||||
ReportCombinedView::PrivateMessage(v) => ('M', v.private_message_report.id.0),
|
ReportCombinedView::PrivateMessage(v) => ('M', v.private_message_report.id.0),
|
||||||
ReportCombinedView::Community(v) => ('Y', v.community_report.id.0),
|
ReportCombinedView::Community(v) => ('Y', v.community_report.id.0),
|
||||||
};
|
};
|
||||||
// hex encoding to prevent ossification
|
PaginationCursor::new(prefix, id)
|
||||||
ReportCombinedPaginationCursor(format!("{prefix}{id:x}"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn read(&self, pool: &mut DbPool<'_>) -> Result<PaginationCursorData, Error> {
|
async fn from_cursor(
|
||||||
let err_msg = || Error::QueryBuilderError("Could not parse pagination token".into());
|
cursor: &PaginationCursor,
|
||||||
let mut query = report_combined::table
|
pool: &mut DbPool<'_>,
|
||||||
.select(ReportCombined::as_select())
|
) -> LemmyResult<Self::CursorData> {
|
||||||
.into_boxed();
|
let conn = &mut get_conn(pool).await?;
|
||||||
let (prefix, id_str) = self.0.split_at_checked(1).ok_or_else(err_msg)?;
|
let (prefix, id) = cursor.prefix_and_id()?;
|
||||||
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?;
|
|
||||||
|
|
||||||
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)]
|
#[derive(Default)]
|
||||||
pub struct ReportCombinedQuery {
|
pub struct ReportCombinedQuery {
|
||||||
pub type_: Option<ReportType>,
|
pub type_: Option<ReportType>,
|
||||||
|
@ -255,8 +255,8 @@ pub struct ReportCombinedQuery {
|
||||||
pub unresolved_only: Option<bool>,
|
pub unresolved_only: Option<bool>,
|
||||||
/// For admins, also show reports with `violates_instance_rules=false`
|
/// For admins, also show reports with `violates_instance_rules=false`
|
||||||
pub show_community_rule_violations: Option<bool>,
|
pub show_community_rule_violations: Option<bool>,
|
||||||
|
pub cursor_data: Option<ReportCombined>,
|
||||||
pub my_reports_only: Option<bool>,
|
pub my_reports_only: Option<bool>,
|
||||||
pub page_after: Option<PaginationCursorData>,
|
|
||||||
pub page_back: Option<bool>,
|
pub page_back: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -342,12 +342,10 @@ impl ReportCombinedQuery {
|
||||||
|
|
||||||
let mut query = PaginatedQueryBuilder::new(query);
|
let mut query = PaginatedQueryBuilder::new(query);
|
||||||
|
|
||||||
let page_after = self.page_after.map(|c| c.0);
|
|
||||||
|
|
||||||
if self.page_back.unwrap_or_default() {
|
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 {
|
} else {
|
||||||
query = query.after(page_after);
|
query = query.after(self.cursor_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(type_) = self.type_ {
|
if let Some(type_) = self.type_ {
|
||||||
|
|
|
@ -4,13 +4,11 @@ use crate::structs::{
|
||||||
LocalUserView,
|
LocalUserView,
|
||||||
PersonView,
|
PersonView,
|
||||||
PostView,
|
PostView,
|
||||||
SearchCombinedPaginationCursor,
|
|
||||||
SearchCombinedView,
|
SearchCombinedView,
|
||||||
SearchCombinedViewInternal,
|
SearchCombinedViewInternal,
|
||||||
};
|
};
|
||||||
use diesel::{
|
use diesel::{
|
||||||
dsl::not,
|
dsl::not,
|
||||||
result::Error,
|
|
||||||
BoolExpressionMethods,
|
BoolExpressionMethods,
|
||||||
ExpressionMethods,
|
ExpressionMethods,
|
||||||
JoinOnDsl,
|
JoinOnDsl,
|
||||||
|
@ -24,7 +22,7 @@ use i_love_jesus::PaginatedQueryBuilder;
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
aliases::{creator_community_actions, creator_local_user},
|
aliases::{creator_community_actions, creator_local_user},
|
||||||
impls::{community::community_follower_select_subscribed_type, local_user::local_user_can_mod},
|
impls::{community::community_follower_select_subscribed_type, local_user::local_user_can_mod},
|
||||||
newtypes::{CommunityId, PersonId},
|
newtypes::{CommunityId, PaginationCursor, PersonId},
|
||||||
schema::{
|
schema::{
|
||||||
comment,
|
comment,
|
||||||
comment_actions,
|
comment_actions,
|
||||||
|
@ -45,7 +43,7 @@ use lemmy_db_schema::{
|
||||||
tag,
|
tag,
|
||||||
},
|
},
|
||||||
source::combined::search::{search_combined_keys as key, SearchCombined},
|
source::combined::search::{search_combined_keys as key, SearchCombined},
|
||||||
traits::InternalToCombinedView,
|
traits::{InternalToCombinedView, PaginationCursorBuilder},
|
||||||
utils::{
|
utils::{
|
||||||
functions::coalesce,
|
functions::coalesce,
|
||||||
fuzzy_search,
|
fuzzy_search,
|
||||||
|
@ -59,7 +57,7 @@ use lemmy_db_schema::{
|
||||||
SearchSortType,
|
SearchSortType,
|
||||||
SearchType,
|
SearchType,
|
||||||
};
|
};
|
||||||
use lemmy_utils::error::LemmyResult;
|
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
|
||||||
use SearchSortType::*;
|
use SearchSortType::*;
|
||||||
|
|
||||||
impl SearchCombinedViewInternal {
|
impl SearchCombinedViewInternal {
|
||||||
|
@ -183,42 +181,43 @@ impl SearchCombinedViewInternal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SearchCombinedPaginationCursor {
|
impl PaginationCursorBuilder for SearchCombinedView {
|
||||||
// get cursor for page that starts immediately after the given post
|
type CursorData = SearchCombined;
|
||||||
pub fn after_post(view: &SearchCombinedView) -> SearchCombinedPaginationCursor {
|
|
||||||
let (prefix, id) = match view {
|
fn to_cursor(&self) -> PaginationCursor {
|
||||||
|
let (prefix, id) = match &self {
|
||||||
SearchCombinedView::Post(v) => ('P', v.post.id.0),
|
SearchCombinedView::Post(v) => ('P', v.post.id.0),
|
||||||
SearchCombinedView::Comment(v) => ('C', v.comment.id.0),
|
SearchCombinedView::Comment(v) => ('C', v.comment.id.0),
|
||||||
SearchCombinedView::Community(v) => ('O', v.community.id.0),
|
SearchCombinedView::Community(v) => ('O', v.community.id.0),
|
||||||
SearchCombinedView::Person(v) => ('E', v.person.id.0),
|
SearchCombinedView::Person(v) => ('E', v.person.id.0),
|
||||||
};
|
};
|
||||||
// hex encoding to prevent ossification
|
PaginationCursor::new(prefix, id)
|
||||||
SearchCombinedPaginationCursor(format!("{prefix}{id:x}"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn read(&self, pool: &mut DbPool<'_>) -> Result<PaginationCursorData, Error> {
|
async fn from_cursor(
|
||||||
let err_msg = || Error::QueryBuilderError("Could not parse pagination token".into());
|
cursor: &PaginationCursor,
|
||||||
let mut query = search_combined::table
|
pool: &mut DbPool<'_>,
|
||||||
.select(SearchCombined::as_select())
|
) -> LemmyResult<Self::CursorData> {
|
||||||
.into_boxed();
|
let conn = &mut get_conn(pool).await?;
|
||||||
let (prefix, id_str) = self.0.split_at_checked(1).ok_or_else(err_msg)?;
|
let (prefix, id) = cursor.prefix_and_id()?;
|
||||||
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?;
|
|
||||||
|
|
||||||
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)]
|
#[derive(Default)]
|
||||||
pub struct SearchCombinedQuery {
|
pub struct SearchCombinedQuery {
|
||||||
pub search_term: Option<String>,
|
pub search_term: Option<String>,
|
||||||
|
@ -232,7 +231,7 @@ pub struct SearchCombinedQuery {
|
||||||
pub post_url_only: Option<bool>,
|
pub post_url_only: Option<bool>,
|
||||||
pub liked_only: Option<bool>,
|
pub liked_only: Option<bool>,
|
||||||
pub disliked_only: Option<bool>,
|
pub disliked_only: Option<bool>,
|
||||||
pub page_after: Option<PaginationCursorData>,
|
pub cursor_data: Option<SearchCombined>,
|
||||||
pub page_back: Option<bool>,
|
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 mut query = PaginatedQueryBuilder::new(query);
|
||||||
|
|
||||||
let page_after = self.page_after.map(|c| c.0);
|
|
||||||
|
|
||||||
if self.page_back.unwrap_or_default() {
|
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 {
|
} else {
|
||||||
query = query.after(page_after);
|
query = query.after(self.cursor_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
query = match self.sort.unwrap_or_default() {
|
query = match self.sort.unwrap_or_default() {
|
||||||
|
@ -415,12 +418,6 @@ impl SearchCombinedQuery {
|
||||||
Top => query.then_desc(key::score),
|
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
|
// finally use unique id as tie breaker
|
||||||
query = query.then_desc(key::id);
|
query = query.then_desc(key::id);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
structs::{PaginationCursor, PostView},
|
structs::{PostPaginationCursor, PostView},
|
||||||
utils::filter_blocked,
|
utils::filter_blocked,
|
||||||
};
|
};
|
||||||
use diesel::{
|
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
|
// 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
|
// 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(
|
pub async fn read(
|
||||||
&self,
|
&self,
|
||||||
|
@ -277,6 +278,7 @@ pub struct PostQuery<'a> {
|
||||||
// literal filter
|
// literal filter
|
||||||
pub community_id_just_for_prefetch: bool,
|
pub community_id_just_for_prefetch: bool,
|
||||||
pub local_user: Option<&'a LocalUser>,
|
pub local_user: Option<&'a LocalUser>,
|
||||||
|
// TODO get rid of this
|
||||||
pub search_term: Option<String>,
|
pub search_term: Option<String>,
|
||||||
pub url_only: Option<bool>,
|
pub url_only: Option<bool>,
|
||||||
pub read_only: Option<bool>,
|
pub read_only: Option<bool>,
|
||||||
|
@ -285,6 +287,7 @@ pub struct PostQuery<'a> {
|
||||||
pub title_only: Option<bool>,
|
pub title_only: Option<bool>,
|
||||||
pub page: Option<i64>,
|
pub page: Option<i64>,
|
||||||
pub limit: Option<i64>,
|
pub limit: Option<i64>,
|
||||||
|
// TODO these should be simple cursors like the others, not data
|
||||||
pub page_after: Option<PaginationCursorData>,
|
pub page_after: Option<PaginationCursorData>,
|
||||||
pub page_before_or_equal: Option<PostAggregates>,
|
pub page_before_or_equal: Option<PostAggregates>,
|
||||||
pub page_back: Option<bool>,
|
pub page_back: Option<bool>,
|
||||||
|
@ -296,6 +299,7 @@ pub struct PostQuery<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> PostQuery<'a> {
|
impl<'a> PostQuery<'a> {
|
||||||
|
// TODO this should not be doing recursive fetching, get rid of it.
|
||||||
#[allow(clippy::expect_used)]
|
#[allow(clippy::expect_used)]
|
||||||
async fn prefetch_upper_bound_for_page_before(
|
async fn prefetch_upper_bound_for_page_before(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
@ -303,28 +303,11 @@ pub struct PostReportView {
|
||||||
/// perspective. stringified since we might want to use arbitrary info later, with a P prepended to
|
/// 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
|
/// 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
|
/// 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)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
#[cfg_attr(feature = "full", derive(TS))]
|
#[cfg_attr(feature = "full", derive(TS))]
|
||||||
#[cfg_attr(feature = "full", ts(export))]
|
#[cfg_attr(feature = "full", ts(export))]
|
||||||
pub struct PaginationCursor(pub String);
|
pub struct PostPaginationCursor(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);
|
|
||||||
|
|
||||||
#[skip_serializing_none]
|
#[skip_serializing_none]
|
||||||
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
|
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
|
||||||
|
@ -538,6 +521,48 @@ pub enum PersonContentCombinedView {
|
||||||
Comment(CommentView),
|
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)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
#[cfg_attr(feature = "full", derive(TS, Queryable, Selectable))]
|
#[cfg_attr(feature = "full", derive(TS, Queryable, Selectable))]
|
||||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||||
|
@ -770,12 +795,6 @@ pub struct PrivateMessageView {
|
||||||
pub recipient: Person,
|
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)]
|
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
|
||||||
#[cfg_attr(feature = "full", derive(Queryable))]
|
#[cfg_attr(feature = "full", derive(Queryable))]
|
||||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||||
|
@ -1057,12 +1076,6 @@ pub struct AdminAllowInstanceView {
|
||||||
pub admin: Option<Person>,
|
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)]
|
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
|
||||||
#[cfg_attr(feature = "full", derive(Queryable, Selectable))]
|
#[cfg_attr(feature = "full", derive(Queryable, Selectable))]
|
||||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||||
|
@ -1150,13 +1163,6 @@ pub enum ModlogCombinedView {
|
||||||
ModTransferCommunity(ModTransferCommunityView),
|
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)]
|
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
|
||||||
#[cfg_attr(feature = "full", derive(Queryable))]
|
#[cfg_attr(feature = "full", derive(Queryable))]
|
||||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||||
|
|
|
@ -158,6 +158,7 @@ pub enum LemmyErrorType {
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
error: Option<FederationError>,
|
error: Option<FederationError>,
|
||||||
},
|
},
|
||||||
|
CouldntParsePaginationToken,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Federation related errors, these dont need to be translated.
|
/// Federation related errors, these dont need to be translated.
|
||||||
|
|
Loading…
Reference in a new issue