-
-
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
Make PmMessage
and StreamMessage
types.
#4506
Make PmMessage
and StreamMessage
types.
#4506
Conversation
38531d4
to
5d78aa8
Compare
5d78aa8
to
ccd8086
Compare
oldMessage && { | ||
...oldMessage, | ||
...(oldMessage: M), |
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.
Similarly, this cast seems not to be needed.
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.
For this one (and likely the others in this file), I find that it's not needed in the commit that introduces it, but it is needed as of the commit that splits type
between PmMessage
and StreamMessage
.
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.
Huh indeed, I reproduce.
Seems like a Flow bug. But the workaround is easy, so 🤷
@@ -31,11 +31,15 @@ export type ServerReaction = $ReadOnly<{| | |||
|}>, | |||
|}>; | |||
|
|||
export type ServerMessage = $ReadOnly<{| |
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.
Let's keep the $ReadOnly
here.
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.
On ServerMessageOf
, right? Or on ServerMessage
?
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.
Yep, ServerMessageOf
(as you've done in the revision.) I.e. where the object-type syntax is. Then ServerMessage
gets the benefit too, as it just uses ServerMessageOf
.
src/unread/unreadStreamsReducer.js
Outdated
const eventNewMessage = (state, action, globalState) => { | ||
if (action.message.type !== 'stream') { | ||
if (getOwnUserId(globalState) === action.message.sender_id) { | ||
return state; | ||
} | ||
|
||
if (getOwnUserId(globalState) === action.message.sender_id) { | ||
if (action.message.type !== 'stream') { | ||
return state; |
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.
- When we start putting things together to call
`addItemsToStreamArray`, we need to be sure `action.message` is a
stream message. We were already sure of that; there's already an
early return on `action.message.type !== stream`. But Flow seems
to think it's done too early, and that something in the
intervening code might thwart the job it's doing. So, move that
early return a bit later.
Ah, yep. Specifically what's happening is: we check action.message.type
, but then we call getOwnUserId
-- and that's some function that might go and do anything. So in particular it might go and mutate the same object that we're looking at as action.message
. So that object had had a type
of 'private'
, but now it might not and so it might not have a stream_id
.
*/ | ||
display_recipient: string | $ReadOnlyArray<PmRecipientUser>, // `string` for type stream, else PmRecipientUser[] |
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 ! Small comments above, but generally this looks great. I see also that the last commit is a draft, and there's one more property |
Thanks for the review! Yeah, I'd actually appreciate it if you could try splitting |
…sage`. In fact, just leave it out of `PmMessage` because it's absent on PMs, as the comment says. Flow gives us one substantive bit of feedback and one kind of false alarm: - We shouldn't be putting a `stream_id` on PMs in exampleData; so, stop doing that. - When we start putting things together to call `addItemsToStreamArray`, we need to be sure `action.message` is a stream message. We were already sure of that; there's already an early return on `action.message.type !== stream`. But that early return needs to be just a bit later in order to satisfy Flow that `action.message` can't be a stream message. In particular, as Greg explains [1], """ Specifically what's happening is: we check `action.message.type`, but then we call `getOwnUserId` -- and that's some function that might go and do anything. So in particular it might go and mutate the same object that we're looking at as `action.message`. So that object *had* had a `type` of `'private'`, but now it might not and so it might not have a `stream_id`. """ [1] zulip#4506 (comment) In particular, Flow sees the `getOwnUserId` call, and that's some function that might go and do anything -- including But Flow seems to think it's done too early, and that something in the intervening code might thwart the job it's doing. So, move that early return a bit later.
ccd8086
to
2fb36c7
Compare
I've just pushed a new revision, and responded to your comments above; please see some queries at #4506 (comment) and #4506 (comment). 🙂 I haven't yet found the right thing to do for |
Flow gives us one substantive bit of feedback and one kind of false alarm: - We shouldn't be putting a `stream_id` on PMs in exampleData; so, stop doing that. - When we start putting things together to call `addItemsToStreamArray`, we need to be sure `action.message` is a stream message. We were already sure of that; there's already an early return on `action.message.type !== stream`. But that early return needs to be just a bit later in order to satisfy Flow that `action.message` can't be a stream message. In particular, as Greg explains [1], """ Specifically what's happening is: we check `action.message.type`, but then we call `getOwnUserId` -- and that's some function that might go and do anything. So in particular it might go and mutate the same object that we're looking at as `action.message`. So that object *had* had a `type` of `'private'`, but now it might not and so it might not have a `stream_id`. """ [1] zulip#4506 (comment) In particular, Flow sees the `getOwnUserId` call, and that's some function that might go and do anything -- including But Flow seems to think it's done too early, and that something in the intervening code might thwart the job it's doing. So, move that early return a bit later.
2fb36c7
to
a5aeb4c
Compare
OK, and I have indeed found a working approach at that site for |
Flow gives us one substantive bit of feedback and one kind of false alarm: - We shouldn't be putting a `stream_id` on PMs in exampleData; so, stop doing that. - When we start putting things together to call `addItemsToStreamArray`, we need to be sure `action.message` is a stream message. We were already sure of that; there's already an early return on `action.message.type !== stream`. But that early return needs to be just a bit later in order to satisfy Flow that `action.message` can't be a stream message. In particular, as Greg explains [1], """ Specifically what's happening is: we check `action.message.type`, but then we call `getOwnUserId` -- and that's some function that might go and do anything. So in particular it might go and mutate the same object that we're looking at as `action.message`. So that object *had* had a `type` of `'private'`, but now it might not and so it might not have a `stream_id`. """ [1] zulip#4506 (comment) In particular, Flow sees the `getOwnUserId` call, and that's some function that might go and do anything -- including But Flow seems to think it's done too early, and that something in the intervening code might thwart the job it's doing. So, move that early return a bit later.
a5aeb4c
to
1a98482
Compare
PmMessage
and StreamMessage
.PmMessage
and StreamMessage
types.
Just rebased this past #4518. |
And just grab the values we want straight from the `message` object itself.
The `avatar_url` and `reactions` fields will overwrite the original values even if we don't use `restMessage`. And it'll make something easier with Flow later in this series.
Above the line that introduces "properties that behave differently for stream vs. private messages". As the comment says, we don't use this property. But if we did, I'm not sure we'd want to describe it as behaving differently for stream messages and PMs. Here's the current doc for it [1]: """ `recipient_id`: integer A unique ID for the set of users receiving the message (either a stream or group of users). Useful primarily for hashing. """ While it's not super clear, it seems like any difference in its behavior between stream and PM messages looks like an implementation detail in the service of being "useful primarily for hashing". If we find that we need to use this, and we also run into problems like unwanted collisions in this field between PM and stream messages...then I suppose we could go back to treating this as different between PM and stream messages. But I imagine the way we'd do that is to put `recipient_id: number` on both `PmMessage` and `StreamMessages` (these types will come later in this series). Which after all wouldn't be helpful for type-checking. [1] https://zulip.com/api/get-messages
This is both unused and (according to the comment) unuseful.
1a98482
to
e9593f0
Compare
Just rebased again and fixed some conflicts, including some from #4448. |
Soon, `Message` will be a union of the two. For now, until we make a few changes to avoid `exponential-spread` errors, just make it `MessageBase`.
`PmMessage` isn't yet distinct from `Message`, but it will be soon.
Like we did in the previous commit for PM-making functions.
…rray. There's no reason we'd want to mutate this array. Given that, and the fact that we're about to make `ServerMessage` a union of `ServerMessageOf<PmMessage>` and `ServerMessageOf<StreamMessage>`, we might as well be courteous to `migrateMessages`' callers by allowing them to pass an array of just one of those types. In other words, we might as well allow this argument, which is an array, to be treated covariantly in the type of its elements. This is only possible when we the array is read-only. See discussion at zulip#4222 (comment), and in particular the example in "What about other types" at the bottom of this article: https://www.stephanboyer.com/post/132/what-are-covariance-and-contravariance.
…ssage`. It shouldn't be unsound if we leave these functions unannotated, or annotate them as `(Message) => Message`. But Flow is wary of having to dive deep into lots of branching conditionals, so we guide it toward a single path of logical reasoning that it's happy to take. As Greg explains [1], """ * The truth is that if the input message is a `PmMessage`, the output message will be a `PmMessage`, and similarly for `StreamMessage`. As a result, so long as the input is one of those, the output will be one of those. * But Flow doesn't want to go diving into a bunch of different cases by splitting up the union. Really it'd be fine in this situation, I think -- there's just the one, two-way, union -- but in general if it had a strategy that did that, it could end up recursively searching through lots of different hypothetical possibilities when trying to type-check something. So it doesn't. * So what we need to do is provide it some kind of guidance to find a single path of logical reasoning, without forking, that leads to the proof that this code is well-typed. """ [1] https://chat.zulip.org/#narrow/stream/243-mobile-team/topic/.60PmMessage.60.2C.20.60StreamMessage.60.20types.3F/near/1126672
In the same basic way as in the previous commit, but with a bit of added complexity: the function isn't `(Message) => Message`; it's `(ServerMessage) => Message`. So, start by expressing how `ServerMessage` relates to `Message`, in a way that applies uniformly to `PmMessage` and `StreamMessage`. Then, annotate the function's param by applying that expression to `M`, which is a subtype of `Message`, and leave the return value as `M`. See discussion at https://chat.zulip.org/#narrow/stream/243-mobile-team/topic/.60PmMessage.60.2C.20.60StreamMessage.60.20types.3F/near/1126688.
This is the first thing that makes `PmMessage` and `StreamMessage` differ. The fact that we don't see Flow errors at this commit is a reasonable sign that we're ready to proceed with giving this treatment to the other pm- and stream-message-specific fields. Flow will likely give us substantive feedback when we do that, so we'll take it one field at a time.
Flow gives us one substantive bit of feedback: we shouldn't be putting a `stream_id` on PMs in exampleData. So, stop doing that.
…ssage. The "Notes from studying the server code" section would likely be helpful in the `StreamMessage` case, if we hadn't decided to deprecate the field on stream messages in favor of using `stream_id`. Since we have, no need to copy that over there.
In particular, not only do some fields, like `display_recipient`, have different types on `PmMessage` and `StreamMessage`, but also, some fields just plain won't exist on one of them, while they will exist on the other. Like `subject_links`. To handle a field like `subject_links`, we can't just make a simple tweak like ``` subject_links: oldMessage.type === 'stream' ? action.subject_links || oldMessage.subject_links : undefined, ``` because an object having `subject_links: undefined` isn't the same as an object not having a `subject_links` at all. `PmMessage` isn't supposed to have `subject_links` at all. A solution like ``` ...(oldMessage.type === 'stream' ? { subject_links: action.subject_links || oldMessage.subject_links } : undefined), ``` might be technically correct, but Flow has a hard time following it, and it panics with an unrelated error about `display_recipient`, and I don't really blame it. :) A solution like this seems to work, while still letting us avoid repeating ourselves by spelling out all the common fields twice.
The last field we need to do this for! :)
Thanks @chrisbobbe ! All looks great -- merging, with a fix to just a nit I spotted in the jsdoc, which stood out with the help of Git's "color moved" feature: Probably a small conflict-resolution error on rebasing past #4518. |
e9593f0
to
ce55fbf
Compare
Great, thanks for the review! 🎉 |
Like we did with `PmMessage` and `StreamMessage` in zulip#4506. We don't actually make `PmOutbox` any different from `StreamOutbox` in this commit; that'll come later.
Like we did with `PmMessage` and `StreamMessage` in zulip#4506. We don't actually make `PmOutbox` any different from `StreamOutbox` in this commit; that'll come later.
Like we did with `PmMessage` and `StreamMessage` in zulip#4506. We don't actually make `PmOutbox` any different from `StreamOutbox` in this commit; that'll come later.
Like we did with `PmMessage` and `StreamMessage` in zulip#4506. We don't actually make `PmOutbox` any different from `StreamOutbox` in this commit; that'll come later.
Like we did with `PmMessage` and `StreamMessage` in zulip#4506. We don't actually make `PmOutbox` any different from `StreamOutbox` in this commit; that'll come later.
(Removed some outdated text here as our thinking proceeded.)
There's a site in the last commit where the handling for
subject
isn't great, and things get worse at that same site when I try to dosubject_links
; it seems to panic a bit (complaining about things other thansubject_links
; it's like it's forgotten some things it would otherwise know) unless I remove the code that givessubject_links
a new value. So I'll pause on this for today and take a fresh look tomorrow (and you could give it a go if you like 🙂).But what do you think of this general approach, @gnprice? I think it's similar to what you proposed in CZO but it doesn't use
MessageBase
; in fact, that can remain unexported, which I kind of like (since it doesn't represent a whole object we'd actually pass around anywhere).You might find it helpful to start by looking at these two consecutive commits:
5c008bc messagesReducer types: Handle
Message
beingPmMessage
|StreamMessage
.68c6742 getMessages types: Handle
Message
beingPmMessage
|StreamMessage
.