Do not confirm messages until we have handled them

This commit is contained in:
Josh Perez 2023-08-21 16:08:27 -04:00 committed by GitHub
parent 29aa188c0f
commit 04f716986c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 990 additions and 960 deletions

View file

@ -9,17 +9,14 @@ import type { SendStateByConversationId } from '../messages/MessageSendState';
import * as Edits from '../messageModifiers/Edits';
import * as log from '../logging/log';
import * as Deletes from '../messageModifiers/Deletes';
import {
MessageReceipts,
MessageReceiptType,
} from '../messageModifiers/MessageReceipts';
import { Reactions } from '../messageModifiers/Reactions';
import * as MessageReceipts from '../messageModifiers/MessageReceipts';
import * as Reactions from '../messageModifiers/Reactions';
import * as ReadSyncs from '../messageModifiers/ReadSyncs';
import * as ViewOnceOpenSyncs from '../messageModifiers/ViewOnceOpenSyncs';
import * as ViewSyncs from '../messageModifiers/ViewSyncs';
import { ReadStatus } from '../messages/MessageReadStatus';
import { ReadSyncs } from '../messageModifiers/ReadSyncs';
import { SeenStatus } from '../MessageSeenStatus';
import { SendActionType, sendStateReducer } from '../messages/MessageSendState';
import { ViewOnceOpenSyncs } from '../messageModifiers/ViewOnceOpenSyncs';
import { ViewSyncs } from '../messageModifiers/ViewSyncs';
import { canConversationBeUnarchived } from './canConversationBeUnarchived';
import { deleteForEveryone } from './deleteForEveryone';
import { handleEditMessage } from './handleEditMessage';
@ -48,33 +45,31 @@ export async function modifyTargetMessage(
const sourceServiceId = getSourceServiceId(message.attributes);
if (type === 'outgoing' || (type === 'story' && ourAci === sourceServiceId)) {
const sendActions = MessageReceipts.getSingleton()
.forMessage(message)
.map(receipt => {
let sendActionType: SendActionType;
const receiptType = receipt.get('type');
switch (receiptType) {
case MessageReceiptType.Delivery:
sendActionType = SendActionType.GotDeliveryReceipt;
break;
case MessageReceiptType.Read:
sendActionType = SendActionType.GotReadReceipt;
break;
case MessageReceiptType.View:
sendActionType = SendActionType.GotViewedReceipt;
break;
default:
throw missingCaseError(receiptType);
}
const sendActions = MessageReceipts.forMessage(message).map(receipt => {
let sendActionType: SendActionType;
const receiptType = receipt.type;
switch (receiptType) {
case MessageReceipts.MessageReceiptType.Delivery:
sendActionType = SendActionType.GotDeliveryReceipt;
break;
case MessageReceipts.MessageReceiptType.Read:
sendActionType = SendActionType.GotReadReceipt;
break;
case MessageReceipts.MessageReceiptType.View:
sendActionType = SendActionType.GotViewedReceipt;
break;
default:
throw missingCaseError(receiptType);
}
return {
destinationConversationId: receipt.get('sourceConversationId'),
action: {
type: sendActionType,
updatedAt: receipt.get('receiptTimestamp'),
},
};
});
return {
destinationConversationId: receipt.sourceConversationId,
action: {
type: sendActionType,
updatedAt: receipt.receiptTimestamp,
},
};
});
const oldSendStateByConversationId =
message.get('sendStateByConversationId') || {};
@ -111,10 +106,10 @@ export async function modifyTargetMessage(
if (type === 'incoming') {
// In a followup (see DESKTOP-2100), we want to make `ReadSyncs#forMessage` return
// an array, not an object. This array wrapping makes that future a bit easier.
const readSync = ReadSyncs.getSingleton().forMessage(message);
const readSync = ReadSyncs.forMessage(message);
const readSyncs = readSync ? [readSync] : [];
const viewSyncs = ViewSyncs.getSingleton().forMessage(message);
const viewSyncs = ViewSyncs.forMessage(message);
const isGroupStoryReply =
isGroup(conversation.attributes) && message.get('storyId');
@ -122,8 +117,8 @@ export async function modifyTargetMessage(
if (readSyncs.length !== 0 || viewSyncs.length !== 0) {
const markReadAt = Math.min(
Date.now(),
...readSyncs.map(sync => sync.get('readAt')),
...viewSyncs.map(sync => sync.get('viewedAt'))
...readSyncs.map(sync => sync.readAt),
...viewSyncs.map(sync => sync.viewedAt)
);
if (message.get('expireTimer')) {
@ -181,8 +176,7 @@ export async function modifyTargetMessage(
// Check for out-of-order view once open syncs
if (isTapToView(message.attributes)) {
const viewOnceOpenSync =
ViewOnceOpenSyncs.getSingleton().forMessage(message);
const viewOnceOpenSync = ViewOnceOpenSyncs.forMessage(message);
if (viewOnceOpenSync) {
await message.markViewOnceMessageViewed({ fromSync: true });
changed = true;
@ -191,7 +185,7 @@ export async function modifyTargetMessage(
}
if (isStory(message.attributes)) {
const viewSyncs = ViewSyncs.getSingleton().forMessage(message);
const viewSyncs = ViewSyncs.forMessage(message);
if (viewSyncs.length !== 0) {
message.set({
@ -202,7 +196,7 @@ export async function modifyTargetMessage(
const markReadAt = Math.min(
Date.now(),
...viewSyncs.map(sync => sync.get('viewedAt'))
...viewSyncs.map(sync => sync.viewedAt)
);
message.setPendingMarkRead(
Math.min(message.getPendingMarkRead() ?? Date.now(), markReadAt)
@ -220,12 +214,12 @@ export async function modifyTargetMessage(
}
// Does message message have any pending, previously-received associated reactions?
const reactions = Reactions.getSingleton().forMessage(message);
const reactions = Reactions.forMessage(message);
await Promise.all(
reactions.map(async reaction => {
if (isStory(message.attributes)) {
// We don't set changed = true here, because we don't modify the original story
const generatedMessage = reaction.get('storyReactionMessage');
const generatedMessage = reaction.storyReactionMessage;
strictAssert(
generatedMessage,
'Story reactions must provide storyReactionMessage'