Skip to content
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

filter: Implement has: filters on client side. #15492

Closed
wants to merge 1 commit into from

Conversation

thedeveloperr
Copy link
Collaborator

Prior to this commit has:link, has:attachment, has:image
filter couldn't be applied locally and deferred filtering to
web server. This commits make sure client filters all messages
it can instead of completely deferring to the server and hence
improve speed.

A tradeoff is also made to turn off local echo for has: narrows
as messages with link sent to has:link narrow were locally echoing
to another narrow and not appearing in the active has:link narrow.

Fixes: #6186.

Testing Plan:

GIFs or Screenshots:

@thedeveloperr
Copy link
Collaborator Author

thedeveloperr commented Jun 20, 2020

@timabbott created a separate PR for implementing the way you suggested in #15363 (comment)

I want to ask one thing, emoji like :zulip: are also images should the filter include them as well in has:image ? Zulip chat discussion for same: https://chat.zulip.org/#narrow/stream/6-frontend/topic/client.20side.20has.3A.20filter/near/910461

// We dont want to locally echo those has: messages
// because:
// 1. has:image, has:attachment are backend only syntax.
// 2. has:link do not get displayed in current narrow
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

has: link might not get displayed is the case, I think.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am able to send messages with link when has:link narrow is active.

return $('<div>').append(message.content).find(`${tagName}:first`).length > 0;
}

exports.does_message_have_link = function (message) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These functions can be better named as exports.message_has_link, etc.

@@ -16,6 +16,23 @@ function add_messages(messages, msg_list, opts) {
return render_info;
}

function does_message_content_have_html_tag(message, tagName) {
// creating a virtual dom to check for html tag in message.content.
return $('<div>').append(message.content).find(`${tagName}:first`).length > 0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious about GC related issues with this. I added a similar check in one of my PRs but I've a feeling JQuery might keep a reference to the created object internally and as a result we could end up with a lot of Query objects each time this function is called.

Do you know if that isn't the case here? I couldn't find anything relevant online so far.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will write a quick jsfiddle and monitor it's memory usage to see if it's the case.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

@aero31aero aero31aero left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks nice! The changes make sense, but I'm confused about the tests as they exist now. Maybe they could use some comments to explain what's the intention?

zrequire('Filter', 'js/filter');

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Accidentally removed this line?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes

@@ -715,6 +717,50 @@ run_test('predicate_basics', () => {
display_recipient: [{id: steve.user_id}, {id: me.user_id}],
}));
assert(!predicate({type: 'stream'}));

const img_msg = {
content: "<p><a href=\"\/user_uploads\/randompath\/test.jpeg\">test.jpeg<\/a><\/p>\n<div class=\"message_inline_image\"><a href=\"\/user_uploads\/randompath\/test.jpeg\" title=\"test.jpeg\"><img src=\"\/user_uploads\/randompath\/test.jpeg\"><\/a><\/div>",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could use strings contained within single quotes or backticks to avoid the escaping here... unless it was actually simpler to use these from our markdown_test_cases.json directly.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think back-tick approach is better.


predicate = get_predicate([['has', 'link']]);
let mockElem = $("<div>").append(link_msg.content);
mockElem.set_find_results("a:first", [$("<a>")]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused; how are these tests working? What is calling find on a div in the DOM to which you're appending these?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Umm wait I will clean it up. I think I mixed up.

assert(!predicate(no_has_filter_matching_msg));

predicate = get_predicate([['has', 'attachment']]);
mockElem = $("<div>").append(non_img_attachment_msg.content);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it intentional that you want to append to the existing JQuery object?

@thedeveloperr
Copy link
Collaborator Author

@aero31aero I have pushed new changes.

narrow_state.filter().has_operator('has')
) {
return;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd write the if statement with fewer lines

};

const non_img_attachment_msg = {
content: `<p><a href="/user_uploads/randompath/attachemnt.ext">attachment.ext</a></p>`,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

attachment is mispelled

$(link_msg.content).set_find_results(".message_inline_image", false);
assert(!predicate(link_msg));
$(no_has_filter_matching_msg.content).set_find_results(".message_inline_image", false);
assert(!predicate(no_has_filter_matching_msg));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this test might be more readable if saved the predicates like this:

check_has_link = get_predicate([['has', 'link']]);
...

And then setup 6 or so messages and just assertions like this:

assert(has_link(img_msg))
assert(has_attachment(img_msg))
assert(has_image(img_msg))

} else if (operand === 'attachment') {
return message_util.message_has_attachment(message);
}
return false; // is:whatever returns true
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment should use has.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and also say false, since that's what we're doing here :)

@timabbott
Copy link
Member

Posted a batch of comments; but this looks great otherwise!

@zulipbot zulipbot added size: L and removed size: XL labels Jun 21, 2020
@thedeveloperr thedeveloperr changed the title [WIP] filter: Implement has: filters on client side. filter: Implement has: filters on client side. Jun 21, 2020
@thedeveloperr thedeveloperr requested a review from timabbott June 21, 2020 13:01
@thedeveloperr
Copy link
Collaborator Author

Prior to this commit has:link, has:attachment, has:image
filter couldn't be applied locally and deferred filtering to
web server. This commits make sure client filters all messages
it can instead of completely deferring to the server and hence
improve speed.

A tradeoff is also made to turn off local echo for has: narrows
as messages with link sent to has:link narrow were locally echoing
to another narrow and not appearing in the active has:link narrow.

Fixes: zulip#6186.
@thedeveloperr
Copy link
Collaborator Author

@timabbott
Copy link
Member

I merged as 02ea52f, after reworking the local echo logic to feel more intuitive; take a look and let me know what you think.

Thanks @thedeveloperr!

@timabbott timabbott closed this Jun 21, 2020
@thedeveloperr
Copy link
Collaborator Author

@timabbott I want to understand more about the reason of local echo change. I mean it feels counter intuitive and logic more hidden.

@timabbott
Copy link
Member

Oh, that was because filter.can_apply_locally is the authoritative function for deciding whether the client supports correctly appending a message to a narrow, and it's a better abstraction boundary to have that function handle all questions around that, rather than having another function handle this corner case around has:. Which is somewhat visible with how complex the if statement you needed to add to do it in try_deliver_locally, duplicating some logic around can_apply_locally.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add web-app filters for has:link, has:attachment, and has:image.
4 participants