mirror of https://github.com/zulip/zulip.git
message_view: Convert module to typescript.
This commit is contained in:
parent
b0785f0f2c
commit
af915523a7
|
@ -155,7 +155,7 @@ EXEMPT_FILES = make_set(
|
||||||
"web/src/message_scroll.js",
|
"web/src/message_scroll.js",
|
||||||
"web/src/message_scroll_state.ts",
|
"web/src/message_scroll_state.ts",
|
||||||
"web/src/message_util.ts",
|
"web/src/message_util.ts",
|
||||||
"web/src/message_view.js",
|
"web/src/message_view.ts",
|
||||||
"web/src/message_view_header.ts",
|
"web/src/message_view_header.ts",
|
||||||
"web/src/message_viewport.ts",
|
"web/src/message_viewport.ts",
|
||||||
"web/src/messages_overlay_ui.ts",
|
"web/src/messages_overlay_ui.ts",
|
||||||
|
|
|
@ -19,7 +19,7 @@ import {user_settings} from "./user_settings";
|
||||||
|
|
||||||
export type RenderInfo = {need_user_to_scroll: boolean};
|
export type RenderInfo = {need_user_to_scroll: boolean};
|
||||||
|
|
||||||
type SelectIdOpts = {
|
export type SelectIdOpts = {
|
||||||
then_scroll?: boolean;
|
then_scroll?: boolean;
|
||||||
target_scroll_offset?: number;
|
target_scroll_offset?: number;
|
||||||
use_closest?: boolean;
|
use_closest?: boolean;
|
||||||
|
@ -66,12 +66,17 @@ export class MessageList {
|
||||||
last_message_historical?: boolean;
|
last_message_historical?: boolean;
|
||||||
should_trigger_message_selected_event?: boolean;
|
should_trigger_message_selected_event?: boolean;
|
||||||
|
|
||||||
constructor(opts: {
|
constructor(
|
||||||
|
opts: (
|
||||||
|
| {
|
||||||
data: MessageListData;
|
data: MessageListData;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
data?: undefined;
|
||||||
filter: Filter;
|
filter: Filter;
|
||||||
excludes_muted_topics: boolean;
|
}
|
||||||
is_node_test: boolean;
|
) & {excludes_muted_topics?: boolean; is_node_test?: boolean},
|
||||||
}) {
|
) {
|
||||||
MessageList.id_counter += 1;
|
MessageList.id_counter += 1;
|
||||||
this.id = MessageList.id_counter;
|
this.id = MessageList.id_counter;
|
||||||
// The MessageListData keeps track of the actual sequence of
|
// The MessageListData keeps track of the actual sequence of
|
||||||
|
@ -85,7 +90,7 @@ export class MessageList {
|
||||||
const filter = opts.filter;
|
const filter = opts.filter;
|
||||||
|
|
||||||
this.data = new MessageListData({
|
this.data = new MessageListData({
|
||||||
excludes_muted_topics: opts.excludes_muted_topics,
|
excludes_muted_topics: opts.excludes_muted_topics ?? false,
|
||||||
filter,
|
filter,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import * as Sentry from "@sentry/browser";
|
import * as Sentry from "@sentry/browser";
|
||||||
import $ from "jquery";
|
import $ from "jquery";
|
||||||
import assert from "minimalistic-assert";
|
import assert from "minimalistic-assert";
|
||||||
|
import {z} from "zod";
|
||||||
|
|
||||||
import * as activity_ui from "./activity_ui";
|
import * as activity_ui from "./activity_ui";
|
||||||
import {all_messages_data} from "./all_messages_data";
|
import {all_messages_data} from "./all_messages_data";
|
||||||
|
@ -15,6 +16,7 @@ import * as compose_recipient from "./compose_recipient";
|
||||||
import * as compose_state from "./compose_state";
|
import * as compose_state from "./compose_state";
|
||||||
import * as condense from "./condense";
|
import * as condense from "./condense";
|
||||||
import * as feedback_widget from "./feedback_widget";
|
import * as feedback_widget from "./feedback_widget";
|
||||||
|
import type {FetchStatus} from "./fetch_status";
|
||||||
import {Filter} from "./filter";
|
import {Filter} from "./filter";
|
||||||
import * as hash_parser from "./hash_parser";
|
import * as hash_parser from "./hash_parser";
|
||||||
import * as hash_util from "./hash_util";
|
import * as hash_util from "./hash_util";
|
||||||
|
@ -27,11 +29,13 @@ import * as message_feed_loading from "./message_feed_loading";
|
||||||
import * as message_feed_top_notices from "./message_feed_top_notices";
|
import * as message_feed_top_notices from "./message_feed_top_notices";
|
||||||
import * as message_fetch from "./message_fetch";
|
import * as message_fetch from "./message_fetch";
|
||||||
import * as message_helper from "./message_helper";
|
import * as message_helper from "./message_helper";
|
||||||
|
import type {MessageList, SelectIdOpts} from "./message_list";
|
||||||
import * as message_list from "./message_list";
|
import * as message_list from "./message_list";
|
||||||
import {MessageListData} from "./message_list_data";
|
import {MessageListData} from "./message_list_data";
|
||||||
import * as message_list_data_cache from "./message_list_data_cache";
|
import * as message_list_data_cache from "./message_list_data_cache";
|
||||||
import * as message_lists from "./message_lists";
|
import * as message_lists from "./message_lists";
|
||||||
import * as message_scroll_state from "./message_scroll_state";
|
import * as message_scroll_state from "./message_scroll_state";
|
||||||
|
import {raw_message_schema} from "./message_store";
|
||||||
import * as message_store from "./message_store";
|
import * as message_store from "./message_store";
|
||||||
import * as message_view_header from "./message_view_header";
|
import * as message_view_header from "./message_view_header";
|
||||||
import * as message_viewport from "./message_viewport";
|
import * as message_viewport from "./message_viewport";
|
||||||
|
@ -48,6 +52,7 @@ import * as resize from "./resize";
|
||||||
import * as scheduled_messages_feed_ui from "./scheduled_messages_feed_ui";
|
import * as scheduled_messages_feed_ui from "./scheduled_messages_feed_ui";
|
||||||
import {web_mark_read_on_scroll_policy_values} from "./settings_config";
|
import {web_mark_read_on_scroll_policy_values} from "./settings_config";
|
||||||
import * as spectators from "./spectators";
|
import * as spectators from "./spectators";
|
||||||
|
import type {NarrowTerm} from "./state_data";
|
||||||
import {realm} from "./state_data";
|
import {realm} from "./state_data";
|
||||||
import * as stream_data from "./stream_data";
|
import * as stream_data from "./stream_data";
|
||||||
import * as stream_list from "./stream_list";
|
import * as stream_list from "./stream_list";
|
||||||
|
@ -61,7 +66,11 @@ import * as util from "./util";
|
||||||
|
|
||||||
const LARGER_THAN_MAX_MESSAGE_ID = 10000000000000000;
|
const LARGER_THAN_MAX_MESSAGE_ID = 10000000000000000;
|
||||||
|
|
||||||
export function reset_ui_state(opts) {
|
const fetch_message_response_schema = z.object({
|
||||||
|
message: raw_message_schema,
|
||||||
|
});
|
||||||
|
|
||||||
|
export function reset_ui_state(opts: {trigger?: string}): void {
|
||||||
// Resets the state of various visual UI elements that are
|
// Resets the state of various visual UI elements that are
|
||||||
// a function of the current narrow.
|
// a function of the current narrow.
|
||||||
narrow_banner.hide_empty_narrow_message();
|
narrow_banner.hide_empty_narrow_message();
|
||||||
|
@ -79,7 +88,7 @@ export function reset_ui_state(opts) {
|
||||||
compose_banner.clear_message_sent_banners(true, skip_automatic_new_visibility_policy_banner);
|
compose_banner.clear_message_sent_banners(true, skip_automatic_new_visibility_policy_banner);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function changehash(newhash, trigger) {
|
export function changehash(newhash: string, trigger?: string): void {
|
||||||
if (browser_history.state.changing_hash) {
|
if (browser_history.state.changing_hash) {
|
||||||
// If we retargeted the narrow operation because a message was moved,
|
// If we retargeted the narrow operation because a message was moved,
|
||||||
// we want to have the current narrow hash in the browser history.
|
// we want to have the current narrow hash in the browser history.
|
||||||
|
@ -104,7 +113,7 @@ export function changehash(newhash, trigger) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function update_hash_to_match_filter(filter, trigger) {
|
export function update_hash_to_match_filter(filter: Filter, trigger?: string): void {
|
||||||
if (browser_history.state.changing_hash && trigger !== "retarget message location") {
|
if (browser_history.state.changing_hash && trigger !== "retarget message location") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -116,7 +125,22 @@ export function update_hash_to_match_filter(filter, trigger) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function create_and_update_message_list(filter, id_info, opts) {
|
type IdInfo = {
|
||||||
|
target_id: number | undefined;
|
||||||
|
final_select_id: number | undefined;
|
||||||
|
local_select_id: number | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
function create_and_update_message_list(
|
||||||
|
filter: Filter,
|
||||||
|
id_info: IdInfo,
|
||||||
|
opts: ShowMessageViewOpts & {
|
||||||
|
then_select_id: number;
|
||||||
|
},
|
||||||
|
): {
|
||||||
|
msg_list: MessageList;
|
||||||
|
restore_rendered_list: boolean;
|
||||||
|
} {
|
||||||
const excludes_muted_topics = filter.excludes_muted_topics();
|
const excludes_muted_topics = filter.excludes_muted_topics();
|
||||||
|
|
||||||
// Check if we already have a rendered message list for the `filter`.
|
// Check if we already have a rendered message list for the `filter`.
|
||||||
|
@ -221,13 +245,16 @@ function create_and_update_message_list(filter, id_info, opts) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function handle_post_message_list_change(
|
function handle_post_message_list_change(
|
||||||
id_info,
|
id_info: IdInfo,
|
||||||
msg_list,
|
msg_list: MessageList,
|
||||||
opts,
|
opts: {
|
||||||
select_immediately,
|
change_hash: boolean;
|
||||||
select_opts,
|
show_more_topics: boolean;
|
||||||
then_select_offset,
|
} & ShowMessageViewOpts,
|
||||||
) {
|
select_immediately: boolean,
|
||||||
|
select_opts: SelectIdOpts,
|
||||||
|
then_select_offset: number | undefined,
|
||||||
|
): void {
|
||||||
// Important: We need to consider opening the compose box
|
// Important: We need to consider opening the compose box
|
||||||
// before calling render_message_list_with_selected_message, so that the logic in
|
// before calling render_message_list_with_selected_message, so that the logic in
|
||||||
// recenter_view for positioning the currently selected
|
// recenter_view for positioning the currently selected
|
||||||
|
@ -253,7 +280,10 @@ function handle_post_message_list_change(
|
||||||
compose_recipient.handle_middle_pane_transition();
|
compose_recipient.handle_middle_pane_transition();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function try_rendering_locally_for_same_narrow(filter, opts) {
|
export function try_rendering_locally_for_same_narrow(
|
||||||
|
filter: Filter,
|
||||||
|
opts: ShowMessageViewOpts,
|
||||||
|
): boolean {
|
||||||
const current_filter = narrow_state.filter();
|
const current_filter = narrow_state.filter();
|
||||||
let target_scroll_offset;
|
let target_scroll_offset;
|
||||||
if (!current_filter) {
|
if (!current_filter) {
|
||||||
|
@ -265,7 +295,7 @@ export function try_rendering_locally_for_same_narrow(filter, opts) {
|
||||||
target_id = opts.then_select_id;
|
target_id = opts.then_select_id;
|
||||||
target_scroll_offset = opts.then_select_offset;
|
target_scroll_offset = opts.then_select_offset;
|
||||||
} else if (filter.has_operator("near")) {
|
} else if (filter.has_operator("near")) {
|
||||||
target_id = Number.parseInt(filter.operands("near")[0], 10);
|
target_id = Number.parseInt(filter.operands("near")[0]!, 10);
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -289,11 +319,12 @@ export function try_rendering_locally_for_same_narrow(filter, opts) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(message_lists.current !== undefined);
|
||||||
const currently_selected_id = message_lists.current?.selected_id();
|
const currently_selected_id = message_lists.current?.selected_id();
|
||||||
if (currently_selected_id !== target_id) {
|
if (currently_selected_id !== target_id) {
|
||||||
message_lists.current.select_id(target_id, {
|
message_lists.current.select_id(target_id, {
|
||||||
then_scroll: true,
|
then_scroll: true,
|
||||||
target_scroll_offset,
|
...(target_scroll_offset !== undefined && {target_scroll_offset}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,7 +334,18 @@ export function try_rendering_locally_for_same_narrow(filter, opts) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function show(raw_terms, show_opts) {
|
type ShowMessageViewOpts = {
|
||||||
|
force_rerender?: boolean;
|
||||||
|
force_close?: boolean;
|
||||||
|
change_hash?: boolean;
|
||||||
|
trigger?: string;
|
||||||
|
fetched_target_message?: boolean;
|
||||||
|
then_select_id?: number;
|
||||||
|
then_select_offset?: number;
|
||||||
|
show_more_topics?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function show(raw_terms: NarrowTerm[], show_opts: ShowMessageViewOpts): void {
|
||||||
/* Main entry point for switching to a new view / message list.
|
/* Main entry point for switching to a new view / message list.
|
||||||
|
|
||||||
Supported parameters:
|
Supported parameters:
|
||||||
|
@ -393,7 +435,6 @@ export function show(raw_terms, show_opts) {
|
||||||
|
|
||||||
const opts = {
|
const opts = {
|
||||||
then_select_id: -1,
|
then_select_id: -1,
|
||||||
then_select_offset: undefined,
|
|
||||||
change_hash: true,
|
change_hash: true,
|
||||||
trigger: "unknown",
|
trigger: "unknown",
|
||||||
show_more_topics: false,
|
show_more_topics: false,
|
||||||
|
@ -417,7 +458,7 @@ export function show(raw_terms, show_opts) {
|
||||||
const scope = Sentry.getCurrentHub().pushScope();
|
const scope = Sentry.getCurrentHub().pushScope();
|
||||||
scope.setSpan(span);
|
scope.setSpan(span);
|
||||||
|
|
||||||
const id_info = {
|
const id_info: IdInfo = {
|
||||||
target_id: undefined,
|
target_id: undefined,
|
||||||
local_select_id: undefined,
|
local_select_id: undefined,
|
||||||
final_select_id: undefined,
|
final_select_id: undefined,
|
||||||
|
@ -428,10 +469,10 @@ export function show(raw_terms, show_opts) {
|
||||||
// These two narrowing operators specify what message should be
|
// These two narrowing operators specify what message should be
|
||||||
// selected and should be the center of the narrow.
|
// selected and should be the center of the narrow.
|
||||||
if (filter.has_operator("near")) {
|
if (filter.has_operator("near")) {
|
||||||
id_info.target_id = Number.parseInt(filter.operands("near")[0], 10);
|
id_info.target_id = Number.parseInt(filter.operands("near")[0]!, 10);
|
||||||
}
|
}
|
||||||
if (filter.has_operator("id")) {
|
if (filter.has_operator("id")) {
|
||||||
id_info.target_id = Number.parseInt(filter.operands("id")[0], 10);
|
id_info.target_id = Number.parseInt(filter.operands("id")[0]!, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Narrow with near / id operator. There are two possibilities:
|
// Narrow with near / id operator. There are two possibilities:
|
||||||
|
@ -453,9 +494,9 @@ export function show(raw_terms, show_opts) {
|
||||||
// the stream/topic pair that was requested to some other
|
// the stream/topic pair that was requested to some other
|
||||||
// location, then we should retarget this narrow operation
|
// location, then we should retarget this narrow operation
|
||||||
// to where the message is located now.
|
// to where the message is located now.
|
||||||
const narrow_topic = filter.operands("topic")[0];
|
const narrow_topic = filter.operands("topic")[0]!;
|
||||||
const narrow_stream_data = stream_data.get_sub_by_id_string(
|
const narrow_stream_data = stream_data.get_sub_by_id_string(
|
||||||
filter.operands("channel")[0],
|
filter.operands("channel")[0]!,
|
||||||
);
|
);
|
||||||
if (!narrow_stream_data) {
|
if (!narrow_stream_data) {
|
||||||
// The stream id is invalid or incorrect in the URL.
|
// The stream id is invalid or incorrect in the URL.
|
||||||
|
@ -493,6 +534,7 @@ export function show(raw_terms, show_opts) {
|
||||||
// topic and then moved back to the current topic. In this
|
// topic and then moved back to the current topic. In this
|
||||||
// situation, narrow_exists_in_edit_history will be true,
|
// situation, narrow_exists_in_edit_history will be true,
|
||||||
// but we don't need to redirect the narrow.
|
// but we don't need to redirect the narrow.
|
||||||
|
assert(target_message.type === "stream");
|
||||||
const narrow_matches_target_message = util.same_stream_and_topic(
|
const narrow_matches_target_message = util.same_stream_and_topic(
|
||||||
target_message,
|
target_message,
|
||||||
narrow_dict,
|
narrow_dict,
|
||||||
|
@ -522,7 +564,8 @@ export function show(raw_terms, show_opts) {
|
||||||
// for it.
|
// for it.
|
||||||
channel.get({
|
channel.get({
|
||||||
url: `/json/messages/${id_info.target_id}`,
|
url: `/json/messages/${id_info.target_id}`,
|
||||||
success(data) {
|
success(raw_data) {
|
||||||
|
const data = fetch_message_response_schema.parse(raw_data);
|
||||||
// After the message is fetched, we make the
|
// After the message is fetched, we make the
|
||||||
// message locally available and then call
|
// message locally available and then call
|
||||||
// message_view.show recursively, setting a flag to
|
// message_view.show recursively, setting a flag to
|
||||||
|
@ -585,7 +628,7 @@ export function show(raw_terms, show_opts) {
|
||||||
const $row = message_lists.current.get_row(opts.then_select_id);
|
const $row = message_lists.current.get_row(opts.then_select_id);
|
||||||
if ($row.length > 0) {
|
if ($row.length > 0) {
|
||||||
const row_props = $row.get_offset_to_window();
|
const row_props = $row.get_offset_to_window();
|
||||||
const navbar_height = $("#navbar-fixed-container").height();
|
const navbar_height = $("#navbar-fixed-container").height()!;
|
||||||
// 30px height + 10px top margin.
|
// 30px height + 10px top margin.
|
||||||
const compose_box_top = $("#compose").get_offset_to_window().top;
|
const compose_box_top = $("#compose").get_offset_to_window().top;
|
||||||
const sticky_header_outer_height = 40;
|
const sticky_header_outer_height = 40;
|
||||||
|
@ -615,9 +658,9 @@ export function show(raw_terms, show_opts) {
|
||||||
opts,
|
opts,
|
||||||
);
|
);
|
||||||
|
|
||||||
let select_immediately;
|
let select_immediately: boolean;
|
||||||
let select_opts;
|
let select_opts: SelectIdOpts;
|
||||||
let then_select_offset;
|
let then_select_offset: number | undefined;
|
||||||
if (restore_rendered_list) {
|
if (restore_rendered_list) {
|
||||||
select_immediately = true;
|
select_immediately = true;
|
||||||
select_opts = {
|
select_opts = {
|
||||||
|
@ -636,6 +679,7 @@ export function show(raw_terms, show_opts) {
|
||||||
// read messages again in the combined feed if user has
|
// read messages again in the combined feed if user has
|
||||||
// marked some messages as unread in the last combined
|
// marked some messages as unread in the last combined
|
||||||
// feed session and thus prevented reading.
|
// feed session and thus prevented reading.
|
||||||
|
assert(message_lists.current !== undefined);
|
||||||
message_lists.current.resume_reading();
|
message_lists.current.resume_reading();
|
||||||
// Reset the collapsed status of messages rows.
|
// Reset the collapsed status of messages rows.
|
||||||
condense.condense_and_collapse(message_lists.current.view.$list.find(".message_row"));
|
condense.condense_and_collapse(message_lists.current.view.$list.find(".message_row"));
|
||||||
|
@ -754,11 +798,13 @@ export function show(raw_terms, show_opts) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function navigate_to_anchor_message({
|
function navigate_to_anchor_message(opts: {
|
||||||
anchor,
|
anchor: string;
|
||||||
fetch_status_shows_anchor_fetched,
|
fetch_status_shows_anchor_fetched: (fetch_status: FetchStatus) => boolean;
|
||||||
message_list_data_to_target_message_id,
|
message_list_data_to_target_message_id: (data: MessageListData) => number;
|
||||||
}) {
|
}): void {
|
||||||
|
const {anchor, fetch_status_shows_anchor_fetched, message_list_data_to_target_message_id} =
|
||||||
|
opts;
|
||||||
// The function navigates user to the anchor in the current
|
// The function navigates user to the anchor in the current
|
||||||
// message list. We don't use `message_view.show` here due
|
// message list. We don't use `message_view.show` here due
|
||||||
// to following reasons:
|
// to following reasons:
|
||||||
|
@ -776,13 +822,15 @@ function navigate_to_anchor_message({
|
||||||
//
|
//
|
||||||
// These functions are scoped inside `navigate_to_anchor_message` to
|
// These functions are scoped inside `navigate_to_anchor_message` to
|
||||||
// to avoid them being used for any other purpose.
|
// to avoid them being used for any other purpose.
|
||||||
function duplicate_current_msg_list_with_new_data(data) {
|
function duplicate_current_msg_list_with_new_data(data: MessageListData): MessageList {
|
||||||
|
assert(message_lists.current !== undefined);
|
||||||
const msg_list = new message_list.MessageList({data});
|
const msg_list = new message_list.MessageList({data});
|
||||||
msg_list.reading_prevented = message_lists.current.reading_prevented;
|
msg_list.reading_prevented = message_lists.current.reading_prevented;
|
||||||
return msg_list;
|
return msg_list;
|
||||||
}
|
}
|
||||||
|
|
||||||
function select_msg_id(msg_id, select_opts) {
|
function select_msg_id(msg_id: number, select_opts?: SelectIdOpts): void {
|
||||||
|
assert(message_lists.current !== undefined);
|
||||||
message_lists.current.select_id(msg_id, {
|
message_lists.current.select_id(msg_id, {
|
||||||
then_scroll: true,
|
then_scroll: true,
|
||||||
from_scroll: false,
|
from_scroll: false,
|
||||||
|
@ -790,13 +838,14 @@ function navigate_to_anchor_message({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function select_anchor_using_data(data) {
|
function select_anchor_using_data(data: MessageListData): void {
|
||||||
const msg_list = duplicate_current_msg_list_with_new_data(data);
|
const msg_list = duplicate_current_msg_list_with_new_data(data);
|
||||||
message_lists.update_current_message_list(msg_list);
|
message_lists.update_current_message_list(msg_list);
|
||||||
// `force_rerender` is required to render the new data.
|
// `force_rerender` is required to render the new data.
|
||||||
select_msg_id(message_list_data_to_target_message_id(data), {force_rerender: true});
|
select_msg_id(message_list_data_to_target_message_id(data), {force_rerender: true});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(message_lists.current !== undefined);
|
||||||
if (fetch_status_shows_anchor_fetched(message_lists.current.data.fetch_status)) {
|
if (fetch_status_shows_anchor_fetched(message_lists.current.data.fetch_status)) {
|
||||||
select_msg_id(message_list_data_to_target_message_id(message_lists.current.data));
|
select_msg_id(message_list_data_to_target_message_id(message_lists.current.data));
|
||||||
} else if (fetch_status_shows_anchor_fetched(all_messages_data.fetch_status)) {
|
} else if (fetch_status_shows_anchor_fetched(all_messages_data.fetch_status)) {
|
||||||
|
@ -830,7 +879,7 @@ function navigate_to_anchor_message({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function fast_track_current_msg_list_to_anchor(anchor) {
|
export function fast_track_current_msg_list_to_anchor(anchor: string): void {
|
||||||
assert(message_lists.current !== undefined);
|
assert(message_lists.current !== undefined);
|
||||||
if (message_lists.current.visibly_empty()) {
|
if (message_lists.current.visibly_empty()) {
|
||||||
return;
|
return;
|
||||||
|
@ -843,7 +892,7 @@ export function fast_track_current_msg_list_to_anchor(anchor) {
|
||||||
return fetch_status.has_found_oldest();
|
return fetch_status.has_found_oldest();
|
||||||
},
|
},
|
||||||
message_list_data_to_target_message_id(msg_list_data) {
|
message_list_data_to_target_message_id(msg_list_data) {
|
||||||
return msg_list_data.first().id;
|
return msg_list_data.first()!.id;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else if (anchor === "newest") {
|
} else if (anchor === "newest") {
|
||||||
|
@ -853,7 +902,7 @@ export function fast_track_current_msg_list_to_anchor(anchor) {
|
||||||
return fetch_status.has_found_newest();
|
return fetch_status.has_found_newest();
|
||||||
},
|
},
|
||||||
message_list_data_to_target_message_id(msg_list_data) {
|
message_list_data_to_target_message_id(msg_list_data) {
|
||||||
return msg_list_data.last().id;
|
return msg_list_data.last()!.id;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -861,7 +910,7 @@ export function fast_track_current_msg_list_to_anchor(anchor) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function min_defined(a, b) {
|
function min_defined(a: number | undefined, b: number | undefined): number | undefined {
|
||||||
if (a === undefined) {
|
if (a === undefined) {
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
@ -871,7 +920,7 @@ function min_defined(a, b) {
|
||||||
return a < b ? a : b;
|
return a < b ? a : b;
|
||||||
}
|
}
|
||||||
|
|
||||||
function load_local_messages(msg_data, superset_data) {
|
function load_local_messages(msg_data: MessageListData, superset_data: MessageListData): boolean {
|
||||||
// This little helper loads messages into our narrow message
|
// This little helper loads messages into our narrow message
|
||||||
// data and returns true unless it's visibly empty. We use this for
|
// data and returns true unless it's visibly empty. We use this for
|
||||||
// cases when our local cache (superset_data) has at least
|
// cases when our local cache (superset_data) has at least
|
||||||
|
@ -883,7 +932,11 @@ function load_local_messages(msg_data, superset_data) {
|
||||||
return !msg_data.visibly_empty();
|
return !msg_data.visibly_empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function maybe_add_local_messages(opts) {
|
export function maybe_add_local_messages(opts: {
|
||||||
|
id_info: IdInfo;
|
||||||
|
msg_data: MessageListData;
|
||||||
|
superset_data: MessageListData;
|
||||||
|
}): void {
|
||||||
// This function determines whether we need to go to the server to
|
// This function determines whether we need to go to the server to
|
||||||
// fetch messages for the requested narrow, or whether we have the
|
// fetch messages for the requested narrow, or whether we have the
|
||||||
// data cached locally to render the narrow correctly without
|
// data cached locally to render the narrow correctly without
|
||||||
|
@ -955,6 +1008,7 @@ export function maybe_add_local_messages(opts) {
|
||||||
// is earlier. See #2091 for a detailed explanation of why we
|
// is earlier. See #2091 for a detailed explanation of why we
|
||||||
// need to look at unread here.
|
// need to look at unread here.
|
||||||
id_info.final_select_id = min_defined(id_info.target_id, unread_info.msg_id);
|
id_info.final_select_id = min_defined(id_info.target_id, unread_info.msg_id);
|
||||||
|
assert(id_info.final_select_id !== undefined);
|
||||||
|
|
||||||
if (!load_local_messages(msg_data, superset_data)) {
|
if (!load_local_messages(msg_data, superset_data)) {
|
||||||
return;
|
return;
|
||||||
|
@ -999,6 +1053,7 @@ export function maybe_add_local_messages(opts) {
|
||||||
// narrow the server could give us, so we can render locally.
|
// narrow the server could give us, so we can render locally.
|
||||||
// and use local latest message id instead of max_int if set earlier.
|
// and use local latest message id instead of max_int if set earlier.
|
||||||
const last_msg = msg_data.last();
|
const last_msg = msg_data.last();
|
||||||
|
assert(last_msg !== undefined);
|
||||||
id_info.final_select_id = last_msg.id;
|
id_info.final_select_id = last_msg.id;
|
||||||
id_info.local_select_id = id_info.final_select_id;
|
id_info.local_select_id = id_info.final_select_id;
|
||||||
return;
|
return;
|
||||||
|
@ -1016,8 +1071,8 @@ export function maybe_add_local_messages(opts) {
|
||||||
// And similarly for `near: max_int` with has_found_newest.
|
// And similarly for `near: max_int` with has_found_newest.
|
||||||
if (
|
if (
|
||||||
superset_data.visibly_empty() ||
|
superset_data.visibly_empty() ||
|
||||||
id_info.target_id < superset_data.first().id ||
|
id_info.target_id < superset_data.first()!.id ||
|
||||||
id_info.target_id > superset_data.last().id
|
id_info.target_id > superset_data.last()!.id
|
||||||
) {
|
) {
|
||||||
// If the target message is outside the range that we had
|
// If the target message is outside the range that we had
|
||||||
// available for local population, we must go to the server.
|
// available for local population, we must go to the server.
|
||||||
|
@ -1041,7 +1096,12 @@ export function maybe_add_local_messages(opts) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function render_message_list_with_selected_message(opts) {
|
export function render_message_list_with_selected_message(opts: {
|
||||||
|
msg_list: MessageList | undefined;
|
||||||
|
id_info: IdInfo;
|
||||||
|
select_offset: number | undefined;
|
||||||
|
select_opts: SelectIdOpts;
|
||||||
|
}): void {
|
||||||
if (message_lists.current !== undefined && message_lists.current !== opts.msg_list) {
|
if (message_lists.current !== undefined && message_lists.current !== opts.msg_list) {
|
||||||
// If we navigated away from a view while we were fetching
|
// If we navigated away from a view while we were fetching
|
||||||
// messages for it, don't attempt to move the currently
|
// messages for it, don't attempt to move the currently
|
||||||
|
@ -1049,6 +1109,7 @@ export function render_message_list_with_selected_message(opts) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(message_lists.current !== undefined);
|
||||||
if (message_lists.current.visibly_empty()) {
|
if (message_lists.current.visibly_empty()) {
|
||||||
// There's nothing to select if there are no messages.
|
// There's nothing to select if there are no messages.
|
||||||
return;
|
return;
|
||||||
|
@ -1090,13 +1151,13 @@ export function render_message_list_with_selected_message(opts) {
|
||||||
narrow_history.save_narrow_state_and_flush();
|
narrow_history.save_narrow_state_and_flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
function activate_stream_for_cycle_hotkey(stream_id) {
|
function activate_stream_for_cycle_hotkey(stream_id: number): void {
|
||||||
// This is the common code for A/D hotkeys.
|
// This is the common code for A/D hotkeys.
|
||||||
const filter_expr = [{operator: "channel", operand: stream_id.toString()}];
|
const filter_expr = [{operator: "channel", operand: stream_id.toString()}];
|
||||||
show(filter_expr, {});
|
show(filter_expr, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function stream_cycle_backward() {
|
export function stream_cycle_backward(): void {
|
||||||
const curr_stream_id = narrow_state.stream_id();
|
const curr_stream_id = narrow_state.stream_id();
|
||||||
|
|
||||||
if (!curr_stream_id) {
|
if (!curr_stream_id) {
|
||||||
|
@ -1112,7 +1173,7 @@ export function stream_cycle_backward() {
|
||||||
activate_stream_for_cycle_hotkey(stream_id);
|
activate_stream_for_cycle_hotkey(stream_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function stream_cycle_forward() {
|
export function stream_cycle_forward(): void {
|
||||||
const curr_stream_id = narrow_state.stream_id();
|
const curr_stream_id = narrow_state.stream_id();
|
||||||
|
|
||||||
if (!curr_stream_id) {
|
if (!curr_stream_id) {
|
||||||
|
@ -1128,7 +1189,7 @@ export function stream_cycle_forward() {
|
||||||
activate_stream_for_cycle_hotkey(stream_id);
|
activate_stream_for_cycle_hotkey(stream_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function narrow_to_next_topic(opts = {}) {
|
export function narrow_to_next_topic(opts: {trigger: string; only_followed_topics: boolean}): void {
|
||||||
const curr_info = {
|
const curr_info = {
|
||||||
stream_id: narrow_state.stream_id(),
|
stream_id: narrow_state.stream_id(),
|
||||||
topic: narrow_state.topic(),
|
topic: narrow_state.topic(),
|
||||||
|
@ -1170,8 +1231,9 @@ export function narrow_to_next_topic(opts = {}) {
|
||||||
show(filter_expr, opts);
|
show(filter_expr, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function narrow_to_next_pm_string(opts = {}) {
|
export function narrow_to_next_pm_string(opts = {}): void {
|
||||||
const current_direct_message = narrow_state.pm_ids_string();
|
const current_direct_message = narrow_state.pm_ids_string();
|
||||||
|
assert(current_direct_message !== undefined);
|
||||||
|
|
||||||
const next_direct_message = topic_generator.get_next_unread_pm_string(current_direct_message);
|
const next_direct_message = topic_generator.get_next_unread_pm_string(current_direct_message);
|
||||||
|
|
||||||
|
@ -1188,6 +1250,7 @@ export function narrow_to_next_pm_string(opts = {}) {
|
||||||
// Hopefully someday we can narrow by user_ids_string instead of
|
// Hopefully someday we can narrow by user_ids_string instead of
|
||||||
// mapping back to emails.
|
// mapping back to emails.
|
||||||
const direct_message = people.user_ids_string_to_emails_string(next_direct_message);
|
const direct_message = people.user_ids_string_to_emails_string(next_direct_message);
|
||||||
|
assert(direct_message !== undefined);
|
||||||
|
|
||||||
const filter_expr = [{operator: "dm", operand: direct_message}];
|
const filter_expr = [{operator: "dm", operand: direct_message}];
|
||||||
|
|
||||||
|
@ -1200,9 +1263,15 @@ export function narrow_to_next_pm_string(opts = {}) {
|
||||||
show(filter_expr, updated_opts);
|
show(filter_expr, updated_opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function narrow_by_topic(target_id, opts) {
|
export function narrow_by_topic(
|
||||||
|
target_id: number,
|
||||||
|
opts: {
|
||||||
|
trigger: string;
|
||||||
|
},
|
||||||
|
): void {
|
||||||
// don't use message_lists.current as it won't work for muted messages or for out-of-narrow links
|
// don't use message_lists.current as it won't work for muted messages or for out-of-narrow links
|
||||||
const original = message_store.get(target_id);
|
const original = message_store.get(target_id);
|
||||||
|
assert(original !== undefined);
|
||||||
if (original.type !== "stream") {
|
if (original.type !== "stream") {
|
||||||
// Only stream messages have topics, but the
|
// Only stream messages have topics, but the
|
||||||
// user wants us to narrow in some way.
|
// user wants us to narrow in some way.
|
||||||
|
@ -1225,14 +1294,19 @@ export function narrow_by_topic(target_id, opts) {
|
||||||
{operator: "channel", operand: original.stream_id.toString()},
|
{operator: "channel", operand: original.stream_id.toString()},
|
||||||
{operator: "topic", operand: original.topic},
|
{operator: "topic", operand: original.topic},
|
||||||
];
|
];
|
||||||
opts = {then_select_id: target_id, ...opts};
|
show(search_terms, {then_select_id: target_id, ...opts});
|
||||||
show(search_terms, opts);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function narrow_by_recipient(target_id, opts) {
|
export function narrow_by_recipient(
|
||||||
|
target_id: number,
|
||||||
|
opts: {
|
||||||
|
trigger: string;
|
||||||
|
},
|
||||||
|
): void {
|
||||||
const show_opts = {then_select_id: target_id, ...opts};
|
const show_opts = {then_select_id: target_id, ...opts};
|
||||||
// don't use message_lists.current as it won't work for muted messages or for out-of-narrow links
|
// don't use message_lists.current as it won't work for muted messages or for out-of-narrow links
|
||||||
const message = message_store.get(target_id);
|
const message = message_store.get(target_id);
|
||||||
|
assert(message !== undefined);
|
||||||
const emails = message.reply_to.split(",");
|
const emails = message.reply_to.split(",");
|
||||||
const reply_to = people.sort_emails_by_username(emails);
|
const reply_to = people.sort_emails_by_username(emails);
|
||||||
|
|
||||||
|
@ -1275,7 +1349,7 @@ export function narrow_by_recipient(target_id, opts) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function to_compose_target() {
|
export function to_compose_target(): void {
|
||||||
if (!compose_state.composing()) {
|
if (!compose_state.composing()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1316,7 +1390,13 @@ export function to_compose_target() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handle_post_view_change(msg_list, opts) {
|
function handle_post_view_change(
|
||||||
|
msg_list: MessageList,
|
||||||
|
opts: {
|
||||||
|
change_hash: boolean;
|
||||||
|
show_more_topics: boolean;
|
||||||
|
},
|
||||||
|
): void {
|
||||||
const filter = msg_list.data.filter;
|
const filter = msg_list.data.filter;
|
||||||
|
|
||||||
if (narrow_state.narrowed_by_reply()) {
|
if (narrow_state.narrowed_by_reply()) {
|
|
@ -12,15 +12,15 @@ export function next_topic(
|
||||||
stream_ids: number[],
|
stream_ids: number[],
|
||||||
get_topics: (stream_id: number) => string[],
|
get_topics: (stream_id: number) => string[],
|
||||||
has_unread_messages: (stream_id: number, topic: string) => boolean,
|
has_unread_messages: (stream_id: number, topic: string) => boolean,
|
||||||
curr_stream_id: number,
|
curr_stream_id: number | undefined,
|
||||||
curr_topic: string,
|
curr_topic: string | undefined,
|
||||||
): {stream_id: number; topic: string} | undefined {
|
): {stream_id: number; topic: string} | undefined {
|
||||||
const curr_stream_index = stream_ids.indexOf(curr_stream_id); // -1 if not found
|
const curr_stream_index = curr_stream_id ? stream_ids.indexOf(curr_stream_id) : -1; // -1 if not found
|
||||||
|
|
||||||
if (curr_stream_index >= 0) {
|
if (curr_stream_index >= 0) {
|
||||||
const stream_id = stream_ids[curr_stream_index]!;
|
const stream_id = stream_ids[curr_stream_index]!;
|
||||||
const topics = get_topics(stream_id);
|
const topics = get_topics(stream_id);
|
||||||
const curr_topic_index = topics.indexOf(curr_topic); // -1 if not found
|
const curr_topic_index = curr_topic ? topics.indexOf(curr_topic) : -1; // -1 if not found
|
||||||
|
|
||||||
for (let i = curr_topic_index + 1; i < topics.length; i += 1) {
|
for (let i = curr_topic_index + 1; i < topics.length; i += 1) {
|
||||||
const topic = topics[i]!;
|
const topic = topics[i]!;
|
||||||
|
@ -59,8 +59,8 @@ export function next_topic(
|
||||||
}
|
}
|
||||||
|
|
||||||
export function get_next_topic(
|
export function get_next_topic(
|
||||||
curr_stream_id: number,
|
curr_stream_id: number | undefined,
|
||||||
curr_topic: string,
|
curr_topic: string | undefined,
|
||||||
only_followed_topics: boolean,
|
only_followed_topics: boolean,
|
||||||
): {stream_id: number; topic: string} | undefined {
|
): {stream_id: number; topic: string} | undefined {
|
||||||
let my_streams = stream_list_sort.get_stream_ids();
|
let my_streams = stream_list_sort.get_stream_ids();
|
||||||
|
|
Loading…
Reference in New Issue