Properly handle long message attachments for edited messages

This commit is contained in:
Scott Nonnenberg 2024-01-30 13:22:23 -08:00 committed by GitHub
parent 4daa1e4569
commit 304287efef
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 114 additions and 19 deletions

View file

@ -517,25 +517,89 @@ async function _addAttachmentToMessage(
const attachmentSignature = getAttachmentSignature(attachment); const attachmentSignature = getAttachmentSignature(attachment);
if (type === 'long-message') { if (type === 'long-message') {
// Attachment wasn't downloaded yet. let handledAnywhere = false;
if (!attachment.path) { let attachmentData: Uint8Array | undefined;
message.set({
bodyAttachment: attachment,
});
return;
}
try { try {
const { data } = await window.Signal.Migrations.loadAttachmentData( if (attachment.path) {
attachment const loaded = await window.Signal.Migrations.loadAttachmentData(
); attachment
);
attachmentData = loaded.data;
}
const editHistory = message.get('editHistory');
if (editHistory) {
let handledInEditHistory = false;
const newEditHistory = editHistory.map(edit => {
// We've already downloaded a bodyAttachment for this edit
if (!edit.bodyAttachment) {
return edit;
}
// This attachment isn't destined for this edit
if (
getAttachmentSignature(edit.bodyAttachment) !== attachmentSignature
) {
return edit;
}
handledInEditHistory = true;
handledAnywhere = true;
// Attachment wasn't downloaded yet.
if (!attachmentData) {
return {
...edit,
bodyAttachment: attachment,
};
}
return {
...edit,
body: Bytes.toString(attachmentData),
bodyAttachment: undefined,
};
});
if (handledInEditHistory) {
message.set({ editHistory: newEditHistory });
}
}
const existingBodyAttachment = message.get('bodyAttachment');
// A bodyAttachment download might apply only to an edit, and not the top-level
if (!existingBodyAttachment) {
return;
}
if (
getAttachmentSignature(existingBodyAttachment) !== attachmentSignature
) {
return;
}
handledAnywhere = true;
// Attachment wasn't downloaded yet.
if (!attachmentData) {
message.set({
bodyAttachment: attachment,
});
return;
}
message.set({ message.set({
body: Bytes.toString(data), body: Bytes.toString(attachmentData),
bodyAttachment: undefined, bodyAttachment: undefined,
}); });
} finally { } finally {
if (attachment.path) { if (attachment.path) {
void window.Signal.Migrations.deleteAttachmentData(attachment.path); await window.Signal.Migrations.deleteAttachmentData(attachment.path);
}
if (!handledAnywhere) {
logger.warn(
`${logPrefix}: Long message attachment found no matching place to apply`
);
} }
} }
return; return;
@ -556,6 +620,7 @@ async function _addAttachmentToMessage(
if (type === 'attachment') { if (type === 'attachment') {
const attachments = message.get('attachments'); const attachments = message.get('attachments');
let handledAnywhere = false;
let handledInEditHistory = false; let handledInEditHistory = false;
const editHistory = message.get('editHistory'); const editHistory = message.get('editHistory');
@ -572,6 +637,7 @@ async function _addAttachmentToMessage(
attachments: edit.attachments.map(item => { attachments: edit.attachments.map(item => {
const newItem = maybeReplaceAttachment(item); const newItem = maybeReplaceAttachment(item);
handledInEditHistory ||= item !== newItem; handledInEditHistory ||= item !== newItem;
handledAnywhere ||= handledInEditHistory;
return newItem; return newItem;
}), }),
}; };
@ -584,10 +650,20 @@ async function _addAttachmentToMessage(
if (attachments) { if (attachments) {
message.set({ message.set({
attachments: attachments.map(item => maybeReplaceAttachment(item)), attachments: attachments.map(item => {
const newItem = maybeReplaceAttachment(item);
handledAnywhere ||= item !== newItem;
return newItem;
}),
}); });
} }
if (!handledAnywhere) {
logger.warn(
`${logPrefix}: 'attachment' type found no matching place to apply`
);
}
return; return;
} }
@ -643,7 +719,7 @@ async function _addAttachmentToMessage(
const contact = message.get('contact'); const contact = message.get('contact');
if (!contact || contact.length <= index) { if (!contact || contact.length <= index) {
throw new Error( throw new Error(
`_addAttachmentToMessage: contact didn't exist or ${index} was too large` `${logPrefix}: contact didn't exist or ${index} was too large`
); );
} }
@ -663,7 +739,7 @@ async function _addAttachmentToMessage(
message.set({ contact: newContact }); message.set({ contact: newContact });
} else { } else {
logger.warn( logger.warn(
`_addAttachmentToMessage: Couldn't update contact with avatar attachment for message ${message.idForLogging()}` `${logPrefix}: Couldn't update contact with avatar attachment for message`
); );
} }
@ -730,7 +806,7 @@ async function _addAttachmentToMessage(
if (type === 'sticker') { if (type === 'sticker') {
const sticker = message.get('sticker'); const sticker = message.get('sticker');
if (!sticker) { if (!sticker) {
throw new Error("_addAttachmentToMessage: sticker didn't exist"); throw new Error(`${logPrefix}: sticker didn't exist`);
} }
message.set({ message.set({
@ -742,9 +818,7 @@ async function _addAttachmentToMessage(
return; return;
} }
throw new Error( throw new Error(`${logPrefix}: Unknown job type ${type}`);
`_addAttachmentToMessage: Unknown job type ${type} for message ${message.idForLogging()}`
);
} }
function _checkOldAttachment( function _checkOldAttachment(

1
ts/model-types.d.ts vendored
View file

@ -119,6 +119,7 @@ export type MessageReactionType = {
export type EditHistoryType = { export type EditHistoryType = {
attachments?: Array<AttachmentType>; attachments?: Array<AttachmentType>;
body?: string; body?: string;
bodyAttachment?: AttachmentType;
bodyRanges?: ReadonlyArray<RawBodyRange>; bodyRanges?: ReadonlyArray<RawBodyRange>;
preview?: Array<LinkPreviewType>; preview?: Array<LinkPreviewType>;
quote?: QuotedMessageType; quote?: QuotedMessageType;

View file

@ -115,6 +115,7 @@ export async function handleEditMessage(
{ {
attachments: mainMessage.attachments, attachments: mainMessage.attachments,
body: mainMessage.body, body: mainMessage.body,
bodyAttachment: mainMessage.bodyAttachment,
bodyRanges: mainMessage.bodyRanges, bodyRanges: mainMessage.bodyRanges,
preview: mainMessage.preview, preview: mainMessage.preview,
quote: mainMessage.quote, quote: mainMessage.quote,
@ -278,6 +279,25 @@ export async function handleEditMessage(
const updatedFields = await queueAttachmentDownloads( const updatedFields = await queueAttachmentDownloads(
mainMessageModel.attributes mainMessageModel.attributes
); );
// If we've scheduled a bodyAttachment download, we need that edit to know about it
if (updatedFields?.bodyAttachment) {
const existing =
updatedFields.editHistory || mainMessageModel.get('editHistory') || [];
updatedFields.editHistory = existing.map(item => {
if (item.timestamp !== editedMessage.timestamp) {
return item;
}
return {
...item,
attachments: updatedFields.attachments,
bodyAttachment: updatedFields.bodyAttachment,
};
});
}
if (updatedFields) { if (updatedFields) {
mainMessageModel.set(updatedFields); mainMessageModel.set(updatedFields);
} }