Skip to content

Commit

Permalink
message_list: Add buttons for different actions.
Browse files Browse the repository at this point in the history
Fixes #30405
Fixes #18728
  • Loading branch information
amanagr authored and timabbott committed Sep 23, 2024
1 parent ec16309 commit f6fe1b1
Show file tree
Hide file tree
Showing 36 changed files with 550 additions and 159 deletions.
7 changes: 3 additions & 4 deletions templates/zerver/app/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -274,14 +274,13 @@
<div id="page_loading_indicator"></div>
<div id="message_feed_errors_container"></div>
<div id="message-lists-container"></div>
<div id="scheduled_message_indicator">
<div id="message-list-navigation">
{% include "zerver/message-list-navigation-buttons.html" %}
</div>
<div id="mark_read_on_scroll_state_banner">
<div id="scheduled_message_indicator">
</div>
<div id="typing_notifications">
</div>
<div id="mark_read_on_scroll_state_banner_place_holder">
</div>
<div id="bottom_whitespace">
</div>
</div>
Expand Down
11 changes: 11 additions & 0 deletions templates/zerver/message-list-navigation-buttons.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<div id="message-list-navigation-buttons-wrapper">
<button id="message-list-navigation-home-view" class="message-list-navigation-button">
{{ _('Home view') }}
</button>
<button id="message-list-navigation-mark-as-read" class="message-list-navigation-button">
{{ _('Mark as read') }}
</button>
<button id="message-list-navigation-next-unread-conversation" class="message-list-navigation-button">
{{ _('Next unread conversation') }}
</button>
</div>
1 change: 1 addition & 0 deletions tools/test-js-with-node
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ EXEMPT_FILES = make_set(
"web/src/message_list_data.ts",
"web/src/message_list_data_cache.ts",
"web/src/message_list_hover.ts",
"web/src/message_list_navigation.ts",
"web/src/message_list_tooltips.ts",
"web/src/message_list_view.ts",
"web/src/message_lists.ts",
Expand Down
11 changes: 11 additions & 0 deletions web/src/click_handlers.js
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,17 @@ export function initialize() {

// MISC

$("#message-list-navigation-next-unread-conversation").on("click", (e) => {
assert(message_lists.current !== undefined);
e.currentTarget.blur();

if (message_lists.current.data.filter.can_show_next_unread_dm_conversation_button()) {
message_view.narrow_to_next_pm_string({trigger: "hotkey"});
} else {
message_view.narrow_to_next_topic({trigger: "hotkey", only_followed_topics: false});
}
});

{
const sel = [
"#stream_filters",
Expand Down
33 changes: 33 additions & 0 deletions web/src/hotkey.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import * as list_util from "./list_util";
import * as message_actions_popover from "./message_actions_popover";
import * as message_edit from "./message_edit";
import * as message_edit_history from "./message_edit_history";
import * as message_list_navigation from "./message_list_navigation";
import * as message_lists from "./message_lists";
import * as message_scroll_state from "./message_scroll_state";
import * as message_view from "./message_view";
Expand Down Expand Up @@ -63,6 +64,7 @@ import * as user_card_popover from "./user_card_popover";
import * as user_group_popover from "./user_group_popover";
import {user_settings} from "./user_settings";
import * as user_topics_ui from "./user_topics_ui";
import * as views_util from "./views_util";

function do_narrow_action(action) {
if (message_lists.current === undefined) {
Expand Down Expand Up @@ -694,6 +696,33 @@ export function process_hotkey(e, hotkey) {
}
}

switch (event_name) {
case "right_arrow":
if (message_lists.current?.navigation_bar_focused && views_util.is_in_focus()) {
return message_list_navigation.handle_right_arrow();
}
break;
case "left_arrow":
if (
message_lists.current?.navigation_bar_focused &&
views_util.is_in_focus() &&
message_list_navigation.is_any_button_focused()
) {
return message_list_navigation.handle_left_arrow();
}
break;
case "enter":
if (
message_lists.current?.navigation_bar_focused &&
views_util.is_in_focus() &&
!message_lists.current.data.filter.is_conversation_view() &&
!message_lists.current.data.filter.is_conversation_view_with_near()
) {
// Do nothing in non conversation views when navigation bar is selected.
return true;
}
}

// We handle the most complex keys in their own functions.
switch (event_name) {
case "escape":
Expand Down Expand Up @@ -1102,6 +1131,10 @@ export function process_hotkey(e, hotkey) {
return true;
}

if (message_lists.current.is_navigation_bar_focused()) {
return false;
}

const msg = message_lists.current.selected_message();

// Shortcuts that operate on a message
Expand Down
2 changes: 2 additions & 0 deletions web/src/message_fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import * as message_feed_top_notices from "./message_feed_top_notices";
import * as message_helper from "./message_helper";
import type {MessageList} from "./message_list";
import type {MessageListData} from "./message_list_data";
import * as message_list_navigation from "./message_list_navigation";
import * as message_lists from "./message_lists";
import {raw_message_schema} from "./message_store";
import * as message_util from "./message_util";
Expand Down Expand Up @@ -130,6 +131,7 @@ function process_result(data: MessageFetchResponse, opts: MessageFetchOptions):
direct_message_group_data.process_loaded_messages(messages);
stream_list.update_streams_sidebar();
stream_list.maybe_scroll_narrow_into_view();
message_list_navigation.update();

if (
message_lists.current !== undefined &&
Expand Down
44 changes: 44 additions & 0 deletions web/src/message_list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,16 @@ export class MessageList {
// the user. Possibly this can be unified in some nice way.
reading_prevented: boolean;

// Tracks if the message list is visibly focused (has blue border
// around the selected message) or if the navigation buttons at
// the bottom of the feed are focused.
navigation_bar_focused: boolean;

// Tracks if the navigation bar is visible or not. Navigation bar
// hidden if there are no buttons to show or if the message feed
// is empty.
navigation_bar_visible: boolean;

// TODO: Clean up these monkey-patched properties somehow.
last_message_historical?: boolean;
should_trigger_message_selected_event?: boolean;
Expand Down Expand Up @@ -102,6 +112,8 @@ export class MessageList {
this.view = new MessageListView(this, collapse_messages, opts.is_node_test);
this.is_combined_feed_view = this.data.filter.is_in_home();
this.reading_prevented = false;
this.navigation_bar_focused = false;
this.navigation_bar_visible = false;

return this;
}
Expand Down Expand Up @@ -389,6 +401,38 @@ export class MessageList {
}
}

focus_navigation_bar(): void {
this.navigation_bar_focused = true;

if (this.navigation_bar_visible) {
this.view.focus_navigation_bar();
}
}

unfocus_navigation_bar(): void {
this.navigation_bar_focused = false;
this.view.unfocus_navigation_bar();
}

hide_navigation_bar(): void {
this.navigation_bar_visible = false;
this.view.unfocus_navigation_bar();
this.view.hide_navigation_bar();
}

show_navigation_bar(): void {
this.navigation_bar_visible = true;
this.view.show_navigation_bar();

if (this.navigation_bar_focused) {
this.focus_navigation_bar();
}
}

is_navigation_bar_focused(): boolean {
return this.navigation_bar_visible && this.navigation_bar_focused;
}

selected_message(): Message | undefined {
return this.get(this.data.selected_id());
}
Expand Down
175 changes: 175 additions & 0 deletions web/src/message_list_navigation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
import $ from "jquery";
import assert from "minimalistic-assert";
import type * as tippy from "tippy.js";

import * as browser_history from "./browser_history";
import * as message_lists from "./message_lists";
import * as narrow_state from "./narrow_state";
import {web_mark_read_on_scroll_policy_values} from "./settings_config";
import * as unread from "./unread";
import {user_settings} from "./user_settings";

let last_clicked_home_view_button = false;

export function update_next_unread_conversation_button(): boolean {
if (message_lists.current === undefined) {
return false;
}

const filter = message_lists.current.data.filter;
const $next_unread = $("#message-list-navigation-next-unread-conversation");
if (
(filter.can_show_next_unread_dm_conversation_button() &&
unread.get_msg_ids_for_private().length !== 0) ||
(filter.can_show_next_unread_topic_conversation_button() &&
unread.get_unread_topics().stream_unread_messages !== 0)
) {
$next_unread.show();
return true;
}
$next_unread.hide();
return false;
}

export function update_mark_as_read_button(): boolean {
if (message_lists.current === undefined) {
return false;
}

const $mark_as_read = $("#message-list-navigation-mark-as-read");

if (!message_lists.current.has_unread_messages()) {
$mark_as_read.hide();
return false;
}

const filter = narrow_state.filter();
const is_conversation_view = filter === undefined ? false : filter.is_conversation_view();
const msg_list_navigation_mark_as_read: tippy.ReferenceElement | undefined =
$mark_as_read.get(0);
assert(msg_list_navigation_mark_as_read !== undefined);

if (
user_settings.web_mark_read_on_scroll_policy ===
web_mark_read_on_scroll_policy_values.never.code
) {
$mark_as_read.show();
return true;
}

if (
user_settings.web_mark_read_on_scroll_policy ===
web_mark_read_on_scroll_policy_values.conversation_only.code &&
!is_conversation_view
) {
$mark_as_read.show();
return true;
}

if (message_lists.current.can_mark_messages_read_without_setting()) {
$mark_as_read.hide();
return false;
}

$mark_as_read.show();
return true;
}

export function update_home_view_button(): boolean {
const $home_view_button = $("#message-list-navigation-home-view");
if (browser_history.is_current_hash_home_view()) {
$home_view_button.hide();
return false;
}
$home_view_button.show();
return true;
}

export function update(): void {
if (message_lists.current === undefined) {
return;
}

if (message_lists.current.visibly_empty()) {
message_lists.current.hide_navigation_bar();
return;
}

const update_button_functions = [
update_home_view_button,
update_mark_as_read_button,
update_next_unread_conversation_button,
];

let any_button_visible = false;
for (const update_function of update_button_functions) {
if (update_function()) {
any_button_visible = true;
}
}

if (any_button_visible) {
message_lists.current.show_navigation_bar();
} else {
message_lists.current.hide_navigation_bar();
}
}

export function is_any_button_focused(): boolean {
return document.activeElement?.classList.contains("message-list-navigation-button") ?? false;
}

export function handle_left_arrow(): boolean {
assert(document.activeElement !== null);

const $focused_button = $(document.activeElement);
const $prev_button = $focused_button.prevAll(":visible").first();

if ($prev_button.length === 0) {
$focused_button.trigger("blur");
} else {
$prev_button.trigger("focus");
}

return true;
}

export function handle_right_arrow(): boolean {
if (!is_any_button_focused()) {
if ($("#message-list-navigation-mark-as-read:visible").length !== 0) {
$("#message-list-navigation-mark-as-read").trigger("focus");
} else if (
$("#message-list-navigation-home-view:visible") &&
last_clicked_home_view_button
) {
$("#message-list-navigation-home-view").trigger("focus");
} else {
$("#message-list-navigation-next-unread-conversation").trigger("focus");
}
return true;
}
assert(document.activeElement !== null);

const $focused_button = $(document.activeElement);
const $next_button = $focused_button.nextAll(":visible").first();
if ($next_button.length === 0) {
$focused_button.trigger("blur");
} else {
$next_button.trigger("focus");
}

return true;
}

export function init(): void {
$("#message-list-navigation-home-view").on("click", (e) => {
last_clicked_home_view_button = true;
e.currentTarget.blur();
browser_history.set_hash("");
$(document).trigger(new $.Event("hashchange"));
});

$("#message-list-navigation-next-unread-conversation").on("click", () => {
last_clicked_home_view_button = false;
});
}
Loading

0 comments on commit f6fe1b1

Please sign in to comment.