mirror of https://github.com/zulip/zulip.git
pm_list: Add search to direct message section.
Fixes #22113. The search will only be visible when in the `more conversations` view. Once we click `back to channels` and close the `more conversations` view, the search will clear and the search box will disappear. We've chosen `pm_list_data.get_conversations` as the function to which we will pass our search term. We could have technically found the value of the filter element via JQuery in pm_list_data, but pm_list_data does not handle any JQuery and we should keep it that way. `pm_list_data.get_list_info` also accepts the search_string so that the info it returns in expanded view is accurate. We've also added some code to `click_handlers` to make sure that clicking the search input does not bring us into the DM narrow.
This commit is contained in:
parent
188dd87eec
commit
76e8ec114a
|
@ -774,7 +774,13 @@ export function initialize() {
|
|||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
window.location.hash = "narrow/is/dm";
|
||||
if (!$(e.target).hasClass("direct-messages-list-filter")) {
|
||||
// Avoiding having clicks on the filter input.
|
||||
//
|
||||
// TODO: Refactor to use more precise selectors for this
|
||||
// click handler in general; this is a fragile pattern.
|
||||
window.location.hash = "narrow/is/dm";
|
||||
}
|
||||
});
|
||||
|
||||
// disable the draggability for left-sidebar components
|
||||
|
|
|
@ -39,8 +39,10 @@ export function close(): void {
|
|||
}
|
||||
|
||||
export function _build_direct_messages_list(): vdom.Tag<PMNode> {
|
||||
const conversations = pm_list_data.get_conversations();
|
||||
const pm_list_info = pm_list_data.get_list_info(zoomed);
|
||||
const $filter = $<HTMLInputElement>(".direct-messages-list-filter").expectOne();
|
||||
const search_term = $filter.val()!;
|
||||
const conversations = pm_list_data.get_conversations(search_term);
|
||||
const pm_list_info = pm_list_data.get_list_info(zoomed, search_term);
|
||||
const conversations_to_be_shown = pm_list_info.conversations_to_be_shown;
|
||||
const more_conversations_unread_count = pm_list_info.more_conversations_unread_count;
|
||||
|
||||
|
@ -55,6 +57,12 @@ export function _build_direct_messages_list(): vdom.Tag<PMNode> {
|
|||
);
|
||||
}
|
||||
const dom_ast = pm_list_dom.pm_ul(pm_list_nodes);
|
||||
|
||||
if (search_term === "") {
|
||||
$("#clear-direct-messages-search-button").hide();
|
||||
} else {
|
||||
$("#clear-direct-messages-search-button").show();
|
||||
}
|
||||
return dom_ast;
|
||||
}
|
||||
|
||||
|
@ -197,16 +205,31 @@ function zoom_in(): void {
|
|||
$(".direct-messages-container").removeClass("zoom-out").addClass("zoom-in");
|
||||
$("#streams_list").hide();
|
||||
$(".left-sidebar .right-sidebar-items").hide();
|
||||
|
||||
const $filter = $(".direct-messages-list-filter").expectOne();
|
||||
$filter.trigger("focus");
|
||||
}
|
||||
|
||||
function zoom_out(): void {
|
||||
zoomed = false;
|
||||
update_private_messages();
|
||||
clear_search(true); // force rerender if the search is empty.
|
||||
$(".direct-messages-container").removeClass("zoom-in").addClass("zoom-out");
|
||||
$("#streams_list").show();
|
||||
$(".left-sidebar .right-sidebar-items").show();
|
||||
}
|
||||
|
||||
export function clear_search(force_rerender = false): void {
|
||||
const $filter = $(".direct-messages-list-filter").expectOne();
|
||||
if ($filter.val() !== "") {
|
||||
$filter.val("");
|
||||
update_private_messages();
|
||||
} else if (force_rerender) {
|
||||
update_private_messages();
|
||||
}
|
||||
}
|
||||
|
||||
const throttled_update_private_message = _.throttle(update_private_messages, 50);
|
||||
|
||||
export function initialize(): void {
|
||||
$(".direct-messages-container").on("click", "#show-more-direct-messages", (e) => {
|
||||
e.stopPropagation();
|
||||
|
@ -221,4 +244,18 @@ export function initialize(): void {
|
|||
|
||||
zoom_out();
|
||||
});
|
||||
|
||||
$(".direct-messages-container").on("input", ".direct-messages-list-filter", (e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
throttled_update_private_message();
|
||||
});
|
||||
|
||||
$(".direct-messages-container").on("click", "#clear-direct-messages-search-button", (e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
clear_search();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ type DisplayObject = {
|
|||
is_bot: boolean;
|
||||
};
|
||||
|
||||
export function get_conversations(): DisplayObject[] {
|
||||
export function get_conversations(search_string = ""): DisplayObject[] {
|
||||
const conversations = pm_conversations.recent.get();
|
||||
const display_objects = [];
|
||||
|
||||
|
@ -66,6 +66,16 @@ export function get_conversations(): DisplayObject[] {
|
|||
|
||||
for (const conversation of conversations) {
|
||||
const user_ids_string = conversation.user_ids_string;
|
||||
|
||||
const users = people.get_users_from_ids(
|
||||
people.user_ids_string_to_ids_array(user_ids_string),
|
||||
);
|
||||
if (!people.dm_matches_search_string(users, search_string)) {
|
||||
// Skip adding the conversation to the display_objects array if it does
|
||||
// not match the search_term.
|
||||
continue;
|
||||
}
|
||||
|
||||
const reply_to = people.user_ids_string_to_emails_string(user_ids_string);
|
||||
assert(reply_to !== undefined);
|
||||
const recipients_string = people.get_recipients(user_ids_string);
|
||||
|
@ -110,11 +120,14 @@ export function get_conversations(): DisplayObject[] {
|
|||
}
|
||||
|
||||
// Designed to closely match topic_list_data.get_list_info().
|
||||
export function get_list_info(zoomed: boolean): {
|
||||
export function get_list_info(
|
||||
zoomed: boolean,
|
||||
search_term = "",
|
||||
): {
|
||||
conversations_to_be_shown: DisplayObject[];
|
||||
more_conversations_unread_count: number;
|
||||
} {
|
||||
const conversations = get_conversations();
|
||||
const conversations = get_conversations(search_term);
|
||||
|
||||
if (zoomed || conversations.length <= max_conversations_to_show) {
|
||||
return {
|
||||
|
|
|
@ -1130,6 +1130,13 @@ li.topic-list-item {
|
|||
}
|
||||
}
|
||||
|
||||
/* Since direct-messages-sticky-header also has the `input-append`
|
||||
class accompanying it. The display property of that class will
|
||||
overwrite display: none if we don't have a more specific CSS
|
||||
rule. It will also overwrite `display: none` even if `.zoom-out`
|
||||
properties are declared after the `.input-append` properties since
|
||||
the latter is more specific. */
|
||||
#direct-messages-sticky-header.zoom-out,
|
||||
.zoom-out {
|
||||
#topics_header {
|
||||
display: none;
|
||||
|
@ -1225,53 +1232,6 @@ li.topic-list-item {
|
|||
vertical centering. */
|
||||
line-height: 20px;
|
||||
white-space: nowrap;
|
||||
|
||||
.stream-list-filter {
|
||||
/* Use the border-box model so flex
|
||||
can do its thing despite whatever
|
||||
padding and border we specify. */
|
||||
box-sizing: border-box;
|
||||
flex: 1 0 100%;
|
||||
/* Match the input height exactly
|
||||
with the row height for a perfect
|
||||
fit and better vertical alignment. */
|
||||
height: 28px;
|
||||
/* Pad the entire clear-button area,
|
||||
so that input text does not bleed
|
||||
into there. */
|
||||
padding-right: 30px;
|
||||
}
|
||||
|
||||
.clear_search_button {
|
||||
/* Use the border-box model so flex
|
||||
can do its thing despite whatever
|
||||
padding and border we specify. */
|
||||
box-sizing: border-box;
|
||||
/* Clear inherited positioning. */
|
||||
position: static;
|
||||
/* We're going to use flexbox, not
|
||||
positioning, to get the clear button
|
||||
over top of the input. This -30px
|
||||
margin accomplishes that, in tandem
|
||||
with the 30px width of this element,
|
||||
which is shared with the ending
|
||||
anchor element in left sidebar header
|
||||
rows. */
|
||||
width: 30px;
|
||||
margin-left: -30px;
|
||||
/* Flexbox respects z-index; this just
|
||||
ensures the button remains over top
|
||||
of the input. */
|
||||
z-index: 1;
|
||||
/* Make the button itself a flex container,
|
||||
so we can perfectly center the X icon. */
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
/* Flexbox will pull the element open
|
||||
to make a generous clickable area. */
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.hide_unread_counts {
|
||||
|
@ -1307,6 +1267,57 @@ li.topic-list-item {
|
|||
}
|
||||
}
|
||||
|
||||
.stream_search_section,
|
||||
.direct-messages-search-section {
|
||||
.stream-list-filter,
|
||||
.direct-messages-list-filter {
|
||||
/* Use the border-box model so flex
|
||||
can do its thing despite whatever
|
||||
padding and border we specify. */
|
||||
box-sizing: border-box;
|
||||
flex: 1 0 100%;
|
||||
/* Match the input height exactly
|
||||
with the row height for a perfect
|
||||
fit and better vertical alignment. */
|
||||
height: 28px;
|
||||
/* Pad the entire clear-button area,
|
||||
so that input text does not bleed
|
||||
into there. */
|
||||
padding-right: 30px;
|
||||
}
|
||||
|
||||
.clear_search_button {
|
||||
/* Use the border-box model so flex
|
||||
can do its thing despite whatever
|
||||
padding and border we specify. */
|
||||
box-sizing: border-box;
|
||||
/* Clear inherited positioning. */
|
||||
position: static;
|
||||
/* We're going to use flexbox, not
|
||||
positioning, to get the clear button
|
||||
over top of the input. This -30px
|
||||
margin accomplishes that, in tandem
|
||||
with the 30px width of this element,
|
||||
which is shared with the ending
|
||||
anchor element in left sidebar header
|
||||
rows. */
|
||||
width: 30px;
|
||||
margin-left: -30px;
|
||||
/* Flexbox respects z-index; this just
|
||||
ensures the button remains over top
|
||||
of the input. */
|
||||
z-index: 1;
|
||||
/* Make the button itself a flex container,
|
||||
so we can perfectly center the X icon. */
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
/* Flexbox will pull the element open
|
||||
to make a generous clickable area. */
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Prepare an adjusted grid for the logged-out state,
|
||||
one that reassigns the vdots space to markers and
|
||||
controls. */
|
||||
|
@ -1415,6 +1426,13 @@ li.topic-list-item {
|
|||
}
|
||||
}
|
||||
|
||||
.direct-messages-search-section {
|
||||
display: flex;
|
||||
grid-column: row-content / markers-and-controls;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.zoom-in-hide {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -155,6 +155,12 @@
|
|||
<a class="zoom-out-hide" id="hide-more-direct-messages">
|
||||
<span class="hide-more-direct-messages-text"> {{t 'back to channels' }}</span>
|
||||
</a>
|
||||
<div class="zoom-out-hide direct-messages-search-section">
|
||||
<input class="direct-messages-list-filter filter_text_input" type="text" autocomplete="off" placeholder="{{t 'Filter direct messages' }}" />
|
||||
<button type="button" class="btn clear_search_button" id="clear-direct-messages-search-button">
|
||||
<i class="fa fa-remove" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{{~!-- squash whitespace --~}}
|
||||
<div id="left_sidebar_scroll_container" class="scrolling_list" data-simplebar data-simplebar-tab-index="-1">
|
||||
|
|
|
@ -163,6 +163,19 @@ test("get_conversations", ({override}) => {
|
|||
set_pm_with_filter("iago@zulip.com");
|
||||
pm_data = pm_list_data.get_conversations();
|
||||
assert.deepEqual(pm_data, expected_data);
|
||||
|
||||
pm_data = pm_list_data.get_conversations("Ia");
|
||||
assert.deepEqual(
|
||||
pm_data,
|
||||
expected_data.filter((item) => item.recipients === "Iago"),
|
||||
);
|
||||
|
||||
// filter should work with email
|
||||
pm_data = pm_list_data.get_conversations("me@zulip");
|
||||
assert.deepEqual(
|
||||
pm_data,
|
||||
expected_data.filter((item) => item.recipients === "Me Myself"),
|
||||
);
|
||||
});
|
||||
|
||||
test("get_conversations bot", ({override}) => {
|
||||
|
|
Loading…
Reference in New Issue