-
-
Notifications
You must be signed in to change notification settings - Fork 655
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
Start type checking MessageList
's props.
#4465
Conversation
Yeah, I think we'll want to keep that work memoized. There's likely a related perf regression at Oh, I guess two of those get removed a couple of commits later. 🙂 That mitigates it. Still, should be memoized so that we only compute it once each time the inputs change. One thing I notice from this refactoring is that in the old code, we aren't getting memoization in the search case! Each time the |
Makes sense. Looking at how best to memoize the work done in Does that sound right? Can we, e.g., use |
5a31e7e
to
739c0db
Compare
Thanks, @gnprice! New revision pushed. |
739c0db
to
3013d77
Compare
@gnprice, I've just rebased this past #4518. Thanks for reviewing #4518 so quickly! Getting
I can be more confident in some parts of that draft after a PR like the current one is merged. 🙂 |
An instance of zulip#3452. PR zulip#4465 is currently open for type-checking `MessageList`'s props.
3013d77
to
31feae8
Compare
Just fixed some rebase conflicts. 🙂 |
31feae8
to
d939fd0
Compare
Just resolved some rebase conflicts. 🙂 |
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 @chrisbobbe ! This looks great -- sorry for the delayed review. Comments below.
The part about OuterProps
is a potential followup, plus some backstory FYI; any refactoring around startEditMessage
(i.e., beyond tweaking the TODO comment) is also a potential followup. Everything else should be pretty straightforward to take care of.
src/search/SearchMessagesCard.js
Outdated
// TODO: handle editing a message from the search results, | ||
// or don't let callers pass this. | ||
startEditMessage={() => undefined} |
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.
What do you have in mind for "don't let callers pass this"? The two natural options I see here are
- handle editing a message from the search results, or
- make this optional.
Probably we don't want to try to handle editing from here, so that'd point to the latter. We already have this where we construct the action sheet:
if (
message.sender_id === ownUser.user_id
// Our "edit message" UI only works in certain kinds of narrows.
&& (isStreamOrTopicNarrow(narrow) || isPmNarrow(narrow))
) {
buttons.push(editMessage);
}
Perhaps that filter on the narrow could move up to ChatScreen
, and control whether we pass a non-void startEditMessage
callback at all? (And here in search, just never pass it.) Then in the action sheet we could just check if the callback is truthy.
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.
don't let callers pass this
I think I meant to say "don't require callers to pass this".
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 that filter on the narrow could move up to
ChatScreen
, and control whether we pass a non-voidstartEditMessage
callback at all? (And here in search, just never pass it.) Then in the action sheet we could just check if the callback is truthy.
Yeah; just this week, it's come up that it'd be nice to do this conditional in just one place: #4799 (comment).
To check if the callback is truthy, in the action sheet, are we OK with constructMessageActionButtons
taking startEditMessage
as another param? Currently it looks like this:
export const constructMessageActionButtons = ({
backgroundData: { ownUser, flags },
message,
narrow,
}: {
backgroundData: $ReadOnly<{
ownUser: User,
flags: FlagsState,
...
}>,
message: Message,
narrow: Narrow,
}): Button<MessageArgs>[] => {
// ...
}
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, it is a little awkward to pass it down there (through constructNonHeaderActionButtons
, too. But I think it's OK.
react-redux was injecting a `typingUsers` prop that shadowed this one, which can cause confusion. Rather than renaming the outer prop, just remove it. This gives the same behavior for current callers: `getCurrentTypingUsers` appropriately contains a sanity check that will return an empty array when the narrow can't have meaningful typing state, i.e., when it's not a "conversation" narrow (a PM narrow or topic narrow) [1]. Search narrows can't have meaningful typing state, and `MessageList`'s only callsite that passed this prop was for search narrows. [1] In fact, it doesn't include topic narrows; we may want to change that at some point.
react-redux was injecting a `fetching` prop that shadowed this one, which can cause confusion. Rather than renaming the outer prop, just remove it. This gives the same behavior for current callers: the one caller that passed this prop is for search narrows, and we don't store fetching state in Redux for search narrows. So we'd go look up the search narrow in the fetching state, not find it, and default to a not-fetching value. This seems better: if, in the future, we do want to start fetching when you scroll to the top or bottom of a search narrow, we'd want that to get indicated, just the same way as we do for other narrows.
…List. Now, both callers of `MessageList` pass these props, so we're set up to have `MessageList` require them. Put the getting of `messages` and `firstUnreadIdInNArrow` in our custom Hook, to help distill the lines of `ChatScreen` down to things that are about UI choices, like the getting of `sayNoMessages`, `showComposeBox`, and the JSX [1]. [1] https://chat.zulip.org/#narrow/stream/243-mobile-team/topic/React.20Navigation.20v5.3A.20.60ChatScreen.60.20and.20Hooks/near/1064349
This change in `MessageList`'s interface might be seen as stretching a bit far to accommodate the search screen's peculiar choices. This is partly fair, but also, the choice to keep reusing a widget in two important places means we should concede, to some extent, that one use isn't peculiar just because it's different from the other.
Like `getHtmlPieceDescriptorsForShownMessages`, but it takes an array of messages instead of picking one from the state. ...Which means, the state is no longer needed as an input. So, this one doesn't really end up being a selector. We do want memoization, though -- so, use `defaultMemoize`. That's the memoize function used by `createSelector` [1]. [1] https://www.npmjs.com/package/reselect#defaultmemoizefunc-equalitycheck--defaultequalitycheck.
The caller that doesn't pass the `htmlPieceDescriptorsForShownMessages` prop uses the same calculation to get the list of messages as `getHtmlPieceDescriptorsForShownMessages` was using. Namely it uses the result of `getShownMessagesForNarrow(state, narrow)`. Also, memoization is done the same way as it was being done with `createSelector` [1]. [1] https://www.npmjs.com/package/reselect#defaultmemoizefunc-equalitycheck--defaultequalitycheck
…ages. This is NFC because the falsy branch of the conditional on this prop in `MessageList`'s `connect`'s `mapStateToProps` computes the same thing we had been passing.
…ages. We stopped passing this at the only callsite that had been passing it, in the previous commit.
…belongs. Now, `OuterProps` matches the subset of `Props` that callers are meant to pass. If `OuterProps` must exist, this is what it should look like.
`connectActionSheet`'s poor typing is one thing stopping `MessageList`'s props from being type checked. Like we did for react-native-safe-area-context in 8f56964, use a type-wrapper instead of a libdef so that we can use our handy `BoundedDiff` type.
This will get us part of the way toward type-checking `MessageList`'s interface. It doesn't get us all of the way there; that'll be done in the next commit.
This is the problem we've run into a few times [1] [2] where adding `() => <C />;` to the file magically causes C's callers even *outside* the file to start getting their props type-checked, where they weren't before. Instead of doing that, annotate the export with `ComponentType<OuterProps>`, which Greg noticed also worked [3], since we happen to have `OuterProps` lying around anyway. [1] fcde700 [2] https://chat.zulip.org/#narrow/stream/243-mobile-team/topic/Flow.20and.20.60connect.60/near/1098203 [3] https://chat.zulip.org/#narrow/stream/243-mobile-team/topic/Flow.20and.20.60connect.60/near/1098230
… code. It turns out that it's handy to have `OuterProps`, for the work done in the previous commit. But at least we can remove some repetition by doing this. See also discussion at zulip@3bdb5fb#r652236395.
d939fd0
to
21fd83a
Compare
Thanks for the review! Revision pushed. |
Thanks, looks good! Merged. There's one open thread about a possible followup: #4465 (comment) |
Thanks for the review! |
By adding a type-wrapper for
connectActionSheet
, and annotating the export of MessageList.js withComponentType<OuterProps>
, as @gnprice mentioned in a recent discussion of Flow andconnect
.I've removed the kind of hacky cases where we use the same name for an "outer" prop as we use for a prop that
connect
provides. Let me know if you see a better way to do this, and in particular, if you don't like the changes toMessageList
's interface. 🙂Also, in this one—
MessageList [nfc]: Remove htmlPieceDescriptorsForShownMessages prop (3/x).
—there's a potential performance regression, where we stop using a caching selector. After I'd removed that, it occurred to me that some of the work it caches (calling
getHtmlPieceDescriptors
on each shown message) is enough that we'll want to keep the caching and find something else to do about thehtmlPieceDescriptorsForShownMessages
prop. What do you think?