Adding views and replaceable schema.

This commit is contained in:
Dessalines 2024-12-06 09:49:11 -05:00
parent 724856d684
commit 1053df1a4b
7 changed files with 155 additions and 162 deletions

View file

@ -7,7 +7,11 @@ use lemmy_db_schema::{
PostListingMode,
PostSortType,
};
use lemmy_db_views::structs::{CommentView, LocalImageView, PostView};
use lemmy_db_views::structs::{
LocalImageView,
ProfileCombinedPaginationCursor,
ProfileCombinedView,
};
use lemmy_db_views_actor::structs::{
CommentReplyView,
CommunityModeratorView,
@ -223,15 +227,13 @@ pub struct GetPersonDetails {
#[cfg_attr(feature = "full", ts(optional))]
pub username: Option<String>,
#[cfg_attr(feature = "full", ts(optional))]
pub sort: Option<PostSortType>,
#[cfg_attr(feature = "full", ts(optional))]
pub page: Option<i64>,
#[cfg_attr(feature = "full", ts(optional))]
pub limit: Option<i64>,
#[cfg_attr(feature = "full", ts(optional))]
pub community_id: Option<CommunityId>,
#[cfg_attr(feature = "full", ts(optional))]
pub saved_only: Option<bool>,
#[cfg_attr(feature = "full", ts(optional))]
pub page_cursor: Option<ProfileCombinedPaginationCursor>,
#[cfg_attr(feature = "full", ts(optional))]
pub page_back: Option<bool>,
}
#[skip_serializing_none]
@ -243,8 +245,7 @@ pub struct GetPersonDetailsResponse {
pub person_view: PersonView,
#[cfg_attr(feature = "full", ts(optional))]
pub site: Option<Site>,
pub comments: Vec<CommentView>,
pub posts: Vec<PostView>,
pub content: Vec<ProfileCombinedView>,
pub moderates: Vec<CommunityModeratorView>,
}

View file

@ -6,10 +6,9 @@ use lemmy_api_common::{
person::{GetPersonDetails, GetPersonDetailsResponse},
utils::{check_private_instance, read_site_for_actor},
};
use lemmy_db_schema::{source::person::Person, utils::post_to_comment_sort_type};
use lemmy_db_schema::source::person::Person;
use lemmy_db_views::{
comment_view::CommentQuery,
post_view::PostQuery,
profile_combined_view::ProfileCombinedQuery,
structs::{LocalUserView, SiteView},
};
use lemmy_db_views_actor::structs::{CommunityModeratorView, PersonView};
@ -47,46 +46,38 @@ pub async fn read_person(
// `my_user`
let person_view = PersonView::read(&mut context.pool(), person_details_id).await?;
let sort = data.sort;
let page = data.page;
let limit = data.limit;
let saved_only = data.saved_only;
let community_id = data.community_id;
// If its saved only, you don't care what creator it was
// Or, if its not saved, then you only want it for that specific creator
let creator_id = if !saved_only.unwrap_or_default() {
Some(person_details_id)
// parse pagination token
let page_after = if let Some(pa) = &data.page_cursor {
Some(pa.read(&mut context.pool()).await?)
} else {
None
};
let page_back = data.page_back;
let saved_only = data.saved_only;
let community_id = data.community_id;
let local_user = local_user_view.as_ref().map(|l| &l.local_user);
// If its saved only, then ignore the person details id,
// and use your local user's id
let creator_id = if !saved_only.unwrap_or_default() {
Some(person_details_id)
} else {
local_user_view.as_ref().map(|u| u.local_user.person_id)
};
let posts = PostQuery {
sort,
saved_only,
local_user,
community_id,
page,
limit,
creator_id,
..Default::default()
}
.list(&local_site.site, &mut context.pool())
.await?;
let comments = CommentQuery {
local_user,
sort: sort.map(post_to_comment_sort_type),
saved_only,
community_id,
page,
limit,
creator_id,
..Default::default()
}
.list(&local_site.site, &mut context.pool())
.await?;
let content = if let Some(creator_id) = creator_id {
ProfileCombinedQuery {
creator_id,
community_id,
saved_only,
page_after,
page_back,
}
.list(&mut context.pool(), &local_user_view)
.await?
} else {
// if the creator is missing (saved_only, and no local_user), then return empty content
Vec::new()
};
let moderates = CommunityModeratorView::for_person(
&mut context.pool(),
@ -97,12 +88,10 @@ pub async fn read_person(
let site = read_site_for_actor(person_view.person.actor_id.clone(), &context).await?;
// Return the jwt
Ok(Json(GetPersonDetailsResponse {
person_view,
site,
moderates,
comments,
posts,
content,
}))
}

View file

@ -685,3 +685,31 @@ CALL r.create_report_combined_trigger ('comment_report');
CALL r.create_report_combined_trigger ('private_message_report');
-- Profile (comment, post)
CREATE PROCEDURE r.create_profile_combined_trigger (table_name text)
LANGUAGE plpgsql
AS $a$
BEGIN
EXECUTE replace($b$ CREATE FUNCTION r.profile_combined_thing_insert ( )
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
BEGIN
INSERT INTO profile_combined (published, thing_id)
VALUES (NEW.published, NEW.id);
RETURN NEW;
END $$;
CREATE TRIGGER profile_combined
AFTER INSERT ON thing
FOR EACH ROW
EXECUTE FUNCTION r.profile_combined_thing_insert ( );
$b$,
'thing',
table_name);
END;
$a$;
CALL r.create_profile_combined_trigger ('post');
CALL r.create_profile_combined_trigger ('comment');

View file

@ -18,14 +18,15 @@ use diesel::{
use diesel_async::RunQueryDsl;
use i_love_jesus::PaginatedQueryBuilder;
use lemmy_db_schema::{
aliases::{self, creator_community_actions},
newtypes::CommunityId,
aliases::creator_community_actions,
newtypes::{CommunityId, PersonId},
schema::{
comment,
comment_actions,
comment_aggregates,
community,
community_actions,
image_details,
local_user,
person,
person_actions,
@ -74,11 +75,11 @@ impl ProfileCombinedPaginationCursor {
#[derive(Clone)]
pub struct PaginationCursorData(ProfileCombined);
// TODO check these
#[derive(Default)]
pub struct ProfileCombinedQuery {
pub creator_id: PersonId,
pub community_id: Option<CommunityId>,
pub unresolved_only: Option<bool>,
pub saved_only: Option<bool>,
pub page_after: Option<PaginationCursorData>,
pub page_back: Option<bool>,
}
@ -87,10 +88,12 @@ impl ProfileCombinedQuery {
pub async fn list(
self,
pool: &mut DbPool<'_>,
user: &LocalUserView,
user: &Option<LocalUserView>,
) -> LemmyResult<Vec<ProfileCombinedView>> {
let my_person_id = user.local_user.person_id;
// let item_creator = aliases::person1.field(person::id);
let my_person_id = user
.as_ref()
.map(|u| u.local_user.person_id)
.unwrap_or(PersonId(-1));
let item_creator = person::id;
let conn = &mut get_conn(pool).await?;
@ -114,20 +117,11 @@ impl ProfileCombinedQuery {
// The item creator
.inner_join(
person::table.on(
post::creator_id
comment::creator_id
.eq(person::id)
.or(comment::creator_id.eq(person::id)),
.or(post::creator_id.eq(person::id)),
),
)
// The item creator
// You can now use aliases::person1.field(person::id) / item_creator for all the item actions
// .inner_join(
// aliases::person1.on(
// post::creator_id
// .eq(item_creator)
// .or(comment::creator_id.eq(item_creator)),
// ),
// )
// The community
.inner_join(community::table.on(post::community_id.eq(community::id)))
.left_join(actions_alias(
@ -153,10 +147,7 @@ impl ProfileCombinedQuery {
Some(my_person_id),
item_creator,
))
.left_join(
post_aggregates::table
.on(profile_combined::post_id.eq(post_aggregates::post_id.nullable())),
)
.inner_join(post_aggregates::table.on(post::id.eq(post_aggregates::post_id)))
.left_join(
comment_aggregates::table
.on(profile_combined::comment_id.eq(comment_aggregates::comment_id.nullable())),
@ -166,43 +157,42 @@ impl ProfileCombinedQuery {
Some(my_person_id),
comment::id,
))
.left_join(image_details::table.on(post::thumbnail_url.eq(image_details::link.nullable())))
// The creator id filter
.filter(item_creator.eq(self.creator_id))
.select((
// Post-specific
post::all_columns.nullable(),
// post_aggregates::all_columns.nullable(),
// coalesce(
// post_aggregates::comments.nullable() - post_actions::read_comments_amount.nullable(),
// post_aggregates::comments,
// )
// .nullable(),
// post_actions::saved.nullable().is_not_null(),
// post_actions::read.nullable().is_not_null(),
// post_actions::hidden.nullable().is_not_null(),
// post_actions::like_score.nullable(),
// // Comment-specific
// comment::all_columns.nullable(),
// comment_aggregates::all_columns.nullable(),
// comment_actions::saved.nullable().is_not_null(),
// comment_actions::like_score.nullable(),
// // Private-message-specific
// private_message_profile::all_columns.nullable(),
// private_message::all_columns.nullable(),
// // Shared
// person::all_columns,
// aliases::person1.fields(person::all_columns),
// community::all_columns.nullable(),
// CommunityFollower::select_subscribed_type(),
// aliases::person2.fields(person::all_columns.nullable()),
// local_user::admin.nullable().is_not_null(),
// creator_community_actions
// .field(community_actions::received_ban)
// .nullable()
// .is_not_null(),
// creator_community_actions
// .field(community_actions::became_moderator)
// .nullable()
// .is_not_null(),
// person_actions::blocked.nullable().is_not_null(),
post_aggregates::all_columns,
coalesce(
post_aggregates::comments.nullable() - post_actions::read_comments_amount.nullable(),
post_aggregates::comments,
),
post_actions::saved.nullable().is_not_null(),
post_actions::read.nullable().is_not_null(),
post_actions::hidden.nullable().is_not_null(),
post_actions::like_score.nullable(),
image_details::all_columns.nullable(),
// Comment-specific
comment::all_columns.nullable(),
comment_aggregates::all_columns.nullable(),
comment_actions::saved.nullable().is_not_null(),
comment_actions::like_score.nullable(),
// Shared
post::all_columns,
community::all_columns,
person::all_columns,
CommunityFollower::select_subscribed_type(),
local_user::admin.nullable().is_not_null(),
creator_community_actions
.field(community_actions::became_moderator)
.nullable()
.is_not_null(),
creator_community_actions
.field(community_actions::received_ban)
.nullable()
.is_not_null(),
person_actions::blocked.nullable().is_not_null(),
community_actions::received_ban.nullable().is_not_null(),
))
.into_boxed();
@ -210,9 +200,13 @@ impl ProfileCombinedQuery {
query = query.filter(community::id.eq(community_id));
}
// If its not an admin, get only the ones you mod
if !user.local_user.admin {
query = query.filter(community_actions::became_moderator.is_not_null());
// If its saved only, then filter
if self.saved_only.unwrap_or_default() {
query = query.filter(
comment_actions::saved
.is_not_null()
.or(post_actions::saved.is_not_null()),
)
}
let mut query = PaginatedQueryBuilder::new(query);
@ -225,25 +219,11 @@ impl ProfileCombinedQuery {
query = query.after(page_after);
}
// If viewing all profiles, order by newest, but if viewing unresolved only, show the oldest
// first (FIFO)
if self.unresolved_only.unwrap_or_default() {
query = query
.filter(
post_profile::resolved
.eq(false)
.or(comment_profile::resolved.eq(false))
.or(private_message_profile::resolved.eq(false)),
)
// TODO: when a `then_asc` method is added, use it here, make the id sort direction match,
// and remove the separate index; unless additional columns are added to this sort
.then_desc(ReverseTimestampKey(key::published));
} else {
query = query.then_desc(key::published);
}
// Tie breaker
query = query.then_desc(key::id);
// Sorting by published
query = query
.then_desc(ReverseTimestampKey(key::published))
// Tie breaker
.then_desc(key::id);
let res = query.load::<ProfileCombinedViewInternal>(conn).await?;
@ -259,14 +239,28 @@ fn map_to_enum(view: ProfileCombinedViewInternal) -> Option<ProfileCombinedView>
// Use for a short alias
let v = view;
if let (Some(post), Some(community), Some(unread_comments), Some(counts)) =
(v.post, v.community, v.post_unread_comments, v.post_counts)
{
Some(ProfileCombinedView::Post(PostView {
post,
community,
unread_comments,
if let (Some(comment), Some(counts)) = (v.comment, v.comment_counts) {
Some(ProfileCombinedView::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,
}))
} else {
Some(ProfileCombinedView::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,
@ -277,28 +271,8 @@ fn map_to_enum(view: ProfileCombinedViewInternal) -> Option<ProfileCombinedView>
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,
}))
} else if let (Some(comment), Some(counts), Some(post), Some(community)) = (
v.comment,
v.comment_counts,
v.post.clone(),
v.community.clone(),
) {
Some(ProfileCombinedView::Comment(CommentView {
comment,
counts,
post,
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,
}))
} else {
None
}
}

View file

@ -310,8 +310,8 @@ pub struct ProfileCombinedViewInternal {
pub my_post_vote: Option<i16>,
pub image_details: Option<ImageDetails>,
// Comment-specific
pub comment: Comment,
pub comment_counts: CommentAggregates,
pub comment: Option<Comment>,
pub comment_counts: Option<CommentAggregates>,
pub comment_saved: bool,
pub my_comment_vote: Option<i16>,
// Shared
@ -323,7 +323,6 @@ pub struct ProfileCombinedViewInternal {
pub item_creator_is_moderator: bool,
pub item_creator_banned_from_community: bool,
pub item_creator_blocked: bool,
pub item_saved: bool,
pub banned_from_community: bool,
}

View file

@ -1 +1,2 @@
DROP TABLE profile_combined;

View file

@ -4,7 +4,7 @@ CREATE TABLE profile_combined (
id serial PRIMARY KEY,
published timestamptz NOT NULL,
post_id int UNIQUE REFERENCES post ON UPDATE CASCADE ON DELETE CASCADE,
comment_id int UNIQUE REFERENCES comment ON UPDATE CASCADE ON DELETE CASCADE,
comment_id int UNIQUE REFERENCES COMMENT ON UPDATE CASCADE ON DELETE CASCADE,
-- Make sure only one of the columns is not null
CHECK ((post_id IS NOT NULL)::integer + (comment_id IS NOT NULL)::integer = 1)
);
@ -27,3 +27,4 @@ SELECT
id
FROM
comment;