Revert " Add post_actions.disable_notifications (fixes #3042) (#5826)" (#5843)

This reverts commit 76fbb29079.
This commit is contained in:
Nutomic 2025-07-02 09:29:42 +00:00 committed by GitHub
parent 0d149dd613
commit 3922f0f325
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 420 additions and 393 deletions

View file

@ -69,5 +69,8 @@ pub async fn distinguish_comment(
)
.await?;
Ok(Json(CommentResponse { comment_view }))
Ok(Json(CommentResponse {
comment_view,
recipient_ids: Vec::new(),
}))
}

View file

@ -8,9 +8,10 @@ use lemmy_api_utils::{
utils::{check_bot_account, check_community_user_action, check_local_vote_mode},
};
use lemmy_db_schema::{
newtypes::PostOrCommentId,
newtypes::{LocalUserId, PostOrCommentId},
source::{
comment::{CommentActions, CommentLikeForm},
comment_reply::CommentReply,
person::PersonActions,
},
traits::Likeable,
@ -34,6 +35,8 @@ pub async fn like_comment(
let comment_id = data.comment_id;
let my_person_id = local_user_view.person.id;
let mut recipient_ids = Vec::<LocalUserId>::new();
check_local_vote_mode(
data.score,
PostOrCommentId::Comment(comment_id),
@ -60,6 +63,16 @@ pub async fn like_comment(
)
.await?;
// Add parent poster or commenter to recipients
let comment_reply = CommentReply::read_by_comment(&mut context.pool(), comment_id).await;
if let Ok(Some(reply)) = comment_reply {
let recipient_id = reply.recipient_id;
if let Ok(local_recipient) = LocalUserView::read_person(&mut context.pool(), recipient_id).await
{
recipient_ids.push(local_recipient.local_user.id);
}
}
let mut like_form = CommentLikeForm::new(my_person_id, data.comment_id, data.score);
// Remove any likes first
@ -106,6 +119,7 @@ pub async fn like_comment(
context.deref(),
comment_id,
Some(local_user_view),
recipient_ids,
local_instance_id,
)
.await?,

View file

@ -34,5 +34,8 @@ pub async fn save_comment(
)
.await?;
Ok(Json(CommentResponse { comment_view }))
Ok(Json(CommentResponse {
comment_view,
recipient_ids: Vec::new(),
}))
}

View file

@ -7,4 +7,3 @@ pub mod lock;
pub mod mark_many_read;
pub mod mark_read;
pub mod save;
pub mod update_notifications;

View file

@ -1,23 +0,0 @@
use activitypub_federation::config::Data;
use actix_web::web::Json;
use lemmy_api_utils::context::LemmyContext;
use lemmy_db_schema::source::post::PostActions;
use lemmy_db_views_local_user::LocalUserView;
use lemmy_db_views_post::api::UpdatePostNotifications;
use lemmy_db_views_site::api::SuccessResponse;
use lemmy_utils::error::LemmyResult;
pub async fn update_post_notifications(
data: Json<UpdatePostNotifications>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> LemmyResult<Json<SuccessResponse>> {
PostActions::update_notification_state(
data.post_id,
local_user_view.person.id,
data.new_state,
&mut context.pool(),
)
.await?;
Ok(Json(SuccessResponse::default()))
}

View file

@ -19,6 +19,7 @@ use lemmy_api_utils::{
};
use lemmy_db_schema::{
impls::actor_language::validate_post_language,
newtypes::PostOrCommentId,
source::{
comment::{Comment, CommentActions, CommentInsertForm, CommentLikeForm},
comment_reply::{CommentReply, CommentReplyUpdateForm},
@ -32,7 +33,7 @@ use lemmy_db_views_post::PostView;
use lemmy_db_views_site::SiteView;
use lemmy_utils::{
error::{LemmyErrorType, LemmyResult},
utils::validation::is_valid_body_field,
utils::{mention::scrape_text_for_mentions, validation::is_valid_body_field},
};
pub async fn create_comment(
@ -112,14 +113,19 @@ pub async fn create_comment(
Comment::create(&mut context.pool(), &comment_form, parent_path.as_ref()).await?;
plugin_hook_after("after_create_local_comment", &inserted_comment)?;
let inserted_comment_id = inserted_comment.id;
// Scan the comment for user mentions, add those rows
let mentions = scrape_text_for_mentions(&content);
let do_send_email = !local_site.disable_email_notifications;
send_local_notifs(
&post,
Some(&inserted_comment),
let recipient_ids = send_local_notifs(
mentions,
PostOrCommentId::Comment(inserted_comment_id),
&local_user_view.person,
&post_view.community,
do_send_email,
&context,
Some(&local_user_view),
local_instance_id,
)
.await?;
@ -179,6 +185,7 @@ pub async fn create_comment(
&context,
inserted_comment.id,
Some(local_user_view),
recipient_ids,
local_instance_id,
)
.await?,

View file

@ -1,12 +1,13 @@
use activitypub_federation::config::Data;
use actix_web::web::Json;
use lemmy_api_utils::{
build_response::build_comment_response,
build_response::{build_comment_response, send_local_notifs},
context::LemmyContext,
send_activity::{ActivityChannel, SendActivityData},
utils::check_community_user_action,
};
use lemmy_db_schema::{
newtypes::PostOrCommentId,
source::comment::{Comment, CommentUpdateForm},
traits::Crud,
};
@ -61,6 +62,16 @@ pub async fn delete_comment(
)
.await?;
let recipient_ids = send_local_notifs(
vec![],
PostOrCommentId::Comment(comment_id),
&local_user_view.person,
false,
&context,
Some(&local_user_view),
local_instance_id,
)
.await?;
let updated_comment_id = updated_comment.id;
ActivityChannel::submit_activity(
@ -77,6 +88,7 @@ pub async fn delete_comment(
&context,
updated_comment_id,
Some(local_user_view),
recipient_ids,
local_instance_id,
)
.await?,

View file

@ -21,6 +21,13 @@ pub async fn get_comment(
check_private_instance(&local_user_view, &local_site)?;
Ok(Json(
build_comment_response(&context, data.id, local_user_view, local_instance_id).await?,
build_comment_response(
&context,
data.id,
local_user_view,
vec![],
local_instance_id,
)
.await?,
))
}

View file

@ -1,12 +1,13 @@
use activitypub_federation::config::Data;
use actix_web::web::Json;
use lemmy_api_utils::{
build_response::build_comment_response,
build_response::{build_comment_response, send_local_notifs},
context::LemmyContext,
send_activity::{ActivityChannel, SendActivityData},
utils::check_community_mod_action,
};
use lemmy_db_schema::{
newtypes::PostOrCommentId,
source::{
comment::{Comment, CommentUpdateForm},
comment_report::CommentReport,
@ -83,6 +84,16 @@ pub async fn remove_comment(
};
ModRemoveComment::create(&mut context.pool(), &form).await?;
let recipient_ids = send_local_notifs(
vec![],
PostOrCommentId::Comment(comment_id),
&local_user_view.person,
false,
&context,
Some(&local_user_view),
local_instance_id,
)
.await?;
let updated_comment_id = updated_comment.id;
ActivityChannel::submit_activity(
@ -100,6 +111,7 @@ pub async fn remove_comment(
&context,
updated_comment_id,
Some(local_user_view),
recipient_ids,
local_instance_id,
)
.await?,

View file

@ -10,6 +10,7 @@ use lemmy_api_utils::{
};
use lemmy_db_schema::{
impls::actor_language::validate_post_language,
newtypes::PostOrCommentId,
source::comment::{Comment, CommentUpdateForm},
traits::Crud,
};
@ -20,7 +21,7 @@ use lemmy_db_views_comment::{
use lemmy_db_views_local_user::LocalUserView;
use lemmy_utils::{
error::{LemmyErrorType, LemmyResult},
utils::validation::is_valid_body_field,
utils::{mention::scrape_text_for_mentions, validation::is_valid_body_field},
};
pub async fn update_comment(
@ -78,13 +79,16 @@ pub async fn update_comment(
plugin_hook_after("after_update_local_comment", &updated_comment)?;
// Do the mentions / recipients
send_local_notifs(
&orig_comment.post,
Some(&updated_comment),
let updated_comment_content = updated_comment.content.clone();
let mentions = scrape_text_for_mentions(&updated_comment_content);
let recipient_ids = send_local_notifs(
mentions,
PostOrCommentId::Comment(comment_id),
&local_user_view.person,
&orig_comment.community,
false,
&context,
Some(&local_user_view),
local_instance_id,
)
.await?;
@ -98,6 +102,7 @@ pub async fn update_comment(
&context,
updated_comment.id,
Some(local_user_view),
recipient_ids,
local_instance_id,
)
.await?,

View file

@ -21,6 +21,7 @@ use lemmy_api_utils::{
};
use lemmy_db_schema::{
impls::actor_language::validate_post_language,
newtypes::PostOrCommentId,
source::post::{Post, PostActions, PostInsertForm, PostLikeForm, PostReadForm},
traits::{Crud, Likeable, Readable},
utils::diesel_url_create,
@ -33,6 +34,7 @@ use lemmy_db_views_site::SiteView;
use lemmy_utils::{
error::LemmyResult,
utils::{
mention::scrape_text_for_mentions,
slurs::check_slurs,
validation::{
is_url_blocked,
@ -167,18 +169,22 @@ pub async fn create_post(
// They like their own post by default
let person_id = local_user_view.person.id;
let post_id = inserted_post.id;
let local_instance_id = local_user_view.person.instance_id;
let like_form = PostLikeForm::new(post_id, person_id, 1);
PostActions::like(&mut context.pool(), &like_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());
let do_send_email = !local_site.disable_email_notifications;
send_local_notifs(
&inserted_post,
None,
mentions,
PostOrCommentId::Post(inserted_post.id),
&local_user_view.person,
community,
do_send_email,
&context,
Some(&local_user_view),
local_instance_id,
)
.await?;

View file

@ -20,6 +20,7 @@ use lemmy_api_utils::{
};
use lemmy_db_schema::{
impls::actor_language::validate_post_language,
newtypes::PostOrCommentId,
source::{
community::Community,
post::{Post, PostUpdateForm},
@ -37,6 +38,7 @@ use lemmy_db_views_site::SiteView;
use lemmy_utils::{
error::{LemmyErrorType, LemmyResult},
utils::{
mention::scrape_text_for_mentions,
slurs::check_slurs,
validation::{
is_url_blocked,
@ -161,13 +163,16 @@ pub async fn update_post(
let updated_post = Post::update(&mut context.pool(), post_id, &post_form).await?;
plugin_hook_after("after_update_local_post", &post_form)?;
// 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(
&updated_post,
None,
mentions,
PostOrCommentId::Post(updated_post.id),
&local_user_view.person,
&orig_post.community,
false,
&context,
Some(&local_user_view),
local_instance_id,
)
.await?;

View file

@ -1,36 +1,38 @@
use crate::{context::LemmyContext, utils::is_mod_or_admin};
use crate::{
context::LemmyContext,
utils::{check_person_instance_community_block, is_mod_or_admin},
};
use actix_web::web::Json;
use lemmy_db_schema::{
newtypes::{CommentId, CommunityId, InstanceId, PersonId, PostId},
newtypes::{CommentId, CommunityId, InstanceId, LocalUserId, PostId, PostOrCommentId},
source::{
actor_language::CommunityLanguage,
comment::Comment,
comment_reply::{CommentReply, CommentReplyInsertForm},
community::{Community, CommunityActions},
instance::InstanceActions,
person::{Person, PersonActions},
community::Community,
person::Person,
person_comment_mention::{PersonCommentMention, PersonCommentMentionInsertForm},
person_post_mention::{PersonPostMention, PersonPostMentionInsertForm},
post::{Post, PostActions},
post::Post,
},
traits::{Blockable, Crud},
traits::Crud,
};
use lemmy_db_schema_file::enums::PostNotifications;
use lemmy_db_views_comment::{api::CommentResponse, CommentView};
use lemmy_db_views_community::{api::CommunityResponse, CommunityView};
use lemmy_db_views_local_user::LocalUserView;
use lemmy_db_views_post::{api::PostResponse, PostView};
use lemmy_email::notifications::{send_mention_email, send_reply_email};
use lemmy_utils::{
error::{LemmyErrorType, LemmyResult},
utils::mention::scrape_text_for_mentions,
use lemmy_email::notifications::{
send_comment_reply_email,
send_mention_email,
send_post_reply_email,
};
use url::Url;
use lemmy_utils::{error::LemmyResult, utils::mention::MentionData};
pub async fn build_comment_response(
context: &LemmyContext,
comment_id: CommentId,
local_user_view: Option<LocalUserView>,
recipient_ids: Vec<LocalUserId>,
local_instance_id: InstanceId,
) -> LemmyResult<CommentResponse> {
let local_user = local_user_view.map(|l| l.local_user);
@ -41,7 +43,10 @@ pub async fn build_comment_response(
local_instance_id,
)
.await?;
Ok(CommentResponse { comment_view })
Ok(CommentResponse {
comment_view,
recipient_ids,
})
}
pub async fn build_community_response(
@ -89,223 +94,220 @@ pub async fn build_post_response(
Ok(Json(PostResponse { post_view }))
}
/// Scans the post/comment content for mentions, then sends notifications via db and email
/// to mentioned users and parent creator.
// TODO: this function is a mess and should be split up to handle email separately
pub async fn send_local_notifs(
post: &Post,
comment_opt: Option<&Comment>,
mentions: Vec<MentionData>,
post_or_comment_id: PostOrCommentId,
person: &Person,
community: &Community,
do_send_email: bool,
context: &LemmyContext,
) -> LemmyResult<()> {
let parent_creator =
notify_parent_creator(person, post, comment_opt, community, do_send_email, context).await?;
local_user_view: Option<&LocalUserView>,
local_instance_id: InstanceId,
) -> LemmyResult<Vec<LocalUserId>> {
let mut recipient_ids = Vec::new();
send_local_mentions(
post,
comment_opt,
person,
parent_creator,
community,
do_send_email,
context,
)
.await?;
Ok(())
}
async fn notify_parent_creator(
person: &Person,
post: &Post,
comment_opt: Option<&Comment>,
community: &Community,
do_send_email: bool,
context: &LemmyContext,
) -> LemmyResult<Option<PersonId>> {
let Some(comment) = comment_opt else {
return Ok(None);
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),
local_instance_id,
false,
)
.await?;
(None, post_view.post, post_view.community)
}
PostOrCommentId::Comment(comment_id) => {
// 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.
if let Some(local_user_view) = local_user_view {
// Read the comment view to get extra info
let comment_view = CommentView::read(
&mut context.pool(),
comment_id,
Some(&local_user_view.local_user),
local_instance_id,
)
.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)
}
}
};
// Get the parent data
let (parent_creator_id, parent_comment) =
// Send the local mentions
for mention in mentions
.iter()
.filter(|m| m.is_local(&context.settings().hostname) && m.name.ne(&person.name))
{
let mention_name = mention.name.clone();
let user_view = LocalUserView::read_from_name(&mut context.pool(), &mention_name).await;
if let Ok(mention_user_view) = user_view {
// TODO
// At some point, make it so you can't tag the parent creator either
// Potential duplication of notifications, one for reply and the other for mention, is handled
// below by checking recipient ids
recipient_ids.push(mention_user_view.local_user.id);
// Make the correct reply form depending on whether its a post or comment mention
let (link, 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
PersonCommentMention::create(&mut context.pool(), &person_comment_mention_form)
.await
.ok();
(
comment.local_url(context.settings())?,
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.local_url(context.settings())?,
post.body.clone().unwrap_or_default(),
)
};
// Send an email to those local users that have notifications on
if do_send_email {
send_mention_email(
&mention_user_view,
&comment_content_or_post_body,
person,
link.into(),
context.settings(),
)
.await;
}
}
}
// Send comment_reply to the parent commenter / poster
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?;
(parent_comment.creator_id, Some(parent_comment))
} else {
(post.creator_id, None)
};
// Dont send notification to yourself
if parent_creator_id == person.id {
return Ok(None);
}
// Get the parent commenter local_user
let parent_creator_id = parent_comment.creator_id;
let is_blocked = check_notifications_allowed(
parent_creator_id,
// Only block from the community's instance_id
community.instance_id,
post,
context,
)
.await
.is_err();
if is_blocked {
return Ok(None);
}
let Ok(user_view) = LocalUserView::read_person(&mut context.pool(), parent_creator_id).await
else {
return Ok(None);
};
let comment_reply_form = CommentReplyInsertForm {
recipient_id: 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();
if do_send_email {
send_reply_email(
&user_view,
comment,
person,
&parent_comment,
post,
context.settings(),
)
.await?;
}
Ok(Some(user_view.person.id))
}
async fn send_local_mentions(
post: &Post,
comment_opt: Option<&Comment>,
person: &Person,
parent_creator_id: Option<PersonId>,
community: &Community,
do_send_email: bool,
context: &LemmyContext,
) -> LemmyResult<()> {
let content = if let Some(comment) = comment_opt {
&comment.content
} else {
&post.body.clone().unwrap_or_default()
};
let mentions = scrape_text_for_mentions(content)
.into_iter()
.filter(|m| m.is_local(&context.settings().hostname) && m.name.ne(&person.name));
for mention in mentions {
// Ignore error if user is remote
let Ok(user_view) = LocalUserView::read_from_name(&mut context.pool(), &mention.name).await
else {
continue;
};
// Dont send any mention notification to parent creator nor to yourself
if Some(user_view.person.id) == parent_creator_id || user_view.person.id == person.id {
continue;
}
let is_blocked = check_notifications_allowed(
user_view.person.id,
// Only block from the community's instance_id
community.instance_id,
post,
context,
)
.await
.is_err();
if is_blocked {
continue;
};
let (link, comment_content_or_post_body) =
insert_post_or_comment_mention(&user_view, post, comment_opt, context).await?;
// Send an email to those local users that have notifications on
if do_send_email {
send_mention_email(
&user_view,
&comment_content_or_post_body,
person,
link.into(),
context.settings(),
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;
.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);
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();
if do_send_email {
send_comment_reply_email(
&parent_user_view,
comment,
person,
&parent_comment,
&post,
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();
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,
};
// 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 {
send_post_reply_email(
&parent_user_view,
comment,
person,
&post,
context.settings(),
)
.await?;
}
}
}
}
}
}
Ok(())
}
/// Make the correct reply form depending on whether its a post or comment mention
async fn insert_post_or_comment_mention(
mention_user_view: &LocalUserView,
post: &Post,
comment_opt: Option<&Comment>,
context: &LemmyContext,
) -> LemmyResult<(Url, String)> {
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
PersonCommentMention::create(&mut context.pool(), &person_comment_mention_form)
.await
.ok();
Ok((
comment.local_url(context.settings())?,
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();
Ok((
post.local_url(context.settings())?,
post.body.clone().unwrap_or_default(),
))
}
}
pub async fn check_notifications_allowed(
potential_blocker_id: PersonId,
community_instance_id: InstanceId,
post: &Post,
context: &LemmyContext,
) -> LemmyResult<()> {
let pool = &mut context.pool();
PersonActions::read_block(pool, potential_blocker_id, post.creator_id).await?;
InstanceActions::read_block(pool, potential_blocker_id, community_instance_id).await?;
CommunityActions::read_block(pool, potential_blocker_id, post.community_id).await?;
let post_notifications = PostActions::read(pool, post.id, potential_blocker_id)
.await
.ok()
.and_then(|a| a.notifications)
.unwrap_or_default();
if post_notifications == PostNotifications::Mute {
// The specific error type is irrelevant
return Err(LemmyErrorType::NotFound.into());
}
Ok(())
Ok(recipient_ids)
}

View file

@ -24,13 +24,13 @@ use lemmy_db_schema::{
ModRemovePostForm,
},
oauth_account::OAuthAccount,
person::{Person, PersonUpdateForm},
person::{Person, PersonActions, PersonUpdateForm},
post::{Post, PostActions, PostReadCommentsForm},
private_message::PrivateMessage,
registration_application::RegistrationApplication,
site::Site,
},
traits::{Crud, Likeable, ReadComments},
traits::{Blockable, Crud, Likeable, ReadComments},
utils::DbPool,
};
use lemmy_db_schema_file::enums::{FederationMode, RegistrationMode};
@ -321,6 +321,19 @@ pub fn check_comment_deleted_or_removed(comment: &Comment) -> LemmyResult<()> {
}
}
pub async fn check_person_instance_community_block(
my_id: PersonId,
potential_blocker_id: PersonId,
community_instance_id: InstanceId,
community_id: CommunityId,
pool: &mut DbPool<'_>,
) -> LemmyResult<()> {
PersonActions::read_block(pool, potential_blocker_id, my_id).await?;
InstanceActions::read_block(pool, potential_blocker_id, community_instance_id).await?;
CommunityActions::read_block(pool, potential_blocker_id, community_id).await?;
Ok(())
}
pub async fn check_local_vote_mode(
score: i16,
post_or_comment_id: PostOrCommentId,

View file

@ -27,7 +27,7 @@ use lemmy_apub_objects::{
},
};
use lemmy_db_schema::{
newtypes::PersonId,
newtypes::{PersonId, PostOrCommentId},
source::{
activity::ActivitySendTargets,
comment::{Comment, CommentActions, CommentLikeForm},
@ -38,7 +38,10 @@ use lemmy_db_schema::{
traits::{Crud, Likeable},
};
use lemmy_db_views_site::SiteView;
use lemmy_utils::error::{LemmyError, LemmyResult};
use lemmy_utils::{
error::{LemmyError, LemmyResult},
utils::mention::scrape_text_for_mentions,
};
use serde_json::{from_value, to_value};
use url::Url;
@ -134,12 +137,12 @@ impl ActivityHandler for CreateOrUpdateNote {
// Need to do this check here instead of Note::from_json because we need the person who
// send the activity, not the comment author.
let existing_comment = self.object.id.dereference_local(context).await.ok();
let (post, _) = self.object.get_parents(context).await?;
if let (Some(distinguished), Some(existing_comment)) =
(self.object.distinguished, existing_comment)
{
if distinguished != existing_comment.distinguished {
let creator = self.actor.dereference(context).await?;
let (post, _) = self.object.get_parents(context).await?;
check_is_mod_or_admin(
&mut context.pool(),
creator.id,
@ -169,14 +172,17 @@ impl ActivityHandler for CreateOrUpdateNote {
// anyway.
// TODO: for compatibility with other projects, it would be much better to read this from cc or
// tags
let community = Community::read(&mut context.pool(), post.community_id).await?;
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(
&post.0,
Some(&comment.0),
mentions,
PostOrCommentId::Comment(comment.id),
&actor,
&community,
do_send_email,
context,
None,
local_instance_id,
)
.await?;
Ok(())

View file

@ -21,7 +21,7 @@ use lemmy_apub_objects::{
},
};
use lemmy_db_schema::{
newtypes::PersonId,
newtypes::{PersonId, PostOrCommentId},
source::{
activity::ActivitySendTargets,
community::Community,
@ -31,7 +31,10 @@ use lemmy_db_schema::{
traits::{Crud, Likeable},
};
use lemmy_db_views_site::SiteView;
use lemmy_utils::error::{LemmyError, LemmyResult};
use lemmy_utils::{
error::{LemmyError, LemmyResult},
utils::mention::scrape_text_for_mentions,
};
use url::Url;
impl CreateOrUpdatePage {
@ -107,6 +110,7 @@ impl ActivityHandler for CreateOrUpdatePage {
async fn receive(self, context: &Data<LemmyContext>) -> LemmyResult<()> {
let site_view = SiteView::read_local(&mut context.pool()).await?;
let local_instance_id = site_view.site.instance_id;
let post = ApubPost::from_json(self.object, context).await?;
@ -121,8 +125,18 @@ impl ActivityHandler for CreateOrUpdatePage {
self.kind == CreateOrUpdateType::Create && !site_view.local_site.disable_email_notifications;
let actor = self.actor.dereference(context).await?;
let community = Community::read(&mut context.pool(), post.community_id).await?;
send_local_notifs(&post.0, None, &actor, &community, do_send_email, 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,
local_instance_id,
)
.await?;
Ok(())
}

View file

@ -25,6 +25,7 @@ use crate::{
SITEMAP_LIMIT,
},
};
use ::url::Url;
use chrono::{DateTime, Utc};
use diesel::{
dsl::{count, insert_into, not, update},
@ -38,15 +39,11 @@ use diesel::{
QueryDsl,
};
use diesel_async::RunQueryDsl;
use lemmy_db_schema_file::{
enums::PostNotifications,
schema::{community, person, post, post_actions},
};
use lemmy_db_schema_file::schema::{community, person, post, post_actions};
use lemmy_utils::{
error::{LemmyErrorExt, LemmyErrorExt2, LemmyErrorType, LemmyResult},
settings::structs::Settings,
};
use url::Url;
impl Crud for Post {
type InsertForm = PostInsertForm;
@ -542,7 +539,9 @@ impl PostActions {
.map(|post_id| (PostReadForm::new(*post_id, person_id)))
.collect::<Vec<_>>()
}
}
impl PostActions {
pub async fn read(
pool: &mut DbPool<'_>,
post_id: PostId,
@ -568,29 +567,6 @@ impl PostActions {
.ok_or(LemmyErrorType::CouldntParsePaginationToken)?;
Self::read(pool, PostId(*post_id), PersonId(*person_id)).await
}
pub async fn update_notification_state(
post_id: PostId,
person_id: PersonId,
new_state: PostNotifications,
pool: &mut DbPool<'_>,
) -> LemmyResult<PostActions> {
let conn = &mut get_conn(pool).await?;
let form = (
post_actions::person_id.eq(person_id),
post_actions::post_id.eq(post_id),
post_actions::notifications.eq(new_state),
);
insert_into(post_actions::table)
.values(form.clone())
.on_conflict((post_actions::person_id, post_actions::post_id))
.do_update()
.set(form)
.get_result::<Self>(conn)
.await
.with_lemmy_type(LemmyErrorType::NotFound)
}
}
#[cfg(test)]

View file

@ -1,6 +1,5 @@
use crate::newtypes::{CommunityId, DbUrl, LanguageId, PersonId, PostId};
use chrono::{DateTime, Utc};
use lemmy_db_schema_file::enums::PostNotifications;
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;
#[cfg(feature = "full")]
@ -202,7 +201,6 @@ pub struct PostActions {
pub like_score: Option<i16>,
/// When the post was hidden.
pub hidden_at: Option<DateTime<Utc>>,
pub notifications: Option<PostNotifications>,
}
#[derive(Clone, derive_new::new)]

View file

@ -226,22 +226,3 @@ pub enum VoteShow {
ShowForOthers,
Hide,
}
#[derive(
EnumString, Display, Debug, Serialize, Deserialize, Default, Clone, Copy, PartialEq, Eq, Hash,
)]
#[cfg_attr(feature = "full", derive(DbEnum))]
#[cfg_attr(
feature = "full",
ExistingTypePath = "crate::schema::sql_types::PostNotificationsModeEnum"
)]
#[cfg_attr(feature = "full", DbValueStyle = "verbatim")]
#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
#[cfg_attr(feature = "ts-rs", ts(export))]
/// Lets you show votes for others only, show all votes, or hide all votes.
pub enum PostNotifications {
#[default]
RepliesAndMentions,
AllComments,
Mute,
}

View file

@ -33,10 +33,6 @@ pub mod sql_types {
#[diesel(postgres_type(name = "post_listing_mode_enum"))]
pub struct PostListingModeEnum;
#[derive(diesel::query_builder::QueryId, diesel::sql_types::SqlType)]
#[diesel(postgres_type(name = "post_notifications_mode_enum"))]
pub struct PostNotificationsModeEnum;
#[derive(diesel::query_builder::QueryId, diesel::sql_types::SqlType)]
#[diesel(postgres_type(name = "post_sort_type_enum"))]
pub struct PostSortTypeEnum;
@ -938,9 +934,6 @@ diesel::table! {
}
diesel::table! {
use diesel::sql_types::*;
use super::sql_types::PostNotificationsModeEnum;
post_actions (person_id, post_id) {
post_id -> Int4,
person_id -> Int4,
@ -951,7 +944,6 @@ diesel::table! {
liked_at -> Nullable<Timestamptz>,
like_score -> Nullable<Int2>,
hidden_at -> Nullable<Timestamptz>,
notifications -> Nullable<PostNotificationsModeEnum>,
}
}

View file

@ -1,5 +1,12 @@
use crate::{CommentSlimView, CommentView};
use lemmy_db_schema::newtypes::{CommentId, CommunityId, LanguageId, PaginationCursor, PostId};
use lemmy_db_schema::newtypes::{
CommentId,
CommunityId,
LanguageId,
LocalUserId,
PaginationCursor,
PostId,
};
use lemmy_db_schema_file::enums::{CommentSortType, ListingType};
use lemmy_db_views_vote::VoteView;
use serde::{Deserialize, Serialize};
@ -12,6 +19,7 @@ use serde_with::skip_serializing_none;
/// A comment response.
pub struct CommentResponse {
pub comment_view: CommentView,
pub recipient_ids: Vec<LocalUserId>,
}
#[skip_serializing_none]

View file

@ -12,7 +12,7 @@ use lemmy_db_schema::{
},
PostFeatureType,
};
use lemmy_db_schema_file::enums::{ListingType, PostNotifications, PostSortType};
use lemmy_db_schema_file::enums::{ListingType, PostSortType};
use lemmy_db_views_community::CommunityView;
use lemmy_db_views_vote::VoteView;
use serde::{Deserialize, Serialize};
@ -93,15 +93,6 @@ pub struct FeaturePost {
pub feature_type: PostFeatureType,
}
#[derive(Debug, Serialize, Deserialize, Clone, Copy, Default, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
#[cfg_attr(feature = "ts-rs", ts(optional_fields, export))]
/// Disable reply notifications for a post and all comments inside it
pub struct UpdatePostNotifications {
pub post_id: PostId,
pub new_state: PostNotifications,
}
#[skip_serializing_none]
#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]

View file

@ -30,42 +30,57 @@ pub async fn send_mention_email(
.await
}
pub async fn send_reply_email(
pub async fn send_comment_reply_email(
parent_user_view: &LocalUserView,
comment: &Comment,
person: &Person,
parent_comment: &Option<Comment>,
parent_comment: &Comment,
post: &Post,
settings: &Settings,
) -> LemmyResult<()> {
let inbox_link = inbox_link(settings);
let lang = user_language(parent_user_view);
let content = markdown_to_html(&comment.content);
let (subject, body) = if let Some(parent_comment) = parent_comment {
(
lang.notification_comment_reply_subject(&person.name),
lang.notification_comment_reply_body(
comment.local_url(settings)?,
&content,
&inbox_link,
&parent_comment.content,
&post.name,
&person.name,
),
)
} else {
(
lang.notification_post_reply_subject(&person.name),
lang.notification_post_reply_body(
comment.local_url(settings)?,
&content,
&inbox_link,
&post.name,
&person.name,
),
)
};
send_email_to_user(parent_user_view, &subject, &body, settings).await;
send_email_to_user(
parent_user_view,
&lang.notification_comment_reply_subject(&person.name),
&lang.notification_comment_reply_body(
comment.local_url(settings)?,
&content,
&inbox_link,
&parent_comment.content,
&post.name,
&person.name,
),
settings,
)
.await;
Ok(())
}
pub async fn send_post_reply_email(
parent_user_view: &LocalUserView,
comment: &Comment,
person: &Person,
post: &Post,
settings: &Settings,
) -> LemmyResult<()> {
let inbox_link = inbox_link(settings);
let lang = user_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(
comment.local_url(settings)?,
&content,
&inbox_link,
&post.name,
&person.name,
),
settings,
)
.await;
Ok(())
}

View file

@ -1,5 +0,0 @@
ALTER TABLE post_actions
DROP COLUMN notifications;
DROP TYPE post_notifications_mode_enum;

View file

@ -1,9 +0,0 @@
CREATE TYPE post_notifications_mode_enum AS enum (
'RepliesAndMentions',
'AllComments',
'Mute'
);
ALTER TABLE post_actions
ADD COLUMN notifications post_notifications_mode_enum;

View file

@ -67,7 +67,6 @@ use lemmy_api::{
mark_many_read::mark_posts_as_read,
mark_read::mark_post_as_read,
save::save_post,
update_notifications::update_post_notifications,
},
private_message::mark_read::mark_pm_as_read,
reports::{
@ -295,11 +294,7 @@ pub fn config(cfg: &mut ServiceConfig, rate_limit: &RateLimit) {
.route("/like/list", get().to(list_post_likes))
.route("/save", put().to(save_post))
.route("/report", post().to(create_post_report))
.route("/report/resolve", put().to(resolve_post_report))
.route(
"/disable_notifications",
post().to(update_post_notifications),
),
.route("/report/resolve", put().to(resolve_post_report)),
)
// Comment
.service(