2021-03-11 05:43:45 +01:00
|
|
|
import $ from "jquery";
|
|
|
|
|
2021-03-30 06:23:09 +02:00
|
|
|
import {all_messages_data} from "./all_messages_data";
|
2021-02-28 01:11:16 +01:00
|
|
|
import * as channel from "./channel";
|
|
|
|
import {Filter} from "./filter";
|
|
|
|
import * as huddle_data from "./huddle_data";
|
2021-03-28 17:57:53 +02:00
|
|
|
import * as message_helper from "./message_helper";
|
2021-02-28 21:31:33 +01:00
|
|
|
import * as message_list from "./message_list";
|
2021-03-30 02:21:21 +02:00
|
|
|
import * as message_lists from "./message_lists";
|
2021-02-28 01:11:16 +01:00
|
|
|
import * as message_scroll from "./message_scroll";
|
|
|
|
import * as message_util from "./message_util";
|
2021-03-26 15:21:47 +01:00
|
|
|
import * as narrow_banner from "./narrow_banner";
|
2021-03-25 22:35:45 +01:00
|
|
|
import {page_params} from "./page_params";
|
2021-02-28 01:11:16 +01:00
|
|
|
import * as people from "./people";
|
2021-06-10 14:18:46 +02:00
|
|
|
import * as recent_topics_ui from "./recent_topics_ui";
|
2021-02-28 01:11:16 +01:00
|
|
|
import * as stream_data from "./stream_data";
|
2021-02-28 21:31:02 +01:00
|
|
|
import * as stream_list from "./stream_list";
|
2021-02-28 01:11:16 +01:00
|
|
|
import * as ui_report from "./ui_report";
|
2020-05-26 13:58:18 +02:00
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const consts = {
|
2018-06-04 21:13:07 +02:00
|
|
|
backfill_idle_time: 10 * 1000,
|
2018-03-08 21:25:14 +01:00
|
|
|
backfill_batch_size: 1000,
|
2018-03-09 15:32:28 +01:00
|
|
|
narrow_before: 50,
|
|
|
|
narrow_after: 50,
|
2020-06-18 08:38:49 +02:00
|
|
|
num_before_home_anchor: 200,
|
|
|
|
num_after_home_anchor: 200,
|
2020-11-04 00:21:38 +01:00
|
|
|
recent_topics_initial_fetch_size: 400,
|
2018-03-08 21:25:14 +01:00
|
|
|
backward_batch_size: 100,
|
2018-03-11 20:19:30 +01:00
|
|
|
forward_batch_size: 100,
|
2018-03-08 21:25:14 +01:00
|
|
|
catch_up_batch_size: 1000,
|
|
|
|
};
|
|
|
|
|
2018-03-16 13:05:54 +01:00
|
|
|
function process_result(data, opts) {
|
2019-11-02 00:06:25 +01:00
|
|
|
let messages = data.messages;
|
2018-03-16 13:05:54 +01:00
|
|
|
|
2020-07-15 01:29:15 +02:00
|
|
|
if (!$("#connection-error").hasClass("get-events-error")) {
|
2018-02-25 13:01:38 +01:00
|
|
|
ui_report.hide_error($("#connection-error"));
|
|
|
|
}
|
2017-03-19 18:19:48 +01:00
|
|
|
|
narrow: Fix messages being cached without flags set.
f0c680e9c0d1a62fd414bccc82e4ac255173aaa9 introduced a call to
message_helper.process_new_message without first calling
message_store.set_message_flags on the message.
This resulted in it being possible as a race, when loading the Zulip
app to a stream/topic/near narrow, for a message to have the
`historical` flag be undefined due to not being initialized.
That invalid state, in turn, resulted in the message_list_view code
path for rendering the message feed incorrectly displaying additional
recipient bars around the message.
We could fix this by just calling message_store.set_message_booleans
in this code path. However, this bug exposes the fact that it's very
fragile to expect every code path to call that function before
message_helper.process_new_message.
So we instead fix this by moving message_store.set_message_booleans
inside message_helper.process_new_message.
One call point of concern in this change is maybe_add_narrow_messages,
which could theoretically reintroduce the double set_message_flags
bugs detailed in 9729b1a4ad51b69c98ce4f8374c9d9f8cf69430c. However, I
believe that to not be possible, because that call should never
experience a cache miss.
The other existing code paths were already calling
set_message_booleans immediately before
message_helper.process_new_message. They are still changing here, in
that we now do a cache lookup before attempting to call
set_message_booleans. Because the message booleans do not affect the
cache lookup and the local message object is discarded in case of a
cache hit, this should have no functional impact.
Because I found the existing comment at that call site confusing and
almost proposed removing it as pointless, extend the block comment to
explicitly mention that the purpose is refreshing our object.
Fixes #21503.
2022-03-24 01:07:56 +01:00
|
|
|
messages = messages.map((message) => message_helper.process_new_message(message));
|
2017-03-19 18:19:48 +01:00
|
|
|
|
2022-10-25 00:34:47 +02:00
|
|
|
// In some rare situations, we expect to discover new unread
|
|
|
|
// messages not tracked in unread.js during this fetching process.
|
|
|
|
message_util.do_unread_count_updates(messages, true);
|
2018-04-23 20:24:55 +02:00
|
|
|
|
2017-03-19 18:19:48 +01:00
|
|
|
// If we're loading more messages into the home view, save them to
|
2021-03-30 06:23:09 +02:00
|
|
|
// the all_messages_data as well, as the message_lists.home is
|
|
|
|
// reconstructed from all_messages_data.
|
2021-03-30 02:21:21 +02:00
|
|
|
if (opts.msg_list === message_lists.home) {
|
2021-03-30 06:23:09 +02:00
|
|
|
all_messages_data.add_messages(messages);
|
2017-03-19 18:19:48 +01:00
|
|
|
}
|
|
|
|
|
2018-03-09 15:35:23 +01:00
|
|
|
if (messages.length !== 0) {
|
2019-01-08 01:26:02 +01:00
|
|
|
message_util.add_old_messages(messages, opts.msg_list);
|
2017-03-19 18:19:48 +01:00
|
|
|
}
|
|
|
|
|
2022-11-22 18:09:11 +01:00
|
|
|
if (
|
|
|
|
opts.msg_list === message_list.narrowed &&
|
|
|
|
message_lists.current === message_list.narrowed &&
|
|
|
|
message_list.narrowed.empty()
|
|
|
|
) {
|
2022-11-16 21:44:33 +01:00
|
|
|
// Even after loading more messages, we have
|
|
|
|
// no messages to display in this narrow.
|
|
|
|
narrow_banner.show_empty_narrow_message();
|
|
|
|
}
|
|
|
|
|
2020-05-26 13:58:18 +02:00
|
|
|
huddle_data.process_loaded_messages(messages);
|
2017-03-19 18:19:48 +01:00
|
|
|
stream_list.update_streams_sidebar();
|
2021-06-10 14:18:46 +02:00
|
|
|
recent_topics_ui.process_messages(messages);
|
2019-12-11 03:07:51 +01:00
|
|
|
|
2018-07-06 20:23:20 +02:00
|
|
|
stream_list.maybe_scroll_narrow_into_view();
|
2017-03-19 18:19:48 +01:00
|
|
|
|
|
|
|
if (opts.cont !== undefined) {
|
2020-06-16 17:58:37 +02:00
|
|
|
opts.cont(data, opts);
|
2017-03-19 18:19:48 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-09 02:17:54 +01:00
|
|
|
function get_messages_success(data, opts) {
|
2021-03-30 02:21:21 +02:00
|
|
|
const update_loading_indicator = opts.msg_list === message_lists.current;
|
2018-12-13 00:57:40 +01:00
|
|
|
if (opts.num_before > 0) {
|
2020-05-30 09:45:12 +02:00
|
|
|
opts.msg_list.data.fetch_status.finish_older_batch({
|
2020-07-20 22:18:43 +02:00
|
|
|
update_loading_indicator,
|
2018-12-13 00:57:40 +01:00
|
|
|
found_oldest: data.found_oldest,
|
|
|
|
history_limited: data.history_limited,
|
|
|
|
});
|
2021-03-30 02:21:21 +02:00
|
|
|
if (opts.msg_list === message_lists.home) {
|
|
|
|
// When we update message_lists.home, we need to also update
|
2021-03-30 06:23:09 +02:00
|
|
|
// the fetch_status data structure for all_messages_data.
|
|
|
|
all_messages_data.fetch_status.finish_older_batch({
|
2020-06-15 12:47:11 +02:00
|
|
|
update_loading_indicator: false,
|
2018-12-13 00:57:40 +01:00
|
|
|
found_oldest: data.found_oldest,
|
|
|
|
history_limited: data.history_limited,
|
|
|
|
});
|
|
|
|
}
|
2020-06-14 12:33:12 +02:00
|
|
|
message_scroll.update_top_of_narrow_notices(opts.msg_list);
|
2018-12-13 00:57:40 +01:00
|
|
|
}
|
|
|
|
|
2018-12-13 01:13:29 +01:00
|
|
|
if (opts.num_after > 0) {
|
2020-07-15 00:34:28 +02:00
|
|
|
opts.fetch_again = opts.msg_list.data.fetch_status.finish_newer_batch(data.messages, {
|
2020-07-20 22:18:43 +02:00
|
|
|
update_loading_indicator,
|
2020-07-15 00:34:28 +02:00
|
|
|
found_newest: data.found_newest,
|
|
|
|
});
|
2021-03-30 02:21:21 +02:00
|
|
|
if (opts.msg_list === message_lists.home) {
|
|
|
|
// When we update message_lists.home, we need to also update
|
2021-03-30 06:23:09 +02:00
|
|
|
// the fetch_status data structure for all_messages_data.
|
|
|
|
opts.fetch_again = all_messages_data.fetch_status.finish_newer_batch(data.messages, {
|
|
|
|
update_loading_indicator: false,
|
|
|
|
found_newest: data.found_newest,
|
|
|
|
});
|
2018-12-13 01:13:29 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-30 02:21:21 +02:00
|
|
|
if (opts.msg_list.narrowed && opts.msg_list !== message_lists.current) {
|
2017-03-19 18:19:48 +01:00
|
|
|
// We unnarrowed before receiving new messages so
|
|
|
|
// don't bother processing the newly arrived messages.
|
|
|
|
return;
|
|
|
|
}
|
2018-06-04 21:09:11 +02:00
|
|
|
if (!data) {
|
2017-11-09 16:26:38 +01:00
|
|
|
// The server occasionally returns no data during a
|
2017-03-19 18:19:48 +01:00
|
|
|
// restart. Ignore those responses and try again
|
2020-07-02 01:45:54 +02:00
|
|
|
setTimeout(() => {
|
2021-02-28 01:11:16 +01:00
|
|
|
load_messages(opts);
|
2017-03-19 18:19:48 +01:00
|
|
|
}, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-03-16 13:05:54 +01:00
|
|
|
process_result(data, opts);
|
2017-03-19 18:19:48 +01:00
|
|
|
}
|
|
|
|
|
2019-07-11 18:54:28 +02:00
|
|
|
// This function modifies the data.narrow filters to use user IDs
|
|
|
|
// instead of emails string if it is supported. We currently don't set
|
|
|
|
// or convert the emails string to user IDs directly into the Filter code
|
|
|
|
// because doing so breaks the app in various modules that expect emails string.
|
2019-08-10 18:14:22 +02:00
|
|
|
function handle_operators_supporting_id_based_api(data) {
|
2020-10-07 10:45:28 +02:00
|
|
|
const operators_supporting_ids = new Set(["pm-with"]);
|
|
|
|
const operators_supporting_id = new Set(["sender", "group-pm-with", "stream"]);
|
2019-07-11 18:54:28 +02:00
|
|
|
|
|
|
|
if (data.narrow === undefined) {
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
data.narrow = JSON.parse(data.narrow);
|
2020-07-02 01:39:34 +02:00
|
|
|
data.narrow = data.narrow.map((filter) => {
|
2020-10-07 10:45:28 +02:00
|
|
|
if (operators_supporting_ids.has(filter.operator)) {
|
2019-07-11 18:54:28 +02:00
|
|
|
filter.operand = people.emails_strings_to_user_ids_array(filter.operand);
|
|
|
|
}
|
|
|
|
|
2020-10-07 10:45:28 +02:00
|
|
|
if (operators_supporting_id.has(filter.operator)) {
|
2020-07-15 01:29:15 +02:00
|
|
|
if (filter.operator === "stream") {
|
2019-08-07 19:17:17 +02:00
|
|
|
const stream_id = stream_data.get_stream_id(filter.operand);
|
|
|
|
if (stream_id !== undefined) {
|
|
|
|
filter.operand = stream_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
return filter;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The other operands supporting object IDs all work with user objects.
|
2019-11-02 00:06:25 +01:00
|
|
|
const person = people.get_by_email(filter.operand);
|
2019-07-13 02:16:35 +02:00
|
|
|
if (person !== undefined) {
|
|
|
|
filter.operand = person.user_id;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-11 18:54:28 +02:00
|
|
|
return filter;
|
|
|
|
});
|
|
|
|
|
|
|
|
data.narrow = JSON.stringify(data.narrow);
|
|
|
|
return data;
|
|
|
|
}
|
2017-03-19 18:19:48 +01:00
|
|
|
|
2022-10-18 00:41:38 +02:00
|
|
|
export function load_messages(opts, attempt = 1) {
|
2020-06-22 22:30:30 +02:00
|
|
|
if (typeof opts.anchor === "number") {
|
|
|
|
// Messages that have been locally echoed messages have
|
|
|
|
// floating point temporary IDs, which is intended to be a.
|
|
|
|
// completely client-side detail. We need to round these to
|
|
|
|
// the nearest integer before sending a request to the server.
|
2021-06-08 04:05:33 +02:00
|
|
|
opts.anchor = opts.anchor.toFixed(0);
|
2020-06-22 22:30:30 +02:00
|
|
|
}
|
2020-07-15 00:34:28 +02:00
|
|
|
let data = {anchor: opts.anchor, num_before: opts.num_before, num_after: opts.num_after};
|
2017-03-19 18:19:48 +01:00
|
|
|
|
2020-11-20 20:44:01 +01:00
|
|
|
// This block is a hack; structurally, we want to set
|
|
|
|
// data.narrow = opts.msg_list.data.filter.public_operators()
|
|
|
|
//
|
2021-03-30 06:23:09 +02:00
|
|
|
// But support for the all_messages_data sharing of data with
|
2021-03-30 02:21:21 +02:00
|
|
|
// message_lists.home and the (hacky) page_params.narrow feature
|
2020-11-20 20:44:01 +01:00
|
|
|
// requires a somewhat ugly bundle of conditionals.
|
2021-03-30 02:21:21 +02:00
|
|
|
if (opts.msg_list === message_lists.home) {
|
2020-11-20 20:44:01 +01:00
|
|
|
if (page_params.narrow_stream !== undefined) {
|
|
|
|
data.narrow = JSON.stringify(page_params.narrow);
|
|
|
|
}
|
2021-03-30 02:21:21 +02:00
|
|
|
// Otherwise, we don't pass narrow for message_lists.home; this is
|
2020-11-20 20:44:01 +01:00
|
|
|
// required because it shares its data with all_msg_list, and
|
|
|
|
// so we need the server to send us message history from muted
|
2021-03-30 02:21:21 +02:00
|
|
|
// streams and topics even though message_lists.home's in:home
|
2020-11-20 20:44:01 +01:00
|
|
|
// operators will filter those.
|
|
|
|
} else {
|
2020-11-20 14:01:12 +01:00
|
|
|
let operators = opts.msg_list.data.filter.public_operators();
|
2017-03-19 18:19:48 +01:00
|
|
|
if (page_params.narrow !== undefined) {
|
|
|
|
operators = operators.concat(page_params.narrow);
|
|
|
|
}
|
|
|
|
data.narrow = JSON.stringify(operators);
|
|
|
|
}
|
|
|
|
|
2021-03-30 02:21:21 +02:00
|
|
|
let update_loading_indicator = opts.msg_list === message_lists.current;
|
2018-12-13 01:06:38 +01:00
|
|
|
if (opts.num_before > 0) {
|
2020-06-15 12:47:11 +02:00
|
|
|
opts.msg_list.data.fetch_status.start_older_batch({
|
2020-07-20 22:18:43 +02:00
|
|
|
update_loading_indicator,
|
2020-06-15 12:47:11 +02:00
|
|
|
});
|
2021-03-30 02:21:21 +02:00
|
|
|
if (opts.msg_list === message_lists.home) {
|
2021-03-30 06:23:09 +02:00
|
|
|
all_messages_data.fetch_status.start_older_batch({
|
2020-07-20 22:18:43 +02:00
|
|
|
update_loading_indicator,
|
2020-06-15 12:47:11 +02:00
|
|
|
});
|
2018-12-13 01:06:38 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-13 01:08:37 +01:00
|
|
|
if (opts.num_after > 0) {
|
2020-06-16 06:22:51 +02:00
|
|
|
// We hide the bottom loading indicator when we're fetching both top and bottom messages.
|
|
|
|
update_loading_indicator = update_loading_indicator && opts.num_before === 0;
|
2020-06-15 11:53:00 +02:00
|
|
|
opts.msg_list.data.fetch_status.start_newer_batch({
|
2020-07-20 22:18:43 +02:00
|
|
|
update_loading_indicator,
|
2020-06-15 11:53:00 +02:00
|
|
|
});
|
2021-03-30 02:21:21 +02:00
|
|
|
if (opts.msg_list === message_lists.home) {
|
2021-03-30 06:23:09 +02:00
|
|
|
all_messages_data.fetch_status.start_newer_batch({
|
2020-07-20 22:18:43 +02:00
|
|
|
update_loading_indicator,
|
2020-06-15 11:53:00 +02:00
|
|
|
});
|
2018-12-13 01:08:37 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-03 16:53:21 +01:00
|
|
|
data.client_gravatar = true;
|
2019-08-10 18:14:22 +02:00
|
|
|
data = handle_operators_supporting_id_based_api(data);
|
2017-11-03 16:53:21 +01:00
|
|
|
|
2021-09-14 17:02:01 +02:00
|
|
|
if (page_params.is_spectator) {
|
|
|
|
// This is a bit of a hack; ideally we'd unify this logic in
|
|
|
|
// some way with the above logic, and not need to do JSON
|
|
|
|
// parsing/stringifying here.
|
|
|
|
const web_public_narrow = {negated: false, operator: "streams", operand: "web-public"};
|
|
|
|
|
|
|
|
if (!data.narrow) {
|
|
|
|
/* For the "All messages" feed, this will be the only operator. */
|
|
|
|
data.narrow = JSON.stringify([web_public_narrow]);
|
|
|
|
} else {
|
|
|
|
// Otherwise, we append the operator. This logic is not
|
|
|
|
// ideal in that in theory an existing `streams:` operator
|
|
|
|
// could be present, but not in a useful way. We don't
|
|
|
|
// attempt to validate the narrow is compatible with
|
|
|
|
// spectators here; the server will return an error if
|
|
|
|
// appropriate.
|
|
|
|
data.narrow = JSON.parse(data.narrow);
|
|
|
|
data.narrow.push(web_public_narrow);
|
|
|
|
data.narrow = JSON.stringify(data.narrow);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-19 18:19:48 +01:00
|
|
|
channel.get({
|
2020-07-15 01:29:15 +02:00
|
|
|
url: "/json/messages",
|
2020-07-20 22:18:43 +02:00
|
|
|
data,
|
|
|
|
success(data) {
|
2018-03-09 02:17:54 +01:00
|
|
|
get_messages_success(data, opts);
|
2017-03-19 18:19:48 +01:00
|
|
|
},
|
2020-07-20 22:18:43 +02:00
|
|
|
error(xhr) {
|
2021-03-30 02:21:21 +02:00
|
|
|
if (opts.msg_list.narrowed && opts.msg_list !== message_lists.current) {
|
2017-03-19 18:19:48 +01:00
|
|
|
// We unnarrowed before getting an error so don't
|
|
|
|
// bother trying again or doing further processing.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (xhr.status === 400) {
|
|
|
|
// Bad request: We probably specified a narrow operator
|
|
|
|
// for a nonexistent stream or something. We shouldn't
|
|
|
|
// retry or display a connection error.
|
|
|
|
//
|
|
|
|
// FIXME: Warn the user when this has happened?
|
2020-11-28 22:37:47 +01:00
|
|
|
message_scroll.hide_indicators();
|
2019-11-02 00:06:25 +01:00
|
|
|
const data = {
|
2018-03-16 13:05:54 +01:00
|
|
|
messages: [],
|
|
|
|
};
|
|
|
|
process_result(data, opts);
|
2017-03-19 18:19:48 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-10-18 00:41:38 +02:00
|
|
|
// Backoff on retries, with full jitter: up to 2s, 4s, 8s, 16s, 32s
|
|
|
|
let delay = Math.random() * 2 ** attempt * 2000;
|
|
|
|
if (attempt >= 5) {
|
|
|
|
delay = 30000;
|
|
|
|
}
|
2022-10-18 00:26:24 +02:00
|
|
|
ui_report.show_error($("#connection-error"));
|
2020-07-02 01:45:54 +02:00
|
|
|
setTimeout(() => {
|
2022-10-18 00:41:38 +02:00
|
|
|
load_messages(opts, attempt + 1);
|
|
|
|
}, delay);
|
2017-03-19 18:19:48 +01:00
|
|
|
},
|
|
|
|
});
|
2021-02-28 01:11:16 +01:00
|
|
|
}
|
2017-03-19 18:19:48 +01:00
|
|
|
|
2021-02-28 01:11:16 +01:00
|
|
|
export function load_messages_for_narrow(opts) {
|
|
|
|
load_messages({
|
2020-01-28 06:57:07 +01:00
|
|
|
anchor: opts.anchor,
|
2018-03-09 15:32:28 +01:00
|
|
|
num_before: consts.narrow_before,
|
|
|
|
num_after: consts.narrow_after,
|
2022-12-09 20:33:10 +01:00
|
|
|
msg_list: opts.msg_list,
|
2020-06-16 06:22:51 +02:00
|
|
|
cont: opts.cont,
|
2018-03-09 15:32:28 +01:00
|
|
|
});
|
2021-02-28 01:11:16 +01:00
|
|
|
}
|
2017-03-19 18:19:48 +01:00
|
|
|
|
2021-02-28 01:11:16 +01:00
|
|
|
export function get_backfill_anchor(msg_list) {
|
2021-03-30 06:23:09 +02:00
|
|
|
const oldest_msg =
|
|
|
|
msg_list === message_lists.home ? all_messages_data.first() : msg_list.first();
|
2018-03-21 16:19:28 +01:00
|
|
|
|
2020-06-06 16:35:50 +02:00
|
|
|
if (oldest_msg) {
|
|
|
|
return oldest_msg.id;
|
2018-03-21 16:03:03 +01:00
|
|
|
}
|
2020-06-06 16:35:50 +02:00
|
|
|
|
|
|
|
// msg_list is empty, which is an impossible
|
|
|
|
// case, raise a fatal error.
|
2020-09-24 07:56:29 +02:00
|
|
|
throw new Error("There are no message available to backfill.");
|
2021-02-28 01:11:16 +01:00
|
|
|
}
|
2018-03-21 16:03:03 +01:00
|
|
|
|
2021-02-28 01:11:16 +01:00
|
|
|
export function get_frontfill_anchor(msg_list) {
|
2021-03-30 06:23:09 +02:00
|
|
|
const last_msg = msg_list === message_lists.home ? all_messages_data.last() : msg_list.last();
|
2018-03-11 20:19:30 +01:00
|
|
|
|
|
|
|
if (last_msg) {
|
|
|
|
return last_msg.id;
|
|
|
|
}
|
|
|
|
|
2020-06-06 16:35:50 +02:00
|
|
|
// Although it is impossible that we reach here since we
|
|
|
|
// are already checking `msg_list.fetch_status.can_load_newer_messages`
|
|
|
|
// and user cannot be scrolling down on an empty message_list to
|
|
|
|
// fetch more data, and if user is, then the available data is wrong
|
|
|
|
// and we raise a fatal error.
|
2020-09-24 07:56:29 +02:00
|
|
|
throw new Error("There are no message available to frontfill.");
|
2021-02-28 01:11:16 +01:00
|
|
|
}
|
2018-03-11 20:19:30 +01:00
|
|
|
|
2021-02-28 01:11:16 +01:00
|
|
|
export function maybe_load_older_messages(opts) {
|
2018-03-09 02:23:49 +01:00
|
|
|
// This function gets called when you scroll to the top
|
|
|
|
// of your window, and you want to get messages older
|
2020-03-28 01:25:56 +01:00
|
|
|
// than what the browsers originally fetched.
|
2019-11-02 00:06:25 +01:00
|
|
|
const msg_list = opts.msg_list;
|
2020-05-30 09:45:12 +02:00
|
|
|
if (!msg_list.data.fetch_status.can_load_older_messages()) {
|
2018-03-09 22:23:23 +01:00
|
|
|
// We may already be loading old messages or already
|
|
|
|
// got the oldest one.
|
2017-03-19 18:19:48 +01:00
|
|
|
return;
|
|
|
|
}
|
2018-03-21 16:39:03 +01:00
|
|
|
|
2021-02-28 01:11:16 +01:00
|
|
|
do_backfill({
|
2020-07-20 22:18:43 +02:00
|
|
|
msg_list,
|
2018-03-21 16:39:03 +01:00
|
|
|
num_before: consts.backward_batch_size,
|
|
|
|
});
|
2021-02-28 01:11:16 +01:00
|
|
|
}
|
2018-03-21 16:39:03 +01:00
|
|
|
|
2021-02-28 01:11:16 +01:00
|
|
|
export function do_backfill(opts) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const msg_list = opts.msg_list;
|
2021-02-28 01:11:16 +01:00
|
|
|
const anchor = get_backfill_anchor(msg_list);
|
2018-03-08 21:25:14 +01:00
|
|
|
|
2021-02-28 01:11:16 +01:00
|
|
|
load_messages({
|
2020-07-20 22:18:43 +02:00
|
|
|
anchor,
|
2018-03-21 16:39:03 +01:00
|
|
|
num_before: opts.num_before,
|
2017-03-19 18:19:48 +01:00
|
|
|
num_after: 0,
|
2020-07-20 22:18:43 +02:00
|
|
|
msg_list,
|
|
|
|
cont() {
|
2018-03-21 16:39:03 +01:00
|
|
|
if (opts.cont) {
|
|
|
|
opts.cont();
|
|
|
|
}
|
2017-03-19 18:19:48 +01:00
|
|
|
},
|
|
|
|
});
|
2021-02-28 01:11:16 +01:00
|
|
|
}
|
2017-03-19 18:19:48 +01:00
|
|
|
|
2021-02-28 01:11:16 +01:00
|
|
|
export function maybe_load_newer_messages(opts) {
|
2020-06-06 16:35:50 +02:00
|
|
|
// This function gets called when you scroll to the bottom
|
2018-03-11 20:19:30 +01:00
|
|
|
// of your window, and you want to get messages newer
|
2020-03-28 01:25:56 +01:00
|
|
|
// than what the browsers originally fetched.
|
2019-11-02 00:06:25 +01:00
|
|
|
const msg_list = opts.msg_list;
|
2018-03-11 20:19:30 +01:00
|
|
|
|
2020-05-30 09:45:12 +02:00
|
|
|
if (!msg_list.data.fetch_status.can_load_newer_messages()) {
|
2018-03-11 20:19:30 +01:00
|
|
|
// We may already be loading new messages or already
|
|
|
|
// got the newest one.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-02-28 01:11:16 +01:00
|
|
|
const anchor = get_frontfill_anchor(msg_list);
|
2018-03-11 20:19:30 +01:00
|
|
|
|
2020-06-16 17:58:37 +02:00
|
|
|
function load_more(data, args) {
|
2021-03-30 02:21:21 +02:00
|
|
|
if (args.fetch_again && args.msg_list === message_lists.current) {
|
|
|
|
maybe_load_newer_messages({msg_list: message_lists.current});
|
2020-06-16 17:58:37 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-28 01:11:16 +01:00
|
|
|
load_messages({
|
2020-07-20 22:18:43 +02:00
|
|
|
anchor,
|
2018-03-11 20:19:30 +01:00
|
|
|
num_before: 0,
|
|
|
|
num_after: consts.forward_batch_size,
|
2020-07-20 22:18:43 +02:00
|
|
|
msg_list,
|
2020-06-16 17:58:37 +02:00
|
|
|
cont: load_more,
|
2018-03-11 20:19:30 +01:00
|
|
|
});
|
2021-02-28 01:11:16 +01:00
|
|
|
}
|
2018-03-11 20:19:30 +01:00
|
|
|
|
2021-02-28 01:11:16 +01:00
|
|
|
export function start_backfilling_messages() {
|
2018-03-20 15:32:43 +01:00
|
|
|
// backfill more messages after the user is idle
|
2020-07-15 00:34:28 +02:00
|
|
|
$(document).idle({
|
|
|
|
idle: consts.backfill_idle_time,
|
2020-07-20 22:18:43 +02:00
|
|
|
onIdle() {
|
2021-02-28 01:11:16 +01:00
|
|
|
do_backfill({
|
2020-07-15 00:34:28 +02:00
|
|
|
num_before: consts.backfill_batch_size,
|
2021-03-30 02:21:21 +02:00
|
|
|
msg_list: message_lists.home,
|
2020-07-15 00:34:28 +02:00
|
|
|
});
|
|
|
|
},
|
|
|
|
});
|
2021-02-28 01:11:16 +01:00
|
|
|
}
|
2018-03-20 15:32:43 +01:00
|
|
|
|
2021-03-28 16:43:58 +02:00
|
|
|
export function initialize(home_view_loaded) {
|
2017-03-19 18:19:48 +01:00
|
|
|
// get the initial message list
|
2018-12-13 00:57:40 +01:00
|
|
|
function load_more(data) {
|
2020-02-19 22:45:57 +01:00
|
|
|
// If we haven't selected a message in the home view yet, and
|
|
|
|
// the home view isn't empty, we select the anchor message here.
|
2021-03-30 02:21:21 +02:00
|
|
|
if (message_lists.home.selected_id() === -1 && !message_lists.home.empty()) {
|
2020-02-19 22:45:57 +01:00
|
|
|
// We fall back to the closest selected id, as the user
|
|
|
|
// may have removed a stream from the home view while we
|
|
|
|
// were loading data.
|
2021-03-30 02:21:21 +02:00
|
|
|
message_lists.home.select_id(data.anchor, {
|
2020-07-15 00:34:28 +02:00
|
|
|
then_scroll: true,
|
|
|
|
use_closest: true,
|
|
|
|
target_scroll_offset: page_params.initial_offset,
|
|
|
|
});
|
2017-03-19 18:19:48 +01:00
|
|
|
}
|
|
|
|
|
2018-03-20 15:28:24 +01:00
|
|
|
if (data.found_newest) {
|
2022-03-05 00:47:57 +01:00
|
|
|
if (page_params.is_spectator) {
|
|
|
|
// Since for spectators, this is the main fetch, we
|
|
|
|
// hide the Recent Topics loading indicator here.
|
|
|
|
recent_topics_ui.hide_loading_indicator();
|
|
|
|
}
|
|
|
|
|
2021-03-28 16:43:58 +02:00
|
|
|
// See server_events.js for this callback.
|
|
|
|
home_view_loaded();
|
2021-02-28 01:11:16 +01:00
|
|
|
start_backfilling_messages();
|
2018-03-20 15:28:24 +01:00
|
|
|
return;
|
2017-03-19 18:19:48 +01:00
|
|
|
}
|
|
|
|
|
2018-03-20 15:28:24 +01:00
|
|
|
// If we fall through here, we need to keep fetching more data, and
|
|
|
|
// we'll call back to the function we're in.
|
2019-11-02 00:06:25 +01:00
|
|
|
const messages = data.messages;
|
2022-01-24 09:05:06 +01:00
|
|
|
const latest_id = messages.at(-1).id;
|
2018-03-20 15:28:24 +01:00
|
|
|
|
2021-02-28 01:11:16 +01:00
|
|
|
load_messages({
|
2020-06-22 22:30:30 +02:00
|
|
|
anchor: latest_id,
|
2018-03-20 15:28:24 +01:00
|
|
|
num_before: 0,
|
|
|
|
num_after: consts.catch_up_batch_size,
|
2021-03-30 02:21:21 +02:00
|
|
|
msg_list: message_lists.home,
|
2018-03-20 15:28:24 +01:00
|
|
|
cont: load_more,
|
|
|
|
});
|
2017-03-19 18:19:48 +01:00
|
|
|
}
|
|
|
|
|
2020-02-19 22:24:00 +01:00
|
|
|
let anchor;
|
|
|
|
if (page_params.initial_pointer) {
|
|
|
|
// If we're doing a server-initiated reload, similar to a
|
|
|
|
// near: narrow query, we want to select a specific message.
|
|
|
|
anchor = page_params.initial_pointer;
|
|
|
|
} else {
|
2020-02-19 22:53:08 +01:00
|
|
|
// Otherwise, we should just use the first unread message in
|
|
|
|
// the user's unmuted history as our anchor.
|
|
|
|
anchor = "first_unread";
|
2020-02-19 22:24:00 +01:00
|
|
|
}
|
2021-02-28 01:11:16 +01:00
|
|
|
load_messages({
|
2020-07-20 22:18:43 +02:00
|
|
|
anchor,
|
2020-06-18 08:38:49 +02:00
|
|
|
num_before: consts.num_before_home_anchor,
|
|
|
|
num_after: consts.num_after_home_anchor,
|
2021-03-30 02:21:21 +02:00
|
|
|
msg_list: message_lists.home,
|
2020-02-19 21:59:26 +01:00
|
|
|
cont: load_more,
|
|
|
|
});
|
2020-11-04 00:21:38 +01:00
|
|
|
|
2020-12-16 10:11:30 +01:00
|
|
|
if (page_params.is_spectator) {
|
|
|
|
// Since spectators never have old unreads, we can skip the
|
|
|
|
// hacky fetch below for them (which would just waste resources).
|
2022-03-05 00:47:57 +01:00
|
|
|
|
|
|
|
// This optimization requires a bit of duplicated loading
|
|
|
|
// indicator code, here and hiding logic in hide_more.
|
|
|
|
recent_topics_ui.show_loading_indicator();
|
2020-12-16 10:11:30 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-11-04 00:21:38 +01:00
|
|
|
// In addition to the algorithm above, which is designed to ensure
|
|
|
|
// that we fetch all message history eventually starting with the
|
|
|
|
// first unread message, we also need to ensure that the Recent
|
|
|
|
// Topics page contains the very most recent threads on page load.
|
|
|
|
//
|
|
|
|
// Long term, we'll want to replace this with something that's
|
|
|
|
// more performant (i.e. avoids this unnecessary extra fetch the
|
|
|
|
// results of which are basically discarded) and better represents
|
|
|
|
// more than a few hundred messages' history, but this strategy
|
2021-05-10 07:02:14 +02:00
|
|
|
// allows "Recent topics" to always show current data (with gaps)
|
2020-11-04 00:21:38 +01:00
|
|
|
// on page load; the data will be complete once the algorithm
|
2021-04-25 22:54:23 +02:00
|
|
|
// above catches up to present.
|
2020-11-04 00:21:38 +01:00
|
|
|
//
|
2021-05-10 07:02:14 +02:00
|
|
|
// (Users will see a weird artifact where Recent topics has a gap
|
2020-11-04 00:21:38 +01:00
|
|
|
// between E.g. 6 days ago and 37 days ago while the catchup
|
|
|
|
// process runs, so this strategy still results in problematic
|
2022-02-08 00:13:33 +01:00
|
|
|
// visual artifacts shortly after page load; just more forgivable
|
2020-11-04 00:21:38 +01:00
|
|
|
// ones).
|
|
|
|
//
|
|
|
|
// This MessageList is defined similarly to home_message_list,
|
|
|
|
// without a `table_name` attached.
|
|
|
|
const recent_topics_message_list = new message_list.MessageList({
|
|
|
|
filter: new Filter([{operator: "in", operand: "home"}]),
|
2021-01-25 06:23:16 +01:00
|
|
|
excludes_muted_topics: true,
|
2020-11-04 00:21:38 +01:00
|
|
|
});
|
2022-02-25 06:08:08 +01:00
|
|
|
// TODO: Ideally we'd have loading indicators for recent topics at
|
|
|
|
// both top and bottom be managed by load_messages, but that
|
|
|
|
// likely depends on other reorganizations of the early loading
|
|
|
|
// sequence.
|
|
|
|
recent_topics_ui.show_loading_indicator();
|
2021-02-28 01:11:16 +01:00
|
|
|
load_messages({
|
2020-11-04 00:21:38 +01:00
|
|
|
anchor: "newest",
|
|
|
|
num_before: consts.recent_topics_initial_fetch_size,
|
|
|
|
num_after: 0,
|
|
|
|
msg_list: recent_topics_message_list,
|
2022-02-25 06:08:08 +01:00
|
|
|
cont: recent_topics_ui.hide_loading_indicator,
|
2020-11-04 00:21:38 +01:00
|
|
|
});
|
2021-02-28 01:11:16 +01:00
|
|
|
}
|