Skip to content

Show user status in UI #1702

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open

Show user status in UI #1702

wants to merge 7 commits into from

Conversation

sm-sayedi
Copy link
Collaborator

The remaining half of #1629. For related images, please see #1629 description.

Fixes: #197

This was referenced Jul 10, 2025
@gnprice gnprice changed the title Track user status - Part 2 Show user status in UI Jul 10, 2025
@gnprice gnprice mentioned this pull request Jul 10, 2025
@gnprice gnprice added the integration review Added by maintainers when PR may be ready for integration label Jul 10, 2025
@gnprice
Copy link
Member

gnprice commented Jul 10, 2025

Copying the review status from #1629 (per #1629 (review)): I've read the first commit except for its tests:
463b938 msglist: Show user status emoji

and the comments I had there have been resolved. Remaining to review are those tests, and the other 4 commits:
14093b2 recent-dms: Show user status emoji in recent DMs page
646f9d9 new-dm: Show user status emoji
846261a autocomplete: Show user status emoji in user-mention autocomplete
e9f682e profile: Show user status

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 again for building this! Comments below.

I've read through the whole of the first commit:
463b938 msglist: Show user status emoji

and the non-test changes in the other commits:
14093b2 recent-dms: Show user status emoji in recent DMs page
646f9d9 new-dm: Show user status emoji
846261a autocomplete: Show user status emoji in user-mention autocomplete
e9f682e profile: Show user status

For this round, I skipped the tests in the later commits because I think a number of my comments on the first commit's tests will apply to those too. So please go ahead and revise the later tests too where applicable.

