mirror of https://github.com/zulip/zulip.git
recent_topics: Toggle topic display according to filters.
This commit is contained in:
parent
bc7136590a
commit
4f1b7542ed
|
@ -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>';
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 () {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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%);
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue