From bc7136590a8c665e31aea418a520fdcc9270fa22 Mon Sep 17 00:00:00 2001 From: Aman Agrawal Date: Sat, 23 May 2020 00:34:03 +0530 Subject: [PATCH] recent_topics: Add avatars of recent senders to topic. --- frontend_tests/node_tests/people.js | 20 +++++++++++ frontend_tests/node_tests/recent_topics.js | 20 +++++++++++ static/js/people.js | 10 ++++++ static/js/recent_topics.js | 11 ++++++ static/styles/recent_topics.scss | 39 ++++++++++++++++++++++ static/templates/recent_topic_row.hbs | 12 +++++++ 6 files changed, 112 insertions(+) diff --git a/frontend_tests/node_tests/people.js b/frontend_tests/node_tests/people.js index d61adda4fe..87aa8421c1 100644 --- a/frontend_tests/node_tests/people.js +++ b/frontend_tests/node_tests/people.js @@ -598,6 +598,26 @@ run_test('message_methods', () => { assert.equal(people.small_avatar_url_for_person(maria), 'https://secure.gravatar.com/avatar/md5-athens@example.com?d=identicon&s=50'); + + assert.deepEqual(people.sender_info_with_small_avatar_urls_for_sender_ids([30]), [ + { + avatar_url_small: 'https://secure.gravatar.com/avatar/md5-me@example.com?d=identicon&s=50', + email: 'me@example.com', + full_name: 'Me the Third', + is_admin: false, + is_bot: false, + is_guest: false, + profile_data: { + 3: { + rendered_value: '

Field value

', + value: 'Field value', + }, + }, + timezone: 'US/Pacific', + user_id: 30, + }, + ]); + let message = { type: 'private', display_recipient: [ diff --git a/frontend_tests/node_tests/recent_topics.js b/frontend_tests/node_tests/recent_topics.js index 44c7ae68cb..0efb38bcbd 100644 --- a/frontend_tests/node_tests/recent_topics.js +++ b/frontend_tests/node_tests/recent_topics.js @@ -13,6 +13,9 @@ set_global('people', { is_my_user_id: function (id) { return id === 1; }, + sender_info_with_small_avatar_urls_for_sender_ids: (ids) => { + return ids; + }, }); set_global('XDate', zrequire('XDate', 'xdate')); set_global('timerender', { @@ -33,6 +36,9 @@ set_global('hash_util', { return "https://www.example.com"; }, }); +set_global('recent_senders', { + get_topic_recent_senders: () => { return [1, 2]; }, +}); // Custom Data @@ -195,6 +201,8 @@ run_test("test_recent_topics_launch", () => { stream_url: 'https://www.example.com', topic_url: 'https://www.example.com', hidden: true, + senders: [1, 2], + count_senders: 0, }, { stream_id: 1, @@ -205,6 +213,8 @@ run_test("test_recent_topics_launch", () => { stream_url: 'https://www.example.com', topic_url: 'https://www.example.com', hidden: false, + senders: [1, 2], + count_senders: 0, }, { stream_id: 1, @@ -215,6 +225,8 @@ run_test("test_recent_topics_launch", () => { stream_url: 'https://www.example.com', topic_url: 'https://www.example.com', hidden: false, + senders: [1, 2], + count_senders: 0, }, { stream_id: 1, @@ -225,6 +237,8 @@ run_test("test_recent_topics_launch", () => { stream_url: 'https://www.example.com', topic_url: 'https://www.example.com', hidden: false, + senders: [1, 2], + count_senders: 0, }, { stream_id: 1, @@ -235,6 +249,8 @@ run_test("test_recent_topics_launch", () => { stream_url: 'https://www.example.com', topic_url: 'https://www.example.com', hidden: false, + senders: [1, 2], + count_senders: 0, }, { stream_id: 1, @@ -245,6 +261,8 @@ run_test("test_recent_topics_launch", () => { stream_url: 'https://www.example.com', topic_url: 'https://www.example.com', hidden: false, + senders: [1, 2], + count_senders: 0, }, { stream_id: 1, @@ -255,6 +273,8 @@ run_test("test_recent_topics_launch", () => { stream_url: 'https://www.example.com', topic_url: 'https://www.example.com', hidden: false, + senders: [1, 2], + count_senders: 0, }, ], }; diff --git a/static/js/people.js b/static/js/people.js index 1db925ca42..a6c6a32c7d 100644 --- a/static/js/people.js +++ b/static/js/people.js @@ -605,6 +605,16 @@ exports.small_avatar_url_for_person = function (person) { return gravatar_url_for_email(person.email); }; +exports.sender_info_with_small_avatar_urls_for_sender_ids = function (sender_ids) { + const senders_info = []; + for (const id of sender_ids) { + const sender = {...exports.get_by_user_id(id)}; + sender.avatar_url_small = exports.small_avatar_url_for_person(sender); + senders_info.push(sender); + } + return senders_info; +}; + exports.small_avatar_url = function (message) { // Try to call this function in all places where we need 25px // avatar images, so that the browser can help diff --git a/static/js/recent_topics.js b/static/js/recent_topics.js index 6985ac2f4c..a6abe4acdd 100644 --- a/static/js/recent_topics.js +++ b/static/js/recent_topics.js @@ -1,6 +1,9 @@ const render_recent_topics_body = require('../templates/recent_topics_table.hbs'); const render_recent_topic_row = require('../templates/recent_topic_row.hbs'); 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; exports.process_messages = function (messages) { // Since a complete re-render is expensive, we @@ -77,6 +80,12 @@ function format_topic(topic_data) { const last_msg_time = timerender.last_seen_status_from_date(time); const unread_count = unread.unread_topic_counter.get(stream_id, topic); const hidden = muting.is_topic_muted(stream_id, topic); + + // Display in most recent sender first order + const all_senders = recent_senders.get_topic_recent_senders(stream_id, topic); + const senders = all_senders.slice(-MAX_AVATAR); + const senders_info = people.sender_info_with_small_avatar_urls_for_sender_ids(senders); + return { stream_id: stream_id, stream: stream, @@ -86,6 +95,8 @@ function format_topic(topic_data) { stream_url: hash_util.by_stream_uri(stream_id), topic_url: hash_util.by_stream_topic_uri(stream_id, topic), hidden: hidden, + senders: senders_info, + count_senders: Math.max(0, all_senders.length - MAX_AVATAR), }; } diff --git a/static/styles/recent_topics.scss b/static/styles/recent_topics.scss index b0fde1b629..79d71e62c2 100644 --- a/static/styles/recent_topics.scss +++ b/static/styles/recent_topics.scss @@ -69,5 +69,44 @@ // is set to header's width. width: 1px; } + + .recent_avatars { + display: inline-flex; /* Causes LI items to display in row. */ + list-style-type: none; + margin: auto; /* Centers vertically / horizontally in flex container. */ + height: 15px !important; + + /* + By using the row-reverse layout, the visual ordering will be opposite of + the DOM ordering. This will allows us to stack the items in the opposite + direction of the natural stacking order without having to mess with the + zIndex value. The MAJOR DOWNSIDE is that the HTML itself now reads + backwards, which super janky. + */ + flex-direction: row-reverse; + } + + .recent_avatars_item { + height: 18px; + margin: 0px 0px 0px 0px; + padding: 0px 0px 0px 0px; + position: relative; + min-width: 18px; + } + + .recent_avatars_img, + .recent_avatars_others { + border: 1px solid hsl(221.1, 23.5%, 15.9%); + border-left: 0 !important; + border-radius: 6px; + color: hsl(0, 0%, 100%); + display: block; + height: 18px; + text-align: center; + } + + .recent_avatars_others { + background-color: hsl(205.2, 76.5%, 50%); + } } } diff --git a/static/templates/recent_topic_row.hbs b/static/templates/recent_topic_row.hbs index 19abebd0d8..aff1b34b54 100644 --- a/static/templates/recent_topic_row.hbs +++ b/static/templates/recent_topic_row.hbs @@ -17,6 +17,18 @@ + {{ last_msg_time }}