message feed: Notify user when messages are not being marked as read.

Notifies user when messages are not being marked as read through a
banner that lets them mark all messages in the narrow as read. Note
that the banner is only displayed if the user's actions, like
scrolling, would've actually marked the messages as read.

This avoids distracting the user when viewing a thread they've already
read.

tabbott has verified that if new messages come in, the banner will reappear.

Fixes: #18768.
This commit is contained in:
Dinesh 2021-08-02 23:39:56 +05:30 committed by Tim Abbott
parent bb5889b0a0
commit 6afdf2410d
10 changed files with 82 additions and 0 deletions

View File

@ -114,6 +114,7 @@ run_test("unread_ops", ({override, override_rewire}) => {
override(message_lists.home, "show_message_as_read", () => {});
override(notifications, "close_notification", () => {});
override(unread_ui, "update_unread_counts", () => {});
override(unread_ui, "notify_messages_remain_unread", () => {});
// Set up a way to capture the options passed in to channel.post.
let channel_post_opts;
@ -127,6 +128,7 @@ run_test("unread_ops", ({override, override_rewire}) => {
// toggle it easily from within the test (and avoid complicated
// data setup).
override(message_lists.current, "can_mark_messages_read", () => can_mark_messages_read);
override(message_lists.current, "has_unread_messages", () => true);
// First, test for a message list that cannot read messages.
can_mark_messages_read = false;

View File

@ -99,6 +99,7 @@ mock_esm("../../static/js/muted_topics", {
});
mock_esm("../../static/js/narrow", {
set_narrow_title: noop,
hide_mark_as_read_turned_off_banner: noop,
});
mock_esm("../../static/js/recent_senders", {
get_topic_recent_senders: () => [1, 2],

View File

@ -10,7 +10,9 @@ import * as message_viewport from "./message_viewport";
import * as narrow_banner from "./narrow_banner";
import * as narrow_state from "./narrow_state";
import * as recent_topics_util from "./recent_topics_util";
import * as unread from "./unread";
import * as unread_ops from "./unread_ops";
import * as unread_ui from "./unread_ui";
let actively_scrolling = false;
@ -245,6 +247,19 @@ export function initialize() {
}
if (event.msg_list.can_mark_messages_read()) {
unread_ops.notify_server_messages_read(messages, {from: "pointer"});
} else if (
unread.get_unread_messages(messages).length !== 0 &&
// The below checks might seem redundant, but it's
// possible this logic, which runs after a delay, lost
// a race with switching to another view, like Recent
// Topics, and we don't want to displ[ay this banner
// in such a view.
//
// This can likely be fixed more cleanly with another approach.
narrow_state.filter() !== undefined &&
message_lists.current === event.msg_list
) {
unread_ui.notify_messages_remain_unread();
}
}
});

View File

@ -153,12 +153,17 @@ function update_narrow_title(filter) {
}
}
export function hide_mark_as_read_turned_off_banner() {
$("#mark_as_read_turned_off_banner").hide();
}
export function reset_ui_state() {
// Resets the state of various visual UI elements that are
// a function of the current narrow.
narrow_banner.hide_empty_narrow_message();
message_scroll.hide_top_of_narrow_notices();
message_scroll.hide_indicators();
hide_mark_as_read_turned_off_banner();
}
export function activate(raw_operators, opts) {

View File

@ -622,6 +622,8 @@ export function show() {
$("#message_view_header_underpadding").hide();
$(".header").css("padding-bottom", "0px");
narrow.hide_mark_as_read_turned_off_banner();
// We want to show `new stream message` instead of
// `new topic`, which we are already doing in this
// function. So, we reuse it here.

View File

@ -102,6 +102,14 @@ export function notify_server_message_read(message, options) {
export function process_scrolled_to_bottom() {
if (message_lists.current.can_mark_messages_read()) {
mark_current_list_as_read();
return;
}
// For message lists that don't support marking messages as read
// automatically, we display a banner offering to let you mark
// them as read manually, only if there are unreads present.
if (message_lists.current.has_unread_messages()) {
unread_ui.notify_messages_remain_unread();
}
}

View File

@ -1,6 +1,9 @@
import $ from "jquery";
import render_mark_as_read_turned_off_banner from "../templates/mark_as_read_turned_off_banner.hbs";
import * as activity from "./activity";
import * as message_lists from "./message_lists";
import * as notifications from "./notifications";
import {page_params} from "./page_params";
import * as pm_list from "./pm_list";
@ -8,6 +11,7 @@ import * as stream_list from "./stream_list";
import * as top_left_corner from "./top_left_corner";
import * as topic_list from "./topic_list";
import * as unread from "./unread";
import {notify_server_messages_read} from "./unread_ops";
let last_mention_count = 0;
@ -89,6 +93,26 @@ export function should_display_bankruptcy_banner() {
return false;
}
export function notify_messages_remain_unread() {
$("#mark_as_read_turned_off_banner").show();
}
export function initialize() {
update_unread_counts();
$("#mark_as_read_turned_off_banner").html(render_mark_as_read_turned_off_banner());
$("#mark_as_read_turned_off_banner").hide();
$("#mark_view_read").on("click", () => {
// Mark all messages in the current view as read.
//
// BUG: This logic only supports marking messages visible in
// the present view as read; we need a server API to mark
// every message matching the current search as read.
const unread_messages = message_lists.current.data
.all_messages()
.filter((message) => unread.message_unread(message));
notify_server_messages_read(unread_messages);
$("#mark_as_read_turned_off_banner").hide();
});
}

View File

@ -1143,6 +1143,20 @@ td.pointer {
padding-right: 2px;
}
#mark_as_read_turned_off_banner {
display: flex;
align-items: center;
justify-content: space-between;
#mark_as_read_turned_off_content {
margin: 0;
}
.mark_as_read_close {
margin-top: 6px;
}
}
.summary_row {
.message_header {
padding: 5px 0 4px 5px;

View File

@ -0,0 +1,9 @@
<p id="mark_as_read_turned_off_content">
{{t 'To preserve your reading state, this view does not mark messages as read.' }}
</p>
<div id="mark_as_read_controls">
<button id="mark_view_read" class="btn btn-warning" title="{{t 'Mark as read' }}">
{{t 'Mark as read' }}
</button>
<button type="button" class="mark_as_read_close close">×</button>
</div>

View File

@ -170,6 +170,8 @@
</div>
<div id="typing_notifications">
</div>
<div id="mark_as_read_turned_off_banner" class="alert home-error-bar">
</div>
<div id="bottom_whitespace">
<div class="bottom-messages-logo">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 773.12 773.12">