From 74ae528f0f7ad16fdde7d81eae0a2da85b5f0a42 Mon Sep 17 00:00:00 2001 From: Chris Bobbe Date: Mon, 10 May 2021 14:24:09 -0400 Subject: [PATCH 1/9] migrations [nfc]: Comment on what migration 28 was for. This provides some helpful context; it also this helps make it clear that (in 3ab6a51cb) we were adding a `browser` property that didn't exist before, rather than changing the value of an existing `browser` property. --- src/boot/store.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/boot/store.js b/src/boot/store.js index 69ded1cf663..8923fa401be 100644 --- a/src/boot/store.js +++ b/src/boot/store.js @@ -283,6 +283,7 @@ const migrations: {| [string]: (GlobalState) => GlobalState |} = { accounts: state.accounts.filter(a => a.email !== ''), }), + // Add "open links with in-app browser" setting. '28': state => ({ ...state, settings: { From 60847c9ef930465efb5d5dafc1ab29a3a06cdd49 Mon Sep 17 00:00:00 2001 From: Chris Bobbe Date: Thu, 15 Apr 2021 21:20:05 -0400 Subject: [PATCH 2/9] types: Make `sender_id` on `Outbox` required. As v27.157 has been out for well over a few weeks. And write a migration to strip any outbox messages that don't have it, as noted we should in a1fad7ca9's TODOs and in #3998. --- src/boot/store.js | 6 ++++++ src/message/getHtmlPieceDescriptors.js | 5 +---- src/types.js | 7 +------ 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/boot/store.js b/src/boot/store.js index 8923fa401be..4b3454c9a45 100644 --- a/src/boot/store.js +++ b/src/boot/store.js @@ -292,6 +292,12 @@ const migrations: {| [string]: (GlobalState) => GlobalState |} = { }, }), + // Make `sender_id` on `Outbox` required. + '29': state => ({ + ...state, + outbox: state.outbox.filter(o => o.sender_id !== undefined), + }), + // TIP: When adding a migration, consider just using `dropCache`. }; diff --git a/src/message/getHtmlPieceDescriptors.js b/src/message/getHtmlPieceDescriptors.js index 089b4c5588e..88fefe5a5f9 100644 --- a/src/message/getHtmlPieceDescriptors.js +++ b/src/message/getHtmlPieceDescriptors.js @@ -34,10 +34,7 @@ export default ( }); } - // TODO(#3764): Use sender_id, not sender_email. Needs making - // Outbox#sender_id required; which needs a migration to drop Outbox - // values that lack it; which is fine once the release that adds it - // has been out for a few weeks. + // TODO(#3764): Use sender_id, not sender_email. const shouldGroupWithPrev = !diffRecipient && !diffDays diff --git a/src/types.js b/src/types.js index ee301f7c487..8f2a9f8ce94 100644 --- a/src/types.js +++ b/src/types.js @@ -184,11 +184,7 @@ export type Outbox = $ReadOnly<{| // The remaining fields are modeled on `Message`. - // TODO(#3764): Make sender_id required. Needs a migration to drop Outbox - // values that lack it; which is fine once the release that adds it has - // been out for a few weeks. - // (Also drop the hack line about it in MessageLike.) - sender_id?: UserId, + sender_id: UserId, /* eslint-disable flowtype/generic-spacing */ ...SubsetProperties< @@ -241,7 +237,6 @@ export type MessageLike = | $ReadOnly<{| // $Shape is unsound, per Flow docs, but $ReadOnly<$Shape> is not ...$Shape<{| [$Keys]: void |}>, - sender_id?: UserId, // TODO: Drop this once required in Outbox. ...Outbox, |}>; From d1e504e1c783d767f970106e736720146f45e016 Mon Sep 17 00:00:00 2001 From: Chris Bobbe Date: Thu, 15 Apr 2021 21:28:16 -0400 Subject: [PATCH 3/9] types [nfc]: Put `sender_id` along with its friends. Now that we've made it required, as it has been on `Message`. Also remove a heading that we probably wished we could have removed in 34bf18b82; the `SubsetProperties` of `Message` makes the meaning quite clear. --- src/types.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/types.js b/src/types.js index 8f2a9f8ce94..9946463d71c 100644 --- a/src/types.js +++ b/src/types.js @@ -182,10 +182,6 @@ export type Outbox = $ReadOnly<{| // It's used for sending the message to the server. markdownContent: string, - // The remaining fields are modeled on `Message`. - - sender_id: UserId, - /* eslint-disable flowtype/generic-spacing */ ...SubsetProperties< Message, @@ -195,6 +191,7 @@ export type Outbox = $ReadOnly<{| display_recipient: mixed, id: mixed, reactions: mixed, + sender_id: mixed, sender_email: mixed, sender_full_name: mixed, subject: mixed, From 2d4c8af430acd02e9730a1a3995d1a16c8241c27 Mon Sep 17 00:00:00 2001 From: Chris Bobbe Date: Wed, 14 Apr 2021 19:40:26 -0400 Subject: [PATCH 4/9] getHtmlPieceDescriptors tests: Add invariant before reading `x.isBrief`. Only `RenderedMessageDescriptor`, not `RenderedTimeDescriptor`, has an `.isBrief` property. As written, it looks like these tests already fail when `.isBrief` is missing (the `expect` would receive `undefined` instead of a boolean), so this change just makes the expectation more explicit, and will let this code pass type-checking, which we'll start doing soon. --- .../__tests__/getHtmlPieceDescriptors-test.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/message/__tests__/getHtmlPieceDescriptors-test.js b/src/message/__tests__/getHtmlPieceDescriptors-test.js index 61456bb9915..3ca472c7e72 100644 --- a/src/message/__tests__/getHtmlPieceDescriptors-test.js +++ b/src/message/__tests__/getHtmlPieceDescriptors-test.js @@ -1,4 +1,5 @@ import deepFreeze from 'deep-freeze'; +import invariant from 'invariant'; import { HOME_NARROW } from '../../utils/narrow'; import getHtmlPieceDescriptors from '../getHtmlPieceDescriptors'; @@ -64,7 +65,10 @@ describe('getHtmlPieceDescriptors', () => { const htmlPieceDescriptors = getHtmlPieceDescriptors(messages, narrow); const messageKeys = htmlPieceDescriptors[1].data.map(x => x.key); - const messageBriefs = htmlPieceDescriptors[1].data.map(x => x.isBrief); + const messageBriefs = htmlPieceDescriptors[1].data.map(x => { + invariant(x.type === 'message', 'expected message item'); + return x.isBrief; + }); expect(messageKeys).toEqual([1, 2, 3]); expect(messageBriefs).toEqual([false, true, true]); }); @@ -106,7 +110,10 @@ describe('getHtmlPieceDescriptors', () => { const htmlPieceDescriptors = getHtmlPieceDescriptors(messages, narrow); const messageKeys = htmlPieceDescriptors[1].data.map(x => x.key); - const messageBriefs = htmlPieceDescriptors[1].data.map(x => x.isBrief); + const messageBriefs = htmlPieceDescriptors[1].data.map(x => { + invariant(x.type === 'message', 'expected message item'); + return x.isBrief; + }); expect(messageKeys).toEqual([1, 2, 3]); expect(messageBriefs).toEqual([false, false, false]); }); @@ -136,7 +143,10 @@ describe('getHtmlPieceDescriptors', () => { const htmlPieceDescriptors = getHtmlPieceDescriptors(messages, narrow); const messageKeys = htmlPieceDescriptors[1].data.map(x => x.key); - const messageBriefs = htmlPieceDescriptors[1].data.map(x => x.isBrief); + const messageBriefs = htmlPieceDescriptors[1].data.map(x => { + invariant(x.type === 'message', 'expected message item'); + return x.isBrief; + }); expect(messageKeys).toEqual([1, 2]); expect(messageBriefs).toEqual([false, false]); }); From 8f3fd726701475af0e4a663a04677be04204ac1c Mon Sep 17 00:00:00 2001 From: Chris Bobbe Date: Wed, 14 Apr 2021 19:34:38 -0400 Subject: [PATCH 5/9] getHtmlPieceDescriptors tests: Start type-checking, use example data. --- .../__tests__/getHtmlPieceDescriptors-test.js | 101 +++--------------- 1 file changed, 16 insertions(+), 85 deletions(-) diff --git a/src/message/__tests__/getHtmlPieceDescriptors-test.js b/src/message/__tests__/getHtmlPieceDescriptors-test.js index 3ca472c7e72..42c26d327d6 100644 --- a/src/message/__tests__/getHtmlPieceDescriptors-test.js +++ b/src/message/__tests__/getHtmlPieceDescriptors-test.js @@ -1,6 +1,8 @@ +/* @flow strict-local */ import deepFreeze from 'deep-freeze'; import invariant from 'invariant'; +import * as eg from '../../__tests__/lib/exampleData'; import { HOME_NARROW } from '../../utils/narrow'; import getHtmlPieceDescriptors from '../getHtmlPieceDescriptors'; @@ -13,13 +15,7 @@ describe('getHtmlPieceDescriptors', () => { }); test('renders time, header and message for a single input', () => { - const messages = deepFreeze([ - { - timestamp: 123, - avatar_url: '', - id: 12345, - }, - ]); + const messages = deepFreeze([{ ...eg.streamMessage({ id: 12345 }), timestamp: 123 }]); const htmlPieceDescriptors = getHtmlPieceDescriptors(messages, narrow); @@ -29,37 +25,13 @@ describe('getHtmlPieceDescriptors', () => { }); test('several messages in same stream, from same person result in time row, header for the stream, three messages, only first of which is full detail', () => { + const stream = eg.stream; + const sender = eg.otherUser; + const messages = deepFreeze([ - { - timestamp: 123, - type: 'stream', - id: 1, - sender_email: 'john@example.com', - sender_full_name: 'John Doe', - display_recipient: 'general', - subject: '', - avatar_url: '', - }, - { - timestamp: 124, - type: 'stream', - id: 2, - sender_email: 'john@example.com', - sender_full_name: 'John Doe', - display_recipient: 'general', - subject: '', - avatar_url: '', - }, - { - timestamp: 125, - type: 'stream', - id: 3, - sender_email: 'john@example.com', - sender_full_name: 'John Doe', - display_recipient: 'general', - subject: '', - avatar_url: '', - }, + eg.streamMessage({ stream, sender, id: 1 }), + eg.streamMessage({ stream, sender, id: 2 }), + eg.streamMessage({ stream, sender, id: 3 }), ]); const htmlPieceDescriptors = getHtmlPieceDescriptors(messages, narrow); @@ -74,37 +46,12 @@ describe('getHtmlPieceDescriptors', () => { }); test('several messages in same stream, from different people result in time row, header for the stream, three messages, only all full detail', () => { + const stream = eg.stream; + const messages = deepFreeze([ - { - timestamp: 123, - type: 'stream', - id: 1, - sender_email: 'john@example.com', - sender_full_name: 'John', - display_recipient: 'general', - subject: '', - avatar_url: '', - }, - { - timestamp: 124, - type: 'stream', - id: 2, - sender_email: 'mark@example.com', - sender_full_name: 'Mark', - display_recipient: 'general', - subject: '', - avatar_url: '', - }, - { - timestamp: 125, - type: 'stream', - id: 3, - sender_email: 'peter@example.com', - sender_full_name: 'Peter', - display_recipient: 'general', - subject: '', - avatar_url: '', - }, + eg.streamMessage({ stream, sender: eg.selfUser, id: 1 }), + eg.streamMessage({ stream, sender: eg.otherUser, id: 2 }), + eg.streamMessage({ stream, sender: eg.thirdUser, id: 3 }), ]); const htmlPieceDescriptors = getHtmlPieceDescriptors(messages, narrow); @@ -120,24 +67,8 @@ describe('getHtmlPieceDescriptors', () => { test('private messages between two people, results in time row, header and two full messages', () => { const messages = deepFreeze([ - { - timestamp: 123, - type: 'private', - id: 1, - sender_email: 'john@example.com', - sender_full_name: 'John', - avatar_url: '', - display_recipient: [{ email: 'john@example.com' }, { email: 'mark@example.com' }], - }, - { - timestamp: 123, - type: 'private', - id: 2, - sender_email: 'mark@example.com', - sender_full_name: 'Mark', - avatar_url: '', - display_recipient: [{ email: 'john@example.com' }, { email: 'mark@example.com' }], - }, + eg.pmMessage({ sender: eg.selfUser, recipients: [eg.selfUser, eg.otherUser], id: 1 }), + eg.pmMessage({ sender: eg.otherUser, recipients: [eg.selfUser, eg.otherUser], id: 2 }), ]); const htmlPieceDescriptors = getHtmlPieceDescriptors(messages, narrow); From 34b6b31e2f85857f6d041ff6a2d8ee35501ef13c Mon Sep 17 00:00:00 2001 From: Chris Bobbe Date: Thu, 15 Apr 2021 21:24:53 -0400 Subject: [PATCH 6/9] getHtmlPieceDescriptors: Use Outbox's `sender_id`, now that we can. From a search of `sender_email` in all JS files, I didn't see any other obvious places we could take advantage of `sender_id`. Part of #3998. --- src/message/getHtmlPieceDescriptors.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/message/getHtmlPieceDescriptors.js b/src/message/getHtmlPieceDescriptors.js index 88fefe5a5f9..57446a841f1 100644 --- a/src/message/getHtmlPieceDescriptors.js +++ b/src/message/getHtmlPieceDescriptors.js @@ -34,12 +34,8 @@ export default ( }); } - // TODO(#3764): Use sender_id, not sender_email. const shouldGroupWithPrev = - !diffRecipient - && !diffDays - && !!prevMessage - && prevMessage.sender_email === message.sender_email; + !diffRecipient && !diffDays && !!prevMessage && prevMessage.sender_id === message.sender_id; sections[sections.length - 1].data.push({ key: message.id, From d346483b6c629da9fe28ef13f265bfb22da99ac9 Mon Sep 17 00:00:00 2001 From: Chris Bobbe Date: Thu, 15 Apr 2021 21:37:39 -0400 Subject: [PATCH 7/9] types: Express Outbox as `PmOutbox | StreamOutbox`. Like we did with `PmMessage` and `StreamMessage` in #4506. We don't actually make `PmOutbox` any different from `StreamOutbox` in this commit; that'll come later. --- src/types.js | 70 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 49 insertions(+), 21 deletions(-) diff --git a/src/types.js b/src/types.js index 9946463d71c..36a634c8b10 100644 --- a/src/types.js +++ b/src/types.js @@ -3,7 +3,15 @@ import type { IntlShape } from 'react-intl'; import type { DangerouslyImpreciseStyleProp } from 'react-native/Libraries/StyleSheet/StyleSheet'; import type { SubsetProperties } from './generics'; -import type { Auth, Topic, Message, ReactionType, UserId } from './api/apiTypes'; +import type { + Auth, + Topic, + Message, + PmMessage, + StreamMessage, + ReactionType, + UserId, +} from './api/apiTypes'; import type { ZulipVersion } from './utils/zulipVersion'; import type { PmKeyUsers } from './utils/recipient'; @@ -145,27 +153,10 @@ export type TopicExtended = {| |}; /** - * A message we're in the process of sending. - * - * We use these objects for two purposes: - * - * (a) They make up the queue of messages the user has asked us to send, and - * which we'll retry sending if initial attempts fail. See - * `trySendMessages`. - * - * (b) We show them immediately in the message list, even before we've - * successfully gotten them to the server (but with an activity - * indicator to show we're still working on them.) - * - * Even after (a) is complete for a given message, we still need the - * `Outbox` object for the sake of (b), until we hear an `EVENT_NEW_MESSAGE` - * event from the server that lets us replace it with the corresponding - * `Message` object. - * - * This type most often appears in the union `Message | Outbox`, and so its - * properties are deliberately similar to those of `Message`. + * Properties in common among the two different flavors of a + * `Outbox`: `PmOutbox` and `StreamOutbox`. */ -export type Outbox = $ReadOnly<{| +export type OutboxBase = $ReadOnly<{| /** Used for distinguishing from a `Message` object. */ isOutbox: true, @@ -184,6 +175,8 @@ export type Outbox = $ReadOnly<{| /* eslint-disable flowtype/generic-spacing */ ...SubsetProperties< + // Could use `MessageBase` here, but Flow would complain anyway if + // we tried to put something that's not in `MessageBase` below. Message, {| avatar_url: mixed, @@ -201,6 +194,41 @@ export type Outbox = $ReadOnly<{| >, |}>; +export type PmOutbox = {| + ...OutboxBase, + + ...SubsetProperties, +|}; + +export type StreamOutbox = {| + ...OutboxBase, + + ...SubsetProperties, +|}; + +/** + * A message we're in the process of sending. + * + * We use these objects for two purposes: + * + * (a) They make up the queue of messages the user has asked us to send, and + * which we'll retry sending if initial attempts fail. See + * `trySendMessages`. + * + * (b) We show them immediately in the message list, even before we've + * successfully gotten them to the server (but with an activity + * indicator to show we're still working on them.) + * + * Even after (a) is complete for a given message, we still need the + * `Outbox` object for the sake of (b), until we hear an `EVENT_NEW_MESSAGE` + * event from the server that lets us replace it with the corresponding + * `Message` object. + * + * This type most often appears in the union `Message | Outbox`, and so its + * properties are deliberately similar to those of `Message`. + */ +export type Outbox = PmOutbox | StreamOutbox; + /** * MessageLike: Imprecise alternative to `Message | Outbox`. * From 0f3bce5763f052b74f5d98c2ca1a4b25879cccbb Mon Sep 17 00:00:00 2001 From: Chris Bobbe Date: Tue, 20 Apr 2021 17:38:15 -0400 Subject: [PATCH 8/9] exampleData [nfc]: Make `makeOutboxMessage` specific to `StreamOutbox`es. To reinforce the point in the section header: """ (Only stream messages for now. Feel free to add PMs, if you need them.) """ We don't need to handle PMs yet, but this'll hopefully make it clearer how to do so when the time comes. --- src/__tests__/lib/exampleData.js | 14 +++++++---- src/chat/__tests__/narrowsSelectors-test.js | 2 +- src/outbox/__tests__/outboxReducer-test.js | 26 ++++++++++----------- 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/__tests__/lib/exampleData.js b/src/__tests__/lib/exampleData.js index ce317b60513..40a57b86877 100644 --- a/src/__tests__/lib/exampleData.js +++ b/src/__tests__/lib/exampleData.js @@ -18,7 +18,7 @@ import type { } from '../../api/modelTypes'; import { makeUserId } from '../../api/idTypes'; import type { Action, GlobalState, MessagesState, RealmState } from '../../reduxTypes'; -import type { Auth, Account, Outbox } from '../../types'; +import type { Auth, Account, OutboxBase, StreamOutbox } from '../../types'; import { UploadedAvatarURL } from '../../utils/avatar'; import { ZulipVersion } from '../../utils/zulipVersion'; import { @@ -415,8 +415,11 @@ export const makeMessagesState = (messages: Message[]): MessagesState => * (Only stream messages for now. Feel free to add PMs, if you need them.) */ -/** An outbox message with no interesting data. */ -const outboxMessageBase: $Diff = deepFreeze({ +/** + * Properties in common among PM and stream outbox messages, with no + * interesting data. + */ +const outboxMessageBase: $Diff = deepFreeze({ isOutbox: true, isSent: false, avatar_url: selfUser.avatar_url, @@ -434,11 +437,12 @@ const outboxMessageBase: $Diff = deep }); /** - * Create an outbox message from an interesting subset of its data. + * Create a stream outbox message from an interesting subset of its + * data. * * `.id` is always identical to `.timestamp` and should not be supplied. */ -export const makeOutboxMessage = (data: $Shape<$Diff>): Outbox => { +export const streamOutbox = (data: $Shape<$Diff>): StreamOutbox => { const { timestamp } = data; const outputTimestamp = timestamp ?? makeTime() / 1000; diff --git a/src/chat/__tests__/narrowsSelectors-test.js b/src/chat/__tests__/narrowsSelectors-test.js index 8dac38d685c..76643129c79 100644 --- a/src/chat/__tests__/narrowsSelectors-test.js +++ b/src/chat/__tests__/narrowsSelectors-test.js @@ -24,7 +24,7 @@ import * as eg from '../../__tests__/lib/exampleData'; describe('getMessagesForNarrow', () => { const message = eg.streamMessage({ id: 123 }); const messages = eg.makeMessagesState([message]); - const outboxMessage = eg.makeOutboxMessage({}); + const outboxMessage = eg.streamOutbox({}); test('if no outbox messages returns messages with no change', () => { const state = eg.reduxState({ diff --git a/src/outbox/__tests__/outboxReducer-test.js b/src/outbox/__tests__/outboxReducer-test.js index 168692d432f..4dbf7cf51ac 100644 --- a/src/outbox/__tests__/outboxReducer-test.js +++ b/src/outbox/__tests__/outboxReducer-test.js @@ -9,9 +9,9 @@ import * as eg from '../../__tests__/lib/exampleData'; describe('outboxReducer', () => { describe('INITIAL_FETCH_COMPLETE', () => { test('filters out isSent', () => { - const message1 = eg.makeOutboxMessage({ content: 'New one' }); - const message2 = eg.makeOutboxMessage({ content: 'Another one' }); - const message3 = eg.makeOutboxMessage({ content: 'Message already sent', isSent: true }); + const message1 = eg.streamOutbox({ content: 'New one' }); + const message2 = eg.streamOutbox({ content: 'Another one' }); + const message3 = eg.streamOutbox({ content: 'Message already sent', isSent: true }); const initialState = deepFreeze([message1, message2, message3]); const action = deepFreeze({ @@ -28,7 +28,7 @@ describe('outboxReducer', () => { describe('MESSAGE_SEND_START', () => { test('add a new message to the outbox', () => { - const message = eg.makeOutboxMessage({ content: 'New one' }); + const message = eg.streamOutbox({ content: 'New one' }); const initialState = deepFreeze([]); @@ -45,8 +45,8 @@ describe('outboxReducer', () => { }); test('do not add a message with a duplicate timestamp to the outbox', () => { - const message1 = eg.makeOutboxMessage({ content: 'hello', timestamp: 123 }); - const message2 = eg.makeOutboxMessage({ content: 'hello twice', timestamp: 123 }); + const message1 = eg.streamOutbox({ content: 'hello', timestamp: 123 }); + const message2 = eg.streamOutbox({ content: 'hello twice', timestamp: 123 }); const initialState = deepFreeze([message1]); @@ -63,7 +63,7 @@ describe('outboxReducer', () => { describe('EVENT_NEW_MESSAGE', () => { test('do not mutate state if a message is not removed', () => { - const initialState = deepFreeze([eg.makeOutboxMessage({ timestamp: 546 })]); + const initialState = deepFreeze([eg.streamOutbox({ timestamp: 546 })]); const message = eg.streamMessage({ timestamp: 123 }); @@ -78,9 +78,9 @@ describe('outboxReducer', () => { }); test('remove message if local_message_id matches', () => { - const message1 = eg.makeOutboxMessage({ timestamp: 546 }); - const message2 = eg.makeOutboxMessage({ timestamp: 150238512430 }); - const message3 = eg.makeOutboxMessage({ timestamp: 150238594540 }); + const message1 = eg.streamOutbox({ timestamp: 546 }); + const message2 = eg.streamOutbox({ timestamp: 150238512430 }); + const message3 = eg.streamOutbox({ timestamp: 150238594540 }); const initialState = deepFreeze([message1, message2, message3]); const action = deepFreeze({ @@ -97,9 +97,9 @@ describe('outboxReducer', () => { }); test("remove nothing if local_message_id doesn't match", () => { - const message1 = eg.makeOutboxMessage({ timestamp: 546 }); - const message2 = eg.makeOutboxMessage({ timestamp: 150238512430 }); - const message3 = eg.makeOutboxMessage({ timestamp: 150238594540 }); + const message1 = eg.streamOutbox({ timestamp: 546 }); + const message2 = eg.streamOutbox({ timestamp: 150238512430 }); + const message3 = eg.streamOutbox({ timestamp: 150238594540 }); const initialState = deepFreeze([message1, message2, message3]); const action = deepFreeze({ From fce3e8b3d040dbf149ec71b0cb4bedabd69e3c57 Mon Sep 17 00:00:00 2001 From: Chris Bobbe Date: Thu, 15 Apr 2021 21:46:22 -0400 Subject: [PATCH 9/9] types: Add optional `stream_id` to `StreamOutbox`. Much like we did for `sender_id` in a1fad7ca9. Once a release that has this commit has been out for a few weeks, we should write a migration to strip `StreamOutbox` messages that don't have `stream_id`, and make `stream_id` required. Part of #3998. --- src/types.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/types.js b/src/types.js index 36a634c8b10..a2ec2429ecc 100644 --- a/src/types.js +++ b/src/types.js @@ -203,6 +203,16 @@ export type PmOutbox = {| export type StreamOutbox = {| ...OutboxBase, + // TODO(#3764): Make stream_id required. Needs a migration to drop + // StreamOutbox values that lack it; that'll be fine once the + // release that adds it has been out for a few weeks. (Also drop + // the hack line about it in MessageLike.) + // + // Once it is required, it + // should go in the second type argument passed to + // `SubsetProperties` of `StreamMessage`, below. + stream_id?: number, + ...SubsetProperties, |}; @@ -262,6 +272,7 @@ export type MessageLike = | $ReadOnly<{| // $Shape is unsound, per Flow docs, but $ReadOnly<$Shape> is not ...$Shape<{| [$Keys]: void |}>, + stream_id?: number, // TODO: Drop this once required in StreamOutbox. ...Outbox, |}>;