From bc8fbb72690694998679d244225dbc95bce577dd Mon Sep 17 00:00:00 2001 From: automated-signal <37887102+automated-signal@users.noreply.github.com> Date: Fri, 10 Oct 2025 16:53:26 -0500 Subject: [PATCH] Remove window.getAccountManager Co-authored-by: Fedor Indutny <79877362+indutny-signal@users.noreply.github.com> --- ts/background.ts | 57 ++++++++++++----------------- ts/messages/handleDataMessage.ts | 8 +++- ts/state/ducks/installer.ts | 4 +- ts/state/smart/App.tsx | 5 +-- ts/textsecure/AccountManager.ts | 2 + ts/textsecure/MessageReceiver.ts | 4 +- ts/textsecure/SendMessage.ts | 2 +- ts/textsecure/UpdateKeysListener.ts | 3 +- ts/util/deliveryReceipt.ts | 42 +++++++++++++++++++++ ts/util/handleEditMessage.ts | 8 +++- ts/util/onDeviceNameChangeSync.ts | 5 +-- ts/util/sendToGroup.ts | 2 +- ts/window.d.ts | 7 ---- ts/windows/main/phase1-ipc.ts | 33 +---------------- 14 files changed, 90 insertions(+), 92 deletions(-) create mode 100644 ts/util/deliveryReceipt.ts diff --git a/ts/background.ts b/ts/background.ts index f704032b83..3a1dc5dc31 100644 --- a/ts/background.ts +++ b/ts/background.ts @@ -31,6 +31,7 @@ import { ThemeType } from './types/Util.js'; import * as durations from './util/durations/index.js'; import { drop } from './util/drop.js'; import { explodePromise } from './util/explodePromise.js'; +import { deliveryReceiptQueue } from './util/deliveryReceipt.js'; import type { ExplodePromiseResultType } from './util/explodePromise.js'; import { isWindowDragElement } from './util/isWindowDragElement.js'; import { assertDev, strictAssert } from './util/assert.js'; @@ -148,7 +149,7 @@ import { reportMessage, unregisterRequestHandler, } from './textsecure/WebAPI.js'; -import AccountManager from './textsecure/AccountManager.js'; +import { accountManager } from './textsecure/AccountManager.js'; import * as KeyChangeListener from './textsecure/KeyChangeListener.js'; import { UpdateKeysListener } from './textsecure/UpdateKeysListener.js'; import { isGroup } from './util/whatTypeOfConversation.js'; @@ -353,8 +354,6 @@ export async function startApp(): Promise { const onRetryRequestQueue = new PQueue({ concurrency: 1 }); onRetryRequestQueue.pause(); - window.Whisper.deliveryReceiptQueue.pause(); - if (window.platform === 'darwin') { window.addEventListener('dblclick', (event: Event) => { const target = event.target as HTMLElement; @@ -421,8 +420,8 @@ export async function startApp(): Promise { 'lowKeys', throttle( async () => { - await window.getAccountManager().maybeUpdateKeys(ServiceIdKind.ACI); - await window.getAccountManager().maybeUpdateKeys(ServiceIdKind.PNI); + await accountManager.maybeUpdateKeys(ServiceIdKind.ACI); + await accountManager.maybeUpdateKeys(ServiceIdKind.PNI); }, durations.MINUTE, { trailing: true, leading: false } @@ -447,34 +446,25 @@ export async function startApp(): Promise { return getSocketStatus(); }; - let accountManager: AccountManager; - window.getAccountManager = () => { - if (accountManager) { - return accountManager; - } + accountManager.addEventListener('startRegistration', () => { + pauseProcessing('startRegistration'); + // We should already be logged out, but this ensures that the next time we connect + // to the auth socket it is from newly-registered credentials + drop(logout()); + authSocketConnectCount = 0; - accountManager = new AccountManager(); - accountManager.addEventListener('startRegistration', () => { - pauseProcessing('startRegistration'); - // We should already be logged out, but this ensures that the next time we connect - // to the auth socket it is from newly-registered credentials - drop(logout()); - authSocketConnectCount = 0; + backupReady.reject(new Error('startRegistration')); + backupReady = explodePromise(); + registrationCompleted = explodePromise(); + }); - backupReady.reject(new Error('startRegistration')); - backupReady = explodePromise(); - registrationCompleted = explodePromise(); - }); + accountManager.addEventListener('endRegistration', () => { + window.Whisper.events.emit('userChanged', false); - accountManager.addEventListener('endRegistration', () => { - window.Whisper.events.emit('userChanged', false); - - drop(itemStorage.put('postRegistrationSyncsStatus', 'incomplete')); - registrationCompleted?.resolve(); - drop(Registration.markDone()); - }); - return accountManager; - }; + drop(itemStorage.put('postRegistrationSyncsStatus', 'incomplete')); + registrationCompleted?.resolve(); + drop(Registration.markDone()); + }); const cancelInitializationMessage = setAppLoadingScreenMessage( undefined, @@ -1826,9 +1816,8 @@ export async function startApp(): Promise { drop(StorageService.reprocessUnknownFields()); - const manager = window.getAccountManager(); await Promise.all([ - manager.maybeUpdateDeviceName(), + accountManager.maybeUpdateDeviceName(), itemStorage.user.removeSignalingKey(), ]); } catch (e) { @@ -1912,7 +1901,7 @@ export async function startApp(): Promise { lightSessionResetQueue.pause(); onDecryptionErrorQueue.pause(); onRetryRequestQueue.pause(); - window.Whisper.deliveryReceiptQueue.pause(); + deliveryReceiptQueue.pause(); notificationService.disable(); } @@ -1923,7 +1912,7 @@ export async function startApp(): Promise { lightSessionResetQueue.start(); onDecryptionErrorQueue.start(); onRetryRequestQueue.start(); - window.Whisper.deliveryReceiptQueue.start(); + deliveryReceiptQueue.start(); notificationService.enable(); } diff --git a/ts/messages/handleDataMessage.ts b/ts/messages/handleDataMessage.ts index e9382d4b3b..608c72c4c4 100644 --- a/ts/messages/handleDataMessage.ts +++ b/ts/messages/handleDataMessage.ts @@ -12,6 +12,10 @@ import { isStory } from './helpers.js'; import { getAuthor } from './sources.js'; import { messageHasPaymentEvent } from './payments.js'; import { getMessageIdForLogging } from '../util/idForLogging.js'; +import { + deliveryReceiptQueue, + deliveryReceiptBatcher, +} from '../util/deliveryReceipt.js'; import { getSenderIdentifier } from '../util/getSenderIdentifier.js'; import { isNormalNumber } from '../util/isNormalNumber.js'; import { upgradeMessageSchema } from '../util/migrations.js'; @@ -381,12 +385,12 @@ export async function handleDataMessage( // processing incoming messages to start sending outgoing delivery receipts. // The queue can be paused easily. drop( - window.Whisper.deliveryReceiptQueue.add(() => { + deliveryReceiptQueue.add(() => { strictAssert( isAciString(sourceServiceId), 'Incoming message must be from ACI' ); - window.Whisper.deliveryReceiptBatcher.add({ + deliveryReceiptBatcher.add({ messageId, conversationId, senderE164: source, diff --git a/ts/state/ducks/installer.ts b/ts/state/ducks/installer.ts index 640ede3235..cf847614fb 100644 --- a/ts/state/ducks/installer.ts +++ b/ts/state/ducks/installer.ts @@ -24,6 +24,7 @@ import { EventKind as ProvisionEventKind, type EnvelopeType as ProvisionEnvelopeType, } from '../../textsecure/Provisioner.js'; +import { accountManager } from '../../textsecure/AccountManager.js'; import { getProvisioningResource } from '../../textsecure/WebAPI.js'; import type { BoundActionCreatorsMapObject } from '../../hooks/useBoundActions.js'; import { useBoundActions } from '../../hooks/useBoundActions.js'; @@ -320,9 +321,6 @@ function finishInstall({ cancelByBaton.get(baton)?.(); cancelByBaton.delete(baton); - const accountManager = window.getAccountManager(); - strictAssert(accountManager, 'Expected an account manager'); - if (isLinkAndSync) { dispatch({ type: SHOW_BACKUP_IMPORT }); } else { diff --git a/ts/state/smart/App.tsx b/ts/state/smart/App.tsx index a3e963224e..5b345549a8 100644 --- a/ts/state/smart/App.tsx +++ b/ts/state/smart/App.tsx @@ -3,6 +3,7 @@ import React, { memo } from 'react'; import { useSelector } from 'react-redux'; import { requestVerification as doRequestVerification } from '../../textsecure/WebAPI.js'; +import { accountManager } from '../../textsecure/AccountManager.js'; import type { VerificationTransport } from '../../types/VerificationTransport.js'; import { DataWriter } from '../../sql/Client.js'; import { App } from '../../components/App.js'; @@ -79,9 +80,7 @@ function registerSingleDevice( code: string, sessionId: string ): Promise { - return window - .getAccountManager() - .registerSingleDevice(number, code, sessionId); + return accountManager.registerSingleDevice(number, code, sessionId); } function readyForUpdates(): void { diff --git a/ts/textsecure/AccountManager.ts b/ts/textsecure/AccountManager.ts index 2e3c41f757..b6d231aff4 100644 --- a/ts/textsecure/AccountManager.ts +++ b/ts/textsecure/AccountManager.ts @@ -1405,3 +1405,5 @@ export default class AccountManager extends EventTarget { } } } + +export const accountManager = new AccountManager(); diff --git a/ts/textsecure/MessageReceiver.ts b/ts/textsecure/MessageReceiver.ts index e57e82a5c1..05831ee1ef 100644 --- a/ts/textsecure/MessageReceiver.ts +++ b/ts/textsecure/MessageReceiver.ts @@ -87,6 +87,7 @@ import EventTarget from './EventTarget.js'; import type { IncomingWebSocketRequest } from './WebsocketResources.js'; import { ServerRequestType } from './WebsocketResources.js'; import { type Storage } from './Storage.js'; +import { accountManager } from './AccountManager.js'; import { WarnOnlyError } from './Errors.js'; import * as Bytes from '../Bytes.js'; import type { @@ -3325,8 +3326,7 @@ export default class MessageReceiver } this.#pniIdentityKeyCheckRequired = false; - const manager = window.getAccountManager(); - await manager.setPni(updatedPni, { + await accountManager.setPni(updatedPni, { identityKeyPair, lastResortKyberPreKey: dropNull(lastResortKyberPreKey), signedPreKey, diff --git a/ts/textsecure/SendMessage.ts b/ts/textsecure/SendMessage.ts index 8b984c8bfb..845dcedb03 100644 --- a/ts/textsecure/SendMessage.ts +++ b/ts/textsecure/SendMessage.ts @@ -96,6 +96,7 @@ import { MAX_MESSAGE_COUNT } from '../util/deleteForMe.types.js'; import { isProtoBinaryEncodingEnabled } from '../util/isProtoBinaryEncodingEnabled.js'; import type { GroupSendToken } from '../types/GroupSendEndorsements.js'; import { itemStorage } from './Storage.js'; +import { accountManager } from './AccountManager.js'; const log = createLogger('SendMessage'); @@ -1057,7 +1058,6 @@ export class MessageSender { timestamp: number; urgent: boolean; }>): Promise { - const accountManager = window.getAccountManager(); try { if (accountManager.areKeysOutOfDate(ServiceIdKind.ACI)) { log.warn( diff --git a/ts/textsecure/UpdateKeysListener.ts b/ts/textsecure/UpdateKeysListener.ts index 0d23a2735b..4c41940730 100644 --- a/ts/textsecure/UpdateKeysListener.ts +++ b/ts/textsecure/UpdateKeysListener.ts @@ -10,6 +10,7 @@ import * as Errors from '../types/errors.js'; import { HTTPError } from '../types/HTTPError.js'; import { isOnline } from './WebAPI.js'; import { itemStorage } from './Storage.js'; +import { accountManager } from './AccountManager.js'; const log = createLogger('UpdateKeysListener'); @@ -54,8 +55,6 @@ export class UpdateKeysListener { async #run(): Promise { log.info('Updating keys...'); try { - const accountManager = window.getAccountManager(); - await accountManager.maybeUpdateKeys(ServiceIdKind.ACI); try { diff --git a/ts/util/deliveryReceipt.ts b/ts/util/deliveryReceipt.ts new file mode 100644 index 0000000000..a36097615b --- /dev/null +++ b/ts/util/deliveryReceipt.ts @@ -0,0 +1,42 @@ +// Copyright 2025 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +import PQueue from 'p-queue'; +import lodash from 'lodash'; + +import { + conversationJobQueue, + conversationQueueJobEnum, +} from '../jobs/conversationJobQueue.js'; +import { ReceiptType } from '../types/Receipt.js'; +import type { Receipt } from '../types/Receipt.js'; +import { MINUTE } from './durations/index.js'; +import { createBatcher } from './batcher.js'; + +const { groupBy } = lodash; + +export const deliveryReceiptQueue = new PQueue({ + concurrency: 1, + timeout: MINUTE * 30, +}); + +deliveryReceiptQueue.pause(); + +export const deliveryReceiptBatcher = createBatcher({ + name: 'deliveryReceiptBatcher', + wait: 500, + maxSize: 100, + processBatch: async deliveryReceipts => { + const groups = groupBy(deliveryReceipts, 'conversationId'); + await Promise.all( + Object.keys(groups).map(async conversationId => { + await conversationJobQueue.add({ + type: conversationQueueJobEnum.enum.Receipts, + conversationId, + receiptsType: ReceiptType.Delivery, + receipts: groups[conversationId], + }); + }) + ); + }, +}); diff --git a/ts/util/handleEditMessage.ts b/ts/util/handleEditMessage.ts index c43601a964..57129eaf65 100644 --- a/ts/util/handleEditMessage.ts +++ b/ts/util/handleEditMessage.ts @@ -14,6 +14,10 @@ import { ReadStatus } from '../messages/MessageReadStatus.js'; import { DataWriter } from '../sql/Client.js'; import { drop } from './drop.js'; import { upgradeMessageSchema } from './migrations.js'; +import { + deliveryReceiptQueue, + deliveryReceiptBatcher, +} from './deliveryReceipt.js'; import { cacheAttachmentBySignature, getCachedAttachmentBySignature, @@ -315,8 +319,8 @@ export async function handleEditMessage( // processing incoming messages to start sending outgoing delivery receipts. // The queue can be paused easily. drop( - window.Whisper.deliveryReceiptQueue.add(() => { - window.Whisper.deliveryReceiptBatcher.add({ + deliveryReceiptQueue.add(() => { + deliveryReceiptBatcher.add({ messageId: mainMessage.id, conversationId: editAttributes.conversationId, senderE164: editAttributes.message.source, diff --git a/ts/util/onDeviceNameChangeSync.ts b/ts/util/onDeviceNameChangeSync.ts index 8b6f343a70..f53c6e3a82 100644 --- a/ts/util/onDeviceNameChangeSync.ts +++ b/ts/util/onDeviceNameChangeSync.ts @@ -11,6 +11,7 @@ import { createLogger } from '../logging/log.js'; import { toLogFormat } from '../types/errors.js'; import { drop } from './drop.js'; import { itemStorage } from '../textsecure/Storage.js'; +import { accountManager } from '../textsecure/AccountManager.js'; const log = createLogger('onDeviceNameChangeSync'); @@ -66,9 +67,7 @@ async function fetchAndUpdateDeviceName() { let newName: string; try { - newName = await window - .getAccountManager() - .decryptDeviceName(newNameEncrypted); + newName = await accountManager.decryptDeviceName(newNameEncrypted); } catch (e) { const deviceNameWasEncrypted = itemStorage.user.getDeviceNameEncrypted(); log.error( diff --git a/ts/util/sendToGroup.ts b/ts/util/sendToGroup.ts index 6824553236..37fa29846c 100644 --- a/ts/util/sendToGroup.ts +++ b/ts/util/sendToGroup.ts @@ -21,6 +21,7 @@ import { padMessage, SenderCertificateMode, } from '../textsecure/OutgoingMessage.js'; +import { accountManager } from '../textsecure/AccountManager.js'; import { Address } from '../types/Address.js'; import { QualifiedAddress } from '../types/QualifiedAddress.js'; import * as Errors from '../types/errors.js'; @@ -198,7 +199,6 @@ export async function sendContentMessageToGroup( } = options; const logId = sendTarget.idForLogging(); - const accountManager = window.getAccountManager(); if (accountManager.areKeysOutOfDate(ServiceIdKind.ACI)) { log.warn(`${logId}: Keys are out of date; updating before send`); await accountManager.maybeUpdateKeys(ServiceIdKind.ACI); diff --git a/ts/window.d.ts b/ts/window.d.ts index 1ea07a177a..ce0e3d9834 100644 --- a/ts/window.d.ts +++ b/ts/window.d.ts @@ -6,18 +6,14 @@ import type EventEmitter from 'node:events'; import type { Store } from 'redux'; import type { SystemPreferences } from 'electron'; -import type PQueue from 'p-queue/dist.js'; import type { assert } from 'chai'; import type { MochaOptions } from 'mocha'; import type { IPCRequest as IPCChallengeRequest } from './challenge.js'; -import type AccountManager from './textsecure/AccountManager.js'; import type { OSType } from './util/os/shared.js'; import type { SystemThemeType, ThemeType } from './types/Util.js'; -import type { Receipt } from './types/Receipt.js'; import type { ConversationController } from './ConversationController.js'; import type { ReduxActions } from './state/types.js'; -import type { BatcherType } from './util/batcher.js'; import type { ScreenShareStatus } from './types/Calling.js'; import type { MessageCache } from './services/MessageCache.js'; import type { StateType } from './state/reducer.js'; @@ -140,7 +136,6 @@ declare global { interface Window { enterKeyboardMode: () => void; enterMouseMode: () => void; - getAccountManager: () => AccountManager; getAppInstance: () => string | undefined; getBuildCreation: () => number; getBuildExpiration: () => number; @@ -234,7 +229,5 @@ declare global { } export type WhisperType = { - deliveryReceiptQueue: PQueue; - deliveryReceiptBatcher: BatcherType; events: EventEmitter; }; diff --git a/ts/windows/main/phase1-ipc.ts b/ts/windows/main/phase1-ipc.ts index 760bf75e58..d08ed39415 100644 --- a/ts/windows/main/phase1-ipc.ts +++ b/ts/windows/main/phase1-ipc.ts @@ -5,7 +5,6 @@ import EventEmitter from 'node:events'; import { ipcRenderer as ipc } from 'electron'; import * as semver from 'semver'; import lodash, { throttle } from 'lodash'; -import PQueue from 'p-queue'; import type { IPCType } from '../../window.d.ts'; import { parseIntWithFallback } from '../../util/parseIntWithFallback.js'; @@ -28,18 +27,10 @@ import { UNAUTHENTICATED_CHANNEL_NAME } from '../../textsecure/SocketManager.js' import { isProduction } from '../../util/version.js'; import { ToastType } from '../../types/Toast.js'; import { ConversationController } from '../../ConversationController.js'; -import { createBatcher } from '../../util/batcher.js'; -import { ReceiptType } from '../../types/Receipt.js'; -import type { Receipt } from '../../types/Receipt.js'; -import { MINUTE } from '../../util/durations/index.js'; -import { - conversationJobQueue, - conversationQueueJobEnum, -} from '../../jobs/conversationJobQueue.js'; import { isEnabled } from '../../RemoteConfig.js'; import { itemStorage } from '../../textsecure/Storage.js'; -const { groupBy, mapValues } = lodash; +const { mapValues } = lodash; const log = createLogger('phase1-ipc'); @@ -62,28 +53,6 @@ window.RETRY_DELAY = false; window.Whisper = { events: new EventEmitter(), - deliveryReceiptQueue: new PQueue({ - concurrency: 1, - timeout: MINUTE * 30, - }), - deliveryReceiptBatcher: createBatcher({ - name: 'Whisper.deliveryReceiptBatcher', - wait: 500, - maxSize: 100, - processBatch: async deliveryReceipts => { - const groups = groupBy(deliveryReceipts, 'conversationId'); - await Promise.all( - Object.keys(groups).map(async conversationId => { - await conversationJobQueue.add({ - type: conversationQueueJobEnum.enum.Receipts, - conversationId, - receiptsType: ReceiptType.Delivery, - receipts: groups[conversationId], - }); - }) - ); - }, - }), }; window.ConversationController = new ConversationController(); window.platform = process.platform;