@@ -435,6 +435,8 @@ class MessageStoreImpl extends HasChannelStore with MessageStore, _OutboxMessage
435435 ifAbsent: () => _reconcileUnrecognizedMessage (message),
436436 (current) => _reconcileRecognizedMessage (current, message));
437437 }
438+
439+ _removeDeliveredOutboxMessages ();
438440 }
439441
440442 Message _reconcileUnrecognizedMessage (Message incoming) {
@@ -952,15 +954,19 @@ const kSendMessageOfferRestoreWaitPeriod = Duration(seconds: 10); // TODO(#1441
952954/// timed out. not finished when
953955/// wait period timed out.
954956///
955- /// Event received. Or [sendMessage]
956- /// request succeeds and we're sending to
957- /// an unsubscribed channel.
957+ /// Event received. Or message found in a
958+ /// fetch after [sendMessage] request succeeds.
959+ /// Or [sendMessage] request succeeds and
960+ /// we're sending to an unsubscribed channel.
958961/// (any state) ───────────────────────────────────────► (delete)
959962/// ```
960963///
961964/// During its lifecycle, it is guaranteed that the outbox message is deleted
962965/// as soon a message event with a matching [MessageEvent.localMessageId]
963966/// arrives.
967+ /// Once the [sendMessage] request has succeeded, the outbox message is also
968+ /// deleted as soon as a message with a matching [OutboxMessage.messageId]
969+ /// is found in a fetch; see [MessageStoreImpl.reconcileMessages] .
964970/// If we're sending to an unsubscribed channel, we don't expect an event
965971/// (see "third buggy behavior" in #1798) so in that case
966972/// the outbox message is deleted when the [sendMessage] request succeeds.
@@ -1001,8 +1007,9 @@ enum OutboxMessageState {
10011007///
10021008/// A request remains "outstanding" even after the [sendMessage] HTTP request
10031009/// completes, whether with success or failure.
1004- /// The outbox-message persists until either the corresponding [MessageEvent]
1005- /// arrives to replace it, or the user discards it (perhaps to try again).
1010+ /// The outbox-message persists until either the corresponding message
1011+ /// arrives to replace it (in a [MessageEvent] or, once the request has
1012+ /// succeeded, in a fetch), or the user discards it (perhaps to try again).
10061013/// For details, see the state diagram at [OutboxMessageState] ,
10071014/// and [MessageStore.takeOutboxMessage] .
10081015sealed class OutboxMessage <T extends Conversation > extends MessageBase <T > {
@@ -1117,6 +1124,9 @@ mixin _OutboxMessageStore on HasChannelStore {
11171124 /// unique within this instance.
11181125 int _nextLocalMessageId = 1 ;
11191126
1127+ /// As in [MessageStoreImpl.messages] .
1128+ Map <int , Message > get messages;
1129+
11201130 /// As in [MessageStoreImpl._messageListViews] .
11211131 Set <MessageListView > get _messageListViews;
11221132
@@ -1317,6 +1327,40 @@ mixin _OutboxMessageStore on HasChannelStore {
13171327 return removed;
13181328 }
13191329
1330+ /// Remove any outbox messages whose anticipated [Message] ,
1331+ /// as identified by [OutboxMessage.messageId] , is in [messages] ,
1332+ /// updating message-list views accordingly.
1333+ ///
1334+ /// This is how outbox messages get removed when their messages
1335+ /// are received through a fetch instead of a [MessageEvent] ;
1336+ /// see [MessageStoreImpl.reconcileMessages] .
1337+ void _removeDeliveredOutboxMessages () {
1338+ assert (! _disposed);
1339+ // This runs on every fetch (see [MessageStoreImpl.reconcileMessages]);
1340+ // return cheaply in the common case of an empty outbox.
1341+ if (_outboxMessages.isEmpty) return ;
1342+ final delivered = _outboxMessages.values
1343+ .where ((outboxMessage) {
1344+ final messageId = outboxMessage._messageId;
1345+ return messageId != null && messages.containsKey (messageId);
1346+ })
1347+ .toList ();
1348+ for (final outboxMessage in delivered) {
1349+ _removeDeliveredOutboxMessage (outboxMessage);
1350+ }
1351+ }
1352+
1353+ /// Remove [outboxMessage] , whose anticipated [Message]
1354+ /// (see [OutboxMessage.messageId] ) must be in [messages] ,
1355+ /// updating message-list views accordingly.
1356+ void _removeDeliveredOutboxMessage (OutboxMessage outboxMessage) {
1357+ assert (messages.containsKey (outboxMessage._messageId));
1358+ _removeOutboxMessage (outboxMessage.localMessageId);
1359+ for (final view in _messageListViews) {
1360+ view.handleOutboxMessageDelivered (outboxMessage);
1361+ }
1362+ }
1363+
13201364 void _handleMessageEventOutbox (MessageEvent event) {
13211365 if (event.localMessageId != null ) {
13221366 final localMessageId = int .parse (event.localMessageId! , radix: 10 );
0 commit comments