diff --git a/crates/api/src/comment/like.rs b/crates/api/src/comment/like.rs index fbc720102..0815b3863 100644 --- a/crates/api/src/comment/like.rs +++ b/crates/api/src/comment/like.rs @@ -5,10 +5,10 @@ use lemmy_api_common::{ comment::{CommentResponse, CreateCommentLike}, context::LemmyContext, send_activity::{ActivityChannel, SendActivityData}, - utils::{check_bot_account, check_community_user_action, check_local_vote_mode, VoteItem}, + utils::{check_bot_account, check_community_user_action, check_local_vote_mode}, }; use lemmy_db_schema::{ - newtypes::LocalUserId, + newtypes::{LocalUserId, PostOrCommentId}, source::{ comment::{CommentLike, CommentLikeForm}, comment_reply::CommentReply, @@ -33,7 +33,7 @@ pub async fn like_comment( check_local_vote_mode( data.score, - VoteItem::Comment(comment_id), + PostOrCommentId::Comment(comment_id), &local_site, local_user_view.person.id, &mut context.pool(), diff --git a/crates/api/src/local_user/notifications/list_comment_mentions.rs b/crates/api/src/local_user/notifications/list_comment_mentions.rs new file mode 100644 index 000000000..4617844ba --- /dev/null +++ b/crates/api/src/local_user/notifications/list_comment_mentions.rs @@ -0,0 +1,36 @@ +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, + context: Data, + local_user_view: LocalUserView, +) -> LemmyResult> { + 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 })) +} diff --git a/crates/api/src/local_user/notifications/list_mentions.rs b/crates/api/src/local_user/notifications/list_post_mentions.rs similarity index 65% rename from crates/api/src/local_user/notifications/list_mentions.rs rename to crates/api/src/local_user/notifications/list_post_mentions.rs index bf3cd8e0d..e39dc59af 100644 --- a/crates/api/src/local_user/notifications/list_mentions.rs +++ b/crates/api/src/local_user/notifications/list_post_mentions.rs @@ -1,18 +1,18 @@ use actix_web::web::{Data, Json, Query}; use lemmy_api_common::{ context::LemmyContext, - person::{GetPersonMentions, GetPersonMentionsResponse}, + person::{GetPersonPostMentions, GetPersonPostMentionsResponse}, }; use lemmy_db_views::structs::LocalUserView; -use lemmy_db_views_actor::person_mention_view::PersonMentionQuery; +use lemmy_db_views_actor::person_post_mention_view::PersonPostMentionQuery; use lemmy_utils::error::LemmyResult; #[tracing::instrument(skip(context))] -pub async fn list_mentions( - data: Query, +pub async fn list_post_mentions( + data: Query, context: Data, local_user_view: LocalUserView, -) -> LemmyResult> { +) -> LemmyResult> { let sort = data.sort; let page = data.page; let limit = data.limit; @@ -20,7 +20,7 @@ pub async fn list_mentions( let person_id = Some(local_user_view.person.id); let show_bot_accounts = local_user_view.local_user.show_bot_accounts; - let mentions = PersonMentionQuery { + let post_mentions = PersonPostMentionQuery { recipient_id: person_id, my_person_id: person_id, sort, @@ -32,5 +32,5 @@ pub async fn list_mentions( .list(&mut context.pool()) .await?; - Ok(Json(GetPersonMentionsResponse { mentions })) + Ok(Json(GetPersonPostMentionsResponse { post_mentions })) } diff --git a/crates/api/src/local_user/notifications/mark_all_read.rs b/crates/api/src/local_user/notifications/mark_all_read.rs index 558d276f7..929af5ac3 100644 --- a/crates/api/src/local_user/notifications/mark_all_read.rs +++ b/crates/api/src/local_user/notifications/mark_all_read.rs @@ -2,7 +2,7 @@ use actix_web::web::{Data, Json}; use lemmy_api_common::{context::LemmyContext, person::GetRepliesResponse}; use lemmy_db_schema::source::{ comment_reply::CommentReply, - person_mention::PersonMention, + person_comment_mention::PersonCommentMention, private_message::PrivateMessage, }; use lemmy_db_views::structs::LocalUserView; @@ -20,8 +20,8 @@ pub async fn mark_all_notifications_read( .await .with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?; - // Mark all user mentions as read - PersonMention::mark_all_as_read(&mut context.pool(), person_id) + // Mark all comment mentions as read + PersonCommentMention::mark_all_as_read(&mut context.pool(), person_id) .await .with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?; diff --git a/crates/api/src/local_user/notifications/mark_comment_mention_read.rs b/crates/api/src/local_user/notifications/mark_comment_mention_read.rs new file mode 100644 index 000000000..4a9c40092 --- /dev/null +++ b/crates/api/src/local_user/notifications/mark_comment_mention_read.rs @@ -0,0 +1,50 @@ +use actix_web::web::{Data, Json}; +use lemmy_api_common::{ + context::LemmyContext, + person::{MarkPersonCommentMentionAsRead, PersonCommentMentionResponse}, +}; +use lemmy_db_schema::{ + source::person_comment_mention::{PersonCommentMention, PersonCommentMentionUpdateForm}, + traits::Crud, +}; +use lemmy_db_views::structs::LocalUserView; +use lemmy_db_views_actor::structs::PersonCommentMentionView; +use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; + +#[tracing::instrument(skip(context))] +pub async fn mark_comment_mention_as_read( + data: Json, + context: Data, + local_user_view: LocalUserView, +) -> LemmyResult> { + let person_comment_mention_id = data.person_comment_mention_id; + let read_person_comment_mention = + PersonCommentMention::read(&mut context.pool(), person_comment_mention_id).await?; + + if local_user_view.person.id != read_person_comment_mention.recipient_id { + Err(LemmyErrorType::CouldntUpdateComment)? + } + + let person_comment_mention_id = read_person_comment_mention.id; + let read = Some(data.read); + PersonCommentMention::update( + &mut context.pool(), + person_comment_mention_id, + &PersonCommentMentionUpdateForm { read }, + ) + .await + .with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?; + + let person_comment_mention_id = read_person_comment_mention.id; + let person_id = local_user_view.person.id; + let person_comment_mention_view = PersonCommentMentionView::read( + &mut context.pool(), + person_comment_mention_id, + Some(person_id), + ) + .await?; + + Ok(Json(PersonCommentMentionResponse { + person_comment_mention_view, + })) +} diff --git a/crates/api/src/local_user/notifications/mark_mention_read.rs b/crates/api/src/local_user/notifications/mark_mention_read.rs deleted file mode 100644 index 9a839b2b4..000000000 --- a/crates/api/src/local_user/notifications/mark_mention_read.rs +++ /dev/null @@ -1,45 +0,0 @@ -use actix_web::web::{Data, Json}; -use lemmy_api_common::{ - context::LemmyContext, - person::{MarkPersonMentionAsRead, PersonMentionResponse}, -}; -use lemmy_db_schema::{ - source::person_mention::{PersonMention, PersonMentionUpdateForm}, - traits::Crud, -}; -use lemmy_db_views::structs::LocalUserView; -use lemmy_db_views_actor::structs::PersonMentionView; -use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; - -#[tracing::instrument(skip(context))] -pub async fn mark_person_mention_as_read( - data: Json, - context: Data, - local_user_view: LocalUserView, -) -> LemmyResult> { - let person_mention_id = data.person_mention_id; - let read_person_mention = PersonMention::read(&mut context.pool(), person_mention_id).await?; - - if local_user_view.person.id != read_person_mention.recipient_id { - Err(LemmyErrorType::CouldntUpdateComment)? - } - - let person_mention_id = read_person_mention.id; - let read = Some(data.read); - PersonMention::update( - &mut context.pool(), - person_mention_id, - &PersonMentionUpdateForm { read }, - ) - .await - .with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?; - - let person_mention_id = read_person_mention.id; - let person_id = local_user_view.person.id; - let person_mention_view = - PersonMentionView::read(&mut context.pool(), person_mention_id, Some(person_id)).await?; - - Ok(Json(PersonMentionResponse { - person_mention_view, - })) -} diff --git a/crates/api/src/local_user/notifications/mark_post_mention_read.rs b/crates/api/src/local_user/notifications/mark_post_mention_read.rs new file mode 100644 index 000000000..35a12c65c --- /dev/null +++ b/crates/api/src/local_user/notifications/mark_post_mention_read.rs @@ -0,0 +1,47 @@ +use actix_web::web::{Data, Json}; +use lemmy_api_common::{ + context::LemmyContext, + person::{MarkPersonPostMentionAsRead, PersonPostMentionResponse}, +}; +use lemmy_db_schema::{ + source::person_post_mention::{PersonPostMention, PersonPostMentionUpdateForm}, + traits::Crud, +}; +use lemmy_db_views::structs::LocalUserView; +use lemmy_db_views_actor::structs::PersonPostMentionView; +use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; + +#[tracing::instrument(skip(context))] +pub async fn mark_post_mention_as_read( + data: Json, + context: Data, + local_user_view: LocalUserView, +) -> LemmyResult> { + let person_post_mention_id = data.person_post_mention_id; + let read_person_post_mention = + PersonPostMention::read(&mut context.pool(), person_post_mention_id).await?; + + if local_user_view.person.id != read_person_post_mention.recipient_id { + Err(LemmyErrorType::CouldntUpdatePost)? + } + + let person_post_mention_id = read_person_post_mention.id; + let read = Some(data.read); + PersonPostMention::update( + &mut context.pool(), + person_post_mention_id, + &PersonPostMentionUpdateForm { read }, + ) + .await + .with_lemmy_type(LemmyErrorType::CouldntUpdatePost)?; + + let person_post_mention_id = read_person_post_mention.id; + let person_id = local_user_view.person.id; + let person_post_mention_view = + PersonPostMentionView::read(&mut context.pool(), person_post_mention_id, Some(person_id)) + .await?; + + Ok(Json(PersonPostMentionResponse { + person_post_mention_view, + })) +} diff --git a/crates/api/src/local_user/notifications/mod.rs b/crates/api/src/local_user/notifications/mod.rs index 35567afde..e62905109 100644 --- a/crates/api/src/local_user/notifications/mod.rs +++ b/crates/api/src/local_user/notifications/mod.rs @@ -1,6 +1,8 @@ -pub mod list_mentions; +pub mod list_comment_mentions; +pub mod list_post_mentions; pub mod list_replies; pub mod mark_all_read; -pub mod mark_mention_read; +pub mod mark_comment_mention_read; +pub mod mark_post_mention_read; pub mod mark_reply_read; pub mod unread_count; diff --git a/crates/api/src/local_user/notifications/unread_count.rs b/crates/api/src/local_user/notifications/unread_count.rs index 4c6c65263..62bf7a0c9 100644 --- a/crates/api/src/local_user/notifications/unread_count.rs +++ b/crates/api/src/local_user/notifications/unread_count.rs @@ -1,7 +1,11 @@ use actix_web::web::{Data, Json}; use lemmy_api_common::{context::LemmyContext, person::GetUnreadCountResponse}; use lemmy_db_views::structs::{LocalUserView, PrivateMessageView}; -use lemmy_db_views_actor::structs::{CommentReplyView, PersonMentionView}; +use lemmy_db_views_actor::structs::{ + CommentReplyView, + PersonCommentMentionView, + PersonPostMentionView, +}; use lemmy_utils::error::LemmyResult; #[tracing::instrument(skip(context))] @@ -12,18 +16,23 @@ pub async fn unread_count( let person_id = local_user_view.person.id; let replies = - CommentReplyView::get_unread_replies(&mut context.pool(), &local_user_view.local_user).await?; + CommentReplyView::get_unread_count(&mut context.pool(), &local_user_view.local_user).await?; - let mentions = - PersonMentionView::get_unread_mentions(&mut context.pool(), &local_user_view.local_user) + let comment_mentions = + PersonCommentMentionView::get_unread_count(&mut context.pool(), &local_user_view.local_user) + .await?; + + let post_mentions = + PersonPostMentionView::get_unread_count(&mut context.pool(), &local_user_view.local_user) .await?; let private_messages = - PrivateMessageView::get_unread_messages(&mut context.pool(), person_id).await?; + PrivateMessageView::get_unread_count(&mut context.pool(), person_id).await?; Ok(Json(GetUnreadCountResponse { replies, - mentions, + comment_mentions, + post_mentions, private_messages, })) } diff --git a/crates/api/src/post/like.rs b/crates/api/src/post/like.rs index 031e3f0db..acf55b60e 100644 --- a/crates/api/src/post/like.rs +++ b/crates/api/src/post/like.rs @@ -5,9 +5,15 @@ use lemmy_api_common::{ context::LemmyContext, post::{CreatePostLike, PostResponse}, send_activity::{ActivityChannel, SendActivityData}, - utils::{check_bot_account, check_community_user_action, check_local_vote_mode, VoteItem}, + utils::{ + check_bot_account, + check_community_user_action, + check_local_vote_mode, + mark_post_as_read, + }, }; use lemmy_db_schema::{ + newtypes::PostOrCommentId, source::{ local_site::LocalSite, post::{PostLike, PostLikeForm, PostRead, PostReadForm}, @@ -29,7 +35,7 @@ pub async fn like_post( check_local_vote_mode( data.score, - VoteItem::Post(post_id), + PostOrCommentId::Post(post_id), &local_site, local_user_view.person.id, &mut context.pool(), diff --git a/crates/api_common/src/build_response.rs b/crates/api_common/src/build_response.rs index b73c0e482..9683de9cf 100644 --- a/crates/api_common/src/build_response.rs +++ b/crates/api_common/src/build_response.rs @@ -12,15 +12,15 @@ use crate::{ }; use actix_web::web::Json; use lemmy_db_schema::{ - newtypes::{CommentId, CommunityId, LocalUserId, PostId}, + newtypes::{CommentId, CommunityId, LocalUserId, PostId, PostOrCommentId}, source::{ actor_language::CommunityLanguage, comment::Comment, comment_reply::{CommentReply, CommentReplyInsertForm}, community::Community, person::Person, - person_mention::{PersonMention, PersonMentionInsertForm}, - post::Post, + person_comment_mention::{PersonCommentMention, PersonCommentMentionInsertForm}, + person_post_mention::{PersonPostMention, PersonPostMentionInsertForm}, }, traits::Crud, }; @@ -94,7 +94,7 @@ pub async fn build_post_response( #[tracing::instrument(skip_all)] pub async fn send_local_notifs( mentions: Vec, - comment_id: CommentId, + post_or_comment_id: PostOrCommentId, person: &Person, do_send_email: bool, context: &LemmyContext, @@ -125,6 +125,34 @@ pub async fn send_local_notifs( 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 { + PostOrCommentId::Post(post_id) => { + let post_view = PostView::read( + &mut context.pool(), + post_id, + local_user_view.map(|view| &view.local_user), + false, + ) + .await?; + (None, post_view.post, post_view.community) + } + PostOrCommentId::Comment(comment_id) => { + let comment_view = CommentView::read( + &mut context.pool(), + comment_id, + local_user_view.map(|view| &view.local_user), + ) + .await?; + ( + Some(comment_view.comment), + comment_view.post, + comment_view.community, + ) + } + }; // Send the local mentions for mention in mentions @@ -140,22 +168,38 @@ pub async fn send_local_notifs( // below by checking recipient ids recipient_ids.push(mention_user_view.local_user.id); - let user_mention_form = PersonMentionInsertForm { - recipient_id: mention_user_view.person.id, - comment_id, - read: None, - }; + // Make the correct reply form depending on whether its a post or comment mention + let comment_content_or_post_body = if let Some(comment) = &comment_opt { + let person_comment_mention_form = PersonCommentMentionInsertForm { + recipient_id: mention_user_view.person.id, + comment_id: comment.id, + read: None, + }; - // Allow this to fail softly, since comment edits might re-update or replace it - // Let the uniqueness handle this fail - PersonMention::create(&mut context.pool(), &user_mention_form) - .await - .ok(); + // Allow this to fail softly, since comment edits might re-update or replace it + // Let the uniqueness handle this fail + PersonCommentMention::create(&mut context.pool(), &person_comment_mention_form) + .await + .ok(); + comment.content.clone() + } else { + let person_post_mention_form = PersonPostMentionInsertForm { + recipient_id: mention_user_view.person.id, + post_id: post.id, + read: None, + }; + + // Allow this to fail softly, since edits might re-update or replace it + PersonPostMention::create(&mut context.pool(), &person_post_mention_form) + .await + .ok(); + post.body.clone().unwrap_or_default() + }; // Send an email to those local users that have notifications on if do_send_email { let lang = get_interface_language(&mention_user_view); - let content = markdown_to_html(&comment.content); + let content = markdown_to_html(&comment_content_or_post_body); send_email_to_user( &mention_user_view, &lang.notification_mentioned_by_subject(&person.name), @@ -168,99 +212,101 @@ pub async fn send_local_notifs( } // Send comment_reply to the parent commenter / poster - if let Some(parent_comment_id) = comment.parent_comment_id() { - let parent_comment = Comment::read(&mut context.pool(), parent_comment_id).await?; + if let Some(comment) = &comment_opt { + if let Some(parent_comment_id) = comment.parent_comment_id() { + let parent_comment = Comment::read(&mut context.pool(), parent_comment_id).await?; - // Get the parent commenter local_user - let parent_creator_id = parent_comment.creator_id; + // Get the parent commenter local_user + let parent_creator_id = parent_comment.creator_id; - let check_blocks = check_person_instance_community_block( - person.id, - parent_creator_id, - // Only block from the community's instance_id - community.instance_id, - community.id, - &mut context.pool(), - ) - .await - .is_err(); + let check_blocks = check_person_instance_community_block( + person.id, + parent_creator_id, + // Only block from the community's instance_id + community.instance_id, + community.id, + &mut context.pool(), + ) + .await + .is_err(); - // Don't send a notif to yourself - if parent_comment.creator_id != person.id && !check_blocks { - let user_view = LocalUserView::read_person(&mut context.pool(), parent_creator_id).await; - if let Ok(parent_user_view) = user_view { - // Don't duplicate notif if already mentioned by checking recipient ids - if !recipient_ids.contains(&parent_user_view.local_user.id) { - recipient_ids.push(parent_user_view.local_user.id); + // Don't send a notif to yourself + if parent_comment.creator_id != person.id && !check_blocks { + let user_view = LocalUserView::read_person(&mut context.pool(), parent_creator_id).await; + if let Ok(parent_user_view) = user_view { + // Don't duplicate notif if already mentioned by checking recipient ids + if !recipient_ids.contains(&parent_user_view.local_user.id) { + recipient_ids.push(parent_user_view.local_user.id); - let comment_reply_form = CommentReplyInsertForm { - recipient_id: parent_user_view.person.id, - comment_id: comment.id, - read: None, - }; + let comment_reply_form = CommentReplyInsertForm { + recipient_id: parent_user_view.person.id, + comment_id: comment.id, + read: None, + }; - // Allow this to fail softly, since comment edits might re-update or replace it - // Let the uniqueness handle this fail - CommentReply::create(&mut context.pool(), &comment_reply_form) - .await - .ok(); + // Allow this to fail softly, since comment edits might re-update or replace it + // Let the uniqueness handle this fail + CommentReply::create(&mut context.pool(), &comment_reply_form) + .await + .ok(); - if do_send_email { - let lang = get_interface_language(&parent_user_view); - let content = markdown_to_html(&comment.content); - send_email_to_user( - &parent_user_view, - &lang.notification_comment_reply_subject(&person.name), - &lang.notification_comment_reply_body(&content, &inbox_link, &person.name), - context.settings(), - ) - .await + if do_send_email { + let lang = get_interface_language(&parent_user_view); + let content = markdown_to_html(&comment.content); + send_email_to_user( + &parent_user_view, + &lang.notification_comment_reply_subject(&person.name), + &lang.notification_comment_reply_body(&content, &inbox_link, &person.name), + context.settings(), + ) + .await + } } } } - } - } else { - // Use the post creator to check blocks - let check_blocks = check_person_instance_community_block( - person.id, - post.creator_id, - // Only block from the community's instance_id - community.instance_id, - community.id, - &mut context.pool(), - ) - .await - .is_err(); + } else { + // Use the post creator to check blocks + let check_blocks = check_person_instance_community_block( + person.id, + post.creator_id, + // Only block from the community's instance_id + community.instance_id, + community.id, + &mut context.pool(), + ) + .await + .is_err(); - if post.creator_id != person.id && !check_blocks { - let creator_id = post.creator_id; - let parent_user = LocalUserView::read_person(&mut context.pool(), creator_id).await; - if let Ok(parent_user_view) = parent_user { - if !recipient_ids.contains(&parent_user_view.local_user.id) { - recipient_ids.push(parent_user_view.local_user.id); + if post.creator_id != person.id && !check_blocks { + let creator_id = post.creator_id; + let parent_user = LocalUserView::read_person(&mut context.pool(), creator_id).await; + if let Ok(parent_user_view) = parent_user { + if !recipient_ids.contains(&parent_user_view.local_user.id) { + recipient_ids.push(parent_user_view.local_user.id); - let comment_reply_form = CommentReplyInsertForm { - recipient_id: parent_user_view.person.id, - comment_id: comment.id, - read: None, - }; + let comment_reply_form = CommentReplyInsertForm { + recipient_id: parent_user_view.person.id, + comment_id: comment.id, + read: None, + }; - // Allow this to fail softly, since comment edits might re-update or replace it - // Let the uniqueness handle this fail - CommentReply::create(&mut context.pool(), &comment_reply_form) - .await - .ok(); + // Allow this to fail softly, since comment edits might re-update or replace it + // Let the uniqueness handle this fail + CommentReply::create(&mut context.pool(), &comment_reply_form) + .await + .ok(); - if do_send_email { - let lang = get_interface_language(&parent_user_view); - let content = markdown_to_html(&comment.content); - send_email_to_user( - &parent_user_view, - &lang.notification_post_reply_subject(&person.name), - &lang.notification_post_reply_body(&content, &inbox_link, &person.name), - context.settings(), - ) - .await + if do_send_email { + let lang = get_interface_language(&parent_user_view); + let content = markdown_to_html(&comment.content); + send_email_to_user( + &parent_user_view, + &lang.notification_post_reply_subject(&person.name), + &lang.notification_post_reply_body(&content, &inbox_link, &person.name), + context.settings(), + ) + .await + } } } } diff --git a/crates/api_common/src/person.rs b/crates/api_common/src/person.rs index 92a524543..cb2a6f85b 100644 --- a/crates/api_common/src/person.rs +++ b/crates/api_common/src/person.rs @@ -1,5 +1,12 @@ use lemmy_db_schema::{ - newtypes::{CommentReplyId, CommunityId, LanguageId, PersonId, PersonMentionId}, + newtypes::{ + CommentReplyId, + CommunityId, + LanguageId, + PersonCommentMentionId, + PersonId, + PersonPostMentionId, + }, sensitive::SensitiveString, source::{login_token::LoginToken, site::Site}, CommentSortType, @@ -16,7 +23,8 @@ use lemmy_db_views::structs::{ use lemmy_db_views_actor::structs::{ CommentReplyView, CommunityModeratorView, - PersonMentionView, + PersonCommentMentionView, + PersonPostMentionView, PersonView, }; use serde::{Deserialize, Serialize}; @@ -394,8 +402,7 @@ pub struct GetRepliesResponse { #[cfg_attr(feature = "full", derive(TS))] #[cfg_attr(feature = "full", ts(export))] /// Get mentions for your user. -pub struct GetPersonMentions { - #[cfg_attr(feature = "full", ts(optional))] +pub struct GetPersonCommentMentions { pub sort: Option, #[cfg_attr(feature = "full", ts(optional))] pub page: Option, @@ -409,16 +416,16 @@ pub struct GetPersonMentions { #[cfg_attr(feature = "full", derive(TS))] #[cfg_attr(feature = "full", ts(export))] /// The response of mentions for your user. -pub struct GetPersonMentionsResponse { - pub mentions: Vec, +pub struct GetPersonCommentMentionsResponse { + pub comment_mentions: Vec, } #[derive(Debug, Serialize, Deserialize, Clone, Copy, Default, PartialEq, Eq, Hash)] #[cfg_attr(feature = "full", derive(TS))] #[cfg_attr(feature = "full", ts(export))] /// Mark a person mention as read. -pub struct MarkPersonMentionAsRead { - pub person_mention_id: PersonMentionId, +pub struct MarkPersonCommentMentionAsRead { + pub person_comment_mention_id: PersonCommentMentionId, pub read: bool, } @@ -426,8 +433,45 @@ pub struct MarkPersonMentionAsRead { #[cfg_attr(feature = "full", derive(TS))] #[cfg_attr(feature = "full", ts(export))] /// The response for a person mention action. -pub struct PersonMentionResponse { - pub person_mention_view: PersonMentionView, +pub struct PersonCommentMentionResponse { + 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, + pub page: Option, + pub limit: Option, + pub unread_only: Option, +} + +#[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, +} + +#[derive(Debug, Serialize, Deserialize, Clone, Copy, Default, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "full", derive(TS))] +#[cfg_attr(feature = "full", ts(export))] +/// Mark a person mention as read. +pub struct MarkPersonPostMentionAsRead { + pub person_post_mention_id: PersonPostMentionId, + pub read: bool, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +#[cfg_attr(feature = "full", derive(TS))] +#[cfg_attr(feature = "full", ts(export))] +/// The response for a person mention action. +pub struct PersonPostMentionResponse { + pub person_post_mention_view: PersonPostMentionView, } #[derive(Debug, Serialize, Deserialize, Clone, Copy, Default, PartialEq, Eq, Hash)] @@ -499,7 +543,8 @@ pub struct GetReportCountResponse { /// A response containing counts for your notifications. pub struct GetUnreadCountResponse { pub replies: i64, - pub mentions: i64, + pub comment_mentions: i64, + pub post_mentions: i64, pub private_messages: i64, } diff --git a/crates/api_common/src/utils.rs b/crates/api_common/src/utils.rs index a66ee68c1..7b3ed820e 100644 --- a/crates/api_common/src/utils.rs +++ b/crates/api_common/src/utils.rs @@ -11,7 +11,7 @@ use chrono::{DateTime, Days, Local, TimeZone, Utc}; use enum_map::{enum_map, EnumMap}; use lemmy_db_schema::{ aggregates::structs::{PersonPostAggregates, PersonPostAggregatesForm}, - newtypes::{CommentId, CommunityId, DbUrl, InstanceId, PersonId, PostId}, + newtypes::{CommentId, CommunityId, DbUrl, InstanceId, PersonId, PostId, PostOrCommentId}, source::{ comment::{Comment, CommentLike, CommentUpdateForm}, community::{Community, CommunityModerator, CommunityUpdateForm}, @@ -293,23 +293,17 @@ pub async fn check_person_instance_community_block( Ok(()) } -/// A vote item type used to check the vote mode. -pub enum VoteItem { - Post(PostId), - Comment(CommentId), -} - #[tracing::instrument(skip_all)] pub async fn check_local_vote_mode( score: i16, - vote_item: VoteItem, + post_or_comment_id: PostOrCommentId, local_site: &LocalSite, person_id: PersonId, pool: &mut DbPool<'_>, ) -> LemmyResult<()> { - let (downvote_setting, upvote_setting) = match vote_item { - VoteItem::Post(_) => (local_site.post_downvotes, local_site.post_upvotes), - VoteItem::Comment(_) => (local_site.comment_downvotes, local_site.comment_upvotes), + let (downvote_setting, upvote_setting) = match post_or_comment_id { + PostOrCommentId::Post(_) => (local_site.post_downvotes, local_site.post_upvotes), + PostOrCommentId::Comment(_) => (local_site.comment_downvotes, local_site.comment_upvotes), }; let downvote_fail = score == -1 && downvote_setting == FederationMode::Disable; @@ -317,9 +311,11 @@ pub async fn check_local_vote_mode( // Undo previous vote for item if new vote fails if downvote_fail || upvote_fail { - match vote_item { - VoteItem::Post(post_id) => PostLike::remove(pool, person_id, post_id).await?, - VoteItem::Comment(comment_id) => CommentLike::remove(pool, person_id, comment_id).await?, + match post_or_comment_id { + PostOrCommentId::Post(post_id) => PostLike::remove(pool, person_id, post_id).await?, + PostOrCommentId::Comment(comment_id) => { + CommentLike::remove(pool, person_id, comment_id).await? + } }; } Ok(()) diff --git a/crates/api_crud/src/comment/create.rs b/crates/api_crud/src/comment/create.rs index edcf7db30..ed3f7402c 100644 --- a/crates/api_crud/src/comment/create.rs +++ b/crates/api_crud/src/comment/create.rs @@ -16,12 +16,13 @@ use lemmy_api_common::{ }, }; use lemmy_db_schema::{ - impls::actor_language::validate_post_language, + impls::actor_language::default_post_language, + newtypes::PostOrCommentId, source::{ comment::{Comment, CommentInsertForm, CommentLike, CommentLikeForm}, comment_reply::{CommentReply, CommentReplyUpdateForm}, local_site::LocalSite, - person_mention::{PersonMention, PersonMentionUpdateForm}, + person_comment_mention::{PersonCommentMention, PersonCommentMentionUpdateForm}, }, traits::{Crud, Likeable}, }; @@ -117,7 +118,7 @@ pub async fn create_comment( let mentions = scrape_text_for_mentions(&content); let recipient_ids = send_local_notifs( mentions, - inserted_comment_id, + PostOrCommentId::Comment(inserted_comment_id), &local_user_view.person, true, &context, @@ -169,17 +170,18 @@ pub async fn create_comment( .with_lemmy_type(LemmyErrorType::CouldntUpdateReplies)?; } - // If the parent has PersonMentions mark them as read too - let person_mention = - PersonMention::read_by_comment_and_person(&mut context.pool(), parent_id, person_id).await; - if let Ok(Some(mention)) = person_mention { - PersonMention::update( + // If the parent has PersonCommentMentions mark them as read too + let person_comment_mention = + PersonCommentMention::read_by_comment_and_person(&mut context.pool(), parent_id, person_id) + .await; + if let Ok(Some(mention)) = person_comment_mention { + PersonCommentMention::update( &mut context.pool(), mention.id, - &PersonMentionUpdateForm { read: Some(true) }, + &PersonCommentMentionUpdateForm { read: Some(true) }, ) .await - .with_lemmy_type(LemmyErrorType::CouldntUpdatePersonMentions)?; + .with_lemmy_type(LemmyErrorType::CouldntUpdatePersonCommentMentions)?; } } diff --git a/crates/api_crud/src/comment/delete.rs b/crates/api_crud/src/comment/delete.rs index 60a22a2ef..cf90df6b6 100644 --- a/crates/api_crud/src/comment/delete.rs +++ b/crates/api_crud/src/comment/delete.rs @@ -8,6 +8,7 @@ use lemmy_api_common::{ utils::check_community_user_action, }; use lemmy_db_schema::{ + newtypes::PostOrCommentId, source::comment::{Comment, CommentUpdateForm}, traits::Crud, }; @@ -60,7 +61,7 @@ pub async fn delete_comment( let recipient_ids = send_local_notifs( vec![], - comment_id, + PostOrCommentId::Comment(comment_id), &local_user_view.person, false, &context, diff --git a/crates/api_crud/src/comment/remove.rs b/crates/api_crud/src/comment/remove.rs index 1ac6201e8..4436f8c87 100644 --- a/crates/api_crud/src/comment/remove.rs +++ b/crates/api_crud/src/comment/remove.rs @@ -8,6 +8,7 @@ use lemmy_api_common::{ utils::check_community_mod_action, }; use lemmy_db_schema::{ + newtypes::PostOrCommentId, source::{ comment::{Comment, CommentUpdateForm}, comment_report::CommentReport, @@ -82,7 +83,7 @@ pub async fn remove_comment( let recipient_ids = send_local_notifs( vec![], - comment_id, + PostOrCommentId::Comment(comment_id), &local_user_view.person, false, &context, diff --git a/crates/api_crud/src/comment/update.rs b/crates/api_crud/src/comment/update.rs index 1af026204..8a3cf35b0 100644 --- a/crates/api_crud/src/comment/update.rs +++ b/crates/api_crud/src/comment/update.rs @@ -14,7 +14,7 @@ use lemmy_api_common::{ }, }; use lemmy_db_schema::{ - impls::actor_language::validate_post_language, + newtypes::PostOrCommentId, source::{ comment::{Comment, CommentUpdateForm}, local_site::LocalSite, @@ -86,7 +86,7 @@ pub async fn update_comment( let mentions = scrape_text_for_mentions(&updated_comment_content); let recipient_ids = send_local_notifs( mentions, - comment_id, + PostOrCommentId::Comment(comment_id), &local_user_view.person, false, &context, diff --git a/crates/api_crud/src/post/create.rs b/crates/api_crud/src/post/create.rs index 948a7617e..75c1021c4 100644 --- a/crates/api_crud/src/post/create.rs +++ b/crates/api_crud/src/post/create.rs @@ -2,7 +2,7 @@ use super::convert_published_time; use activitypub_federation::config::Data; use actix_web::web::Json; use lemmy_api_common::{ - build_response::build_post_response, + build_response::{build_post_response, send_local_notifs}, context::LemmyContext, post::{CreatePost, PostResponse}, request::generate_post_link_metadata, @@ -16,7 +16,8 @@ use lemmy_api_common::{ }, }; use lemmy_db_schema::{ - impls::actor_language::validate_post_language, + impls::actor_language::default_post_language, + newtypes::PostOrCommentId, source::{ community::Community, local_site::LocalSite, @@ -32,6 +33,7 @@ use lemmy_utils::{ error::{LemmyErrorExt, LemmyErrorType, LemmyResult}, spawn_try_task, utils::{ + mention::scrape_text_for_mentions, slurs::check_slurs, validation::{ is_url_blocked, @@ -148,8 +150,21 @@ pub async fn create_post( .await .with_lemmy_type(LemmyErrorType::CouldntLikePost)?; - let read_form = PostReadForm::new(post_id, person_id); - PostRead::mark_as_read(&mut context.pool(), &read_form).await?; + // Scan the post body for user mentions, add those rows + let mentions = scrape_text_for_mentions(&inserted_post.body.clone().unwrap_or_default()); + send_local_notifs( + mentions, + PostOrCommentId::Post(inserted_post.id), + &local_user_view.person, + true, + &context, + Some(&local_user_view), + ) + .await?; + + // TODO +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 } diff --git a/crates/api_crud/src/post/update.rs b/crates/api_crud/src/post/update.rs index 24bbed009..a9585be08 100644 --- a/crates/api_crud/src/post/update.rs +++ b/crates/api_crud/src/post/update.rs @@ -3,7 +3,7 @@ use activitypub_federation::config::Data; use actix_web::web::Json; use chrono::Utc; use lemmy_api_common::{ - build_response::build_post_response, + build_response::{build_post_response, send_local_notifs}, context::LemmyContext, post::{EditPost, PostResponse}, request::generate_post_link_metadata, @@ -16,6 +16,7 @@ use lemmy_api_common::{ }, }; use lemmy_db_schema::{ + newtypes::PostOrCommentId, impls::actor_language::validate_post_language, source::{ community::Community, @@ -29,6 +30,7 @@ use lemmy_db_views::structs::{LocalUserView, PostView}; use lemmy_utils::{ error::{LemmyErrorExt, LemmyErrorType, LemmyResult}, utils::{ + mention::scrape_text_for_mentions, slurs::check_slurs, validation::{ is_url_blocked, @@ -142,6 +144,18 @@ pub async fn update_post( .await .with_lemmy_type(LemmyErrorType::CouldntUpdatePost)?; + // Scan the post body for user mentions, add those rows + let mentions = scrape_text_for_mentions(&updated_post.body.clone().unwrap_or_default()); + send_local_notifs( + mentions, + PostOrCommentId::Post(updated_post.id), + &local_user_view.person, + false, + &context, + Some(&local_user_view), + ) + .await?; + // send out federation/webmention if necessary match ( orig_post.post.scheduled_publish_time, diff --git a/crates/apub/src/activities/create_or_update/comment.rs b/crates/apub/src/activities/create_or_update/comment.rs index 72dae48b7..ba617e2dc 100644 --- a/crates/apub/src/activities/create_or_update/comment.rs +++ b/crates/apub/src/activities/create_or_update/comment.rs @@ -29,7 +29,7 @@ use lemmy_api_common::{ }; use lemmy_db_schema::{ aggregates::structs::CommentAggregates, - newtypes::PersonId, + newtypes::{PersonId, PostOrCommentId}, source::{ activity::ActivitySendTargets, comment::{Comment, CommentLike, CommentLikeForm}, @@ -176,10 +176,17 @@ impl ActivityHandler for CreateOrUpdateNote { // TODO: for compatibility with other projects, it would be much better to read this from cc or // tags let mentions = scrape_text_for_mentions(&comment.content); - // TODO: this fails in local community comment as CommentView::read() returns nothing // without passing LocalUser - send_local_notifs(mentions, comment.id, &actor, do_send_email, context, None).await?; + send_local_notifs( + mentions, + PostOrCommentId::Comment(comment.id), + &actor, + do_send_email, + context, + None, + ) + .await?; Ok(()) } } diff --git a/crates/apub/src/activities/create_or_update/post.rs b/crates/apub/src/activities/create_or_update/post.rs index 832b2da6d..f661d39b7 100644 --- a/crates/apub/src/activities/create_or_update/post.rs +++ b/crates/apub/src/activities/create_or_update/post.rs @@ -20,10 +20,10 @@ use activitypub_federation::{ protocol::verification::{verify_domains_match, verify_urls_match}, traits::{ActivityHandler, Actor, Object}, }; -use lemmy_api_common::context::LemmyContext; +use lemmy_api_common::{build_response::send_local_notifs, context::LemmyContext}; use lemmy_db_schema::{ aggregates::structs::PostAggregates, - newtypes::PersonId, + newtypes::{PersonId, PostOrCommentId}, source::{ activity::ActivitySendTargets, community::Community, @@ -32,7 +32,10 @@ use lemmy_db_schema::{ }, traits::{Crud, Likeable}, }; -use lemmy_utils::error::{LemmyError, LemmyResult}; +use lemmy_utils::{ + error::{LemmyError, LemmyResult}, + utils::mention::scrape_text_for_mentions, +}; use url::Url; impl CreateOrUpdatePage { @@ -124,6 +127,21 @@ impl ActivityHandler for CreateOrUpdatePage { // Calculate initial hot_rank for post PostAggregates::update_ranks(&mut context.pool(), post.id).await?; + let do_send_email = self.kind == CreateOrUpdateType::Create; + let actor = self.actor.dereference(context).await?; + + // Send the post body mentions + let mentions = scrape_text_for_mentions(&post.body.clone().unwrap_or_default()); + send_local_notifs( + mentions, + PostOrCommentId::Post(post.id), + &actor, + do_send_email, + context, + None, + ) + .await?; + Ok(()) } } diff --git a/crates/db_schema/src/impls/mod.rs b/crates/db_schema/src/impls/mod.rs index d4ea47800..80b579c03 100644 --- a/crates/db_schema/src/impls/mod.rs +++ b/crates/db_schema/src/impls/mod.rs @@ -27,7 +27,8 @@ pub mod oauth_provider; pub mod password_reset_request; pub mod person; pub mod person_block; -pub mod person_mention; +pub mod person_comment_mention; +pub mod person_post_mention; pub mod post; pub mod post_report; pub mod private_message; diff --git a/crates/db_schema/src/impls/person_comment_mention.rs b/crates/db_schema/src/impls/person_comment_mention.rs new file mode 100644 index 000000000..2cc303396 --- /dev/null +++ b/crates/db_schema/src/impls/person_comment_mention.rs @@ -0,0 +1,83 @@ +use crate::{ + diesel::OptionalExtension, + newtypes::{CommentId, PersonCommentMentionId, PersonId}, + schema::person_comment_mention, + source::person_comment_mention::{ + PersonCommentMention, + PersonCommentMentionInsertForm, + PersonCommentMentionUpdateForm, + }, + traits::Crud, + utils::{get_conn, DbPool}, +}; +use diesel::{dsl::insert_into, result::Error, ExpressionMethods, QueryDsl}; +use diesel_async::RunQueryDsl; + +#[async_trait] +impl Crud for PersonCommentMention { + type InsertForm = PersonCommentMentionInsertForm; + type UpdateForm = PersonCommentMentionUpdateForm; + type IdType = PersonCommentMentionId; + + async fn create( + pool: &mut DbPool<'_>, + person_comment_mention_form: &Self::InsertForm, + ) -> Result { + let conn = &mut get_conn(pool).await?; + // since the return here isnt utilized, we dont need to do an update + // but get_result doesn't return the existing row here + insert_into(person_comment_mention::table) + .values(person_comment_mention_form) + .on_conflict(( + person_comment_mention::recipient_id, + person_comment_mention::comment_id, + )) + .do_update() + .set(person_comment_mention_form) + .get_result::(conn) + .await + } + + async fn update( + pool: &mut DbPool<'_>, + person_comment_mention_id: PersonCommentMentionId, + person_comment_mention_form: &Self::UpdateForm, + ) -> Result { + let conn = &mut get_conn(pool).await?; + diesel::update(person_comment_mention::table.find(person_comment_mention_id)) + .set(person_comment_mention_form) + .get_result::(conn) + .await + } +} + +impl PersonCommentMention { + pub async fn mark_all_as_read( + pool: &mut DbPool<'_>, + for_recipient_id: PersonId, + ) -> Result, Error> { + let conn = &mut get_conn(pool).await?; + diesel::update( + person_comment_mention::table + .filter(person_comment_mention::recipient_id.eq(for_recipient_id)) + .filter(person_comment_mention::read.eq(false)), + ) + .set(person_comment_mention::read.eq(true)) + .get_results::(conn) + .await + } + + pub async fn read_by_comment_and_person( + pool: &mut DbPool<'_>, + for_comment_id: CommentId, + for_recipient_id: PersonId, + ) -> Result, Error> { + let conn = &mut get_conn(pool).await?; + person_comment_mention::table + .filter(person_comment_mention::comment_id.eq(for_comment_id)) + .filter(person_comment_mention::recipient_id.eq(for_recipient_id)) + .first(conn) + .await + .optional() + } +} diff --git a/crates/db_schema/src/impls/person_mention.rs b/crates/db_schema/src/impls/person_mention.rs deleted file mode 100644 index 433176683..000000000 --- a/crates/db_schema/src/impls/person_mention.rs +++ /dev/null @@ -1,76 +0,0 @@ -use crate::{ - diesel::OptionalExtension, - newtypes::{CommentId, PersonId, PersonMentionId}, - schema::person_mention, - source::person_mention::{PersonMention, PersonMentionInsertForm, PersonMentionUpdateForm}, - traits::Crud, - utils::{get_conn, DbPool}, -}; -use diesel::{dsl::insert_into, result::Error, ExpressionMethods, QueryDsl}; -use diesel_async::RunQueryDsl; - -#[async_trait] -impl Crud for PersonMention { - type InsertForm = PersonMentionInsertForm; - type UpdateForm = PersonMentionUpdateForm; - type IdType = PersonMentionId; - - async fn create( - pool: &mut DbPool<'_>, - person_mention_form: &Self::InsertForm, - ) -> Result { - let conn = &mut get_conn(pool).await?; - // since the return here isnt utilized, we dont need to do an update - // but get_result doesn't return the existing row here - insert_into(person_mention::table) - .values(person_mention_form) - .on_conflict((person_mention::recipient_id, person_mention::comment_id)) - .do_update() - .set(person_mention_form) - .get_result::(conn) - .await - } - - async fn update( - pool: &mut DbPool<'_>, - person_mention_id: PersonMentionId, - person_mention_form: &Self::UpdateForm, - ) -> Result { - let conn = &mut get_conn(pool).await?; - diesel::update(person_mention::table.find(person_mention_id)) - .set(person_mention_form) - .get_result::(conn) - .await - } -} - -impl PersonMention { - pub async fn mark_all_as_read( - pool: &mut DbPool<'_>, - for_recipient_id: PersonId, - ) -> Result, Error> { - let conn = &mut get_conn(pool).await?; - diesel::update( - person_mention::table - .filter(person_mention::recipient_id.eq(for_recipient_id)) - .filter(person_mention::read.eq(false)), - ) - .set(person_mention::read.eq(true)) - .get_results::(conn) - .await - } - - pub async fn read_by_comment_and_person( - pool: &mut DbPool<'_>, - for_comment_id: CommentId, - for_recipient_id: PersonId, - ) -> Result, Error> { - let conn = &mut get_conn(pool).await?; - person_mention::table - .filter(person_mention::comment_id.eq(for_comment_id)) - .filter(person_mention::recipient_id.eq(for_recipient_id)) - .first(conn) - .await - .optional() - } -} diff --git a/crates/db_schema/src/impls/person_post_mention.rs b/crates/db_schema/src/impls/person_post_mention.rs new file mode 100644 index 000000000..ef59b60e1 --- /dev/null +++ b/crates/db_schema/src/impls/person_post_mention.rs @@ -0,0 +1,83 @@ +use crate::{ + diesel::OptionalExtension, + newtypes::{PersonId, PersonPostMentionId, PostId}, + schema::person_post_mention, + source::person_post_mention::{ + PersonPostMention, + PersonPostMentionInsertForm, + PersonPostMentionUpdateForm, + }, + traits::Crud, + utils::{get_conn, DbPool}, +}; +use diesel::{dsl::insert_into, result::Error, ExpressionMethods, QueryDsl}; +use diesel_async::RunQueryDsl; + +#[async_trait] +impl Crud for PersonPostMention { + type InsertForm = PersonPostMentionInsertForm; + type UpdateForm = PersonPostMentionUpdateForm; + type IdType = PersonPostMentionId; + + async fn create( + pool: &mut DbPool<'_>, + person_post_mention_form: &Self::InsertForm, + ) -> Result { + let conn = &mut get_conn(pool).await?; + // since the return here isnt utilized, we dont need to do an update + // but get_result doesn't return the existing row here + insert_into(person_post_mention::table) + .values(person_post_mention_form) + .on_conflict(( + person_post_mention::recipient_id, + person_post_mention::post_id, + )) + .do_update() + .set(person_post_mention_form) + .get_result::(conn) + .await + } + + async fn update( + pool: &mut DbPool<'_>, + person_post_mention_id: PersonPostMentionId, + person_post_mention_form: &Self::UpdateForm, + ) -> Result { + let conn = &mut get_conn(pool).await?; + diesel::update(person_post_mention::table.find(person_post_mention_id)) + .set(person_post_mention_form) + .get_result::(conn) + .await + } +} + +impl PersonPostMention { + pub async fn mark_all_as_read( + pool: &mut DbPool<'_>, + for_recipient_id: PersonId, + ) -> Result, Error> { + let conn = &mut get_conn(pool).await?; + diesel::update( + person_post_mention::table + .filter(person_post_mention::recipient_id.eq(for_recipient_id)) + .filter(person_post_mention::read.eq(false)), + ) + .set(person_post_mention::read.eq(true)) + .get_results::(conn) + .await + } + + pub async fn read_by_post_and_person( + pool: &mut DbPool<'_>, + for_post_id: PostId, + for_recipient_id: PersonId, + ) -> Result, Error> { + let conn = &mut get_conn(pool).await?; + person_post_mention::table + .filter(person_post_mention::post_id.eq(for_post_id)) + .filter(person_post_mention::recipient_id.eq(for_recipient_id)) + .first(conn) + .await + .optional() + } +} diff --git a/crates/db_schema/src/newtypes.rs b/crates/db_schema/src/newtypes.rs index 228de75be..8184cda5a 100644 --- a/crates/db_schema/src/newtypes.rs +++ b/crates/db_schema/src/newtypes.rs @@ -55,6 +55,11 @@ impl fmt::Display for CommentId { } } +pub enum PostOrCommentId { + Post(PostId), + Comment(CommentId), +} + #[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Default, Serialize, Deserialize)] #[cfg_attr(feature = "full", derive(DieselNewType, TS))] #[cfg_attr(feature = "full", ts(export))] @@ -82,8 +87,14 @@ impl fmt::Display for PrivateMessageId { #[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)] #[cfg_attr(feature = "full", derive(DieselNewType, TS))] #[cfg_attr(feature = "full", ts(export))] -/// The person mention id. -pub struct PersonMentionId(i32); +/// The person comment mention id. +pub struct PersonCommentMentionId(i32); + +#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)] +#[cfg_attr(feature = "full", derive(DieselNewType, TS))] +#[cfg_attr(feature = "full", ts(export))] +/// The person post mention id. +pub struct PersonPostMentionId(i32); #[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)] #[cfg_attr(feature = "full", derive(DieselNewType, TS))] diff --git a/crates/db_schema/src/schema.rs b/crates/db_schema/src/schema.rs index 431e249df..15f0cd0f9 100644 --- a/crates/db_schema/src/schema.rs +++ b/crates/db_schema/src/schema.rs @@ -753,6 +753,16 @@ diesel::table! { } } +diesel::table! { + person_comment_mention (id) { + id -> Int4, + recipient_id -> Int4, + comment_id -> Int4, + read -> Bool, + published -> Timestamptz, + } +} + diesel::table! { person_content_combined (id) { id -> Int4, @@ -763,10 +773,10 @@ diesel::table! { } diesel::table! { - person_mention (id) { + person_post_mention (id) { id -> Int4, recipient_id -> Int4, - comment_id -> Int4, + post_id -> Int4, read -> Bool, published -> Timestamptz, } @@ -1090,10 +1100,12 @@ diesel::joinable!(password_reset_request -> local_user (local_user_id)); diesel::joinable!(person -> instance (instance_id)); diesel::joinable!(person_aggregates -> person (person_id)); diesel::joinable!(person_ban -> person (person_id)); +diesel::joinable!(person_comment_mention -> comment (comment_id)); +diesel::joinable!(person_comment_mention -> person (recipient_id)); diesel::joinable!(person_content_combined -> comment (comment_id)); diesel::joinable!(person_content_combined -> post (post_id)); -diesel::joinable!(person_mention -> comment (comment_id)); -diesel::joinable!(person_mention -> person (recipient_id)); +diesel::joinable!(person_post_mention -> person (recipient_id)); +diesel::joinable!(person_post_mention -> post (post_id)); diesel::joinable!(person_saved_combined -> comment (comment_id)); diesel::joinable!(person_saved_combined -> person (person_id)); diesel::joinable!(person_saved_combined -> post (post_id)); @@ -1172,8 +1184,9 @@ diesel::allow_tables_to_appear_in_same_query!( person_actions, person_aggregates, person_ban, + person_comment_mention, person_content_combined, - person_mention, + person_post_mention, person_saved_combined, post, post_actions, diff --git a/crates/db_schema/src/source/mod.rs b/crates/db_schema/src/source/mod.rs index ea5f2c9d3..4ad78c7dc 100644 --- a/crates/db_schema/src/source/mod.rs +++ b/crates/db_schema/src/source/mod.rs @@ -33,7 +33,8 @@ pub mod oauth_provider; pub mod password_reset_request; pub mod person; pub mod person_block; -pub mod person_mention; +pub mod person_comment_mention; +pub mod person_post_mention; pub mod post; pub mod post_report; pub mod private_message; diff --git a/crates/db_schema/src/source/person_mention.rs b/crates/db_schema/src/source/person_comment_mention.rs similarity index 63% rename from crates/db_schema/src/source/person_mention.rs rename to crates/db_schema/src/source/person_comment_mention.rs index 9c3005655..bd70af307 100644 --- a/crates/db_schema/src/source/person_mention.rs +++ b/crates/db_schema/src/source/person_comment_mention.rs @@ -1,6 +1,6 @@ -use crate::newtypes::{CommentId, PersonId, PersonMentionId}; +use crate::newtypes::{CommentId, PersonCommentMentionId, PersonId}; #[cfg(feature = "full")] -use crate::schema::person_mention; +use crate::schema::person_comment_mention; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; #[cfg(feature = "full")] @@ -12,12 +12,12 @@ use ts_rs::TS; derive(Queryable, Selectable, Associations, Identifiable, TS) )] #[cfg_attr(feature = "full", diesel(belongs_to(crate::source::comment::Comment)))] -#[cfg_attr(feature = "full", diesel(table_name = person_mention))] +#[cfg_attr(feature = "full", diesel(table_name = person_comment_mention))] #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] #[cfg_attr(feature = "full", ts(export))] /// A person mention. -pub struct PersonMention { - pub id: PersonMentionId, +pub struct PersonCommentMention { + pub id: PersonCommentMentionId, pub recipient_id: PersonId, pub comment_id: CommentId, pub read: bool, @@ -25,15 +25,15 @@ pub struct PersonMention { } #[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] -#[cfg_attr(feature = "full", diesel(table_name = person_mention))] -pub struct PersonMentionInsertForm { +#[cfg_attr(feature = "full", diesel(table_name = person_comment_mention))] +pub struct PersonCommentMentionInsertForm { pub recipient_id: PersonId, pub comment_id: CommentId, pub read: Option, } #[cfg_attr(feature = "full", derive(AsChangeset))] -#[cfg_attr(feature = "full", diesel(table_name = person_mention))] -pub struct PersonMentionUpdateForm { +#[cfg_attr(feature = "full", diesel(table_name = person_comment_mention))] +pub struct PersonCommentMentionUpdateForm { pub read: Option, } diff --git a/crates/db_schema/src/source/person_post_mention.rs b/crates/db_schema/src/source/person_post_mention.rs new file mode 100644 index 000000000..b1c00febf --- /dev/null +++ b/crates/db_schema/src/source/person_post_mention.rs @@ -0,0 +1,39 @@ +use crate::newtypes::{PersonId, PersonPostMentionId, PostId}; +#[cfg(feature = "full")] +use crate::schema::person_post_mention; +use chrono::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; +#[cfg(feature = "full")] +use ts_rs::TS; + +#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)] +#[cfg_attr( + feature = "full", + derive(Queryable, Selectable, Associations, Identifiable, TS) +)] +#[cfg_attr(feature = "full", diesel(belongs_to(crate::source::post::Post)))] +#[cfg_attr(feature = "full", diesel(table_name = person_post_mention))] +#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] +#[cfg_attr(feature = "full", ts(export))] +/// A person mention. +pub struct PersonPostMention { + pub id: PersonPostMentionId, + pub recipient_id: PersonId, + pub post_id: PostId, + pub read: bool, + pub published: DateTime, +} + +#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] +#[cfg_attr(feature = "full", diesel(table_name = person_post_mention))] +pub struct PersonPostMentionInsertForm { + pub recipient_id: PersonId, + pub post_id: PostId, + pub read: Option, +} + +#[cfg_attr(feature = "full", derive(AsChangeset))] +#[cfg_attr(feature = "full", diesel(table_name = person_post_mention))] +pub struct PersonPostMentionUpdateForm { + pub read: Option, +} diff --git a/crates/db_views/src/private_message_view.rs b/crates/db_views/src/private_message_view.rs index 2286b7dc6..81951575c 100644 --- a/crates/db_views/src/private_message_view.rs +++ b/crates/db_views/src/private_message_view.rs @@ -114,7 +114,7 @@ impl PrivateMessageView { } /// Gets the number of unread messages - pub async fn get_unread_messages( + pub async fn get_unread_count( pool: &mut DbPool<'_>, my_person_id: PersonId, ) -> Result { @@ -348,7 +348,7 @@ mod tests { assert_length!(1, &timmy_messages); - let timmy_unread_messages = PrivateMessageView::get_unread_messages(pool, timmy.id).await?; + let timmy_unread_messages = PrivateMessageView::get_unread_count(pool, timmy.id).await?; assert_eq!(timmy_unread_messages, 1); cleanup(instance.id, pool).await @@ -390,7 +390,7 @@ mod tests { assert_length!(0, &timmy_messages); - let timmy_unread_messages = PrivateMessageView::get_unread_messages(pool, timmy.id).await?; + let timmy_unread_messages = PrivateMessageView::get_unread_count(pool, timmy.id).await?; assert_eq!(timmy_unread_messages, 0); cleanup(instance.id, pool).await } diff --git a/crates/db_views_actor/src/comment_reply_view.rs b/crates/db_views_actor/src/comment_reply_view.rs index 6c5442e6a..97d719370 100644 --- a/crates/db_views_actor/src/comment_reply_view.rs +++ b/crates/db_views_actor/src/comment_reply_view.rs @@ -165,7 +165,7 @@ impl CommentReplyView { } /// Gets the number of unread replies - pub async fn get_unread_replies( + pub async fn get_unread_count( pool: &mut DbPool<'_>, local_user: &LocalUser, ) -> Result { @@ -305,7 +305,7 @@ mod tests { CommentReply::update(pool, inserted_reply.id, &comment_reply_update_form).await?; // Test to make sure counts and blocks work correctly - let unread_replies = CommentReplyView::get_unread_replies(pool, &recipient_local_user).await?; + let unread_replies = CommentReplyView::get_unread_count(pool, &recipient_local_user).await?; let query = CommentReplyQuery { recipient_id: Some(recipient_id), @@ -328,7 +328,7 @@ mod tests { PersonBlock::block(pool, &block_form).await?; let unread_replies_after_block = - CommentReplyView::get_unread_replies(pool, &recipient_local_user).await?; + CommentReplyView::get_unread_count(pool, &recipient_local_user).await?; let replies_after_block = query.clone().list(pool).await?; assert_eq!(0, unread_replies_after_block); assert_eq!(0, replies_after_block.len()); @@ -356,7 +356,7 @@ mod tests { let recipient_local_user_view = LocalUserView::read(pool, recipient_local_user.id).await?; let unread_replies_after_hide_bots = - CommentReplyView::get_unread_replies(pool, &recipient_local_user_view.local_user).await?; + CommentReplyView::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; diff --git a/crates/db_views_actor/src/lib.rs b/crates/db_views_actor/src/lib.rs index 2ec9652e3..04c56a8c4 100644 --- a/crates/db_views_actor/src/lib.rs +++ b/crates/db_views_actor/src/lib.rs @@ -9,7 +9,7 @@ pub mod community_person_ban_view; #[cfg(feature = "full")] pub mod community_view; #[cfg(feature = "full")] -pub mod person_mention_view; +pub mod person_comment_mention_view; #[cfg(feature = "full")] pub mod person_view; pub mod structs; diff --git a/crates/db_views_actor/src/person_mention_view.rs b/crates/db_views_actor/src/person_comment_mention_view.rs similarity index 76% rename from crates/db_views_actor/src/person_mention_view.rs rename to crates/db_views_actor/src/person_comment_mention_view.rs index 08be67a82..d447fea0a 100644 --- a/crates/db_views_actor/src/person_mention_view.rs +++ b/crates/db_views_actor/src/person_comment_mention_view.rs @@ -1,4 +1,4 @@ -use crate::structs::PersonMentionView; +use crate::structs::PersonCommentMentionView; use diesel::{ dsl::{exists, not}, pg::Pg, @@ -12,7 +12,7 @@ use diesel::{ use diesel_async::RunQueryDsl; use lemmy_db_schema::{ aliases::{self, creator_community_actions}, - newtypes::{PersonId, PersonMentionId}, + newtypes::{PersonCommentMentionId, PersonId}, schema::{ comment, comment_actions, @@ -22,7 +22,7 @@ use lemmy_db_schema::{ local_user, person, person_actions, - person_mention, + person_comment_mention, post, }, source::{community::CommunityFollower, local_user::LocalUser}, @@ -41,8 +41,8 @@ use lemmy_db_schema::{ }; fn queries<'a>() -> Queries< - impl ReadFn<'a, PersonMentionView, (PersonMentionId, Option)>, - impl ListFn<'a, PersonMentionView, PersonMentionQuery>, + impl ReadFn<'a, PersonCommentMentionView, (PersonCommentMentionId, Option)>, + impl ListFn<'a, PersonCommentMentionView, PersonCommentMentionQuery>, > { let creator_is_admin = exists( local_user::table.filter( @@ -52,7 +52,7 @@ fn queries<'a>() -> Queries< ), ); - let all_joins = move |query: person_mention::BoxedQuery<'a, Pg>, + let all_joins = move |query: person_comment_mention::BoxedQuery<'a, Pg>, my_person_id: Option| { query .inner_join(comment::table) @@ -78,7 +78,7 @@ fn queries<'a>() -> Queries< post::community_id, )) .select(( - person_mention::all_columns, + person_comment_mention::all_columns, comment::all_columns, person::all_columns, post::all_columns, @@ -102,35 +102,42 @@ fn queries<'a>() -> Queries< )) }; - let read = - move |mut conn: DbConn<'a>, - (person_mention_id, my_person_id): (PersonMentionId, Option)| async move { - all_joins( - person_mention::table.find(person_mention_id).into_boxed(), - my_person_id, - ) - .first(&mut conn) - .await - }; + let read = move |mut conn: DbConn<'a>, + (person_comment_mention_id, my_person_id): ( + PersonCommentMentionId, + Option, + )| 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: PersonMentionQuery| async move { + let list = move |mut conn: DbConn<'a>, options: PersonCommentMentionQuery| async move { // These filters need to be kept in sync with the filters in - // PersonMentionView::get_unread_mentions() - let mut query = all_joins(person_mention::table.into_boxed(), options.my_person_id); + // 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_mention::recipient_id.eq(recipient_id)); + query = query.filter(person_comment_mention::recipient_id.eq(recipient_id)); } if options.unread_only { - query = query.filter(person_mention::read.eq(false)); + 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::Hot) { + 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()) @@ -148,33 +155,33 @@ fn queries<'a>() -> Queries< query .limit(limit) .offset(offset) - .load::(&mut conn) + .load::(&mut conn) .await }; Queries::new(read, list) } -impl PersonMentionView { +impl PersonCommentMentionView { pub async fn read( pool: &mut DbPool<'_>, - person_mention_id: PersonMentionId, + person_comment_mention_id: PersonCommentMentionId, my_person_id: Option, ) -> Result { queries() - .read(pool, (person_mention_id, my_person_id)) + .read(pool, (person_comment_mention_id, my_person_id)) .await } /// Gets the number of unread mentions - pub async fn get_unread_mentions( + pub async fn get_unread_count( pool: &mut DbPool<'_>, local_user: &LocalUser, ) -> Result { use diesel::dsl::count; let conn = &mut get_conn(pool).await?; - let mut query = person_mention::table + let mut query = person_comment_mention::table .inner_join(comment::table) .left_join(actions( person_actions::table, @@ -192,18 +199,18 @@ impl PersonMentionView { query // Don't count replies from blocked users .filter(person_actions::blocked.is_null()) - .filter(person_mention::recipient_id.eq(local_user.person_id)) - .filter(person_mention::read.eq(false)) + .filter(person_comment_mention::recipient_id.eq(local_user.person_id)) + .filter(person_comment_mention::read.eq(false)) .filter(comment::deleted.eq(false)) .filter(comment::removed.eq(false)) - .select(count(person_mention::id)) + .select(count(person_comment_mention::id)) .first::(conn) .await } } #[derive(Default, Clone)] -pub struct PersonMentionQuery { +pub struct PersonCommentMentionQuery { pub my_person_id: Option, pub recipient_id: Option, pub sort: Option, @@ -213,8 +220,8 @@ pub struct PersonMentionQuery { pub limit: Option, } -impl PersonMentionQuery { - pub async fn list(self, pool: &mut DbPool<'_>) -> Result, Error> { +impl PersonCommentMentionQuery { + pub async fn list(self, pool: &mut DbPool<'_>) -> Result, Error> { queries().list(pool, self).await } } @@ -222,7 +229,10 @@ impl PersonMentionQuery { #[cfg(test)] mod tests { - use crate::{person_mention_view::PersonMentionQuery, structs::PersonMentionView}; + use crate::{ + person_comment_mention_view::PersonCommentMentionQuery, + structs::PersonCommentMentionView, + }; use lemmy_db_schema::{ source::{ comment::{Comment, CommentInsertForm}, @@ -231,7 +241,11 @@ mod tests { local_user::{LocalUser, LocalUserInsertForm, LocalUserUpdateForm}, person::{Person, PersonInsertForm, PersonUpdateForm}, person_block::{PersonBlock, PersonBlockForm}, - person_mention::{PersonMention, PersonMentionInsertForm, PersonMentionUpdateForm}, + person_comment_mention::{ + PersonCommentMention, + PersonCommentMentionInsertForm, + PersonCommentMentionUpdateForm, + }, post::{Post, PostInsertForm}, }, traits::{Blockable, Crud}, @@ -284,15 +298,15 @@ mod tests { ); let inserted_comment = Comment::create(pool, &comment_form, None).await?; - let person_mention_form = PersonMentionInsertForm { + let person_comment_mention_form = PersonCommentMentionInsertForm { recipient_id: inserted_recipient.id, comment_id: inserted_comment.id, read: None, }; - let inserted_mention = PersonMention::create(pool, &person_mention_form).await?; + let inserted_mention = PersonCommentMention::create(pool, &person_comment_mention_form).await?; - let expected_mention = PersonMention { + let expected_mention = PersonCommentMention { id: inserted_mention.id, recipient_id: inserted_mention.recipient_id, comment_id: inserted_mention.comment_id, @@ -300,17 +314,21 @@ mod tests { published: inserted_mention.published, }; - let read_mention = PersonMention::read(pool, inserted_mention.id).await?; + let read_mention = PersonCommentMention::read(pool, inserted_mention.id).await?; - let person_mention_update_form = PersonMentionUpdateForm { read: Some(false) }; - let updated_mention = - PersonMention::update(pool, inserted_mention.id, &person_mention_update_form).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 = - PersonMentionView::get_unread_mentions(pool, &recipient_local_user).await?; + PersonCommentMentionView::get_unread_count(pool, &recipient_local_user).await?; - let query = PersonMentionQuery { + let query = PersonCommentMentionQuery { recipient_id: Some(recipient_id), my_person_id: Some(recipient_id), sort: None, @@ -331,7 +349,7 @@ mod tests { PersonBlock::block(pool, &block_form).await?; let unread_mentions_after_block = - PersonMentionView::get_unread_mentions(pool, &recipient_local_user).await?; + 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()); @@ -359,7 +377,8 @@ mod tests { let recipient_local_user_view = LocalUserView::read(pool, recipient_local_user.id).await?; let unread_mentions_after_hide_bots = - PersonMentionView::get_unread_mentions(pool, &recipient_local_user_view.local_user).await?; + 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; diff --git a/crates/db_views_actor/src/structs.rs b/crates/db_views_actor/src/structs.rs index 6b609a753..d997f31d4 100644 --- a/crates/db_views_actor/src/structs.rs +++ b/crates/db_views_actor/src/structs.rs @@ -1,13 +1,14 @@ #[cfg(feature = "full")] use diesel::Queryable; use lemmy_db_schema::{ - aggregates::structs::{CommentAggregates, CommunityAggregates, PersonAggregates}, + aggregates::structs::{CommentAggregates, CommunityAggregates, PersonAggregates, PostAggregates}, source::{ comment::Comment, comment_reply::CommentReply, community::Community, person::Person, - person_mention::PersonMention, + person_comment_mention::PersonCommentMention, + person_post_mention::PersonPostMention, post::Post, }, SubscribedType, @@ -93,9 +94,9 @@ pub enum CommunitySortType { #[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 person mention view. -pub struct PersonMentionView { - pub person_mention: PersonMention, +/// A person comment mention view. +pub struct PersonCommentMentionView { + pub person_comment_mention: PersonCommentMention, pub comment: Comment, pub creator: Person, pub post: Post, @@ -113,6 +114,29 @@ pub struct PersonMentionView { pub my_vote: Option, } +#[skip_serializing_none] +#[derive(Debug, PartialEq, 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 person post mention view. +pub struct PersonPostMentionView { + pub person_post_mention: PersonPostMention, + pub post: Post, + pub creator: Person, + pub community: Community, + pub recipient: Person, + pub counts: PostAggregates, + pub creator_banned_from_community: bool, + pub banned_from_community: bool, + pub creator_is_moderator: bool, + pub creator_is_admin: bool, + pub subscribed: SubscribedType, + pub saved: bool, + pub creator_blocked: bool, + pub my_vote: Option, +} + #[skip_serializing_none] #[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] #[cfg_attr(feature = "full", derive(TS, Queryable))] diff --git a/crates/routes/src/feeds.rs b/crates/routes/src/feeds.rs index 55e9cc7f3..31a565aa3 100644 --- a/crates/routes/src/feeds.rs +++ b/crates/routes/src/feeds.rs @@ -17,8 +17,9 @@ use lemmy_db_views::{ }; use lemmy_db_views_actor::{ comment_reply_view::CommentReplyQuery, - person_mention_view::PersonMentionQuery, - structs::{CommentReplyView, PersonMentionView}, + person_comment_mention_view::PersonCommentMentionQuery, + person_post_mention_view::PersonPostMentionQuery, + structs::{CommentReplyView, PersonCommentMentionView, PersonPostMentionView}, }; use lemmy_utils::{ cache_header::cache_1hour, @@ -360,37 +361,53 @@ async fn get_feed_front( async fn get_feed_inbox(context: &LemmyContext, jwt: &str) -> LemmyResult { let site_view = SiteView::read_local(&mut context.pool()).await?; let local_user = local_user_view_from_jwt(jwt, context).await?; - let person_id = local_user.local_user.person_id; + let my_person_id = Some(local_user.person.id); + let recipient_id = Some(local_user.local_user.person_id); let show_bot_accounts = local_user.local_user.show_bot_accounts; - - let sort = CommentSortType::New; + let limit = Some(RSS_FETCH_LIMIT); check_private_instance(&Some(local_user.clone()), &site_view.local_site)?; let replies = CommentReplyQuery { - recipient_id: (Some(person_id)), - my_person_id: (Some(person_id)), - show_bot_accounts: (show_bot_accounts), - sort: (Some(sort)), - limit: (Some(RSS_FETCH_LIMIT)), + recipient_id, + my_person_id, + show_bot_accounts, + sort: Some(CommentSortType::New), + limit, ..Default::default() } .list(&mut context.pool()) .await?; - let mentions = PersonMentionQuery { - recipient_id: (Some(person_id)), - my_person_id: (Some(person_id)), - show_bot_accounts: (show_bot_accounts), - sort: (Some(sort)), - limit: (Some(RSS_FETCH_LIMIT)), + 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()) .await?; let protocol_and_hostname = context.settings().get_protocol_and_hostname(); - let items = create_reply_and_mention_items(replies, mentions, &protocol_and_hostname)?; + let items = create_reply_and_mention_items( + replies, + comment_mentions, + post_mentions, + &protocol_and_hostname, + )?; let mut channel = Channel { namespaces: RSS_NAMESPACE.clone(), @@ -410,7 +427,8 @@ async fn get_feed_inbox(context: &LemmyContext, jwt: &str) -> LemmyResult, - mentions: Vec, + comment_mentions: Vec, + post_mentions: Vec, protocol_and_hostname: &str, ) -> LemmyResult> { let mut reply_items: Vec = replies @@ -427,7 +445,7 @@ fn create_reply_and_mention_items( }) .collect::>>()?; - let mut mention_items: Vec = mentions + let mut comment_mention_items: Vec = comment_mentions .iter() .map(|m| { let mention_url = format!("{}/comment/{}", protocol_and_hostname, m.comment.id); @@ -441,7 +459,24 @@ fn create_reply_and_mention_items( }) .collect::>>()?; - reply_items.append(&mut mention_items); + reply_items.append(&mut comment_mention_items); + + let mut post_mention_items: Vec = 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::>>()?; + + reply_items.append(&mut post_mention_items); + Ok(reply_items) } diff --git a/crates/utils/src/error.rs b/crates/utils/src/error.rs index f45bc271f..d9db32428 100644 --- a/crates/utils/src/error.rs +++ b/crates/utils/src/error.rs @@ -106,7 +106,7 @@ pub enum LemmyErrorType { CouldntHidePost, CouldntUpdateCommunity, CouldntUpdateReplies, - CouldntUpdatePersonMentions, + CouldntUpdatePersonCommentMentions, CouldntCreatePost, CouldntCreatePrivateMessage, CouldntUpdatePrivate, diff --git a/migrations/2024-11-02-161125_add_post_body_mention/down.sql b/migrations/2024-11-02-161125_add_post_body_mention/down.sql new file mode 100644 index 000000000..37bb14e3c --- /dev/null +++ b/migrations/2024-11-02-161125_add_post_body_mention/down.sql @@ -0,0 +1,5 @@ +-- Rename the person_mention table to person_comment_mention +ALTER TABLE person_comment_mention RENAME TO person_mention; + +-- Drop the new table +DROP TABLE person_post_mention; diff --git a/migrations/2024-11-02-161125_add_post_body_mention/up.sql b/migrations/2024-11-02-161125_add_post_body_mention/up.sql new file mode 100644 index 000000000..ae8e0bcad --- /dev/null +++ b/migrations/2024-11-02-161125_add_post_body_mention/up.sql @@ -0,0 +1,12 @@ +-- 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) +);