mirror of
https://github.com/signalapp/Signal-Desktop.git
synced 2026-04-17 23:34:14 +01:00
Send viewed receipt for view-once opens
This commit is contained in:
@@ -14,11 +14,19 @@ import * as Errors from '../types/errors.std.js';
|
||||
import { createLogger } from '../logging/log.std.js';
|
||||
import { isValidTapToView } from '../util/isValidTapToView.std.js';
|
||||
import { getMessageIdForLogging } from '../util/idForLogging.preload.js';
|
||||
import { getMessageSentTimestamp } from '../util/getMessageSentTimestamp.std.js';
|
||||
import { eraseMessageContents } from '../util/cleanup.preload.js';
|
||||
import { getSource, getSourceServiceId } from '../messages/sources.preload.js';
|
||||
import { isAciString } from '../util/isAciString.std.js';
|
||||
import { viewOnceOpenJobQueue } from '../jobs/viewOnceOpenJobQueue.preload.js';
|
||||
import { drop } from '../util/drop.std.js';
|
||||
import { isIncoming } from '../messages/helpers.std.js';
|
||||
import {
|
||||
conversationJobQueue,
|
||||
conversationQueueJobEnum,
|
||||
} from '../jobs/conversationJobQueue.preload.js';
|
||||
import { ReceiptType } from '../types/Receipt.std.js';
|
||||
import { isDirectConversation } from '../util/whatTypeOfConversation.dom.js';
|
||||
|
||||
const log = createLogger('MessageUpdater');
|
||||
|
||||
@@ -99,12 +107,39 @@ export async function markViewOnceMessageViewed(
|
||||
if (!fromSync) {
|
||||
const senderE164 = getSource(message.attributes);
|
||||
const senderAci = getSourceServiceId(message.attributes);
|
||||
const timestamp = message.get('sent_at');
|
||||
const timestamp = getMessageSentTimestamp(message.attributes, { log });
|
||||
|
||||
if (senderAci === undefined || !isAciString(senderAci)) {
|
||||
throw new Error('markViewOnceMessageViewed: senderAci is undefined');
|
||||
}
|
||||
|
||||
// Send viewed receipt to sender for incoming view-once messages
|
||||
if (isIncoming(message.attributes)) {
|
||||
const conversationId = message.get('conversationId');
|
||||
const conversation = window.ConversationController.get(conversationId);
|
||||
const isDirectConversationValue = conversation
|
||||
? isDirectConversation(conversation.attributes)
|
||||
: true;
|
||||
|
||||
drop(
|
||||
conversationJobQueue.add({
|
||||
type: conversationQueueJobEnum.enum.Receipts,
|
||||
conversationId,
|
||||
receiptsType: ReceiptType.Viewed,
|
||||
receipts: [
|
||||
{
|
||||
messageId: message.id,
|
||||
conversationId,
|
||||
senderE164,
|
||||
senderAci,
|
||||
timestamp,
|
||||
isDirectConversation: isDirectConversationValue,
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (window.ConversationController.areWePrimaryDevice()) {
|
||||
log.warn(
|
||||
'markViewOnceMessageViewed: We are primary device; not sending view once open sync'
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
type Device,
|
||||
type Group,
|
||||
PrimaryDevice,
|
||||
type Proto,
|
||||
Proto,
|
||||
StorageState,
|
||||
} from '@signalapp/mock-server';
|
||||
import { assert } from 'chai';
|
||||
@@ -30,6 +30,30 @@ export function bufferToUuid(buffer: Buffer): string {
|
||||
].join('-');
|
||||
}
|
||||
|
||||
function isProfileKeyUpdate(flags: number | null | undefined): boolean {
|
||||
if (flags == null) {
|
||||
return false;
|
||||
}
|
||||
// eslint-disable-next-line no-bitwise
|
||||
return (flags & Proto.DataMessage.Flags.PROFILE_KEY_UPDATE) !== 0;
|
||||
}
|
||||
|
||||
export async function waitForNonProfileKeyUpdateMessage(
|
||||
device: PrimaryDevice,
|
||||
{ maxAttempts = 5 }: { maxAttempts?: number } = {}
|
||||
): Promise<Awaited<ReturnType<PrimaryDevice['waitForMessage']>>> {
|
||||
for (let i = 0; i < maxAttempts; i += 1) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const message = await device.waitForMessage();
|
||||
if (isProfileKeyUpdate(message.dataMessage.flags)) {
|
||||
debug('Skipping profile key update');
|
||||
continue;
|
||||
}
|
||||
return message;
|
||||
}
|
||||
throw new Error(`No message with body after ${maxAttempts} attempts`);
|
||||
}
|
||||
|
||||
export async function typeIntoInput(
|
||||
input: Locator,
|
||||
additionalText: string,
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
expectSystemMessages,
|
||||
typeIntoInput,
|
||||
waitForEnabledComposer,
|
||||
waitForNonProfileKeyUpdateMessage,
|
||||
} from '../helpers.node.js';
|
||||
|
||||
export const debug = createDebug('mock:test:messaging');
|
||||
@@ -27,30 +28,6 @@ const IdentifierType = Proto.ManifestRecord.Identifier.Type;
|
||||
|
||||
const DAY = 24 * 3600;
|
||||
|
||||
function isProfileKeyUpdate(flags: number | null | undefined): boolean {
|
||||
if (flags == null) {
|
||||
return false;
|
||||
}
|
||||
// eslint-disable-next-line no-bitwise
|
||||
return (flags & Proto.DataMessage.Flags.PROFILE_KEY_UPDATE) !== 0;
|
||||
}
|
||||
|
||||
async function waitForNonProfileKeyUpdateMessage(
|
||||
device: PrimaryDevice
|
||||
): Promise<{ body: string; dataMessage: Proto.IDataMessage }> {
|
||||
const maxAttempts = 5;
|
||||
for (let i = 0; i < maxAttempts; i += 1) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const message = await device.waitForMessage();
|
||||
if (isProfileKeyUpdate(message.dataMessage.flags)) {
|
||||
debug('Skipping profile key update');
|
||||
continue;
|
||||
}
|
||||
return message;
|
||||
}
|
||||
throw new Error(`No message with body after ${maxAttempts} attempts`);
|
||||
}
|
||||
|
||||
describe('messaging/expireTimerVersion', function (this: Mocha.Suite) {
|
||||
this.timeout(durations.MINUTE);
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
acceptConversation,
|
||||
expectSystemMessages,
|
||||
typeIntoInput,
|
||||
waitForNonProfileKeyUpdateMessage,
|
||||
waitForEnabledComposer,
|
||||
} from '../helpers.node.js';
|
||||
|
||||
@@ -376,7 +377,8 @@ describe('pnp/PNI Signature', function (this: Mocha.Suite) {
|
||||
|
||||
debug('Wait for a ACI message');
|
||||
{
|
||||
const { source, body, serviceIdKind } = await stranger.waitForMessage();
|
||||
const { source, body, serviceIdKind } =
|
||||
await waitForNonProfileKeyUpdateMessage(stranger);
|
||||
|
||||
assert.strictEqual(source, desktop, 'ACI message has valid source');
|
||||
assert.strictEqual(body, 'Hello ACI', 'ACI message has valid body');
|
||||
|
||||
Reference in New Issue
Block a user