Simplify edit-related send functionality

This commit is contained in:
Scott Nonnenberg 2023-12-15 17:43:31 -08:00 committed by GitHub
parent 183619ad90
commit 0918b3da7f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 121 additions and 83 deletions

View file

@ -56,7 +56,7 @@ import * as Bytes from '../../Bytes';
import {
getPropForTimestamp,
getTargetOfThisEditTimestamp,
setPropForTimestamp,
getChangesForPropAtTimestamp,
} from '../../util/editHelpers';
import { getMessageSentTimestamp } from '../../util/getMessageSentTimestamp';
@ -115,7 +115,7 @@ export async function sendNormalMessage(
// The timestamp identifying the target of this edit; could be the original timestamp
// or the most recent edit prior to this one
const targetOfThisEditTimestamp = getTargetOfThisEditTimestamp({
message,
message: message.attributes,
targetTimestamp,
});
@ -486,7 +486,7 @@ function getMessageRecipients({
const sendStateByConversationId =
getPropForTimestamp({
log,
message,
message: message.attributes,
prop: 'sendStateByConversationId',
targetTimestamp,
}) || {};
@ -579,7 +579,7 @@ async function getMessageSendData({
// Figure out if we need to upload message body as an attachment.
let body = getPropForTimestamp({
log,
message,
message: message.attributes,
prop: 'body',
targetTimestamp,
});
@ -603,7 +603,7 @@ async function getMessageSendData({
const preUploadAttachments =
getPropForTimestamp({
log,
message,
message: message.attributes,
prop: 'attachments',
targetTimestamp,
}) || [];
@ -677,7 +677,7 @@ async function getMessageSendData({
expireTimer: message.get('expireTimer'),
bodyRanges: getPropForTimestamp({
log,
message,
message: message.attributes,
prop: 'bodyRanges',
targetTimestamp,
}),
@ -720,7 +720,7 @@ async function uploadSingleAttachment({
const logId = `uploadSingleAttachment(${message.idForLogging()}`;
const oldAttachments = getPropForTimestamp({
log,
message,
message: message.attributes,
prop: 'attachments',
targetTimestamp,
});
@ -742,13 +742,16 @@ async function uploadSingleAttachment({
...copyCdnFields(uploaded),
};
setPropForTimestamp({
const attributesToUpdate = getChangesForPropAtTimestamp({
log,
message,
message: message.attributes,
prop: 'attachments',
targetTimestamp,
value: newAttachments,
});
if (attributesToUpdate) {
message.set(attributesToUpdate);
}
return uploaded;
}
@ -772,7 +775,7 @@ async function uploadMessageQuote({
// sends are failing, let's not add the complication of a cache.
const startingQuote = getPropForTimestamp({
log,
message,
message: message.attributes,
prop: 'quote',
targetTimestamp,
});
@ -808,7 +811,7 @@ async function uploadMessageQuote({
const logId = `uploadMessageQuote(${message.idForLogging()}`;
const oldQuote = getPropForTimestamp({
log,
message,
message: message.attributes,
prop: 'quote',
targetTimestamp,
});
@ -842,13 +845,16 @@ async function uploadMessageQuote({
};
}),
};
setPropForTimestamp({
const attributesToUpdate = getChangesForPropAtTimestamp({
log,
message,
message: message.attributes,
prop: 'quote',
targetTimestamp,
value: newQuote,
});
if (attributesToUpdate) {
message.set(attributesToUpdate);
}
return {
isGiftBadge: loadedQuote.isGiftBadge,
@ -879,7 +885,7 @@ async function uploadMessagePreviews({
// attachments.
const startingPreview = getPropForTimestamp({
log,
message,
message: message.attributes,
prop: 'preview',
targetTimestamp,
});
@ -919,7 +925,7 @@ async function uploadMessagePreviews({
const logId = `uploadMessagePreviews(${message.idForLogging()}`;
const oldPreview = getPropForTimestamp({
log,
message,
message: message.attributes,
prop: 'preview',
targetTimestamp,
});
@ -945,13 +951,16 @@ async function uploadMessagePreviews({
};
});
setPropForTimestamp({
const attributesToUpdate = getChangesForPropAtTimestamp({
log,
message,
message: message.attributes,
prop: 'preview',
targetTimestamp,
value: newPreview,
});
if (attributesToUpdate) {
message.set(attributesToUpdate);
}
return uploadedPreviews;
}
@ -1119,7 +1128,7 @@ function didSendToEveryone({
const sendStateByConversationId =
getPropForTimestamp({
log,
message,
message: message.attributes,
prop: 'sendStateByConversationId',
targetTimestamp,
}) || {};

View file

@ -155,7 +155,10 @@ import { getSenderIdentifier } from '../util/getSenderIdentifier';
import { getNotificationDataForMessage } from '../util/getNotificationDataForMessage';
import { getNotificationTextForMessage } from '../util/getNotificationTextForMessage';
import { getMessageAuthorText } from '../util/getMessageAuthorText';
import { getPropForTimestamp, setPropForTimestamp } from '../util/editHelpers';
import {
getPropForTimestamp,
getChangesForPropAtTimestamp,
} from '../util/editHelpers';
import { getMessageSentTimestamp } from '../util/getMessageSentTimestamp';
/* eslint-disable more/no-then */
@ -838,7 +841,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
const targetTimestamp = editMessageTimestamp || this.get('timestamp');
const sendStateByConversationId = getPropForTimestamp({
log,
message: this,
message: this.attributes,
prop: 'sendStateByConversationId',
targetTimestamp,
});
@ -852,13 +855,16 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
})
);
setPropForTimestamp({
const attributesToUpdate = getChangesForPropAtTimestamp({
log,
message: this,
message: this.attributes,
prop: 'sendStateByConversationId',
targetTimestamp,
value: newSendStateByConversationId,
});
if (attributesToUpdate) {
this.set(attributesToUpdate);
}
// We aren't trying to send this message anymore, so we'll delete these caches
delete this.cachedOutgoingContactData;
@ -956,7 +962,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
const sendStateByConversationId = {
...(getPropForTimestamp({
log,
message: this,
message: this.attributes,
prop: 'sendStateByConversationId',
targetTimestamp,
}) || {}),
@ -1101,7 +1107,15 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
// We may overwrite this in the `saveErrors` call below.
attributesToUpdate.errors = [];
this.set(attributesToUpdate);
const additionalProps = getChangesForPropAtTimestamp({
log,
message: this.attributes,
prop: 'sendStateByConversationId',
targetTimestamp,
value: sendStateByConversationId,
});
this.set({ ...attributesToUpdate, ...additionalProps });
if (saveErrors) {
saveErrors(errorsToSave);
} else {
@ -1109,14 +1123,6 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
void this.saveErrors(errorsToSave, { skipSave: true });
}
setPropForTimestamp({
log,
message: this,
prop: 'sendStateByConversationId',
targetTimestamp,
value: sendStateByConversationId,
});
if (!this.doNotSave) {
await window.Signal.Data.saveMessage(this.attributes, {
ourAci: window.textsecure.storage.user.getCheckedAci(),
@ -1237,7 +1243,12 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
const conv = this.getConversation()!;
const sendEntries = Object.entries(
this.get('sendStateByConversationId') || {}
getPropForTimestamp({
log,
message: this.attributes,
prop: 'sendStateByConversationId',
targetTimestamp,
}) || {}
);
const sentEntries = filter(sendEntries, ([_conversationId, { status }]) =>
isSent(status)
@ -1292,7 +1303,12 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
).then(async result => {
let newSendStateByConversationId: undefined | SendStateByConversationId;
const sendStateByConversationId =
this.get('sendStateByConversationId') || {};
getPropForTimestamp({
log,
message: this.attributes,
prop: 'sendStateByConversationId',
targetTimestamp,
}) || {};
const ourOldSendState = getOwn(
sendStateByConversationId,
ourConversation.id
@ -1310,12 +1326,20 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
}
}
const attributesForUpdate = newSendStateByConversationId
? getChangesForPropAtTimestamp({
log,
message: this.attributes,
prop: 'sendStateByConversationId',
value: newSendStateByConversationId,
targetTimestamp,
})
: null;
this.set({
synced: true,
dataMessage: null,
...(newSendStateByConversationId
? { sendStateByConversationId: newSendStateByConversationId }
: {}),
...attributesForUpdate,
});
// Return early, skip the save

View file

@ -7,8 +7,6 @@ import { strictAssert } from './assert';
import type { EditHistoryType, MessageAttributesType } from '../model-types';
import type { LoggerType } from '../types/Logging';
import { getMessageIdForLogging } from './idForLogging';
import type { MessageModel } from '../models/messages';
// The tricky bit for this function is if we are on our second+ attempt to send a given
// edit, we're still sending that edit.
@ -16,11 +14,13 @@ export function getTargetOfThisEditTimestamp({
message,
targetTimestamp,
}: {
message: MessageModel;
message: MessageAttributesType;
targetTimestamp: number;
}): number {
const originalTimestamp = message.get('timestamp');
const editHistory = message.get('editHistory') || [];
const { timestamp: originalTimestamp, editHistory } = message;
if (!editHistory) {
return originalTimestamp;
}
const sentItems = editHistory.filter(item => {
return item.timestamp <= targetTimestamp;
@ -52,18 +52,13 @@ export function getPropForTimestamp<T extends keyof EditHistoryType>({
targetTimestamp,
}: {
log: LoggerType;
message: MessageModel | MessageAttributesType;
message: MessageAttributesType;
prop: T;
targetTimestamp: number;
}): EditHistoryType[T] {
const attributes =
message instanceof window.Whisper.Message ? message.attributes : message;
const logId = `getPropForTimestamp(${targetTimestamp}})`;
const logId = `getPropForTimestamp(${getMessageIdForLogging(
attributes
)}, target=${targetTimestamp}})`;
const { editHistory } = attributes;
const { editHistory } = message;
const targetEdit = editHistory?.find(
item => item.timestamp === targetTimestamp
);
@ -71,13 +66,13 @@ export function getPropForTimestamp<T extends keyof EditHistoryType>({
if (editHistory) {
log.warn(`${logId}: No edit found, using top-level data`);
}
return attributes[prop];
return message[prop];
}
return targetEdit[prop];
}
export function setPropForTimestamp<T extends keyof EditHistoryType>({
export function getChangesForPropAtTimestamp<T extends keyof EditHistoryType>({
log,
message,
prop,
@ -85,48 +80,58 @@ export function setPropForTimestamp<T extends keyof EditHistoryType>({
value,
}: {
log: LoggerType;
message: MessageModel;
message: MessageAttributesType;
prop: T;
targetTimestamp: number;
value: EditHistoryType[T];
}): void {
const logId = `setPropForTimestamp(${message.idForLogging()}, target=${targetTimestamp}})`;
}): Partial<MessageAttributesType> | undefined {
const logId = `getChangesForPropAtTimestamp(${targetTimestamp})`;
const editHistory = message.get('editHistory');
const targetEditIndex = editHistory?.findIndex(
item => item.timestamp === targetTimestamp
);
const targetEdit =
editHistory && isNumber(targetEditIndex)
const { editHistory } = message;
let partialProps: Partial<MessageAttributesType> | undefined;
if (editHistory) {
const targetEditIndex = editHistory.findIndex(
item => item.timestamp === targetTimestamp
);
const targetEdit = isNumber(targetEditIndex)
? editHistory[targetEditIndex]
: undefined;
if (!targetEdit) {
if (editHistory) {
log.warn(`${logId}: No edit found, updating top-level data`);
if (!targetEdit) {
if (editHistory) {
log.warn(`${logId}: No edit found, updating top-level data`);
}
return {
[prop]: value,
};
}
message.set({
[prop]: value,
});
return;
strictAssert(
isNumber(targetEditIndex),
'Got targetEdit, but no targetEditIndex'
);
const newEditHistory = [...editHistory];
newEditHistory[targetEditIndex] = { ...targetEdit, [prop]: value };
partialProps = {
editHistory: newEditHistory,
};
}
strictAssert(editHistory, 'Got targetEdit, but no editHistory');
strictAssert(
isNumber(targetEditIndex),
'Got targetEdit, but no targetEditIndex'
);
const newEditHistory = [...editHistory];
newEditHistory[targetEditIndex] = { ...targetEdit, [prop]: value };
message.set('editHistory', newEditHistory);
// We always edit the top-level attribute if this is the most recent send
const editMessageTimestamp = message.get('editMessageTimestamp');
if (!editMessageTimestamp || editMessageTimestamp === targetTimestamp) {
message.set({
const { editMessageTimestamp } = message;
if (
!editHistory ||
!editMessageTimestamp ||
editMessageTimestamp === targetTimestamp
) {
partialProps = {
...partialProps,
[prop]: value,
});
};
}
return partialProps;
}