2024-06-10 18:44:15 +00:00
|
|
|
// Copyright 2024 Signal Messenger, LLC
|
|
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
|
|
|
|
import { omit } from 'lodash';
|
|
|
|
|
|
|
|
import * as log from '../logging/log';
|
|
|
|
import type { QuotedMessageType } from '../model-types';
|
2024-09-09 23:29:19 +00:00
|
|
|
import type {
|
|
|
|
MessageAttributesType,
|
|
|
|
ReadonlyMessageAttributesType,
|
|
|
|
} from '../model-types.d';
|
2024-06-10 18:44:15 +00:00
|
|
|
import { SignalService } from '../protobuf';
|
|
|
|
import { isGiftBadge, isTapToView } from '../state/selectors/message';
|
|
|
|
import type { ProcessedQuote } from '../textsecure/Types';
|
|
|
|
import { IMAGE_JPEG } from '../types/MIME';
|
|
|
|
import { strictAssert } from '../util/assert';
|
|
|
|
import { getQuoteBodyText } from '../util/getQuoteBodyText';
|
|
|
|
import { isQuoteAMatch, messageHasPaymentEvent } from './helpers';
|
|
|
|
import * as Errors from '../types/errors';
|
|
|
|
import { isDownloadable } from '../types/Attachment';
|
|
|
|
|
2024-09-09 23:29:19 +00:00
|
|
|
export type MinimalMessageCache = Readonly<{
|
|
|
|
findBySentAt(
|
|
|
|
sentAt: number,
|
|
|
|
predicate: (attributes: ReadonlyMessageAttributesType) => boolean
|
|
|
|
): Promise<MessageAttributesType | undefined>;
|
|
|
|
upgradeSchema(
|
|
|
|
attributes: MessageAttributesType,
|
|
|
|
minSchemaVersion: number
|
|
|
|
): Promise<MessageAttributesType>;
|
|
|
|
}>;
|
|
|
|
|
|
|
|
export type CopyQuoteOptionsType = Readonly<{
|
|
|
|
messageCache?: MinimalMessageCache;
|
|
|
|
}>;
|
|
|
|
|
2024-06-10 18:44:15 +00:00
|
|
|
export const copyFromQuotedMessage = async (
|
|
|
|
quote: ProcessedQuote,
|
2024-09-09 23:29:19 +00:00
|
|
|
conversationId: string,
|
|
|
|
options: CopyQuoteOptionsType = {}
|
2024-06-10 18:44:15 +00:00
|
|
|
): Promise<QuotedMessageType> => {
|
2024-09-09 23:29:19 +00:00
|
|
|
const { messageCache = window.MessageCache } = options;
|
2024-06-10 18:44:15 +00:00
|
|
|
const { id } = quote;
|
|
|
|
strictAssert(id, 'Quote must have an id');
|
|
|
|
|
|
|
|
const result: QuotedMessageType = {
|
|
|
|
...omit(quote, 'type'),
|
|
|
|
|
|
|
|
id,
|
|
|
|
|
|
|
|
attachments: quote.attachments.slice(),
|
|
|
|
bodyRanges: quote.bodyRanges?.slice(),
|
|
|
|
|
|
|
|
// Just placeholder values for the fields
|
|
|
|
referencedMessageNotFound: false,
|
|
|
|
isGiftBadge: quote.type === SignalService.DataMessage.Quote.Type.GIFT_BADGE,
|
|
|
|
isViewOnce: false,
|
|
|
|
};
|
|
|
|
|
2024-09-09 23:29:19 +00:00
|
|
|
const queryMessage = await messageCache.findBySentAt(id, attributes =>
|
2024-09-04 22:59:39 +00:00
|
|
|
isQuoteAMatch(attributes, conversationId, result)
|
2024-06-10 18:44:15 +00:00
|
|
|
);
|
|
|
|
|
2024-09-04 22:59:39 +00:00
|
|
|
if (queryMessage == null) {
|
|
|
|
result.referencedMessageNotFound = true;
|
|
|
|
return result;
|
2024-06-10 18:44:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (queryMessage) {
|
2024-09-09 23:29:19 +00:00
|
|
|
await copyQuoteContentFromOriginal(queryMessage, result, options);
|
2024-06-10 18:44:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
};
|
|
|
|
|
|
|
|
export const copyQuoteContentFromOriginal = async (
|
2024-09-04 22:59:39 +00:00
|
|
|
providedOriginalMessage: MessageAttributesType,
|
2024-09-09 23:29:19 +00:00
|
|
|
quote: QuotedMessageType,
|
|
|
|
{ messageCache = window.MessageCache }: CopyQuoteOptionsType = {}
|
2024-06-10 18:44:15 +00:00
|
|
|
): Promise<void> => {
|
2024-09-04 22:59:39 +00:00
|
|
|
let originalMessage = providedOriginalMessage;
|
|
|
|
|
2024-06-10 18:44:15 +00:00
|
|
|
const { attachments } = quote;
|
|
|
|
const firstAttachment = attachments ? attachments[0] : undefined;
|
|
|
|
|
2024-09-04 22:59:39 +00:00
|
|
|
if (messageHasPaymentEvent(originalMessage)) {
|
2024-06-10 18:44:15 +00:00
|
|
|
// eslint-disable-next-line no-param-reassign
|
2024-09-04 22:59:39 +00:00
|
|
|
quote.payment = originalMessage.payment;
|
2024-06-10 18:44:15 +00:00
|
|
|
}
|
|
|
|
|
2024-09-04 22:59:39 +00:00
|
|
|
if (isTapToView(originalMessage)) {
|
2024-06-10 18:44:15 +00:00
|
|
|
// eslint-disable-next-line no-param-reassign
|
|
|
|
quote.text = undefined;
|
|
|
|
// eslint-disable-next-line no-param-reassign
|
|
|
|
quote.attachments = [
|
|
|
|
{
|
|
|
|
contentType: IMAGE_JPEG,
|
|
|
|
},
|
|
|
|
];
|
|
|
|
// eslint-disable-next-line no-param-reassign
|
|
|
|
quote.isViewOnce = true;
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-09-04 22:59:39 +00:00
|
|
|
const isMessageAGiftBadge = isGiftBadge(originalMessage);
|
2024-06-10 18:44:15 +00:00
|
|
|
if (isMessageAGiftBadge !== quote.isGiftBadge) {
|
|
|
|
log.warn(
|
|
|
|
`copyQuoteContentFromOriginal: Quote.isGiftBadge: ${quote.isGiftBadge}, isGiftBadge(message): ${isMessageAGiftBadge}`
|
|
|
|
);
|
|
|
|
// eslint-disable-next-line no-param-reassign
|
|
|
|
quote.isGiftBadge = isMessageAGiftBadge;
|
|
|
|
}
|
|
|
|
if (isMessageAGiftBadge) {
|
|
|
|
// eslint-disable-next-line no-param-reassign
|
|
|
|
quote.text = undefined;
|
|
|
|
// eslint-disable-next-line no-param-reassign
|
|
|
|
quote.attachments = [];
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// eslint-disable-next-line no-param-reassign
|
|
|
|
quote.isViewOnce = false;
|
|
|
|
|
|
|
|
// eslint-disable-next-line no-param-reassign
|
2024-09-04 22:59:39 +00:00
|
|
|
quote.text = getQuoteBodyText(originalMessage, quote.id);
|
2024-06-10 18:44:15 +00:00
|
|
|
|
|
|
|
// eslint-disable-next-line no-param-reassign
|
2024-09-04 22:59:39 +00:00
|
|
|
quote.bodyRanges = originalMessage.bodyRanges;
|
2024-06-10 18:44:15 +00:00
|
|
|
|
|
|
|
if (!firstAttachment || !firstAttachment.contentType) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
2024-09-09 23:29:19 +00:00
|
|
|
originalMessage = await messageCache.upgradeSchema(
|
2024-09-04 22:59:39 +00:00
|
|
|
originalMessage,
|
|
|
|
window.Signal.Types.Message.VERSION_NEEDED_FOR_DISPLAY
|
|
|
|
);
|
2024-06-10 18:44:15 +00:00
|
|
|
} catch (error) {
|
|
|
|
log.error(
|
|
|
|
'Problem upgrading message quoted message from database',
|
|
|
|
Errors.toLogFormat(error)
|
|
|
|
);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-09-04 22:59:39 +00:00
|
|
|
const {
|
|
|
|
attachments: queryAttachments = [],
|
|
|
|
preview: queryPreview = [],
|
|
|
|
sticker,
|
|
|
|
} = originalMessage;
|
|
|
|
|
2024-06-10 18:44:15 +00:00
|
|
|
if (queryAttachments.length > 0) {
|
|
|
|
const queryFirst = queryAttachments[0];
|
|
|
|
const { thumbnail } = queryFirst;
|
|
|
|
|
|
|
|
if (thumbnail && thumbnail.path) {
|
|
|
|
firstAttachment.thumbnail = {
|
|
|
|
...thumbnail,
|
|
|
|
copied: true,
|
|
|
|
};
|
|
|
|
} else if (!firstAttachment.thumbnail || !isDownloadable(queryFirst)) {
|
|
|
|
firstAttachment.contentType = queryFirst.contentType;
|
|
|
|
firstAttachment.fileName = queryFirst.fileName;
|
|
|
|
firstAttachment.thumbnail = undefined;
|
|
|
|
} else {
|
|
|
|
// there is a thumbnail, but the original message attachment has not been
|
|
|
|
// downloaded yet, so we leave the quote attachment as is for now
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (queryPreview.length > 0) {
|
|
|
|
const queryFirst = queryPreview[0];
|
|
|
|
const { image } = queryFirst;
|
|
|
|
|
|
|
|
if (image && image.path) {
|
|
|
|
firstAttachment.thumbnail = {
|
|
|
|
...image,
|
|
|
|
copied: true,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sticker && sticker.data && sticker.data.path) {
|
|
|
|
firstAttachment.thumbnail = {
|
|
|
|
...sticker.data,
|
|
|
|
copied: true,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
};
|