mirror of https://github.com/zulip/zulip.git
left-sidebar: Add topic filter input in zoomed topic view.
Fixes: #18505.
This commit is contained in:
parent
59f8466de3
commit
3f0ed46fa2
|
@ -16,6 +16,10 @@ const narrow_state = mock_esm("../../static/js/narrow_state", {
|
||||||
topic() {},
|
topic() {},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const topic_list = mock_esm("../../static/js/topic_list", {
|
||||||
|
get_topic_search_term() {},
|
||||||
|
});
|
||||||
|
|
||||||
const stream_data = zrequire("stream_data");
|
const stream_data = zrequire("stream_data");
|
||||||
const stream_topic_history = zrequire("stream_topic_history");
|
const stream_topic_history = zrequire("stream_topic_history");
|
||||||
const topic_list_data = zrequire("topic_list_data");
|
const topic_list_data = zrequire("topic_list_data");
|
||||||
|
@ -50,14 +54,16 @@ test("get_list_info w/real stream_topic_history", (override) => {
|
||||||
num_possible_topics: 0,
|
num_possible_topics: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const i of _.range(7)) {
|
function add_topic_message(topic_name, message_id) {
|
||||||
const topic_name = "topic " + i;
|
|
||||||
stream_topic_history.add_message({
|
stream_topic_history.add_message({
|
||||||
stream_id: general.stream_id,
|
stream_id: general.stream_id,
|
||||||
topic_name,
|
topic_name,
|
||||||
message_id: 1000 + i,
|
message_id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
for (const i of _.range(7)) {
|
||||||
|
add_topic_message("topic " + i, 1000 + i);
|
||||||
|
}
|
||||||
|
|
||||||
override(narrow_state, "topic", () => "topic 6");
|
override(narrow_state, "topic", () => "topic 6");
|
||||||
|
|
||||||
|
@ -75,12 +81,24 @@ test("get_list_info w/real stream_topic_history", (override) => {
|
||||||
url: "#narrow/stream/556-general/topic/topic.206",
|
url: "#narrow/stream/556-general/topic/topic.206",
|
||||||
});
|
});
|
||||||
|
|
||||||
// If we zoom in, we'll show all 7 topics.
|
// If we zoom in, our results based on topic filter.
|
||||||
|
// If topic search input is empty, we show all 7 topics.
|
||||||
|
|
||||||
const zoomed = true;
|
const zoomed = true;
|
||||||
|
override(topic_list, "get_topic_search_term", () => "");
|
||||||
list_info = get_list_info(zoomed);
|
list_info = get_list_info(zoomed);
|
||||||
assert.equal(list_info.items.length, 7);
|
assert.equal(list_info.items.length, 7);
|
||||||
assert.equal(list_info.more_topics_unreads, 0);
|
assert.equal(list_info.more_topics_unreads, 0);
|
||||||
assert.equal(list_info.num_possible_topics, 7);
|
assert.equal(list_info.num_possible_topics, 7);
|
||||||
|
|
||||||
|
add_topic_message("After Brooklyn", 1008);
|
||||||
|
add_topic_message("Catering", 1009);
|
||||||
|
// when topic search is open then we list topics based on search term.
|
||||||
|
override(topic_list, "get_topic_search_term", () => "b,c");
|
||||||
|
list_info = get_list_info(zoomed);
|
||||||
|
assert.equal(list_info.items.length, 2);
|
||||||
|
assert.equal(list_info.more_topics_unreads, 0);
|
||||||
|
assert.equal(list_info.num_possible_topics, 2);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("get_list_info unreads", (override) => {
|
test("get_list_info unreads", (override) => {
|
||||||
|
|
|
@ -40,6 +40,7 @@ import * as settings_toggle from "./settings_toggle";
|
||||||
import * as stream_edit from "./stream_edit";
|
import * as stream_edit from "./stream_edit";
|
||||||
import * as stream_list from "./stream_list";
|
import * as stream_list from "./stream_list";
|
||||||
import * as stream_popover from "./stream_popover";
|
import * as stream_popover from "./stream_popover";
|
||||||
|
import * as topic_list from "./topic_list";
|
||||||
import * as ui_util from "./ui_util";
|
import * as ui_util from "./ui_util";
|
||||||
import * as unread_ops from "./unread_ops";
|
import * as unread_ops from "./unread_ops";
|
||||||
import * as user_status_ui from "./user_status_ui";
|
import * as user_status_ui from "./user_status_ui";
|
||||||
|
@ -694,6 +695,8 @@ export function initialize() {
|
||||||
|
|
||||||
// LEFT SIDEBAR
|
// LEFT SIDEBAR
|
||||||
|
|
||||||
|
$("body").on("click", "#clear_search_topic_button", topic_list.clear_topic_search);
|
||||||
|
|
||||||
$(".streams_filter_icon").on("click", (e) => {
|
$(".streams_filter_icon").on("click", (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
stream_list.toggle_filter_displayed(e);
|
stream_list.toggle_filter_displayed(e);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import $ from "jquery";
|
import $ from "jquery";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
|
|
||||||
|
import render_filter_topics from "../templates/filter_topics.hbs";
|
||||||
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";
|
||||||
import render_topic_list_item from "../templates/topic_list_item.hbs";
|
import render_topic_list_item from "../templates/topic_list_item.hbs";
|
||||||
|
@ -115,12 +116,24 @@ export function spinner_li() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function filter_topics_li() {
|
||||||
|
const eq = (other) => other.filter_topics;
|
||||||
|
|
||||||
|
return {
|
||||||
|
key: "filter",
|
||||||
|
filter_topics: true,
|
||||||
|
render: render_filter_topics,
|
||||||
|
eq,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export class TopicListWidget {
|
export class TopicListWidget {
|
||||||
prior_dom = undefined;
|
prior_dom = undefined;
|
||||||
|
|
||||||
constructor(parent_elem, my_stream_id) {
|
constructor(parent_elem, my_stream_id) {
|
||||||
this.parent_elem = parent_elem;
|
this.parent_elem = parent_elem;
|
||||||
this.my_stream_id = my_stream_id;
|
this.my_stream_id = my_stream_id;
|
||||||
|
this.topic_search_text = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
build_list(spinner) {
|
build_list(spinner) {
|
||||||
|
@ -141,6 +154,10 @@ export class TopicListWidget {
|
||||||
nodes.push(spinner_li());
|
nodes.push(spinner_li());
|
||||||
} else if (!is_showing_all_possible_topics) {
|
} else if (!is_showing_all_possible_topics) {
|
||||||
nodes.push(more_li(more_topics_unreads));
|
nodes.push(more_li(more_topics_unreads));
|
||||||
|
} else if (zoomed) {
|
||||||
|
// In the zoomed topic view, we need to add the input
|
||||||
|
// for filtering through list of topics.
|
||||||
|
nodes.unshift(filter_topics_li());
|
||||||
}
|
}
|
||||||
|
|
||||||
const dom = vdom.ul({
|
const dom = vdom.ul({
|
||||||
|
@ -159,7 +176,36 @@ export class TopicListWidget {
|
||||||
return this.my_stream_id;
|
return this.my_stream_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
update_topic_search_text(text) {
|
||||||
|
this.topic_search_text = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
update_topic_search_input() {
|
||||||
|
const input = this.parent_elem.find("#filter-topic-input");
|
||||||
|
if (input.length) {
|
||||||
|
// Restore topic search text saved in remove()
|
||||||
|
// after the element was rerendered.
|
||||||
|
input.val(this.topic_search_text);
|
||||||
|
input.trigger("focus");
|
||||||
|
|
||||||
|
// setup event handlers.
|
||||||
|
const rebuild_list = () => this.build();
|
||||||
|
input.on("input", rebuild_list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
remove() {
|
remove() {
|
||||||
|
// If text was present in the topic search filter, we store
|
||||||
|
// the input value lazily before removing old elements. This
|
||||||
|
// is a workaround for the quirk that the filter input is part
|
||||||
|
// of the region that we rerender.
|
||||||
|
const input = this.parent_elem.find("#filter-topic-input");
|
||||||
|
if (input.length) {
|
||||||
|
this.update_topic_search_text(input.val());
|
||||||
|
} else {
|
||||||
|
// Clear the topic search input when zooming out.
|
||||||
|
this.update_topic_search_text("");
|
||||||
|
}
|
||||||
this.parent_elem.find(".topic-list").remove();
|
this.parent_elem.find(".topic-list").remove();
|
||||||
this.prior_dom = undefined;
|
this.prior_dom = undefined;
|
||||||
}
|
}
|
||||||
|
@ -170,6 +216,7 @@ export class TopicListWidget {
|
||||||
const replace_content = (html) => {
|
const replace_content = (html) => {
|
||||||
this.remove();
|
this.remove();
|
||||||
this.parent_elem.append(html);
|
this.parent_elem.append(html);
|
||||||
|
this.update_topic_search_input();
|
||||||
};
|
};
|
||||||
|
|
||||||
const find = () => this.parent_elem.find(".topic-list");
|
const find = () => this.parent_elem.find(".topic-list");
|
||||||
|
@ -180,6 +227,25 @@ export class TopicListWidget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function clear_topic_search(e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
const input = $("#filter-topic-input");
|
||||||
|
if (input.length) {
|
||||||
|
input.val("");
|
||||||
|
input.trigger("blur");
|
||||||
|
|
||||||
|
// Since this changes the contents of the search input, we
|
||||||
|
// need to rerender the topic list.
|
||||||
|
const stream_ids = Array.from(active_widgets.keys());
|
||||||
|
|
||||||
|
const stream_id = stream_ids[0];
|
||||||
|
const widget = active_widgets.get(stream_id);
|
||||||
|
const parent_widget = widget.get_parent();
|
||||||
|
|
||||||
|
rebuild(parent_widget, stream_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function active_stream_id() {
|
export function active_stream_id() {
|
||||||
const stream_ids = Array.from(active_widgets.keys());
|
const stream_ids = Array.from(active_widgets.keys());
|
||||||
|
|
||||||
|
@ -255,6 +321,14 @@ 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() {
|
||||||
|
const filter = $("#filter-topic-input");
|
||||||
|
if (filter.val() === undefined) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return filter.val().trim();
|
||||||
|
}
|
||||||
|
|
||||||
export function initialize() {
|
export function initialize() {
|
||||||
$("#stream_filters").on("click", ".topic-box", (e) => {
|
$("#stream_filters").on("click", ".topic-box", (e) => {
|
||||||
if (e.metaKey || e.ctrlKey) {
|
if (e.metaKey || e.ctrlKey) {
|
||||||
|
|
|
@ -2,7 +2,9 @@ import * as hash_util from "./hash_util";
|
||||||
import * as muting from "./muting";
|
import * as muting from "./muting";
|
||||||
import * as narrow_state from "./narrow_state";
|
import * as narrow_state from "./narrow_state";
|
||||||
import * as stream_topic_history from "./stream_topic_history";
|
import * as stream_topic_history from "./stream_topic_history";
|
||||||
|
import * as topic_list from "./topic_list";
|
||||||
import * as unread from "./unread";
|
import * as unread from "./unread";
|
||||||
|
import * as util from "./util";
|
||||||
|
|
||||||
const max_topics = 5;
|
const max_topics = 5;
|
||||||
const max_topics_with_unread = 8;
|
const max_topics_with_unread = 8;
|
||||||
|
@ -17,7 +19,11 @@ export function get_list_info(stream_id, zoomed) {
|
||||||
active_topic = active_topic.toLowerCase();
|
active_topic = active_topic.toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
const topic_names = stream_topic_history.get_recent_topic_names(stream_id);
|
let topic_names = stream_topic_history.get_recent_topic_names(stream_id);
|
||||||
|
if (zoomed) {
|
||||||
|
const search_term = topic_list.get_topic_search_term();
|
||||||
|
topic_names = util.filter_by_word_prefix_match(topic_names, search_term, (item) => item);
|
||||||
|
}
|
||||||
|
|
||||||
const items = [];
|
const items = [];
|
||||||
|
|
||||||
|
|
|
@ -413,6 +413,12 @@ div.overlay {
|
||||||
width: calc(100% - 34px);
|
width: calc(100% - 34px);
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
|
.input-append .topic-list-filter {
|
||||||
|
/* Input width = 100% - 11px margin x2 - 6px padding x2 - 1px border x2. */
|
||||||
|
width: calc(100% - 36px);
|
||||||
|
margin-left: 11px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.stream-selection-header-colorblock {
|
.stream-selection-header-colorblock {
|
||||||
|
|
|
@ -100,6 +100,15 @@ li.show-more-topics {
|
||||||
padding-right: 0;
|
padding-right: 0;
|
||||||
padding-bottom: 2px;
|
padding-bottom: 2px;
|
||||||
padding-left: $topic_indent;
|
padding-left: $topic_indent;
|
||||||
|
|
||||||
|
&.filter-topics {
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-append.topic_search_section {
|
||||||
|
margin-bottom: 3px;
|
||||||
|
margin-left: 3px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -515,6 +524,14 @@ li.expanded_private_message {
|
||||||
width: 236px;
|
width: 236px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.topic-list-filter {
|
||||||
|
/* Input width = 100% - 30px right-margin - 6px right-padding */
|
||||||
|
/* To keep the right edge of input along with its borders inline with other
|
||||||
|
topic items we consider to subtract the space given for right margin of
|
||||||
|
other items, and right padding of input element. */
|
||||||
|
width: calc(100% - 36px);
|
||||||
|
}
|
||||||
|
|
||||||
.zero_count {
|
.zero_count {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
<li class="topic-list-item filter-topics">
|
||||||
|
<div class="input-append topic_search_section">
|
||||||
|
<input class="topic-list-filter home-page-input" id="filter-topic-input" type="text" autocomplete="off" placeholder="{{t 'Filter topics'}}" />
|
||||||
|
<button type="button" class="btn clear_search_button" id="clear_search_topic_button">
|
||||||
|
<i class="fa fa-remove" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</li>
|
Loading…
Reference in New Issue