diff --git a/ts/background.preload.ts b/ts/background.preload.ts index cde3da358a..cbe3289b53 100644 --- a/ts/background.preload.ts +++ b/ts/background.preload.ts @@ -2495,6 +2495,8 @@ export async function startApp(): Promise { pinnedByAci: data.sourceAci, sentAtTimestamp: data.timestamp, receivedAtTimestamp: data.receivedAtDate, + expireTimer: data.message.expireTimer, + expirationStartTimestamp: null, }); confirm(); return; @@ -3003,6 +3005,8 @@ export async function startApp(): Promise { pinnedByAci: sourceServiceId, sentAtTimestamp: data.timestamp, receivedAtTimestamp: data.receivedAtDate, + expireTimer: data.message.expireTimer, + expirationStartTimestamp: data.expirationStartTimestamp ?? null, }); confirm(); return; diff --git a/ts/jobs/conversationJobQueue.preload.ts b/ts/jobs/conversationJobQueue.preload.ts index 12460605b0..dee724de04 100644 --- a/ts/jobs/conversationJobQueue.preload.ts +++ b/ts/jobs/conversationJobQueue.preload.ts @@ -209,6 +209,7 @@ const pinMessageJobDataSchema = z.object({ targetAuthorAci: aciSchema, targetSentTimestamp: z.number(), pinDurationSeconds: z.number().nullable(), + pinnedAt: z.number(), }); export type PinMessageJobData = z.infer; @@ -290,6 +291,7 @@ const unpinMessageJobDataSchema = z.object({ targetMessageId: z.string(), targetAuthorAci: aciSchema, targetSentTimestamp: z.number(), + unpinnedAt: z.number(), }); export type UnpinMessageJobData = z.infer; diff --git a/ts/jobs/helpers/createSendMessageJob.preload.ts b/ts/jobs/helpers/createSendMessageJob.preload.ts index 8ec99b1bd3..2f8e190f57 100644 --- a/ts/jobs/helpers/createSendMessageJob.preload.ts +++ b/ts/jobs/helpers/createSendMessageJob.preload.ts @@ -27,9 +27,9 @@ export type SendMessageJobOptions = Readonly<{ sendType: SendTypesType; getMessageId: (data: Data) => string | null; getMessageOptions: ( - data: Data, - jobTimestamp: number + data: Data ) => Omit; + getExpirationStartTimestamp: (data: Data) => number | null; }>; export function createSendMessageJob( @@ -40,7 +40,13 @@ export function createSendMessageJob( job: ConversationQueueJobBundle, data: Data ): Promise { - const { sendName, sendType, getMessageId, getMessageOptions } = options; + const { + sendName, + sendType, + getMessageId, + getMessageOptions, + getExpirationStartTimestamp, + } = options; const logId = `${sendName}(${conversation.idForLogging()}/${job.timestamp})`; const log = job.log.child(logId); @@ -71,7 +77,12 @@ export function createSendMessageJob( } const messageId = getMessageId(data); - const messageOptions = getMessageOptions(data, job.timestamp); + const messageOptions = { + ...getMessageOptions(data), + expireTimer: conversation.get('expireTimer'), + expireTimerVersion: conversation.getExpireTimerVersion(), + }; + const expirationStartTimestamp = getExpirationStartTimestamp(data); try { if (recipientServiceIdsWithoutMe.length === 0) { @@ -96,7 +107,7 @@ export function createSendMessageJob( timestamp: job.timestamp, destinationE164: conversation.get('e164'), destinationServiceId: conversation.getServiceId(), - expirationStartTimestamp: null, + expirationStartTimestamp, isUpdate: false, options: sendOptions, urgent: false, @@ -128,7 +139,7 @@ export function createSendMessageJob( send: sender => { return sender.sendMessageToServiceId({ serviceId: recipientServiceId, - messageOptions: getMessageOptions(data, job.timestamp), + messageOptions, groupId: undefined, contentHint: ContentHint.Resendable, options: sendOptions, @@ -138,6 +149,7 @@ export function createSendMessageJob( }, sendType, timestamp: job.timestamp, + expirationStartTimestamp, }); } ); @@ -162,8 +174,8 @@ export function createSendMessageJob( abortSignal, contentHint: ContentHint.Resendable, groupSendOptions: { + ...messageOptions, groupV2: groupV2Info, - ...getMessageOptions(data, job.timestamp), }, messageId: messageId ?? undefined, sendOptions, @@ -174,6 +186,7 @@ export function createSendMessageJob( }, sendType, timestamp: job.timestamp, + expirationStartTimestamp, }); } ); diff --git a/ts/jobs/helpers/sendDeleteForEveryone.preload.ts b/ts/jobs/helpers/sendDeleteForEveryone.preload.ts index f96c1e9338..e065ab23d4 100644 --- a/ts/jobs/helpers/sendDeleteForEveryone.preload.ts +++ b/ts/jobs/helpers/sendDeleteForEveryone.preload.ts @@ -213,6 +213,7 @@ export async function sendDeleteForEveryone( }), sendType, timestamp, + expirationStartTimestamp: null, }); await updateMessageWithSuccessfulSends(message); @@ -252,6 +253,7 @@ export async function sendDeleteForEveryone( }), sendType, timestamp, + expirationStartTimestamp: null, }); await updateMessageWithSuccessfulSends(message); diff --git a/ts/jobs/helpers/sendDirectExpirationTimerUpdate.preload.ts b/ts/jobs/helpers/sendDirectExpirationTimerUpdate.preload.ts index 58ef6d7a60..9edfa54553 100644 --- a/ts/jobs/helpers/sendDirectExpirationTimerUpdate.preload.ts +++ b/ts/jobs/helpers/sendDirectExpirationTimerUpdate.preload.ts @@ -153,6 +153,7 @@ export async function sendDirectExpirationTimerUpdate( }), sendType, timestamp, + expirationStartTimestamp: null, }); } } catch (error: unknown) { diff --git a/ts/jobs/helpers/sendGroupCallUpdate.preload.ts b/ts/jobs/helpers/sendGroupCallUpdate.preload.ts index d58c51da88..81ef0f607f 100644 --- a/ts/jobs/helpers/sendGroupCallUpdate.preload.ts +++ b/ts/jobs/helpers/sendGroupCallUpdate.preload.ts @@ -97,6 +97,7 @@ export async function sendGroupCallUpdate( ), sendType, timestamp, + expirationStartTimestamp: null, }); } catch (error: unknown) { await handleMultipleSendErrors({ diff --git a/ts/jobs/helpers/sendGroupUpdate.preload.ts b/ts/jobs/helpers/sendGroupUpdate.preload.ts index de4d49ced1..9be62ab550 100644 --- a/ts/jobs/helpers/sendGroupUpdate.preload.ts +++ b/ts/jobs/helpers/sendGroupUpdate.preload.ts @@ -117,6 +117,7 @@ export async function sendGroupUpdate( }), sendType, timestamp, + expirationStartTimestamp: null, }) ); } catch (error: unknown) { diff --git a/ts/jobs/helpers/sendPinMessage.preload.ts b/ts/jobs/helpers/sendPinMessage.preload.ts index e2f2e48222..3ad7300cbd 100644 --- a/ts/jobs/helpers/sendPinMessage.preload.ts +++ b/ts/jobs/helpers/sendPinMessage.preload.ts @@ -9,9 +9,9 @@ export const sendPinMessage = createSendMessageJob({ getMessageId(data) { return data.targetMessageId; }, - getMessageOptions(data, jobTimestamp) { + getMessageOptions(data) { return { - timestamp: jobTimestamp, + timestamp: data.pinnedAt, pinMessage: { targetAuthorAci: data.targetAuthorAci, targetSentTimestamp: data.targetSentTimestamp, @@ -19,4 +19,7 @@ export const sendPinMessage = createSendMessageJob({ }, }; }, + getExpirationStartTimestamp(data) { + return data.pinnedAt; + }, }); diff --git a/ts/jobs/helpers/sendPollTerminate.preload.ts b/ts/jobs/helpers/sendPollTerminate.preload.ts index 54f13a9beb..9111c18431 100644 --- a/ts/jobs/helpers/sendPollTerminate.preload.ts +++ b/ts/jobs/helpers/sendPollTerminate.preload.ts @@ -133,6 +133,7 @@ export async function sendPollTerminate( }), sendType: 'pollTerminate', timestamp, + expirationStartTimestamp: null, }); await markTerminateSuccess(pollMessage, jobLog); diff --git a/ts/jobs/helpers/sendUnpinMessage.preload.ts b/ts/jobs/helpers/sendUnpinMessage.preload.ts index 5def2ff373..d44a9aae82 100644 --- a/ts/jobs/helpers/sendUnpinMessage.preload.ts +++ b/ts/jobs/helpers/sendUnpinMessage.preload.ts @@ -10,13 +10,16 @@ export const sendUnpinMessage = createSendMessageJob({ getMessageId(data) { return data.targetMessageId; }, - getMessageOptions(data, jobTimestamp) { + getMessageOptions(data) { return { - timestamp: jobTimestamp, + timestamp: data.unpinnedAt, unpinMessage: { targetAuthorAci: data.targetAuthorAci, targetSentTimestamp: data.targetSentTimestamp, }, }; }, + getExpirationStartTimestamp(data) { + return data.unpinnedAt; + }, }); diff --git a/ts/messageModifiers/PinnedMessages.preload.ts b/ts/messageModifiers/PinnedMessages.preload.ts index d85902d937..2fec799352 100644 --- a/ts/messageModifiers/PinnedMessages.preload.ts +++ b/ts/messageModifiers/PinnedMessages.preload.ts @@ -30,6 +30,8 @@ export type PinnedMessageAddProps = Readonly<{ pinnedByAci: AciString; sentAtTimestamp: number; receivedAtTimestamp: number; + expireTimer: DurationInSeconds | null; + expirationStartTimestamp: number | null; }>; export type PinnedMessageRemoveProps = Readonly<{ @@ -125,6 +127,8 @@ export async function onPinnedMessageAdd( senderAci: props.pinnedByAci, sentAtTimestamp: props.sentAtTimestamp, receivedAtTimestamp: props.receivedAtTimestamp, + expireTimer: props.expireTimer, + expirationStartTimestamp: props.expirationStartTimestamp, }); } diff --git a/ts/models/conversations.preload.ts b/ts/models/conversations.preload.ts index 3876a624fc..62fadb8d32 100644 --- a/ts/models/conversations.preload.ts +++ b/ts/models/conversations.preload.ts @@ -3567,6 +3567,8 @@ export class ConversationModel { senderAci: AciString; sentAtTimestamp: number; receivedAtTimestamp: number; + expireTimer: DurationInSeconds | null; + expirationStartTimestamp: number | null; }): Promise { const ourAci = itemStorage.user.getCheckedAci(); const senderIsMe = params.senderAci === ourAci; @@ -3581,8 +3583,8 @@ export class ConversationModel { readStatus: senderIsMe ? ReadStatus.Read : ReadStatus.Unread, seenStatus: senderIsMe ? SeenStatus.Seen : SeenStatus.Unseen, sourceServiceId: params.senderAci, - expireTimer: this.get('expireTimer'), - expirationStartTimestamp: senderIsMe ? params.sentAtTimestamp : null, + expireTimer: params.expireTimer ?? undefined, + expirationStartTimestamp: params.expirationStartTimestamp, pinMessage: params.pinMessage, }); diff --git a/ts/state/ducks/conversations.preload.ts b/ts/state/ducks/conversations.preload.ts index ac9c006f20..d17dbe3b32 100644 --- a/ts/state/ducks/conversations.preload.ts +++ b/ts/state/ducks/conversations.preload.ts @@ -5199,15 +5199,17 @@ function onPinnedMessageAdd( ); strictAssert(targetConversation != null, 'Missing target conversation'); + const pinnedAt = Date.now(); + await conversationJobQueue.add({ type: conversationQueueJobEnum.enum.PinMessage, ...target, pinDurationSeconds, + pinnedAt, }); const pinnedMessagesLimit = getPinnedMessagesLimit(); - const pinnedAt = Date.now(); const expiresAt = getPinnedMessageExpiresAt(pinnedAt, pinDurationSeconds); await DataWriter.appendPinnedMessage(pinnedMessagesLimit, { @@ -5226,6 +5228,8 @@ function onPinnedMessageAdd( senderAci: itemStorage.user.getCheckedAci(), sentAtTimestamp: pinnedAt, receivedAtTimestamp: pinnedAt, + expireTimer: targetConversation.get('expireTimer') ?? null, + expirationStartTimestamp: pinnedAt, }); dispatch(onPinnedMessagesChanged(target.conversationId)); @@ -5238,6 +5242,7 @@ function onPinnedMessageRemove(targetMessageId: string): StateThunk { await conversationJobQueue.add({ type: conversationQueueJobEnum.enum.UnpinMessage, ...target, + unpinnedAt: Date.now(), }); await DataWriter.deletePinnedMessageByMessageId(targetMessageId); drop(pinnedMessagesCleanupService.trigger('onPinnedMessageRemove')); diff --git a/ts/util/wrapWithSyncMessageSend.preload.ts b/ts/util/wrapWithSyncMessageSend.preload.ts index 8672829ebd..d19ac6cdb0 100644 --- a/ts/util/wrapWithSyncMessageSend.preload.ts +++ b/ts/util/wrapWithSyncMessageSend.preload.ts @@ -25,6 +25,7 @@ export async function wrapWithSyncMessageSend({ send, sendType, timestamp, + expirationStartTimestamp, }: { conversation: ConversationModel; logId: string; @@ -32,6 +33,7 @@ export async function wrapWithSyncMessageSend({ send: (sender: MessageSender) => Promise; sendType: SendTypesType; timestamp: number; + expirationStartTimestamp: number | null; }): Promise { const logId = `wrapWithSyncMessageSend(${parentLogId}, ${timestamp})`; @@ -84,7 +86,7 @@ export async function wrapWithSyncMessageSend({ destinationE164: conversation.get('e164'), destinationServiceId: conversation.getServiceId(), encodedDataMessage: dataMessage, - expirationStartTimestamp: null, + expirationStartTimestamp, options, timestamp, urgent: false,