Skip to content

Commit

Permalink
left_sidebar: Add unread counts to the Alert Views.
Browse files Browse the repository at this point in the history
This commit adds the unread counts to the Alert Views, both in
condensed and non condensed state.

Fixes: #28778.
  • Loading branch information
roanster007 committed Feb 11, 2024
1 parent 573a0e1 commit 696c636
Show file tree
Hide file tree
Showing 12 changed files with 318 additions and 2 deletions.
10 changes: 10 additions & 0 deletions api_docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ format used by the Zulip server that they are interacting with.

## Changes in Zulip 9.0

**Feature level 240**

* [`POST /register`](/api/register-queue):
Added a new property - `alerts` to the `unread_msgs`, which is an
array that includes message ids of all the messages which contain alert
words set by the user.
* [`GET /events`](/api/get-events):
Added an `has_alert_words` flag which indicates whether a message includes
any of the alert words set by the user or not.

Feature levels 238-239 are reserved for future use in 8.x maintenance
releases.

Expand Down
2 changes: 2 additions & 0 deletions web/src/left_sidebar_navigation_area.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,13 @@ export function update_dom_with_unread_counts(counts, skip_animations) {

// mentioned/home views have simple integer counts
const $mentioned_li = $(".top_left_mentions");
const $alerted_li = $(".top_left_alerts");
const $home_view_li = $(".selected-home-view");
const $streams_header = $("#streams_header");
const $back_to_streams = $("#topics_header");

ui_util.update_unread_count_in_dom($mentioned_li, counts.mentioned_message_count);
ui_util.update_unread_count_in_dom($alerted_li, counts.alert_message_count);
ui_util.update_unread_count_in_dom($home_view_li, counts.home_unread_messages);
ui_util.update_unread_count_in_dom($streams_header, counts.stream_unread_messages);
ui_util.update_unread_count_in_dom($back_to_streams, counts.stream_unread_messages);
Expand Down
5 changes: 5 additions & 0 deletions web/src/left_sidebar_navigation_area_popovers.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import * as settings_config from "./settings_config";
import * as starred_messages from "./starred_messages";
import * as starred_messages_ui from "./starred_messages_ui";
import * as ui_util from "./ui_util";
import {get_counts} from "./unread";
import * as unread_ops from "./unread_ops";
import {user_settings} from "./user_settings";

Expand Down Expand Up @@ -257,6 +258,10 @@ export function initialize() {
);
},
onMount() {
ui_util.update_unread_count_in_dom(
$(".condensed-views-popover-menu-alerts"),
get_counts().alert_message_count,
);
ui_util.update_unread_count_in_dom(
$(".condensed-views-popover-menu-drafts"),
drafts.draft_model.getDraftCount(),
Expand Down
36 changes: 36 additions & 0 deletions web/src/unread.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ export const unread_mentions_counter = new Set<number>();
export const direct_message_with_mention_count = new Set();
const unread_messages = new Set<number>();

export const unread_alerts_counter = new Set<number>();

// Map with keys of the form "{stream_id}:{topic.toLowerCase()}" and
// values being Sets of message IDs for unread messages mentioning the
// user within that topic. Use `recent_view_util.get_topic_key` to
Expand Down Expand Up @@ -676,6 +678,7 @@ export function process_loaded_messages(
if (message.type === "private") {
process_unread_message({
id: message.id,
alerted: message.alerted,
mentioned: message.mentioned,
mentioned_me_directly: message.mentioned_me_directly,
type: message.type,
Expand All @@ -685,6 +688,7 @@ export function process_loaded_messages(
} else {
process_unread_message({
id: message.id,
alerted: message.alerted,
mentioned: message.mentioned,
mentioned_me_directly: message.mentioned_me_directly,
type: message.type,
Expand All @@ -702,6 +706,7 @@ export function process_loaded_messages(

type UnreadMessageData = {
id: number;
alerted: boolean;
mentioned: boolean;
mentioned_me_directly: boolean;
unread: boolean;
Expand Down Expand Up @@ -740,6 +745,7 @@ export function process_unread_message(message: UnreadMessageData): void {
}

update_message_for_mention(message);
update_message_for_alert(message);
}

export function update_message_for_mention(
Expand Down Expand Up @@ -787,6 +793,27 @@ export function update_message_for_mention(
return false;
}

export function update_message_for_alert(
message: UnreadMessageData,
content_edited = false,
): boolean {
// Returns true if this is a stream message whose content was
// changed, and thus the caller might need to trigger a rerender
// of UI elements displaying whether the message's topic contains
// an unread mention of the user.
if (!message.unread) {
unread_alerts_counter.delete(message.id);
return false;
}
if (message.alerted) {
unread_alerts_counter.add(message.id);
}
if (content_edited && message.type === "stream") {
return true;
}
return false;
}

export function mark_as_read(message_id: number): void {
// We don't need to check anything about the message, since all
// the following methods are cheap and work fine even if message_id
Expand All @@ -799,6 +826,7 @@ export function mark_as_read(message_id: number): void {
remove_message_from_unread_mention_topics(message_id);
unread_topic_counter.delete(message_id);
unread_mentions_counter.delete(message_id);
unread_alerts_counter.delete(message_id);
direct_message_with_mention_count.delete(message_id);
unread_messages.delete(message_id);

Expand All @@ -813,6 +841,7 @@ export function declare_bankruptcy(): void {
unread_direct_message_counter.clear();
unread_topic_counter.clear();
unread_mentions_counter.clear();
unread_alerts_counter.clear();
direct_message_with_mention_count.clear();
unread_messages.clear();
unread_mention_topics.clear();
Expand All @@ -833,6 +862,7 @@ export function get_unread_topics(include_per_topic_latest_msg_id = false): Unre
export type FullUnreadCountsData = {
direct_message_count: number;
mentioned_message_count: number;
alert_message_count: number;
direct_message_with_mention_count: number;
stream_unread_messages: number;
followed_topic_unread_messages_count: number;
Expand All @@ -854,6 +884,7 @@ export function get_counts(): FullUnreadCountsData {
return {
direct_message_count: pm_res.total_count,
mentioned_message_count: unread_mentions_counter.size,
alert_message_count: unread_alerts_counter.size,
direct_message_with_mention_count: direct_message_with_mention_count.size,
stream_unread_messages: topic_res.stream_unread_messages,
followed_topic_unread_messages_count: topic_res.followed_topic_unread_messages,
Expand Down Expand Up @@ -1023,6 +1054,7 @@ type UnreadMessagesParams = {
streams: UnreadStreamInfo[];
huddles: UnreadHuddleInfo[];
mentions: number[];
alerts: number[];
count: number;
old_unreads_missing: boolean;
};
Expand All @@ -1043,6 +1075,10 @@ export function initialize(params: UnreadMessagesParams): void {
}
clear_and_populate_unread_mention_topics();

for (const message_id of unread_msgs.alerts) {
unread_alerts_counter.add(message_id);
}

for (const obj of unread_msgs.huddles) {
for (const message_id of obj.unread_message_ids) {
unread_messages.add(message_id);
Expand Down
1 change: 1 addition & 0 deletions web/src/unread_ops.js
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,7 @@ export function process_unread_messages_event({message_ids, message_details}) {
unread.process_unread_message({
id: message_id,
mentioned: message_info.mentioned,
alerted: message_info.alert,
mentioned_me_directly,
stream_id: message_info.stream_id,
topic: message_info.topic,
Expand Down
4 changes: 4 additions & 0 deletions web/tests/left_sidebar_navigation_area.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,13 @@ run_test("update_count_in_dom", () => {
mentioned_message_count: 222,
home_unread_messages: 333,
stream_unread_messages: 666,
alert_message_count: 999,
};

make_elem($(".top_left_mentions"), "<mentioned-count>");

make_elem($(".top_left_alerts"), "<alerted-count>");

make_elem($(".top_left_inbox"), "<home-count>");

make_elem($(".selected-home-view"), "<home-count>");
Expand All @@ -104,6 +107,7 @@ run_test("update_count_in_dom", () => {
left_sidebar_navigation_area.initialize();

assert.equal($("<mentioned-count>").text(), "222");
assert.equal($("<alerted-count>").text(), "999");
assert.equal($("<home-count>").text(), "333");
assert.equal($("<starred-count>").text(), "444");
assert.equal($("<scheduled-count>").text(), "555");
Expand Down
1 change: 1 addition & 0 deletions web/tests/unread.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -768,6 +768,7 @@ test("server_counts", () => {
},
],
mentions: [31, 34, 40, 41],
alerts: [23, 74, 10, 99],
},
};

Expand Down
1 change: 1 addition & 0 deletions zerver/lib/event_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -1753,6 +1753,7 @@ def check_update_message(
],
optional_keys=[
("mentioned", bool),
("has_alert_words", bool),
("user_ids", ListType(int)),
("stream_id", int),
("topic", str),
Expand Down
23 changes: 22 additions & 1 deletion zerver/lib/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
class MessageDetailsDict(TypedDict, total=False):
type: str
mentioned: bool
has_alert_words: bool
user_ids: List[int]
stream_id: int
topic: str
Expand Down Expand Up @@ -120,6 +121,7 @@ class RawUnreadMessagesResult(TypedDict):
stream_dict: Dict[int, RawUnreadStreamDict]
huddle_dict: Dict[int, RawUnreadHuddleDict]
mentions: Set[int]
alerts: Set[int]
muted_stream_ids: Set[int]
unmuted_stream_msgs: Set[int]
old_unreads_missing: bool
Expand Down Expand Up @@ -148,6 +150,7 @@ class UnreadMessagesResult(TypedDict):
streams: List[UnreadStreamInfo]
huddles: List[UnreadHuddleInfo]
mentions: List[int]
alerts: List[int]
count: int
old_unreads_missing: bool

Expand Down Expand Up @@ -1155,6 +1158,7 @@ def extract_unread_data_from_um_rows(
unmuted_stream_msgs: Set[int] = set()
huddle_dict: Dict[int, RawUnreadHuddleDict] = {}
mentions: Set[int] = set()
alerts: Set[int] = set()
total_unreads = 0

raw_unread_messages: RawUnreadMessagesResult = dict(
Expand All @@ -1164,6 +1168,7 @@ def extract_unread_data_from_um_rows(
unmuted_stream_msgs=unmuted_stream_msgs,
huddle_dict=huddle_dict,
mentions=mentions,
alerts=alerts,
old_unreads_missing=False,
)

Expand Down Expand Up @@ -1240,7 +1245,7 @@ def get_huddle_users(recipient_id: int) -> str:
user_ids_string=user_ids_string,
)

# TODO: Add support for alert words here as well.
is_alert = row["flags"] & UserMessage.flags.has_alert_word
is_mentioned = (row["flags"] & UserMessage.flags.mentioned) != 0
is_stream_wildcard_mentioned = (
row["flags"] & UserMessage.flags.stream_wildcard_mentioned
Expand All @@ -1250,6 +1255,8 @@ def get_huddle_users(recipient_id: int) -> str:
) != 0
if is_mentioned:
mentions.add(message_id)
if is_alert:
alerts.add(message_id)
if is_stream_wildcard_mentioned or is_topic_wildcard_mentioned:
if msg_type == Recipient.STREAM:
stream_id = row["message__recipient__type_id"]
Expand Down Expand Up @@ -1348,6 +1355,7 @@ def aggregate_unread_data(raw_data: RawUnreadMessagesResult) -> UnreadMessagesRe
unmuted_stream_msgs = raw_data["unmuted_stream_msgs"]
huddle_dict = raw_data["huddle_dict"]
mentions = list(raw_data["mentions"])
alerts = list(raw_data["alerts"])

count = len(pm_dict) + len(unmuted_stream_msgs) + len(huddle_dict)

Expand All @@ -1360,6 +1368,7 @@ def aggregate_unread_data(raw_data: RawUnreadMessagesResult) -> UnreadMessagesRe
streams=stream_objects,
huddles=huddle_objects,
mentions=mentions,
alerts=alerts,
count=count,
old_unreads_missing=raw_data["old_unreads_missing"],
)
Expand Down Expand Up @@ -1427,6 +1436,8 @@ def apply_unread_message_event(

if "mentioned" in flags:
state["mentions"].add(message_id)
if "has_alert_word" in flags:
state["alerts"].add(message_id)
if (
"stream_wildcard_mentioned" in flags or "topic_wildcard_mentioned" in flags
) and message_id in state["unmuted_stream_msgs"]:
Expand All @@ -1441,6 +1452,7 @@ def remove_message_id_from_unread_mgs(state: RawUnreadMessagesResult, message_id
state["huddle_dict"].pop(message_id, None)
state["unmuted_stream_msgs"].discard(message_id)
state["mentions"].discard(message_id)
state["alerts"].discard(message_id)


def format_unread_message_details(
Expand All @@ -1464,6 +1476,8 @@ def format_unread_message_details(
)
if message_id in raw_unread_data["mentions"]:
message_details["mentioned"] = True
if message_id in raw_unread_data["alerts"]:
message_details["has_alert_words"] = True
unread_data[str(message_id)] = message_details

for message_id, stream_message_details in raw_unread_data["stream_dict"].items():
Expand All @@ -1478,6 +1492,8 @@ def format_unread_message_details(
)
if message_id in raw_unread_data["mentions"]:
message_details["mentioned"] = True
if message_id in raw_unread_data["alerts"]:
message_details["has_alert_words"] = True
unread_data[str(message_id)] = message_details

for message_id, huddle_message_details in raw_unread_data["huddle_dict"].items():
Expand All @@ -1494,6 +1510,8 @@ def format_unread_message_details(
)
if message_id in raw_unread_data["mentions"]:
message_details["mentioned"] = True
if message_id in raw_unread_data["alerts"]:
message_details["has_alert_words"] = True
unread_data[str(message_id)] = message_details

return unread_data
Expand All @@ -1508,6 +1526,9 @@ def add_message_to_unread_msgs(
if message_details.get("mentioned"):
state["mentions"].add(message_id)

if message_details.get("has_alert_words"):
state["alerts"].add(message_id)

if message_details["type"] == "private":
user_ids: List[int] = message_details["user_ids"]
user_ids = [user_id for user_id in user_ids if user_id != my_user_id]
Expand Down
18 changes: 18 additions & 0 deletions zerver/openapi/zulip.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3041,6 +3041,15 @@ paths:
of the user.

Present only if the message mentions the current user.
has_alert_words:
type: boolean
description: |
A flag which indicates whether the message contains an alert
word set by the user.

Present only if the message contains the alert word.

**Changes**: New in Zulip 9.0 (feature level 240).
user_ids:
type: array
items:
Expand Down Expand Up @@ -12723,6 +12732,15 @@ paths:
topic features were not handled correctly in calculating this field.
items:
type: integer
alerts:
type: array
description: |
Array containing the IDs of all unread messages which contain the alert
words set by the user.

**Changes**: New in Zulip 9.0 (feature level 240).
items:
type: integer
old_unreads_missing:
type: boolean
description: |
Expand Down
Loading

0 comments on commit 696c636

Please sign in to comment.