mirror of https://github.com/zulip/zulip.git
topic list: Convert module to typescript.
This commit is contained in:
parent
7154539e80
commit
ea04f7d27d
|
@ -244,7 +244,7 @@ EXEMPT_FILES = make_set(
|
||||||
"web/src/timerender.ts",
|
"web/src/timerender.ts",
|
||||||
"web/src/tippyjs.ts",
|
"web/src/tippyjs.ts",
|
||||||
"web/src/todo_widget.js",
|
"web/src/todo_widget.js",
|
||||||
"web/src/topic_list.js",
|
"web/src/topic_list.ts",
|
||||||
"web/src/topic_popover.js",
|
"web/src/topic_popover.js",
|
||||||
"web/src/tutorial.js",
|
"web/src/tutorial.js",
|
||||||
"web/src/types.ts",
|
"web/src/types.ts",
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import $ from "jquery";
|
import $ from "jquery";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
|
import assert from "minimalistic-assert";
|
||||||
|
|
||||||
import render_more_topics from "../templates/more_topics.hbs";
|
import render_more_topics from "../templates/more_topics.hbs";
|
||||||
import render_more_topics_spinner from "../templates/more_topics_spinner.hbs";
|
import render_more_topics_spinner from "../templates/more_topics_spinner.hbs";
|
||||||
|
@ -11,28 +12,29 @@ import * as scroll_util from "./scroll_util";
|
||||||
import * as stream_topic_history from "./stream_topic_history";
|
import * as stream_topic_history from "./stream_topic_history";
|
||||||
import * as stream_topic_history_util from "./stream_topic_history_util";
|
import * as stream_topic_history_util from "./stream_topic_history_util";
|
||||||
import * as topic_list_data from "./topic_list_data";
|
import * as topic_list_data from "./topic_list_data";
|
||||||
|
import type {TopicInfo} from "./topic_list_data";
|
||||||
import * as vdom from "./vdom";
|
import * as vdom from "./vdom";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Track all active widgets with a Map.
|
Track all active widgets with a Map by stream_id.
|
||||||
|
|
||||||
(We have at max one for now, but we may
|
(We have at max one for now, but we may
|
||||||
eventually allow multiple streams to be
|
eventually allow multiple streams to be
|
||||||
expanded.)
|
expanded.)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const active_widgets = new Map();
|
const active_widgets = new Map<number, TopicListWidget>();
|
||||||
|
|
||||||
// We know whether we're zoomed or not.
|
// We know whether we're zoomed or not.
|
||||||
let zoomed = false;
|
let zoomed = false;
|
||||||
|
|
||||||
export function update() {
|
export function update(): void {
|
||||||
for (const widget of active_widgets.values()) {
|
for (const widget of active_widgets.values()) {
|
||||||
widget.build();
|
widget.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function clear() {
|
export function clear(): void {
|
||||||
popover_menus.get_topic_menu_popover()?.hide();
|
popover_menus.get_topic_menu_popover()?.hide();
|
||||||
|
|
||||||
for (const widget of active_widgets.values()) {
|
for (const widget of active_widgets.values()) {
|
||||||
|
@ -42,12 +44,12 @@ export function clear() {
|
||||||
active_widgets.clear();
|
active_widgets.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function close() {
|
export function close(): void {
|
||||||
zoomed = false;
|
zoomed = false;
|
||||||
clear();
|
clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function zoom_out() {
|
export function zoom_out(): void {
|
||||||
zoomed = false;
|
zoomed = false;
|
||||||
|
|
||||||
const stream_ids = [...active_widgets.keys()];
|
const stream_ids = [...active_widgets.keys()];
|
||||||
|
@ -59,75 +61,96 @@ export function zoom_out() {
|
||||||
|
|
||||||
const stream_id = stream_ids[0];
|
const stream_id = stream_ids[0];
|
||||||
const widget = active_widgets.get(stream_id);
|
const widget = active_widgets.get(stream_id);
|
||||||
|
assert(widget !== undefined);
|
||||||
const parent_widget = widget.get_parent();
|
const parent_widget = widget.get_parent();
|
||||||
|
|
||||||
rebuild(parent_widget, stream_id);
|
rebuild(parent_widget, stream_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function keyed_topic_li(conversation) {
|
type ListInfoNodeOptions =
|
||||||
const render = () => render_topic_list_item(conversation);
|
| {
|
||||||
|
type: "topic";
|
||||||
|
conversation: TopicInfo;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: "more_items";
|
||||||
|
more_topics_unreads: number;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: "spinner";
|
||||||
|
};
|
||||||
|
|
||||||
const eq = (other) => _.isEqual(conversation, other.conversation);
|
type ListInfoNode = vdom.Node<ListInfoNodeOptions>;
|
||||||
|
|
||||||
|
export function keyed_topic_li(conversation: TopicInfo): ListInfoNode {
|
||||||
|
const render = (): string => render_topic_list_item(conversation);
|
||||||
|
|
||||||
|
const eq = (other: ListInfoNode): boolean =>
|
||||||
|
other.type === "topic" && _.isEqual(conversation, other.conversation);
|
||||||
|
|
||||||
const key = "t:" + conversation.topic_name;
|
const key = "t:" + conversation.topic_name;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
key,
|
key,
|
||||||
render,
|
render,
|
||||||
|
type: "topic",
|
||||||
conversation,
|
conversation,
|
||||||
eq,
|
eq,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function more_li(
|
export function more_li(
|
||||||
more_topics_unreads,
|
more_topics_unreads: number,
|
||||||
more_topics_have_unread_mention_messages,
|
more_topics_have_unread_mention_messages: boolean,
|
||||||
more_topics_unread_count_muted,
|
more_topics_unread_count_muted: boolean,
|
||||||
) {
|
): ListInfoNode {
|
||||||
const render = () =>
|
const render = (): string =>
|
||||||
render_more_topics({
|
render_more_topics({
|
||||||
more_topics_unreads,
|
more_topics_unreads,
|
||||||
more_topics_have_unread_mention_messages,
|
more_topics_have_unread_mention_messages,
|
||||||
more_topics_unread_count_muted,
|
more_topics_unread_count_muted,
|
||||||
});
|
});
|
||||||
|
|
||||||
const eq = (other) => other.more_items && more_topics_unreads === other.more_topics_unreads;
|
const eq = (other: ListInfoNode): boolean =>
|
||||||
|
other.type === "more_items" && more_topics_unreads === other.more_topics_unreads;
|
||||||
|
|
||||||
const key = "more";
|
const key = "more";
|
||||||
|
|
||||||
return {
|
return {
|
||||||
key,
|
key,
|
||||||
more_items: true,
|
type: "more_items",
|
||||||
more_topics_unreads,
|
more_topics_unreads,
|
||||||
render,
|
render,
|
||||||
eq,
|
eq,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function spinner_li() {
|
export function spinner_li(): ListInfoNode {
|
||||||
const render = () => render_more_topics_spinner();
|
const render = (): string => render_more_topics_spinner();
|
||||||
|
|
||||||
const eq = (other) => other.spinner;
|
const eq = (other: ListInfoNode): boolean => other.type === "spinner";
|
||||||
|
|
||||||
const key = "more";
|
const key = "more";
|
||||||
|
|
||||||
return {
|
return {
|
||||||
key,
|
key,
|
||||||
spinner: true,
|
type: "spinner",
|
||||||
render,
|
render,
|
||||||
eq,
|
eq,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TopicListWidget {
|
export class TopicListWidget {
|
||||||
prior_dom = undefined;
|
prior_dom?: vdom.Tag<ListInfoNodeOptions> = undefined;
|
||||||
|
$parent_elem: JQuery;
|
||||||
|
my_stream_id: number;
|
||||||
|
|
||||||
constructor($parent_elem, my_stream_id) {
|
constructor($parent_elem: JQuery, my_stream_id: number) {
|
||||||
this.$parent_elem = $parent_elem;
|
this.$parent_elem = $parent_elem;
|
||||||
this.my_stream_id = my_stream_id;
|
this.my_stream_id = my_stream_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
build_list(spinner) {
|
build_list(spinner: boolean): vdom.Tag<ListInfoNodeOptions> {
|
||||||
const list_info = topic_list_data.get_list_info(
|
const list_info = topic_list_data.get_list_info(
|
||||||
this.my_stream_id,
|
this.my_stream_id,
|
||||||
zoomed,
|
zoomed,
|
||||||
|
@ -143,7 +166,7 @@ export class TopicListWidget {
|
||||||
list_info.items.length === num_possible_topics &&
|
list_info.items.length === num_possible_topics &&
|
||||||
stream_topic_history.is_complete_for_stream_id(this.my_stream_id);
|
stream_topic_history.is_complete_for_stream_id(this.my_stream_id);
|
||||||
|
|
||||||
const attrs = [["class", "topic-list"]];
|
const attrs: [string, string][] = [["class", "topic-list"]];
|
||||||
|
|
||||||
const nodes = list_info.items.map((conversation) => keyed_topic_li(conversation));
|
const nodes = list_info.items.map((conversation) => keyed_topic_li(conversation));
|
||||||
|
|
||||||
|
@ -167,28 +190,29 @@ export class TopicListWidget {
|
||||||
return dom;
|
return dom;
|
||||||
}
|
}
|
||||||
|
|
||||||
get_parent() {
|
get_parent(): JQuery {
|
||||||
return this.$parent_elem;
|
return this.$parent_elem;
|
||||||
}
|
}
|
||||||
|
|
||||||
get_stream_id() {
|
get_stream_id(): number {
|
||||||
return this.my_stream_id;
|
return this.my_stream_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
remove() {
|
remove(): void {
|
||||||
this.$parent_elem.find(".topic-list").remove();
|
this.$parent_elem.find(".topic-list").remove();
|
||||||
this.prior_dom = undefined;
|
this.prior_dom = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
build(spinner) {
|
build(spinner = false): void {
|
||||||
const new_dom = this.build_list(spinner);
|
const new_dom = this.build_list(spinner);
|
||||||
|
|
||||||
const replace_content = (html) => {
|
const replace_content = (html?: string): void => {
|
||||||
|
assert(html !== undefined);
|
||||||
this.remove();
|
this.remove();
|
||||||
this.$parent_elem.append(html);
|
this.$parent_elem.append(html);
|
||||||
};
|
};
|
||||||
|
|
||||||
const find = () => this.$parent_elem.find(".topic-list");
|
const find = (): JQuery => this.$parent_elem.find(".topic-list");
|
||||||
|
|
||||||
vdom.update(replace_content, find, new_dom, this.prior_dom);
|
vdom.update(replace_content, find, new_dom, this.prior_dom);
|
||||||
|
|
||||||
|
@ -202,7 +226,7 @@ export class TopicListWidget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function clear_topic_search(e) {
|
export function clear_topic_search(e: JQuery.Event): void {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
const $input = $("#filter-topic-input");
|
const $input = $("#filter-topic-input");
|
||||||
if ($input.length) {
|
if ($input.length) {
|
||||||
|
@ -215,13 +239,14 @@ export function clear_topic_search(e) {
|
||||||
|
|
||||||
const stream_id = stream_ids[0];
|
const stream_id = stream_ids[0];
|
||||||
const widget = active_widgets.get(stream_id);
|
const widget = active_widgets.get(stream_id);
|
||||||
|
assert(widget !== undefined);
|
||||||
const parent_widget = widget.get_parent();
|
const parent_widget = widget.get_parent();
|
||||||
|
|
||||||
rebuild(parent_widget, stream_id);
|
rebuild(parent_widget, stream_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function active_stream_id() {
|
export function active_stream_id(): number | undefined {
|
||||||
const stream_ids = [...active_widgets.keys()];
|
const stream_ids = [...active_widgets.keys()];
|
||||||
|
|
||||||
if (stream_ids.length !== 1) {
|
if (stream_ids.length !== 1) {
|
||||||
|
@ -231,7 +256,7 @@ export function active_stream_id() {
|
||||||
return stream_ids[0];
|
return stream_ids[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function get_stream_li() {
|
export function get_stream_li(): JQuery | undefined {
|
||||||
const widgets = [...active_widgets.values()];
|
const widgets = [...active_widgets.values()];
|
||||||
|
|
||||||
if (widgets.length !== 1) {
|
if (widgets.length !== 1) {
|
||||||
|
@ -242,7 +267,7 @@ export function get_stream_li() {
|
||||||
return $stream_li;
|
return $stream_li;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function rebuild($stream_li, stream_id) {
|
export function rebuild($stream_li: JQuery, stream_id: number): void {
|
||||||
const active_widget = active_widgets.get(stream_id);
|
const active_widget = active_widgets.get(stream_id);
|
||||||
|
|
||||||
if (active_widget) {
|
if (active_widget) {
|
||||||
|
@ -259,19 +284,20 @@ export function rebuild($stream_li, stream_id) {
|
||||||
|
|
||||||
// For zooming, we only do topic-list stuff here...let stream_list
|
// For zooming, we only do topic-list stuff here...let stream_list
|
||||||
// handle hiding/showing the non-narrowed streams
|
// handle hiding/showing the non-narrowed streams
|
||||||
export function zoom_in() {
|
export function zoom_in(): void {
|
||||||
zoomed = true;
|
zoomed = true;
|
||||||
|
|
||||||
const stream_id = active_stream_id();
|
const stream_id = active_stream_id();
|
||||||
if (!stream_id) {
|
if (stream_id === undefined) {
|
||||||
blueslip.error("Cannot find widget for topic history zooming.");
|
blueslip.error("Cannot find widget for topic history zooming.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const active_widget = active_widgets.get(stream_id);
|
const active_widget = active_widgets.get(stream_id);
|
||||||
|
assert(active_widget !== undefined);
|
||||||
|
|
||||||
function on_success() {
|
function on_success(): void {
|
||||||
if (!active_widgets.has(stream_id)) {
|
if (!active_widgets.has(stream_id!)) {
|
||||||
blueslip.warn("User re-narrowed before topic history was returned.");
|
blueslip.warn("User re-narrowed before topic history was returned.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -285,7 +311,7 @@ export function zoom_in() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
active_widget.build();
|
active_widget!.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
scroll_util.get_scroll_element($("#left_sidebar_scroll_container")).scrollTop(0);
|
scroll_util.get_scroll_element($("#left_sidebar_scroll_container")).scrollTop(0);
|
||||||
|
@ -296,15 +322,20 @@ export function zoom_in() {
|
||||||
stream_topic_history_util.get_server_history(stream_id, on_success);
|
stream_topic_history_util.get_server_history(stream_id, on_success);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function get_topic_search_term() {
|
export function get_topic_search_term(): string {
|
||||||
const $filter = $("#filter-topic-input");
|
const $filter = $<HTMLInputElement>("input#filter-topic-input");
|
||||||
if ($filter.val() === undefined) {
|
const filter_val = $filter.val();
|
||||||
|
if (filter_val === undefined) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
return $filter.val().trim();
|
return filter_val.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function initialize({on_topic_click}) {
|
export function initialize({
|
||||||
|
on_topic_click,
|
||||||
|
}: {
|
||||||
|
on_topic_click: (stream_id: number, topic?: string) => void;
|
||||||
|
}): void {
|
||||||
$("#stream_filters").on(
|
$("#stream_filters").on(
|
||||||
"click",
|
"click",
|
||||||
".sidebar-topic-check, .topic-name, .topic-markers-and-controls",
|
".sidebar-topic-check, .topic-name, .topic-markers-and-controls",
|
||||||
|
@ -317,7 +348,9 @@ export function initialize({on_topic_click}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const $stream_row = $(e.target).parents(".narrow-filter");
|
const $stream_row = $(e.target).parents(".narrow-filter");
|
||||||
const stream_id = Number.parseInt($stream_row.attr("data-stream-id"), 10);
|
const stream_id_string = $stream_row.attr("data-stream-id");
|
||||||
|
assert(stream_id_string !== undefined);
|
||||||
|
const stream_id = Number.parseInt(stream_id_string, 10);
|
||||||
const topic = $(e.target).parents("li").attr("data-topic-name");
|
const topic = $(e.target).parents("li").attr("data-topic-name");
|
||||||
on_topic_click(stream_id, topic);
|
on_topic_click(stream_id, topic);
|
||||||
|
|
||||||
|
@ -325,7 +358,9 @@ export function initialize({on_topic_click}) {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
$("body").on("input", "#filter-topic-input", () => {
|
$("body").on("input", "#filter-topic-input", (): void => {
|
||||||
active_widgets.get(active_stream_id()).build();
|
const stream_id = active_stream_id();
|
||||||
|
assert(stream_id !== undefined);
|
||||||
|
active_widgets.get(stream_id)?.build();
|
||||||
});
|
});
|
||||||
}
|
}
|
|
@ -13,7 +13,7 @@ import * as util from "./util";
|
||||||
const max_topics = 8;
|
const max_topics = 8;
|
||||||
const max_topics_with_unread = 12;
|
const max_topics_with_unread = 12;
|
||||||
|
|
||||||
type TopicInfo = {
|
export type TopicInfo = {
|
||||||
topic_name: string;
|
topic_name: string;
|
||||||
topic_resolved_prefix: string;
|
topic_resolved_prefix: string;
|
||||||
topic_display_name: string;
|
topic_display_name: string;
|
||||||
|
|
Loading…
Reference in New Issue