// Copyright 2019 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import type { AciString } from '../types/ServiceId'; import type { MessageModel } from '../models/messages'; import * as Errors from '../types/errors'; import * as log from '../logging/log'; import { getMessageIdForLogging } from '../util/idForLogging'; export type ViewOnceOpenSyncAttributesType = { removeFromMessageReceiverCache: () => unknown; source?: string; sourceAci: AciString; timestamp: number; }; const viewOnceSyncs = new Map(); function remove(sync: ViewOnceOpenSyncAttributesType): void { viewOnceSyncs.delete(sync.timestamp); sync.removeFromMessageReceiverCache(); } export function forMessage( message: MessageModel ): ViewOnceOpenSyncAttributesType | null { const logId = `ViewOnceOpenSyncs.forMessage(${getMessageIdForLogging( message.attributes )})`; const viewOnceSyncValues = Array.from(viewOnceSyncs.values()); const syncBySourceServiceId = viewOnceSyncValues.find(item => { return ( item.sourceAci === message.get('sourceServiceId') && item.timestamp === message.get('sent_at') ); }); if (syncBySourceServiceId) { log.info(`${logId}: Found early view once open sync for message`); remove(syncBySourceServiceId); return syncBySourceServiceId; } const syncBySource = viewOnceSyncValues.find(item => { return ( item.source === message.get('source') && item.timestamp === message.get('sent_at') ); }); if (syncBySource) { log.info(`${logId}: Found early view once open sync for message`); remove(syncBySource); return syncBySource; } return null; } export async function onSync( sync: ViewOnceOpenSyncAttributesType ): Promise { viewOnceSyncs.set(sync.timestamp, sync); const logId = `ViewOnceOpenSyncs.onSync(timestamp=${sync.timestamp})`; try { const messages = await window.Signal.Data.getMessagesBySentAt( sync.timestamp ); const found = messages.find(item => { const itemSourceAci = item.sourceServiceId; const syncSourceAci = sync.sourceAci; const itemSource = item.source; const syncSource = sync.source; return Boolean( (itemSourceAci && syncSourceAci && itemSourceAci === syncSourceAci) || (itemSource && syncSource && itemSource === syncSource) ); }); const syncSource = sync.source; const syncSourceAci = sync.sourceAci; const syncTimestamp = sync.timestamp; const wasMessageFound = Boolean(found); log.info(`${logId} receive:`, { syncSource, syncSourceAci, syncTimestamp, wasMessageFound, }); if (!found) { return; } const message = window.MessageController.register(found.id, found); await message.markViewOnceMessageViewed({ fromSync: true }); viewOnceSyncs.delete(sync.timestamp); sync.removeFromMessageReceiverCache(); } catch (error) { log.error(`${logId} error:`, Errors.toLogFormat(error)); } }