-
Notifications
You must be signed in to change notification settings - Fork 318
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
base: main
Are you sure you want to change the base?
Show user status in UI #1702
Conversation
Copying the review status from #1629 (per #1629 (review)): I've read the first commit except for its tests: and the comments I had there have been resolved. Remaining to review are those tests, and the other 4 commits: |
There was a problem hiding this 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.
test/widgets/message_list_test.dart
Outdated
/// 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)); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 onlySizedBox.shrink()
, including when there’s no status set for a user. So only usingfind.byType(UserStatusEmoji)
will pass the tests even when we don’t pass status for a user.
test/widgets/message_list_test.dart
Outdated
void checkUserStatusEmoji(Finder emojiFinder, {required bool isAnimated}) { | ||
check((emojiFinder | ||
.evaluate().first.widget as UserStatusEmoji).neverAnimate).equals(!isAnimated); |
There was a problem hiding this comment.
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.
test/widgets/message_list_test.dart
Outdated
check((emojiFinder | ||
.evaluate().first.widget as UserStatusEmoji).neverAnimate).equals(!isAnimated); |
There was a problem hiding this comment.
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:
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.)
There was a problem hiding this comment.
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.🙂
test/widgets/message_list_test.dart
Outdated
@@ -91,6 +110,7 @@ void main() { | |||
if (mutedUserIds != null) { | |||
await store.setMutedUsers(mutedUserIds); | |||
} | |||
await store.changeUserStatuses(userStatuses ?? []); |
There was a problem hiding this comment.
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.)
test/widgets/message_list_test.dart
Outdated
userStatuses: [ | ||
( | ||
user1.userId, | ||
UserStatusChange( | ||
text: OptionSome('Busy'), |
There was a problem hiding this comment.
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:
userStatuses: [ | |
( | |
user1.userId, | |
UserStatusChange( | |
text: OptionSome('Busy'), | |
userStatuses: { | |
user1.userId: UserStatusChange( | |
text: OptionSome('Busy'), |
lib/widgets/autocomplete.dart
Outdated
Flexible(child: labelWidget), | ||
if (option case UserMentionAutocompleteResult(:var userId)) | ||
UserStatusEmoji(userId: userId, size: 18, | ||
padding: const EdgeInsetsDirectional.only(start: 5.0))]), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: similarly:
padding: const EdgeInsetsDirectional.only(start: 5.0))]), | |
padding: const EdgeInsetsDirectional.only(start: 5.0)), | |
]), |
lib/widgets/autocomplete.dart
Outdated
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, | ||
])), |
There was a problem hiding this comment.
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
.
lib/widgets/profile.dart
Outdated
@@ -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, |
There was a problem hiding this comment.
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
lib/widgets/profile.dart
Outdated
@@ -47,6 +48,7 @@ class ProfilePage extends StatelessWidget { | |||
if (user == null) { | |||
return const _ProfileErrorPage(); | |||
} | |||
final userStatus = store.getUserStatus(userId); |
There was a problem hiding this comment.
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
lib/widgets/profile.dart
Outdated
color: DesignVariables.of(context).userStatusText)), | ||
|
||
if (displayEmail != null) |
There was a problem hiding this comment.
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.
eccaa7a
to
686130f
Compare
Thanks @gnprice for the detailed review. Revision pushed, PTAL. Note: CI is failing with the following issue:
I tried pushing several times, but it didn't solve the problem. |
Status emojis are only shown for self-1:1 and 1:1 conversation items. They're ignored for group conversations as that's what the Web does.
Yeah, that CI failure is unrelated. Filed as #1710, and I'll fix it. |
The remaining half of #1629. For related images, please see #1629 description.
Fixes: #197