New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Mentions in comments backend changes #6818
Mentions in comments backend changes #6818
Conversation
@@ -172,6 +172,10 @@ def self.search_order | |||
}.call | |||
end | |||
|
|||
def self.people_names(people) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
## TODO ----
# don't put presentation logic in the model!
Also, this is only used once, why don't you just move .map(&:name).join(", ")
to where its used? Maybe the controller is also not the best place, but it's better than split this and add it to the model.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, I like to have it as a separate method, if possible. I don't know of a better place for that though. Any ideas?
a5d6316
to
de16d09
Compare
de16d09
to
4025abc
Compare
4025abc
to
a2e010b
Compare
Ok, I have completed backend support for mentions in comments here. Front end changes are completely separate thing and I suppose they can be implemented&reviewed separetely. This PR can be tested by using the markdown syntax of mention directly: Can be reviewed now. |
post_link: link_to(post_page_title(post), post_path(post)).html_safe | ||
}.tap {|opts| | ||
if note.linked_object.instance_of?(Comment) | ||
opts.merge!(comment_path: "#{post_path(post)}##{note.linked_object.guid}".html_safe) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIRC path helpers take an anchor
option, post_path(post, anchor: note.linked_object.guid)
.
Sorry I don't remember, since we allow to bring new people into the conversation by allowing the commenter to mention anyone of their contacts, did we decide how to federate the comment in case the mentioned person never got the post initially? |
I walked quickly through the discussions and as far as I can see there was no decision. We allow to bring new people into the conversation for the public posts only. In this case we can add a mentioned person to the list of the comments subscribers and the comment will be federated to a new pod. After that, the new receiver pod may fetch the post if it's yet unknown. AFAIK, with the new federation it is nothing that we can't do. Also I'm a bit concerned with the way notifications work in this PR. In cases when we mention a participant, the participant received two notifications (e.g. AlsoCommented and MentionedInComment). That's not too elegant. I'll seek for a way to mute CommentOnPost and AlsoCommented in case when we send MentionedInComment to a person. Since these two issues are unresolved in the PR I'll set the status to [WIP] again. |
a2e010b
to
e02a8e9
Compare
This functionality is not something we support and it doesn't look it will be reintroduced soon. see discussion diaspora#6818 (comment)
# mentioned in comment. | ||
# | ||
# I left intact the Notifications::Mentioned.notify and didn't investigate the message dispatcher | ||
# but here I add parent_owner_id to the list so the filtering passes well. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't work, because private posts are received multiple times (to create the ShareVisibilities), so the mention-notification is only sent, if the user is in the recipient_ids
(the user who received the visibility). But comments are only received once (all others are ignored), because all visibilities are already there. See AlsoCommented
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I just tried to remove this method and everything seems to work fine. I made a mistake, thanks for pointing!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, that will not work either. If you use the parent-method, it will work for public posts, but for private-posts it will only send a notification, if the mentioned person is the first who receives it.
I'm not even sure if we should extend from Mentioned
, because the notify
-method would be simpler (than the parent) if you completely write the logic, instead of trying to use some parent logic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, that will not work either. If you use the parent-method, it will work for public posts, but for private-posts it will only send a notification, if the mentioned person is the first who receives it.
Ah, ok, that means my tests don't cover that :)
I'm not even sure if we should extend from Mentioned, because the notify-method would be simpler (than the parent) if you completely write the logic, instead of trying to use some parent logic.
Yes, that what I probably should do.
e02a8e9
to
8ed5bd4
Compare
This functionality is not something we support and it doesn't look it will be reintroduced soon. see discussion diaspora#6818 (comment)
8ed5bd4
to
048d6b0
Compare
This functionality is not something we support and it doesn't look it will be reintroduced soon. see discussion diaspora#6818 (comment)
@SuperTux88, I ended up with keeping inheritance. Please check 048d6b0 and tell me if you have any objections on the approach. |
create_notification(recipient.owner_id, mention, actor).email_the_user(mention, actor) | ||
end | ||
end | ||
|
||
def self.get_mentions(mentionable, recipient_user_ids) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we shouldn't use get_
, rubocop has a cop for this, but only for instance methods.
No there is no front-end at the moment. I'm also open to merge this on d-fr if needed, though I agree with @SuperTux88 that it doesn't bring anything more than the local testing as it brings nothing new in the UI. |
I think we can write a quick UI then. The code is already available. We can reuse the mention system used in the publisher. I won't be a big deal. |
It's not necessary. We can test mentions writing mention markdown clause in comments directly: |
I'm not sure our users will use it with the |
No, there isn't since I haven't ever started the UI part.
The syntax is It's enough even if one person (except of me) conducts testing if it is done well. If it is fine to test changes on the end-users right away, then why don't we just merge the PR to the develop branch right away without any more manual tests? |
Yes. I don't think that we have much benefit if we deploy this to a prod-pod and ask the users to test it. It is better when one person does a careful local testing with different users and as many edge cases the person can think of. This person can then also check the logs and check the expected behavior on every involved user. If you let multiple users test this in production, then they probably test it with their best friends (and always have a mutual connection), and that doesn't help much. But I can't stop somebody if he wants to test it in production, but I think local testing is easier and better. |
The users of our pod is already a small group. We are talking about less than 50 persons here ;)
Really, the UI is a one-liner. mention_view.js can be used as is, as long as there's a path to query mentionnable people. See, I'm not kidding, it's really a one-liner: this.mention = new app.views.PublisherMention({ el: this.$("#publisher_textarea_wrapper") }); That's it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Temporary review (but I wanted to give some quick feedback, so you can already fix some things, so we can merge this faster :) ).
I'll do a more careful review again on the weekend and also some testing.
def build_relayable_federation_entity(type, data={}, additional_xml_elements={}) | ||
attributes = FactoryGirl.attributes_for("#{type}_entity".to_sym, data) | ||
entity_class = "DiasporaFederation::Entities::#{type.capitalize}".constantize | ||
singlable_fields = attributes.keys - [:author_signature] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
still: signable_fields
false | ||
else # post | ||
object.author.local? | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can do:
object.is_a?(Post) && object.author.local?
Nit: Add a ?
to the method-name: do_check_subscribers?
or maybe only check_subscribers?
end | ||
|
||
def text_mentioning(user) | ||
"this is a text mentioning @{#{user.name}; #{user.diaspora_handle}} ... have fun testing!" | ||
def user_is_notified?(user, object) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can move this to the :be_mentioned_in
matcher.
before do | ||
allow(DiasporaFederation.callbacks).to receive(:trigger).and_call_original | ||
allow(DiasporaFederation.callbacks).to receive(:trigger).with( | ||
:fetch_private_key, remote_raphael.diaspora_handle |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need the private key of remote_raphael
to receive it.
end | ||
|
||
let(:mentioned_user) { | ||
FactoryGirl.build(:user).tap {|user| |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
text_mentioning
can also handle a person, so you don't need to create a user-object for this.
Also, if you are using it only in one test, you can assign it to a local variable in the test.
@@ -42,4 +44,18 @@ def get_response_for_user_agent(app, userAgent) | |||
body.close if body.respond_to?(:close) # avoids deadlock after 3 tests | |||
ActionDispatch::TestResponse.new(status, headers, body) | |||
end | |||
|
|||
def text_mentioning(*users) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: I would rename the parameter to people
, it works for both, users and people (because the user delegates to the person entity), but "people" is more generic and a user is also a person.
def subscribers | ||
super.tap {|subscribers| | ||
if parent.public? && parent.author.local? | ||
subscribers.concat(legitimate_mention_recipients.select(&:remote?)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is now somehow redundant. You check public?
here and also check public in legitimate_mention_recipients
(which calls people_allowed_to_be_mentioned). Also we never need to add subscribers for private posts (status messages and comments)
I would rename legitimate_mention_recipients
to add_mention_recipients
, then you can do something like this:
def add_mention_recipients(subscribers)
subscribers.concat(mentions.map(&:person).select(&:remote?)) if public?
end
And you can use this for comments and status messages.
Just some quick thoughts, maybe you have a better idea how to simplify this? :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see 39cd3ca
expect(TestNotification).to receive(:filter_mentions).with( | ||
Mention.where(mentions_container: status_message, person: [alice, bob].map(&:person)), | ||
status_message, | ||
:passthrough_argument |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This confused me, what about [alice.id, bob.id]
?
|
||
Notifications::Mentioned.notify(private_sm, [alice.id]) | ||
end | ||
it "doesnt't create notification if it was filtered out by filter_mentions" do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"doesn't ..."
expect(bob).to receive(:mail).with(Workers::Mail::Mentioned, bob.id, sm.author.id, sm.mentions.first.id) | ||
|
||
Notifications::Mentioned.notify(sm, []) | ||
def email_the_user(*) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can remove this.
subscribers.concat(legitimate_mention_recipients) | ||
} | ||
def add_mention_subscribers? | ||
super && author.local? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we really need the author.local?
here? When sending a StatusMessage the author is always local, and the subscribers of the StatusMessage are only added to the relayable-subscribers if the author is local.
Keep up the good work @cmrd-senya! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks really good so far, I think we can merge this soon :)
I also did some local testing now with different scenarios, and everything works as expected 👍
Can you please fix these last remarks and then squash your commits? :)
{ | ||
post_link: link_to(post_page_title(post), post_path(post)).html_safe | ||
}.tap {|opts| | ||
opts.merge!(comment_path: post_path(post, anchor: mentioned.guid).html_safe) if mentioned.instance_of?(Comment) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: while thinking about it, I think that would be better readable:
opts[:comment_path] = post_path(post, anchor: mentioned.guid).html_safe if mentioned.instance_of?(Comment)
one: "%{actors} has mentioned you in the post %{post_link}." | ||
other: "%{actors} have mentioned you in the post %{post_link}." | ||
mentioned_in_comment: | ||
one: "%{actors} has mentioned you in the <a href='%{comment_path}'>comment</a> to the post %{post_link}." |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: I think "... in a comment ..." is better here.
link = object_link(notification, notification_people_link(notification)) | ||
expect(link).to include("mentioned you in the") | ||
expect(link).to include("comment") | ||
expect(link).to include(post_path(status_message)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line is redundant with the line below. If you want to check if it contains a link to the post without comment-guid, you need to change this, otherwise remove it.
I have just merged #7223 and that causes some conflicts with this PR (sorry about that, but they should be easy to resolve :) ). When rebasing, can you please also remove |
This functionality is not something we support and it doesn't look it will be reintroduced soon. see discussion diaspora#6818 (comment)
Introduce Workers::Mail::NotifierBase to be a base for all appropriate mail workers to reduce code duplication
79d4924
to
1ddcf37
Compare
1ddcf37
to
33ad411
Compare
Remarks corrected, squashed, rebased, signed. Tests are passing, except of random cukes. |
Mentions in comments backend changes
Merged to develop branch, thanks @cmrd-senya :) |
Great! Thanks for your work either. I know it required many much efforts to review it multiple times. I'll do my best to produce a better code in my next contributions :) We also need UI to get #1851 fixed. I'm going to work on it soon. I don't really think it is a one-liner, as @AugierLe42e says, when I looked at it last time I didn't quite get it. But that's on my list anyway. |
Whoo whoo whoo! Thanks so much, @cmrd-senya, and thanks to all those who helped and reviewed. |
A massive thank you for this very needed work, this was definitely a missing key feature on diaspora. |
Same as @Flaburgan : huge thanks to @cmrd-senya and @SuperTux88.
As explained, as soon as there's a route that answers mentionnable people for a particular post, it's easy. Maybe not a one-liner, but 3 or 4 lines at most. As I refactored the mention system, I can help you or even write it myself. Just ping me on IRC if you need it. |
This is an early preliminary PR with backend-changes to support mentions in comments (#1851).
It introduces changes to the DB schema making Mention model polymorphic and allowing a mention to belong to a comment. It also creates module Diaspora::MentionsContainer which incapsulates the logic of an object that supports keeping mentions. Now it includes logic I picked from the StatusMessage model.
ATM the mentions in comments are supported to the extent of placing user profile links instead of mention code in the mobile version (it is so because the mobile version doesn't require javascript code changes).
Changes summary:
Drive-by refactorings:
StatusMessage#mentioned_people_names
out of the model. Create aPersonPresenter.people_names
method instead;user_params
method;TODO:
Ensure the filtered out mentions are replaced with links properlyImplement frontend javascript changes for comments publisher