mirror of https://github.com/zulip/zulip.git
message_list: Don't always cache "Combined feed" view.
Important changes in this commit: * We only cache message list for "Combined feed" if it is the default view. * We modify existing handling of home message list code so that it can be used to for any message list that we want to cache using a new `preserve_rendered_state` variable. * narrow_state.filter() returns the filter of combined feed view list instead of `undefined`. * We start fetching messages from the latest message on app load. * Messages in all messages data and Recent view are always synced. * If combined feed view list is not cached, we don't track it's last pointer, effectively sending user to the latest unread message always .
This commit is contained in:
parent
2ccbb9bc00
commit
103c37f23a
|
@ -108,6 +108,7 @@ EXEMPT_FILES = make_set(
|
|||
"web/src/emojisets.ts",
|
||||
"web/src/favicon.ts",
|
||||
"web/src/feedback_widget.ts",
|
||||
"web/src/fetch_status.ts",
|
||||
"web/src/flatpickr.ts",
|
||||
"web/src/gear_menu.js",
|
||||
"web/src/giphy.js",
|
||||
|
|
|
@ -11,6 +11,12 @@ async function get_stream_li(page: Page, stream_name: string): Promise<string> {
|
|||
|
||||
async function expect_home(page: Page): Promise<void> {
|
||||
const message_list_id = await common.get_current_msg_list_id(page, true);
|
||||
await page.waitForSelector(`.message-list[data-message-list-id='${message_list_id}']`, {
|
||||
visible: true,
|
||||
});
|
||||
// Assert that there is only one message list.
|
||||
assert.equal((await page.$$(".message-list")).length, 1);
|
||||
assert.strictEqual(await page.title(), "Combined feed - Zulip Dev - Zulip");
|
||||
await common.check_messages_sent(page, message_list_id, [
|
||||
["Verona > test", ["verona test a", "verona test b"]],
|
||||
["Verona > other topic", ["verona other topic c"]],
|
||||
|
@ -107,10 +113,6 @@ async function un_narrow(page: Page): Promise<void> {
|
|||
await page.keyboard.press("Escape");
|
||||
}
|
||||
await page.click("#left-sidebar-navigation-list .top_left_all_messages");
|
||||
await page.waitForSelector(".message-list .message_row", {visible: true});
|
||||
// Assert that there is only one message list.
|
||||
assert.equal((await page.$$(".message-list")).length, 1);
|
||||
assert.strictEqual(await page.title(), "Combined feed - Zulip Dev - Zulip");
|
||||
}
|
||||
|
||||
async function un_narrow_by_clicking_org_icon(page: Page): Promise<void> {
|
||||
|
@ -322,6 +324,7 @@ async function test_narrow_by_clicking_the_left_sidebar(page: Page): Promise<voi
|
|||
await expect_all_direct_messages(page);
|
||||
|
||||
await un_narrow(page);
|
||||
await expect_home(page);
|
||||
}
|
||||
|
||||
async function arrow(page: Page, direction: "Up" | "Down"): Promise<void> {
|
||||
|
|
|
@ -206,7 +206,8 @@ export function initialize(): void {
|
|||
const narrow_filter = narrow_state.filter();
|
||||
let display_current_view;
|
||||
if (narrow_state.is_message_feed_visible()) {
|
||||
if (narrow_filter === undefined) {
|
||||
assert(narrow_filter !== undefined);
|
||||
if (narrow_filter.is_in_home()) {
|
||||
display_current_view = $t({
|
||||
defaultMessage: "Currently viewing your combined feed.",
|
||||
});
|
||||
|
|
|
@ -12,7 +12,6 @@ import * as inbox_ui from "./inbox_ui";
|
|||
import * as inbox_util from "./inbox_util";
|
||||
import * as info_overlay from "./info_overlay";
|
||||
import * as message_fetch from "./message_fetch";
|
||||
import * as message_lists from "./message_lists";
|
||||
import * as message_viewport from "./message_viewport";
|
||||
import * as modals from "./modals";
|
||||
import * as narrow from "./narrow";
|
||||
|
@ -161,7 +160,6 @@ function do_hashchange_normal(from_reload) {
|
|||
if (from_reload) {
|
||||
blueslip.debug("We are narrowing as part of a reload.");
|
||||
if (message_fetch.initial_narrow_pointer !== undefined) {
|
||||
message_lists.home.pre_narrow_offset = message_fetch.initial_offset;
|
||||
narrow_opts.then_select_id = message_fetch.initial_narrow_pointer;
|
||||
narrow_opts.then_select_offset = message_fetch.initial_narrow_offset;
|
||||
}
|
||||
|
|
|
@ -1241,7 +1241,7 @@ export function delete_topic(stream_id, topic_name, failures = 0) {
|
|||
});
|
||||
}
|
||||
|
||||
export function handle_narrow_deactivated() {
|
||||
export function restore_edit_state_after_message_view_change() {
|
||||
assert(message_lists.current !== undefined);
|
||||
for (const [idx, elem] of currently_editing_messages) {
|
||||
if (message_lists.current.get(idx) !== undefined) {
|
||||
|
|
|
@ -166,7 +166,6 @@ export function insert_new_messages(messages, sent_by_this_client) {
|
|||
message_notifications.received_messages(messages);
|
||||
stream_list.update_streams_sidebar();
|
||||
pm_list.update_private_messages();
|
||||
recent_view_ui.process_messages(messages);
|
||||
}
|
||||
|
||||
export function update_messages(events) {
|
||||
|
@ -532,7 +531,6 @@ export function update_messages(events) {
|
|||
// propagated edits to be updated (since the topic edits can have
|
||||
// changed the correct grouping of messages).
|
||||
if (any_topic_edited || any_stream_changed) {
|
||||
message_lists.home.update_muting_and_rerender();
|
||||
// However, we don't need to rerender message_list if
|
||||
// we just changed the narrow earlier in this function.
|
||||
//
|
||||
|
@ -542,8 +540,15 @@ export function update_messages(events) {
|
|||
// edit. Doing so could save significant work, since most
|
||||
// topic edits will not match the current topic narrow in
|
||||
// large organizations.
|
||||
if (!changed_narrow && message_lists.current?.narrowed) {
|
||||
message_lists.current.update_muting_and_rerender();
|
||||
|
||||
for (const list of message_lists.all_rendered_message_lists()) {
|
||||
if (changed_narrow && list === message_lists.current) {
|
||||
// Avoid updating current message list if user switched to a different narrow and
|
||||
// we don't want to preserver the rendered state for the current one.
|
||||
continue;
|
||||
}
|
||||
|
||||
list.view.rerender_messages(messages_to_rerender, any_message_content_edited);
|
||||
}
|
||||
} else {
|
||||
// If the content of the message was edited, we do a special animation.
|
||||
|
|
|
@ -3,12 +3,10 @@ import $ from "jquery";
|
|||
import {all_messages_data} from "./all_messages_data";
|
||||
import * as blueslip from "./blueslip";
|
||||
import * as channel from "./channel";
|
||||
import {Filter} from "./filter";
|
||||
import * as huddle_data from "./huddle_data";
|
||||
import * as message_feed_loading from "./message_feed_loading";
|
||||
import * as message_feed_top_notices from "./message_feed_top_notices";
|
||||
import * as message_helper from "./message_helper";
|
||||
import * as message_list_data from "./message_list_data";
|
||||
import * as message_lists from "./message_lists";
|
||||
import * as message_util from "./message_util";
|
||||
import * as narrow_banner from "./narrow_banner";
|
||||
|
@ -19,21 +17,17 @@ import * as stream_data from "./stream_data";
|
|||
import * as stream_list from "./stream_list";
|
||||
import * as ui_report from "./ui_report";
|
||||
|
||||
export let initial_pointer;
|
||||
export let initial_offset;
|
||||
let first_messages_fetch = true;
|
||||
export let initial_narrow_pointer;
|
||||
export let initial_narrow_offset;
|
||||
|
||||
let is_all_messages_data_loaded = false;
|
||||
|
||||
const consts = {
|
||||
backfill_idle_time: 10 * 1000,
|
||||
backfill_batch_size: 1000,
|
||||
narrow_before: 50,
|
||||
narrow_after: 50,
|
||||
num_before_home_anchor: 200,
|
||||
num_after_home_anchor: 200,
|
||||
recent_view_initial_fetch_size: 400,
|
||||
initial_backfill_fetch_size: 400,
|
||||
maximum_initial_backfill_size: 4000,
|
||||
narrowed_view_backward_batch_size: 100,
|
||||
narrowed_view_forward_batch_size: 100,
|
||||
recent_view_fetch_more_batch_size: 1000,
|
||||
|
@ -54,13 +48,6 @@ function process_result(data, opts) {
|
|||
// messages not tracked in unread.ts during this fetching process.
|
||||
message_util.do_unread_count_updates(messages, true);
|
||||
|
||||
// If we're loading more messages into the home view, save them to
|
||||
// the all_messages_data as well, as the message_lists.home is
|
||||
// reconstructed from all_messages_data.
|
||||
if (opts.msg_list === message_lists.home) {
|
||||
all_messages_data.add_messages(messages);
|
||||
}
|
||||
|
||||
if (messages.length !== 0) {
|
||||
if (opts.msg_list) {
|
||||
// Since this adds messages to the MessageList and renders MessageListView,
|
||||
|
@ -69,21 +56,6 @@ function process_result(data, opts) {
|
|||
} else {
|
||||
opts.msg_list_data.add_messages(messages);
|
||||
}
|
||||
|
||||
// To avoid non-contiguous blocks of data in recent view from
|
||||
// message_lists.home and recent_view_message_list_data, we
|
||||
// only process data from message_lists.home if we have found
|
||||
// the newest message in message_lists.home. We check this via
|
||||
// is_all_messages_data_loaded, to avoid unnecessary
|
||||
// double-processing of the last batch of messages;
|
||||
// is_all_messages_data_loaded is set via opts.cont, below.
|
||||
if (
|
||||
opts.is_recent_view_data ||
|
||||
(opts.msg_list === message_lists.home && is_all_messages_data_loaded)
|
||||
) {
|
||||
const msg_list_data = opts.msg_list_data ?? opts.msg_list.data;
|
||||
recent_view_ui.process_messages(messages, msg_list_data);
|
||||
}
|
||||
}
|
||||
|
||||
huddle_data.process_loaded_messages(messages);
|
||||
|
@ -137,15 +109,6 @@ function get_messages_success(data, opts) {
|
|||
found_oldest: data.found_oldest,
|
||||
history_limited: data.history_limited,
|
||||
});
|
||||
if (opts.msg_list === message_lists.home) {
|
||||
// When we update message_lists.home, we need to also update
|
||||
// the fetch_status data structure for all_messages_data.
|
||||
all_messages_data.fetch_status.finish_older_batch({
|
||||
update_loading_indicator: false,
|
||||
found_oldest: data.found_oldest,
|
||||
history_limited: data.history_limited,
|
||||
});
|
||||
}
|
||||
message_feed_top_notices.update_top_of_narrow_notices(opts.msg_list);
|
||||
}
|
||||
|
||||
|
@ -154,14 +117,6 @@ function get_messages_success(data, opts) {
|
|||
update_loading_indicator,
|
||||
found_newest: data.found_newest,
|
||||
});
|
||||
if (opts.msg_list === message_lists.home) {
|
||||
// When we update message_lists.home, we need to also update
|
||||
// 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,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (opts.msg_list && opts.msg_list.narrowed && opts.msg_list !== message_lists.current) {
|
||||
|
@ -255,16 +210,16 @@ export function load_messages(opts, attempt = 1) {
|
|||
// data.narrow = opts.msg_list.data.filter.public_terms()
|
||||
//
|
||||
// But support for the all_messages_data sharing of data with
|
||||
// message_lists.home and the (hacky) page_params.narrow feature
|
||||
// the combined feed view and the (hacky) page_params.narrow feature
|
||||
// requires a somewhat ugly bundle of conditionals.
|
||||
if (msg_list_data.filter.is_in_home()) {
|
||||
if (page_params.narrow_stream !== undefined) {
|
||||
data.narrow = JSON.stringify(page_params.narrow);
|
||||
}
|
||||
// Otherwise, we don't pass narrow for message_lists.home; this is
|
||||
// required because it shares its data with all_msg_list, and
|
||||
// so we need the server to send us message history from muted
|
||||
// streams and topics even though message_lists.home's in:home
|
||||
// Otherwise, we don't pass narrow for the combined feed view; this is
|
||||
// required to display messages if their muted status changes without a new
|
||||
// network request, and so we need the server to send us message history from muted
|
||||
// streams and topics even though the combined feed view's in:home
|
||||
// operators will filter those.
|
||||
} else {
|
||||
let terms = msg_list_data.filter.public_terms();
|
||||
|
@ -280,11 +235,6 @@ export function load_messages(opts, attempt = 1) {
|
|||
msg_list_data.fetch_status.start_older_batch({
|
||||
update_loading_indicator,
|
||||
});
|
||||
if (opts.msg_list === message_lists.home) {
|
||||
all_messages_data.fetch_status.start_older_batch({
|
||||
update_loading_indicator,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (opts.num_after > 0) {
|
||||
|
@ -293,11 +243,6 @@ export function load_messages(opts, attempt = 1) {
|
|||
msg_list_data.fetch_status.start_newer_batch({
|
||||
update_loading_indicator,
|
||||
});
|
||||
if (opts.msg_list === message_lists.home) {
|
||||
all_messages_data.fetch_status.start_newer_batch({
|
||||
update_loading_indicator,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
data.client_gravatar = true;
|
||||
|
@ -416,12 +361,8 @@ export function load_messages_for_narrow(opts) {
|
|||
});
|
||||
}
|
||||
|
||||
export function get_backfill_anchor(msg_list) {
|
||||
const oldest_msg =
|
||||
msg_list === message_lists.home
|
||||
? all_messages_data.first_including_muted()
|
||||
: msg_list.data.first_including_muted();
|
||||
|
||||
export function get_backfill_anchor(msg_list_data) {
|
||||
const oldest_msg = msg_list_data.first_including_muted();
|
||||
if (oldest_msg) {
|
||||
return oldest_msg.id;
|
||||
}
|
||||
|
@ -430,10 +371,7 @@ export function get_backfill_anchor(msg_list) {
|
|||
}
|
||||
|
||||
export function get_frontfill_anchor(msg_list) {
|
||||
const last_msg =
|
||||
msg_list === message_lists.home
|
||||
? all_messages_data.last_including_muted()
|
||||
: msg_list.data.last_including_muted();
|
||||
const last_msg = msg_list.data.last_including_muted();
|
||||
|
||||
if (last_msg) {
|
||||
return last_msg.id;
|
||||
|
@ -469,15 +407,15 @@ export function maybe_load_older_messages(opts) {
|
|||
// This function gets called when you scroll to the top
|
||||
// of your window, and you want to get messages older
|
||||
// than what the browsers originally fetched.
|
||||
const msg_list = opts.msg_list;
|
||||
if (!msg_list.data.fetch_status.can_load_older_messages()) {
|
||||
const msg_list_data = opts.msg_list_data ?? opts.msg_list.data;
|
||||
if (!msg_list_data.fetch_status.can_load_older_messages()) {
|
||||
// We may already be loading old messages or already
|
||||
// got the oldest one.
|
||||
return;
|
||||
}
|
||||
|
||||
do_backfill({
|
||||
msg_list,
|
||||
...opts,
|
||||
num_before: opts.recent_view
|
||||
? consts.recent_view_fetch_more_batch_size
|
||||
: consts.narrowed_view_backward_batch_size,
|
||||
|
@ -485,14 +423,18 @@ export function maybe_load_older_messages(opts) {
|
|||
}
|
||||
|
||||
export function do_backfill(opts) {
|
||||
const msg_list = opts.msg_list;
|
||||
const anchor = get_backfill_anchor(msg_list);
|
||||
const msg_list_data = opts.msg_list_data ?? opts.msg_list.data;
|
||||
const anchor = get_backfill_anchor(msg_list_data);
|
||||
|
||||
// `load_messages` behaves differently for `msg_list` and `msg_list_data` as
|
||||
// parameters as which one is passed affects the behavior of the function.
|
||||
// So, we need to need them as they were provided to us.
|
||||
load_messages({
|
||||
anchor,
|
||||
num_before: opts.num_before,
|
||||
num_after: 0,
|
||||
msg_list,
|
||||
msg_list: opts.msg_list,
|
||||
msg_list_data: opts.msg_list_data,
|
||||
cont() {
|
||||
if (opts.cont) {
|
||||
opts.cont();
|
||||
|
@ -541,15 +483,13 @@ export function start_backfilling_messages() {
|
|||
onIdle() {
|
||||
do_backfill({
|
||||
num_before: consts.backfill_batch_size,
|
||||
msg_list: message_lists.home,
|
||||
msg_list_data: all_messages_data,
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function set_initial_pointer_and_offset({pointer, offset, narrow_pointer, narrow_offset}) {
|
||||
initial_pointer = pointer;
|
||||
initial_offset = offset;
|
||||
export function set_initial_pointer_and_offset({narrow_pointer, narrow_offset}) {
|
||||
initial_narrow_pointer = narrow_pointer;
|
||||
initial_narrow_offset = narrow_offset;
|
||||
}
|
||||
|
@ -557,36 +497,19 @@ export function set_initial_pointer_and_offset({pointer, offset, narrow_pointer,
|
|||
export function initialize(finished_initial_fetch) {
|
||||
// get the initial message list
|
||||
function load_more(data) {
|
||||
// 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.
|
||||
if (message_lists.home.selected_id() === -1 && !message_lists.home.visibly_empty()) {
|
||||
// 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.
|
||||
message_lists.home.select_id(data.anchor, {
|
||||
then_scroll: true,
|
||||
use_closest: true,
|
||||
target_scroll_offset: initial_offset,
|
||||
});
|
||||
if (first_messages_fetch) {
|
||||
// See server_events.js for this callback.
|
||||
// Start processing server events.
|
||||
finished_initial_fetch();
|
||||
recent_view_ui.hide_loading_indicator();
|
||||
first_messages_fetch = false;
|
||||
}
|
||||
|
||||
if (data.found_newest) {
|
||||
// Mark that we've finishing loading all the way to the
|
||||
// present in the all_messages_data data set. At this
|
||||
// time, it's safe to call recent_view_ui.process_messages
|
||||
// with all the messages in our cache.
|
||||
is_all_messages_data_loaded = true;
|
||||
recent_view_ui.process_messages(all_messages_data.all_messages(), all_messages_data);
|
||||
|
||||
if (page_params.is_spectator) {
|
||||
// Since for spectators, this is the main fetch, we
|
||||
// hide the Recent Conversations loading indicator here.
|
||||
recent_view_ui.hide_loading_indicator();
|
||||
}
|
||||
|
||||
// See server_events.js for this callback.
|
||||
finished_initial_fetch();
|
||||
if (data.found_oldest) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (all_messages_data.num_items() >= consts.maximum_initial_backfill_size) {
|
||||
start_backfilling_messages();
|
||||
return;
|
||||
}
|
||||
|
@ -596,72 +519,25 @@ export function initialize(finished_initial_fetch) {
|
|||
//
|
||||
// But we do it with a bit of delay, to reduce risk that we
|
||||
// hit rate limits with these backfills.
|
||||
const latest_id = data.messages.at(-1).id;
|
||||
const oldest_id = data.messages.at(0).id;
|
||||
setTimeout(() => {
|
||||
load_messages({
|
||||
anchor: latest_id,
|
||||
num_before: 0,
|
||||
num_after: consts.catch_up_batch_size,
|
||||
msg_list: message_lists.home,
|
||||
anchor: oldest_id,
|
||||
num_before: consts.catch_up_batch_size,
|
||||
num_after: 0,
|
||||
msg_list_data: all_messages_data,
|
||||
cont: load_more,
|
||||
});
|
||||
}, consts.catch_up_backfill_delay);
|
||||
}
|
||||
|
||||
let anchor;
|
||||
if (initial_pointer !== undefined) {
|
||||
// If we're doing a server-initiated reload, similar to a
|
||||
// near: narrow query, we want to select a specific message.
|
||||
anchor = initial_pointer;
|
||||
} else {
|
||||
// Otherwise, we should just use the first unread message in
|
||||
// the user's unmuted history as our anchor.
|
||||
anchor = "first_unread";
|
||||
}
|
||||
load_messages({
|
||||
anchor,
|
||||
num_before: consts.num_before_home_anchor,
|
||||
num_after: consts.num_after_home_anchor,
|
||||
msg_list: message_lists.home,
|
||||
cont: load_more,
|
||||
// Since `all_messages_data` contains continuous message history
|
||||
// which always contains the latest message, it makes sense for
|
||||
// Recent view to display the same data and be in sync.
|
||||
all_messages_data.set_add_messages_callback((messages) => {
|
||||
recent_view_ui.process_messages(messages, all_messages_data);
|
||||
});
|
||||
|
||||
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).
|
||||
|
||||
// This optimization requires a bit of duplicated loading
|
||||
// indicator code, here and hiding logic in hide_more.
|
||||
recent_view_ui.show_loading_indicator();
|
||||
return;
|
||||
}
|
||||
|
||||
// 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
|
||||
// allows "Recent Conversations" to always show current data (with gaps)
|
||||
// on page load; the data will be complete once the algorithm
|
||||
// above catches up to present.
|
||||
//
|
||||
// (Users will see a weird artifact where Recent Conversations has a gap
|
||||
// between E.g. 6 days ago and 37 days ago while the catchup
|
||||
// process runs, so this strategy still results in problematic
|
||||
// visual artifacts shortly after page load; just more forgivable
|
||||
// ones).
|
||||
//
|
||||
// We only initialize MessageListData here, since we don't
|
||||
// want update the UI and confuse the functions in MessageList.
|
||||
// Recent view can handle the UI updates itself.
|
||||
const recent_view_message_list_data = new message_list_data.MessageListData({
|
||||
filter: new Filter([{operator: "in", operand: "home"}]),
|
||||
excludes_muted_topics: true,
|
||||
});
|
||||
// TODO: Ideally we'd have loading indicators for Recent Conversations
|
||||
// at both top and bottom be managed by load_messages, but that
|
||||
// likely depends on other reorganizations of the early loading
|
||||
|
@ -669,10 +545,9 @@ export function initialize(finished_initial_fetch) {
|
|||
recent_view_ui.show_loading_indicator();
|
||||
load_messages({
|
||||
anchor: "newest",
|
||||
num_before: consts.recent_view_initial_fetch_size,
|
||||
num_before: consts.initial_backfill_fetch_size,
|
||||
num_after: 0,
|
||||
msg_list_data: recent_view_message_list_data,
|
||||
is_recent_view_data: true,
|
||||
cont: recent_view_ui.hide_loading_indicator,
|
||||
msg_list_data: all_messages_data,
|
||||
cont: load_more,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -2,13 +2,10 @@ import autosize from "autosize";
|
|||
import $ from "jquery";
|
||||
import assert from "minimalistic-assert";
|
||||
|
||||
import {all_messages_data} from "./all_messages_data";
|
||||
import * as blueslip from "./blueslip";
|
||||
import {Filter} from "./filter";
|
||||
import {MessageListData} from "./message_list_data";
|
||||
import * as message_list_tooltips from "./message_list_tooltips";
|
||||
import {MessageListView} from "./message_list_view";
|
||||
import * as message_lists from "./message_lists";
|
||||
import * as narrow_banner from "./narrow_banner";
|
||||
import * as narrow_state from "./narrow_state";
|
||||
import {page_params} from "./page_params";
|
||||
|
@ -59,8 +56,7 @@ export class MessageList {
|
|||
// DOM.
|
||||
this.view = new MessageListView(this, collapse_messages, opts.is_node_test);
|
||||
|
||||
// Whether this is a narrowed message list. The only message
|
||||
// list that is not is the home_msg_list global.
|
||||
// If this message list is not for the global feed.
|
||||
this.narrowed = !this.data.filter.is_in_home();
|
||||
|
||||
// Keeps track of whether the user has done a UI interaction,
|
||||
|
@ -72,6 +68,17 @@ export class MessageList {
|
|||
// the user. Possibly this can be unified in some nice way.
|
||||
this.reading_prevented = false;
|
||||
|
||||
// Whether this message list's is preserved in the DOM even
|
||||
// when viewing other views -- a valuable optimization for
|
||||
// fast toggling between the combined feed and other views,
|
||||
// which we enable only when that is the user's home view.
|
||||
//
|
||||
// This is intentionally not live-updated when web_home_view
|
||||
// changes, since it's easier to reason about if this
|
||||
// optimization is active or not for an entire session.
|
||||
this.preserve_rendered_state =
|
||||
user_settings.web_home_view === "all_messages" && !this.narrowed;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -110,14 +117,14 @@ export class MessageList {
|
|||
render_info = this.append_to_view(bottom_messages, opts);
|
||||
}
|
||||
|
||||
if (this.narrowed && !this.visibly_empty()) {
|
||||
if (!this.visibly_empty()) {
|
||||
// If adding some new messages to the message tables caused
|
||||
// our current narrow to no longer be empty, hide the empty
|
||||
// feed placeholder text.
|
||||
narrow_banner.hide_empty_narrow_message();
|
||||
}
|
||||
|
||||
if (this.narrowed && !this.visibly_empty() && this.selected_id() === -1) {
|
||||
if (!this.visibly_empty() && this.selected_id() === -1) {
|
||||
// The message list was previously empty, but now isn't
|
||||
// due to adding these messages, and we need to select a
|
||||
// message. Regardless of whether the messages are new or
|
||||
|
@ -468,19 +475,6 @@ export class MessageList {
|
|||
}
|
||||
|
||||
update_muting_and_rerender() {
|
||||
// For the home message list, we need to re-initialize
|
||||
// _all_items for stream muting/topic unmuting from
|
||||
// all_messages_data, since otherwise unmuting a previously
|
||||
// muted stream won't work.
|
||||
//
|
||||
// "in-home" filter doesn't included muted stream messages, so we
|
||||
// need to repopulate the message list with all messages to include
|
||||
// the previous messages in muted streams so that update_items_for_muting works.
|
||||
if (this.data.filter.is_in_home()) {
|
||||
this.data.clear();
|
||||
this.data.add_messages(all_messages_data.all_messages());
|
||||
}
|
||||
|
||||
this.data.update_items_for_muting();
|
||||
// We need to rerender whether or not the narrow hides muted
|
||||
// topics, because we need to update recipient bars for topics
|
||||
|
@ -529,12 +523,3 @@ export class MessageList {
|
|||
return this.data.get_last_message_sent_by_me();
|
||||
}
|
||||
}
|
||||
|
||||
export function initialize() {
|
||||
/* Create home_msg_list and register it. */
|
||||
const home_msg_list = new MessageList({
|
||||
filter: new Filter([{operator: "in", operand: "home"}]),
|
||||
excludes_muted_topics: true,
|
||||
});
|
||||
message_lists.set_home(home_msg_list);
|
||||
}
|
||||
|
|
|
@ -41,6 +41,8 @@ export class MessageListData {
|
|||
// there are no messages matching the current filter.
|
||||
_selected_id: number;
|
||||
predicate?: (message: Message) => boolean;
|
||||
// This is a callback that is called when messages are added to the message list.
|
||||
add_messages_callback?: (messages: Message[]) => void;
|
||||
|
||||
// MessageListData is a core data structure for keeping track of a
|
||||
// contiguous block of messages matching a given narrow that can
|
||||
|
@ -60,6 +62,10 @@ export class MessageListData {
|
|||
this._selected_id = -1;
|
||||
}
|
||||
|
||||
set_add_messages_callback(callback: () => void): void {
|
||||
this.add_messages_callback = callback;
|
||||
}
|
||||
|
||||
all_messages(): Message[] {
|
||||
return this._items;
|
||||
}
|
||||
|
@ -333,6 +339,10 @@ export class MessageListData {
|
|||
bottom_messages = this.append(bottom_messages);
|
||||
}
|
||||
|
||||
if (this.add_messages_callback) {
|
||||
this.add_messages_callback(messages);
|
||||
}
|
||||
|
||||
const info = {
|
||||
top_messages,
|
||||
bottom_messages,
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import $ from "jquery";
|
||||
import assert from "minimalistic-assert";
|
||||
|
||||
import * as blueslip from "./blueslip";
|
||||
import * as inbox_util from "./inbox_util";
|
||||
|
@ -33,6 +32,7 @@ export type SelectIdOpts = {
|
|||
|
||||
export type MessageList = {
|
||||
id: number;
|
||||
preserve_rendered_state: boolean;
|
||||
view: MessageListView;
|
||||
selected_id: () => number;
|
||||
selected_row: () => JQuery;
|
||||
|
@ -52,39 +52,37 @@ export type MessageList = {
|
|||
) => RenderInfo | undefined;
|
||||
};
|
||||
|
||||
export let home: MessageList | undefined;
|
||||
export let current: MessageList | undefined;
|
||||
export const rendered_message_lists = new Map<number, MessageList>();
|
||||
|
||||
export function set_current(msg_list: MessageList | undefined): void {
|
||||
// NOTE: Use update_current_message_list instead of this function.
|
||||
// NOTE: Strictly used for mocking in node tests.
|
||||
// Use `update_current_message_list` instead in production.
|
||||
current = msg_list;
|
||||
}
|
||||
|
||||
export function update_current_message_list(msg_list: MessageList | undefined): void {
|
||||
if (msg_list !== home) {
|
||||
home?.view.$list.removeClass("focused-message-list");
|
||||
if (current && !current.preserve_rendered_state) {
|
||||
// Remove the current message list from the DOM.
|
||||
current.view.$list.remove();
|
||||
rendered_message_lists.delete(current.id);
|
||||
} else {
|
||||
current?.view.$list.removeClass("focused-message-list");
|
||||
}
|
||||
|
||||
if (current !== home) {
|
||||
// Remove old msg list from DOM.
|
||||
current?.view.$list.remove();
|
||||
current = msg_list;
|
||||
if (current !== undefined) {
|
||||
rendered_message_lists.set(current.id, current);
|
||||
current.view.$list.addClass("focused-message-list");
|
||||
}
|
||||
|
||||
set_current(msg_list);
|
||||
current?.view.$list.addClass("focused-message-list");
|
||||
}
|
||||
|
||||
export function set_home(msg_list: MessageList): void {
|
||||
home = msg_list;
|
||||
}
|
||||
|
||||
export function all_rendered_message_lists(): MessageList[] {
|
||||
assert(home !== undefined);
|
||||
const rendered_message_lists = [home];
|
||||
if (current !== undefined && current !== home) {
|
||||
rendered_message_lists.push(current);
|
||||
}
|
||||
return rendered_message_lists;
|
||||
return [...rendered_message_lists.values()];
|
||||
}
|
||||
|
||||
export function add_rendered_message_list(msg_list: MessageList): void {
|
||||
rendered_message_lists.set(msg_list.id, msg_list);
|
||||
}
|
||||
|
||||
export function all_rendered_row_for_message_id(message_id: number): JQuery {
|
||||
|
@ -112,7 +110,11 @@ export function update_recipient_bar_background_color(): void {
|
|||
}
|
||||
|
||||
export function save_pre_narrow_offset_for_reload(): void {
|
||||
if (current === undefined) {
|
||||
// Only save the pre_narrow_offset if the message list will be cached if user
|
||||
// switches to a different narrow, otherwise the pre_narrow_offset would just be lost when
|
||||
// user switches to a different narrow. In case of a reload, offset for the current
|
||||
// message is captured and restored by `reload` library.
|
||||
if (!current?.preserve_rendered_state) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -59,7 +59,6 @@ import * as util from "./util";
|
|||
import * as widgetize from "./widgetize";
|
||||
|
||||
const LARGER_THAN_MAX_MESSAGE_ID = 10000000000000000;
|
||||
export let has_visited_all_messages = false;
|
||||
|
||||
export function reset_ui_state() {
|
||||
// Resets the state of various visual UI elements that are
|
||||
|
@ -98,7 +97,7 @@ export function activate(raw_terms, opts) {
|
|||
|
||||
raw_terms: Narrowing/search terms; used to construct
|
||||
a Filter object that decides which messages belong in the
|
||||
view. Required (See the above note on how `message_lists.home` works)
|
||||
view.
|
||||
|
||||
All other options are encoded via the `opts` dictionary:
|
||||
|
||||
|
@ -128,14 +127,14 @@ export function activate(raw_terms, opts) {
|
|||
raw_terms = [{operator: "is", operand: "home"}];
|
||||
}
|
||||
const filter = new Filter(raw_terms);
|
||||
|
||||
const is_narrowed_to_all_messages_view = narrow_state.filter()?.is_in_home();
|
||||
if (has_visited_all_messages && is_narrowed_to_all_messages_view && filter.is_in_home()) {
|
||||
const is_combined_feed_global_view = filter.is_in_home();
|
||||
const is_narrowed_to_combined_feed_view = narrow_state.filter()?.is_in_home();
|
||||
if (is_narrowed_to_combined_feed_view && is_combined_feed_global_view) {
|
||||
// If we're already looking at the combined feed, exit without doing any work.
|
||||
return;
|
||||
}
|
||||
|
||||
if (filter.is_in_home() && message_scroll_state.actively_scrolling) {
|
||||
if (is_combined_feed_global_view && message_scroll_state.actively_scrolling) {
|
||||
// TODO: Figure out why puppeteer test for this fails when run for narrows
|
||||
// other than `Combined feed`.
|
||||
|
||||
|
@ -149,8 +148,7 @@ export function activate(raw_terms, opts) {
|
|||
}
|
||||
|
||||
// Use to determine if user read any unread messages outside the combined feed.
|
||||
// BUG: This doesn't check for the combined feed?
|
||||
const was_narrowed_already = narrow_state.filter() !== undefined;
|
||||
const was_narrowed_already = message_lists.current?.narrowed;
|
||||
|
||||
// Since narrow.activate is called directly from various
|
||||
// places in our code without passing through hashchange,
|
||||
|
@ -161,7 +159,7 @@ export function activate(raw_terms, opts) {
|
|||
// TODO: is:home is currently not permitted for spectators
|
||||
// because they can't mute things; maybe that's the wrong
|
||||
// policy?
|
||||
!filter.is_in_home() &&
|
||||
!is_combined_feed_global_view &&
|
||||
raw_terms.some(
|
||||
(raw_term) => !hash_parser.allowed_web_public_narrows.includes(raw_term.operator),
|
||||
)
|
||||
|
@ -424,11 +422,21 @@ export function activate(raw_terms, opts) {
|
|||
|
||||
const excludes_muted_topics = filter.excludes_muted_topics();
|
||||
|
||||
// Check if we already have a rendered message list for the `filter`.
|
||||
// TODO: If we add a message list other than `is_in_home` to be save as rendered,
|
||||
// we need to add a `is_equal` function to `Filter` to compare the filters.
|
||||
let msg_list;
|
||||
if (filter.is_in_home()) {
|
||||
has_visited_all_messages = true;
|
||||
msg_list = message_lists.home;
|
||||
} else {
|
||||
let restore_rendered_list = false;
|
||||
for (const list of message_lists.all_rendered_message_lists()) {
|
||||
if (is_combined_feed_global_view && list.preserve_rendered_state) {
|
||||
assert(list.data.filter.is_in_home());
|
||||
msg_list = list;
|
||||
restore_rendered_list = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!restore_rendered_list) {
|
||||
let msg_data = new MessageListData({
|
||||
filter,
|
||||
excludes_muted_topics,
|
||||
|
@ -461,7 +469,6 @@ export function activate(raw_terms, opts) {
|
|||
}
|
||||
assert(msg_list !== undefined);
|
||||
|
||||
narrow_state.set_has_shown_message_list_view();
|
||||
// Show the new set of messages. It is important to set message_lists.current to
|
||||
// the view right as it's being shown, because we rely on message_lists.current
|
||||
// being shown for deciding when to condense messages.
|
||||
|
@ -473,7 +480,7 @@ export function activate(raw_terms, opts) {
|
|||
let select_immediately;
|
||||
let select_opts;
|
||||
let then_select_offset;
|
||||
if (filter.is_in_home()) {
|
||||
if (restore_rendered_list) {
|
||||
select_immediately = true;
|
||||
select_opts = {
|
||||
empty_ok: true,
|
||||
|
@ -502,7 +509,7 @@ export function activate(raw_terms, opts) {
|
|||
message_lists.current.resume_reading();
|
||||
// Reset the collapsed status of messages rows.
|
||||
condense.condense_and_collapse(message_lists.current.view.$list.find(".message_row"));
|
||||
message_edit.handle_narrow_deactivated();
|
||||
message_edit.restore_edit_state_after_message_view_change();
|
||||
widgetize.set_widgets_for_list();
|
||||
message_feed_top_notices.update_top_of_narrow_notices(msg_list);
|
||||
|
||||
|
|
|
@ -92,7 +92,7 @@ function pick_empty_narrow_banner(): NarrowBannerData {
|
|||
|
||||
const current_filter = narrow_state.filter();
|
||||
|
||||
if (current_filter === undefined) {
|
||||
if (current_filter === undefined || current_filter.is_in_home()) {
|
||||
return default_banner;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,15 +8,9 @@ import * as stream_data from "./stream_data";
|
|||
import type {StreamSubscription} from "./sub_store";
|
||||
import * as unread from "./unread";
|
||||
|
||||
export let has_shown_message_list_view = false;
|
||||
|
||||
export function filter(): Filter | undefined {
|
||||
// `Combined feed`, `Recent Conversations` and `Inbox` return undefined;
|
||||
if (message_lists.current === undefined || message_lists.current.data.filter.is_in_home()) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return message_lists.current.data.filter;
|
||||
// `Recent Conversations` and `Inbox` return undefined;
|
||||
return message_lists.current?.data.filter;
|
||||
}
|
||||
|
||||
export function search_terms(current_filter: Filter | undefined = filter()): NarrowTerm[] {
|
||||
|
@ -381,7 +375,3 @@ export function is_for_stream_id(stream_id: number, filter?: Filter): boolean {
|
|||
|
||||
return stream_id === narrow_sub.stream_id;
|
||||
}
|
||||
|
||||
export function set_has_shown_message_list_view(): void {
|
||||
has_shown_message_list_view = true;
|
||||
}
|
||||
|
|
|
@ -403,14 +403,7 @@ export function hide_loading_indicator() {
|
|||
}
|
||||
|
||||
export function process_messages(messages, msg_list_data) {
|
||||
// This code path processes messages from 3 sources:
|
||||
// 1. Newly sent messages from the server_events system. This is safe to
|
||||
// process because we always will have the latest previously sent messages.
|
||||
// 2. Messages in all_messages_data, the main cache of contiguous
|
||||
// message history that the client maintains.
|
||||
// 3. Latest messages fetched specifically for Recent view when
|
||||
// the browser first loads. We will be able to remove this once
|
||||
// all_messages_data is fetched in a better order.
|
||||
// Always synced with messages in all_messages_data.
|
||||
|
||||
let conversation_data_updated = false;
|
||||
if (messages.length > 0) {
|
||||
|
|
|
@ -26,7 +26,7 @@ function call_reload_hooks() {
|
|||
}
|
||||
}
|
||||
|
||||
function preserve_state(send_after_reload, save_pointer, save_compose) {
|
||||
function preserve_state(send_after_reload, save_compose) {
|
||||
if (!localstorage.supported()) {
|
||||
// If local storage is not supported by the browser, we can't
|
||||
// save the browser's position across reloads (since there's
|
||||
|
@ -68,23 +68,7 @@ function preserve_state(send_after_reload, save_pointer, save_compose) {
|
|||
}
|
||||
}
|
||||
|
||||
if (save_pointer) {
|
||||
const pointer = message_lists.home.selected_id();
|
||||
if (pointer !== -1) {
|
||||
url += "+pointer=" + pointer;
|
||||
}
|
||||
}
|
||||
|
||||
if (message_lists.current === message_lists.home) {
|
||||
const $row = message_lists.home.selected_row();
|
||||
if ($row.length > 0) {
|
||||
url += "+offset=" + $row.get_offset_to_window().top;
|
||||
}
|
||||
} else if (message_lists.current !== undefined) {
|
||||
url += "+offset=" + message_lists.home.pre_narrow_offset;
|
||||
|
||||
// narrow_state.filter() is not undefined, so this is the current
|
||||
// narrowed message list.
|
||||
if (message_lists.current !== undefined) {
|
||||
const narrow_pointer = message_lists.current.selected_id();
|
||||
if (narrow_pointer !== -1) {
|
||||
url += "+narrow_pointer=" + narrow_pointer;
|
||||
|
@ -93,8 +77,6 @@ function preserve_state(send_after_reload, save_pointer, save_compose) {
|
|||
if ($narrow_row.length > 0) {
|
||||
url += "+narrow_offset=" + $narrow_row.get_offset_to_window().top;
|
||||
}
|
||||
} else {
|
||||
url += "+offset=" + message_lists.home.pre_narrow_offset;
|
||||
}
|
||||
|
||||
url += hash_util.build_reload_url();
|
||||
|
@ -146,7 +128,7 @@ function delete_stale_tokens(ls) {
|
|||
);
|
||||
}
|
||||
|
||||
function do_reload_app(send_after_reload, save_pointer, save_compose, message_html) {
|
||||
function do_reload_app(send_after_reload, save_compose, message_html) {
|
||||
if (reload_state.is_in_progress()) {
|
||||
blueslip.log("do_reload_app: Doing nothing since reload_in_progress");
|
||||
return;
|
||||
|
@ -154,7 +136,7 @@ function do_reload_app(send_after_reload, save_pointer, save_compose, message_ht
|
|||
|
||||
// TODO: we should completely disable the UI here
|
||||
try {
|
||||
preserve_state(send_after_reload, save_pointer, save_compose);
|
||||
preserve_state(send_after_reload, save_compose);
|
||||
} catch (error) {
|
||||
blueslip.error("Failed to preserve state", undefined, error);
|
||||
}
|
||||
|
@ -199,13 +181,12 @@ function do_reload_app(send_after_reload, save_pointer, save_compose, message_ht
|
|||
|
||||
export function initiate({
|
||||
immediate = false,
|
||||
save_pointer = true,
|
||||
save_compose = true,
|
||||
send_after_reload = false,
|
||||
message_html = "Reloading ...",
|
||||
}) {
|
||||
if (immediate) {
|
||||
do_reload_app(send_after_reload, save_pointer, save_compose, message_html);
|
||||
do_reload_app(send_after_reload, save_compose, message_html);
|
||||
}
|
||||
|
||||
if (reload_state.is_pending() || reload_state.is_in_progress()) {
|
||||
|
@ -242,7 +223,7 @@ export function initiate({
|
|||
let compose_started_handler;
|
||||
|
||||
function reload_from_idle() {
|
||||
do_reload_app(false, save_pointer, save_compose, message_html);
|
||||
do_reload_app(false, save_compose, message_html);
|
||||
}
|
||||
|
||||
// Make sure we always do a reload eventually after
|
||||
|
@ -296,7 +277,6 @@ reload_state.set_csrf_failed_handler(() => {
|
|||
|
||||
initiate({
|
||||
immediate: true,
|
||||
save_pointer: true,
|
||||
save_compose: true,
|
||||
});
|
||||
});
|
||||
|
|
|
@ -68,13 +68,15 @@ export function initialize() {
|
|||
}
|
||||
}
|
||||
|
||||
const pointer = Number.parseInt(vars.pointer, 10);
|
||||
const offset = Number.parseInt(vars.offset, 10);
|
||||
// We only restore pointer and offset for the current narrow, even if there are narrows that
|
||||
// were cached before the reload, they are no longer cached after the reload. We could possibly
|
||||
// store the pointer and offset for these narrows but it might lead to a confusing experience if
|
||||
// user gets back to these narrow much later (maybe days) and finds them at a random position in
|
||||
// narrow which they didn't navigate to while they were trying to just get to the latest unread
|
||||
// message in that narrow which will now take more effort to find.
|
||||
const narrow_pointer = Number.parseInt(vars.narrow_pointer, 10);
|
||||
const narrow_offset = Number.parseInt(vars.narrow_offset, 10);
|
||||
message_fetch.set_initial_pointer_and_offset({
|
||||
pointer: Number.isNaN(pointer) ? undefined : pointer,
|
||||
offset: Number.isNaN(offset) ? undefined : offset,
|
||||
narrow_pointer: Number.isNaN(narrow_pointer) ? undefined : narrow_pointer,
|
||||
narrow_offset: Number.isNaN(narrow_offset) ? undefined : narrow_offset,
|
||||
});
|
||||
|
|
|
@ -5,7 +5,6 @@ import * as blueslip from "./blueslip";
|
|||
import * as channel from "./channel";
|
||||
import * as echo from "./echo";
|
||||
import * as message_events from "./message_events";
|
||||
import * as message_lists from "./message_lists";
|
||||
import {page_params} from "./page_params";
|
||||
import * as reload from "./reload";
|
||||
import * as reload_state from "./reload_state";
|
||||
|
@ -128,10 +127,6 @@ function get_events_success(events) {
|
|||
}
|
||||
}
|
||||
|
||||
if (message_lists.home.selected_id() === -1 && !message_lists.home.visibly_empty()) {
|
||||
message_lists.home.select_id(message_lists.home.first().id, {then_scroll: false});
|
||||
}
|
||||
|
||||
if (update_message_events.length !== 0) {
|
||||
try {
|
||||
message_events.update_messages(update_message_events);
|
||||
|
@ -221,7 +216,6 @@ function get_events({dont_block = false} = {}) {
|
|||
event_queue_expired = true;
|
||||
reload.initiate({
|
||||
immediate: true,
|
||||
save_pointer: false,
|
||||
save_compose: true,
|
||||
});
|
||||
return;
|
||||
|
|
|
@ -175,7 +175,6 @@ export function dispatch_normal_event(event) {
|
|||
|
||||
case "web_reload_client": {
|
||||
const reload_options = {
|
||||
save_pointer: true,
|
||||
save_compose: true,
|
||||
message_html: "The application has been updated; reloading!",
|
||||
};
|
||||
|
|
|
@ -62,7 +62,6 @@ export function send_message(request, on_success, error) {
|
|||
// The error might be due to the server changing
|
||||
reload.initiate({
|
||||
immediate: true,
|
||||
save_pointer: true,
|
||||
save_compose: true,
|
||||
send_after_reload: true,
|
||||
});
|
||||
|
|
|
@ -14,6 +14,7 @@ import * as activity from "./activity";
|
|||
import * as activity_ui from "./activity_ui";
|
||||
import * as add_stream_options_popover from "./add_stream_options_popover";
|
||||
import * as alert_words from "./alert_words";
|
||||
import {all_messages_data} from "./all_messages_data";
|
||||
import * as audible_notifications from "./audible_notifications";
|
||||
import * as blueslip from "./blueslip";
|
||||
import * as bot_data from "./bot_data";
|
||||
|
@ -63,7 +64,6 @@ import * as markdown_config from "./markdown_config";
|
|||
import * as message_actions_popover from "./message_actions_popover";
|
||||
import * as message_edit_history from "./message_edit_history";
|
||||
import * as message_fetch from "./message_fetch";
|
||||
import * as message_list from "./message_list";
|
||||
import * as message_list_hover from "./message_list_hover";
|
||||
import * as message_list_tooltips from "./message_list_tooltips";
|
||||
import * as message_lists from "./message_lists";
|
||||
|
@ -726,7 +726,6 @@ export function initialize_everything(state_data) {
|
|||
|
||||
realm_logo.initialize();
|
||||
message_lists.initialize();
|
||||
message_list.initialize();
|
||||
recent_view_ui.initialize({
|
||||
on_click_participant(avatar_element, participant_user_id) {
|
||||
const user = people.get_by_user_id(participant_user_id);
|
||||
|
@ -736,7 +735,7 @@ export function initialize_everything(state_data) {
|
|||
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,
|
||||
msg_list_data: all_messages_data,
|
||||
recent_view: true,
|
||||
});
|
||||
},
|
||||
|
|
|
@ -47,10 +47,11 @@ export function handle_topic_updates(user_topic_event) {
|
|||
}
|
||||
|
||||
setTimeout(0, () => {
|
||||
/* Rerender the combined feed view if necessary, but defer until after
|
||||
* the browser has rendered the DOM updates scheduled above. */
|
||||
if (message_lists.current !== message_lists.home) {
|
||||
message_lists.home.update_muting_and_rerender();
|
||||
// Defer updates for any background-rendered messages lists until the visible one has been updated.
|
||||
for (const list of message_lists.all_rendered_message_lists()) {
|
||||
if (list.preserve_rendered_state && message_lists.current !== list) {
|
||||
list.update_muting_and_rerender();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -72,9 +72,7 @@ export function show(opts: {
|
|||
complete_rerender: () => void;
|
||||
is_recent_view?: boolean;
|
||||
}): void {
|
||||
if (narrow_state.has_shown_message_list_view) {
|
||||
message_lists.save_pre_narrow_offset_for_reload();
|
||||
}
|
||||
message_lists.save_pre_narrow_offset_for_reload();
|
||||
|
||||
if (opts.is_visible()) {
|
||||
// If we're already visible, E.g. because the user hit Esc
|
||||
|
|
|
@ -92,6 +92,7 @@ const user_groups = mock_esm("../src/user_groups");
|
|||
const user_group_edit = mock_esm("../src/user_group_edit");
|
||||
const overlays = mock_esm("../src/overlays");
|
||||
mock_esm("../src/giphy");
|
||||
const {Filter} = zrequire("filter");
|
||||
|
||||
const electron_bridge = set_global("electron_bridge", {});
|
||||
|
||||
|
@ -101,19 +102,17 @@ message_lists.current = {
|
|||
rerender_view: noop,
|
||||
data: {
|
||||
get_messages_sent_by_user: () => [],
|
||||
filter: {
|
||||
is_in_home: () => true,
|
||||
},
|
||||
filter: new Filter([]),
|
||||
},
|
||||
};
|
||||
message_lists.home = {
|
||||
const cached_message_list = {
|
||||
get_row: noop,
|
||||
rerender_view: noop,
|
||||
data: {
|
||||
get_messages_sent_by_user: () => [],
|
||||
},
|
||||
};
|
||||
message_lists.all_rendered_message_lists = () => [message_lists.home, message_lists.current];
|
||||
message_lists.all_rendered_message_lists = () => [cached_message_list, message_lists.current];
|
||||
|
||||
// page_params is highly coupled to dispatching now
|
||||
page_params.test_suite = false;
|
||||
|
@ -783,7 +782,6 @@ run_test("web_reload_client", ({override}) => {
|
|||
dispatch(event);
|
||||
assert.equal(stub.num_calls, 1);
|
||||
const args = stub.get_args("options");
|
||||
assert.equal(args.options.save_pointer, true);
|
||||
assert.equal(args.options.immediate, true);
|
||||
});
|
||||
|
||||
|
@ -890,13 +888,16 @@ run_test("user_settings", ({override}) => {
|
|||
message_lists.current.rerender = () => {
|
||||
called = true;
|
||||
};
|
||||
|
||||
override(message_lists.home, "rerender", noop);
|
||||
let called_for_cached_msg_list = false;
|
||||
cached_message_list.rerender = () => {
|
||||
called_for_cached_msg_list = true;
|
||||
};
|
||||
event = event_fixtures.user_settings__twenty_four_hour_time;
|
||||
user_settings.twenty_four_hour_time = false;
|
||||
dispatch(event);
|
||||
assert_same(user_settings.twenty_four_hour_time, true);
|
||||
assert_same(called, true);
|
||||
assert_same(called_for_cached_msg_list, true);
|
||||
|
||||
event = event_fixtures.user_settings__translate_emoticons;
|
||||
user_settings.translate_emoticons = false;
|
||||
|
|
|
@ -41,14 +41,15 @@ message_lists.current = {
|
|||
},
|
||||
change_message_id: noop,
|
||||
};
|
||||
message_lists.home = {
|
||||
const home_msg_list = {
|
||||
view: {
|
||||
rerender_messages: noop,
|
||||
change_message_id: noop,
|
||||
},
|
||||
preserver_rendered_state: true,
|
||||
change_message_id: noop,
|
||||
};
|
||||
message_lists.all_rendered_message_lists = () => [message_lists.home, message_lists.current];
|
||||
message_lists.all_rendered_message_lists = () => [home_msg_list, message_lists.current];
|
||||
|
||||
const echo = zrequire("echo");
|
||||
const people = zrequire("people");
|
||||
|
@ -76,7 +77,7 @@ run_test("process_from_server for un-echoed messages", () => {
|
|||
run_test("process_from_server for differently rendered messages", ({override}) => {
|
||||
let messages_to_rerender = [];
|
||||
|
||||
override(message_lists.home.view, "rerender_messages", (msgs) => {
|
||||
override(home_msg_list.view, "rerender_messages", (msgs) => {
|
||||
messages_to_rerender = msgs;
|
||||
});
|
||||
|
||||
|
@ -193,13 +194,13 @@ run_test("build_display_recipient", () => {
|
|||
});
|
||||
|
||||
run_test("update_message_lists", () => {
|
||||
message_lists.home.view = {};
|
||||
home_msg_list.view = {};
|
||||
|
||||
const stub = make_stub();
|
||||
const view_stub = make_stub();
|
||||
|
||||
message_lists.home.change_message_id = stub.f;
|
||||
message_lists.home.view.change_message_id = view_stub.f;
|
||||
home_msg_list.change_message_id = stub.f;
|
||||
home_msg_list.view.change_message_id = view_stub.f;
|
||||
|
||||
echo.update_message_lists({old_id: 401, new_id: 402});
|
||||
|
||||
|
|
|
@ -25,7 +25,6 @@ const message_lists = mock_esm("../src/message_lists");
|
|||
const message_notifications = mock_esm("../src/message_notifications");
|
||||
const message_util = mock_esm("../src/message_util");
|
||||
const pm_list = mock_esm("../src/pm_list");
|
||||
const recent_view_data = mock_esm("../src/recent_view_data");
|
||||
const stream_list = mock_esm("../src/stream_list");
|
||||
const unread_ops = mock_esm("../src/unread_ops");
|
||||
const unread_ui = mock_esm("../src/unread_ui");
|
||||
|
@ -39,8 +38,7 @@ message_lists.current = {
|
|||
},
|
||||
},
|
||||
};
|
||||
message_lists.home = message_lists.current;
|
||||
message_lists.all_rendered_message_lists = () => [message_lists.home, message_lists.current];
|
||||
message_lists.all_rendered_message_lists = () => [message_lists.current];
|
||||
|
||||
// And we will also test some real code, of course.
|
||||
const message_events = zrequire("message_events");
|
||||
|
@ -101,7 +99,6 @@ run_test("insert_message", ({override}) => {
|
|||
helper.redirect(message_notifications, "received_messages");
|
||||
helper.redirect(message_util, "add_new_messages_data");
|
||||
helper.redirect(message_util, "add_new_messages");
|
||||
helper.redirect(recent_view_data, "process_message");
|
||||
helper.redirect(stream_list, "update_streams_sidebar");
|
||||
helper.redirect(unread_ops, "process_visible");
|
||||
helper.redirect(unread_ui, "update_unread_counts");
|
||||
|
@ -116,12 +113,10 @@ run_test("insert_message", ({override}) => {
|
|||
[huddle_data, "process_loaded_messages"],
|
||||
[message_util, "add_new_messages_data"],
|
||||
[message_util, "add_new_messages"],
|
||||
[message_util, "add_new_messages"],
|
||||
[unread_ui, "update_unread_counts"],
|
||||
[unread_ops, "process_visible"],
|
||||
[message_notifications, "received_messages"],
|
||||
[stream_list, "update_streams_sidebar"],
|
||||
[recent_view_data, "process_message"],
|
||||
]);
|
||||
|
||||
// Despite all of our stubbing/mocking, the call to
|
||||
|
|
|
@ -59,8 +59,7 @@ const message_viewport = mock_esm("../src/message_viewport");
|
|||
const unread_ui = mock_esm("../src/unread_ui");
|
||||
|
||||
message_lists.current = {view: {}};
|
||||
message_lists.home = {view: {}};
|
||||
message_lists.all_rendered_message_lists = () => [message_lists.home, message_lists.current];
|
||||
message_lists.all_rendered_message_lists = () => [message_lists.current];
|
||||
|
||||
const message_store = zrequire("message_store");
|
||||
const stream_data = zrequire("stream_data");
|
||||
|
@ -107,7 +106,6 @@ run_test("unread_ops", ({override}) => {
|
|||
|
||||
// Ignore these interactions for now:
|
||||
override(message_lists.current.view, "show_message_as_read", noop);
|
||||
override(message_lists.home.view, "show_message_as_read", noop);
|
||||
override(desktop_notifications, "close_notification", noop);
|
||||
override(unread_ui, "update_unread_counts", noop);
|
||||
override(unread_ui, "notify_messages_remain_unread", noop);
|
||||
|
|
|
@ -14,7 +14,7 @@ const pm_list = mock_esm("../src/pm_list");
|
|||
const stream_list = mock_esm("../src/stream_list");
|
||||
const unread_ui = mock_esm("../src/unread_ui");
|
||||
message_lists.current = {};
|
||||
message_lists.all_rendered_message_lists = () => [message_lists.home, message_lists.current];
|
||||
message_lists.all_rendered_message_lists = () => [message_lists.current];
|
||||
|
||||
const people = zrequire("people");
|
||||
const message_events = zrequire("message_events");
|
||||
|
@ -95,7 +95,6 @@ run_test("update_messages", () => {
|
|||
rendered_mgs = msgs_to_rerender;
|
||||
assert.equal(message_content_edited, true);
|
||||
};
|
||||
message_lists.home = message_lists.current;
|
||||
|
||||
const side_effects = [
|
||||
[message_edit, "end_message_edit"],
|
||||
|
|
|
@ -1,490 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
const {strict: assert} = require("assert");
|
||||
|
||||
const _ = require("lodash");
|
||||
|
||||
const {mock_esm, set_global, zrequire} = require("./lib/namespace");
|
||||
const {run_test, noop} = require("./lib/test");
|
||||
const $ = require("./lib/zjquery");
|
||||
const {page_params} = require("./lib/zpage_params");
|
||||
|
||||
set_global("document", "document-stub");
|
||||
|
||||
function MessageListView() {
|
||||
return {
|
||||
maybe_rerender: noop,
|
||||
append: noop,
|
||||
prepend: noop,
|
||||
};
|
||||
}
|
||||
mock_esm("../src/message_list_view", {
|
||||
MessageListView,
|
||||
});
|
||||
|
||||
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,
|
||||
});
|
||||
|
||||
const channel = mock_esm("../src/channel");
|
||||
const message_helper = mock_esm("../src/message_helper");
|
||||
const message_lists = mock_esm("../src/message_lists");
|
||||
const message_util = mock_esm("../src/message_util");
|
||||
const stream_list = mock_esm("../src/stream_list", {
|
||||
maybe_scroll_narrow_into_view() {},
|
||||
});
|
||||
mock_esm("../src/message_feed_top_notices", {
|
||||
update_top_of_narrow_notices() {},
|
||||
});
|
||||
mock_esm("../src/message_feed_loading", {
|
||||
show_loading_older: noop,
|
||||
hide_loading_older: noop,
|
||||
show_loading_newer: noop,
|
||||
hide_loading_newer: noop,
|
||||
});
|
||||
set_global("document", "document-stub");
|
||||
|
||||
const message_fetch = zrequire("message_fetch");
|
||||
|
||||
const {all_messages_data} = zrequire("all_messages_data");
|
||||
const {Filter} = zrequire("../src/filter");
|
||||
const message_list = zrequire("message_list");
|
||||
const people = zrequire("people");
|
||||
|
||||
const alice = {
|
||||
email: "alice@example.com",
|
||||
user_id: 7,
|
||||
full_name: "Alice",
|
||||
};
|
||||
people.add_active_user(alice);
|
||||
|
||||
function make_home_msg_list() {
|
||||
const filter = new Filter([]);
|
||||
|
||||
const list = new message_list.MessageList({
|
||||
filter,
|
||||
});
|
||||
return list;
|
||||
}
|
||||
|
||||
function reset_lists() {
|
||||
message_lists.home = make_home_msg_list();
|
||||
message_lists.current = message_lists.home;
|
||||
all_messages_data.clear();
|
||||
}
|
||||
|
||||
function config_fake_channel(conf) {
|
||||
const self = {};
|
||||
let called;
|
||||
let called_with_newest_flag = false;
|
||||
|
||||
channel.get = (opts) => {
|
||||
assert.equal(opts.url, "/json/messages");
|
||||
// There's a separate call with anchor="newest" that happens
|
||||
// unconditionally; do basic verification of that call.
|
||||
if (opts.data.anchor === "newest") {
|
||||
assert.ok(!called_with_newest_flag, "Only one 'newest' call allowed");
|
||||
called_with_newest_flag = true;
|
||||
assert.equal(opts.data.num_after, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
assert.ok(!called || conf.can_call_again, "only use this for one call");
|
||||
if (!conf.can_call_again) {
|
||||
assert.equal(self.success, undefined);
|
||||
}
|
||||
assert.deepEqual(opts.data, conf.expected_opts_data);
|
||||
self.success = opts.success;
|
||||
called = true;
|
||||
};
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
function config_process_results(messages) {
|
||||
const self = {};
|
||||
|
||||
const messages_processed_for_new = [];
|
||||
|
||||
message_helper.process_new_message = (message) => {
|
||||
messages_processed_for_new.push(message);
|
||||
return message;
|
||||
};
|
||||
|
||||
message_util.do_unread_count_updates = (arg) => {
|
||||
assert.deepEqual(arg, messages);
|
||||
};
|
||||
|
||||
message_util.add_old_messages = (new_messages, msg_list) => {
|
||||
assert.deepEqual(new_messages, messages);
|
||||
msg_list.add_messages(new_messages);
|
||||
};
|
||||
|
||||
stream_list.update_streams_sidebar = noop;
|
||||
|
||||
self.verify = () => {
|
||||
assert.deepEqual(messages_processed_for_new, messages);
|
||||
};
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
function message_range(start, end) {
|
||||
return _.range(start, end).map((idx) => ({
|
||||
id: idx,
|
||||
}));
|
||||
}
|
||||
|
||||
const initialize_data = {
|
||||
initial_fetch: {
|
||||
req: {
|
||||
anchor: "first_unread",
|
||||
num_before: 200,
|
||||
num_after: 200,
|
||||
client_gravatar: true,
|
||||
// Same as message_lists.home.data.public_terms() after `reset_lists` is called.
|
||||
narrow: JSON.stringify([]),
|
||||
},
|
||||
resp: {
|
||||
messages: message_range(201, 801),
|
||||
found_newest: false,
|
||||
anchor: 444,
|
||||
},
|
||||
},
|
||||
|
||||
forward_fill: {
|
||||
req: {
|
||||
anchor: "800",
|
||||
num_before: 0,
|
||||
num_after: 1000,
|
||||
client_gravatar: true,
|
||||
narrow: JSON.stringify([]),
|
||||
},
|
||||
resp: {
|
||||
messages: message_range(800, 1000),
|
||||
found_newest: true,
|
||||
},
|
||||
},
|
||||
|
||||
back_fill: {
|
||||
req: {
|
||||
anchor: "201",
|
||||
num_before: 1000,
|
||||
num_after: 0,
|
||||
client_gravatar: true,
|
||||
narrow: JSON.stringify([]),
|
||||
},
|
||||
resp: {
|
||||
messages: message_range(100, 200),
|
||||
found_oldest: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
function test_fetch_success(opts) {
|
||||
const response = opts.response;
|
||||
const messages = response.messages;
|
||||
|
||||
const process_results = config_process_results(messages);
|
||||
opts.fetch.success(response);
|
||||
process_results.verify();
|
||||
}
|
||||
|
||||
function initial_fetch_step(finished_initial_fetch) {
|
||||
const self = {};
|
||||
|
||||
let fetch;
|
||||
const response = initialize_data.initial_fetch.resp;
|
||||
|
||||
self.prep = () => {
|
||||
fetch = config_fake_channel({
|
||||
expected_opts_data: initialize_data.initial_fetch.req,
|
||||
});
|
||||
|
||||
message_fetch.initialize(finished_initial_fetch);
|
||||
};
|
||||
|
||||
self.finish = () => {
|
||||
test_fetch_success({
|
||||
fetch,
|
||||
response,
|
||||
});
|
||||
};
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
function forward_fill_step() {
|
||||
const self = {};
|
||||
|
||||
let fetch;
|
||||
|
||||
self.prep = () => {
|
||||
/* Don't wait for the timeout before recursively calling `load_messages`. */
|
||||
const expected_delay = 150;
|
||||
set_global("setTimeout", (f, delay) => {
|
||||
assert.equal(delay, expected_delay);
|
||||
f();
|
||||
});
|
||||
fetch = config_fake_channel({
|
||||
expected_opts_data: initialize_data.forward_fill.req,
|
||||
});
|
||||
};
|
||||
|
||||
self.finish = () => {
|
||||
const response = initialize_data.forward_fill.resp;
|
||||
|
||||
let idle_config;
|
||||
$("document-stub").idle = (config) => {
|
||||
idle_config = config;
|
||||
};
|
||||
|
||||
test_fetch_success({
|
||||
fetch,
|
||||
response,
|
||||
});
|
||||
|
||||
assert.equal(idle_config.idle, 10000);
|
||||
|
||||
return idle_config;
|
||||
};
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
function test_backfill_idle(idle_config) {
|
||||
const fetch = config_fake_channel({
|
||||
expected_opts_data: initialize_data.back_fill.req,
|
||||
});
|
||||
|
||||
const response = initialize_data.back_fill.resp;
|
||||
|
||||
idle_config.onIdle();
|
||||
|
||||
test_fetch_success({
|
||||
fetch,
|
||||
response,
|
||||
});
|
||||
}
|
||||
|
||||
run_test("initialize", () => {
|
||||
reset_lists();
|
||||
|
||||
let home_loaded = false;
|
||||
page_params.unread_msgs = {
|
||||
old_unreads_missing: false,
|
||||
};
|
||||
|
||||
function finished_initial_fetch() {
|
||||
home_loaded = true;
|
||||
}
|
||||
|
||||
const step1 = initial_fetch_step(finished_initial_fetch);
|
||||
|
||||
step1.prep();
|
||||
|
||||
const step2 = forward_fill_step();
|
||||
|
||||
step2.prep();
|
||||
step1.finish();
|
||||
|
||||
assert.ok(!home_loaded);
|
||||
const idle_config = step2.finish();
|
||||
assert.ok(home_loaded);
|
||||
|
||||
test_backfill_idle(idle_config);
|
||||
});
|
||||
|
||||
function simulate_narrow() {
|
||||
const filter = new Filter([{operator: "dm", operand: alice.email}]);
|
||||
|
||||
const msg_list = new message_list.MessageList({
|
||||
filter,
|
||||
});
|
||||
message_lists.current = msg_list;
|
||||
|
||||
return msg_list;
|
||||
}
|
||||
|
||||
run_test("loading_newer", () => {
|
||||
function test_dup_new_fetch(msg_list) {
|
||||
assert.equal(msg_list.data.fetch_status.can_load_newer_messages(), false);
|
||||
message_fetch.maybe_load_newer_messages({
|
||||
msg_list,
|
||||
});
|
||||
}
|
||||
|
||||
function test_happy_path(opts) {
|
||||
const msg_list = opts.msg_list;
|
||||
const data = opts.data;
|
||||
|
||||
const fetch = config_fake_channel({
|
||||
expected_opts_data: data.req,
|
||||
can_call_again: true,
|
||||
});
|
||||
|
||||
message_fetch.maybe_load_newer_messages({
|
||||
msg_list,
|
||||
show_loading: noop,
|
||||
hide_loading: noop,
|
||||
});
|
||||
|
||||
test_dup_new_fetch(msg_list);
|
||||
|
||||
test_fetch_success({
|
||||
fetch,
|
||||
response: data.resp,
|
||||
});
|
||||
}
|
||||
|
||||
(function test_narrow() {
|
||||
let msg_list = simulate_narrow();
|
||||
page_params.unread_msgs = {
|
||||
old_unreads_missing: true,
|
||||
};
|
||||
|
||||
// Test what happens when an empty list is returned with found_newest false.
|
||||
const empty_list_data = {
|
||||
req: {
|
||||
anchor: "oldest",
|
||||
num_before: 0,
|
||||
num_after: 100,
|
||||
narrow: `[{"negated":false,"operator":"dm","operand":[${alice.user_id}]}]`,
|
||||
client_gravatar: true,
|
||||
},
|
||||
resp: {
|
||||
messages: message_range(500, 600),
|
||||
found_newest: false,
|
||||
},
|
||||
};
|
||||
|
||||
test_happy_path({
|
||||
msg_list,
|
||||
data: empty_list_data,
|
||||
});
|
||||
|
||||
msg_list = simulate_narrow();
|
||||
msg_list.append_to_view = noop;
|
||||
// Instead of using 444 as page_param.pointer, we
|
||||
// should have a message with that id in the message_list.
|
||||
msg_list.append(message_range(444, 445), false);
|
||||
|
||||
const data = {
|
||||
req: {
|
||||
anchor: "444",
|
||||
num_before: 0,
|
||||
num_after: 100,
|
||||
narrow: `[{"negated":false,"operator":"dm","operand":[${alice.user_id}]}]`,
|
||||
client_gravatar: true,
|
||||
},
|
||||
resp: {
|
||||
messages: message_range(500, 600),
|
||||
found_newest: false,
|
||||
},
|
||||
};
|
||||
|
||||
test_happy_path({
|
||||
msg_list,
|
||||
data,
|
||||
});
|
||||
|
||||
assert.equal(msg_list.data.fetch_status.can_load_newer_messages(), true);
|
||||
|
||||
// The server successfully responded with messages having id's from 500-599.
|
||||
// We test for the case that this was the last batch of messages for the narrow
|
||||
// so no more fetching should occur.
|
||||
// And also while fetching for the above condition the server received a new message
|
||||
// event, updating the last message's id for that narrow to 600 from 599.
|
||||
data.resp.found_newest = true;
|
||||
msg_list.data.fetch_status.update_expected_max_message_id([{id: 600}]);
|
||||
|
||||
test_happy_path({
|
||||
msg_list,
|
||||
data,
|
||||
});
|
||||
|
||||
// To handle this special case we should allow another fetch to occur,
|
||||
// since the last message event's data had been discarded.
|
||||
// This fetch goes on until the newest message has been found.
|
||||
assert.equal(msg_list.data.fetch_status.can_load_newer_messages(), false);
|
||||
})();
|
||||
|
||||
(function test_home() {
|
||||
reset_lists();
|
||||
let msg_list = message_lists.home;
|
||||
|
||||
// Test what happens when an empty list is returned with found_newest false.
|
||||
const empty_list_data = {
|
||||
req: {
|
||||
anchor: "oldest",
|
||||
num_before: 0,
|
||||
num_after: 100,
|
||||
client_gravatar: true,
|
||||
narrow: JSON.stringify([]),
|
||||
},
|
||||
resp: {
|
||||
messages: message_range(500, 600),
|
||||
found_newest: false,
|
||||
},
|
||||
};
|
||||
|
||||
test_happy_path({
|
||||
msg_list,
|
||||
data: empty_list_data,
|
||||
});
|
||||
|
||||
reset_lists();
|
||||
|
||||
const data = [
|
||||
{
|
||||
req: {
|
||||
anchor: "444",
|
||||
num_before: 0,
|
||||
num_after: 100,
|
||||
client_gravatar: true,
|
||||
narrow: JSON.stringify([]),
|
||||
},
|
||||
resp: {
|
||||
messages: message_range(500, 600),
|
||||
found_newest: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
req: {
|
||||
anchor: "599",
|
||||
num_before: 0,
|
||||
num_after: 100,
|
||||
client_gravatar: true,
|
||||
narrow: JSON.stringify([]),
|
||||
},
|
||||
resp: {
|
||||
messages: message_range(700, 800),
|
||||
found_newest: true,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
msg_list = message_lists.home;
|
||||
all_messages_data.append(message_range(444, 445), false);
|
||||
|
||||
test_happy_path({
|
||||
msg_list,
|
||||
data: data[0],
|
||||
});
|
||||
|
||||
assert.equal(msg_list.data.fetch_status.can_load_newer_messages(), true);
|
||||
|
||||
test_happy_path({
|
||||
msg_list,
|
||||
data: data[1],
|
||||
});
|
||||
|
||||
assert.equal(msg_list.data.fetch_status.can_load_newer_messages(), false);
|
||||
})();
|
||||
});
|
|
@ -21,14 +21,6 @@ const compose_recipient = mock_esm("../src/compose_recipient");
|
|||
const message_fetch = mock_esm("../src/message_fetch");
|
||||
const message_list = mock_esm("../src/message_list");
|
||||
const message_lists = mock_esm("../src/message_lists", {
|
||||
home: {
|
||||
view: {
|
||||
$list: {
|
||||
removeClass: noop,
|
||||
addClass: noop,
|
||||
},
|
||||
},
|
||||
},
|
||||
current: {
|
||||
view: {
|
||||
$list: {
|
||||
|
@ -44,6 +36,9 @@ const message_lists = mock_esm("../src/message_lists", {
|
|||
update_current_message_list(msg_list) {
|
||||
message_lists.current = msg_list;
|
||||
},
|
||||
all_rendered_message_lists() {
|
||||
return [message_lists.current];
|
||||
},
|
||||
});
|
||||
const message_feed_top_notices = mock_esm("../src/message_feed_top_notices");
|
||||
const message_feed_loading = mock_esm("../src/message_feed_loading");
|
||||
|
|
|
@ -10,18 +10,11 @@ const {page_params} = require("./lib/zpage_params");
|
|||
set_global("addEventListener", noop);
|
||||
|
||||
const channel = mock_esm("../src/channel");
|
||||
const message_lists = mock_esm("../src/message_lists");
|
||||
mock_esm("../src/reload_state", {
|
||||
is_in_progress() {
|
||||
return false;
|
||||
},
|
||||
});
|
||||
message_lists.home = {
|
||||
select_id: noop,
|
||||
selected_id() {
|
||||
return 1;
|
||||
},
|
||||
};
|
||||
page_params.test_suite = false;
|
||||
|
||||
// we also directly write to pointer
|
||||
|
|
|
@ -80,7 +80,6 @@ run_test("transmit_message_ajax_reload_pending", () => {
|
|||
reload_initiated = true;
|
||||
assert.deepEqual(opts, {
|
||||
immediate: true,
|
||||
save_pointer: true,
|
||||
save_compose: true,
|
||||
send_after_reload: true,
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue