mirror of
https://github.com/zulip/zulip.git
synced 2025-11-05 14:35:27 +00:00
recent view: Add button to fetch more conversations.
We add a new timerender format for this context, where there's plenty of space. Fixes: #18461
This commit is contained in:
@@ -29,6 +29,7 @@ const consts = {
|
||||
recent_view_initial_fetch_size: 400,
|
||||
narrowed_view_backward_batch_size: 100,
|
||||
narrowed_view_forward_batch_size: 100,
|
||||
recent_view_fetch_more_batch_size: 1000,
|
||||
catch_up_batch_size: 1000,
|
||||
};
|
||||
|
||||
@@ -36,6 +37,8 @@ function process_result(data, opts) {
|
||||
let messages = data.messages;
|
||||
|
||||
messages = messages.map((message) => message_helper.process_new_message(message));
|
||||
const has_found_oldest = opts.msg_list?.data.fetch_status.has_found_oldest() ?? false;
|
||||
const has_found_newest = opts.msg_list?.data.fetch_status.has_found_newest() ?? false;
|
||||
|
||||
// In some rare situations, we expect to discover new unread
|
||||
// messages not tracked in unread.js during this fetching process.
|
||||
@@ -58,6 +61,17 @@ function process_result(data, opts) {
|
||||
}
|
||||
}
|
||||
|
||||
if (messages.length > 0 && opts.msg_list === message_lists.home) {
|
||||
// We keep track of how far back we've fetched messages for, for messaging in
|
||||
// the recent view. This assumes `data.messages` is already sorted.
|
||||
const oldest_timestamp = all_messages_data.first().timestamp;
|
||||
recent_view_ui.set_oldest_message_date(
|
||||
oldest_timestamp,
|
||||
has_found_oldest,
|
||||
has_found_newest,
|
||||
);
|
||||
}
|
||||
|
||||
huddle_data.process_loaded_messages(messages);
|
||||
recent_view_ui.process_messages(messages);
|
||||
stream_list.update_streams_sidebar();
|
||||
@@ -74,8 +88,6 @@ function process_result(data, opts) {
|
||||
// the messages we requested, and all of them are in muted
|
||||
// topics, but there are older messages for this stream that
|
||||
// we need to ask the server for.
|
||||
const has_found_oldest = opts.msg_list.data.fetch_status.has_found_oldest();
|
||||
const has_found_newest = opts.msg_list.data.fetch_status.has_found_newest();
|
||||
if (has_found_oldest && has_found_newest) {
|
||||
// Even after loading more messages, we have
|
||||
// no messages to display in this narrow.
|
||||
@@ -407,7 +419,9 @@ export function maybe_load_older_messages(opts) {
|
||||
|
||||
do_backfill({
|
||||
msg_list,
|
||||
num_before: consts.narrowed_view_backward_batch_size,
|
||||
num_before: opts.recent_view
|
||||
? consts.recent_view_fetch_more_batch_size
|
||||
: consts.narrowed_view_backward_batch_size,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -118,6 +118,80 @@ export function set_default_focus() {
|
||||
compose_closed_ui.set_standard_text_for_reply_button();
|
||||
}
|
||||
|
||||
// When there are no messages loaded, we don't show a banner yet.
|
||||
const NO_MESSAGES_LOADED = 0;
|
||||
// When some messages are loaded, but we're still loading newer messages,
|
||||
// we show a simple loading banner.
|
||||
const SOME_MESSAGES_LOADED = 1;
|
||||
// Once we've found the newest message, we allow the user to load
|
||||
// more messages further back in time.
|
||||
const SOME_MESSAGES_LOADED_INCLUDING_NEWEST = 2;
|
||||
// Once all messages are loaded, we hide the banner.
|
||||
const ALL_MESSAGES_LOADED = 3;
|
||||
|
||||
let loading_state = NO_MESSAGES_LOADED;
|
||||
let oldest_message_timestamp;
|
||||
|
||||
export function set_oldest_message_date(timestamp, has_found_oldest, has_found_newest) {
|
||||
if (has_found_oldest) {
|
||||
loading_state = ALL_MESSAGES_LOADED;
|
||||
} else if (has_found_newest) {
|
||||
loading_state = SOME_MESSAGES_LOADED_INCLUDING_NEWEST;
|
||||
} else {
|
||||
loading_state = SOME_MESSAGES_LOADED;
|
||||
}
|
||||
oldest_message_timestamp = timestamp;
|
||||
|
||||
// We might be loading messages in another narrow before the recent view
|
||||
// is shown, so we keep the state updated and update the banner only
|
||||
// once it's actually rendered.
|
||||
if ($("#recent_view_table table tbody").length) {
|
||||
update_load_more_banner();
|
||||
}
|
||||
}
|
||||
|
||||
function update_load_more_banner() {
|
||||
if (loading_state === NO_MESSAGES_LOADED) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (loading_state === ALL_MESSAGES_LOADED) {
|
||||
$(".recent-view-load-more-container").toggleClass("notvisible", true);
|
||||
return;
|
||||
}
|
||||
|
||||
// There are some messages loaded, but not all messages yet. The banner was
|
||||
// hidden on page load, and we make sure to show it now that there are messages
|
||||
// we can display.
|
||||
$(".recent-view-load-more-container").toggleClass("notvisible", false);
|
||||
|
||||
// Until we've found the newest message, we only show the banner with a messages
|
||||
// explaining we're still fetching messages. We don't allow the user to fetch
|
||||
// more messages.
|
||||
if (loading_state === SOME_MESSAGES_LOADED) {
|
||||
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,
|
||||
"full_weekday_dayofyear_year_time",
|
||||
);
|
||||
$banner_text.text($t({defaultMessage: "Showing messages since {time_string}."}, {time_string}));
|
||||
|
||||
$button_label.toggleClass("invisible", false);
|
||||
$button.prop("disabled", false);
|
||||
loading.destroy_indicator(
|
||||
$(".recent-view-load-more-container .fetch-messages-button .loading-indicator"),
|
||||
);
|
||||
}
|
||||
|
||||
function get_min_load_count(already_rendered_count, load_count) {
|
||||
const extra_rows_for_viewing_pleasure = 15;
|
||||
if (row_focus > already_rendered_count + load_count) {
|
||||
@@ -874,6 +948,9 @@ export function complete_rerender() {
|
||||
// was not the first view loaded in the app.
|
||||
show_selected_filters();
|
||||
|
||||
// Update the banner now that it's rendered.
|
||||
update_load_more_banner();
|
||||
|
||||
const $container = $("#recent_view_table table tbody");
|
||||
$container.empty();
|
||||
topics_widget = ListWidget.create($container, mapped_topic_values, {
|
||||
@@ -1244,7 +1321,12 @@ export function change_focused_element($elt, input_key) {
|
||||
return false;
|
||||
}
|
||||
|
||||
export function initialize({on_click_participant, on_mark_pm_as_read, on_mark_topic_as_read}) {
|
||||
export function initialize({
|
||||
on_click_participant,
|
||||
on_mark_pm_as_read,
|
||||
on_mark_topic_as_read,
|
||||
maybe_load_older_messages,
|
||||
}) {
|
||||
// load filters from local storage.
|
||||
if (!page_params.is_spectator) {
|
||||
// A user may have a stored filter and can log out
|
||||
@@ -1388,4 +1470,14 @@ export function initialize({on_click_participant, on_mark_pm_as_read, on_mark_to
|
||||
$("#recent_view_search").val("");
|
||||
update_filters_view();
|
||||
});
|
||||
|
||||
$("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},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -25,7 +25,11 @@ export function clear_for_testing(): void {
|
||||
}
|
||||
|
||||
type DateFormat = "weekday" | "dayofyear" | "weekday_dayofyear_year" | "dayofyear_year";
|
||||
type DateWithTimeFormat = "dayofyear_time" | "dayofyear_year_time" | "weekday_dayofyear_year_time";
|
||||
type DateWithTimeFormat =
|
||||
| "dayofyear_time"
|
||||
| "dayofyear_year_time"
|
||||
| "weekday_dayofyear_year_time"
|
||||
| "full_weekday_dayofyear_year_time";
|
||||
type TimeFormat = "time" | "time_sec";
|
||||
|
||||
type DateOrTimeFormat = DateFormat | TimeFormat | DateWithTimeFormat;
|
||||
@@ -83,6 +87,8 @@ function get_format_options_for_type(type: DateOrTimeFormat): Intl.DateTimeForma
|
||||
return full_format_options;
|
||||
case "weekday_dayofyear_year_time": // Wed, Jul 27, 2016, 13:30
|
||||
return {...long_format_options, ...time_format_options};
|
||||
case "full_weekday_dayofyear_year_time": // Wednesday, July 27, 2016, 13:30
|
||||
return {...long_format_options, ...time_format_options, weekday: "long", month: "long"};
|
||||
default:
|
||||
throw new Error("Wrong format provided.");
|
||||
}
|
||||
|
||||
@@ -604,6 +604,12 @@ export function initialize_everything() {
|
||||
},
|
||||
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() {
|
||||
message_fetch.maybe_load_older_messages({
|
||||
msg_list: message_lists.home,
|
||||
recent_view: true,
|
||||
});
|
||||
},
|
||||
});
|
||||
inbox_ui.initialize();
|
||||
alert_words.initialize(alert_words_params);
|
||||
|
||||
@@ -80,9 +80,7 @@
|
||||
);
|
||||
}
|
||||
|
||||
.table_fix_head table {
|
||||
/* To keep border properties to the thead th. */
|
||||
border-collapse: separate;
|
||||
.recent-view-container {
|
||||
/*
|
||||
Add margin bottom equal to `#bottom-whitespace`. This helps us keep
|
||||
#compose visible at its max-height without overlapping with any visible
|
||||
@@ -93,6 +91,31 @@
|
||||
user is at the bottom of scroll container when the compose box is open.
|
||||
*/
|
||||
margin-bottom: var(--max-unexpanded-compose-height);
|
||||
}
|
||||
|
||||
.recent-view-load-more-container {
|
||||
margin: 20px 10px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.fetch-messages-button {
|
||||
display: grid;
|
||||
justify-items: center;
|
||||
|
||||
.loading_indicator_spinner {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
path {
|
||||
fill: var(--color-recent-view-loading-spinner);
|
||||
}
|
||||
}
|
||||
|
||||
.table_fix_head table {
|
||||
/* To keep border properties to the thead th. */
|
||||
border-collapse: separate;
|
||||
|
||||
border-spacing: 0;
|
||||
width: 100%;
|
||||
|
||||
|
||||
@@ -255,6 +255,7 @@ body {
|
||||
/* Message feed loading indicator colors */
|
||||
--color-zulip-logo: hsl(0deg 0% 0% / 34%);
|
||||
--color-zulip-logo-loading: hsl(0deg 0% 27%);
|
||||
--color-recent-view-loading-spinner: hsl(0deg 0% 27%);
|
||||
--color-zulip-logo-z: hsl(0deg 0% 100%);
|
||||
|
||||
/* Message collapsing/condensing button colors */
|
||||
@@ -383,6 +384,7 @@ body {
|
||||
/* Message feed loading indicator colors */
|
||||
--color-zulip-logo: hsl(0deg 0% 100% / 50%);
|
||||
--color-zulip-logo-loading: hsl(0deg 0% 100%);
|
||||
--color-recent-view-loading-spinner: hsl(0deg 0% 100% / 60%);
|
||||
--color-zulip-logo-z: hsl(214deg 27% 18%);
|
||||
|
||||
/* Message collapsing/condensing button colors */
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="table_fix_head" data-simplebar>
|
||||
<div class="recent-view-container">
|
||||
<table class="table table-responsive">
|
||||
<tbody data-empty="{{t 'No conversations match your filters.' }}" class="required-text"></tbody>
|
||||
<thead>
|
||||
@@ -24,4 +25,13 @@
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
{{!-- Don't show the banner until we have some messages loaded. --}}
|
||||
<div class="recent-view-load-more-container main-view-banner info notvisible">
|
||||
<div class="last-fetched-message banner_content">{{t "This view is still loading messages."}}</div>
|
||||
<button class="fetch-messages-button main-view-banner-action-button right_edge notvisible">
|
||||
<div class="loading-indicator"></div>
|
||||
<span class="button-label">{{t "Load more"}}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -28,6 +28,7 @@ mock_esm("../src/recent_view_ui", {
|
||||
process_messages: noop,
|
||||
show_loading_indicator: noop,
|
||||
hide_loading_indicator: noop,
|
||||
set_oldest_message_date: noop,
|
||||
});
|
||||
mock_esm("../src/ui_report", {
|
||||
hide_error: noop,
|
||||
|
||||
@@ -105,6 +105,12 @@ run_test("get_localized_date_or_time_for_format returns correct format", () => {
|
||||
date: "Wed, Jan 27, 2021, 1:53 AM",
|
||||
},
|
||||
},
|
||||
{
|
||||
format: "full_weekday_dayofyear_year_time",
|
||||
expected: {
|
||||
date: "Wednesday, January 27, 2021 at 1:53 AM",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
for (const format of formats) {
|
||||
|
||||
Reference in New Issue
Block a user