-
Notifications
You must be signed in to change notification settings - Fork 270
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
Add UserStore; pull out getUser, userDisplayName, others #1327
base: main
Are you sure you want to change the base?
Conversation
A series of follow-up changes that are enabled by this, but that I didn't get to before deciding I was done with this for the evening:
That would be a nice further improvement because those two methods are two of the very few remaining bits of specific model logic remaining on |
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 for the PR! Left some comments.
@@ -376,6 +376,10 @@ class PerAccountStore extends ChangeNotifier with EmojiStore, UserStore, Channel | |||
/// This returns null if [reference] fails to parse as a URL. | |||
Uri? tryResolveUrl(String reference) => _tryResolveUrl(realmUrl, reference); | |||
|
|||
/// Always equal to `connection.zulipFeatureLevel` | |||
/// and `account.zulipFeatureLevel`. | |||
int get zulipFeatureLevel => connection.zulipFeatureLevel!; |
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.
Really convenient!
@@ -329,8 +329,7 @@ class PerAccountStore extends ChangeNotifier with EmojiStore, UserStore, Channel | |||
required this.unreads, | |||
required this.recentDmConversationsView, | |||
required this.recentSenders, | |||
}) : assert(selfUserId == globalStore.getAccount(accountId)!.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.
I can see that this assertion was already ineffective — data stores like TypingStatus
, Unreads
, and RecentDmConversationsView
could all take different selfUserId
s without detecting the discrepancy.
In contrast, the three surviving assertions check that realmUrl
is consistent across the data stores. While we can do that for selfUserId
too, I'm not sure if that's useful. A bug can slip through if we add a new realmUrl
without adding a new assertion below, and the same issue applies to selfUserId
assertions if we are to maintain them.
Perhaps a better way would be sharing the reference to realmUrl
and selfUserId
, instead of passing them multiple times, but the way PerAccountStore.fromInitialSnapshot
is set up already makes it easy to check for mistakes, making this less of an issue.
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.
Yeah, this check is basically already internal to the implementation of the PerAccountStore constructors, so I think it's not essential to keep.
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.
Perhaps a better way would be sharing the reference to
realmUrl
andselfUserId
, instead of passing them multiple times,
This is also something I think we'll want to do — what I'm imagining is making a type named perhaps BasePerAccountStore, with those and perhaps connection
and/or account
(which subsumes them), and then these other sub-stores can say on BasePerAccountStore
to get access to those getters.
(Naturally that's for a future PR, though.)
test/model/user_test.dart
Outdated
@@ -21,6 +21,36 @@ void main() { | |||
}); | |||
}); | |||
|
|||
group('senderDisplayName', () { | |||
test('on a known user', () { |
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.
Need async
here. Expressions like store.addUser(…)
need to be await
ed.
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, yeah.
That doesn't have any actual effect on behavior, because handleEvent
doesn't have any await
s in it. But it will in the future, for the reasons described in baea6d7 which made that method async, so in principle these should get awaited.
Looks like there are a small number of other places in the tests that lack such awaits. I'll fix those too while at it.
lib/model/store.dart
Outdated
@@ -438,7 +438,10 @@ class PerAccountStore extends ChangeNotifier with EmojiStore, UserStore, Channel | |||
int get selfUserId => _users.selfUserId; | |||
|
|||
@override | |||
Map<int, User> get users => _users.users; |
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.
There are some outstanding references to store.users
the Map
.
$ git grep 'store\.users'
test/model/autocomplete_test.dart: test('MentionAutocompleteView mutating store.users while in progress does not '
test/widgets/recent_dm_conversations_test.dart: testWidgets('no error when user somehow missing from store.users', (tester) async {
test/widgets/recent_dm_conversations_test.dart: testWidgets('no error when one user somehow missing from store.users', (tester) async {
This is NFC because there isn't currently actually anything to wait for; the implementation of handleEvent is entirely synchronous. But in principle it's async, because in the future it will be, for the reasons described in baea6d7. Thanks to Zixuan for spotting this pattern in some newly-added tests (coming up): zulip#1327 (comment)
Like ChannelStore and others, this helps reduce the amount of complexity that's concentrated centrally in PerAccountStore. This change is all NFC except that if we get a RealmUserUpdateEvent for an unknown user, we'll now call notifyListeners, and so might cause some widgets to rebuild, when previously we wouldn't. That's pretty low-stakes, so I'm not bothering to wire through the data flow to avoid it.
At the end of this series, there won't be a `users` member on the store, so this code-like reference will no longer make sense. The underlying meaning in each of these contexts will still be apt, though: the data structure where the known users are tracked. So update them to make that reference more generically.
This way we don't actually do the lookup unless we need it.
Also add some blank lines to make the structure of this switch a bit easier to see.
This is more convenient than going through store.connection, and should reduce the temptation to go through store.account.
Now that we have a UserStore type, we can give this a bit more abstraction.
This gives somewhat better encapsulation -- other code isn't expected to add or remove items from this map.
4b42269
to
e07a052
Compare
Thanks for the review! Pushed an update (including rebasing atop #1331 so that CI should work); PTAL. |
I've been saying for a while that I plan to make a UserStore type akin to ChannelStore and MessageStore. Today merging #1148 and in particular 76425ed gave me a fresh reminder that such a thing would be useful. And I decided some nice refactoring would be a relaxing task, so I sat down and did it. 🙂
The immediate motivating improvement is the new
userDisplayName
method, which simplifies a lot of spots that were otherwise falling back explicitly to the "(unknown user)" translation.There's also a bunch of new dartdoc, particularly on the subject of unknown users (cf #716), which there effectively wasn't room for when it would have had to live in the middle of the PerAccountStore definition. And at the end of the branch, the actual
Map
of users becomes private — the bulk of the former references to it now usegetUser
, as they're looking up a particular user by ID, while two others stand out (as they should) because they iterate through the newallUsers
, an expensive operation.Along the way, and basically as a side effect of looking at all the code touched in this PR, the branch also includes identifying some spots that need to change for #716 (handling unknown users), plus commenting (in the "autocomplete [nfc]: Make explicit …" commit below) on a couple of spots that look like they might need to change for that issue but in fact don't.
These changes are all NFC with the small exception noted in the first commit.
Selected commit messages
6f82fda user: Split a UserStore out from PerAccountStore
Like ChannelStore and others, this helps reduce the amount of
complexity that's concentrated centrally in PerAccountStore.
This change is all NFC except that if we get a RealmUserUpdateEvent
for an unknown user, we'll now call notifyListeners, and so might
cause some widgets to rebuild, when previously we wouldn't.
That's pretty low-stakes, so I'm not bothering to wire through
the data flow to avoid it.
c253f93 user [nfc]: Document users map, especially its incompleteness
c448083 user [nfc]: Factor out a userDisplayName
06be949 store [nfc]: Add a zulipFeatureLevel getter
This is more convenient than going through store.connection,
and should reduce the temptation to go through store.account.
2063975 user [nfc]: Add a selfUser getter
196966d user [nfc]: Introduce senderDisplayName
7103868 user [nfc]: Note unknown-user crashes where senderDisplayName can help
ff9c6a9 user [nfc]: Note places lacking live-update where senderDisplayName helps
f62379a autocomplete [nfc]: Make explicit why two user lookups have null-assertions
40c06f2 user [nfc]: Factor out a getUser method
3655582 user [nfc]: Factor out an allUsers iterable
4b42269 user [nfc]: Make the actual users Map private
This gives somewhat better encapsulation -- other code isn't expected
to add or remove items from this map.