diaspora/app/services/post_service.rb
Benjamin Neff 649d8c5b56
Mark notifications as read in a single SQL query
There is no need to load all comments only to count them. Lets just let
the database do all the work. If there are no comments found, nothing
will happen anyway.

Also already filter the comments to only search for notifications for
own comments.

And add some tests :)
2024-06-03 00:11:45 +02:00

111 lines
3.3 KiB
Ruby

# frozen_string_literal: true
class PostService
def initialize(user=nil)
@user = user
end
def find(id)
if user
user.find_visible_shareable_by_id(Post, id)
else
Post.find_by_id_and_public(id, true)
end
end
def find!(id_or_guid)
if user
find_non_public_by_guid_or_id_with_user!(id_or_guid)
else
find_public!(id_or_guid)
end
end
def present_json
PostPresenter.new(post, user)
end
def present_interactions_json
PostInteractionPresenter.new(post, user)
end
def mark_user_notifications(post_id)
return unless user
mark_comment_reshare_like_notifications_read(post_id)
mark_mention_notifications_read(post_id)
mark_like_on_comment_notifications_read(post_id)
end
def destroy(post_id, private_allowed=true)
post = if private_allowed
find_non_public_by_guid_or_id_with_user!(post_id)
else
find_public!(post_id)
end
raise Diaspora::NotMine unless post.author == user.person
user.retract(post)
end
def mentionable_in_comment(post_id, query)
post = find!(post_id)
Person
.allowed_to_be_mentioned_in_a_comment_to(post)
.where.not(id: user.person_id)
.find_by_substring(query)
.sort_for_mention_suggestion(post, user)
.for_json
.limit(15)
end
private
attr_reader :user
def find_public!(id_or_guid)
Post.where(post_key(id_or_guid) => id_or_guid).first.tap do |post|
raise ActiveRecord::RecordNotFound, "could not find a post with id #{id_or_guid}" unless post
raise Diaspora::NonPublic unless post.public?
end
end
def find_non_public_by_guid_or_id_with_user!(id_or_guid)
user.find_visible_shareable_by_id(Post, id_or_guid, key: post_key(id_or_guid)).tap do |post|
raise ActiveRecord::RecordNotFound, "could not find a post with id #{id_or_guid} for user #{user.id}" unless post
end
end
# We can assume a guid is at least 16 characters long as we have guids set to hex(8) since we started using them.
def post_key(id_or_guid)
id_or_guid.to_s.length < 16 ? :id : :guid
end
def mark_comment_reshare_like_notifications_read(post_id)
Notification.where(recipient_id: user.id, target_type: "Post", target_id: post_id, unread: true)
.update_all(unread: false)
end
def mark_mention_notifications_read(post_id)
mention_ids = Mention.where(
mentions_container_id: post_id,
mentions_container_type: "Post",
person_id: user.person_id
).ids
mention_ids.concat(mentions_in_comments_for_post(post_id).pluck(:id))
Notification.where(recipient_id: user.id, target_type: "Mention", target_id: mention_ids, unread: true)
.update_all(unread: false) if mention_ids.any?
end
def mentions_in_comments_for_post(post_id)
Mention
.joins("INNER JOIN comments ON mentions_container_id = comments.id AND mentions_container_type = 'Comment'")
.where(comments: {commentable_id: post_id, commentable_type: "Post"})
end
def mark_like_on_comment_notifications_read(post_id)
Notification.where(recipient_id: user.id, target_type: "Comment",
target_id: Comment.where(commentable_id: post_id, author_id: user.person.id),
unread: true).update_all(unread: false) # rubocop:disable Rails/SkipsModelValidations
end
end