Skip to content

Conversation

@chrisbobbe
Copy link
Collaborator

@chrisbobbe chrisbobbe commented Nov 20, 2025

Stacked atop #1998.

"Subject to user setting" refers to the "toggle starred messages counter" setting: https://zulip.com/help/star-a-message#toggle-starred-messages-counter

Fixes-partly: #1088


Before this (but after #1998) After
image image
image image

@chrisbobbe chrisbobbe added the maintainer review PR ready for review by Zulip maintainers label Nov 20, 2025
@chrisbobbe
Copy link
Collaborator Author

cc @alya

@chrisbobbe chrisbobbe added the product review Added by maintainers when a PR needs product review label Nov 20, 2025
@alya
Copy link
Collaborator

alya commented Nov 20, 2025

The screenshots look good to me! We should remember to note that the setting affects the mobile app in https://zulip.com/help/star-a-message#toggle-starred-messages-counter.

@chrisbobbe chrisbobbe force-pushed the pr-starred-message-count-main-menu branch from 5d32321 to 1e74454 Compare November 20, 2025 22:17
Copy link
Member

@rajveermalviya rajveermalviya left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @chrisbobbe! LGTM and test great, moving over to Greg's review.

@rajveermalviya rajveermalviya added integration review Added by maintainers when PR may be ready for integration and removed maintainer review PR ready for review by Zulip maintainers labels Nov 21, 2025
Copy link
Member

@gnprice gnprice left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Here's comments on the first 7/8 commits, including the new data structure:
a3d7fd8 unreads [nfc]: Factor out countInAllDms, for public use and as a helper
45bfcb0 unreads: Exclude muted DM conversations in combined-feed and all-dm counts
248cab2 home: Show unread counts in main menu
e8f51b2 home: Tweak main-menu buttons to follow Figma
247a3d4 api: Add UserSettings.starredMessageCounts
a22b49f api: Add starredMessages to initial snapshot
34f9a0b message: Add MessageStore.starredMessages

and a brief look at the last commit with the UI changes:
1e74454 home: Show starred-message count in main menu, subject to user setting

Comment on lines 752 to 753
};

if (isAdd && (event as UpdateMessageFlagsAddEvent).all) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: these stanzas don't become any more closely related with this change; in fact they become more separate, because this adds another stanza below which consumes isAdd separately

Comment on lines +27 to +28
/// All starred messages, as message IDs.
Set<int> get starredMessages;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I see.

I'm trying to think if there are other cases where this should get updated. I guess maybe not:

  • We can learn of messages through handleMessageEvent. But maybe those can't ever be starred.
  • We can learn of messages through reconcileMessages. But maybe if those are starred then they should already be in this set.

I think it'd be good to make that reasoning explicit, though, in those methods. Probably including asserts to verify those expectations.

Comment on lines +883 to 886
if (_messages.handleDeleteMessageEvent(event)) {
notifyListeners();
}
unreads.handleDeleteMessageEvent(event);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes me a bit nervous to be calling notifyListeners at a point where the event has been only partially applied — the store is in a somewhat inconsistent state.

Instead let's delay the notifyListeners call to the end.

await prepareMessages([message]);
check(store).starredMessages.single.equals(message.id);
await store.handleEvent(eg.deleteMessageEvent([message]));
checkNotifiedOnce();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is a bit confusing — it checks that the list view-model notified its listeners, but not whether the store did.

I think the key case here would actually be with prepareMessages not knowing about the message. That exercises the situation where the app hasn't gone and fetched a message list that had the starred message, and only knows about it through the list in the initial snapshot.

Comment on lines +1722 to +1724
void checkPerAccountStoreNotified({required int count}) {
check(perAccountStoreNotifiedCount).equals(count);
notifiedCount = 0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
void checkPerAccountStoreNotified({required int count}) {
check(perAccountStoreNotifiedCount).equals(count);
notifiedCount = 0;
void checkPerAccountStoreNotified({required int count}) {
check(perAccountStoreNotifiedCount).equals(count);
perAccountStoreNotifiedCount = 0;

right?

Comment on lines +1736 to +1739
await prepareMessages([message1, message2]);
check(store).starredMessages.isEmpty();
await store.handleEvent(
mkAddEvent(MessageFlag.starred, [message1.id, message2.id]));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
await prepareMessages([message1, message2]);
check(store).starredMessages.isEmpty();
await store.handleEvent(
mkAddEvent(MessageFlag.starred, [message1.id, message2.id]));
await prepareMessages([message2]);
check(store).starredMessages.isEmpty();
await store.handleEvent(
mkAddEvent(MessageFlag.starred, [message1.id, message2.id]));

That way we check that it works both with messages that the message store knows about, and those that it doesn't.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Similarly for removing the flag, below.)

class UnreadCountBadge extends StatelessWidget {
const UnreadCountBadge({
/// See [CounterStyle] and [CounterKind] for the possible variants.
class Counter extends StatelessWidget {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe CounterBadge?

The name "counter" feels awfully generic — it doesn't sound like necessarily even a widget, more like some kind of data structure that keeps count of things.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

integration review Added by maintainers when PR may be ready for integration product review Added by maintainers when a PR needs product review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants