Use correct timestamp for receipts of edited messages

This commit is contained in:
Fedor Indutny 2023-05-16 10:37:12 -07:00 committed by GitHub
parent 8fe0047822
commit 5869717cd3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 156 additions and 52 deletions

View file

@ -15,6 +15,7 @@ import { SignalService as Proto } from '../../protobuf';
import { handleMessageSend } from '../../util/handleMessageSend'; import { handleMessageSend } from '../../util/handleMessageSend';
import { findAndFormatContact } from '../../util/findAndFormatContact'; import { findAndFormatContact } from '../../util/findAndFormatContact';
import { uploadAttachment } from '../../util/uploadAttachment'; import { uploadAttachment } from '../../util/uploadAttachment';
import { getMessageSentTimestamp } from '../../util/getMessageSentTimestamp';
import type { CallbackResultType } from '../../textsecure/Types.d'; import type { CallbackResultType } from '../../textsecure/Types.d';
import { isSent } from '../../messages/MessageSendState'; import { isSent } from '../../messages/MessageSendState';
import { isOutgoing, canReact } from '../../state/selectors/message'; import { isOutgoing, canReact } from '../../state/selectors/message';
@ -499,22 +500,11 @@ async function getMessageSendData({
storyContext?: StoryContextType; storyContext?: StoryContextType;
}> { }> {
const editMessageTimestamp = message.get('editMessageTimestamp'); const editMessageTimestamp = message.get('editMessageTimestamp');
const sentAt = message.get('sent_at');
const timestamp = message.get('timestamp');
let mainMessageTimestamp: number;
if (sentAt) {
mainMessageTimestamp = sentAt;
} else if (timestamp) {
log.error('message lacked sent_at. Falling back to timestamp');
mainMessageTimestamp = timestamp;
} else {
log.error(
'message lacked sent_at and timestamp. Falling back to current time'
);
mainMessageTimestamp = Date.now();
}
const mainMessageTimestamp = getMessageSentTimestamp(message.attributes, {
includeEdits: false,
log,
});
const messageTimestamp = editMessageTimestamp || mainMessageTimestamp; const messageTimestamp = editMessageTimestamp || mainMessageTimestamp;
const storyId = message.get('storyId'); const storyId = message.get('storyId');

View file

@ -10,6 +10,7 @@ import * as log from '../logging/log';
import * as Errors from '../types/errors'; import * as Errors from '../types/errors';
import { deleteForEveryone } from '../util/deleteForEveryone'; import { deleteForEveryone } from '../util/deleteForEveryone';
import { drop } from '../util/drop'; import { drop } from '../util/drop';
import { getMessageSentTimestampSet } from '../util/getMessageSentTimestampSet';
export type DeleteAttributesType = { export type DeleteAttributesType = {
targetSentTimestamp: number; targetSentTimestamp: number;
@ -31,10 +32,11 @@ export class Deletes extends Collection<DeleteModel> {
} }
forMessage(message: MessageModel): Array<DeleteModel> { forMessage(message: MessageModel): Array<DeleteModel> {
const sentTimestamps = getMessageSentTimestampSet(message.attributes);
const matchingDeletes = this.filter(item => { const matchingDeletes = this.filter(item => {
return ( return (
item.get('targetSentTimestamp') === message.get('sent_at') && item.get('fromId') === getContactId(message.attributes) &&
item.get('fromId') === getContactId(message.attributes) sentTimestamps.has(item.get('targetSentTimestamp'))
); );
}); });

View file

@ -9,6 +9,7 @@ import { drop } from '../util/drop';
import { filter, size } from '../util/iterables'; import { filter, size } from '../util/iterables';
import { getContactId } from '../messages/helpers'; import { getContactId } from '../messages/helpers';
import { handleEditMessage } from '../util/handleEditMessage'; import { handleEditMessage } from '../util/handleEditMessage';
import { getMessageSentTimestamp } from '../util/getMessageSentTimestamp';
export type EditAttributesType = { export type EditAttributesType = {
conversationId: string; conversationId: string;
@ -20,9 +21,10 @@ export type EditAttributesType = {
const edits = new Set<EditAttributesType>(); const edits = new Set<EditAttributesType>();
export function forMessage(message: MessageModel): Array<EditAttributesType> { export function forMessage(message: MessageModel): Array<EditAttributesType> {
const sentAt = getMessageSentTimestamp(message.attributes, { log });
const matchingEdits = filter(edits, item => { const matchingEdits = filter(edits, item => {
return ( return (
item.targetSentTimestamp === message.get('sent_at') && item.targetSentTimestamp === sentAt &&
item.fromId === getContactId(message.attributes) item.fromId === getContactId(message.attributes)
); );
}); });

View file

@ -24,6 +24,7 @@ import dataInterface from '../sql/Client';
import * as log from '../logging/log'; import * as log from '../logging/log';
import { getSourceUuid } from '../messages/helpers'; import { getSourceUuid } from '../messages/helpers';
import { queueUpdateMessage } from '../util/messageBatcher'; import { queueUpdateMessage } from '../util/messageBatcher';
import { getMessageSentTimestamp } from '../util/getMessageSentTimestamp';
const { deleteSentProtoRecipient } = dataInterface; const { deleteSentProtoRecipient } = dataInterface;
@ -159,7 +160,7 @@ export class MessageReceipts extends Collection<MessageReceiptModel> {
return []; return [];
} }
const sentAt = message.get('sent_at'); const sentAt = getMessageSentTimestamp(message.attributes, { log });
const receipts = this.filter( const receipts = this.filter(
receipt => receipt.get('messageSentAt') === sentAt receipt => receipt.get('messageSentAt') === sentAt
); );

View file

@ -16,6 +16,7 @@ import { getContactId, getContact } from '../messages/helpers';
import { isDirectConversation, isMe } from '../util/whatTypeOfConversation'; import { isDirectConversation, isMe } from '../util/whatTypeOfConversation';
import { isOutgoing, isStory } from '../state/selectors/message'; import { isOutgoing, isStory } from '../state/selectors/message';
import { strictAssert } from '../util/assert'; import { strictAssert } from '../util/assert';
import { getMessageSentTimestampSet } from '../util/getMessageSentTimestampSet';
export class ReactionModel extends Model<ReactionAttributesType> {} export class ReactionModel extends Model<ReactionAttributesType> {}
@ -31,9 +32,10 @@ export class Reactions extends Collection<ReactionModel> {
} }
forMessage(message: MessageModel): Array<ReactionModel> { forMessage(message: MessageModel): Array<ReactionModel> {
const sentTimestamps = getMessageSentTimestampSet(message.attributes);
if (isOutgoing(message.attributes)) { if (isOutgoing(message.attributes)) {
const outgoingReactions = this.filter( const outgoingReactions = this.filter(item =>
item => item.get('targetTimestamp') === message.get('sent_at') sentTimestamps.has(item.get('targetTimestamp'))
); );
if (outgoingReactions.length > 0) { if (outgoingReactions.length > 0) {
@ -44,14 +46,15 @@ export class Reactions extends Collection<ReactionModel> {
} }
const senderId = getContactId(message.attributes); const senderId = getContactId(message.attributes);
const sentAt = message.get('sent_at');
const reactionsBySource = this.filter(re => { const reactionsBySource = this.filter(re => {
const targetSender = window.ConversationController.lookupOrCreate({ const targetSender = window.ConversationController.lookupOrCreate({
uuid: re.get('targetAuthorUuid'), uuid: re.get('targetAuthorUuid'),
reason: 'Reactions.forMessage', reason: 'Reactions.forMessage',
}); });
const targetTimestamp = re.get('targetTimestamp'); const targetTimestamp = re.get('targetTimestamp');
return targetSender?.id === senderId && targetTimestamp === sentAt; return (
targetSender?.id === senderId && sentTimestamps.has(targetTimestamp)
);
}); });
if (reactionsBySource.length > 0) { if (reactionsBySource.length > 0) {

View file

@ -13,6 +13,7 @@ import * as log from '../logging/log';
import * as Errors from '../types/errors'; import * as Errors from '../types/errors';
import { StartupQueue } from '../util/StartupQueue'; import { StartupQueue } from '../util/StartupQueue';
import { queueUpdateMessage } from '../util/messageBatcher'; import { queueUpdateMessage } from '../util/messageBatcher';
import { getMessageSentTimestamp } from '../util/getMessageSentTimestamp';
export type ReadSyncAttributesType = { export type ReadSyncAttributesType = {
senderId: string; senderId: string;
@ -66,10 +67,13 @@ export class ReadSyncs extends Collection {
uuid: message.get('sourceUuid'), uuid: message.get('sourceUuid'),
reason: 'ReadSyncs.forMessage', reason: 'ReadSyncs.forMessage',
}); });
const messageTimestamp = getMessageSentTimestamp(message.attributes, {
log,
});
const sync = this.find(item => { const sync = this.find(item => {
return ( return (
item.get('senderId') === sender?.id && item.get('senderId') === sender?.id &&
item.get('timestamp') === message.get('sent_at') item.get('timestamp') === messageTimestamp
); );
}); });
if (sync) { if (sync) {

View file

@ -16,6 +16,7 @@ import { queueAttachmentDownloads } from '../util/queueAttachmentDownloads';
import * as log from '../logging/log'; import * as log from '../logging/log';
import { GiftBadgeStates } from '../components/conversation/Message'; import { GiftBadgeStates } from '../components/conversation/Message';
import { queueUpdateMessage } from '../util/messageBatcher'; import { queueUpdateMessage } from '../util/messageBatcher';
import { getMessageSentTimestamp } from '../util/getMessageSentTimestamp';
export type ViewSyncAttributesType = { export type ViewSyncAttributesType = {
senderId: string; senderId: string;
@ -44,17 +45,18 @@ export class ViewSyncs extends Collection {
uuid: message.get('sourceUuid'), uuid: message.get('sourceUuid'),
reason: 'ViewSyncs.forMessage', reason: 'ViewSyncs.forMessage',
}); });
const messageTimestamp = getMessageSentTimestamp(message.attributes, {
log,
});
const syncs = this.filter(item => { const syncs = this.filter(item => {
return ( return (
item.get('senderId') === sender?.id && item.get('senderId') === sender?.id &&
item.get('timestamp') === message.get('sent_at') item.get('timestamp') === messageTimestamp
); );
}); });
if (syncs.length) { if (syncs.length) {
log.info( log.info(
`Found ${syncs.length} early view sync(s) for message ${message.get( `Found ${syncs.length} early view sync(s) for message ${messageTimestamp}`
'sent_at'
)}`
); );
this.remove(syncs); this.remove(syncs);
} }

View file

@ -29,6 +29,7 @@ import { memoizeByThis } from '../util/memoizeByThis';
import { getInitials } from '../util/getInitials'; import { getInitials } from '../util/getInitials';
import { normalizeUuid } from '../util/normalizeUuid'; import { normalizeUuid } from '../util/normalizeUuid';
import { clearTimeoutIfNecessary } from '../util/clearTimeoutIfNecessary'; import { clearTimeoutIfNecessary } from '../util/clearTimeoutIfNecessary';
import { getMessageSentTimestamp } from '../util/getMessageSentTimestamp';
import type { AttachmentType, ThumbnailType } from '../types/Attachment'; import type { AttachmentType, ThumbnailType } from '../types/Attachment';
import { toDayMillis } from '../util/timestamp'; import { toDayMillis } from '../util/timestamp';
import { isVoiceMessage } from '../types/Attachment'; import { isVoiceMessage } from '../types/Attachment';
@ -2321,7 +2322,7 @@ export class ConversationModel extends window.Backbone
conversationId, conversationId,
senderE164: m.source, senderE164: m.source,
senderUuid: m.sourceUuid, senderUuid: m.sourceUuid,
timestamp: m.sent_at, timestamp: getMessageSentTimestamp(m, { log }),
isDirectConversation: isDirectConversation(this.attributes), isDirectConversation: isDirectConversation(this.attributes),
})), })),
}); });

View file

@ -48,6 +48,7 @@ import type {
import { SendMessageProtoError } from '../textsecure/Errors'; import { SendMessageProtoError } from '../textsecure/Errors';
import * as expirationTimer from '../util/expirationTimer'; import * as expirationTimer from '../util/expirationTimer';
import { getUserLanguages } from '../util/userLanguages'; import { getUserLanguages } from '../util/userLanguages';
import { getMessageSentTimestamp } from '../util/getMessageSentTimestamp';
import type { ReactionType } from '../types/Reactions'; import type { ReactionType } from '../types/Reactions';
import { UUID, UUIDKind } from '../types/UUID'; import { UUID, UUIDKind } from '../types/UUID';
@ -1802,9 +1803,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
); );
const isEditedMessage = Boolean(this.get('editHistory')); const isEditedMessage = Boolean(this.get('editHistory'));
const mainMessageTimestamp = this.get('sent_at') || this.get('timestamp'); const timestamp = getMessageSentTimestamp(this.attributes, { log });
const timestamp =
this.get('editMessageTimestamp') || mainMessageTimestamp;
const encodedContent = isEditedMessage const encodedContent = isEditedMessage
? { ? {

View file

@ -9,8 +9,10 @@ import { strictAssert } from '../util/assert';
import { isDirectConversation } from '../util/whatTypeOfConversation'; import { isDirectConversation } from '../util/whatTypeOfConversation';
import { incrementMessageCounter } from '../util/incrementMessageCounter'; import { incrementMessageCounter } from '../util/incrementMessageCounter';
import { repeat, zipObject } from '../util/iterables'; import { repeat, zipObject } from '../util/iterables';
import { getMessageSentTimestamp } from '../util/getMessageSentTimestamp';
import { SendStatus } from '../messages/MessageSendState'; import { SendStatus } from '../messages/MessageSendState';
import { UUID } from '../types/UUID'; import { UUID } from '../types/UUID';
import * as log from '../logging/log';
export async function enqueueReactionForSend({ export async function enqueueReactionForSend({
emoji, emoji,
@ -30,7 +32,9 @@ export async function enqueueReactionForSend({
`enqueueReactionForSend: message ${message.idForLogging()} had no source UUID` `enqueueReactionForSend: message ${message.idForLogging()} had no source UUID`
); );
const targetTimestamp = message.get('sent_at') || message.get('timestamp'); const targetTimestamp = getMessageSentTimestamp(message.attributes, {
log,
});
strictAssert( strictAssert(
targetTimestamp, targetTimestamp,
`enqueueReactionForSend: message ${message.idForLogging()} had no timestamp` `enqueueReactionForSend: message ${message.idForLogging()} had no timestamp`

View file

@ -389,7 +389,7 @@ export type FTSOptimizationStateType = Readonly<{
}>; }>;
export type EditedMessageType = Readonly<{ export type EditedMessageType = Readonly<{
fromId: string; conversationId: string;
messageId: string; messageId: string;
sentAt: number; sentAt: number;
readStatus: MessageType['readStatus']; readStatus: MessageType['readStatus'];
@ -523,7 +523,7 @@ export type DataInterface = {
storyId?: string; storyId?: string;
}) => Promise<GetUnreadByConversationAndMarkReadResultType>; }) => Promise<GetUnreadByConversationAndMarkReadResultType>;
getUnreadEditedMessagesAndMarkRead: (options: { getUnreadEditedMessagesAndMarkRead: (options: {
fromId: string; conversationId: string;
newestUnreadAt: number; newestUnreadAt: number;
}) => Promise<GetUnreadByConversationAndMarkReadResultType>; }) => Promise<GetUnreadByConversationAndMarkReadResultType>;
getUnreadReactionsAndMarkRead: (options: { getUnreadReactionsAndMarkRead: (options: {

View file

@ -5630,7 +5630,7 @@ async function removeAllProfileKeyCredentials(): Promise<void> {
async function saveEditedMessage( async function saveEditedMessage(
mainMessage: MessageType, mainMessage: MessageType,
ourUuid: UUIDStringType, ourUuid: UUIDStringType,
{ fromId, messageId, readStatus, sentAt }: EditedMessageType { conversationId, messageId, readStatus, sentAt }: EditedMessageType
): Promise<void> { ): Promise<void> {
const db = getInstance(); const db = getInstance();
@ -5644,12 +5644,12 @@ async function saveEditedMessage(
const [query, params] = sql` const [query, params] = sql`
INSERT INTO edited_messages ( INSERT INTO edited_messages (
fromId, conversationId,
messageId, messageId,
sentAt, sentAt,
readStatus readStatus
) VALUES ( ) VALUES (
${fromId}, ${conversationId},
${messageId}, ${messageId},
${sentAt}, ${sentAt},
${readStatus} ${readStatus}
@ -5675,10 +5675,10 @@ async function _getAllEditedMessages(): Promise<
} }
async function getUnreadEditedMessagesAndMarkRead({ async function getUnreadEditedMessagesAndMarkRead({
fromId, conversationId,
newestUnreadAt, newestUnreadAt,
}: { }: {
fromId: string; conversationId: string;
newestUnreadAt: number; newestUnreadAt: number;
}): Promise<GetUnreadByConversationAndMarkReadResultType> { }): Promise<GetUnreadByConversationAndMarkReadResultType> {
const db = getInstance(); const db = getInstance();
@ -5695,7 +5695,7 @@ async function getUnreadEditedMessagesAndMarkRead({
ON messages.id = edited_messages.messageId ON messages.id = edited_messages.messageId
WHERE WHERE
edited_messages.readStatus = ${ReadStatus.Unread} AND edited_messages.readStatus = ${ReadStatus.Unread} AND
edited_messages.fromId = ${fromId} AND edited_messages.conversationId = ${conversationId} AND
received_at <= ${newestUnreadAt} received_at <= ${newestUnreadAt}
ORDER BY messages.received_at DESC, messages.sent_at DESC; ORDER BY messages.received_at DESC, messages.sent_at DESC;
`; `;
@ -5711,7 +5711,7 @@ async function getUnreadEditedMessagesAndMarkRead({
readStatus = ${ReadStatus.Read} readStatus = ${ReadStatus.Read}
WHERE WHERE
readStatus = ${ReadStatus.Unread} AND readStatus = ${ReadStatus.Unread} AND
fromId = ${fromId} AND conversationId = ${conversationId} AND
sentAt <= ${newestSentAt}; sentAt <= ${newestSentAt};
`; `;

View file

@ -0,0 +1,29 @@
// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { Database } from '@signalapp/better-sqlite3';
import type { LoggerType } from '../../types/Logging';
export default function updateToSchemaVersion82(
currentVersion: number,
db: Database,
logger: LoggerType
): void {
if (currentVersion >= 82) {
return;
}
db.transaction(() => {
db.exec(`
ALTER TABLE edited_messages DROP COLUMN fromId;
ALTER TABLE edited_messages ADD COLUMN conversationId STRING;
CREATE INDEX edited_messages_unread ON edited_messages (readStatus, conversationId);
`);
db.pragma('user_version = 82');
})();
logger.info('updateToSchemaVersion82: success!');
}

View file

@ -57,6 +57,7 @@ import updateToSchemaVersion78 from './78-merge-receipt-jobs';
import updateToSchemaVersion79 from './79-paging-lightbox'; import updateToSchemaVersion79 from './79-paging-lightbox';
import updateToSchemaVersion80 from './80-edited-messages'; import updateToSchemaVersion80 from './80-edited-messages';
import updateToSchemaVersion81 from './81-contact-removed-notification'; import updateToSchemaVersion81 from './81-contact-removed-notification';
import updateToSchemaVersion82 from './82-edited-messages-read-index';
function updateToSchemaVersion1( function updateToSchemaVersion1(
currentVersion: number, currentVersion: number,
@ -1984,6 +1985,7 @@ export const SCHEMA_VERSIONS = [
updateToSchemaVersion80, updateToSchemaVersion80,
updateToSchemaVersion81, updateToSchemaVersion81,
updateToSchemaVersion82,
]; ];
export function updateSchema(db: Database, logger: LoggerType): void { export function updateSchema(db: Database, logger: LoggerType): void {

View file

@ -138,6 +138,7 @@ import { DAY } from '../../util/durations';
import { isNotNil } from '../../util/isNotNil'; import { isNotNil } from '../../util/isNotNil';
import { PanelType } from '../../types/Panels'; import { PanelType } from '../../types/Panels';
import { startConversation } from '../../util/startConversation'; import { startConversation } from '../../util/startConversation';
import { getMessageSentTimestamp } from '../../util/getMessageSentTimestamp';
import { UUIDKind } from '../../types/UUID'; import { UUIDKind } from '../../types/UUID';
import { removeLinkPreview } from '../../services/LinkPreview'; import { removeLinkPreview } from '../../services/LinkPreview';
import type { import type {
@ -1822,7 +1823,7 @@ export const markViewed = (messageId: string): void => {
const senderE164 = message.get('source'); const senderE164 = message.get('source');
const senderUuid = message.get('sourceUuid'); const senderUuid = message.get('sourceUuid');
const timestamp = message.get('sent_at'); const timestamp = getMessageSentTimestamp(message.attributes, { log });
message.set(messageUpdaterMarkViewed(message.attributes, Date.now())); message.set(messageUpdaterMarkViewed(message.attributes, Date.now()));
@ -2985,7 +2986,7 @@ function deleteMessagesForEveryone(
await sendDeleteForEveryoneMessage(conversation.attributes, { await sendDeleteForEveryoneMessage(conversation.attributes, {
id: message.id, id: message.id,
timestamp: message.get('sent_at'), timestamp: getMessageSentTimestamp(message.attributes, { log }),
}); });
} catch (error) { } catch (error) {
hasError = true; hasError = true;

View file

@ -0,0 +1,42 @@
// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { MessageAttributesType } from '../model-types.d';
import type { LoggerType } from '../types/Logging';
import { assertDev } from './assert';
export type GetMessageSentTimestampOptionsType = Readonly<{
includeEdits?: boolean;
log: LoggerType;
}>;
export function getMessageSentTimestamp(
{
editMessageTimestamp,
sent_at: sentAt,
timestamp,
}: Pick<
MessageAttributesType,
'editMessageTimestamp' | 'sent_at' | 'timestamp'
>,
{ includeEdits = true, log }: GetMessageSentTimestampOptionsType
): number {
if (includeEdits && editMessageTimestamp) {
return editMessageTimestamp;
}
if (sentAt) {
return sentAt;
}
if (timestamp) {
log.error('message lacked sent_at. Falling back to timestamp');
return timestamp;
}
assertDev(
false,
'message lacked sent_at and timestamp. Falling back to current time'
);
return Date.now();
}

View file

@ -0,0 +1,17 @@
// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { MessageAttributesType } from '../model-types.d';
export function getMessageSentTimestampSet({
sent_at: sentAt,
editHistory,
}: Pick<
MessageAttributesType,
'sent_at' | 'editHistory'
>): ReadonlySet<number> {
return new Set([
sentAt,
...(editHistory?.map(({ timestamp }) => timestamp) ?? []),
]);
}

View file

@ -240,7 +240,7 @@ export async function handleEditMessage(
window.Whisper.deliveryReceiptQueue.add(() => { window.Whisper.deliveryReceiptQueue.add(() => {
window.Whisper.deliveryReceiptBatcher.add({ window.Whisper.deliveryReceiptBatcher.add({
messageId: mainMessage.id, messageId: mainMessage.id,
conversationId: editAttributes.fromId, conversationId: editAttributes.conversationId,
senderE164: editAttributes.message.source, senderE164: editAttributes.message.source,
senderUuid: editAttributes.message.sourceUuid, senderUuid: editAttributes.message.sourceUuid,
timestamp: editAttributes.message.timestamp, timestamp: editAttributes.message.timestamp,
@ -263,7 +263,7 @@ export async function handleEditMessage(
mainMessageModel.attributes, mainMessageModel.attributes,
window.textsecure.storage.user.getCheckedUuid().toString(), window.textsecure.storage.user.getCheckedUuid().toString(),
{ {
fromId: editAttributes.fromId, conversationId: editAttributes.conversationId,
messageId: mainMessage.id, messageId: mainMessage.id,
readStatus, readStatus,
sentAt: upgradedEditedMessageData.timestamp, sentAt: upgradedEditedMessageData.timestamp,

View file

@ -14,8 +14,10 @@ import { getContact } from '../messages/helpers';
import { getQuoteBodyText } from './getQuoteBodyText'; import { getQuoteBodyText } from './getQuoteBodyText';
import { isGIF } from '../types/Attachment'; import { isGIF } from '../types/Attachment';
import { isGiftBadge, isTapToView } from '../state/selectors/message'; import { isGiftBadge, isTapToView } from '../state/selectors/message';
import * as log from '../logging/log';
import { map, take, collect } from './iterables'; import { map, take, collect } from './iterables';
import { strictAssert } from './assert'; import { strictAssert } from './assert';
import { getMessageSentTimestamp } from './getMessageSentTimestamp';
export async function makeQuote( export async function makeQuote(
quotedMessage: MessageAttributesType quotedMessage: MessageAttributesType
@ -27,14 +29,13 @@ export async function makeQuote(
const { const {
attachments, attachments,
bodyRanges, bodyRanges,
editMessageTimestamp,
id: messageId, id: messageId,
payment, payment,
preview, preview,
sticker, sticker,
} = quotedMessage; } = quotedMessage;
const quoteId = editMessageTimestamp || quotedMessage.sent_at; const quoteId = getMessageSentTimestamp(quotedMessage, { log });
return { return {
authorUuid: contact.get('uuid'), authorUuid: contact.get('uuid'),

View file

@ -45,7 +45,7 @@ export async function markConversationRead(
includeStoryReplies: !isGroup(conversationAttrs), includeStoryReplies: !isGroup(conversationAttrs),
}), }),
window.Signal.Data.getUnreadEditedMessagesAndMarkRead({ window.Signal.Data.getUnreadEditedMessagesAndMarkRead({
fromId: conversationId, conversationId,
newestUnreadAt, newestUnreadAt,
}), }),
window.Signal.Data.getUnreadReactionsAndMarkRead({ window.Signal.Data.getUnreadReactionsAndMarkRead({

View file

@ -30,6 +30,7 @@ import { isSignalConversation } from './isSignalConversation';
import { strictAssert } from './assert'; import { strictAssert } from './assert';
import { timeAndLogIfTooLong } from './timeAndLogIfTooLong'; import { timeAndLogIfTooLong } from './timeAndLogIfTooLong';
import { makeQuote } from './makeQuote'; import { makeQuote } from './makeQuote';
import { getMessageSentTimestamp } from './getMessageSentTimestamp';
const SEND_REPORT_THRESHOLD_MS = 25; const SEND_REPORT_THRESHOLD_MS = 25;
@ -82,9 +83,12 @@ export async function sendEditedMessage(
} }
const timestamp = Date.now(); const timestamp = Date.now();
const targetSentTimestamp = const targetSentTimestamp = getMessageSentTimestamp(
targetMessage.attributes.editMessageTimestamp ?? targetMessage.attributes,
targetMessage.attributes.timestamp; {
log,
}
);
log.info(`${idLog}: edited(${timestamp}) original(${targetSentTimestamp})`); log.info(`${idLog}: edited(${timestamp}) original(${targetSentTimestamp})`);