From 4619a1e028e71a5cb6a327f9c922312ba91a1bcc Mon Sep 17 00:00:00 2001 From: Aman Agrawal Date: Wed, 8 May 2024 10:21:39 +0000 Subject: [PATCH] recent_view: Make load more fetch substantially more data. We keep fetching until we reach first unread message or have 50k messages worth of data displayed once user hits `Load more` in recent view. --- web/src/message_fetch.js | 35 ++++++++++++++++++++++++++++++++++- web/src/recent_view_ui.js | 22 ++++++++++++++++------ web/src/ui_init.js | 7 +++++++ 3 files changed, 57 insertions(+), 7 deletions(-) diff --git a/web/src/message_fetch.js b/web/src/message_fetch.js index 2d94a9f621..57d0bad4f7 100644 --- a/web/src/message_fetch.js +++ b/web/src/message_fetch.js @@ -52,6 +52,7 @@ const consts = { // Parameters for asking for more history in the recent view. recent_view_fetch_more_batch_size: 2000, + recent_view_minimum_load_more_fetch_size: 50000, }; function process_result(data, opts) { @@ -428,9 +429,41 @@ export function maybe_load_older_messages(opts) { if (!msg_list_data.fetch_status.can_load_older_messages()) { // We may already be loading old messages or already // got the oldest one. + if (opts.recent_view) { + recent_view_ui.set_backfill_in_progress(false); + } return; } + if (opts.recent_view && recent_view_ui.is_backfill_in_progress) { + // The recent view "load more" button does a tail-recursive + // backfill, so that one doesn't have to click dozens of times + // to get a large amount of history. + // + // We can afford to do this in our server load budget, because + // this is a rare operation; prior to May 2024, Zulip fetched + // all history since the oldest unread unconditionally. + + let fetched_substantial_history = false; + if (msg_list_data.num_items() >= consts.recent_view_minimum_load_more_fetch_size) { + fetched_substantial_history = true; + } + + let found_first_unread = opts.first_unread_message_id === undefined; + // This is a soft check because `first_unread_message_id` can be deleted. + if (!found_first_unread && msg_list_data.first() <= opts.first_unread_message_id) { + found_first_unread = true; + } + + if (fetched_substantial_history && found_first_unread) { + recent_view_ui.set_backfill_in_progress(false); + return; + } + + opts.cont = () => + setTimeout(() => maybe_load_older_messages(opts), consts.catch_up_backfill_delay); + } + do_backfill({ ...opts, num_before: opts.recent_view @@ -515,7 +548,7 @@ export function initialize(finished_initial_fetch) { return; } - // Stop once we've hit the minimum backfill quantiy of + // Stop once we've hit the minimum backfill quantity of // messages if we've received a message older than // `target_days_of_history`. const latest_message = all_messages_data.first(); diff --git a/web/src/recent_view_ui.js b/web/src/recent_view_ui.js index b57e3feae8..94c173b9eb 100644 --- a/web/src/recent_view_ui.js +++ b/web/src/recent_view_ui.js @@ -46,6 +46,7 @@ import * as views_util from "./views_util"; let topics_widget; let filters_dropdown_widget; +export let is_backfill_in_progress = false; // Sets the number of avatars to display. // Rest of the avatars, if present, are displayed as {+x} const MAX_AVATAR = 4; @@ -102,6 +103,11 @@ export function set_initial_message_fetch_status(value) { is_initial_message_fetch_pending = value; } +export function set_backfill_in_progress(value) { + is_backfill_in_progress = value; + update_load_more_banner(); +} + export function clear_for_tests() { filters.clear(); dropdown_filters.clear(); @@ -199,12 +205,7 @@ function update_load_more_banner() { return; } - const $button = $(".recent-view-load-more-container .fetch-messages-button"); - const $button_label = $(".recent-view-load-more-container .button-label"); const $banner_text = $(".recent-view-load-more-container .last-fetched-message"); - - $button.toggleClass("notvisible", false); - const time_obj = new Date(oldest_message_timestamp * 1000); const time_string = timerender.get_localized_date_or_time_for_format( time_obj, @@ -212,6 +213,15 @@ function update_load_more_banner() { ); $banner_text.text($t({defaultMessage: "Showing messages since {time_string}."}, {time_string})); + if (is_backfill_in_progress) { + // Keep the button disabled and the loading indicator running + // until we've finished our recursive backfill. + return; + } + const $button = $(".recent-view-load-more-container .fetch-messages-button"); + const $button_label = $(".recent-view-load-more-container .button-label"); + $button.toggleClass("notvisible", false); + $button_label.toggleClass("invisible", false); $button.prop("disabled", false); loading.destroy_indicator( @@ -1593,13 +1603,13 @@ export function initialize({ }); $("body").on("click", ".recent-view-load-more-container .fetch-messages-button", () => { - maybe_load_older_messages(); $(".recent-view-load-more-container .button-label").toggleClass("invisible", true); $(".recent-view-load-more-container .fetch-messages-button").prop("disabled", true); loading.make_indicator( $(".recent-view-load-more-container .fetch-messages-button .loading-indicator"), {width: 20}, ); + maybe_load_older_messages(); }); $(document).on("compose_canceled.zulip", () => { diff --git a/web/src/ui_init.js b/web/src/ui_init.js index d6e415efbf..f334ee0c14 100644 --- a/web/src/ui_init.js +++ b/web/src/ui_init.js @@ -734,9 +734,16 @@ export function initialize_everything(state_data) { on_mark_pm_as_read: unread_ops.mark_pm_as_read, on_mark_topic_as_read: unread_ops.mark_topic_as_read, maybe_load_older_messages() { + recent_view_ui.set_backfill_in_progress(true); message_fetch.maybe_load_older_messages({ msg_list_data: all_messages_data, recent_view: true, + // To have a hard anchor on our target of first unread message id, + // we pass it from here, otherwise it might get updated and lead to confusion. + // + // TODO: Ideally, muted unread messages would not + // count for this purpose. + first_unread_message_id: unread.get_all_msg_ids()[0], }); }, });