Repository: zulip/zulip
Pull request: unread: Indicate which streams and topics have unread @-mentions.
<!-- Describe your pull request here.-->

Fixes: #21637 

<!-- If the PR makes UI changes, always include one or more still screenshots to demonstrate your changes. If it seems helpful, add a screen capture of the new functionality as well.

Tooling tips: https://zulip.readthedocs.io/en/latest/tutorials/screenshot-and-gif-software.html
-->

**Screenshots and screen captures:**

**Self-review checklist**

<!-- Prior to submitting a PR, follow our step-by-step guide to review your own code:
https://zulip.readthedocs.io/en/latest/contributing/code-reviewing.html#how-to-review-code -->

<!-- Once you create the PR, check off all the steps below that you have completed.
If any of these steps are not relevant or you have not completed, leave them unchecked.-->

- [x] [Self-reviewed](https://zulip.readthedocs.io/en/latest/contributing/code-reviewing.html#how-to-review-code) the changes for clarity and maintainability
      (variable names, code reuse, readability, etc.).

Communicate decisions, questions, and potential concerns.

- [x] Explains differences from previous plans (e.g., issue description).
- [x] Highlights technical choices and bugs encountered.
- [x] Calls out remaining decisions and concerns.
- [x] Automated tests verify logic where appropriate.

Individual commits are ready for review (see [commit discipline](https://zulip.readthedocs.io/en/latest/contributing/version-control.html)).

- [x] Each commit is a coherent idea.
- [x] Commit message(s) explain reasoning and motivation for changes.

Completed manual review and testing of the following:

- [x] Visual appearance of the changes.
- [x] Responsiveness and internationalization.
- [x] Strings and tooltips.
- [x] End-to-end functionality of buttons, interactions and flows.
- [x] Corner cases, error conditions, and easily imagined bugs.


Problem statement:
Indicate which streams and topics have unread @-mentions
When looking at streams and topics in the left sidebar, it would be helpful to know which ones have unread messages where the user has been @-mentioned. To address this, we should display an `@` to the left of the unread message count when there is at least one unread message with an @-mention in the stream or topic.

Additional notes:
* The same messages as we show in the @-mention tab should count. I.e. group mentions are included; silent mentions are excluded.
* The @ should be displayed both in the regular left sidebar view and in the "more topics" view.
* As a follow-up, we should plan to figure out a UI for showing this in Recent topics as well.

Hints from issue discussion:
Hello @zulip/server-sidebars members, this issue was labeled with the "area: left-sidebar" label, so you may want to check it out!

<!-- areaLabelAddition -->

@zulipbot claim
Hello @AnushaNathRoy, you have been unassigned from this issue because you have not updated this issue or any referenced pull requests for over 14 days.

You can reclaim this issue or claim any other issue by commenting `@zulipbot claim` on that issue.

Thanks for your contributions, and hope to see you again soon!

Bumping up the priority, as this feature has been very nice on mobile!
@zulipbot  claim
@jai2201 You have been unassigned from this issue because you have not made any updates for over 14 days. Please feel free to reclaim the issue if you decide to pick up again. Thanks!

@zulipbot  claim

Commit message: bucketer: Store bucket_keys in reverse_lookup maps.

This makes it possible to fetch the bucket_key, not just the bucket
itself, given an item_id.

Patch to review:
diff --git a/static/js/unread.js b/static/js/unread.js
index 8f58cbe13acd4..fbac66d479b46 100644
--- a/static/js/unread.js
+++ b/static/js/unread.js
@@ -31,6 +31,7 @@ export const unread_mentions_counter = new Set();
 const unread_messages = new Set();
 
 class Bucketer {
+    // Maps item_id => bucket_key for items present in a bucket.
     reverse_lookup = new Map();
 
     constructor(options) {
@@ -58,12 +59,13 @@ class Bucketer {
         } else {
             bucket.add(item_id);
         }
-        this.reverse_lookup.set(item_id, bucket);
+        this.reverse_lookup.set(item_id, bucket_key);
     }
 
     delete(item_id) {
-        const bucket = this.reverse_lookup.get(item_id);
-        if (bucket) {
+        const bucket_key = this.reverse_lookup.get(item_id);
+        if (bucket_key) {
+            const bucket = this.get_bucket(bucket_key);
             bucket.delete(item_id);
             this.reverse_lookup.delete(item_id);
         }


Code review comments:
diff_hunk: @@ -563,6 +563,11 @@ div.overlay {
     color: hsl(0, 0%, 100%);
 }
 
+.unread_mention_info:not(:empty) {
path: static/styles/app_components.css
text: @user1:
I think a 2px `margin-left` here would fix the overflow issue; test-deploying that but you might want to play with it.