mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-12-12 05:54:51 +00:00
Most of the bulk work done, need to add tests yet.
This commit is contained in:
parent
f133079f0b
commit
05f218d53c
3
Cargo.lock
generated
3
Cargo.lock
generated
|
@ -2699,8 +2699,10 @@ name = "lemmy_db_views_actor"
|
||||||
version = "0.19.6-beta.7"
|
version = "0.19.6-beta.7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
|
"derive-new",
|
||||||
"diesel",
|
"diesel",
|
||||||
"diesel-async",
|
"diesel-async",
|
||||||
|
"i-love-jesus",
|
||||||
"lemmy_db_schema",
|
"lemmy_db_schema",
|
||||||
"lemmy_db_views",
|
"lemmy_db_views",
|
||||||
"lemmy_utils",
|
"lemmy_utils",
|
||||||
|
@ -2710,6 +2712,7 @@ dependencies = [
|
||||||
"serial_test",
|
"serial_test",
|
||||||
"strum",
|
"strum",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tracing",
|
||||||
"ts-rs",
|
"ts-rs",
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
use actix_web::web::{Data, Json, Query};
|
|
||||||
use lemmy_api_common::{
|
|
||||||
context::LemmyContext,
|
|
||||||
person::{GetPersonCommentMentions, GetPersonCommentMentionsResponse},
|
|
||||||
};
|
|
||||||
use lemmy_db_views::structs::LocalUserView;
|
|
||||||
use lemmy_db_views_actor::person_comment_mention_view::PersonCommentMentionQuery;
|
|
||||||
use lemmy_utils::error::LemmyResult;
|
|
||||||
|
|
||||||
#[tracing::instrument(skip(context))]
|
|
||||||
pub async fn list_comment_mentions(
|
|
||||||
data: Query<GetPersonCommentMentions>,
|
|
||||||
context: Data<LemmyContext>,
|
|
||||||
local_user_view: LocalUserView,
|
|
||||||
) -> LemmyResult<Json<GetPersonCommentMentionsResponse>> {
|
|
||||||
let sort = data.sort;
|
|
||||||
let page = data.page;
|
|
||||||
let limit = data.limit;
|
|
||||||
let unread_only = data.unread_only.unwrap_or_default();
|
|
||||||
let person_id = Some(local_user_view.person.id);
|
|
||||||
let show_bot_accounts = local_user_view.local_user.show_bot_accounts;
|
|
||||||
|
|
||||||
let comment_mentions = PersonCommentMentionQuery {
|
|
||||||
recipient_id: person_id,
|
|
||||||
my_person_id: person_id,
|
|
||||||
sort,
|
|
||||||
unread_only,
|
|
||||||
show_bot_accounts,
|
|
||||||
page,
|
|
||||||
limit,
|
|
||||||
}
|
|
||||||
.list(&mut context.pool())
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(Json(GetPersonCommentMentionsResponse { comment_mentions }))
|
|
||||||
}
|
|
39
crates/api/src/local_user/notifications/list_inbox.rs
Normal file
39
crates/api/src/local_user/notifications/list_inbox.rs
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
use actix_web::web::{Data, Json, Query};
|
||||||
|
use lemmy_api_common::{
|
||||||
|
context::LemmyContext,
|
||||||
|
person::{ListInbox, ListInboxResponse},
|
||||||
|
};
|
||||||
|
use lemmy_db_views::structs::LocalUserView;
|
||||||
|
use lemmy_db_views_actor::inbox_combined_view::InboxCombinedQuery;
|
||||||
|
use lemmy_utils::error::LemmyResult;
|
||||||
|
|
||||||
|
#[tracing::instrument(skip(context))]
|
||||||
|
pub async fn list_inbox(
|
||||||
|
data: Query<ListInbox>,
|
||||||
|
context: Data<LemmyContext>,
|
||||||
|
local_user_view: LocalUserView,
|
||||||
|
) -> LemmyResult<Json<ListInboxResponse>> {
|
||||||
|
let unread_only = data.unread_only;
|
||||||
|
let person_id = local_user_view.person.id;
|
||||||
|
let show_bot_accounts = Some(local_user_view.local_user.show_bot_accounts);
|
||||||
|
|
||||||
|
// parse pagination token
|
||||||
|
let page_after = if let Some(pa) = &data.page_cursor {
|
||||||
|
Some(pa.read(&mut context.pool()).await?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let page_back = data.page_back;
|
||||||
|
|
||||||
|
let inbox = InboxCombinedQuery {
|
||||||
|
my_person_id: person_id,
|
||||||
|
unread_only,
|
||||||
|
show_bot_accounts,
|
||||||
|
page_after,
|
||||||
|
page_back,
|
||||||
|
}
|
||||||
|
.list(&mut context.pool())
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(Json(ListInboxResponse { inbox }))
|
||||||
|
}
|
|
@ -1,36 +0,0 @@
|
||||||
use actix_web::web::{Data, Json, Query};
|
|
||||||
use lemmy_api_common::{
|
|
||||||
context::LemmyContext,
|
|
||||||
person::{GetPersonPostMentions, GetPersonPostMentionsResponse},
|
|
||||||
};
|
|
||||||
use lemmy_db_views::structs::LocalUserView;
|
|
||||||
use lemmy_db_views_actor::person_post_mention_view::PersonPostMentionQuery;
|
|
||||||
use lemmy_utils::error::LemmyResult;
|
|
||||||
|
|
||||||
#[tracing::instrument(skip(context))]
|
|
||||||
pub async fn list_post_mentions(
|
|
||||||
data: Query<GetPersonPostMentions>,
|
|
||||||
context: Data<LemmyContext>,
|
|
||||||
local_user_view: LocalUserView,
|
|
||||||
) -> LemmyResult<Json<GetPersonPostMentionsResponse>> {
|
|
||||||
let sort = data.sort;
|
|
||||||
let page = data.page;
|
|
||||||
let limit = data.limit;
|
|
||||||
let unread_only = data.unread_only.unwrap_or_default();
|
|
||||||
let person_id = Some(local_user_view.person.id);
|
|
||||||
let show_bot_accounts = local_user_view.local_user.show_bot_accounts;
|
|
||||||
|
|
||||||
let post_mentions = PersonPostMentionQuery {
|
|
||||||
recipient_id: person_id,
|
|
||||||
my_person_id: person_id,
|
|
||||||
sort,
|
|
||||||
unread_only,
|
|
||||||
show_bot_accounts,
|
|
||||||
page,
|
|
||||||
limit,
|
|
||||||
}
|
|
||||||
.list(&mut context.pool())
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(Json(GetPersonPostMentionsResponse { post_mentions }))
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
use actix_web::web::{Data, Json, Query};
|
|
||||||
use lemmy_api_common::{
|
|
||||||
context::LemmyContext,
|
|
||||||
person::{GetReplies, GetRepliesResponse},
|
|
||||||
};
|
|
||||||
use lemmy_db_views::structs::LocalUserView;
|
|
||||||
use lemmy_db_views_actor::comment_reply_view::CommentReplyQuery;
|
|
||||||
use lemmy_utils::error::LemmyResult;
|
|
||||||
|
|
||||||
#[tracing::instrument(skip(context))]
|
|
||||||
pub async fn list_replies(
|
|
||||||
data: Query<GetReplies>,
|
|
||||||
context: Data<LemmyContext>,
|
|
||||||
local_user_view: LocalUserView,
|
|
||||||
) -> LemmyResult<Json<GetRepliesResponse>> {
|
|
||||||
let sort = data.sort;
|
|
||||||
let page = data.page;
|
|
||||||
let limit = data.limit;
|
|
||||||
let unread_only = data.unread_only.unwrap_or_default();
|
|
||||||
let person_id = Some(local_user_view.person.id);
|
|
||||||
let show_bot_accounts = local_user_view.local_user.show_bot_accounts;
|
|
||||||
|
|
||||||
let replies = CommentReplyQuery {
|
|
||||||
recipient_id: person_id,
|
|
||||||
my_person_id: person_id,
|
|
||||||
sort,
|
|
||||||
unread_only,
|
|
||||||
show_bot_accounts,
|
|
||||||
page,
|
|
||||||
limit,
|
|
||||||
}
|
|
||||||
.list(&mut context.pool())
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(Json(GetRepliesResponse { replies }))
|
|
||||||
}
|
|
|
@ -1,8 +1,9 @@
|
||||||
use actix_web::web::{Data, Json};
|
use actix_web::web::{Data, Json};
|
||||||
use lemmy_api_common::{context::LemmyContext, person::GetRepliesResponse};
|
use lemmy_api_common::{context::LemmyContext, SuccessResponse};
|
||||||
use lemmy_db_schema::source::{
|
use lemmy_db_schema::source::{
|
||||||
comment_reply::CommentReply,
|
comment_reply::CommentReply,
|
||||||
person_comment_mention::PersonCommentMention,
|
person_comment_mention::PersonCommentMention,
|
||||||
|
person_post_mention::PersonPostMention,
|
||||||
private_message::PrivateMessage,
|
private_message::PrivateMessage,
|
||||||
};
|
};
|
||||||
use lemmy_db_views::structs::LocalUserView;
|
use lemmy_db_views::structs::LocalUserView;
|
||||||
|
@ -12,7 +13,7 @@ use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||||
pub async fn mark_all_notifications_read(
|
pub async fn mark_all_notifications_read(
|
||||||
context: Data<LemmyContext>,
|
context: Data<LemmyContext>,
|
||||||
local_user_view: LocalUserView,
|
local_user_view: LocalUserView,
|
||||||
) -> LemmyResult<Json<GetRepliesResponse>> {
|
) -> LemmyResult<Json<SuccessResponse>> {
|
||||||
let person_id = local_user_view.person.id;
|
let person_id = local_user_view.person.id;
|
||||||
|
|
||||||
// Mark all comment_replies as read
|
// Mark all comment_replies as read
|
||||||
|
@ -25,10 +26,15 @@ pub async fn mark_all_notifications_read(
|
||||||
.await
|
.await
|
||||||
.with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?;
|
.with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?;
|
||||||
|
|
||||||
|
// Mark all post mentions as read
|
||||||
|
PersonPostMention::mark_all_as_read(&mut context.pool(), person_id)
|
||||||
|
.await
|
||||||
|
.with_lemmy_type(LemmyErrorType::CouldntUpdatePost)?;
|
||||||
|
|
||||||
// Mark all private_messages as read
|
// Mark all private_messages as read
|
||||||
PrivateMessage::mark_all_as_read(&mut context.pool(), person_id)
|
PrivateMessage::mark_all_as_read(&mut context.pool(), person_id)
|
||||||
.await
|
.await
|
||||||
.with_lemmy_type(LemmyErrorType::CouldntUpdatePrivateMessage)?;
|
.with_lemmy_type(LemmyErrorType::CouldntUpdatePrivateMessage)?;
|
||||||
|
|
||||||
Ok(Json(GetRepliesResponse { replies: vec![] }))
|
Ok(Json(SuccessResponse::default()))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
pub mod list_comment_mentions;
|
pub mod list_inbox;
|
||||||
pub mod list_post_mentions;
|
|
||||||
pub mod list_replies;
|
|
||||||
pub mod mark_all_read;
|
pub mod mark_all_read;
|
||||||
pub mod mark_comment_mention_read;
|
pub mod mark_comment_mention_read;
|
||||||
pub mod mark_post_mention_read;
|
pub mod mark_post_mention_read;
|
||||||
|
|
|
@ -1,11 +1,7 @@
|
||||||
use actix_web::web::{Data, Json};
|
use actix_web::web::{Data, Json};
|
||||||
use lemmy_api_common::{context::LemmyContext, person::GetUnreadCountResponse};
|
use lemmy_api_common::{context::LemmyContext, person::GetUnreadCountResponse};
|
||||||
use lemmy_db_views::structs::{LocalUserView, PrivateMessageView};
|
use lemmy_db_views::structs::LocalUserView;
|
||||||
use lemmy_db_views_actor::structs::{
|
use lemmy_db_views_actor::structs::InboxCombinedViewInternal;
|
||||||
CommentReplyView,
|
|
||||||
PersonCommentMentionView,
|
|
||||||
PersonPostMentionView,
|
|
||||||
};
|
|
||||||
use lemmy_utils::error::LemmyResult;
|
use lemmy_utils::error::LemmyResult;
|
||||||
|
|
||||||
#[tracing::instrument(skip(context))]
|
#[tracing::instrument(skip(context))]
|
||||||
|
@ -14,25 +10,10 @@ pub async fn unread_count(
|
||||||
local_user_view: LocalUserView,
|
local_user_view: LocalUserView,
|
||||||
) -> LemmyResult<Json<GetUnreadCountResponse>> {
|
) -> LemmyResult<Json<GetUnreadCountResponse>> {
|
||||||
let person_id = local_user_view.person.id;
|
let person_id = local_user_view.person.id;
|
||||||
|
let show_bot_accounts = local_user_view.local_user.show_bot_accounts;
|
||||||
let replies =
|
let count =
|
||||||
CommentReplyView::get_unread_count(&mut context.pool(), &local_user_view.local_user).await?;
|
InboxCombinedViewInternal::get_unread_count(&mut context.pool(), person_id, show_bot_accounts)
|
||||||
|
|
||||||
let comment_mentions =
|
|
||||||
PersonCommentMentionView::get_unread_count(&mut context.pool(), &local_user_view.local_user)
|
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let post_mentions =
|
Ok(Json(GetUnreadCountResponse { count }))
|
||||||
PersonPostMentionView::get_unread_count(&mut context.pool(), &local_user_view.local_user)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let private_messages =
|
|
||||||
PrivateMessageView::get_unread_count(&mut context.pool(), person_id).await?;
|
|
||||||
|
|
||||||
Ok(Json(GetUnreadCountResponse {
|
|
||||||
replies,
|
|
||||||
comment_mentions,
|
|
||||||
post_mentions,
|
|
||||||
private_messages,
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,12 +5,7 @@ use lemmy_api_common::{
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
post::{CreatePostLike, PostResponse},
|
post::{CreatePostLike, PostResponse},
|
||||||
send_activity::{ActivityChannel, SendActivityData},
|
send_activity::{ActivityChannel, SendActivityData},
|
||||||
utils::{
|
utils::{check_bot_account, check_community_user_action, check_local_vote_mode},
|
||||||
check_bot_account,
|
|
||||||
check_community_user_action,
|
|
||||||
check_local_vote_mode,
|
|
||||||
mark_post_as_read,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
newtypes::PostOrCommentId,
|
newtypes::PostOrCommentId,
|
||||||
|
|
|
@ -7,7 +7,8 @@ use lemmy_db_schema::{
|
||||||
source::private_message::{PrivateMessage, PrivateMessageUpdateForm},
|
source::private_message::{PrivateMessage, PrivateMessageUpdateForm},
|
||||||
traits::Crud,
|
traits::Crud,
|
||||||
};
|
};
|
||||||
use lemmy_db_views::structs::{LocalUserView, PrivateMessageView};
|
use lemmy_db_views::structs::LocalUserView;
|
||||||
|
use lemmy_db_views_actor::structs::PrivateMessageView;
|
||||||
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||||
|
|
||||||
#[tracing::instrument(skip(context))]
|
#[tracing::instrument(skip(context))]
|
||||||
|
|
|
@ -21,6 +21,7 @@ use lemmy_db_schema::{
|
||||||
person::Person,
|
person::Person,
|
||||||
person_comment_mention::{PersonCommentMention, PersonCommentMentionInsertForm},
|
person_comment_mention::{PersonCommentMention, PersonCommentMentionInsertForm},
|
||||||
person_post_mention::{PersonPostMention, PersonPostMentionInsertForm},
|
person_post_mention::{PersonPostMention, PersonPostMentionInsertForm},
|
||||||
|
post::Post,
|
||||||
},
|
},
|
||||||
traits::Crud,
|
traits::Crud,
|
||||||
};
|
};
|
||||||
|
@ -103,31 +104,6 @@ pub async fn send_local_notifs(
|
||||||
let mut recipient_ids = Vec::new();
|
let mut recipient_ids = Vec::new();
|
||||||
let inbox_link = format!("{}/inbox", context.settings().get_protocol_and_hostname());
|
let inbox_link = format!("{}/inbox", context.settings().get_protocol_and_hostname());
|
||||||
|
|
||||||
// When called from api code, we have local user view and can read with CommentView
|
|
||||||
// to reduce db queries. But when receiving a federated comment the user view is None,
|
|
||||||
// which means that comments inside private communities cant be read. As a workaround
|
|
||||||
// we need to read the items manually to bypass this check.
|
|
||||||
let (comment, post, community) = if let Some(local_user_view) = local_user_view {
|
|
||||||
let comment_view = CommentView::read(
|
|
||||||
&mut context.pool(),
|
|
||||||
comment_id,
|
|
||||||
Some(&local_user_view.local_user),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
(
|
|
||||||
comment_view.comment,
|
|
||||||
comment_view.post,
|
|
||||||
comment_view.community,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
let comment = Comment::read(&mut context.pool(), comment_id).await?;
|
|
||||||
let post = Post::read(&mut context.pool(), comment.post_id).await?;
|
|
||||||
let community = Community::read(&mut context.pool(), post.community_id).await?;
|
|
||||||
(comment, post, community)
|
|
||||||
};
|
|
||||||
// let person = my_local_user.person;
|
|
||||||
// Read the comment view to get extra info
|
|
||||||
|
|
||||||
let (comment_opt, post, community) = match post_or_comment_id {
|
let (comment_opt, post, community) = match post_or_comment_id {
|
||||||
PostOrCommentId::Post(post_id) => {
|
PostOrCommentId::Post(post_id) => {
|
||||||
let post_view = PostView::read(
|
let post_view = PostView::read(
|
||||||
|
@ -140,17 +116,29 @@ pub async fn send_local_notifs(
|
||||||
(None, post_view.post, post_view.community)
|
(None, post_view.post, post_view.community)
|
||||||
}
|
}
|
||||||
PostOrCommentId::Comment(comment_id) => {
|
PostOrCommentId::Comment(comment_id) => {
|
||||||
let comment_view = CommentView::read(
|
// When called from api code, we have local user view and can read with CommentView
|
||||||
&mut context.pool(),
|
// to reduce db queries. But when receiving a federated comment the user view is None,
|
||||||
comment_id,
|
// which means that comments inside private communities cant be read. As a workaround
|
||||||
local_user_view.map(|view| &view.local_user),
|
// we need to read the items manually to bypass this check.
|
||||||
)
|
if let Some(local_user_view) = local_user_view {
|
||||||
.await?;
|
// Read the comment view to get extra info
|
||||||
(
|
let comment_view = CommentView::read(
|
||||||
Some(comment_view.comment),
|
&mut context.pool(),
|
||||||
comment_view.post,
|
comment_id,
|
||||||
comment_view.community,
|
Some(&local_user_view.local_user),
|
||||||
)
|
)
|
||||||
|
.await?;
|
||||||
|
(
|
||||||
|
Some(comment_view.comment),
|
||||||
|
comment_view.post,
|
||||||
|
comment_view.community,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
let comment = Comment::read(&mut context.pool(), comment_id).await?;
|
||||||
|
let post = Post::read(&mut context.pool(), comment.post_id).await?;
|
||||||
|
let community = Community::read(&mut context.pool(), post.community_id).await?;
|
||||||
|
(Some(comment), post, community)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,8 @@ use lemmy_db_views::structs::{
|
||||||
use lemmy_db_views_actor::structs::{
|
use lemmy_db_views_actor::structs::{
|
||||||
CommentReplyView,
|
CommentReplyView,
|
||||||
CommunityModeratorView,
|
CommunityModeratorView,
|
||||||
|
InboxCombinedPaginationCursor,
|
||||||
|
InboxCombinedView,
|
||||||
PersonCommentMentionView,
|
PersonCommentMentionView,
|
||||||
PersonPostMentionView,
|
PersonPostMentionView,
|
||||||
PersonView,
|
PersonView,
|
||||||
|
@ -373,51 +375,25 @@ pub struct BlockPersonResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[skip_serializing_none]
|
#[skip_serializing_none]
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, Copy, Default, PartialEq, Eq, Hash)]
|
#[derive(Debug, Serialize, Deserialize, Clone, Default, 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))]
|
||||||
/// Get comment replies.
|
/// Get your inbox (replies, comment mentions, post mentions, and messages)
|
||||||
pub struct GetReplies {
|
pub struct ListInbox {
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
|
||||||
pub sort: Option<CommentSortType>,
|
|
||||||
#[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))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub unread_only: Option<bool>,
|
pub unread_only: Option<bool>,
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
|
|
||||||
#[cfg_attr(feature = "full", derive(TS))]
|
|
||||||
#[cfg_attr(feature = "full", ts(export))]
|
|
||||||
/// Fetches your replies.
|
|
||||||
// TODO, replies and mentions below should be redone as tagged enums.
|
|
||||||
pub struct GetRepliesResponse {
|
|
||||||
pub replies: Vec<CommentReplyView>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[skip_serializing_none]
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, Copy, Default, PartialEq, Eq, Hash)]
|
|
||||||
#[cfg_attr(feature = "full", derive(TS))]
|
|
||||||
#[cfg_attr(feature = "full", ts(export))]
|
|
||||||
/// Get mentions for your user.
|
|
||||||
pub struct GetPersonCommentMentions {
|
|
||||||
pub sort: Option<CommentSortType>,
|
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub page: Option<i64>,
|
pub page_cursor: Option<InboxCombinedPaginationCursor>,
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub limit: Option<i64>,
|
pub page_back: Option<bool>,
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
|
||||||
pub unread_only: Option<bool>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
#[cfg_attr(feature = "full", derive(TS))]
|
#[cfg_attr(feature = "full", derive(TS))]
|
||||||
#[cfg_attr(feature = "full", ts(export))]
|
#[cfg_attr(feature = "full", ts(export))]
|
||||||
/// The response of mentions for your user.
|
/// Get your inbox (replies, comment mentions, post mentions, and messages)
|
||||||
pub struct GetPersonCommentMentionsResponse {
|
pub struct ListInboxResponse {
|
||||||
pub comment_mentions: Vec<PersonCommentMentionView>,
|
pub inbox: Vec<InboxCombinedView>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, Copy, Default, PartialEq, Eq, Hash)]
|
#[derive(Debug, Serialize, Deserialize, Clone, Copy, Default, PartialEq, Eq, Hash)]
|
||||||
|
@ -437,26 +413,6 @@ pub struct PersonCommentMentionResponse {
|
||||||
pub person_comment_mention_view: PersonCommentMentionView,
|
pub person_comment_mention_view: PersonCommentMentionView,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[skip_serializing_none]
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, Copy, Default, PartialEq, Eq, Hash)]
|
|
||||||
#[cfg_attr(feature = "full", derive(TS))]
|
|
||||||
#[cfg_attr(feature = "full", ts(export))]
|
|
||||||
/// Get mentions for your user.
|
|
||||||
pub struct GetPersonPostMentions {
|
|
||||||
pub sort: Option<PostSortType>,
|
|
||||||
pub page: Option<i64>,
|
|
||||||
pub limit: Option<i64>,
|
|
||||||
pub unread_only: Option<bool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
|
||||||
#[cfg_attr(feature = "full", derive(TS))]
|
|
||||||
#[cfg_attr(feature = "full", ts(export))]
|
|
||||||
/// The response of mentions for your user.
|
|
||||||
pub struct GetPersonPostMentionsResponse {
|
|
||||||
pub post_mentions: Vec<PersonPostMentionView>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, Copy, Default, PartialEq, Eq, Hash)]
|
#[derive(Debug, Serialize, Deserialize, Clone, Copy, Default, 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))]
|
||||||
|
@ -540,12 +496,9 @@ pub struct GetReportCountResponse {
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
#[cfg_attr(feature = "full", derive(TS))]
|
#[cfg_attr(feature = "full", derive(TS))]
|
||||||
#[cfg_attr(feature = "full", ts(export))]
|
#[cfg_attr(feature = "full", ts(export))]
|
||||||
/// A response containing counts for your notifications.
|
/// A response containing a count of unread notifications.
|
||||||
pub struct GetUnreadCountResponse {
|
pub struct GetUnreadCountResponse {
|
||||||
pub replies: i64,
|
pub count: i64,
|
||||||
pub comment_mentions: i64,
|
|
||||||
pub post_mentions: i64,
|
|
||||||
pub private_messages: i64,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Default, Debug, PartialEq, Eq, Hash)]
|
#[derive(Serialize, Deserialize, Clone, Default, Debug, PartialEq, Eq, Hash)]
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use lemmy_db_schema::newtypes::{PersonId, PrivateMessageId};
|
use lemmy_db_schema::newtypes::{PersonId, PrivateMessageId};
|
||||||
use lemmy_db_views::structs::PrivateMessageView;
|
use lemmy_db_views_actor::structs::PrivateMessageView;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_with::skip_serializing_none;
|
|
||||||
#[cfg(feature = "full")]
|
#[cfg(feature = "full")]
|
||||||
use ts_rs::TS;
|
use ts_rs::TS;
|
||||||
|
|
||||||
|
@ -41,30 +40,6 @@ pub struct MarkPrivateMessageAsRead {
|
||||||
pub read: bool,
|
pub read: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[skip_serializing_none]
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, Copy, Default, PartialEq, Eq, Hash)]
|
|
||||||
#[cfg_attr(feature = "full", derive(TS))]
|
|
||||||
#[cfg_attr(feature = "full", ts(export))]
|
|
||||||
/// Get your private messages.
|
|
||||||
pub struct GetPrivateMessages {
|
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
|
||||||
pub unread_only: Option<bool>,
|
|
||||||
#[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 creator_id: Option<PersonId>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
|
||||||
#[cfg_attr(feature = "full", derive(TS))]
|
|
||||||
#[cfg_attr(feature = "full", ts(export))]
|
|
||||||
/// The private messages response.
|
|
||||||
pub struct PrivateMessagesResponse {
|
|
||||||
pub private_messages: Vec<PrivateMessageView>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
#[cfg_attr(feature = "full", derive(TS))]
|
#[cfg_attr(feature = "full", derive(TS))]
|
||||||
#[cfg_attr(feature = "full", ts(export))]
|
#[cfg_attr(feature = "full", ts(export))]
|
||||||
|
|
|
@ -11,7 +11,7 @@ use lemmy_db_schema::{
|
||||||
private_message::PrivateMessage,
|
private_message::PrivateMessage,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use lemmy_db_views::structs::PrivateMessageView;
|
use lemmy_db_views_actor::structs::PrivateMessageView;
|
||||||
use lemmy_utils::error::LemmyResult;
|
use lemmy_utils::error::LemmyResult;
|
||||||
use std::sync::{LazyLock, OnceLock};
|
use std::sync::{LazyLock, OnceLock};
|
||||||
use tokio::{
|
use tokio::{
|
||||||
|
|
|
@ -16,7 +16,7 @@ use lemmy_api_common::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
impls::actor_language::default_post_language,
|
impls::actor_language::validate_post_language,
|
||||||
newtypes::PostOrCommentId,
|
newtypes::PostOrCommentId,
|
||||||
source::{
|
source::{
|
||||||
comment::{Comment, CommentInsertForm, CommentLike, CommentLikeForm},
|
comment::{Comment, CommentInsertForm, CommentLike, CommentLikeForm},
|
||||||
|
|
|
@ -14,6 +14,7 @@ use lemmy_api_common::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
|
impls::actor_language::validate_post_language,
|
||||||
newtypes::PostOrCommentId,
|
newtypes::PostOrCommentId,
|
||||||
source::{
|
source::{
|
||||||
comment::{Comment, CommentUpdateForm},
|
comment::{Comment, CommentUpdateForm},
|
||||||
|
|
|
@ -16,7 +16,7 @@ use lemmy_api_common::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
impls::actor_language::default_post_language,
|
impls::actor_language::validate_post_language,
|
||||||
newtypes::PostOrCommentId,
|
newtypes::PostOrCommentId,
|
||||||
source::{
|
source::{
|
||||||
community::Community,
|
community::Community,
|
||||||
|
@ -162,9 +162,8 @@ pub async fn create_post(
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// TODO
|
let read_form = PostReadForm::new(post_id, person_id);
|
||||||
PostRead::mark_as_read(&mut context.pool(), &read_form).await?;
|
PostRead::mark_as_read(&mut context.pool(), &read_form).await?;
|
||||||
mark_post_as_read(person_id, post_id, &mut context.pool()).await?;
|
|
||||||
|
|
||||||
build_post_response(&context, community_id, local_user_view, post_id).await
|
build_post_response(&context, community_id, local_user_view, post_id).await
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,8 @@ use lemmy_db_schema::{
|
||||||
},
|
},
|
||||||
traits::Crud,
|
traits::Crud,
|
||||||
};
|
};
|
||||||
use lemmy_db_views::structs::{LocalUserView, PrivateMessageView};
|
use lemmy_db_views::structs::LocalUserView;
|
||||||
|
use lemmy_db_views_actor::structs::PrivateMessageView;
|
||||||
use lemmy_utils::{
|
use lemmy_utils::{
|
||||||
error::{LemmyErrorExt, LemmyErrorType, LemmyResult},
|
error::{LemmyErrorExt, LemmyErrorType, LemmyResult},
|
||||||
utils::{markdown::markdown_to_html, validation::is_valid_body_field},
|
utils::{markdown::markdown_to_html, validation::is_valid_body_field},
|
||||||
|
|
|
@ -9,7 +9,8 @@ use lemmy_db_schema::{
|
||||||
source::private_message::{PrivateMessage, PrivateMessageUpdateForm},
|
source::private_message::{PrivateMessage, PrivateMessageUpdateForm},
|
||||||
traits::Crud,
|
traits::Crud,
|
||||||
};
|
};
|
||||||
use lemmy_db_views::structs::{LocalUserView, PrivateMessageView};
|
use lemmy_db_views::structs::LocalUserView;
|
||||||
|
use lemmy_db_views_actor::structs::PrivateMessageView;
|
||||||
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||||
|
|
||||||
#[tracing::instrument(skip(context))]
|
#[tracing::instrument(skip(context))]
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
pub mod create;
|
pub mod create;
|
||||||
pub mod delete;
|
pub mod delete;
|
||||||
pub mod read;
|
|
||||||
pub mod update;
|
pub mod update;
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
use actix_web::web::{Data, Json, Query};
|
|
||||||
use lemmy_api_common::{
|
|
||||||
context::LemmyContext,
|
|
||||||
private_message::{GetPrivateMessages, PrivateMessagesResponse},
|
|
||||||
};
|
|
||||||
use lemmy_db_views::{private_message_view::PrivateMessageQuery, structs::LocalUserView};
|
|
||||||
use lemmy_utils::error::LemmyResult;
|
|
||||||
|
|
||||||
#[tracing::instrument(skip(context))]
|
|
||||||
pub async fn get_private_message(
|
|
||||||
data: Query<GetPrivateMessages>,
|
|
||||||
context: Data<LemmyContext>,
|
|
||||||
local_user_view: LocalUserView,
|
|
||||||
) -> LemmyResult<Json<PrivateMessagesResponse>> {
|
|
||||||
let person_id = local_user_view.person.id;
|
|
||||||
|
|
||||||
let page = data.page;
|
|
||||||
let limit = data.limit;
|
|
||||||
let unread_only = data.unread_only.unwrap_or_default();
|
|
||||||
let creator_id = data.creator_id;
|
|
||||||
let messages = PrivateMessageQuery {
|
|
||||||
page,
|
|
||||||
limit,
|
|
||||||
unread_only,
|
|
||||||
creator_id,
|
|
||||||
}
|
|
||||||
.list(&mut context.pool(), person_id)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(Json(PrivateMessagesResponse {
|
|
||||||
private_messages: messages,
|
|
||||||
}))
|
|
||||||
}
|
|
|
@ -14,7 +14,8 @@ use lemmy_db_schema::{
|
||||||
},
|
},
|
||||||
traits::Crud,
|
traits::Crud,
|
||||||
};
|
};
|
||||||
use lemmy_db_views::structs::{LocalUserView, PrivateMessageView};
|
use lemmy_db_views::structs::LocalUserView;
|
||||||
|
use lemmy_db_views_actor::structs::PrivateMessageView;
|
||||||
use lemmy_utils::{
|
use lemmy_utils::{
|
||||||
error::{LemmyErrorExt, LemmyErrorType, LemmyResult},
|
error::{LemmyErrorExt, LemmyErrorType, LemmyResult},
|
||||||
utils::validation::is_valid_body_field,
|
utils::validation::is_valid_body_field,
|
||||||
|
|
|
@ -14,7 +14,7 @@ use activitypub_federation::{
|
||||||
};
|
};
|
||||||
use lemmy_api_common::context::LemmyContext;
|
use lemmy_api_common::context::LemmyContext;
|
||||||
use lemmy_db_schema::source::activity::ActivitySendTargets;
|
use lemmy_db_schema::source::activity::ActivitySendTargets;
|
||||||
use lemmy_db_views::structs::PrivateMessageView;
|
use lemmy_db_views_actor::structs::PrivateMessageView;
|
||||||
use lemmy_utils::error::{LemmyError, LemmyResult};
|
use lemmy_utils::error::{LemmyError, LemmyResult};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
|
|
@ -836,3 +836,35 @@ CALL r.create_modlog_combined_trigger ('mod_remove_post');
|
||||||
|
|
||||||
CALL r.create_modlog_combined_trigger ('mod_transfer_community');
|
CALL r.create_modlog_combined_trigger ('mod_transfer_community');
|
||||||
|
|
||||||
|
|
||||||
|
-- Inbox: (replies, comment mentions, post mentions, and private_messages)
|
||||||
|
CREATE PROCEDURE r.create_inbox_combined_trigger (table_name text)
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
AS $a$
|
||||||
|
BEGIN
|
||||||
|
EXECUTE replace($b$ CREATE FUNCTION r.inbox_combined_thing_insert ( )
|
||||||
|
RETURNS TRIGGER
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
AS $$
|
||||||
|
BEGIN
|
||||||
|
INSERT INTO inbox_combined (published, thing_id)
|
||||||
|
VALUES (NEW.published, NEW.id);
|
||||||
|
RETURN NEW;
|
||||||
|
END $$;
|
||||||
|
CREATE TRIGGER inbox_combined
|
||||||
|
AFTER INSERT ON thing
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION r.inbox_combined_thing_insert ( );
|
||||||
|
$b$,
|
||||||
|
'thing',
|
||||||
|
table_name);
|
||||||
|
END;
|
||||||
|
$a$;
|
||||||
|
|
||||||
|
CALL r.create_inbox_combined_trigger ('comment_reply');
|
||||||
|
|
||||||
|
CALL r.create_inbox_combined_trigger ('person_comment_mention');
|
||||||
|
|
||||||
|
CALL r.create_inbox_combined_trigger ('person_post_mention');
|
||||||
|
|
||||||
|
CALL r.create_inbox_combined_trigger ('private_message');
|
||||||
|
|
|
@ -76,7 +76,7 @@ pub struct LocalUserId(pub i32);
|
||||||
#[cfg_attr(feature = "full", derive(DieselNewType, TS))]
|
#[cfg_attr(feature = "full", derive(DieselNewType, TS))]
|
||||||
#[cfg_attr(feature = "full", ts(export))]
|
#[cfg_attr(feature = "full", ts(export))]
|
||||||
/// The private message id.
|
/// The private message id.
|
||||||
pub struct PrivateMessageId(i32);
|
pub struct PrivateMessageId(pub i32);
|
||||||
|
|
||||||
impl fmt::Display for PrivateMessageId {
|
impl fmt::Display for PrivateMessageId {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
@ -88,13 +88,13 @@ impl fmt::Display for PrivateMessageId {
|
||||||
#[cfg_attr(feature = "full", derive(DieselNewType, TS))]
|
#[cfg_attr(feature = "full", derive(DieselNewType, TS))]
|
||||||
#[cfg_attr(feature = "full", ts(export))]
|
#[cfg_attr(feature = "full", ts(export))]
|
||||||
/// The person comment mention id.
|
/// The person comment mention id.
|
||||||
pub struct PersonCommentMentionId(i32);
|
pub struct PersonCommentMentionId(pub i32);
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)]
|
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)]
|
||||||
#[cfg_attr(feature = "full", derive(DieselNewType, TS))]
|
#[cfg_attr(feature = "full", derive(DieselNewType, TS))]
|
||||||
#[cfg_attr(feature = "full", ts(export))]
|
#[cfg_attr(feature = "full", ts(export))]
|
||||||
/// The person post mention id.
|
/// The person post mention id.
|
||||||
pub struct PersonPostMentionId(i32);
|
pub struct PersonPostMentionId(pub i32);
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)]
|
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)]
|
||||||
#[cfg_attr(feature = "full", derive(DieselNewType, TS))]
|
#[cfg_attr(feature = "full", derive(DieselNewType, TS))]
|
||||||
|
@ -130,7 +130,7 @@ pub struct LanguageId(pub i32);
|
||||||
#[cfg_attr(feature = "full", derive(DieselNewType, TS))]
|
#[cfg_attr(feature = "full", derive(DieselNewType, TS))]
|
||||||
#[cfg_attr(feature = "full", ts(export))]
|
#[cfg_attr(feature = "full", ts(export))]
|
||||||
/// The comment reply id.
|
/// The comment reply id.
|
||||||
pub struct CommentReplyId(i32);
|
pub struct CommentReplyId(pub i32);
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default, Ord, PartialOrd,
|
Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default, Ord, PartialOrd,
|
||||||
|
@ -211,6 +211,11 @@ pub struct PersonSavedCombinedId(i32);
|
||||||
#[cfg_attr(feature = "full", derive(DieselNewType))]
|
#[cfg_attr(feature = "full", derive(DieselNewType))]
|
||||||
pub struct ModlogCombinedId(i32);
|
pub struct ModlogCombinedId(i32);
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)]
|
||||||
|
#[cfg_attr(feature = "full", derive(DieselNewType))]
|
||||||
|
/// The inbox combined id
|
||||||
|
pub struct InboxCombinedId(i32);
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)]
|
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)]
|
||||||
#[cfg_attr(feature = "full", derive(DieselNewType, TS))]
|
#[cfg_attr(feature = "full", derive(DieselNewType, TS))]
|
||||||
#[cfg_attr(feature = "full", ts(export))]
|
#[cfg_attr(feature = "full", ts(export))]
|
||||||
|
|
|
@ -330,6 +330,17 @@ diesel::table! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
diesel::table! {
|
||||||
|
inbox_combined (id) {
|
||||||
|
id -> Int4,
|
||||||
|
published -> Timestamptz,
|
||||||
|
comment_reply_id -> Nullable<Int4>,
|
||||||
|
person_comment_mention_id -> Nullable<Int4>,
|
||||||
|
person_post_mention_id -> Nullable<Int4>,
|
||||||
|
private_message_id -> Nullable<Int4>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
diesel::table! {
|
diesel::table! {
|
||||||
instance (id) {
|
instance (id) {
|
||||||
id -> Int4,
|
id -> Int4,
|
||||||
|
@ -1052,6 +1063,10 @@ diesel::joinable!(email_verification -> local_user (local_user_id));
|
||||||
diesel::joinable!(federation_allowlist -> instance (instance_id));
|
diesel::joinable!(federation_allowlist -> instance (instance_id));
|
||||||
diesel::joinable!(federation_blocklist -> instance (instance_id));
|
diesel::joinable!(federation_blocklist -> instance (instance_id));
|
||||||
diesel::joinable!(federation_queue_state -> instance (instance_id));
|
diesel::joinable!(federation_queue_state -> instance (instance_id));
|
||||||
|
diesel::joinable!(inbox_combined -> comment_reply (comment_reply_id));
|
||||||
|
diesel::joinable!(inbox_combined -> person_comment_mention (person_comment_mention_id));
|
||||||
|
diesel::joinable!(inbox_combined -> person_post_mention (person_post_mention_id));
|
||||||
|
diesel::joinable!(inbox_combined -> private_message (private_message_id));
|
||||||
diesel::joinable!(instance_actions -> instance (instance_id));
|
diesel::joinable!(instance_actions -> instance (instance_id));
|
||||||
diesel::joinable!(instance_actions -> person (person_id));
|
diesel::joinable!(instance_actions -> person (person_id));
|
||||||
diesel::joinable!(local_image -> local_user (local_user_id));
|
diesel::joinable!(local_image -> local_user (local_user_id));
|
||||||
|
@ -1154,6 +1169,7 @@ diesel::allow_tables_to_appear_in_same_query!(
|
||||||
federation_blocklist,
|
federation_blocklist,
|
||||||
federation_queue_state,
|
federation_queue_state,
|
||||||
image_details,
|
image_details,
|
||||||
|
inbox_combined,
|
||||||
instance,
|
instance,
|
||||||
instance_actions,
|
instance_actions,
|
||||||
language,
|
language,
|
||||||
|
|
33
crates/db_schema/src/source/combined/inbox.rs
Normal file
33
crates/db_schema/src/source/combined/inbox.rs
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
use crate::newtypes::{
|
||||||
|
CommentReplyId,
|
||||||
|
InboxCombinedId,
|
||||||
|
PersonCommentMentionId,
|
||||||
|
PersonPostMentionId,
|
||||||
|
PrivateMessageId,
|
||||||
|
};
|
||||||
|
#[cfg(feature = "full")]
|
||||||
|
use crate::schema::inbox_combined;
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
#[cfg(feature = "full")]
|
||||||
|
use i_love_jesus::CursorKeysModule;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_with::skip_serializing_none;
|
||||||
|
|
||||||
|
#[skip_serializing_none]
|
||||||
|
#[derive(PartialEq, Eq, Serialize, Deserialize, Debug, Clone)]
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "full",
|
||||||
|
derive(Identifiable, Queryable, Selectable, CursorKeysModule)
|
||||||
|
)]
|
||||||
|
#[cfg_attr(feature = "full", diesel(table_name = inbox_combined))]
|
||||||
|
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||||
|
#[cfg_attr(feature = "full", cursor_keys_module(name = inbox_combined_keys))]
|
||||||
|
/// A combined inbox table.
|
||||||
|
pub struct InboxCombined {
|
||||||
|
pub id: InboxCombinedId,
|
||||||
|
pub published: DateTime<Utc>,
|
||||||
|
pub comment_reply_id: Option<CommentReplyId>,
|
||||||
|
pub person_comment_mention_id: Option<PersonCommentMentionId>,
|
||||||
|
pub person_post_mention_id: Option<PersonPostMentionId>,
|
||||||
|
pub private_message_id: Option<PrivateMessageId>,
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
pub mod inbox;
|
||||||
pub mod modlog;
|
pub mod modlog;
|
||||||
pub mod person_content;
|
pub mod person_content;
|
||||||
pub mod person_saved;
|
pub mod person_saved;
|
||||||
|
|
|
@ -22,8 +22,6 @@ pub mod post_view;
|
||||||
#[cfg(feature = "full")]
|
#[cfg(feature = "full")]
|
||||||
pub mod private_message_report_view;
|
pub mod private_message_report_view;
|
||||||
#[cfg(feature = "full")]
|
#[cfg(feature = "full")]
|
||||||
pub mod private_message_view;
|
|
||||||
#[cfg(feature = "full")]
|
|
||||||
pub mod registration_application_view;
|
pub mod registration_application_view;
|
||||||
#[cfg(feature = "full")]
|
#[cfg(feature = "full")]
|
||||||
pub mod report_combined_view;
|
pub mod report_combined_view;
|
||||||
|
|
|
@ -171,17 +171,6 @@ pub struct PostView {
|
||||||
pub unread_comments: i64,
|
pub unread_comments: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)]
|
|
||||||
#[cfg_attr(feature = "full", derive(TS, Queryable))]
|
|
||||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
|
||||||
#[cfg_attr(feature = "full", ts(export))]
|
|
||||||
/// A private message view.
|
|
||||||
pub struct PrivateMessageView {
|
|
||||||
pub private_message: PrivateMessage,
|
|
||||||
pub creator: Person,
|
|
||||||
pub recipient: Person,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[skip_serializing_none]
|
#[skip_serializing_none]
|
||||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)]
|
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)]
|
||||||
#[cfg_attr(feature = "full", derive(TS, Queryable))]
|
#[cfg_attr(feature = "full", derive(TS, Queryable))]
|
||||||
|
|
|
@ -18,6 +18,8 @@ workspace = true
|
||||||
full = [
|
full = [
|
||||||
"lemmy_db_schema/full",
|
"lemmy_db_schema/full",
|
||||||
"lemmy_utils/full",
|
"lemmy_utils/full",
|
||||||
|
"tracing",
|
||||||
|
"i-love-jesus",
|
||||||
"diesel",
|
"diesel",
|
||||||
"diesel-async",
|
"diesel-async",
|
||||||
"ts-rs",
|
"ts-rs",
|
||||||
|
@ -40,6 +42,9 @@ ts-rs = { workspace = true, optional = true }
|
||||||
chrono.workspace = true
|
chrono.workspace = true
|
||||||
strum = { workspace = true }
|
strum = { workspace = true }
|
||||||
lemmy_utils = { workspace = true, optional = true }
|
lemmy_utils = { workspace = true, optional = true }
|
||||||
|
tracing = { workspace = true, optional = true }
|
||||||
|
i-love-jesus = { workspace = true, optional = true }
|
||||||
|
derive-new.workspace = true
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
serial_test = { workspace = true }
|
serial_test = { workspace = true }
|
||||||
|
|
|
@ -40,6 +40,7 @@ use lemmy_db_schema::{
|
||||||
CommentSortType,
|
CommentSortType,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO get rid of all this
|
||||||
fn queries<'a>() -> Queries<
|
fn queries<'a>() -> Queries<
|
||||||
impl ReadFn<'a, CommentReplyView, (CommentReplyId, Option<PersonId>)>,
|
impl ReadFn<'a, CommentReplyView, (CommentReplyId, Option<PersonId>)>,
|
||||||
impl ListFn<'a, CommentReplyView, CommentReplyQuery>,
|
impl ListFn<'a, CommentReplyView, CommentReplyQuery>,
|
||||||
|
@ -130,6 +131,9 @@ fn queries<'a>() -> Queries<
|
||||||
query = query.filter(not(person::bot_account));
|
query = query.filter(not(person::bot_account));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Don't show replies from blocked persons
|
||||||
|
query = query.filter(person_actions::blocked.is_null());
|
||||||
|
|
||||||
query = match options.sort.unwrap_or(CommentSortType::New) {
|
query = match options.sort.unwrap_or(CommentSortType::New) {
|
||||||
CommentSortType::Hot => query.then_order_by(comment_aggregates::hot_rank.desc()),
|
CommentSortType::Hot => query.then_order_by(comment_aggregates::hot_rank.desc()),
|
||||||
CommentSortType::Controversial => {
|
CommentSortType::Controversial => {
|
||||||
|
@ -140,9 +144,6 @@ fn queries<'a>() -> Queries<
|
||||||
CommentSortType::Top => query.order_by(comment_aggregates::score.desc()),
|
CommentSortType::Top => query.order_by(comment_aggregates::score.desc()),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Don't show replies from blocked persons
|
|
||||||
query = query.filter(person_actions::blocked.is_null());
|
|
||||||
|
|
||||||
let (limit, offset) = limit_and_offset(options.page, options.limit)?;
|
let (limit, offset) = limit_and_offset(options.page, options.limit)?;
|
||||||
|
|
||||||
query
|
query
|
||||||
|
|
971
crates/db_views_actor/src/inbox_combined_view.rs
Normal file
971
crates/db_views_actor/src/inbox_combined_view.rs
Normal file
|
@ -0,0 +1,971 @@
|
||||||
|
use crate::structs::{
|
||||||
|
CommentReplyView,
|
||||||
|
InboxCombinedPaginationCursor,
|
||||||
|
InboxCombinedView,
|
||||||
|
InboxCombinedViewInternal,
|
||||||
|
PersonCommentMentionView,
|
||||||
|
PersonPostMentionView,
|
||||||
|
PrivateMessageView,
|
||||||
|
};
|
||||||
|
use diesel::{
|
||||||
|
dsl::not,
|
||||||
|
result::Error,
|
||||||
|
BoolExpressionMethods,
|
||||||
|
ExpressionMethods,
|
||||||
|
JoinOnDsl,
|
||||||
|
NullableExpressionMethods,
|
||||||
|
QueryDsl,
|
||||||
|
SelectableHelper,
|
||||||
|
};
|
||||||
|
use diesel_async::RunQueryDsl;
|
||||||
|
use i_love_jesus::PaginatedQueryBuilder;
|
||||||
|
use lemmy_db_schema::{
|
||||||
|
aliases::{self, creator_community_actions},
|
||||||
|
newtypes::PersonId,
|
||||||
|
schema::{
|
||||||
|
comment,
|
||||||
|
comment_actions,
|
||||||
|
comment_aggregates,
|
||||||
|
comment_reply,
|
||||||
|
community,
|
||||||
|
community_actions,
|
||||||
|
image_details,
|
||||||
|
inbox_combined,
|
||||||
|
instance_actions,
|
||||||
|
local_user,
|
||||||
|
person,
|
||||||
|
person_actions,
|
||||||
|
person_comment_mention,
|
||||||
|
person_post_mention,
|
||||||
|
post,
|
||||||
|
post_actions,
|
||||||
|
post_aggregates,
|
||||||
|
private_message,
|
||||||
|
},
|
||||||
|
source::{
|
||||||
|
combined::inbox::{inbox_combined_keys as key, InboxCombined},
|
||||||
|
community::CommunityFollower,
|
||||||
|
},
|
||||||
|
utils::{actions, actions_alias, functions::coalesce, get_conn, DbPool},
|
||||||
|
InternalToCombinedView,
|
||||||
|
};
|
||||||
|
use lemmy_utils::error::LemmyResult;
|
||||||
|
|
||||||
|
impl InboxCombinedViewInternal {
|
||||||
|
/// Gets the number of unread mentions
|
||||||
|
// TODO need to test this
|
||||||
|
pub async fn get_unread_count(
|
||||||
|
pool: &mut DbPool<'_>,
|
||||||
|
my_person_id: PersonId,
|
||||||
|
show_bot_accounts: bool,
|
||||||
|
) -> Result<i64, Error> {
|
||||||
|
use diesel::dsl::count;
|
||||||
|
let conn = &mut get_conn(pool).await?;
|
||||||
|
|
||||||
|
let item_creator = person::id;
|
||||||
|
let recipient_person = aliases::person1.field(person::id);
|
||||||
|
|
||||||
|
let unread_filter = comment_reply::read
|
||||||
|
.eq(false)
|
||||||
|
.or(person_comment_mention::read.eq(false))
|
||||||
|
.or(person_post_mention::read.eq(false))
|
||||||
|
// If its unread, I only want the messages to me
|
||||||
|
.or(
|
||||||
|
private_message::read
|
||||||
|
.eq(false)
|
||||||
|
.and(private_message::recipient_id.eq(my_person_id)),
|
||||||
|
);
|
||||||
|
|
||||||
|
let item_creator_join = comment::creator_id
|
||||||
|
.eq(item_creator)
|
||||||
|
.or(
|
||||||
|
inbox_combined::person_post_mention_id
|
||||||
|
.is_not_null()
|
||||||
|
.and(post::creator_id.eq(item_creator)),
|
||||||
|
)
|
||||||
|
.or(private_message::creator_id.eq(item_creator));
|
||||||
|
|
||||||
|
let recipient_join = comment_reply::recipient_id
|
||||||
|
.eq(recipient_person)
|
||||||
|
.or(person_comment_mention::recipient_id.eq(recipient_person))
|
||||||
|
.or(person_post_mention::recipient_id.eq(recipient_person));
|
||||||
|
|
||||||
|
let comment_join = comment_reply::comment_id
|
||||||
|
.eq(comment::id)
|
||||||
|
.or(person_comment_mention::comment_id.eq(comment::id));
|
||||||
|
|
||||||
|
let post_join = person_post_mention::post_id
|
||||||
|
.eq(post::id)
|
||||||
|
.or(comment::post_id.eq(post::id));
|
||||||
|
|
||||||
|
let mut query = inbox_combined::table
|
||||||
|
.left_join(comment_reply::table)
|
||||||
|
.left_join(person_comment_mention::table)
|
||||||
|
.left_join(person_post_mention::table)
|
||||||
|
.left_join(private_message::table)
|
||||||
|
.left_join(comment::table.on(comment_join))
|
||||||
|
.left_join(post::table.on(post_join))
|
||||||
|
// The item creator
|
||||||
|
.inner_join(person::table.on(item_creator_join))
|
||||||
|
// The recipient
|
||||||
|
.inner_join(aliases::person1.on(recipient_join))
|
||||||
|
.left_join(actions(
|
||||||
|
instance_actions::table,
|
||||||
|
Some(my_person_id),
|
||||||
|
person::instance_id,
|
||||||
|
))
|
||||||
|
.left_join(actions(
|
||||||
|
person_actions::table,
|
||||||
|
Some(my_person_id),
|
||||||
|
item_creator,
|
||||||
|
))
|
||||||
|
// Filter for your user
|
||||||
|
.filter(recipient_person.eq(my_person_id))
|
||||||
|
// Filter unreads
|
||||||
|
.filter(unread_filter)
|
||||||
|
// Don't count replies from blocked users
|
||||||
|
.filter(person_actions::blocked.is_null())
|
||||||
|
.filter(instance_actions::blocked.is_null())
|
||||||
|
.filter(comment::deleted.eq(false))
|
||||||
|
.filter(comment::removed.eq(false))
|
||||||
|
.filter(post::deleted.eq(false))
|
||||||
|
.filter(post::removed.eq(false))
|
||||||
|
.filter(private_message::deleted.eq(false))
|
||||||
|
.into_boxed();
|
||||||
|
|
||||||
|
// These filters need to be kept in sync with the filters in queries().list()
|
||||||
|
if !show_bot_accounts {
|
||||||
|
query = query.filter(not(person::bot_account));
|
||||||
|
}
|
||||||
|
|
||||||
|
query
|
||||||
|
.select(count(inbox_combined::id))
|
||||||
|
.first::<i64>(conn)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InboxCombinedPaginationCursor {
|
||||||
|
// get cursor for page that starts immediately after the given post
|
||||||
|
pub fn after_post(view: &InboxCombinedView) -> InboxCombinedPaginationCursor {
|
||||||
|
let (prefix, id) = match view {
|
||||||
|
InboxCombinedView::CommentReply(v) => ('R', v.comment_reply.id.0),
|
||||||
|
InboxCombinedView::CommentMention(v) => ('C', v.person_comment_mention.id.0),
|
||||||
|
InboxCombinedView::PostMention(v) => ('P', v.person_post_mention.id.0),
|
||||||
|
InboxCombinedView::PrivateMessage(v) => ('M', v.private_message.id.0),
|
||||||
|
};
|
||||||
|
// hex encoding to prevent ossification
|
||||||
|
InboxCombinedPaginationCursor(format!("{prefix}{id:x}"))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn read(&self, pool: &mut DbPool<'_>) -> Result<PaginationCursorData, Error> {
|
||||||
|
let err_msg = || Error::QueryBuilderError("Could not parse pagination token".into());
|
||||||
|
let mut query = inbox_combined::table
|
||||||
|
.select(InboxCombined::as_select())
|
||||||
|
.into_boxed();
|
||||||
|
let (prefix, id_str) = self.0.split_at_checked(1).ok_or_else(err_msg)?;
|
||||||
|
let id = i32::from_str_radix(id_str, 16).map_err(|_err| err_msg())?;
|
||||||
|
query = match prefix {
|
||||||
|
"R" => query.filter(inbox_combined::comment_reply_id.eq(id)),
|
||||||
|
"C" => query.filter(inbox_combined::person_comment_mention_id.eq(id)),
|
||||||
|
"P" => query.filter(inbox_combined::person_post_mention_id.eq(id)),
|
||||||
|
"M" => query.filter(inbox_combined::private_message_id.eq(id)),
|
||||||
|
_ => return Err(err_msg()),
|
||||||
|
};
|
||||||
|
let token = query.first(&mut get_conn(pool).await?).await?;
|
||||||
|
|
||||||
|
Ok(PaginationCursorData(token))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct PaginationCursorData(InboxCombined);
|
||||||
|
|
||||||
|
#[derive(derive_new::new)]
|
||||||
|
pub struct InboxCombinedQuery {
|
||||||
|
pub my_person_id: PersonId,
|
||||||
|
#[new(default)]
|
||||||
|
pub unread_only: Option<bool>,
|
||||||
|
#[new(default)]
|
||||||
|
pub show_bot_accounts: Option<bool>,
|
||||||
|
#[new(default)]
|
||||||
|
pub page_after: Option<PaginationCursorData>,
|
||||||
|
#[new(default)]
|
||||||
|
pub page_back: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InboxCombinedQuery {
|
||||||
|
pub async fn list(self, pool: &mut DbPool<'_>) -> LemmyResult<Vec<InboxCombinedView>> {
|
||||||
|
let conn = &mut get_conn(pool).await?;
|
||||||
|
|
||||||
|
let my_person_id = Some(self.my_person_id);
|
||||||
|
let item_creator = person::id;
|
||||||
|
let recipient_person = aliases::person1.field(person::id);
|
||||||
|
|
||||||
|
let item_creator_join = comment::creator_id
|
||||||
|
.eq(item_creator)
|
||||||
|
.or(
|
||||||
|
inbox_combined::person_post_mention_id
|
||||||
|
.is_not_null()
|
||||||
|
.and(post::creator_id.eq(item_creator)),
|
||||||
|
)
|
||||||
|
.or(private_message::creator_id.eq(item_creator));
|
||||||
|
|
||||||
|
let recipient_join = comment_reply::recipient_id
|
||||||
|
.eq(recipient_person)
|
||||||
|
.or(person_comment_mention::recipient_id.eq(recipient_person))
|
||||||
|
.or(person_post_mention::recipient_id.eq(recipient_person));
|
||||||
|
// TODO this might need fixing, because if its not unread, you want all pms, even the ones you
|
||||||
|
// sent
|
||||||
|
// .or(private_message::recipient_id.eq(recipient_person));
|
||||||
|
|
||||||
|
let comment_join = comment_reply::comment_id
|
||||||
|
.eq(comment::id)
|
||||||
|
.or(person_comment_mention::comment_id.eq(comment::id));
|
||||||
|
|
||||||
|
let post_join = person_post_mention::post_id
|
||||||
|
.eq(post::id)
|
||||||
|
.or(comment::post_id.eq(post::id));
|
||||||
|
|
||||||
|
let community_join = post::id.eq(community::id);
|
||||||
|
|
||||||
|
let mut query = inbox_combined::table
|
||||||
|
.left_join(comment_reply::table)
|
||||||
|
.left_join(person_comment_mention::table)
|
||||||
|
.left_join(person_post_mention::table)
|
||||||
|
.left_join(private_message::table)
|
||||||
|
.left_join(comment::table.on(comment_join))
|
||||||
|
.left_join(post::table.on(post_join))
|
||||||
|
.left_join(community::table.on(community_join))
|
||||||
|
// The item creator
|
||||||
|
.inner_join(person::table.on(item_creator_join))
|
||||||
|
// The recipient
|
||||||
|
.inner_join(aliases::person1.on(recipient_join))
|
||||||
|
.left_join(actions_alias(
|
||||||
|
creator_community_actions,
|
||||||
|
item_creator,
|
||||||
|
post::community_id,
|
||||||
|
))
|
||||||
|
.left_join(
|
||||||
|
local_user::table.on(
|
||||||
|
item_creator
|
||||||
|
.eq(local_user::person_id)
|
||||||
|
.and(local_user::admin.eq(true)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.left_join(actions(
|
||||||
|
community_actions::table,
|
||||||
|
my_person_id,
|
||||||
|
post::community_id,
|
||||||
|
))
|
||||||
|
.left_join(actions(
|
||||||
|
instance_actions::table,
|
||||||
|
my_person_id,
|
||||||
|
person::instance_id,
|
||||||
|
))
|
||||||
|
.left_join(actions(post_actions::table, my_person_id, post::id))
|
||||||
|
.left_join(actions(person_actions::table, my_person_id, item_creator))
|
||||||
|
.left_join(post_aggregates::table.on(post::id.eq(post_aggregates::post_id)))
|
||||||
|
.left_join(comment_aggregates::table.on(comment::id.eq(comment_aggregates::comment_id)))
|
||||||
|
.left_join(actions(comment_actions::table, my_person_id, comment::id))
|
||||||
|
.left_join(image_details::table.on(post::thumbnail_url.eq(image_details::link.nullable())))
|
||||||
|
// The recipient filter (IE only show replies to you)
|
||||||
|
.filter(recipient_person.eq(self.my_person_id))
|
||||||
|
.select((
|
||||||
|
// Specific
|
||||||
|
comment_reply::all_columns.nullable(),
|
||||||
|
person_comment_mention::all_columns.nullable(),
|
||||||
|
person_post_mention::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(),
|
||||||
|
image_details::all_columns.nullable(),
|
||||||
|
private_message::all_columns.nullable(),
|
||||||
|
// Shared
|
||||||
|
post::all_columns.nullable(),
|
||||||
|
community::all_columns.nullable(),
|
||||||
|
comment::all_columns.nullable(),
|
||||||
|
comment_aggregates::all_columns.nullable(),
|
||||||
|
comment_actions::saved.nullable().is_not_null(),
|
||||||
|
comment_actions::like_score.nullable(),
|
||||||
|
CommunityFollower::select_subscribed_type(),
|
||||||
|
person::all_columns,
|
||||||
|
aliases::person1.fields(person::all_columns),
|
||||||
|
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();
|
||||||
|
|
||||||
|
// Filters
|
||||||
|
if self.unread_only.unwrap_or_default() {
|
||||||
|
query = query.filter(
|
||||||
|
comment_reply::read
|
||||||
|
.eq(false)
|
||||||
|
.or(person_comment_mention::read.eq(false))
|
||||||
|
.or(person_post_mention::read.eq(false))
|
||||||
|
// If its unread, I only want the messages to me
|
||||||
|
.or(
|
||||||
|
private_message::read
|
||||||
|
.eq(false)
|
||||||
|
.and(private_message::recipient_id.eq(self.my_person_id)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(self.show_bot_accounts.unwrap_or_default()) {
|
||||||
|
query = query.filter(not(person::bot_account));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Dont show replies from blocked users or instances
|
||||||
|
query = query
|
||||||
|
.filter(person_actions::blocked.is_null())
|
||||||
|
.filter(instance_actions::blocked.is_null());
|
||||||
|
|
||||||
|
let mut query = PaginatedQueryBuilder::new(query);
|
||||||
|
|
||||||
|
let page_after = self.page_after.map(|c| c.0);
|
||||||
|
|
||||||
|
if self.page_back.unwrap_or_default() {
|
||||||
|
query = query.before(page_after).limit_and_offset_from_end();
|
||||||
|
} else {
|
||||||
|
query = query.after(page_after);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sorting by published
|
||||||
|
query = query
|
||||||
|
.then_desc(key::published)
|
||||||
|
// Tie breaker
|
||||||
|
.then_desc(key::id);
|
||||||
|
|
||||||
|
let res = query.load::<InboxCombinedViewInternal>(conn).await?;
|
||||||
|
|
||||||
|
// Map the query results to the enum
|
||||||
|
let out = res.into_iter().filter_map(|u| u.map_to_enum()).collect();
|
||||||
|
|
||||||
|
Ok(out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InternalToCombinedView for InboxCombinedViewInternal {
|
||||||
|
type CombinedView = InboxCombinedView;
|
||||||
|
|
||||||
|
fn map_to_enum(&self) -> Option<Self::CombinedView> {
|
||||||
|
// Use for a short alias
|
||||||
|
let v = self.clone();
|
||||||
|
|
||||||
|
if let (Some(comment_reply), Some(comment), Some(counts), Some(post), Some(community)) = (
|
||||||
|
v.comment_reply,
|
||||||
|
v.comment.clone(),
|
||||||
|
v.comment_counts.clone(),
|
||||||
|
v.post.clone(),
|
||||||
|
v.community.clone(),
|
||||||
|
) {
|
||||||
|
Some(InboxCombinedView::CommentReply(CommentReplyView {
|
||||||
|
comment_reply,
|
||||||
|
comment,
|
||||||
|
counts,
|
||||||
|
recipient: v.item_recipient,
|
||||||
|
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,
|
||||||
|
banned_from_community: v.banned_from_community,
|
||||||
|
}))
|
||||||
|
} else if let (
|
||||||
|
Some(person_comment_mention),
|
||||||
|
Some(comment),
|
||||||
|
Some(counts),
|
||||||
|
Some(post),
|
||||||
|
Some(community),
|
||||||
|
) = (
|
||||||
|
v.person_comment_mention,
|
||||||
|
v.comment,
|
||||||
|
v.comment_counts,
|
||||||
|
v.post.clone(),
|
||||||
|
v.community.clone(),
|
||||||
|
) {
|
||||||
|
Some(InboxCombinedView::CommentMention(
|
||||||
|
PersonCommentMentionView {
|
||||||
|
person_comment_mention,
|
||||||
|
comment,
|
||||||
|
counts,
|
||||||
|
recipient: v.item_recipient,
|
||||||
|
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,
|
||||||
|
banned_from_community: v.banned_from_community,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
} else if let (
|
||||||
|
Some(person_post_mention),
|
||||||
|
Some(post),
|
||||||
|
Some(counts),
|
||||||
|
Some(unread_comments),
|
||||||
|
Some(community),
|
||||||
|
) = (
|
||||||
|
v.person_post_mention,
|
||||||
|
v.post,
|
||||||
|
v.post_counts,
|
||||||
|
v.post_unread_comments,
|
||||||
|
v.community,
|
||||||
|
) {
|
||||||
|
Some(InboxCombinedView::PostMention(PersonPostMentionView {
|
||||||
|
person_post_mention,
|
||||||
|
counts,
|
||||||
|
post,
|
||||||
|
community,
|
||||||
|
recipient: v.item_recipient,
|
||||||
|
unread_comments,
|
||||||
|
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,
|
||||||
|
}))
|
||||||
|
} else if let Some(private_message) = v.private_message {
|
||||||
|
Some(InboxCombinedView::PrivateMessage(PrivateMessageView {
|
||||||
|
private_message,
|
||||||
|
creator: v.item_creator,
|
||||||
|
recipient: v.item_recipient,
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Dont delete these
|
||||||
|
// #[cfg(test)]
|
||||||
|
// #[expect(clippy::indexing_slicing)]
|
||||||
|
// mod tests {
|
||||||
|
|
||||||
|
// use crate::{inbox_combined_view::InboxCombinedQuery, structs::InboxCombinedView};
|
||||||
|
// use lemmy_db_schema::{
|
||||||
|
// source::{
|
||||||
|
// comment::{Comment, CommentInsertForm},
|
||||||
|
// community::{Community, CommunityInsertForm},
|
||||||
|
// instance::Instance,
|
||||||
|
// person::{Person, PersonInsertForm},
|
||||||
|
// post::{Post, PostInsertForm},
|
||||||
|
// },
|
||||||
|
// traits::Crud,
|
||||||
|
// utils::{build_db_pool_for_tests, DbPool},
|
||||||
|
// };
|
||||||
|
// use lemmy_utils::error::LemmyResult;
|
||||||
|
// use pretty_assertions::assert_eq;
|
||||||
|
// use serial_test::serial;
|
||||||
|
|
||||||
|
// struct Data {
|
||||||
|
// instance: Instance,
|
||||||
|
// timmy: Person,
|
||||||
|
// sara: Person,
|
||||||
|
// timmy_post: Post,
|
||||||
|
// timmy_post_2: Post,
|
||||||
|
// sara_post: Post,
|
||||||
|
// timmy_comment: Comment,
|
||||||
|
// sara_comment: Comment,
|
||||||
|
// sara_comment_2: Comment,
|
||||||
|
// }
|
||||||
|
|
||||||
|
// async fn init_data(pool: &mut DbPool<'_>) -> LemmyResult<Data> {
|
||||||
|
// let instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
||||||
|
|
||||||
|
// let timmy_form = PersonInsertForm::test_form(instance.id, "timmy_pcv");
|
||||||
|
// let timmy = Person::create(pool, &timmy_form).await?;
|
||||||
|
|
||||||
|
// let sara_form = PersonInsertForm::test_form(instance.id, "sara_pcv");
|
||||||
|
// let sara = Person::create(pool, &sara_form).await?;
|
||||||
|
|
||||||
|
// let community_form = CommunityInsertForm::new(
|
||||||
|
// instance.id,
|
||||||
|
// "test community pcv".to_string(),
|
||||||
|
// "nada".to_owned(),
|
||||||
|
// "pubkey".to_string(),
|
||||||
|
// );
|
||||||
|
// let community = Community::create(pool, &community_form).await?;
|
||||||
|
|
||||||
|
// let timmy_post_form = PostInsertForm::new("timmy post prv".into(), timmy.id, community.id);
|
||||||
|
// let timmy_post = Post::create(pool, &timmy_post_form).await?;
|
||||||
|
|
||||||
|
// let timmy_post_form_2 = PostInsertForm::new("timmy post prv 2".into(), timmy.id,
|
||||||
|
// community.id); let timmy_post_2 = Post::create(pool, &timmy_post_form_2).await?;
|
||||||
|
|
||||||
|
// let sara_post_form = PostInsertForm::new("sara post prv".into(), sara.id, community.id);
|
||||||
|
// let sara_post = Post::create(pool, &sara_post_form).await?;
|
||||||
|
|
||||||
|
// let timmy_comment_form =
|
||||||
|
// CommentInsertForm::new(timmy.id, timmy_post.id, "timmy comment prv".into());
|
||||||
|
// let timmy_comment = Comment::create(pool, &timmy_comment_form, None).await?;
|
||||||
|
|
||||||
|
// let sara_comment_form =
|
||||||
|
// CommentInsertForm::new(sara.id, timmy_post.id, "sara comment prv".into());
|
||||||
|
// let sara_comment = Comment::create(pool, &sara_comment_form, None).await?;
|
||||||
|
|
||||||
|
// let sara_comment_form_2 =
|
||||||
|
// CommentInsertForm::new(sara.id, timmy_post_2.id, "sara comment prv 2".into());
|
||||||
|
// let sara_comment_2 = Comment::create(pool, &sara_comment_form_2, None).await?;
|
||||||
|
|
||||||
|
// Ok(Data {
|
||||||
|
// instance,
|
||||||
|
// timmy,
|
||||||
|
// sara,
|
||||||
|
// timmy_post,
|
||||||
|
// timmy_post_2,
|
||||||
|
// sara_post,
|
||||||
|
// timmy_comment,
|
||||||
|
// sara_comment,
|
||||||
|
// sara_comment_2,
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
// async fn cleanup(data: Data, pool: &mut DbPool<'_>) -> LemmyResult<()> {
|
||||||
|
// Instance::delete(pool, data.instance.id).await?;
|
||||||
|
|
||||||
|
// Ok(())
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[tokio::test]
|
||||||
|
// #[serial]
|
||||||
|
// async fn test_combined() -> LemmyResult<()> {
|
||||||
|
// let pool = &build_db_pool_for_tests();
|
||||||
|
// let pool = &mut pool.into();
|
||||||
|
// let data = init_data(pool).await?;
|
||||||
|
|
||||||
|
// // Do a batch read of timmy
|
||||||
|
// let timmy_content = InboxCombinedQuery::new(data.timmy.id)
|
||||||
|
// .list(pool, &None)
|
||||||
|
// .await?;
|
||||||
|
// assert_eq!(3, timmy_content.len());
|
||||||
|
|
||||||
|
// // Make sure the types are correct
|
||||||
|
// if let InboxCombinedView::Comment(v) = &timmy_content[0] {
|
||||||
|
// assert_eq!(data.timmy_comment.id, v.comment.id);
|
||||||
|
// assert_eq!(data.timmy.id, v.creator.id);
|
||||||
|
// } else {
|
||||||
|
// panic!("wrong type");
|
||||||
|
// }
|
||||||
|
// if let InboxCombinedView::Post(v) = &timmy_content[1] {
|
||||||
|
// assert_eq!(data.timmy_post_2.id, v.post.id);
|
||||||
|
// assert_eq!(data.timmy.id, v.post.creator_id);
|
||||||
|
// } else {
|
||||||
|
// panic!("wrong type");
|
||||||
|
// }
|
||||||
|
// if let InboxCombinedView::Post(v) = &timmy_content[2] {
|
||||||
|
// assert_eq!(data.timmy_post.id, v.post.id);
|
||||||
|
// assert_eq!(data.timmy.id, v.post.creator_id);
|
||||||
|
// } else {
|
||||||
|
// panic!("wrong type");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Do a batch read of sara
|
||||||
|
// let sara_content = InboxCombinedQuery::new(data.sara.id)
|
||||||
|
// .list(pool, &None)
|
||||||
|
// .await?;
|
||||||
|
// assert_eq!(3, sara_content.len());
|
||||||
|
|
||||||
|
// // Make sure the report types are correct
|
||||||
|
// if let InboxCombinedView::Comment(v) = &sara_content[0] {
|
||||||
|
// assert_eq!(data.sara_comment_2.id, v.comment.id);
|
||||||
|
// assert_eq!(data.sara.id, v.creator.id);
|
||||||
|
// // This one was to timmy_post_2
|
||||||
|
// assert_eq!(data.timmy_post_2.id, v.post.id);
|
||||||
|
// assert_eq!(data.timmy.id, v.post.creator_id);
|
||||||
|
// } else {
|
||||||
|
// panic!("wrong type");
|
||||||
|
// }
|
||||||
|
// if let InboxCombinedView::Comment(v) = &sara_content[1] {
|
||||||
|
// assert_eq!(data.sara_comment.id, v.comment.id);
|
||||||
|
// assert_eq!(data.sara.id, v.creator.id);
|
||||||
|
// assert_eq!(data.timmy_post.id, v.post.id);
|
||||||
|
// assert_eq!(data.timmy.id, v.post.creator_id);
|
||||||
|
// } else {
|
||||||
|
// panic!("wrong type");
|
||||||
|
// }
|
||||||
|
// if let InboxCombinedView::Post(v) = &sara_content[2] {
|
||||||
|
// assert_eq!(data.sara_post.id, v.post.id);
|
||||||
|
// assert_eq!(data.sara.id, v.post.creator_id);
|
||||||
|
// } else {
|
||||||
|
// panic!("wrong type");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// cleanup(data, pool).await?;
|
||||||
|
|
||||||
|
// Ok(())
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
// #[cfg(test)]
|
||||||
|
// mod tests {
|
||||||
|
|
||||||
|
// use crate::{
|
||||||
|
// person_comment_mention_view::PersonCommentMentionQuery,
|
||||||
|
// structs::PersonCommentMentionView,
|
||||||
|
// };
|
||||||
|
// use lemmy_db_schema::{
|
||||||
|
// source::{
|
||||||
|
// comment::{Comment, CommentInsertForm},
|
||||||
|
// community::{Community, CommunityInsertForm},
|
||||||
|
// instance::Instance,
|
||||||
|
// local_user::{LocalUser, LocalUserInsertForm, LocalUserUpdateForm},
|
||||||
|
// person::{Person, PersonInsertForm, PersonUpdateForm},
|
||||||
|
// person_block::{PersonBlock, PersonBlockForm},
|
||||||
|
// person_comment_mention::{
|
||||||
|
// PersonCommentMention,
|
||||||
|
// PersonCommentMentionInsertForm,
|
||||||
|
// PersonCommentMentionUpdateForm,
|
||||||
|
// },
|
||||||
|
// post::{Post, PostInsertForm},
|
||||||
|
// },
|
||||||
|
// traits::{Blockable, Crud},
|
||||||
|
// utils::build_db_pool_for_tests,
|
||||||
|
// };
|
||||||
|
// use lemmy_db_views::structs::LocalUserView;
|
||||||
|
// use lemmy_utils::error::LemmyResult;
|
||||||
|
// use pretty_assertions::assert_eq;
|
||||||
|
// use serial_test::serial;
|
||||||
|
|
||||||
|
// #[tokio::test]
|
||||||
|
// #[serial]
|
||||||
|
// async fn test_crud() -> LemmyResult<()> {
|
||||||
|
// let pool = &build_db_pool_for_tests();
|
||||||
|
// let pool = &mut pool.into();
|
||||||
|
|
||||||
|
// let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
||||||
|
|
||||||
|
// let new_person = PersonInsertForm::test_form(inserted_instance.id, "terrylake");
|
||||||
|
|
||||||
|
// let inserted_person = Person::create(pool, &new_person).await?;
|
||||||
|
|
||||||
|
// let recipient_form = PersonInsertForm::test_form(inserted_instance.id, "terrylakes
|
||||||
|
// recipient");
|
||||||
|
|
||||||
|
// let inserted_recipient = Person::create(pool, &recipient_form).await?;
|
||||||
|
// let recipient_id = inserted_recipient.id;
|
||||||
|
|
||||||
|
// let recipient_local_user =
|
||||||
|
// LocalUser::create(pool, &LocalUserInsertForm::test_form(recipient_id), vec![]).await?;
|
||||||
|
|
||||||
|
// let new_community = CommunityInsertForm::new(
|
||||||
|
// inserted_instance.id,
|
||||||
|
// "test community lake".to_string(),
|
||||||
|
// "nada".to_owned(),
|
||||||
|
// "pubkey".to_string(),
|
||||||
|
// );
|
||||||
|
// let inserted_community = Community::create(pool, &new_community).await?;
|
||||||
|
|
||||||
|
// let new_post = PostInsertForm::new(
|
||||||
|
// "A test post".into(),
|
||||||
|
// inserted_person.id,
|
||||||
|
// inserted_community.id,
|
||||||
|
// );
|
||||||
|
// let inserted_post = Post::create(pool, &new_post).await?;
|
||||||
|
|
||||||
|
// let comment_form = CommentInsertForm::new(
|
||||||
|
// inserted_person.id,
|
||||||
|
// inserted_post.id,
|
||||||
|
// "A test comment".into(),
|
||||||
|
// );
|
||||||
|
// let inserted_comment = Comment::create(pool, &comment_form, None).await?;
|
||||||
|
|
||||||
|
// let person_comment_mention_form = PersonCommentMentionInsertForm {
|
||||||
|
// recipient_id: inserted_recipient.id,
|
||||||
|
// comment_id: inserted_comment.id,
|
||||||
|
// read: None,
|
||||||
|
// };
|
||||||
|
|
||||||
|
// let inserted_mention = PersonCommentMention::create(pool,
|
||||||
|
// &person_comment_mention_form).await?;
|
||||||
|
|
||||||
|
// let expected_mention = PersonCommentMention {
|
||||||
|
// id: inserted_mention.id,
|
||||||
|
// recipient_id: inserted_mention.recipient_id,
|
||||||
|
// comment_id: inserted_mention.comment_id,
|
||||||
|
// read: false,
|
||||||
|
// published: inserted_mention.published,
|
||||||
|
// };
|
||||||
|
|
||||||
|
// let read_mention = PersonCommentMention::read(pool, inserted_mention.id).await?;
|
||||||
|
|
||||||
|
// let person_comment_mention_update_form = PersonCommentMentionUpdateForm { read: Some(false)
|
||||||
|
// }; let updated_mention = PersonCommentMention::update(
|
||||||
|
// pool,
|
||||||
|
// inserted_mention.id,
|
||||||
|
// &person_comment_mention_update_form,
|
||||||
|
// )
|
||||||
|
// .await?;
|
||||||
|
|
||||||
|
// // Test to make sure counts and blocks work correctly
|
||||||
|
// let unread_mentions =
|
||||||
|
// PersonCommentMentionView::get_unread_count(pool, &recipient_local_user).await?;
|
||||||
|
|
||||||
|
// let query = PersonCommentMentionQuery {
|
||||||
|
// recipient_id: Some(recipient_id),
|
||||||
|
// my_person_id: Some(recipient_id),
|
||||||
|
// sort: None,
|
||||||
|
// unread_only: false,
|
||||||
|
// show_bot_accounts: true,
|
||||||
|
// page: None,
|
||||||
|
// limit: None,
|
||||||
|
// };
|
||||||
|
// let mentions = query.clone().list(pool).await?;
|
||||||
|
// assert_eq!(1, unread_mentions);
|
||||||
|
// assert_eq!(1, mentions.len());
|
||||||
|
|
||||||
|
// // Block the person, and make sure these counts are now empty
|
||||||
|
// let block_form = PersonBlockForm {
|
||||||
|
// person_id: recipient_id,
|
||||||
|
// target_id: inserted_person.id,
|
||||||
|
// };
|
||||||
|
// PersonBlock::block(pool, &block_form).await?;
|
||||||
|
|
||||||
|
// let unread_mentions_after_block =
|
||||||
|
// PersonCommentMentionView::get_unread_count(pool, &recipient_local_user).await?;
|
||||||
|
// let mentions_after_block = query.clone().list(pool).await?;
|
||||||
|
// assert_eq!(0, unread_mentions_after_block);
|
||||||
|
// assert_eq!(0, mentions_after_block.len());
|
||||||
|
|
||||||
|
// // Unblock user so we can reuse the same person
|
||||||
|
// PersonBlock::unblock(pool, &block_form).await?;
|
||||||
|
|
||||||
|
// // Turn Terry into a bot account
|
||||||
|
// let person_update_form = PersonUpdateForm {
|
||||||
|
// bot_account: Some(true),
|
||||||
|
// ..Default::default()
|
||||||
|
// };
|
||||||
|
// Person::update(pool, inserted_person.id, &person_update_form).await?;
|
||||||
|
|
||||||
|
// let recipient_local_user_update_form = LocalUserUpdateForm {
|
||||||
|
// show_bot_accounts: Some(false),
|
||||||
|
// ..Default::default()
|
||||||
|
// };
|
||||||
|
// LocalUser::update(
|
||||||
|
// pool,
|
||||||
|
// recipient_local_user.id,
|
||||||
|
// &recipient_local_user_update_form,
|
||||||
|
// )
|
||||||
|
// .await?;
|
||||||
|
// let recipient_local_user_view = LocalUserView::read(pool, recipient_local_user.id).await?;
|
||||||
|
|
||||||
|
// let unread_mentions_after_hide_bots =
|
||||||
|
// PersonCommentMentionView::get_unread_count(pool, &recipient_local_user_view.local_user)
|
||||||
|
// .await?;
|
||||||
|
|
||||||
|
// let mut query_without_bots = query.clone();
|
||||||
|
// query_without_bots.show_bot_accounts = false;
|
||||||
|
// let replies_after_hide_bots = query_without_bots.list(pool).await?;
|
||||||
|
// assert_eq!(0, unread_mentions_after_hide_bots);
|
||||||
|
// assert_eq!(0, replies_after_hide_bots.len());
|
||||||
|
|
||||||
|
// Comment::delete(pool, inserted_comment.id).await?;
|
||||||
|
// Post::delete(pool, inserted_post.id).await?;
|
||||||
|
// Community::delete(pool, inserted_community.id).await?;
|
||||||
|
// Person::delete(pool, inserted_person.id).await?;
|
||||||
|
// Person::delete(pool, inserted_recipient.id).await?;
|
||||||
|
// Instance::delete(pool, inserted_instance.id).await?;
|
||||||
|
|
||||||
|
// assert_eq!(expected_mention, read_mention);
|
||||||
|
// assert_eq!(expected_mention, inserted_mention);
|
||||||
|
// assert_eq!(expected_mention, updated_mention);
|
||||||
|
|
||||||
|
// Ok(())
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// #[cfg(test)]
|
||||||
|
// mod tests {
|
||||||
|
|
||||||
|
// use crate::{person_post_mention_view::PersonPostMentionQuery, structs::PersonPostMentionView};
|
||||||
|
// use lemmy_db_schema::{
|
||||||
|
// source::{
|
||||||
|
// community::{Community, CommunityInsertForm},
|
||||||
|
// instance::Instance,
|
||||||
|
// local_user::{LocalUser, LocalUserInsertForm, LocalUserUpdateForm},
|
||||||
|
// person::{Person, PersonInsertForm, PersonUpdateForm},
|
||||||
|
// person_block::{PersonBlock, PersonBlockForm},
|
||||||
|
// person_post_mention::{
|
||||||
|
// PersonPostMention,
|
||||||
|
// PersonPostMentionInsertForm,
|
||||||
|
// PersonPostMentionUpdateForm,
|
||||||
|
// },
|
||||||
|
// post::{Post, PostInsertForm},
|
||||||
|
// },
|
||||||
|
// traits::{Blockable, Crud},
|
||||||
|
// utils::build_db_pool_for_tests,
|
||||||
|
// };
|
||||||
|
// use lemmy_db_views::structs::LocalUserView;
|
||||||
|
// use lemmy_utils::error::LemmyResult;
|
||||||
|
// use pretty_assertions::assert_eq;
|
||||||
|
// use serial_test::serial;
|
||||||
|
|
||||||
|
// #[tokio::test]
|
||||||
|
// #[serial]
|
||||||
|
// async fn test_crud() -> LemmyResult<()> {
|
||||||
|
// let pool = &build_db_pool_for_tests().await;
|
||||||
|
// let pool = &mut pool.into();
|
||||||
|
|
||||||
|
// let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
||||||
|
|
||||||
|
// let new_person = PersonInsertForm::test_form(inserted_instance.id, "terrylake");
|
||||||
|
|
||||||
|
// let inserted_person = Person::create(pool, &new_person).await?;
|
||||||
|
|
||||||
|
// let recipient_form = PersonInsertForm::test_form(inserted_instance.id, "terrylakes
|
||||||
|
// recipient");
|
||||||
|
|
||||||
|
// let inserted_recipient = Person::create(pool, &recipient_form).await?;
|
||||||
|
// let recipient_id = inserted_recipient.id;
|
||||||
|
|
||||||
|
// let recipient_local_user =
|
||||||
|
// LocalUser::create(pool, &LocalUserInsertForm::test_form(recipient_id), vec![]).await?;
|
||||||
|
|
||||||
|
// let new_community = CommunityInsertForm::new(
|
||||||
|
// inserted_instance.id,
|
||||||
|
// "test community lake".to_string(),
|
||||||
|
// "nada".to_owned(),
|
||||||
|
// "pubkey".to_string(),
|
||||||
|
// );
|
||||||
|
// let inserted_community = Community::create(pool, &new_community).await?;
|
||||||
|
|
||||||
|
// let new_post = PostInsertForm::new(
|
||||||
|
// "A test post".into(),
|
||||||
|
// inserted_person.id,
|
||||||
|
// inserted_community.id,
|
||||||
|
// );
|
||||||
|
// let inserted_post = Post::create(pool, &new_post).await?;
|
||||||
|
|
||||||
|
// let person_post_mention_form = PersonPostMentionInsertForm {
|
||||||
|
// recipient_id: inserted_recipient.id,
|
||||||
|
// post_id: inserted_post.id,
|
||||||
|
// read: None,
|
||||||
|
// };
|
||||||
|
|
||||||
|
// let inserted_mention = PersonPostMention::create(pool, &person_post_mention_form).await?;
|
||||||
|
|
||||||
|
// let expected_mention = PersonPostMention {
|
||||||
|
// id: inserted_mention.id,
|
||||||
|
// recipient_id: inserted_mention.recipient_id,
|
||||||
|
// post_id: inserted_mention.post_id,
|
||||||
|
// read: false,
|
||||||
|
// published: inserted_mention.published,
|
||||||
|
// };
|
||||||
|
|
||||||
|
// let read_mention = PersonPostMention::read(pool, inserted_mention.id).await?;
|
||||||
|
|
||||||
|
// let person_post_mention_update_form = PersonPostMentionUpdateForm { read: Some(false) };
|
||||||
|
// let updated_mention =
|
||||||
|
// PersonPostMention::update(pool, inserted_mention.id, &person_post_mention_update_form)
|
||||||
|
// .await?;
|
||||||
|
|
||||||
|
// // Test to make sure counts and blocks work correctly
|
||||||
|
// let unread_mentions =
|
||||||
|
// PersonPostMentionView::get_unread_count(pool, &recipient_local_user).await?;
|
||||||
|
|
||||||
|
// let query = PersonPostMentionQuery {
|
||||||
|
// recipient_id: Some(recipient_id),
|
||||||
|
// my_person_id: Some(recipient_id),
|
||||||
|
// sort: None,
|
||||||
|
// unread_only: false,
|
||||||
|
// show_bot_accounts: true,
|
||||||
|
// page: None,
|
||||||
|
// limit: None,
|
||||||
|
// };
|
||||||
|
// let mentions = query.clone().list(pool).await?;
|
||||||
|
// assert_eq!(1, unread_mentions);
|
||||||
|
// assert_eq!(1, mentions.len());
|
||||||
|
|
||||||
|
// // Block the person, and make sure these counts are now empty
|
||||||
|
// let block_form = PersonBlockForm {
|
||||||
|
// person_id: recipient_id,
|
||||||
|
// target_id: inserted_person.id,
|
||||||
|
// };
|
||||||
|
// PersonBlock::block(pool, &block_form).await?;
|
||||||
|
|
||||||
|
// let unread_mentions_after_block =
|
||||||
|
// PersonPostMentionView::get_unread_count(pool, &recipient_local_user).await?;
|
||||||
|
// let mentions_after_block = query.clone().list(pool).await?;
|
||||||
|
// assert_eq!(0, unread_mentions_after_block);
|
||||||
|
// assert_eq!(0, mentions_after_block.len());
|
||||||
|
|
||||||
|
// // Unblock user so we can reuse the same person
|
||||||
|
// PersonBlock::unblock(pool, &block_form).await?;
|
||||||
|
|
||||||
|
// // Turn Terry into a bot account
|
||||||
|
// let person_update_form = PersonUpdateForm {
|
||||||
|
// bot_account: Some(true),
|
||||||
|
// ..Default::default()
|
||||||
|
// };
|
||||||
|
// Person::update(pool, inserted_person.id, &person_update_form).await?;
|
||||||
|
|
||||||
|
// let recipient_local_user_update_form = LocalUserUpdateForm {
|
||||||
|
// show_bot_accounts: Some(false),
|
||||||
|
// ..Default::default()
|
||||||
|
// };
|
||||||
|
// LocalUser::update(
|
||||||
|
// pool,
|
||||||
|
// recipient_local_user.id,
|
||||||
|
// &recipient_local_user_update_form,
|
||||||
|
// )
|
||||||
|
// .await?;
|
||||||
|
// let recipient_local_user_view = LocalUserView::read(pool, recipient_local_user.id).await?;
|
||||||
|
|
||||||
|
// let unread_mentions_after_hide_bots =
|
||||||
|
// PersonPostMentionView::get_unread_count(pool,
|
||||||
|
// &recipient_local_user_view.local_user).await?;
|
||||||
|
|
||||||
|
// let mut query_without_bots = query.clone();
|
||||||
|
// query_without_bots.show_bot_accounts = false;
|
||||||
|
// let replies_after_hide_bots = query_without_bots.list(pool).await?;
|
||||||
|
// assert_eq!(0, unread_mentions_after_hide_bots);
|
||||||
|
// assert_eq!(0, replies_after_hide_bots.len());
|
||||||
|
|
||||||
|
// Post::delete(pool, inserted_post.id).await?;
|
||||||
|
// Post::delete(pool, inserted_post.id).await?;
|
||||||
|
// Community::delete(pool, inserted_community.id).await?;
|
||||||
|
// Person::delete(pool, inserted_person.id).await?;
|
||||||
|
// Person::delete(pool, inserted_recipient.id).await?;
|
||||||
|
// Instance::delete(pool, inserted_instance.id).await?;
|
||||||
|
|
||||||
|
// assert_eq!(expected_mention, read_mention);
|
||||||
|
// assert_eq!(expected_mention, inserted_mention);
|
||||||
|
// assert_eq!(expected_mention, updated_mention);
|
||||||
|
|
||||||
|
// Ok(())
|
||||||
|
// }
|
||||||
|
// }
|
|
@ -9,7 +9,13 @@ pub mod community_person_ban_view;
|
||||||
#[cfg(feature = "full")]
|
#[cfg(feature = "full")]
|
||||||
pub mod community_view;
|
pub mod community_view;
|
||||||
#[cfg(feature = "full")]
|
#[cfg(feature = "full")]
|
||||||
|
pub mod inbox_combined_view;
|
||||||
|
#[cfg(feature = "full")]
|
||||||
pub mod person_comment_mention_view;
|
pub mod person_comment_mention_view;
|
||||||
#[cfg(feature = "full")]
|
#[cfg(feature = "full")]
|
||||||
|
pub mod person_post_mention_view;
|
||||||
|
#[cfg(feature = "full")]
|
||||||
pub mod person_view;
|
pub mod person_view;
|
||||||
|
#[cfg(feature = "full")]
|
||||||
|
pub mod private_message_view;
|
||||||
pub mod structs;
|
pub mod structs;
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use crate::structs::PersonCommentMentionView;
|
use crate::structs::PersonCommentMentionView;
|
||||||
use diesel::{
|
use diesel::{
|
||||||
dsl::{exists, not},
|
dsl::{exists, not},
|
||||||
pg::Pg,
|
|
||||||
result::Error,
|
result::Error,
|
||||||
BoolExpressionMethods,
|
BoolExpressionMethods,
|
||||||
ExpressionMethods,
|
ExpressionMethods,
|
||||||
|
@ -26,35 +25,27 @@ use lemmy_db_schema::{
|
||||||
post,
|
post,
|
||||||
},
|
},
|
||||||
source::{community::CommunityFollower, local_user::LocalUser},
|
source::{community::CommunityFollower, local_user::LocalUser},
|
||||||
utils::{
|
utils::{actions, actions_alias, get_conn, DbPool},
|
||||||
actions,
|
|
||||||
actions_alias,
|
|
||||||
get_conn,
|
|
||||||
limit_and_offset,
|
|
||||||
DbConn,
|
|
||||||
DbPool,
|
|
||||||
ListFn,
|
|
||||||
Queries,
|
|
||||||
ReadFn,
|
|
||||||
},
|
|
||||||
CommentSortType,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
fn queries<'a>() -> Queries<
|
impl PersonCommentMentionView {
|
||||||
impl ReadFn<'a, PersonCommentMentionView, (PersonCommentMentionId, Option<PersonId>)>,
|
pub async fn read(
|
||||||
impl ListFn<'a, PersonCommentMentionView, PersonCommentMentionQuery>,
|
pool: &mut DbPool<'_>,
|
||||||
> {
|
person_comment_mention_id: PersonCommentMentionId,
|
||||||
let creator_is_admin = exists(
|
my_person_id: Option<PersonId>,
|
||||||
local_user::table.filter(
|
) -> Result<Self, Error> {
|
||||||
comment::creator_id
|
let conn = &mut get_conn(pool).await?;
|
||||||
.eq(local_user::person_id)
|
|
||||||
.and(local_user::admin.eq(true)),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
let all_joins = move |query: person_comment_mention::BoxedQuery<'a, Pg>,
|
let creator_is_admin = exists(
|
||||||
my_person_id: Option<PersonId>| {
|
local_user::table.filter(
|
||||||
query
|
comment::creator_id
|
||||||
|
.eq(local_user::person_id)
|
||||||
|
.and(local_user::admin.eq(true)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
person_comment_mention::table
|
||||||
|
.find(person_comment_mention_id)
|
||||||
.inner_join(comment::table)
|
.inner_join(comment::table)
|
||||||
.inner_join(person::table.on(comment::creator_id.eq(person::id)))
|
.inner_join(person::table.on(comment::creator_id.eq(person::id)))
|
||||||
.inner_join(post::table.on(comment::post_id.eq(post::id)))
|
.inner_join(post::table.on(comment::post_id.eq(post::id)))
|
||||||
|
@ -100,80 +91,12 @@ fn queries<'a>() -> Queries<
|
||||||
person_actions::blocked.nullable().is_not_null(),
|
person_actions::blocked.nullable().is_not_null(),
|
||||||
comment_actions::like_score.nullable(),
|
comment_actions::like_score.nullable(),
|
||||||
))
|
))
|
||||||
};
|
.first(conn)
|
||||||
|
|
||||||
let read = move |mut conn: DbConn<'a>,
|
|
||||||
(person_comment_mention_id, my_person_id): (
|
|
||||||
PersonCommentMentionId,
|
|
||||||
Option<PersonId>,
|
|
||||||
)| async move {
|
|
||||||
all_joins(
|
|
||||||
person_comment_mention::table
|
|
||||||
.find(person_comment_mention_id)
|
|
||||||
.into_boxed(),
|
|
||||||
my_person_id,
|
|
||||||
)
|
|
||||||
.first(&mut conn)
|
|
||||||
.await
|
|
||||||
};
|
|
||||||
|
|
||||||
let list = move |mut conn: DbConn<'a>, options: PersonCommentMentionQuery| async move {
|
|
||||||
// These filters need to be kept in sync with the filters in
|
|
||||||
// PersonCommentMentionView::get_unread_mentions()
|
|
||||||
let mut query = all_joins(
|
|
||||||
person_comment_mention::table.into_boxed(),
|
|
||||||
options.my_person_id,
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Some(recipient_id) = options.recipient_id {
|
|
||||||
query = query.filter(person_comment_mention::recipient_id.eq(recipient_id));
|
|
||||||
}
|
|
||||||
|
|
||||||
if options.unread_only {
|
|
||||||
query = query.filter(person_comment_mention::read.eq(false));
|
|
||||||
}
|
|
||||||
|
|
||||||
if !options.show_bot_accounts {
|
|
||||||
query = query.filter(not(person::bot_account));
|
|
||||||
};
|
|
||||||
|
|
||||||
query = match options.sort.unwrap_or(CommentSortType::New) {
|
|
||||||
CommentSortType::Hot => query.then_order_by(comment_aggregates::hot_rank.desc()),
|
|
||||||
CommentSortType::Controversial => {
|
|
||||||
query.then_order_by(comment_aggregates::controversy_rank.desc())
|
|
||||||
}
|
|
||||||
CommentSortType::New => query.then_order_by(comment::published.desc()),
|
|
||||||
CommentSortType::Old => query.then_order_by(comment::published.asc()),
|
|
||||||
CommentSortType::Top => query.order_by(comment_aggregates::score.desc()),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Don't show mentions from blocked persons
|
|
||||||
query = query.filter(person_actions::blocked.is_null());
|
|
||||||
|
|
||||||
let (limit, offset) = limit_and_offset(options.page, options.limit)?;
|
|
||||||
|
|
||||||
query
|
|
||||||
.limit(limit)
|
|
||||||
.offset(offset)
|
|
||||||
.load::<PersonCommentMentionView>(&mut conn)
|
|
||||||
.await
|
|
||||||
};
|
|
||||||
|
|
||||||
Queries::new(read, list)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PersonCommentMentionView {
|
|
||||||
pub async fn read(
|
|
||||||
pool: &mut DbPool<'_>,
|
|
||||||
person_comment_mention_id: PersonCommentMentionId,
|
|
||||||
my_person_id: Option<PersonId>,
|
|
||||||
) -> Result<Self, Error> {
|
|
||||||
queries()
|
|
||||||
.read(pool, (person_comment_mention_id, my_person_id))
|
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the number of unread mentions
|
/// Gets the number of unread mentions
|
||||||
|
// TODO get rid of this
|
||||||
pub async fn get_unread_count(
|
pub async fn get_unread_count(
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
local_user: &LocalUser,
|
local_user: &LocalUser,
|
||||||
|
@ -208,195 +131,3 @@ impl PersonCommentMentionView {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
|
||||||
pub struct PersonCommentMentionQuery {
|
|
||||||
pub my_person_id: Option<PersonId>,
|
|
||||||
pub recipient_id: Option<PersonId>,
|
|
||||||
pub sort: Option<CommentSortType>,
|
|
||||||
pub unread_only: bool,
|
|
||||||
pub show_bot_accounts: bool,
|
|
||||||
pub page: Option<i64>,
|
|
||||||
pub limit: Option<i64>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PersonCommentMentionQuery {
|
|
||||||
pub async fn list(self, pool: &mut DbPool<'_>) -> Result<Vec<PersonCommentMentionView>, Error> {
|
|
||||||
queries().list(pool, self).await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
person_comment_mention_view::PersonCommentMentionQuery,
|
|
||||||
structs::PersonCommentMentionView,
|
|
||||||
};
|
|
||||||
use lemmy_db_schema::{
|
|
||||||
source::{
|
|
||||||
comment::{Comment, CommentInsertForm},
|
|
||||||
community::{Community, CommunityInsertForm},
|
|
||||||
instance::Instance,
|
|
||||||
local_user::{LocalUser, LocalUserInsertForm, LocalUserUpdateForm},
|
|
||||||
person::{Person, PersonInsertForm, PersonUpdateForm},
|
|
||||||
person_block::{PersonBlock, PersonBlockForm},
|
|
||||||
person_comment_mention::{
|
|
||||||
PersonCommentMention,
|
|
||||||
PersonCommentMentionInsertForm,
|
|
||||||
PersonCommentMentionUpdateForm,
|
|
||||||
},
|
|
||||||
post::{Post, PostInsertForm},
|
|
||||||
},
|
|
||||||
traits::{Blockable, Crud},
|
|
||||||
utils::build_db_pool_for_tests,
|
|
||||||
};
|
|
||||||
use lemmy_db_views::structs::LocalUserView;
|
|
||||||
use lemmy_utils::error::LemmyResult;
|
|
||||||
use pretty_assertions::assert_eq;
|
|
||||||
use serial_test::serial;
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
#[serial]
|
|
||||||
async fn test_crud() -> LemmyResult<()> {
|
|
||||||
let pool = &build_db_pool_for_tests();
|
|
||||||
let pool = &mut pool.into();
|
|
||||||
|
|
||||||
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
|
||||||
|
|
||||||
let new_person = PersonInsertForm::test_form(inserted_instance.id, "terrylake");
|
|
||||||
|
|
||||||
let inserted_person = Person::create(pool, &new_person).await?;
|
|
||||||
|
|
||||||
let recipient_form = PersonInsertForm::test_form(inserted_instance.id, "terrylakes recipient");
|
|
||||||
|
|
||||||
let inserted_recipient = Person::create(pool, &recipient_form).await?;
|
|
||||||
let recipient_id = inserted_recipient.id;
|
|
||||||
|
|
||||||
let recipient_local_user =
|
|
||||||
LocalUser::create(pool, &LocalUserInsertForm::test_form(recipient_id), vec![]).await?;
|
|
||||||
|
|
||||||
let new_community = CommunityInsertForm::new(
|
|
||||||
inserted_instance.id,
|
|
||||||
"test community lake".to_string(),
|
|
||||||
"nada".to_owned(),
|
|
||||||
"pubkey".to_string(),
|
|
||||||
);
|
|
||||||
let inserted_community = Community::create(pool, &new_community).await?;
|
|
||||||
|
|
||||||
let new_post = PostInsertForm::new(
|
|
||||||
"A test post".into(),
|
|
||||||
inserted_person.id,
|
|
||||||
inserted_community.id,
|
|
||||||
);
|
|
||||||
let inserted_post = Post::create(pool, &new_post).await?;
|
|
||||||
|
|
||||||
let comment_form = CommentInsertForm::new(
|
|
||||||
inserted_person.id,
|
|
||||||
inserted_post.id,
|
|
||||||
"A test comment".into(),
|
|
||||||
);
|
|
||||||
let inserted_comment = Comment::create(pool, &comment_form, None).await?;
|
|
||||||
|
|
||||||
let person_comment_mention_form = PersonCommentMentionInsertForm {
|
|
||||||
recipient_id: inserted_recipient.id,
|
|
||||||
comment_id: inserted_comment.id,
|
|
||||||
read: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let inserted_mention = PersonCommentMention::create(pool, &person_comment_mention_form).await?;
|
|
||||||
|
|
||||||
let expected_mention = PersonCommentMention {
|
|
||||||
id: inserted_mention.id,
|
|
||||||
recipient_id: inserted_mention.recipient_id,
|
|
||||||
comment_id: inserted_mention.comment_id,
|
|
||||||
read: false,
|
|
||||||
published: inserted_mention.published,
|
|
||||||
};
|
|
||||||
|
|
||||||
let read_mention = PersonCommentMention::read(pool, inserted_mention.id).await?;
|
|
||||||
|
|
||||||
let person_comment_mention_update_form = PersonCommentMentionUpdateForm { read: Some(false) };
|
|
||||||
let updated_mention = PersonCommentMention::update(
|
|
||||||
pool,
|
|
||||||
inserted_mention.id,
|
|
||||||
&person_comment_mention_update_form,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
// Test to make sure counts and blocks work correctly
|
|
||||||
let unread_mentions =
|
|
||||||
PersonCommentMentionView::get_unread_count(pool, &recipient_local_user).await?;
|
|
||||||
|
|
||||||
let query = PersonCommentMentionQuery {
|
|
||||||
recipient_id: Some(recipient_id),
|
|
||||||
my_person_id: Some(recipient_id),
|
|
||||||
sort: None,
|
|
||||||
unread_only: false,
|
|
||||||
show_bot_accounts: true,
|
|
||||||
page: None,
|
|
||||||
limit: None,
|
|
||||||
};
|
|
||||||
let mentions = query.clone().list(pool).await?;
|
|
||||||
assert_eq!(1, unread_mentions);
|
|
||||||
assert_eq!(1, mentions.len());
|
|
||||||
|
|
||||||
// Block the person, and make sure these counts are now empty
|
|
||||||
let block_form = PersonBlockForm {
|
|
||||||
person_id: recipient_id,
|
|
||||||
target_id: inserted_person.id,
|
|
||||||
};
|
|
||||||
PersonBlock::block(pool, &block_form).await?;
|
|
||||||
|
|
||||||
let unread_mentions_after_block =
|
|
||||||
PersonCommentMentionView::get_unread_count(pool, &recipient_local_user).await?;
|
|
||||||
let mentions_after_block = query.clone().list(pool).await?;
|
|
||||||
assert_eq!(0, unread_mentions_after_block);
|
|
||||||
assert_eq!(0, mentions_after_block.len());
|
|
||||||
|
|
||||||
// Unblock user so we can reuse the same person
|
|
||||||
PersonBlock::unblock(pool, &block_form).await?;
|
|
||||||
|
|
||||||
// Turn Terry into a bot account
|
|
||||||
let person_update_form = PersonUpdateForm {
|
|
||||||
bot_account: Some(true),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
Person::update(pool, inserted_person.id, &person_update_form).await?;
|
|
||||||
|
|
||||||
let recipient_local_user_update_form = LocalUserUpdateForm {
|
|
||||||
show_bot_accounts: Some(false),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
LocalUser::update(
|
|
||||||
pool,
|
|
||||||
recipient_local_user.id,
|
|
||||||
&recipient_local_user_update_form,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
let recipient_local_user_view = LocalUserView::read(pool, recipient_local_user.id).await?;
|
|
||||||
|
|
||||||
let unread_mentions_after_hide_bots =
|
|
||||||
PersonCommentMentionView::get_unread_count(pool, &recipient_local_user_view.local_user)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let mut query_without_bots = query.clone();
|
|
||||||
query_without_bots.show_bot_accounts = false;
|
|
||||||
let replies_after_hide_bots = query_without_bots.list(pool).await?;
|
|
||||||
assert_eq!(0, unread_mentions_after_hide_bots);
|
|
||||||
assert_eq!(0, replies_after_hide_bots.len());
|
|
||||||
|
|
||||||
Comment::delete(pool, inserted_comment.id).await?;
|
|
||||||
Post::delete(pool, inserted_post.id).await?;
|
|
||||||
Community::delete(pool, inserted_community.id).await?;
|
|
||||||
Person::delete(pool, inserted_person.id).await?;
|
|
||||||
Person::delete(pool, inserted_recipient.id).await?;
|
|
||||||
Instance::delete(pool, inserted_instance.id).await?;
|
|
||||||
|
|
||||||
assert_eq!(expected_mention, read_mention);
|
|
||||||
assert_eq!(expected_mention, inserted_mention);
|
|
||||||
assert_eq!(expected_mention, updated_mention);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
103
crates/db_views_actor/src/person_post_mention_view.rs
Normal file
103
crates/db_views_actor/src/person_post_mention_view.rs
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
use crate::structs::PersonPostMentionView;
|
||||||
|
use diesel::{
|
||||||
|
dsl::exists,
|
||||||
|
result::Error,
|
||||||
|
BoolExpressionMethods,
|
||||||
|
ExpressionMethods,
|
||||||
|
JoinOnDsl,
|
||||||
|
NullableExpressionMethods,
|
||||||
|
QueryDsl,
|
||||||
|
};
|
||||||
|
use diesel_async::RunQueryDsl;
|
||||||
|
use lemmy_db_schema::{
|
||||||
|
aliases::{self, creator_community_actions},
|
||||||
|
newtypes::{PersonId, PersonPostMentionId},
|
||||||
|
schema::{
|
||||||
|
community,
|
||||||
|
community_actions,
|
||||||
|
image_details,
|
||||||
|
local_user,
|
||||||
|
person,
|
||||||
|
person_actions,
|
||||||
|
person_post_mention,
|
||||||
|
post,
|
||||||
|
post_actions,
|
||||||
|
post_aggregates,
|
||||||
|
},
|
||||||
|
source::community::CommunityFollower,
|
||||||
|
utils::{actions, actions_alias, functions::coalesce, get_conn, DbPool},
|
||||||
|
};
|
||||||
|
|
||||||
|
impl PersonPostMentionView {
|
||||||
|
pub async fn read(
|
||||||
|
pool: &mut DbPool<'_>,
|
||||||
|
person_post_mention_id: PersonPostMentionId,
|
||||||
|
my_person_id: Option<PersonId>,
|
||||||
|
) -> Result<Self, Error> {
|
||||||
|
let conn = &mut get_conn(pool).await?;
|
||||||
|
|
||||||
|
let creator_is_admin = exists(
|
||||||
|
local_user::table.filter(
|
||||||
|
post::creator_id
|
||||||
|
.eq(local_user::person_id)
|
||||||
|
.and(local_user::admin.eq(true)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
person_post_mention::table
|
||||||
|
.find(person_post_mention_id)
|
||||||
|
.inner_join(post::table)
|
||||||
|
.inner_join(person::table.on(post::creator_id.eq(person::id)))
|
||||||
|
.inner_join(community::table.on(post::community_id.eq(community::id)))
|
||||||
|
.inner_join(aliases::person1)
|
||||||
|
.inner_join(post_aggregates::table.on(post::id.eq(post_aggregates::post_id)))
|
||||||
|
.left_join(image_details::table.on(post::thumbnail_url.eq(image_details::link.nullable())))
|
||||||
|
.left_join(actions(
|
||||||
|
community_actions::table,
|
||||||
|
my_person_id,
|
||||||
|
post::community_id,
|
||||||
|
))
|
||||||
|
.left_join(actions(post_actions::table, my_person_id, post::id))
|
||||||
|
.left_join(actions(
|
||||||
|
person_actions::table,
|
||||||
|
my_person_id,
|
||||||
|
post::creator_id,
|
||||||
|
))
|
||||||
|
.left_join(actions_alias(
|
||||||
|
creator_community_actions,
|
||||||
|
post::creator_id,
|
||||||
|
post::community_id,
|
||||||
|
))
|
||||||
|
.select((
|
||||||
|
person_post_mention::all_columns,
|
||||||
|
post::all_columns,
|
||||||
|
person::all_columns,
|
||||||
|
community::all_columns,
|
||||||
|
image_details::all_columns.nullable(),
|
||||||
|
aliases::person1.fields(person::all_columns),
|
||||||
|
post_aggregates::all_columns,
|
||||||
|
creator_community_actions
|
||||||
|
.field(community_actions::received_ban)
|
||||||
|
.nullable()
|
||||||
|
.is_not_null(),
|
||||||
|
community_actions::received_ban.nullable().is_not_null(),
|
||||||
|
creator_community_actions
|
||||||
|
.field(community_actions::became_moderator)
|
||||||
|
.nullable()
|
||||||
|
.is_not_null(),
|
||||||
|
creator_is_admin,
|
||||||
|
CommunityFollower::select_subscribed_type(),
|
||||||
|
post_actions::saved.nullable().is_not_null(),
|
||||||
|
post_actions::read.nullable().is_not_null(),
|
||||||
|
post_actions::hidden.nullable().is_not_null(),
|
||||||
|
person_actions::blocked.nullable().is_not_null(),
|
||||||
|
post_actions::like_score.nullable(),
|
||||||
|
coalesce(
|
||||||
|
post_aggregates::comments.nullable() - post_actions::read_comments_amount.nullable(),
|
||||||
|
post_aggregates::comments,
|
||||||
|
),
|
||||||
|
))
|
||||||
|
.first(conn)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,10 +6,12 @@ use lemmy_db_schema::{
|
||||||
comment::Comment,
|
comment::Comment,
|
||||||
comment_reply::CommentReply,
|
comment_reply::CommentReply,
|
||||||
community::Community,
|
community::Community,
|
||||||
|
images::ImageDetails,
|
||||||
person::Person,
|
person::Person,
|
||||||
person_comment_mention::PersonCommentMention,
|
person_comment_mention::PersonCommentMention,
|
||||||
person_post_mention::PersonPostMention,
|
person_post_mention::PersonPostMention,
|
||||||
post::Post,
|
post::Post,
|
||||||
|
private_message::PrivateMessage,
|
||||||
},
|
},
|
||||||
SubscribedType,
|
SubscribedType,
|
||||||
};
|
};
|
||||||
|
@ -125,6 +127,8 @@ pub struct PersonPostMentionView {
|
||||||
pub post: Post,
|
pub post: Post,
|
||||||
pub creator: Person,
|
pub creator: Person,
|
||||||
pub community: Community,
|
pub community: Community,
|
||||||
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
|
pub image_details: Option<ImageDetails>,
|
||||||
pub recipient: Person,
|
pub recipient: Person,
|
||||||
pub counts: PostAggregates,
|
pub counts: PostAggregates,
|
||||||
pub creator_banned_from_community: bool,
|
pub creator_banned_from_community: bool,
|
||||||
|
@ -133,8 +137,12 @@ pub struct PersonPostMentionView {
|
||||||
pub creator_is_admin: bool,
|
pub creator_is_admin: bool,
|
||||||
pub subscribed: SubscribedType,
|
pub subscribed: SubscribedType,
|
||||||
pub saved: bool,
|
pub saved: bool,
|
||||||
|
pub read: bool,
|
||||||
|
pub hidden: bool,
|
||||||
pub creator_blocked: bool,
|
pub creator_blocked: bool,
|
||||||
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub my_vote: Option<i16>,
|
pub my_vote: Option<i16>,
|
||||||
|
pub unread_comments: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[skip_serializing_none]
|
#[skip_serializing_none]
|
||||||
|
@ -183,3 +191,69 @@ pub struct PendingFollow {
|
||||||
pub is_new_instance: bool,
|
pub is_new_instance: bool,
|
||||||
pub subscribed: SubscribedType,
|
pub subscribed: SubscribedType,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)]
|
||||||
|
#[cfg_attr(feature = "full", derive(TS, Queryable))]
|
||||||
|
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||||
|
#[cfg_attr(feature = "full", ts(export))]
|
||||||
|
/// A private message view.
|
||||||
|
pub struct PrivateMessageView {
|
||||||
|
pub private_message: PrivateMessage,
|
||||||
|
pub creator: 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)]
|
||||||
|
#[cfg_attr(feature = "full", derive(Queryable))]
|
||||||
|
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||||
|
/// A combined inbox view
|
||||||
|
pub struct InboxCombinedViewInternal {
|
||||||
|
// Comment reply
|
||||||
|
pub comment_reply: Option<CommentReply>,
|
||||||
|
// Person comment mention
|
||||||
|
pub person_comment_mention: Option<PersonCommentMention>,
|
||||||
|
// Person post mention
|
||||||
|
pub person_post_mention: Option<PersonPostMention>,
|
||||||
|
pub post_counts: Option<PostAggregates>,
|
||||||
|
pub post_unread_comments: Option<i64>,
|
||||||
|
pub post_saved: bool,
|
||||||
|
pub post_read: bool,
|
||||||
|
pub post_hidden: bool,
|
||||||
|
pub my_post_vote: Option<i16>,
|
||||||
|
pub image_details: Option<ImageDetails>,
|
||||||
|
// Private message
|
||||||
|
pub private_message: Option<PrivateMessage>,
|
||||||
|
// Shared
|
||||||
|
pub post: Option<Post>,
|
||||||
|
pub community: Option<Community>,
|
||||||
|
pub comment: Option<Comment>,
|
||||||
|
pub comment_counts: Option<CommentAggregates>,
|
||||||
|
pub comment_saved: bool,
|
||||||
|
pub my_comment_vote: Option<i16>,
|
||||||
|
pub subscribed: SubscribedType,
|
||||||
|
pub item_creator: Person,
|
||||||
|
pub item_recipient: Person,
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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 InboxCombinedView {
|
||||||
|
CommentReply(CommentReplyView),
|
||||||
|
CommentMention(PersonCommentMentionView),
|
||||||
|
PostMention(PersonPostMentionView),
|
||||||
|
PrivateMessage(PrivateMessageView),
|
||||||
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ use lemmy_api_common::{context::LemmyContext, utils::check_private_instance};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::{community::Community, person::Person},
|
source::{community::Community, person::Person},
|
||||||
traits::ApubActor,
|
traits::ApubActor,
|
||||||
CommentSortType,
|
|
||||||
CommunityVisibility,
|
CommunityVisibility,
|
||||||
ListingType,
|
ListingType,
|
||||||
PostSortType,
|
PostSortType,
|
||||||
|
@ -15,12 +14,7 @@ use lemmy_db_views::{
|
||||||
post_view::PostQuery,
|
post_view::PostQuery,
|
||||||
structs::{PostView, SiteView},
|
structs::{PostView, SiteView},
|
||||||
};
|
};
|
||||||
use lemmy_db_views_actor::{
|
use lemmy_db_views_actor::{inbox_combined_view::InboxCombinedQuery, structs::InboxCombinedView};
|
||||||
comment_reply_view::CommentReplyQuery,
|
|
||||||
person_comment_mention_view::PersonCommentMentionQuery,
|
|
||||||
person_post_mention_view::PersonPostMentionQuery,
|
|
||||||
structs::{CommentReplyView, PersonCommentMentionView, PersonPostMentionView},
|
|
||||||
};
|
|
||||||
use lemmy_utils::{
|
use lemmy_utils::{
|
||||||
cache_header::cache_1hour,
|
cache_header::cache_1hour,
|
||||||
error::{LemmyError, LemmyErrorType, LemmyResult},
|
error::{LemmyError, LemmyErrorType, LemmyResult},
|
||||||
|
@ -361,53 +355,24 @@ async fn get_feed_front(
|
||||||
async fn get_feed_inbox(context: &LemmyContext, jwt: &str) -> LemmyResult<Channel> {
|
async fn get_feed_inbox(context: &LemmyContext, jwt: &str) -> LemmyResult<Channel> {
|
||||||
let site_view = SiteView::read_local(&mut context.pool()).await?;
|
let site_view = SiteView::read_local(&mut context.pool()).await?;
|
||||||
let local_user = local_user_view_from_jwt(jwt, context).await?;
|
let local_user = local_user_view_from_jwt(jwt, context).await?;
|
||||||
let my_person_id = Some(local_user.person.id);
|
let my_person_id = local_user.person.id;
|
||||||
let recipient_id = Some(local_user.local_user.person_id);
|
let show_bot_accounts = Some(local_user.local_user.show_bot_accounts);
|
||||||
let show_bot_accounts = local_user.local_user.show_bot_accounts;
|
let unread_only = Some(false);
|
||||||
let limit = Some(RSS_FETCH_LIMIT);
|
|
||||||
|
|
||||||
check_private_instance(&Some(local_user.clone()), &site_view.local_site)?;
|
check_private_instance(&Some(local_user.clone()), &site_view.local_site)?;
|
||||||
|
|
||||||
let replies = CommentReplyQuery {
|
let inbox = InboxCombinedQuery {
|
||||||
recipient_id,
|
|
||||||
my_person_id,
|
my_person_id,
|
||||||
|
unread_only,
|
||||||
show_bot_accounts,
|
show_bot_accounts,
|
||||||
sort: Some(CommentSortType::New),
|
page_after: None,
|
||||||
limit,
|
page_back: None,
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
.list(&mut context.pool())
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let comment_mentions = PersonCommentMentionQuery {
|
|
||||||
recipient_id,
|
|
||||||
my_person_id,
|
|
||||||
show_bot_accounts,
|
|
||||||
sort: Some(CommentSortType::New),
|
|
||||||
limit,
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
.list(&mut context.pool())
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let post_mentions = PersonPostMentionQuery {
|
|
||||||
recipient_id,
|
|
||||||
my_person_id,
|
|
||||||
show_bot_accounts,
|
|
||||||
sort: Some(PostSortType::New),
|
|
||||||
limit,
|
|
||||||
..Default::default()
|
|
||||||
}
|
}
|
||||||
.list(&mut context.pool())
|
.list(&mut context.pool())
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let protocol_and_hostname = context.settings().get_protocol_and_hostname();
|
let protocol_and_hostname = context.settings().get_protocol_and_hostname();
|
||||||
let items = create_reply_and_mention_items(
|
let items = create_reply_and_mention_items(inbox, &protocol_and_hostname)?;
|
||||||
replies,
|
|
||||||
comment_mentions,
|
|
||||||
post_mentions,
|
|
||||||
&protocol_and_hostname,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let mut channel = Channel {
|
let mut channel = Channel {
|
||||||
namespaces: RSS_NAMESPACE.clone(),
|
namespaces: RSS_NAMESPACE.clone(),
|
||||||
|
@ -426,57 +391,55 @@ async fn get_feed_inbox(context: &LemmyContext, jwt: &str) -> LemmyResult<Channe
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
fn create_reply_and_mention_items(
|
fn create_reply_and_mention_items(
|
||||||
replies: Vec<CommentReplyView>,
|
inbox: Vec<InboxCombinedView>,
|
||||||
comment_mentions: Vec<PersonCommentMentionView>,
|
|
||||||
post_mentions: Vec<PersonPostMentionView>,
|
|
||||||
protocol_and_hostname: &str,
|
protocol_and_hostname: &str,
|
||||||
) -> LemmyResult<Vec<Item>> {
|
) -> LemmyResult<Vec<Item>> {
|
||||||
let mut reply_items: Vec<Item> = replies
|
let reply_items: Vec<Item> = inbox
|
||||||
.iter()
|
.iter()
|
||||||
.map(|r| {
|
.map(|r| match r {
|
||||||
let reply_url = format!("{}/comment/{}", protocol_and_hostname, r.comment.id);
|
InboxCombinedView::CommentReply(v) => {
|
||||||
build_item(
|
let reply_url = format!("{}/comment/{}", protocol_and_hostname, v.comment.id);
|
||||||
&r.creator.name,
|
build_item(
|
||||||
&r.comment.published,
|
&v.creator.name,
|
||||||
&reply_url,
|
&v.comment.published,
|
||||||
&r.comment.content,
|
&reply_url,
|
||||||
protocol_and_hostname,
|
&v.comment.content,
|
||||||
)
|
protocol_and_hostname,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
InboxCombinedView::CommentMention(v) => {
|
||||||
|
let mention_url = format!("{}/comment/{}", protocol_and_hostname, v.comment.id);
|
||||||
|
build_item(
|
||||||
|
&v.creator.name,
|
||||||
|
&v.comment.published,
|
||||||
|
&mention_url,
|
||||||
|
&v.comment.content,
|
||||||
|
protocol_and_hostname,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
InboxCombinedView::PostMention(v) => {
|
||||||
|
let mention_url = format!("{}/post/{}", protocol_and_hostname, v.post.id);
|
||||||
|
build_item(
|
||||||
|
&v.creator.name,
|
||||||
|
&v.post.published,
|
||||||
|
&mention_url,
|
||||||
|
&v.post.body.clone().unwrap_or_default(),
|
||||||
|
protocol_and_hostname,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
InboxCombinedView::PrivateMessage(v) => {
|
||||||
|
let inbox_url = format!("{}/inbox", protocol_and_hostname);
|
||||||
|
build_item(
|
||||||
|
&v.creator.name,
|
||||||
|
&v.private_message.published,
|
||||||
|
&inbox_url,
|
||||||
|
&v.private_message.content,
|
||||||
|
protocol_and_hostname,
|
||||||
|
)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.collect::<LemmyResult<Vec<Item>>>()?;
|
.collect::<LemmyResult<Vec<Item>>>()?;
|
||||||
|
|
||||||
let mut comment_mention_items: Vec<Item> = comment_mentions
|
|
||||||
.iter()
|
|
||||||
.map(|m| {
|
|
||||||
let mention_url = format!("{}/comment/{}", protocol_and_hostname, m.comment.id);
|
|
||||||
build_item(
|
|
||||||
&m.creator.name,
|
|
||||||
&m.comment.published,
|
|
||||||
&mention_url,
|
|
||||||
&m.comment.content,
|
|
||||||
protocol_and_hostname,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect::<LemmyResult<Vec<Item>>>()?;
|
|
||||||
|
|
||||||
reply_items.append(&mut comment_mention_items);
|
|
||||||
|
|
||||||
let mut post_mention_items: Vec<Item> = post_mentions
|
|
||||||
.iter()
|
|
||||||
.map(|m| {
|
|
||||||
let mention_url = format!("{}/post/{}", protocol_and_hostname, m.post.id);
|
|
||||||
build_item(
|
|
||||||
&m.creator.name,
|
|
||||||
&m.post.published,
|
|
||||||
&mention_url,
|
|
||||||
&m.post.body.clone().unwrap_or_default(),
|
|
||||||
protocol_and_hostname,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect::<LemmyResult<Vec<Item>>>()?;
|
|
||||||
|
|
||||||
reply_items.append(&mut post_mention_items);
|
|
||||||
|
|
||||||
Ok(reply_items)
|
Ok(reply_items)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
-- Rename the person_mention table to person_comment_mention
|
|
||||||
ALTER TABLE person_mention RENAME TO person_comment_mention;
|
|
||||||
|
|
||||||
-- Create the new post_mention table
|
|
||||||
CREATE TABLE person_post_mention (
|
|
||||||
id serial PRIMARY KEY,
|
|
||||||
recipient_id int REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
|
|
||||||
post_id int REFERENCES post ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
|
|
||||||
read boolean DEFAULT FALSE NOT NULL,
|
|
||||||
published timestamptz NOT NULL DEFAULT now(),
|
|
||||||
UNIQUE (recipient_id, post_id)
|
|
||||||
);
|
|
|
@ -1,5 +1,5 @@
|
||||||
-- Rename the person_mention table to person_comment_mention
|
-- Rename the person_mention table to person_comment_mention
|
||||||
ALTER TABLE person_comment_mention RENAME TO person_mention;
|
ALTER TABLE person_comment_mention RENAME TO person_mention;
|
||||||
|
|
||||||
-- Drop the new table
|
-- Drop the new tables
|
||||||
DROP TABLE person_post_mention;
|
DROP TABLE person_post_mention, inbox_combined;
|
71
migrations/2024-12-10-193418_add_inbox_combined_table/up.sql
Normal file
71
migrations/2024-12-10-193418_add_inbox_combined_table/up.sql
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
-- Creates combined tables for
|
||||||
|
-- Inbox: (replies, comment mentions, post mentions, and private_messages)
|
||||||
|
|
||||||
|
-- Also add post mentions, since these didn't exist before.
|
||||||
|
|
||||||
|
-- Rename the person_mention table to person_comment_mention
|
||||||
|
ALTER TABLE person_mention RENAME TO person_comment_mention;
|
||||||
|
|
||||||
|
-- Create the new post_mention table
|
||||||
|
CREATE TABLE person_post_mention (
|
||||||
|
id serial PRIMARY KEY,
|
||||||
|
recipient_id int REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
|
||||||
|
post_id int REFERENCES post ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
|
||||||
|
read boolean DEFAULT FALSE NOT NULL,
|
||||||
|
published timestamptz NOT NULL DEFAULT now(),
|
||||||
|
UNIQUE (recipient_id, post_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE inbox_combined (
|
||||||
|
id serial PRIMARY KEY,
|
||||||
|
published timestamptz NOT NULL,
|
||||||
|
comment_reply_id int UNIQUE REFERENCES comment_reply ON UPDATE CASCADE ON DELETE CASCADE,
|
||||||
|
person_comment_mention_id int UNIQUE REFERENCES person_comment_mention ON UPDATE CASCADE ON DELETE CASCADE,
|
||||||
|
person_post_mention_id int UNIQUE REFERENCES person_post_mention ON UPDATE CASCADE ON DELETE CASCADE,
|
||||||
|
private_message_id int UNIQUE REFERENCES private_message ON UPDATE CASCADE ON DELETE CASCADE,
|
||||||
|
-- Make sure only one of the columns is not null
|
||||||
|
CHECK (num_nonnulls (comment_reply_id, person_comment_mention_id, person_post_mention_id, private_message_id) = 1)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_inbox_combined_published ON inbox_combined (published DESC, id DESC);
|
||||||
|
|
||||||
|
CREATE INDEX idx_inbox_combined_published_asc ON inbox_combined (reverse_timestamp_sort (published) DESC, id DESC);
|
||||||
|
|
||||||
|
-- Updating the history
|
||||||
|
INSERT INTO inbox_combined (published, comment_reply_id, person_comment_mention_id, person_post_mention_id, private_message_id)
|
||||||
|
SELECT
|
||||||
|
published,
|
||||||
|
id,
|
||||||
|
NULL::int,
|
||||||
|
NULL::int,
|
||||||
|
NULL::int
|
||||||
|
FROM
|
||||||
|
comment_reply
|
||||||
|
UNION ALL
|
||||||
|
SELECT
|
||||||
|
published,
|
||||||
|
NULL::int,
|
||||||
|
id,
|
||||||
|
NULL::int,
|
||||||
|
NULL::int
|
||||||
|
FROM
|
||||||
|
person_comment_mention
|
||||||
|
UNION ALL
|
||||||
|
SELECT
|
||||||
|
published,
|
||||||
|
NULL::int,
|
||||||
|
NULL::int,
|
||||||
|
id,
|
||||||
|
NULL::int
|
||||||
|
FROM
|
||||||
|
person_post_mention
|
||||||
|
UNION ALL
|
||||||
|
SELECT
|
||||||
|
published,
|
||||||
|
NULL::int,
|
||||||
|
NULL::int,
|
||||||
|
NULL::int,
|
||||||
|
id
|
||||||
|
FROM
|
||||||
|
private_message;
|
||||||
|
|
|
@ -28,10 +28,9 @@ use lemmy_api::{
|
||||||
login::login,
|
login::login,
|
||||||
logout::logout,
|
logout::logout,
|
||||||
notifications::{
|
notifications::{
|
||||||
list_mentions::list_mentions,
|
|
||||||
list_replies::list_replies,
|
|
||||||
mark_all_read::mark_all_notifications_read,
|
mark_all_read::mark_all_notifications_read,
|
||||||
mark_mention_read::mark_person_mention_as_read,
|
mark_comment_mention_read::mark_comment_mention_as_read,
|
||||||
|
mark_post_mention_read::mark_post_mention_as_read,
|
||||||
mark_reply_read::mark_reply_as_read,
|
mark_reply_read::mark_reply_as_read,
|
||||||
unread_count::unread_count,
|
unread_count::unread_count,
|
||||||
},
|
},
|
||||||
|
@ -109,7 +108,6 @@ use lemmy_api_crud::{
|
||||||
private_message::{
|
private_message::{
|
||||||
create::create_private_message,
|
create::create_private_message,
|
||||||
delete::delete_private_message,
|
delete::delete_private_message,
|
||||||
read::get_private_message,
|
|
||||||
update::update_private_message,
|
update::update_private_message,
|
||||||
},
|
},
|
||||||
site::{create::create_site, read::get_site_v3, update::update_site},
|
site::{create::create_site, read::get_site_v3, update::update_site},
|
||||||
|
@ -242,7 +240,6 @@ pub fn config(cfg: &mut ServiceConfig, rate_limit: &RateLimitCell) {
|
||||||
.service(
|
.service(
|
||||||
scope("/private_message")
|
scope("/private_message")
|
||||||
.wrap(rate_limit.message())
|
.wrap(rate_limit.message())
|
||||||
.route("/list", get().to(get_private_message))
|
|
||||||
.route("", post().to(create_private_message))
|
.route("", post().to(create_private_message))
|
||||||
.route("", put().to(update_private_message))
|
.route("", put().to(update_private_message))
|
||||||
.route("/delete", post().to(delete_private_message))
|
.route("/delete", post().to(delete_private_message))
|
||||||
|
@ -302,12 +299,14 @@ pub fn config(cfg: &mut ServiceConfig, rate_limit: &RateLimitCell) {
|
||||||
scope("/user")
|
scope("/user")
|
||||||
.wrap(rate_limit.message())
|
.wrap(rate_limit.message())
|
||||||
.route("", get().to(read_person))
|
.route("", get().to(read_person))
|
||||||
.route("/mention", get().to(list_mentions))
|
|
||||||
.route(
|
.route(
|
||||||
"/mention/mark_as_read",
|
"/mention/comment/mark_as_read",
|
||||||
post().to(mark_person_mention_as_read),
|
post().to(mark_comment_mention_as_read),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/mention/post/mark_as_read",
|
||||||
|
post().to(mark_post_mention_as_read),
|
||||||
)
|
)
|
||||||
.route("/replies", get().to(list_replies))
|
|
||||||
// Admin action. I don't like that it's in /user
|
// Admin action. I don't like that it's in /user
|
||||||
.route("/ban", post().to(ban_from_site))
|
.route("/ban", post().to(ban_from_site))
|
||||||
.route("/banned", get().to(list_banned_users))
|
.route("/banned", get().to(list_banned_users))
|
||||||
|
|
|
@ -35,10 +35,10 @@ use lemmy_api::{
|
||||||
login::login,
|
login::login,
|
||||||
logout::logout,
|
logout::logout,
|
||||||
notifications::{
|
notifications::{
|
||||||
list_mentions::list_mentions,
|
list_inbox::list_inbox,
|
||||||
list_replies::list_replies,
|
|
||||||
mark_all_read::mark_all_notifications_read,
|
mark_all_read::mark_all_notifications_read,
|
||||||
mark_mention_read::mark_person_mention_as_read,
|
mark_comment_mention_read::mark_comment_mention_as_read,
|
||||||
|
mark_post_mention_read::mark_post_mention_as_read,
|
||||||
mark_reply_read::mark_reply_as_read,
|
mark_reply_read::mark_reply_as_read,
|
||||||
unread_count::unread_count,
|
unread_count::unread_count,
|
||||||
},
|
},
|
||||||
|
@ -126,7 +126,6 @@ use lemmy_api_crud::{
|
||||||
private_message::{
|
private_message::{
|
||||||
create::create_private_message,
|
create::create_private_message,
|
||||||
delete::delete_private_message,
|
delete::delete_private_message,
|
||||||
read::get_private_message,
|
|
||||||
update::update_private_message,
|
update::update_private_message,
|
||||||
},
|
},
|
||||||
site::{create::create_site, read::get_site_v4, update::update_site},
|
site::{create::create_site, read::get_site_v4, update::update_site},
|
||||||
|
@ -256,7 +255,6 @@ pub fn config(cfg: &mut ServiceConfig, rate_limit: &RateLimitCell) {
|
||||||
// Private Message
|
// Private Message
|
||||||
.service(
|
.service(
|
||||||
scope("/private_message")
|
scope("/private_message")
|
||||||
.route("/list", get().to(get_private_message))
|
|
||||||
.route("", post().to(create_private_message))
|
.route("", post().to(create_private_message))
|
||||||
.route("", put().to(update_private_message))
|
.route("", put().to(update_private_message))
|
||||||
.route("/delete", post().to(delete_private_message))
|
.route("/delete", post().to(delete_private_message))
|
||||||
|
@ -298,12 +296,15 @@ pub fn config(cfg: &mut ServiceConfig, rate_limit: &RateLimitCell) {
|
||||||
scope("/account")
|
scope("/account")
|
||||||
.route("", get().to(get_my_user))
|
.route("", get().to(get_my_user))
|
||||||
.route("/list_media", get().to(list_media))
|
.route("/list_media", get().to(list_media))
|
||||||
.route("/mention", get().to(list_mentions))
|
.route("/inbox", get().to(list_inbox))
|
||||||
.route("/replies", get().to(list_replies))
|
|
||||||
.route("/delete", post().to(delete_account))
|
.route("/delete", post().to(delete_account))
|
||||||
.route(
|
.route(
|
||||||
"/mention/mark_as_read",
|
"/mention/comment/mark_as_read",
|
||||||
post().to(mark_person_mention_as_read),
|
post().to(mark_comment_mention_as_read),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/mention/post/mark_as_read",
|
||||||
|
post().to(mark_post_mention_as_read),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/mention/mark_as_read/all",
|
"/mention/mark_as_read/all",
|
||||||
|
|
Loading…
Reference in a new issue