recent_topics: Toggle topic display according to filters.

This commit is contained in:
Aman Agrawal 2020-05-23 12:34:51 +05:30 committed by Tim Abbott
parent bc7136590a
commit 4f1b7542ed
6 changed files with 380 additions and 6 deletions

View File

@ -1,6 +1,8 @@
zrequire('message_util');
set_global('$', global.make_zjquery());
set_global('$', global.make_zjquery({
silent: true,
}));
set_global('hashchange', {
exit_overlay: () => {},
});
@ -25,7 +27,13 @@ set_global('timerender', {
});
set_global('unread', {
unread_topic_counter: {
get: () => { return 1; },
get: (stream_id, topic) => {
if (stream_id === 1 && topic === "topic-1") {
// Only stream1, topic-1 is read.
return 0;
}
return 1;
},
},
});
set_global('hash_util', {
@ -47,8 +55,8 @@ const stream1 = 1;
const stream2 = 2;
const stream3 = 3;
// Topics in the stream
const topic1 = "topic-1"; // No Other sender
// Topics in the stream, all unread except topic1 & stream1.
const topic1 = "topic-1"; // No Other sender & read.
const topic2 = "topic-2"; // Other sender
const topic3 = "topic-3"; // User not present
const topic4 = "topic-4"; // User not present
@ -268,7 +276,7 @@ run_test("test_recent_topics_launch", () => {
stream_id: 1,
stream: 'stream1',
topic: 'topic-1',
unread_count: 1,
unread_count: 0,
last_msg_time: 'Just now',
stream_url: 'https://www.example.com',
topic_url: 'https://www.example.com',
@ -295,6 +303,274 @@ run_test("test_recent_topics_launch", () => {
assert.equal(rt.inplace_rerender('stream_unknown:topic_unknown'), false);
});
run_test('test_filter_all', () => {
// Just tests inplace rerender of a message
// in All topics filter.
let expected = {
count_senders: 0,
hidden: false,
last_msg_time: 'Just now',
senders: [1, 2],
stream: 'stream1',
stream_id: 1,
stream_url: 'https://www.example.com',
topic: 'topic-1',
topic_url: 'https://www.example.com',
unread_count: 0,
};
global.stub_templates(function (template_name, data) {
assert.equal(template_name, 'recent_topic_row');
assert.deepEqual(data, expected);
return '<recent_topics row stub>';
});
// topic is not muted
const rt = zrequire('recent_topics');
rt.process_messages([messages[0]]);
expected = {
stream_id: 1,
stream: 'stream1',
topic: 'topic-7',
unread_count: 1,
last_msg_time: 'Just now',
stream_url: 'https://www.example.com',
topic_url: 'https://www.example.com',
hidden: true,
senders: [1, 2],
count_senders: 0,
};
// topic is muted (=== hidden)
rt.process_messages([messages[9]]);
});
run_test('test_filter_unread', () => {
// Tests rerender of all topics when filter changes to "unread".
const expected = {
recent_topics: [
{
count_senders: 0,
hidden: true,
last_msg_time: 'Just now',
senders: [1, 2],
stream: 'stream1',
stream_id: 1,
stream_url: 'https://www.example.com',
topic: 'topic-7',
topic_url: 'https://www.example.com',
unread_count: 1,
},
{
count_senders: 0,
hidden: false,
last_msg_time: 'Just now',
senders: [1, 2],
stream: 'stream1',
stream_id: 1,
stream_url: 'https://www.example.com',
topic: 'topic-6',
topic_url: 'https://www.example.com',
unread_count: 1,
},
{
count_senders: 0,
hidden: false,
last_msg_time: 'Just now',
senders: [1, 2],
stream: 'stream1',
stream_id: 1,
stream_url: 'https://www.example.com',
topic: 'topic-5',
topic_url: 'https://www.example.com',
unread_count: 1,
},
{
count_senders: 0,
hidden: false,
last_msg_time: 'Just now',
senders: [1, 2],
stream: 'stream1',
stream_id: 1,
stream_url: 'https://www.example.com',
topic: 'topic-4',
topic_url: 'https://www.example.com',
unread_count: 1,
},
{
count_senders: 0,
hidden: false,
last_msg_time: 'Just now',
senders: [1, 2],
stream: 'stream1',
stream_id: 1,
stream_url: 'https://www.example.com',
topic: 'topic-3',
topic_url: 'https://www.example.com',
unread_count: 1,
},
{
count_senders: 0,
hidden: false,
last_msg_time: 'Just now',
senders: [1, 2],
stream: 'stream1',
stream_id: 1,
stream_url: 'https://www.example.com',
topic: 'topic-2',
topic_url: 'https://www.example.com',
unread_count: 1,
},
{
count_senders: 0,
hidden: true, // This is the only thing we are testing here
last_msg_time: 'Just now',
senders: [1, 2],
stream: 'stream1',
stream_id: 1,
stream_url: 'https://www.example.com',
topic: 'topic-1',
topic_url: 'https://www.example.com',
unread_count: 0,
},
],
};
const rt = zrequire('recent_topics');
global.stub_templates(function () {
return '<recent_topics table stub>';
});
rt.process_messages(messages);
$('#recent_topics_filter_buttons').removeClass('btn-recent-selected');
global.stub_templates(function (template_name, data) {
assert.equal(template_name, 'recent_topics_table');
assert.deepEqual(data, expected);
return '<recent_topics table stub>';
});
rt.set_filter('unread');
// Unselect "unread" filter by clicking twice.
expected.recent_topics[6].hidden = false;
rt.set_filter('unread');
// Now clicking "all" filter should have no change to expected data.
rt.set_filter('all');
});
run_test('test_filter_participated', () => {
// Tests rerender of all topics when filter changes to "unread".
const expected = {
recent_topics: [
{
count_senders: 0,
hidden: true,
last_msg_time: 'Just now',
senders: [1, 2],
stream: 'stream1',
stream_id: 1,
stream_url: 'https://www.example.com',
topic: 'topic-7',
topic_url: 'https://www.example.com',
unread_count: 1,
},
{
count_senders: 0,
hidden: false,
last_msg_time: 'Just now',
senders: [1, 2],
stream: 'stream1',
stream_id: 1,
stream_url: 'https://www.example.com',
topic: 'topic-6',
topic_url: 'https://www.example.com',
unread_count: 1,
},
{
count_senders: 0,
hidden: false,
last_msg_time: 'Just now',
senders: [1, 2],
stream: 'stream1',
stream_id: 1,
stream_url: 'https://www.example.com',
topic: 'topic-5',
topic_url: 'https://www.example.com',
unread_count: 1,
},
{
count_senders: 0,
hidden: true,
last_msg_time: 'Just now',
senders: [1, 2],
stream: 'stream1',
stream_id: 1,
stream_url: 'https://www.example.com',
topic: 'topic-4',
topic_url: 'https://www.example.com',
unread_count: 1,
},
{
count_senders: 0,
hidden: true,
last_msg_time: 'Just now',
senders: [1, 2],
stream: 'stream1',
stream_id: 1,
stream_url: 'https://www.example.com',
topic: 'topic-3',
topic_url: 'https://www.example.com',
unread_count: 1,
},
{
count_senders: 0,
hidden: false,
last_msg_time: 'Just now',
senders: [1, 2],
stream: 'stream1',
stream_id: 1,
stream_url: 'https://www.example.com',
topic: 'topic-2',
topic_url: 'https://www.example.com',
unread_count: 1,
},
{
count_senders: 0,
hidden: false,
last_msg_time: 'Just now',
senders: [1, 2],
stream: 'stream1',
stream_id: 1,
stream_url: 'https://www.example.com',
topic: 'topic-1',
topic_url: 'https://www.example.com',
unread_count: 0,
},
],
};
const rt = zrequire('recent_topics');
global.stub_templates(function () {
return '<recent_topics table stub>';
});
rt.process_messages(messages);
$('#recent_topics_filter_buttons').removeClass('btn-recent-selected');
global.stub_templates(function (template_name, data) {
assert.equal(template_name, 'recent_topics_table');
assert.deepEqual(data, expected);
return '<recent_topics table stub>';
});
rt.set_filter('participated');
expected.recent_topics[3].hidden = false;
expected.recent_topics[4].hidden = false;
rt.set_filter('all');
});
// template rendering is tested in test_recent_topics_launch.
global.stub_templates(function () {
return '<recent_topics table stub>';

View File

@ -355,6 +355,31 @@ exports.initialize = function () {
unread_ops.mark_topic_as_read(stream_id, topic);
});
$('body').on('click', '.btn-recent-filters', function (e) {
e.stopPropagation();
recent_topics.set_filter(e.currentTarget.dataset.filter);
});
// Search for all table rows (this combines stream & topic names)
$('body').on('keyup', '#recent_topics_search', _.debounce(function () {
// take all rows and slice off the header.
const $rows = $('.recent_topics_table tr').slice(1);
// split the search text around whitespace(s).
// eg: "Denamark recent" -> ["Denamrk", "recent"]
const search_keywords = $.trim($(this).val()).split(/\s+/);
// turn the search keywords into word boundry groups
// eg: ["Denamrk", "recent"] -> "^(?=.*\bDenmark\b)(?=.*\brecent\b).*$"
const val = '^(?=.*\\b' + search_keywords.join('\\b)(?=.*\\b') + ').*$';
const reg = RegExp(val, 'i'); // i for ignorecase
let text;
$rows.show().filter(function () {
text = $(this).text().replace(/\s+/g, ' ');
return !reg.test(text);
}).hide();
// Wait for user to go idle before initiating search.
}, 300));
// RECIPIENT BARS
function get_row_id_for_narrowing(narrow_link_elem) {

View File

@ -4,6 +4,7 @@ const topics = new Map(); // Key is stream-id:topic.
// Sets the number of avatars to display.
// Rest of the avatars, if present, are displayed as {+x}
const MAX_AVATAR = 4;
let filters = new Set();
exports.process_messages = function (messages) {
// Since a complete re-render is expensive, we
@ -78,8 +79,16 @@ function format_topic(topic_data) {
const topic = last_msg.topic;
const time = new XDate(last_msg.timestamp * 1000);
const last_msg_time = timerender.last_seen_status_from_date(time);
// We hide the row according to filters or if it's muted.
let hidden = muting.is_topic_muted(stream_id, topic);
const unread_count = unread.unread_topic_counter.get(stream_id, topic);
const hidden = muting.is_topic_muted(stream_id, topic);
if (unread_count === 0 && filters.has('unread')) {
hidden = true;
}
if (!topic_data.participated && filters.has('participated')) {
hidden = true;
}
// Display in most recent sender first order
const all_senders = recent_senders.get_topic_recent_senders(stream_id, topic);
@ -168,6 +177,20 @@ exports.update_topic_unread_count = function (message) {
exports.inplace_rerender(topic_key);
};
exports.set_filter = function (filter) {
const filter_elem = $('#recent_topics_filter_buttons')
.find('[data-filter="' + filter + '"]');
if (filter === 'all' && filters.size !== 0) {
filters = new Set();
} else if (filter_elem.hasClass('btn-recent-selected')) {
filters.delete(filter);
} else {
filters.add(filter);
}
exports.complete_rerender();
};
exports.complete_rerender = function () {
// NOTE: This function is grows expensive with
// number of topics. Only call when necessary.
@ -176,6 +199,18 @@ exports.complete_rerender = function () {
recent_topics: format_all_topics(),
});
$('#recent_topics_table').html(rendered_body);
if (filters.size === 0) {
$('#recent_topics_filter_buttons')
.find('[data-filter="all"]')
.addClass('btn-recent-selected');
} else {
for (const filter of filters) {
$('#recent_topics_filter_buttons')
.find('[data-filter="' + filter + '"]')
.addClass('btn-recent-selected');
}
}
};
exports.launch = function () {

View File

@ -401,6 +401,17 @@ on a dark background, and don't change the dark labels dark either. */
background-color: hsla(0, 0%, 0%, 0.2);
}
.btn-recent-filters {
background-color: hsl(0, 0%, 26%);
border-color: hsl(0, 0%, 0%);
color: hsl(0, 0%, 100%);
}
.btn-recent-selected {
background-color: hsl(0, 0%, 0%) !important;
}
thead,
.drafts-container .drafts-header,
.recent_topics_container .recent_topics_header,

View File

@ -57,6 +57,27 @@
padding: 15px;
overflow: auto;
#recent_topics_filter_buttons {
margin: 0 10px 0 10px;
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
}
#recent_topics_search {
margin: 0 0 10px 0px;
flex-grow: 1;
}
.btn-recent-filters {
border-radius: 40px;
margin: 0 5px 10px 0;
}
.btn-recent-selected {
background-color: hsl(0, 11%, 93%);
}
thead {
background-color: hsl(0, 0%, 27%);
color: hsl(0, 0%, 100%);

View File

@ -1,3 +1,9 @@
<div id="recent_topics_filter_buttons" class="btn-group" role="group">
<button data-filter="all" type="button" class="btn btn-default btn-recent-filters">{{t 'All' }}</button>
<button data-filter="unread" type="button" class="btn btn-default btn-recent-filters">{{t 'Unread' }}</button>
<button data-filter="participated" type="button" class="btn btn-default btn-recent-filters">{{t 'Participated' }}</button>
<input type="text" id="recent_topics_search" placeholder="{{t 'Search stream / topic' }}">
</div>
<table class="table table-responsive table-hover">
<thead>
<tr>