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
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 60 additions & 1 deletion frontend_tests/node_tests/filter.js
Original file line number Diff line number Diff line change
@@ -3,6 +3,9 @@ zrequire('unread');
zrequire('stream_data');
zrequire('people');
set_global('Handlebars', global.make_handlebars());
global.stub_out_jquery();
set_global('$', global.make_zjquery());
zrequire('message_util', 'js/message_util');
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

set_global('message_store', {});
@@ -121,7 +124,7 @@ run_test('basics', () => {
];
filter = new Filter(operators);
assert(filter.has_operator('has'));
assert(!filter.can_apply_locally());
assert(filter.can_apply_locally());
assert(!filter.includes_full_stream_history());
assert(!filter.can_mark_messages_read());
assert(!filter.is_personal_filter());
@@ -715,6 +718,62 @@ 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><div class="message_inline_image"><a href="/user_uploads/randompath/test.jpeg" title="test.jpeg"><img src="/user_uploads/randompath/test.jpeg"></a></div>`,
};

const link_msg = {
content: `<p><a href="http://chat.zulip.org">chat.zulip.org</a></p>`,
};

const non_img_attachment_msg = {
content: `<p><a href="/user_uploads/randompath/attachment.ext">attachment.ext</a></p>`,
};

const no_has_filter_matching_msg = {
content: "<p>Testing</p>",
};

predicate = get_predicate([['has', 'non_valid_operand']]);
assert(!predicate(img_msg));
assert(!predicate(non_img_attachment_msg));
assert(!predicate(link_msg));
assert(!predicate(no_has_filter_matching_msg));

// HTML content of message is used to determine if image have link, image or attachment.
// We are using jquery to parse the html and find existence of relevant tags/elements.
// In tests we need to stub the calls to jquery so using zjquery's .set_find_results method.
const has_link = get_predicate([['has', 'link']]);
$(img_msg.content).set_find_results("a", [$("<a>")]);
assert(has_link(img_msg));
$(non_img_attachment_msg.content).set_find_results("a", [$("<a>")]);
assert(has_link(non_img_attachment_msg));
$(link_msg.content).set_find_results("a", [$("<a>")]);
assert(has_link(link_msg));
$(no_has_filter_matching_msg.content).set_find_results("a", false);
assert(!has_link(no_has_filter_matching_msg));

const has_attachment = get_predicate([['has', 'attachment']]);
$(img_msg.content).set_find_results("a[href^='/user_uploads']", [$("<a>")]);
assert(has_attachment(img_msg));
$(non_img_attachment_msg.content).set_find_results("a[href^='/user_uploads']", [$("<a>")]);
assert(has_attachment(non_img_attachment_msg));
$(link_msg.content).set_find_results("a[href^='/user_uploads']", false);
assert(!has_attachment(link_msg));
$(no_has_filter_matching_msg.content).set_find_results("a[href^='/user_uploads']", false);
assert(!has_attachment(no_has_filter_matching_msg));

const has_image = get_predicate([['has', 'image']]);
$(img_msg.content).set_find_results(".message_inline_image", [$("<img>")]);
assert(has_image(img_msg));
$(non_img_attachment_msg.content).set_find_results(".message_inline_image", false);
assert(!has_image(non_img_attachment_msg));
$(link_msg.content).set_find_results(".message_inline_image", false);
assert(!has_image(link_msg));
$(no_has_filter_matching_msg.content).set_find_results(".message_inline_image", false);
assert(!has_image(no_has_filter_matching_msg));

});

run_test('negated_predicates', () => {
11 changes: 11 additions & 0 deletions static/js/echo.js
Original file line number Diff line number Diff line change
@@ -142,6 +142,17 @@ exports.is_slash_command = function (content) {


exports.try_deliver_locally = function (message_request) {

// Even though has: filers can be applied locally,
// 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.

// if local echo is on.
if (narrow_state.active() && narrow_state.filter() && 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


if (markdown.contains_backend_only_syntax(message_request.content)) {
return;
}
14 changes: 10 additions & 4 deletions static/js/filter.js
Original file line number Diff line number Diff line change
@@ -46,6 +46,15 @@ function message_in_home(message) {

function message_matches_search_term(message, operator, operand) {
switch (operator) {
case 'has':
if (operand === 'image') {
return message_util.message_has_image(message);
} else if (operand === 'link') {
return message_util.message_has_link(message);
} else if (operand === 'attachment') {
return message_util.message_has_attachment(message);
}
return false; // has:something_else returns false
case 'is':
if (operand === 'private') {
return message.type === 'private';
@@ -630,10 +639,7 @@ Filter.prototype = {
}

if (this.has_operator('has')) {
// See #6186 to see why we currently punt on 'has:foo'
// queries. This can be fixed, there are just some random
// complications that make it non-trivial.
return false;
return true;
}

if (this.has_operator('streams') ||
12 changes: 12 additions & 0 deletions static/js/message_util.js
Original file line number Diff line number Diff line change
@@ -16,6 +16,18 @@ function add_messages(messages, msg_list, opts) {
return render_info;
}

exports.message_has_link = function (message) {
return $(message.content).find(`a`).length > 0;
};

exports.message_has_image = function (message) {
return $(message.content).find(`.message_inline_image`).length > 0;
};

exports.message_has_attachment = function (message) {
return $(message.content).find(`a[href^='/user_uploads']`).length > 0;
};

exports.add_old_messages = function (messages, msg_list) {
return add_messages(messages, msg_list, {messages_are_new: false});
};