Comment on lines 55 to 63
/// Finder for [UserStatusEmoji] widget.
///
/// Use [type] to specify the exact emoji child widget. It can be either
/// [UnicodeEmojiWidget] or [ImageEmojiWidget].
Finder findStatusEmoji(Type type) {
assert(type == UnicodeEmojiWidget || type == ImageEmojiWidget);
return find.ancestor(
of: find.byType(type),
matching: find.byType(UserStatusEmoji));
Copy link
Member

Choose a reason for hiding this comment

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

This seems like it's clearer if just inlined at its call sites.

The implementation (minus the assert, which becomes unnecessary) isn't much longer than the call; and it's more transparent about what it's doing. I also don't feel like this is particularly encapsulating any knowledge of the details of how the UserStatusEmoji widget works.

Copy link
Member

Choose a reason for hiding this comment

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

Also, after doing that: how about just find.byType(UserStatusEmoji)? It's not clear to me what's gained by adding the condition that the widget have a UnicodeEmojiWidget descendant, or an ImageEmojiWidget descendant.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Also, after doing that: how about just find.byType(UserStatusEmoji)?

Copying #1629 (comment) here: 🙂

As we discussed in this week’s check-in call, UserStatusEmoji for many cases can contain only SizedBox.shrink(), including when there’s no status set for a user. So only using find.byType(UserStatusEmoji) will pass the tests even when we don’t pass status for a user.

Comment on lines 66 to 68
void checkUserStatusEmoji(Finder emojiFinder, {required bool isAnimated}) {
check((emojiFinder
.evaluate().first.widget as UserStatusEmoji).neverAnimate).equals(!isAnimated);
Copy link
Member

Choose a reason for hiding this comment

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

This also feels like it'd be clearer inlined. The actual check is all about neverAnimate, but the name doesn't sound like that — it sounds like it's going to be doing some other, more general, check.

Comment on lines 67 to 68
check((emojiFinder
.evaluate().first.widget as UserStatusEmoji).neverAnimate).equals(!isAnimated);
Copy link
Member

Choose a reason for hiding this comment

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

Instead of finder.evaluate(), use tester explicitly:

Suggested change
check((emojiFinder
.evaluate().first.widget as UserStatusEmoji).neverAnimate).equals(!isAnimated);
check(tester.firstWidget<UserStatusEmoji>(emojiFinder).neverAnimate)
.equals(!isAnimated);

That way it's conceptually clearer what's going on. (The implementation of finder.evaluate will end up using the same WidgetTester instance anyway, getting hold of it behind the scenes via a singleton.)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The main motivation for not using tester was to minimize the number of params passed to the helper methods (checkStatusEmoji and others in the next commits). Now that it helps in making the tests clearer, so going to use that in the new revision.🙂

@@ -91,6 +110,7 @@ void main() {
if (mutedUserIds != null) {
await store.setMutedUsers(mutedUserIds);
}
await store.changeUserStatuses(userStatuses ?? []);
Copy link
Member

Choose a reason for hiding this comment

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

How about having the individual test cases do this? That way we avoid adding yet another feature to this shared helper. (It probably should have fewer features already ­— that'd help make fewer things for the reader of each test case to potentially have to think about.)

Comment on lines 1805 to 1809
userStatuses: [
(
user1.userId,
UserStatusChange(
text: OptionSome('Busy'),
Copy link
Member

Choose a reason for hiding this comment

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

Hmm, these tuples are a bit of a pain to read.

How about making changeUserStatuses instead take a Map? That seems semantically appropriate, as there's no reason to have the same user ID more than once in the same call. Then:

Suggested change
userStatuses: [
(
user1.userId,
UserStatusChange(
text: OptionSome('Busy'),
userStatuses: {
user1.userId: UserStatusChange(
text: OptionSome('Busy'),

Flexible(child: labelWidget),
if (option case UserMentionAutocompleteResult(:var userId))
UserStatusEmoji(userId: userId, size: 18,
padding: const EdgeInsetsDirectional.only(start: 5.0))]),
Copy link
Member

Choose a reason for hiding this comment

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

nit: similarly:

Suggested change
padding: const EdgeInsetsDirectional.only(start: 5.0))]),
padding: const EdgeInsetsDirectional.only(start: 5.0)),
]),

Comment on lines 316 to 323
children: [
labelWidget,
Row(children: [
Flexible(child: labelWidget),
if (option case UserMentionAutocompleteResult(:var userId))
UserStatusEmoji(userId: userId, size: 18,
padding: const EdgeInsetsDirectional.only(start: 5.0))]),
if (sublabelWidget != null) sublabelWidget,
])),
Copy link
Member

Choose a reason for hiding this comment

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

I think this new logic is best included as part of labelWidget. Conceptually it's closely tied to the user's name, which is part of the label.

Probably in fact the cleanest home for this is in the switch (option) above, which is already the place where we inspect the details of option.

@@ -73,17 +75,28 @@ class ProfilePage extends StatelessWidget {
),
// TODO write a test where the user is muted; check this and avatar
TextSpan(text: store.userDisplayName(userId, replaceIfMuted: false)),
UserStatusEmoji.asWidgetSpan(
userId: userId,
fontSize: 20,
Copy link
Member

Choose a reason for hiding this comment

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

nit: this says fontSize: 20 but the presence circle above says fontSize: nameStyle.fontSize!; should say the same thing both places, since the intent is for them to be the same

@@ -47,6 +48,7 @@ class ProfilePage extends StatelessWidget {
if (user == null) {
return const _ProfileErrorPage();
}
final userStatus = store.getUserStatus(userId);
Copy link
Member

Choose a reason for hiding this comment

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

nit: put this next to displayEmail; conceptually they're here for the very same reason

Comment on lines 91 to 92
color: DesignVariables.of(context).userStatusText)),

if (displayEmail != null)
Copy link
Member

Choose a reason for hiding this comment

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

Is this blank line intended?

The items just above here feel to me closely related to the items just below, and in particular just as closely related as other items that are grouped next to each other.

@sm-sayedi sm-sayedi force-pushed the 197-P2 branch 2 times, most recently from eccaa7a to 686130f Compare July 17, 2025 18:22
@sm-sayedi
Copy link
Collaborator Author

Thanks @gnprice for the detailed review. Revision pushed, PTAL.

Note: CI is failing with the following issue:

Run flutter pub get
Resolving dependencies...
The current Flutter SDK version is 0.0.0-unknown.

Because zulip requires Flutter SDK version >=3.33.0-1.0.pre.832, version solving failed.


You can try the following suggestion to make the pubspec resolve:
* Try using the Flutter SDK version: 3.35.0-0.0.pre. 
Failed to update packages.
Error: Process completed with exit code 1.

I tried pushing several times, but it didn't solve the problem.

@sm-sayedi sm-sayedi requested a review from gnprice July 17, 2025 18:26
@gnprice
Copy link
Member

gnprice commented Jul 17, 2025

Yeah, that CI failure is unrelated. Filed as #1710, and I'll fix it.

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
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Track user status
2 participants