diff --git a/frontend_tests/node_tests/recent_topics.js b/frontend_tests/node_tests/recent_topics.js index 355c4ec505..2ff4728d6a 100644 --- a/frontend_tests/node_tests/recent_topics.js +++ b/frontend_tests/node_tests/recent_topics.js @@ -74,6 +74,16 @@ set_global('list_render', { formatted_topics.push(opts.modifier(item)); opts.filter.predicate(item); } + // Just for coverage, the mechanisms + // are tested in list_render + if (mapped_topic_values.length >= 2) { + opts.sort_fields.stream_sort(mapped_topic_values[0], mapped_topic_values[1]); + opts.sort_fields.stream_sort(mapped_topic_values[1], mapped_topic_values[0]); + opts.sort_fields.stream_sort(mapped_topic_values[0], mapped_topic_values[0]); + opts.sort_fields.topic_sort(mapped_topic_values[0], mapped_topic_values[1]); + opts.sort_fields.topic_sort(mapped_topic_values[1], mapped_topic_values[0]); + opts.sort_fields.topic_sort(mapped_topic_values[0], mapped_topic_values[0]); + } return list_render; }, hard_redraw: noop, @@ -88,6 +98,7 @@ set_global('list_render', { const stream1 = 1; const stream2 = 2; const stream3 = 3; +const stream4 = 4; // Topics in the stream, all unread except topic1 & stream1. const topic1 = "topic-1"; // No Other sender & read. @@ -99,6 +110,7 @@ const topic6 = "topic-6"; // other sender const topic7 = "topic-7"; // muted topic const topic8 = "topic-8"; const topic9 = "topic-9"; +const topic10 = "topic-10"; set_global('muting', { is_topic_muted: (stream_id, topic) => { @@ -221,6 +233,16 @@ messages[9] = { type: 'stream', }; +// a message of stream4 +messages[10] = { + stream_id: stream4, + stream: 'stream4', + id: id += 1, + topic: topic10, + sender_id: sender1, + type: 'stream', +}; + function generate_topic_data(topic_info_array) { // Since most of the fields are common, this function helps generate fixtures // with non common fields. @@ -355,6 +377,7 @@ run_test('test_filter_unread', () => { const row_data = generate_topic_data([ // stream_id, topic, unread_count, muted, participated + [4, 'topic-10', 1, false, true], [1, 'topic-7', 1, true, true], [1, 'topic-6', 1, false, true], [1, 'topic-5', 1, false, true], @@ -420,6 +443,7 @@ run_test('test_filter_participated', () => { const row_data = generate_topic_data([ // stream_id, topic, unread_count, muted, participated + [4, 'topic-10', 1, false, true], [1, 'topic-7', 1, true, true], [1, 'topic-6', 1, false, true], [1, 'topic-5', 1, false, true], @@ -499,19 +523,19 @@ run_test('basic assertions', () => { generate_topic_data([[1, 'topic-7', 1, false, true]]); rt.process_messages([messages[9]]); // Check for expected lengths. - // total 7 topics, 1 muted - assert.equal(all_topics.size, 7); + // total 8 topics, 1 muted + assert.equal(all_topics.size, 8); assert.equal(Array.from(all_topics.keys()).toString(), - '1:topic-7,1:topic-6,1:topic-5,1:topic-4,1:topic-3,1:topic-2,1:topic-1'); + '4:topic-10,1:topic-7,1:topic-6,1:topic-5,1:topic-4,1:topic-3,1:topic-2,1:topic-1'); rt.process_message({ type: 'private', }); // Private msgs are not processed. - assert.equal(all_topics.size, 7); + assert.equal(all_topics.size, 8); assert.equal(Array.from(all_topics.keys()).toString(), - '1:topic-7,1:topic-6,1:topic-5,1:topic-4,1:topic-3,1:topic-2,1:topic-1'); + '4:topic-10,1:topic-7,1:topic-6,1:topic-5,1:topic-4,1:topic-3,1:topic-2,1:topic-1'); // participated verify_topic_data(all_topics, stream1, topic1, messages[0].id, true); @@ -533,7 +557,7 @@ run_test('basic assertions', () => { all_topics = rt.get(); assert.equal(Array.from(all_topics.keys()).toString(), - '1:topic-3,1:topic-7,1:topic-6,1:topic-5,1:topic-4,1:topic-2,1:topic-1'); + '1:topic-3,4:topic-10,1:topic-7,1:topic-6,1:topic-5,1:topic-4,1:topic-2,1:topic-1'); verify_topic_data(all_topics, stream1, topic3, id, true); // Send new message to topic7 (muted) @@ -548,7 +572,7 @@ run_test('basic assertions', () => { all_topics = rt.get(); assert.equal(Array.from(all_topics.keys()).toString(), - '1:topic-7,1:topic-3,1:topic-6,1:topic-5,1:topic-4,1:topic-2,1:topic-1'); + '1:topic-7,1:topic-3,4:topic-10,1:topic-6,1:topic-5,1:topic-4,1:topic-2,1:topic-1'); // update_topic_is_muted now relies on external libraries completely // so we don't need to check anythere here. @@ -607,7 +631,7 @@ run_test('test_topic_edit', () => { let all_topics = rt.get(); assert.equal(Array.from(all_topics.keys()).toString(), - '1:topic-7,1:topic-6,1:topic-5,1:topic-4,1:topic-3,1:topic-2,1:topic-1'); + '4:topic-10,1:topic-7,1:topic-6,1:topic-5,1:topic-4,1:topic-3,1:topic-2,1:topic-1'); ////////////////// test change topic ////////////////// verify_topic_data(all_topics, stream1, topic6, messages[8].id, true); diff --git a/static/js/recent_topics.js b/static/js/recent_topics.js index def10de6d0..0a6f4e2247 100644 --- a/static/js/recent_topics.js +++ b/static/js/recent_topics.js @@ -249,6 +249,29 @@ exports.update_filters_view = function () { topics_widget.hard_redraw(); }; + +function stream_sort(a, b) { + const a_stream = message_store.get(a.last_msg_id).stream; + const b_stream = message_store.get(b.last_msg_id).stream; + if (a_stream > b_stream) { + return 1; + } else if (a_stream === b_stream) { + return 0; + } + return -1; +} + +function topic_sort(a, b) { + const a_topic = message_store.get(a.last_msg_id).topic; + const b_topic = message_store.get(b.last_msg_id).topic; + if (a_topic > b_topic) { + return 1; + } else if (a_topic === b_topic) { + return 0; + } + return -1; +} + exports.complete_rerender = function () { if (!overlays.recent_topics_open()) { return false; @@ -272,6 +295,7 @@ exports.complete_rerender = function () { topics_widget = list_render.create(container, mapped_topic_values, { name: "recent_topics_table", + parent_container: $("#recent_topics_table"), modifier: function (item) { return render_recent_topic_row(format_topic(item)); }, @@ -282,6 +306,10 @@ exports.complete_rerender = function () { return !is_topic_hidden(topic_data); }, }, + sort_fields: { + stream_sort: stream_sort, + topic_sort: topic_sort, + }, html_selector: get_topic_row, }); }; diff --git a/static/styles/recent_topics.scss b/static/styles/recent_topics.scss index a6c80a3a46..6d7846ab4f 100644 --- a/static/styles/recent_topics.scss +++ b/static/styles/recent_topics.scss @@ -76,8 +76,6 @@ position: sticky; top: 0; z-index: 1000; - background-color: hsl(0, 0%, 27%); - color: hsl(0, 0%, 100%); } #recent_topics_filter_buttons { @@ -151,5 +149,42 @@ .recent_avatars_others { background-color: hsl(205.2, 76.5%, 50%); } + + thead th { + background-color: inherit; + color: inherit; + border-top: 1px solid hsla(0, 0%, 0%, 0.2) !important; + border-bottom: 1px solid hsla(0, 0%, 0%, 0.2) !important; + + &.active::after, + &[data-sort]:hover::after { + content: " \f0d8"; + white-space: pre; + display: inline-block; + position: absolute; + padding-top: 3px; + font: normal normal normal 12px/1 FontAwesome; + font-size: inherit; + } + + &.active { + opacity: 1; + transition: opacity 100ms ease-out; + + &.descend::after { + content: " \f0d7"; + } + } + + &[data-sort]:hover { + cursor: pointer; + background-color: hsla(0, 0%, 0%, 0.05); + transition: background-color 100ms ease-in-out; + + &:not(.active)::after { + opacity: 0.3; + } + } + } } } diff --git a/static/templates/recent_topics_table.hbs b/static/templates/recent_topics_table.hbs index 39c47766cd..1e7cdb08fe 100644 --- a/static/templates/recent_topics_table.hbs +++ b/static/templates/recent_topics_table.hbs @@ -11,11 +11,11 @@ - - + + - +
{{t 'Stream' }}{{t 'Topic' }}{{t 'Stream' }}{{t 'Topic' }} {{t 'Actions' }} {{t 'Participants' }}{{t 'Last message' }}{{t 'Last message' }}