diff --git a/ts/background.ts b/ts/background.ts index 65bfdb78cb..bdc9da2f2f 100644 --- a/ts/background.ts +++ b/ts/background.ts @@ -38,6 +38,7 @@ import { assertDev, strictAssert } from './util/assert.js'; import { filter } from './util/iterables.js'; import { isNotNil } from './util/isNotNil.js'; import { areRemoteBackupsTurnedOn } from './util/isBackupEnabled.js'; +import { lightSessionResetQueue } from './util/lightSessionResetQueue.js'; import { setAppLoadingScreenMessage } from './setAppLoadingScreenMessage.js'; import { IdleDetector } from './IdleDetector.js'; import { @@ -50,11 +51,16 @@ import { } from './services/notificationProfilesService.js'; import { tapToViewMessagesDeletionService } from './services/tapToViewMessagesDeletionService.js'; import { senderCertificateService } from './services/senderCertificate.js'; -import { GROUP_CREDENTIALS_KEY } from './services/groupCredentialFetcher.js'; +import { + GROUP_CREDENTIALS_KEY, + initializeGroupCredentialFetcher, +} from './services/groupCredentialFetcher.js'; +import { initializeNetworkObserver } from './services/networkObserver.js'; import * as KeyboardLayout from './services/keyboardLayout.js'; import * as StorageService from './services/storage.js'; import { usernameIntegrity } from './services/usernameIntegrity.js'; import { updateIdentityKey } from './services/profiles.js'; +import { initializeUpdateListener } from './services/updateListener.js'; import { RoutineProfileRefresher } from './routineProfileRefresh.js'; import { isOlderThan } from './util/timestamp.js'; import { isValidReactionEmoji } from './reactions/isValidReactionEmoji.js'; @@ -116,6 +122,7 @@ import * as KeyChangeListener from './textsecure/KeyChangeListener.js'; import { UpdateKeysListener } from './textsecure/UpdateKeysListener.js'; import { isDirectConversation } from './util/whatTypeOfConversation.js'; import { BackOff, FIBONACCI_TIMEOUTS } from './util/BackOff.js'; +import { createApp as createAppRoot } from './state/roots/createApp.js'; import { AppViewType } from './state/ducks/app.js'; import { areAnyCallsActiveOrRinging } from './state/selectors/calling.js'; import { badgeImageFileDownloader } from './badges/badgeImageFileDownloader.js'; @@ -162,6 +169,7 @@ import { normalizeAci } from './util/normalizeAci.js'; import { createLogger } from './logging/log.js'; import { deleteAllLogs } from './util/deleteAllLogs.js'; import { startInteractionMode } from './services/InteractionMode.js'; +import { calling } from './services/calling.js'; import { ReactionSource } from './reactions/ReactionSource.js'; import { singleProtoJobQueue } from './jobs/singleProtoJobQueue.js'; import { conversationJobQueue } from './jobs/conversationJobQueue.js'; @@ -183,7 +191,7 @@ import { initializeMessageCounter, } from './util/incrementMessageCounter.js'; import { generateMessageId } from './util/generateMessageId.js'; -import { RetryPlaceholders } from './util/retryPlaceholders.js'; +import { retryPlaceholders } from './services/retryPlaceholders.js'; import { setBatchingStrategy } from './util/messageBatcher.js'; import { parseRemoteClientExpiration } from './util/parseRemoteClientExpiration.js'; import { addGlobalKeyboardShortcuts } from './services/addGlobalKeyboardShortcuts.js'; @@ -207,7 +215,13 @@ import { AttachmentBackupManager } from './jobs/AttachmentBackupManager.js'; import { getConversationIdForLogging } from './util/idForLogging.js'; import { encryptConversationAttachments } from './util/encryptConversationAttachments.js'; import { DataReader, DataWriter } from './sql/Client.js'; -import { restoreRemoteConfigFromStorage } from './RemoteConfig.js'; +import { + restoreRemoteConfigFromStorage, + getValue as getRemoteConfigValue, + onChange as onRemoteConfigChange, + maybeRefreshRemoteConfig, + forceRefreshRemoteConfig, +} from './RemoteConfig.js'; import { getParametersForRedux, loadAll } from './services/allLoaders.js'; import { checkFirstEnvelope } from './util/checkFirstEnvelope.js'; import { BLOCKED_UUIDS_ID } from './textsecure/storage/Blocked.js'; @@ -305,10 +319,6 @@ export async function startApp(): Promise { const profileKeyResponseQueue = new PQueue(); profileKeyResponseQueue.pause(); - const lightSessionResetQueue = new PQueue({ concurrency: 1 }); - window.Signal.Services.lightSessionResetQueue = lightSessionResetQueue; - lightSessionResetQueue.pause(); - const onDecryptionErrorQueue = new PQueue({ concurrency: 1 }); onDecryptionErrorQueue.pause(); @@ -768,7 +778,7 @@ export async function startApp(): Promise { flushMessageCounter(); // Hangup active calls - window.Signal.Services.calling.hangupAllCalls({ + calling.hangupAllCalls({ excludeRinging: true, reason: 'background/shutdown: shutdown requested', }); @@ -1097,10 +1107,7 @@ export async function startApp(): Promise { } }); - const retryPlaceholders = new RetryPlaceholders({ - retryReceiptLifespan: HOUR, - }); - window.Signal.Services.retryPlaceholders = retryPlaceholders; + retryPlaceholders.start(window.storage); setInterval(async () => { const now = Date.now(); @@ -1108,7 +1115,7 @@ export async function startApp(): Promise { try { sentProtoMaxAge = parseIntOrThrow( - window.Signal.RemoteConfig.getValue('desktop.retryRespondMaxAge'), + getRemoteConfigValue('desktop.retryRespondMaxAge'), 'retryRespondMaxAge' ); } catch (error) { @@ -1186,14 +1193,12 @@ export async function startApp(): Promise { } finally { setupAppState(); drop(start()); - window.Signal.Services.initializeNetworkObserver( + initializeNetworkObserver( window.reduxActions.network, () => window.getSocketStatus().authenticated.status ); - window.Signal.Services.initializeUpdateListener( - window.reduxActions.updates - ); - window.Signal.Services.calling.initialize( + initializeUpdateListener(window.reduxActions.updates); + calling.initialize( { ...window.reduxActions.calling, areAnyCallsActiveOrRinging: () => @@ -1467,9 +1472,7 @@ export async function startApp(): Promise { const appContainer = document.getElementById('app-container'); strictAssert(appContainer != null, 'No #app-container'); - createRoot(appContainer).render( - window.Signal.State.Roots.createApp(window.reduxStore) - ); + createRoot(appContainer).render(createAppRoot(window.reduxStore)); const hideMenuBar = window.storage.get('hide-menu-bar', false); window.IPC.setAutoHideMenuBar(hideMenuBar); window.IPC.setMenuBarVisibility(!hideMenuBar); @@ -1522,7 +1525,7 @@ export async function startApp(): Promise { strictAssert(server !== undefined, 'WebAPI not ready'); try { - await window.Signal.RemoteConfig.maybeRefreshRemoteConfig(server); + await maybeRefreshRemoteConfig(server); } catch (error) { if (error instanceof HTTPError) { log.warn( @@ -1535,24 +1538,20 @@ export async function startApp(): Promise { }); // Listen for changes to the `desktop.clientExpiration` remote flag - window.Signal.RemoteConfig.onChange( - 'desktop.clientExpiration', - ({ enabled, value }) => { - if (!enabled) { - return; - } - const remoteBuildExpirationTimestamp = - parseRemoteClientExpiration(value); - if (remoteBuildExpirationTimestamp) { - drop( - window.storage.put( - 'remoteBuildExpiration', - remoteBuildExpirationTimestamp - ) - ); - } + onRemoteConfigChange('desktop.clientExpiration', ({ enabled, value }) => { + if (!enabled) { + return; } - ); + const remoteBuildExpirationTimestamp = parseRemoteClientExpiration(value); + if (remoteBuildExpirationTimestamp) { + drop( + window.storage.put( + 'remoteBuildExpiration', + remoteBuildExpirationTimestamp + ) + ); + } + }); if (resolveOnAppView) { resolveOnAppView(); @@ -1679,7 +1678,7 @@ export async function startApp(): Promise { // 2. Fetch remote config, before we process the message queue if (isFirstAuthSocketConnect) { try { - await window.Signal.RemoteConfig.forceRefreshRemoteConfig( + await forceRefreshRemoteConfig( server, 'afterAuthSocketConnect/firstConnect' ); @@ -1925,7 +1924,7 @@ export async function startApp(): Promise { drop(challengeHandler.onOnline()); reconnectBackOff.reset(); - drop(window.Signal.Services.initializeGroupCredentialFetcher()); + drop(initializeGroupCredentialFetcher()); drop(AttachmentDownloadManager.start()); if (areRemoteBackupsTurnedOn()) { diff --git a/ts/components/CallScreen.tsx b/ts/components/CallScreen.tsx index c2cf0f6a5f..9f3b916237 100644 --- a/ts/components/CallScreen.tsx +++ b/ts/components/CallScreen.tsx @@ -97,7 +97,10 @@ import { isEmojiVariantValue, } from './fun/data/emojis.js'; import { useFunEmojiLocalizer } from './fun/useFunEmojiLocalizer.js'; -import { BeforeNavigateResponse } from '../services/BeforeNavigate.js'; +import { + BeforeNavigateResponse, + beforeNavigateService, +} from '../services/BeforeNavigate.js'; const { isEqual, noop } = lodash; @@ -323,12 +326,12 @@ export function CallScreen({ togglePip(); return BeforeNavigateResponse.MadeChanges; }; - window.Signal.Services.beforeNavigate.registerCallback({ + beforeNavigateService.registerCallback({ callback, name, }); return () => { - window.Signal.Services.beforeNavigate.unregisterCallback({ + beforeNavigateService.unregisterCallback({ callback, name, }); diff --git a/ts/components/ConversationList.stories.tsx b/ts/components/ConversationList.stories.tsx index 127d004f58..7ca1148c16 100644 --- a/ts/components/ConversationList.stories.tsx +++ b/ts/components/ConversationList.stories.tsx @@ -10,7 +10,7 @@ import type { Row, PropsType } from './ConversationList.js'; import { ConversationList, RowType } from './ConversationList.js'; import { MessageSearchResult } from './conversationList/MessageSearchResult.js'; import type { PropsData as ConversationListItemPropsType } from './conversationList/ConversationListItem.js'; -import { MessageStatuses } from './conversationList/ConversationListItem.js'; +import { MessageStatuses } from '../types/message/MessageStatus.js'; import { ContactCheckboxDisabledReason } from './conversationList/ContactCheckbox.js'; import { getDefaultConversation } from '../test-helpers/getDefaultConversation.js'; import { ThemeType } from '../types/Util.js'; diff --git a/ts/components/Preferences.stories.tsx b/ts/components/Preferences.stories.tsx index c8ee988f57..a957c0a065 100644 --- a/ts/components/Preferences.stories.tsx +++ b/ts/components/Preferences.stories.tsx @@ -9,7 +9,7 @@ import { action } from '@storybook/addon-actions'; import lodash from 'lodash'; import { Preferences } from './Preferences.js'; import { DEFAULT_CONVERSATION_COLOR } from '../types/Colors.js'; -import { PhoneNumberSharingMode } from '../util/phoneNumberSharingMode.js'; +import { PhoneNumberSharingMode } from '../types/PhoneNumberSharingMode.js'; import { PhoneNumberDiscoverability } from '../util/phoneNumberDiscoverability.js'; import { EmojiSkinTone } from './fun/data/emojis.js'; import { DAY, DurationInSeconds, WEEK } from '../util/durations/index.js'; diff --git a/ts/components/Preferences.tsx b/ts/components/Preferences.tsx index d35a8082fb..e6f9a19963 100644 --- a/ts/components/Preferences.tsx +++ b/ts/components/Preferences.tsx @@ -22,7 +22,7 @@ import { WidthBreakpoint } from './_util.js'; import { ConfirmationDialog } from './ConfirmationDialog.js'; import { DisappearingTimeDialog } from './DisappearingTimeDialog.js'; import { PhoneNumberDiscoverability } from '../util/phoneNumberDiscoverability.js'; -import { PhoneNumberSharingMode } from '../util/phoneNumberSharingMode.js'; +import { PhoneNumberSharingMode } from '../types/PhoneNumberSharingMode.js'; import { Select } from './Select.js'; import { Spinner } from './Spinner.js'; import { getCustomColorStyle } from '../util/getCustomColorStyle.js'; diff --git a/ts/components/conversation/GroupV2Change.stories.tsx b/ts/components/conversation/GroupV2Change.stories.tsx index 7693d2cde6..50d50958d1 100644 --- a/ts/components/conversation/GroupV2Change.stories.tsx +++ b/ts/components/conversation/GroupV2Change.stories.tsx @@ -6,7 +6,7 @@ import { action } from '@storybook/addon-actions'; import type { Meta } from '@storybook/react'; import { generateAci, generatePni } from '../../types/ServiceId.js'; import type { ServiceIdString, AciString } from '../../types/ServiceId.js'; -import type { GroupV2ChangeType } from '../../groups.js'; +import type { GroupV2ChangeType } from '../../types/groups.ts'; import { SignalService as Proto } from '../../protobuf/index.js'; import type { SmartContactRendererType } from '../../groupChange.js'; import type { PropsType } from './GroupV2Change.js'; diff --git a/ts/components/conversation/GroupV2Change.tsx b/ts/components/conversation/GroupV2Change.tsx index 5b8523a800..6e00ac8de9 100644 --- a/ts/components/conversation/GroupV2Change.tsx +++ b/ts/components/conversation/GroupV2Change.tsx @@ -24,7 +24,7 @@ import { SystemMessage } from './SystemMessage.js'; import type { GroupV2ChangeType, GroupV2ChangeDetailType, -} from '../../groups.js'; +} from '../../types/groups.ts'; import type { SmartContactRendererType } from '../../groupChange.js'; import { renderChange } from '../../groupChange.js'; diff --git a/ts/components/conversation/Message.tsx b/ts/components/conversation/Message.tsx index 808aef4378..ae48d7d06b 100644 --- a/ts/components/conversation/Message.tsx +++ b/ts/components/conversation/Message.tsx @@ -47,6 +47,7 @@ import type { import { ReactionViewer } from './ReactionViewer.js'; import { LinkPreviewDate } from './LinkPreviewDate.js'; import type { LinkPreviewForUIType } from '../../types/message/LinkPreviews.js'; +import type { MessageStatusType } from '../../types/message/MessageStatus.js'; import { shouldUseFullSizeLinkPreviewImage } from '../../linkPreviews/shouldUseFullSizeLinkPreviewImage.js'; import type { WidthBreakpoint } from '../_util.js'; import { OutgoingGiftBadgeModal } from '../OutgoingGiftBadgeModal.js'; @@ -178,18 +179,6 @@ const TextDirectionToDirAttribute = { [TextDirection.None]: 'auto', }; -export const MessageStatuses = [ - 'delivered', - 'error', - 'paused', - 'partial-sent', - 'read', - 'sending', - 'sent', - 'viewed', -] as const; -export type MessageStatusType = (typeof MessageStatuses)[number]; - export const Directions = ['incoming', 'outgoing'] as const; export type DirectionType = (typeof Directions)[number]; diff --git a/ts/components/conversation/MessageAudio.tsx b/ts/components/conversation/MessageAudio.tsx index df26a75a83..99f52ff4bd 100644 --- a/ts/components/conversation/MessageAudio.tsx +++ b/ts/components/conversation/MessageAudio.tsx @@ -9,9 +9,10 @@ import { animated, useSpring } from '@react-spring/web'; import type { LocalizerType } from '../../types/Util.js'; import type { AttachmentForUIType } from '../../types/Attachment.js'; +import type { MessageStatusType } from '../../types/message/MessageStatus.js'; import type { PushPanelForConversationActionType } from '../../state/ducks/conversations.js'; import { isDownloaded } from '../../types/Attachment.js'; -import type { DirectionType, MessageStatusType } from './Message.js'; +import type { DirectionType } from './Message.js'; import type { ComputePeaksResult } from '../VoiceNotesPlaybackContext.js'; import { MessageMetadata } from './MessageMetadata.js'; diff --git a/ts/components/conversation/MessageMetadata.tsx b/ts/components/conversation/MessageMetadata.tsx index 0be9798de0..79ac97f295 100644 --- a/ts/components/conversation/MessageMetadata.tsx +++ b/ts/components/conversation/MessageMetadata.tsx @@ -6,7 +6,8 @@ import React, { forwardRef, useCallback, useState } from 'react'; import classNames from 'classnames'; import type { LocalizerType } from '../../types/Util.js'; -import type { DirectionType, MessageStatusType } from './Message.js'; +import type { MessageStatusType } from '../../types/message/MessageStatus.js'; +import type { DirectionType } from './Message.js'; import type { PushPanelForConversationActionType } from '../../state/ducks/conversations.js'; import { missingCaseError } from '../../util/missingCaseError.js'; import { ExpireTimer } from './ExpireTimer.js'; diff --git a/ts/components/conversationList/ConversationListItem.tsx b/ts/components/conversationList/ConversationListItem.tsx index 321c5ea101..84d3d46966 100644 --- a/ts/components/conversationList/ConversationListItem.tsx +++ b/ts/components/conversationList/ConversationListItem.tsx @@ -25,18 +25,6 @@ import { RenderLocation } from '../conversation/MessageTextRenderer.js'; const EMPTY_OBJECT = Object.freeze(Object.create(null)); const MESSAGE_STATUS_ICON_CLASS_NAME = `${MESSAGE_TEXT_CLASS_NAME}__status-icon`; -export const MessageStatuses = [ - 'sending', - 'sent', - 'delivered', - 'read', - 'paused', - 'error', - 'partial-sent', -] as const; - -export type MessageStatusType = (typeof MessageStatuses)[number]; - export type PropsData = Pick< ConversationType, | 'avatarPlaceholderGradient' diff --git a/ts/groupChange.ts b/ts/groupChange.ts index 9ff481d5e1..91132ffe68 100644 --- a/ts/groupChange.ts +++ b/ts/groupChange.ts @@ -14,7 +14,10 @@ import type { } from './types/ServiceId.js'; import { missingCaseError } from './util/missingCaseError.js'; -import type { GroupV2ChangeDetailType, GroupV2ChangeType } from './groups.js'; +import type { + GroupV2ChangeDetailType, + GroupV2ChangeType, +} from './types/groups.ts'; import { SignalService as Proto } from './protobuf/index.js'; import { createLogger } from './logging/log.js'; diff --git a/ts/groups.ts b/ts/groups.ts index de824451d3..d410639b50 100644 --- a/ts/groups.ts +++ b/ts/groups.ts @@ -67,6 +67,7 @@ import { } from './util/whatTypeOfConversation.js'; import * as Bytes from './Bytes.js'; import type { AvatarDataType } from './types/Avatar.js'; +import type { GroupV2ChangeDetailType } from './types/groups.ts'; import type { ServiceIdString, AciString, @@ -115,155 +116,6 @@ type AccessRequiredEnum = Proto.AccessControl.AccessRequired; export { joinViaLink } from './groups/joinViaLink.js'; -type GroupV2AccessCreateChangeType = { - type: 'create'; -}; -type GroupV2AccessAttributesChangeType = { - type: 'access-attributes'; - newPrivilege: number; -}; -type GroupV2AccessMembersChangeType = { - type: 'access-members'; - newPrivilege: number; -}; -type GroupV2AccessInviteLinkChangeType = { - type: 'access-invite-link'; - newPrivilege: number; -}; -type GroupV2AnnouncementsOnlyChangeType = { - type: 'announcements-only'; - announcementsOnly: boolean; -}; -type GroupV2AvatarChangeType = { - type: 'avatar'; - removed: boolean; -}; -type GroupV2TitleChangeType = { - type: 'title'; - // Allow for null, because the title could be removed entirely - newTitle?: string; -}; -type GroupV2GroupLinkAddChangeType = { - type: 'group-link-add'; - privilege: number; -}; -type GroupV2GroupLinkResetChangeType = { - type: 'group-link-reset'; -}; -type GroupV2GroupLinkRemoveChangeType = { - type: 'group-link-remove'; -}; - -// No disappearing messages timer change type - message.expirationTimerUpdate used instead - -type GroupV2MemberAddChangeType = { - type: 'member-add'; - aci: AciString; -}; -type GroupV2MemberAddFromInviteChangeType = { - type: 'member-add-from-invite'; - aci: AciString; - pni?: PniString; - inviter?: AciString; -}; -type GroupV2MemberAddFromLinkChangeType = { - type: 'member-add-from-link'; - aci: AciString; -}; -type GroupV2MemberAddFromAdminApprovalChangeType = { - type: 'member-add-from-admin-approval'; - aci: AciString; -}; -type GroupV2MemberPrivilegeChangeType = { - type: 'member-privilege'; - aci: AciString; - newPrivilege: number; -}; -type GroupV2MemberRemoveChangeType = { - type: 'member-remove'; - aci: AciString; -}; - -type GroupV2PendingAddOneChangeType = { - type: 'pending-add-one'; - serviceId: ServiceIdString; -}; -type GroupV2PendingAddManyChangeType = { - type: 'pending-add-many'; - count: number; -}; -// Note: pending-remove is only used if user didn't also join the group at the same time -type GroupV2PendingRemoveOneChangeType = { - type: 'pending-remove-one'; - serviceId?: ServiceIdString; - inviter?: AciString; -}; -// Note: pending-remove is only used if user didn't also join the group at the same time -type GroupV2PendingRemoveManyChangeType = { - type: 'pending-remove-many'; - count: number; - inviter?: AciString; -}; - -type GroupV2AdminApprovalAddOneChangeType = { - type: 'admin-approval-add-one'; - aci: AciString; -}; -// Note: admin-approval-remove-one is only used if user didn't also join the group at -// the same time -type GroupV2AdminApprovalRemoveOneChangeType = { - type: 'admin-approval-remove-one'; - aci: AciString; - inviter?: AciString; -}; -type GroupV2AdminApprovalBounceChangeType = { - type: 'admin-approval-bounce'; - times: number; - isApprovalPending: boolean; - aci: AciString; -}; -export type GroupV2DescriptionChangeType = { - type: 'description'; - removed?: boolean; - // Adding this field; cannot remove previous field for backwards compatibility - description?: string; -}; -export type GroupV2SummaryType = { - type: 'summary'; -}; - -export type GroupV2ChangeDetailType = - | GroupV2AccessAttributesChangeType - | GroupV2AccessCreateChangeType - | GroupV2AccessInviteLinkChangeType - | GroupV2AccessMembersChangeType - | GroupV2AdminApprovalAddOneChangeType - | GroupV2AdminApprovalRemoveOneChangeType - | GroupV2AdminApprovalBounceChangeType - | GroupV2AnnouncementsOnlyChangeType - | GroupV2AvatarChangeType - | GroupV2DescriptionChangeType - | GroupV2GroupLinkAddChangeType - | GroupV2GroupLinkRemoveChangeType - | GroupV2GroupLinkResetChangeType - | GroupV2MemberAddChangeType - | GroupV2MemberAddFromAdminApprovalChangeType - | GroupV2MemberAddFromInviteChangeType - | GroupV2MemberAddFromLinkChangeType - | GroupV2MemberPrivilegeChangeType - | GroupV2MemberRemoveChangeType - | GroupV2PendingAddManyChangeType - | GroupV2PendingAddOneChangeType - | GroupV2PendingRemoveManyChangeType - | GroupV2PendingRemoveOneChangeType - | GroupV2SummaryType - | GroupV2TitleChangeType; - -export type GroupV2ChangeType = { - from?: ServiceIdString; - details: ReadonlyArray; -}; - export type GroupFields = { readonly id: Uint8Array; readonly secretParams: Uint8Array; @@ -335,8 +187,6 @@ type GroupChangeMessageType = BasicMessageType & export const MASTER_KEY_LENGTH = 32; const GROUP_TITLE_MAX_ENCRYPTED_BYTES = 1024; const GROUP_DESC_MAX_ENCRYPTED_BYTES = 8192; -export const ID_V1_LENGTH = 16; -export const ID_LENGTH = 32; const TEMPORAL_AUTH_REJECTED_CODE = 401; const GROUP_ACCESS_DENIED_CODE = 403; const GROUP_NONEXISTENT_CODE = 404; @@ -356,9 +206,7 @@ export async function getPreJoinGroupInfo( inviteLinkPasswordBase64: string, masterKeyBase64: string ): Promise { - const data = window.Signal.Groups.deriveGroupFields( - Bytes.fromBase64(masterKeyBase64) - ); + const data = deriveGroupFields(Bytes.fromBase64(masterKeyBase64)); return makeRequestWithCredentials({ logId: `getPreJoinInfo/groupv2(${data.id})`, @@ -1569,7 +1417,7 @@ export async function modifyGroupV2({ // Apply change locally, just like we would with an incoming change. This will // change conversation state and add change notifications to the timeline. - await window.Signal.Groups.maybeUpdateGroup({ + await maybeUpdateGroup({ conversation, groupChange: { base64: groupChangeBase64, diff --git a/ts/hooks/useConfirmDiscard.tsx b/ts/hooks/useConfirmDiscard.tsx index ac8d8ecc0d..3de83f3791 100644 --- a/ts/hooks/useConfirmDiscard.tsx +++ b/ts/hooks/useConfirmDiscard.tsx @@ -4,7 +4,10 @@ import React, { useEffect, useRef, useState } from 'react'; import { ConfirmDiscardDialog } from '../components/ConfirmDiscardDialog.js'; -import { BeforeNavigateResponse } from '../services/BeforeNavigate.js'; +import { + BeforeNavigateResponse, + beforeNavigateService, +} from '../services/BeforeNavigate.js'; import { explodePromise, type ExplodePromiseResultType, @@ -60,13 +63,13 @@ export function useConfirmDiscard({ close(); return confirmDiscardPromise.current.promise; }; - window.Signal.Services.beforeNavigate.registerCallback({ + beforeNavigateService.registerCallback({ name, callback, }); return () => { - window.Signal.Services.beforeNavigate.unregisterCallback({ + beforeNavigateService.unregisterCallback({ name, callback, }); diff --git a/ts/hooks/useNavBlocker.ts b/ts/hooks/useNavBlocker.ts index f295169956..9700a1792a 100644 --- a/ts/hooks/useNavBlocker.ts +++ b/ts/hooks/useNavBlocker.ts @@ -2,7 +2,10 @@ // SPDX-License-Identifier: AGPL-3.0-only import { useEffect, useRef, useState } from 'react'; -import { BeforeNavigateResponse } from '../services/BeforeNavigate.js'; +import { + beforeNavigateService, + BeforeNavigateResponse, +} from '../services/BeforeNavigate.js'; import type { BeforeNavigateCallback, BeforeNavigateTransitionDetails, @@ -80,12 +83,12 @@ export function useNavBlocker( return promise; }; - window.Signal.Services.beforeNavigate.registerCallback({ + beforeNavigateService.registerCallback({ callback, name: nameValue, }); return () => { - window.Signal.Services.beforeNavigate.unregisterCallback({ + beforeNavigateService.unregisterCallback({ callback, name: nameValue, }); diff --git a/ts/jobs/AttachmentDownloadManager.ts b/ts/jobs/AttachmentDownloadManager.ts index 11119e94a4..e7b316aec3 100644 --- a/ts/jobs/AttachmentDownloadManager.ts +++ b/ts/jobs/AttachmentDownloadManager.ts @@ -35,6 +35,7 @@ import { hasRequiredInformationForBackup, } from '../types/Attachment.js'; import type { ReadonlyMessageAttributesType } from '../model-types.d.ts'; +import { backupsService } from '../services/backups/index.js'; import { getMessageById } from '../messages/getMessageById.js'; import { KIBIBYTE, @@ -208,7 +209,7 @@ export class AttachmentDownloadManager extends JobManager shouldAttachmentEndUpInRemoteBackup({ attachment: job.attachment, - hasMediaBackups: window.Signal.Services.backups.hasMediaBackups(), + hasMediaBackups: backupsService.hasMediaBackups(), }) ? BACKUP_RETRY_CONFIG : DEFAULT_RETRY_CONFIG, @@ -225,7 +226,7 @@ export class AttachmentDownloadManager extends JobManager window.Signal.Services.backups.hasMediaBackups(), + hasMediaBackups: () => backupsService.hasMediaBackups(), getMessageQueueTime: () => doGetMessageQueueTime(), statfs, }; diff --git a/ts/jobs/helpers/sendResendRequest.ts b/ts/jobs/helpers/sendResendRequest.ts index 1889d440c9..dc42dab137 100644 --- a/ts/jobs/helpers/sendResendRequest.ts +++ b/ts/jobs/helpers/sendResendRequest.ts @@ -22,8 +22,8 @@ import { UnregisteredUserError, } from '../../textsecure/Errors.js'; import { drop } from '../../util/drop.js'; -import { strictAssert } from '../../util/assert.js'; import type { DecryptionErrorEventData } from '../../textsecure/messageReceiverEvents.js'; +import { retryPlaceholders } from '../../services/retryPlaceholders.js'; import type { LoggerType } from '../../types/Logging.js'; import { startAutomaticSessionReset } from '../../util/handleRetry.js'; import * as Bytes from '../../Bytes.js'; @@ -123,9 +123,6 @@ export async function sendResendRequest( // 1. We believe that it could be successfully re-sent, so we'll add a placeholder. if (contentHint === ContentHint.Resendable) { - const { retryPlaceholders } = window.Signal.Services; - strictAssert(retryPlaceholders, 'sendResendRequest: adding placeholder'); - log.info('contentHint is RESENDABLE, adding placeholder'); const state = window.reduxStore.getState(); diff --git a/ts/messages/handleDataMessage.ts b/ts/messages/handleDataMessage.ts index 9e096f814c..cb3020efdd 100644 --- a/ts/messages/handleDataMessage.ts +++ b/ts/messages/handleDataMessage.ts @@ -25,6 +25,7 @@ import { isGroup, isGroupV1, } from '../util/whatTypeOfConversation.js'; +import { respondToGroupV2Migration, maybeUpdateGroup } from '../groups.js'; import { generateMessageId } from '../util/generateMessageId.js'; import { hasErrors, @@ -234,7 +235,7 @@ export async function handleDataMessage( // If we received a GroupV2 message in a GroupV1 group, we migrate! const { revision, groupChange } = initialMessage.groupV2; - await window.Signal.Groups.respondToGroupV2Migration({ + await respondToGroupV2Migration({ conversation, groupChange: groupChange ? { @@ -270,7 +271,7 @@ export async function handleDataMessage( if (isV2GroupUpdate && initialMessage.groupV2) { const { revision, groupChange } = initialMessage.groupV2; try { - await window.Signal.Groups.maybeUpdateGroup({ + await maybeUpdateGroup({ conversation, groupChange: groupChange ? { diff --git a/ts/model-types.d.ts b/ts/model-types.d.ts index cc9cd56110..d785b7c8b3 100644 --- a/ts/model-types.d.ts +++ b/ts/model-types.d.ts @@ -3,7 +3,7 @@ import type { ReadonlyDeep } from 'type-fest'; -import type { GroupV2ChangeType } from './groups.js'; +import type { GroupV2ChangeType } from './types/groups.ts'; import type { DraftBodyRanges, RawBodyRange } from './types/BodyRange.js'; import type { CustomColorType, ConversationColorType } from './types/Colors.js'; import type { SendMessageChallengeData } from './textsecure/Errors.js'; diff --git a/ts/models/conversations.ts b/ts/models/conversations.ts index 87d746fe49..3e698e221b 100644 --- a/ts/models/conversations.ts +++ b/ts/models/conversations.ts @@ -197,7 +197,30 @@ import { getIsInitialContactSync } from '../services/contactSync.js'; import { queueAttachmentDownloadsAndMaybeSaveMessage } from '../util/queueAttachmentDownloads.js'; import { cleanupMessages } from '../util/cleanup.js'; import { MessageModel } from './messages.js'; -import { applyNewAvatar } from '../groups.js'; +import { + applyNewAvatar, + buildAccessControlAddFromInviteLinkChange, + buildAccessControlAttributesChange, + buildAccessControlMembersChange, + buildAddBannedMemberChange, + buildAddMember, + buildAddPendingAdminApprovalMemberChange, + buildAnnouncementsOnlyChange, + buildDeleteMemberChange, + buildDeletePendingAdminApprovalMemberChange, + buildDisappearingMessagesTimerChange, + buildGroupLink, + buildInviteLinkPasswordChange, + buildModifyMemberRoleChange, + buildNewGroupLinkChange, + buildPromoteMemberChange, + generateGroupInviteLinkPassword, + hasV1GroupBeenMigrated, + joinGroupV2ViaLinkAndMigrate, + modifyGroupV2, + waitThenMaybeUpdateGroup, + waitThenRespondToGroupV2Migration, +} from '../groups.js'; import { safeSetTimeout } from '../util/timeout.js'; import { getTypingIndicatorSetting } from '../types/Util.js'; import { INITIAL_EXPIRE_TIMER_VERSION } from '../util/expirationTimer.js'; @@ -534,7 +557,7 @@ export class ConversationModel { return undefined; } - return window.Signal.Groups.buildDisappearingMessagesTimerChange({ + return buildDisappearingMessagesTimerChange({ expireTimer: seconds || DurationInSeconds.ZERO, group: this.attributes, }); @@ -569,7 +592,7 @@ export class ConversationModel { strictAssert(profileKeyCredentialBase64, 'Must have profileKeyCredential'); if (serviceIdKind === ServiceIdKind.ACI) { - return window.Signal.Groups.buildPromoteMemberChange({ + return buildPromoteMemberChange({ group: this.attributes, isPendingPniAciProfileKey: false, profileKeyCredentialBase64, @@ -582,7 +605,7 @@ export class ConversationModel { 'Must be a PNI promotion' ); - return window.Signal.Groups.buildPromoteMemberChange({ + return buildPromoteMemberChange({ group: this.attributes, isPendingPniAciProfileKey: true, profileKeyCredentialBase64, @@ -608,7 +631,7 @@ export class ConversationModel { const ourAci = window.textsecure.storage.user.getCheckedAci(); - return window.Signal.Groups.buildDeletePendingAdminApprovalMemberChange({ + return buildDeletePendingAdminApprovalMemberChange({ group: this.attributes, ourAci, aci, @@ -652,7 +675,7 @@ export class ConversationModel { return undefined; } - return window.Signal.Groups.buildAddPendingAdminApprovalMemberChange({ + return buildAddPendingAdminApprovalMemberChange({ group: this.attributes, profileKeyCredentialBase64, serverPublicParamsBase64: window.getServerPublicParams(), @@ -697,7 +720,7 @@ export class ConversationModel { return undefined; } - return window.Signal.Groups.buildAddMember({ + return buildAddMember({ group: this.attributes, profileKeyCredentialBase64, serverPublicParamsBase64: window.getServerPublicParams(), @@ -728,7 +751,7 @@ export class ConversationModel { const ourAci = window.textsecure.storage.user.getCheckedAci(); - return window.Signal.Groups.buildDeleteMemberChange({ + return buildDeleteMemberChange({ group: this.attributes, ourAci, serviceId, @@ -757,7 +780,7 @@ export class ConversationModel { ? MEMBER_ROLES.DEFAULT : MEMBER_ROLES.ADMINISTRATOR; - return window.Signal.Groups.buildModifyMemberRoleChange({ + return buildModifyMemberRoleChange({ group: this.attributes, serviceId, role, @@ -779,7 +802,7 @@ export class ConversationModel { name: string; syncMessageOnly?: boolean; }): Promise { - await window.Signal.Groups.modifyGroupV2({ + await modifyGroupV2({ conversation: this, usingCredentialsFrom, createGroupChange, @@ -1188,7 +1211,7 @@ export class ConversationModel { return; } - await window.Signal.Groups.waitThenMaybeUpdateGroup({ + await waitThenMaybeUpdateGroup({ force: options.force, conversation: this, }); @@ -1240,12 +1263,12 @@ export class ConversationModel { return; } - const isMigrated = await window.Signal.Groups.hasV1GroupBeenMigrated(this); + const isMigrated = await hasV1GroupBeenMigrated(this); if (!isMigrated) { return; } - await window.Signal.Groups.waitThenRespondToGroupV2Migration({ + await waitThenRespondToGroupV2Migration({ conversation: this, }); } @@ -2612,7 +2635,7 @@ export class ConversationModel { inviteLinkPassword: string; revision: number; }): Promise { - await window.Signal.Groups.joinGroupV2ViaLinkAndMigrate({ + await joinGroupV2ViaLinkAndMigrate({ approvalRequired, conversation: this, inviteLinkPassword, @@ -2758,7 +2781,7 @@ export class ConversationModel { return; } - return window.Signal.Groups.buildAddBannedMemberChange({ + return buildAddBannedMemberChange({ group: this.attributes, serviceId, }); @@ -3755,7 +3778,7 @@ export class ConversationModel { return undefined; } - return window.Signal.Groups.buildGroupLink(this.attributes); + return buildGroupLink(this.attributes); } getMembers( @@ -4423,7 +4446,7 @@ export class ConversationModel { } const groupInviteLinkPassword = Bytes.toBase64( - window.Signal.Groups.generateGroupInviteLinkPassword() + generateGroupInviteLinkPassword() ); log.info('refreshGroupLink for conversation', this.idForLogging()); @@ -4432,10 +4455,7 @@ export class ConversationModel { name: 'updateInviteLinkPassword', usingCredentialsFrom: [], createGroupChange: async () => - window.Signal.Groups.buildInviteLinkPasswordChange( - this.attributes, - groupInviteLinkPassword - ), + buildInviteLinkPasswordChange(this.attributes, groupInviteLinkPassword), }); this.set({ groupInviteLinkPassword }); @@ -4450,7 +4470,7 @@ export class ConversationModel { value && !this.get('groupInviteLinkPassword'); const groupInviteLinkPassword = this.get('groupInviteLinkPassword') || - Bytes.toBase64(window.Signal.Groups.generateGroupInviteLinkPassword()); + Bytes.toBase64(generateGroupInviteLinkPassword()); log.info('toggleGroupLink for conversation', this.idForLogging(), value); @@ -4464,7 +4484,7 @@ export class ConversationModel { name: 'updateNewGroupLink', usingCredentialsFrom: [], createGroupChange: async () => - window.Signal.Groups.buildNewGroupLinkChange( + buildNewGroupLinkChange( this.attributes, groupInviteLinkPassword, addFromInviteLink @@ -4475,7 +4495,7 @@ export class ConversationModel { name: 'updateAccessControlAddFromInviteLink', usingCredentialsFrom: [], createGroupChange: async () => - window.Signal.Groups.buildAccessControlAddFromInviteLinkChange( + buildAccessControlAddFromInviteLinkChange( this.attributes, addFromInviteLink ), @@ -4510,7 +4530,7 @@ export class ConversationModel { name: 'updateAccessControlAddFromInviteLink', usingCredentialsFrom: [], createGroupChange: async () => - window.Signal.Groups.buildAccessControlAddFromInviteLinkChange( + buildAccessControlAddFromInviteLinkChange( this.attributes, addFromInviteLink ), @@ -4534,10 +4554,7 @@ export class ConversationModel { name: 'updateAccessControlAttributes', usingCredentialsFrom: [], createGroupChange: async () => - window.Signal.Groups.buildAccessControlAttributesChange( - this.attributes, - value - ), + buildAccessControlAttributesChange(this.attributes, value), }); const ACCESS_ENUM = Proto.AccessControl.AccessRequired; @@ -4560,10 +4577,7 @@ export class ConversationModel { name: 'updateAccessControlMembers', usingCredentialsFrom: [], createGroupChange: async () => - window.Signal.Groups.buildAccessControlMembersChange( - this.attributes, - value - ), + buildAccessControlMembersChange(this.attributes, value), }); const ACCESS_ENUM = Proto.AccessControl.AccessRequired; @@ -4586,10 +4600,7 @@ export class ConversationModel { name: 'updateAnnouncementsOnly', usingCredentialsFrom: [], createGroupChange: async () => - window.Signal.Groups.buildAnnouncementsOnlyChange( - this.attributes, - value - ), + buildAnnouncementsOnlyChange(this.attributes, value), }); this.set({ announcementsOnly: value }); diff --git a/ts/services/LinkPreview.ts b/ts/services/LinkPreview.ts index b87f08eb9c..c210172d82 100644 --- a/ts/services/LinkPreview.ts +++ b/ts/services/LinkPreview.ts @@ -22,6 +22,13 @@ import * as LinkPreview from '../types/LinkPreview.js'; import * as Stickers from '../types/Stickers.js'; import * as VisualAttachment from '../types/VisualAttachment.js'; import { createLogger } from '../logging/log.js'; +import { + parseGroupLink, + deriveGroupFields, + getPreJoinGroupInfo, + decryptGroupTitle, + decryptGroupAvatar, +} from '../groups.js'; import { IMAGE_JPEG, IMAGE_WEBP, stringToMIMEType } from '../types/MIME.js'; import { SECOND } from '../util/durations/index.js'; import { autoScale } from '../util/handleImageAttachment.js'; @@ -532,31 +539,23 @@ async function getGroupPreview( } const groupData = hash.slice(1); - const { inviteLinkPassword, masterKey } = - window.Signal.Groups.parseGroupLink(groupData); + const { inviteLinkPassword, masterKey } = parseGroupLink(groupData); - const fields = window.Signal.Groups.deriveGroupFields( - Bytes.fromBase64(masterKey) - ); + const fields = deriveGroupFields(Bytes.fromBase64(masterKey)); const id = Bytes.toBase64(fields.id); const logId = `groupv2(${id})`; const secretParams = Bytes.toBase64(fields.secretParams); log.info(`getGroupPreview/${logId}: Fetching pre-join state`); - const result = await window.Signal.Groups.getPreJoinGroupInfo( - inviteLinkPassword, - masterKey - ); + const result = await getPreJoinGroupInfo(inviteLinkPassword, masterKey); if (abortSignal.aborted) { return null; } const title = - window.Signal.Groups.decryptGroupTitle( - dropNull(result.title), - secretParams - ) || window.i18n('icu:unknownGroup'); + decryptGroupTitle(dropNull(result.title), secretParams) || + window.i18n('icu:unknownGroup'); const description = window.i18n('icu:GroupV2--join--group-metadata--full', { memberCount: result?.memberCount ?? 0, }); @@ -564,10 +563,7 @@ async function getGroupPreview( if (result.avatar) { try { - const data = await window.Signal.Groups.decryptGroupAvatar( - result.avatar, - secretParams - ); + const data = await decryptGroupAvatar(result.avatar, secretParams); image = { data, size: data.byteLength, diff --git a/ts/services/backups/export.ts b/ts/services/backups/export.ts index bd838f4fce..521f84627e 100644 --- a/ts/services/backups/export.ts +++ b/ts/services/backups/export.ts @@ -67,7 +67,7 @@ import { import { PhoneNumberSharingMode, parsePhoneNumberSharingMode, -} from '../../util/phoneNumberSharingMode.js'; +} from '../../types/PhoneNumberSharingMode.js'; import { missingCaseError } from '../../util/missingCaseError.js'; import { isCallHistory, diff --git a/ts/services/backups/import.ts b/ts/services/backups/import.ts index 25b9b6aa10..7ea07ce677 100644 --- a/ts/services/backups/import.ts +++ b/ts/services/backups/import.ts @@ -78,7 +78,7 @@ import { incrementMessageCounter } from '../../util/incrementMessageCounter.js'; import { generateMessageId } from '../../util/generateMessageId.js'; import { isAciString } from '../../util/isAciString.js'; import { PhoneNumberDiscoverability } from '../../util/phoneNumberDiscoverability.js'; -import { PhoneNumberSharingMode } from '../../util/phoneNumberSharingMode.js'; +import { PhoneNumberSharingMode } from '../../types/PhoneNumberSharingMode.js'; import { bytesToUuid } from '../../util/uuidToBytes.js'; import { missingCaseError } from '../../util/missingCaseError.js'; import { ReadStatus } from '../../messages/MessageReadStatus.js'; @@ -92,7 +92,7 @@ import { UnsupportedBackupVersion } from './errors.js'; import type { AboutMe, LocalChatStyle } from './types.js'; import { BackupType } from './types.js'; import { getBackupMediaRootKey } from './crypto.js'; -import type { GroupV2ChangeDetailType } from '../../groups.js'; +import type { GroupV2ChangeDetailType } from '../../types/groups.ts'; import { queueAttachmentDownloads } from '../../util/queueAttachmentDownloads.js'; import { isNotNil } from '../../util/isNotNil.js'; import { isGroup } from '../../util/whatTypeOfConversation.js'; diff --git a/ts/util/retryPlaceholders.ts b/ts/services/retryPlaceholders.ts similarity index 69% rename from ts/util/retryPlaceholders.ts rename to ts/services/retryPlaceholders.ts index 436d75aef1..ddc3fb6586 100644 --- a/ts/util/retryPlaceholders.ts +++ b/ts/services/retryPlaceholders.ts @@ -5,7 +5,9 @@ import { z } from 'zod'; import lodash from 'lodash'; import { createLogger } from '../logging/log.js'; import { aciSchema } from '../types/ServiceId.js'; -import { safeParseStrict } from './schemas.js'; +import { safeParseStrict } from '../util/schemas.js'; +import { HOUR } from '../util/durations/index.js'; +import type { StorageInterface } from '../types/Storage.d.ts'; const { groupBy } = lodash; @@ -35,7 +37,6 @@ export function getItemId(conversationId: string, sentAt: number): string { return `${conversationId}--${sentAt}`; } -const HOUR = 60 * 60 * 1000; export const STORAGE_KEY = 'retryPlaceholders'; export function getDeltaIntoPast(delta?: number): number { @@ -43,21 +44,25 @@ export function getDeltaIntoPast(delta?: number): number { } export class RetryPlaceholders { - #items: Array; - #byConversation: ByConversationLookupType; - #byMessage: ByMessageLookupType; + #isStarted = false; + #items = new Array(); + #byConversation: ByConversationLookupType = {}; + #byMessage: ByMessageLookupType = new Map(); #retryReceiptLifespan: number; + #storage: StorageInterface | undefined; constructor(options: { retryReceiptLifespan?: number } = {}) { - if (!window.storage) { - throw new Error( - 'RetryPlaceholders.constructor: window.storage not available!' - ); + this.#retryReceiptLifespan = options.retryReceiptLifespan || HOUR; + } + + start(storage: StorageInterface): void { + if (this.#isStarted) { + throw new Error('RetryPlaceholders: already started'); } const parsed = safeParseStrict( retryItemListSchema, - window.storage.get(STORAGE_KEY, new Array()) + storage.get(STORAGE_KEY, new Array()) ); if (!parsed.success) { log.warn( @@ -68,11 +73,11 @@ export class RetryPlaceholders { } this.#items = parsed.success ? parsed.data : []; - this.sortByExpiresAtAsc(); - this.#byConversation = this.makeByConversationLookup(); - this.#byMessage = this.makeByMessageLookup(); - this.#retryReceiptLifespan = options.retryReceiptLifespan || HOUR; - + this.#sortByExpiresAtAsc(); + this.#byConversation = this.#makeByConversationLookup(); + this.#byMessage = this.#makeByMessageLookup(); + this.#isStarted = true; + this.#storage = storage; log.info( `constructor: Started with ${this.#items.length} items, lifespan of ${this.#retryReceiptLifespan}` ); @@ -80,18 +85,18 @@ export class RetryPlaceholders { // Arranging local data for efficiency - sortByExpiresAtAsc(): void { + #sortByExpiresAtAsc(): void { this.#items.sort( (left: RetryItemType, right: RetryItemType) => left.receivedAt - right.receivedAt ); } - makeByConversationLookup(): ByConversationLookupType { + #makeByConversationLookup(): ByConversationLookupType { return groupBy(this.#items, item => item.conversationId); } - makeByMessageLookup(): ByMessageLookupType { + #makeByMessageLookup(): ByMessageLookupType { const lookup = new Map(); this.#items.forEach(item => { lookup.set(getItemId(item.conversationId, item.sentAt), item); @@ -99,14 +104,18 @@ export class RetryPlaceholders { return lookup; } - makeLookups(): void { - this.#byConversation = this.makeByConversationLookup(); - this.#byMessage = this.makeByMessageLookup(); + #makeLookups(): void { + this.#byConversation = this.#makeByConversationLookup(); + this.#byMessage = this.#makeByMessageLookup(); } // Basic data management async add(item: RetryItemType): Promise { + if (!this.#isStarted) { + throw new Error('RetryPlaceholders: not started'); + } + const parsed = safeParseStrict(retryItemSchema, item); if (!parsed.success) { throw new Error( @@ -117,26 +126,40 @@ export class RetryPlaceholders { } this.#items.push(item); - this.sortByExpiresAtAsc(); - this.makeLookups(); + this.#sortByExpiresAtAsc(); + this.#makeLookups(); await this.save(); } async save(): Promise { - await window.storage.put(STORAGE_KEY, this.#items); + if (!this.#isStarted || this.#storage == null) { + throw new Error('RetryPlaceholders: not started'); + } + + await this.#storage.put(STORAGE_KEY, this.#items); } // Finding items in different ways getCount(): number { + if (!this.#isStarted) { + throw new Error('RetryPlaceholders: not started'); + } + return this.#items.length; } getNextToExpire(): RetryItemType | undefined { + if (!this.#isStarted) { + throw new Error('RetryPlaceholders: not started'); + } return this.#items[0]; } async getExpiredAndRemove(): Promise> { + if (!this.#isStarted) { + throw new Error('RetryPlaceholders: not started'); + } const expiration = getDeltaIntoPast(this.#retryReceiptLifespan); const max = this.#items.length; const result: Array = []; @@ -153,13 +176,16 @@ export class RetryPlaceholders { log.info(`getExpiredAndRemove: Found ${result.length} expired items`); this.#items.splice(0, result.length); - this.makeLookups(); + this.#makeLookups(); await this.save(); return result; } async findByConversationAndMarkOpened(conversationId: string): Promise { + if (!this.#isStarted) { + throw new Error('RetryPlaceholders: not started'); + } let changed = 0; const items = this.#byConversation[conversationId]; (items || []).forEach(item => { @@ -183,6 +209,9 @@ export class RetryPlaceholders { conversationId: string, sentAt: number ): Promise { + if (!this.#isStarted) { + throw new Error('RetryPlaceholders: not started'); + } const result = this.#byMessage.get(getItemId(conversationId, sentAt)); if (!result) { return undefined; @@ -191,7 +220,7 @@ export class RetryPlaceholders { const index = this.#items.findIndex(item => item === result); this.#items.splice(index, 1); - this.makeLookups(); + this.#makeLookups(); log.info( `findByMessageAndRemove: Removing ${sentAt} from conversation ${conversationId}` @@ -201,3 +230,7 @@ export class RetryPlaceholders { return result; } } + +export const retryPlaceholders = new RetryPlaceholders({ + retryReceiptLifespan: HOUR, +}); diff --git a/ts/services/storageRecordOps.ts b/ts/services/storageRecordOps.ts index 25991a89b2..797fac7dde 100644 --- a/ts/services/storageRecordOps.ts +++ b/ts/services/storageRecordOps.ts @@ -20,7 +20,7 @@ import { isNotNil } from '../util/isNotNil.js'; import { PhoneNumberSharingMode, parsePhoneNumberSharingMode, -} from '../util/phoneNumberSharingMode.js'; +} from '../types/PhoneNumberSharingMode.js'; import { PhoneNumberDiscoverability, parsePhoneNumberDiscoverability, diff --git a/ts/signal.ts b/ts/signal.ts index 5b1fd1e1cd..865a3ebe5f 100644 --- a/ts/signal.ts +++ b/ts/signal.ts @@ -5,21 +5,10 @@ import type { ReadonlyDeep } from 'type-fest'; -import * as Crypto from './Crypto.js'; -import * as Curve from './Curve.js'; -import * as Groups from './groups.js'; import OS from './util/os/osMain.js'; import { isProduction } from './util/version.js'; -import * as RemoteConfig from './RemoteConfig.js'; import { DataReader, DataWriter } from './sql/Client.js'; -// Components -import { ConfirmationDialog } from './components/ConfirmationDialog.js'; - -// State -import { createApp } from './state/roots/createApp.js'; -import { createSafetyNumberViewer } from './state/roots/createSafetyNumberViewer.js'; - // Types import * as TypesAttachment from './types/Attachment.js'; import * as VisualAttachment from './types/VisualAttachment.js'; @@ -28,9 +17,6 @@ import { Address } from './types/Address.js'; import { QualifiedAddress } from './types/QualifiedAddress.js'; // Processes / Services -import { initializeGroupCredentialFetcher } from './services/groupCredentialFetcher.js'; -import { initializeNetworkObserver } from './services/networkObserver.js'; -import { initializeUpdateListener } from './services/updateListener.js'; import { calling } from './services/calling.js'; import * as storage from './services/storage.js'; import { backupsService } from './services/backups/index.js'; @@ -57,7 +43,6 @@ import type { LinkPreviewWithHydratedData, } from './types/message/LinkPreviews.js'; import type { StickerType, StickerWithHydratedData } from './types/Stickers.js'; -import { beforeNavigateService } from './services/BeforeNavigate.js'; import type { MessageAttachmentType } from './types/AttachmentDownload.js'; type EncryptedReader = ( @@ -464,30 +449,12 @@ export const setup = (options: { userDataPath, }); - const Components = { - ConfirmationDialog, - }; - - const Roots = { - createApp, - createSafetyNumberViewer, - }; - + // Only for testing const Services = { - backups: backupsService, - beforeNavigate: beforeNavigateService, - calling, - initializeGroupCredentialFetcher, - initializeNetworkObserver, - initializeUpdateListener, - donations, - - // Testing storage, - }; - - const State = { - Roots, + calling, + donations, + backups: backupsService, }; const Types = { @@ -499,15 +466,9 @@ export const setup = (options: { }; return { - Components, - Crypto, - Curve, - Groups, Migrations, OS, - RemoteConfig, Services, - State, Types, ...(isProduction(window.getVersion()) diff --git a/ts/state/ducks/conversations.ts b/ts/state/ducks/conversations.ts index d5cbd227ba..1ded628f29 100644 --- a/ts/state/ducks/conversations.ts +++ b/ts/state/ducks/conversations.ts @@ -13,6 +13,7 @@ import type { StateType as RootStateType } from '../reducer.js'; import * as groups from '../../groups.js'; import { createLogger } from '../../logging/log.js'; import { calling } from '../../services/calling.js'; +import { retryPlaceholders } from '../../services/retryPlaceholders.js'; import { getOwn } from '../../util/getOwn.js'; import { assertDev, strictAssert } from '../../util/assert.js'; import { drop } from '../../util/drop.js'; @@ -4816,10 +4817,7 @@ function onConversationOpened( log.warn(`${logId}: Did not find message ${messageId}`); } - const { retryPlaceholders } = window.Signal.Services; - if (retryPlaceholders) { - await retryPlaceholders.findByConversationAndMarkOpened(conversation.id); - } + await retryPlaceholders.findByConversationAndMarkOpened(conversation.id); const loadAndUpdate = async () => { drop( diff --git a/ts/state/ducks/nav.ts b/ts/state/ducks/nav.ts index 8cdb0a2861..9971fd0af3 100644 --- a/ts/state/ducks/nav.ts +++ b/ts/state/ducks/nav.ts @@ -7,6 +7,7 @@ import type { ThunkAction } from 'redux-thunk'; import { createLogger } from '../../logging/log.js'; import { useBoundActions } from '../../hooks/useBoundActions.js'; import { NavTab, SettingsPage } from '../../types/Nav.js'; +import { beforeNavigateService } from '../../services/BeforeNavigate.js'; import type { BoundActionCreatorsMapObject } from '../../hooks/useBoundActions.js'; import type { StateType as RootStateType } from '../reducer.js'; @@ -53,12 +54,11 @@ export function changeLocation( const existingLocation = getState().nav.selectedLocation; const logId = `changeLocation/${printLocation(newLocation)}`; - const needToCancel = - await window.Signal.Services.beforeNavigate.shouldCancelNavigation({ - context: logId, - existingLocation, - newLocation, - }); + const needToCancel = await beforeNavigateService.shouldCancelNavigation({ + context: logId, + existingLocation, + newLocation, + }); if (needToCancel) { log.info(`${logId}: Canceling navigation`); diff --git a/ts/state/roots/createSafetyNumberViewer.tsx b/ts/state/roots/createSafetyNumberViewer.tsx deleted file mode 100644 index de875ba2fb..0000000000 --- a/ts/state/roots/createSafetyNumberViewer.tsx +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2020 Signal Messenger, LLC -// SPDX-License-Identifier: AGPL-3.0-only - -import React from 'react'; -import { Provider } from 'react-redux'; - -import type { Store } from 'redux'; - -import type { SafetyNumberProps } from '../../components/SafetyNumberChangeDialog.js'; -import { SmartSafetyNumberViewer } from '../smart/SafetyNumberViewer.js'; - -export const createSafetyNumberViewer = ( - store: Store, - props: SafetyNumberProps -): React.ReactElement => ( - - - -); diff --git a/ts/state/smart/Preferences.tsx b/ts/state/smart/Preferences.tsx index 3d3e7ad3a0..338b557f16 100644 --- a/ts/state/smart/Preferences.tsx +++ b/ts/state/smart/Preferences.tsx @@ -46,7 +46,7 @@ import { assertDev, strictAssert } from '../../util/assert.js'; import { backupsService } from '../../services/backups/index.js'; import { DurationInSeconds } from '../../util/durations/duration-in-seconds.js'; import { PhoneNumberDiscoverability } from '../../util/phoneNumberDiscoverability.js'; -import { PhoneNumberSharingMode } from '../../util/phoneNumberSharingMode.js'; +import { PhoneNumberSharingMode } from '../../types/PhoneNumberSharingMode.js'; import { writeProfile } from '../../services/writeProfile.js'; import { getConversation } from '../../util/getConversation.js'; import { waitForEvent } from '../../shims/events.js'; @@ -251,9 +251,9 @@ export function SmartPreferences(): JSX.Element | null { const doDeleteAllData = () => renderClearingDataView(); const refreshCloudBackupStatus = - window.Signal.Services.backups.throttledFetchCloudBackupStatus; + backupsService.throttledFetchCloudBackupStatus; const refreshBackupSubscriptionStatus = - window.Signal.Services.backups.throttledFetchSubscriptionStatus; + backupsService.throttledFetchSubscriptionStatus; // Context - these don't change per startup diff --git a/ts/test-electron/backup/backup_groupv2_notifications_test.ts b/ts/test-electron/backup/backup_groupv2_notifications_test.ts index e34358362f..13bf295e10 100644 --- a/ts/test-electron/backup/backup_groupv2_notifications_test.ts +++ b/ts/test-electron/backup/backup_groupv2_notifications_test.ts @@ -8,7 +8,7 @@ import { SignalService as Proto } from '../../protobuf/index.js'; import { generateAci, generatePni } from '../../types/ServiceId.js'; import type { MessageAttributesType } from '../../model-types.js'; -import type { GroupV2ChangeType } from '../../groups.js'; +import type { GroupV2ChangeType } from '../../types/groups.ts'; import { getRandomBytes } from '../../Crypto.js'; import * as Bytes from '../../Bytes.js'; import { strictAssert } from '../../util/assert.js'; diff --git a/ts/test-electron/backup/bubble_test.ts b/ts/test-electron/backup/bubble_test.ts index 21109e27bb..ede2e925f3 100644 --- a/ts/test-electron/backup/bubble_test.ts +++ b/ts/test-electron/backup/bubble_test.ts @@ -13,7 +13,7 @@ import * as Bytes from '../../Bytes.js'; import { generateAci } from '../../types/ServiceId.js'; import { ReadStatus } from '../../messages/MessageReadStatus.js'; import { SeenStatus } from '../../MessageSeenStatus.js'; -import { ID_V1_LENGTH } from '../../groups.js'; +import { ID_V1_LENGTH } from '../../types/groups.js'; import { DurationInSeconds, WEEK } from '../../util/durations/index.js'; import { setupBasics, diff --git a/ts/test-electron/state/selectors/conversations-extra_test.ts b/ts/test-electron/state/selectors/conversations-extra_test.ts index 906d4ebb40..1416b0c5b4 100644 --- a/ts/test-electron/state/selectors/conversations-extra_test.ts +++ b/ts/test-electron/state/selectors/conversations-extra_test.ts @@ -18,7 +18,7 @@ import { getByDistributionListConversationsStoppingSend } from '../../../state/s import { generateAci } from '../../../types/ServiceId.js'; import { generateStoryDistributionId } from '../../../types/StoryDistributionId.js'; import { noopAction } from '../../../state/ducks/noop.js'; -import { ID_LENGTH } from '../../../groups.js'; +import { ID_LENGTH } from '../../../types/groups.js'; import { ConversationVerificationState } from '../../../state/ducks/conversationsEnums.js'; describe('both/state/selectors/conversations-extra', () => { diff --git a/ts/test-electron/textsecure/AccountManager_test.ts b/ts/test-electron/textsecure/AccountManager_test.ts index 9310afe6e2..d006667060 100644 --- a/ts/test-electron/textsecure/AccountManager_test.ts +++ b/ts/test-electron/textsecure/AccountManager_test.ts @@ -6,6 +6,7 @@ import lodash from 'lodash'; import * as sinon from 'sinon'; import { getRandomBytes } from '../../Crypto.js'; +import { generateKeyPair } from '../../Curve.js'; import AccountManager from '../../textsecure/AccountManager.js'; import type { KyberPreKeyType, @@ -29,7 +30,7 @@ describe('AccountManager', () => { const ourAci = generateAci(); const ourPni = generatePni(); - const identityKey = window.Signal.Curve.generateKeyPair(); + const identityKey = generateKeyPair(); const pubKey = getRandomBytes(33); const privKey = getRandomBytes(32); diff --git a/ts/test-node/util/retryPlaceholders_test.ts b/ts/test-node/services/retryPlaceholders_test.ts similarity index 93% rename from ts/test-node/util/retryPlaceholders_test.ts rename to ts/test-node/services/retryPlaceholders_test.ts index 6e30086329..f1c2cf92d2 100644 --- a/ts/test-node/util/retryPlaceholders_test.ts +++ b/ts/test-node/services/retryPlaceholders_test.ts @@ -5,12 +5,12 @@ import { assert } from 'chai'; import sinon from 'sinon'; import { generateAci } from '../../types/ServiceId.js'; -import type { RetryItemType } from '../../util/retryPlaceholders.js'; +import type { RetryItemType } from '../../services/retryPlaceholders.js'; import { getDeltaIntoPast, RetryPlaceholders, STORAGE_KEY, -} from '../../util/retryPlaceholders.js'; +} from '../../services/retryPlaceholders.js'; /* eslint-disable @typescript-eslint/no-explicit-any */ @@ -50,6 +50,7 @@ describe('RetryPlaceholders', () => { await window.storage.put(STORAGE_KEY, items); const placeholders = new RetryPlaceholders(); + placeholders.start(window.storage); assert.strictEqual(2, placeholders.getCount()); }); @@ -60,6 +61,7 @@ describe('RetryPlaceholders', () => { ] as any); const placeholders = new RetryPlaceholders(); + placeholders.start(window.storage); assert.strictEqual(0, placeholders.getCount()); }); @@ -68,12 +70,14 @@ describe('RetryPlaceholders', () => { describe('#add', () => { it('adds one item', async () => { const placeholders = new RetryPlaceholders(); + placeholders.start(window.storage); await placeholders.add(getDefaultItem()); assert.strictEqual(1, placeholders.getCount()); }); it('throws if provided data fails to parse', async () => { const placeholders = new RetryPlaceholders(); + placeholders.start(window.storage); await assert.isRejected( placeholders.add({ item: 'is wrong shape!', @@ -86,6 +90,7 @@ describe('RetryPlaceholders', () => { describe('#getNextToExpire', () => { it('returns nothing if no items', () => { const placeholders = new RetryPlaceholders(); + placeholders.start(window.storage); assert.strictEqual(0, placeholders.getCount()); assert.isUndefined(placeholders.getNextToExpire()); }); @@ -95,6 +100,7 @@ describe('RetryPlaceholders', () => { await window.storage.put(STORAGE_KEY, items); const placeholders = new RetryPlaceholders(); + placeholders.start(window.storage); assert.strictEqual(1, placeholders.getCount()); assert.deepEqual(item, placeholders.getNextToExpire()); }); @@ -111,6 +117,7 @@ describe('RetryPlaceholders', () => { await window.storage.put(STORAGE_KEY, items); const placeholders = new RetryPlaceholders(); + placeholders.start(window.storage); assert.strictEqual(2, placeholders.getCount()); assert.deepEqual(older, placeholders.getNextToExpire()); @@ -139,6 +146,7 @@ describe('RetryPlaceholders', () => { await window.storage.put(STORAGE_KEY, items); const placeholders = new RetryPlaceholders(); + placeholders.start(window.storage); assert.strictEqual(2, placeholders.getCount()); assert.deepEqual([], await placeholders.getExpiredAndRemove()); assert.strictEqual(2, placeholders.getCount()); @@ -156,6 +164,7 @@ describe('RetryPlaceholders', () => { await window.storage.put(STORAGE_KEY, items); const placeholders = new RetryPlaceholders(); + placeholders.start(window.storage); assert.strictEqual(2, placeholders.getCount()); assert.deepEqual([older], await placeholders.getExpiredAndRemove()); assert.strictEqual(1, placeholders.getCount()); @@ -174,6 +183,7 @@ describe('RetryPlaceholders', () => { await window.storage.put(STORAGE_KEY, items); const placeholders = new RetryPlaceholders(); + placeholders.start(window.storage); assert.strictEqual(2, placeholders.getCount()); assert.deepEqual( [older, newer], @@ -197,6 +207,7 @@ describe('RetryPlaceholders', () => { await window.storage.put(STORAGE_KEY, items); const placeholders = new RetryPlaceholders(); + placeholders.start(window.storage); assert.strictEqual(2, placeholders.getCount()); await placeholders.findByConversationAndMarkOpened('conversation-id-3'); assert.strictEqual(2, placeholders.getCount()); @@ -224,6 +235,7 @@ describe('RetryPlaceholders', () => { await window.storage.put(STORAGE_KEY, items); const placeholders = new RetryPlaceholders(); + placeholders.start(window.storage); assert.strictEqual(3, placeholders.getCount()); await placeholders.findByConversationAndMarkOpened('conversation-id-1'); assert.strictEqual(3, placeholders.getCount()); @@ -298,6 +310,7 @@ describe('RetryPlaceholders', () => { await window.storage.put(STORAGE_KEY, items); const placeholders = new RetryPlaceholders(); + placeholders.start(window.storage); assert.strictEqual(2, placeholders.getCount()); assert.isUndefined( await placeholders.findByMessageAndRemove('conversation-id-1', sentAt) @@ -321,6 +334,7 @@ describe('RetryPlaceholders', () => { await window.storage.put(STORAGE_KEY, items); const placeholders = new RetryPlaceholders(); + placeholders.start(window.storage); assert.strictEqual(2, placeholders.getCount()); assert.deepEqual( newer, diff --git a/ts/test-node/types/Stickers_test.ts b/ts/test-node/types/Stickers_test.ts index 3569ce0ace..d4481c7171 100644 --- a/ts/test-node/types/Stickers_test.ts +++ b/ts/test-node/types/Stickers_test.ts @@ -3,6 +3,7 @@ import { assert } from 'chai'; import * as Stickers from '../../types/Stickers.js'; +import { isPackIdValid, redactPackId } from '../../util/Stickers.js'; describe('Stickers', () => { describe('getDataFromLink', () => { @@ -103,45 +104,41 @@ describe('Stickers', () => { describe('isPackIdValid', () => { it('returns false for non-strings', () => { - assert.isFalse(Stickers.isPackIdValid(undefined)); - assert.isFalse(Stickers.isPackIdValid(null)); - assert.isFalse(Stickers.isPackIdValid(123)); - assert.isFalse(Stickers.isPackIdValid(123)); - assert.isFalse( - Stickers.isPackIdValid(['b9439fa5fdc8b9873fe64f01b88b8ccf']) - ); + assert.isFalse(isPackIdValid(undefined)); + assert.isFalse(isPackIdValid(null)); + assert.isFalse(isPackIdValid(123)); + assert.isFalse(isPackIdValid(123)); + assert.isFalse(isPackIdValid(['b9439fa5fdc8b9873fe64f01b88b8ccf'])); assert.isFalse( // eslint-disable-next-line no-new-wrappers - Stickers.isPackIdValid(new String('b9439fa5fdc8b9873fe64f01b88b8ccf')) + isPackIdValid(new String('b9439fa5fdc8b9873fe64f01b88b8ccf')) ); }); it('returns false for invalid pack IDs', () => { - assert.isFalse(Stickers.isPackIdValid('')); - assert.isFalse( - Stickers.isPackIdValid('x9439fa5fdc8b9873fe64f01b88b8ccf') - ); + assert.isFalse(isPackIdValid('')); + assert.isFalse(isPackIdValid('x9439fa5fdc8b9873fe64f01b88b8ccf')); assert.isFalse( // This is one character too short. - Stickers.isPackIdValid('b9439fa5fdc8b9873fe64f01b88b8cc') + isPackIdValid('b9439fa5fdc8b9873fe64f01b88b8cc') ); assert.isFalse( // This is one character too long. - Stickers.isPackIdValid('b9439fa5fdc8b9873fe64f01b88b8ccfa') + isPackIdValid('b9439fa5fdc8b9873fe64f01b88b8ccfa') ); }); it('returns true for valid pack IDs', () => { - assert.isTrue(Stickers.isPackIdValid('b9439fa5fdc8b9873fe64f01b88b8ccf')); - assert.isTrue(Stickers.isPackIdValid('3eff225a1036a58a7530b312dd92f8d8')); - assert.isTrue(Stickers.isPackIdValid('DDFD48B8097DA7A4E928192B10963F6A')); + assert.isTrue(isPackIdValid('b9439fa5fdc8b9873fe64f01b88b8ccf')); + assert.isTrue(isPackIdValid('3eff225a1036a58a7530b312dd92f8d8')); + assert.isTrue(isPackIdValid('DDFD48B8097DA7A4E928192B10963F6A')); }); }); describe('redactPackId', () => { it('redacts pack IDs', () => { assert.strictEqual( - Stickers.redactPackId('b9439fa5fdc8b9873fe64f01b88b8ccf'), + redactPackId('b9439fa5fdc8b9873fe64f01b88b8ccf'), '[REDACTED]ccf' ); }); diff --git a/ts/textsecure/MessageReceiver.ts b/ts/textsecure/MessageReceiver.ts index 76d218dfba..ca2bfa081b 100644 --- a/ts/textsecure/MessageReceiver.ts +++ b/ts/textsecure/MessageReceiver.ts @@ -66,6 +66,8 @@ import { } from '../types/ServiceId.js'; import { normalizeAci } from '../util/normalizeAci.js'; import { isAciString } from '../util/isAciString.js'; +import { calling } from '../services/calling.js'; +import { retryPlaceholders } from '../services/retryPlaceholders.js'; import * as Errors from '../types/errors.js'; import { isPQRatchetEnabled } from '../util/isPQRatchetEnabled.js'; @@ -2517,12 +2519,6 @@ export default class MessageReceiver async #maybeUpdateTimestamp( envelope: UnsealedEnvelope ): Promise { - const { retryPlaceholders } = window.Signal.Services; - if (!retryPlaceholders) { - log.warn('maybeUpdateTimestamp: retry placeholders not available!'); - return envelope; - } - const { timestamp } = envelope; const identifier = envelope.groupId || envelope.sourceServiceId; const conversation = window.ConversationController.get(identifier); @@ -2773,10 +2769,7 @@ export default class MessageReceiver } log.info(`${logId}: Passing to ringrtc`); - await window.Signal.Services.calling.handleCallingMessage( - envelope, - callingMessage - ); + await calling.handleCallingMessage(envelope, callingMessage); } async #handleReceiptMessage( diff --git a/ts/textsecure/WebAPI.ts b/ts/textsecure/WebAPI.ts index e746b2e6bb..aa414b9da7 100644 --- a/ts/textsecure/WebAPI.ts +++ b/ts/textsecure/WebAPI.ts @@ -38,7 +38,7 @@ import type { FetchFunctionType } from '../util/uploads/tusProtocol.js'; import { VerificationTransport } from '../types/VerificationTransport.js'; import { ZERO_ACCESS_KEY } from '../types/SealedSender.js'; import { toLogFormat } from '../types/errors.js'; -import { isPackIdValid, redactPackId } from '../types/Stickers.js'; +import { isPackIdValid, redactPackId } from '../util/Stickers.js'; import type { ServiceIdString, AciString, @@ -64,6 +64,7 @@ import { import type { CDSAuthType, CDSResponseType } from './cds/Types.d.ts'; import { CDSI } from './cds/CDSI.js'; import { SignalService as Proto } from '../protobuf/index.js'; +import { isEnabled as isRemoteConfigEnabled } from '../RemoteConfig.js'; import { HTTPError } from './Errors.js'; import type MessageSender from './SendMessage.js'; @@ -2030,34 +2031,24 @@ export function initialize({ let activeRegistration: ExplodePromiseResultType | undefined; const libsignalRemoteConfig = new Map(); - if ( - window.Signal.RemoteConfig.isEnabled( - 'desktop.libsignalNet.enforceMinimumTls' - ) - ) { + if (isRemoteConfigEnabled('desktop.libsignalNet.enforceMinimumTls')) { log.info('libsignal net will require TLS 1.3'); libsignalRemoteConfig.set('enforceMinimumTls', 'true'); } if ( - window.Signal.RemoteConfig.isEnabled( - 'desktop.libsignalNet.shadowUnauthChatWithNoise' - ) + isRemoteConfigEnabled('desktop.libsignalNet.shadowUnauthChatWithNoise') ) { log.info('libsignal net will shadow unauth chat connections'); libsignalRemoteConfig.set('shadowUnauthChatWithNoise', 'true'); } - if ( - window.Signal.RemoteConfig.isEnabled( - 'desktop.libsignalNet.shadowAuthChatWithNoise' - ) - ) { + if (isRemoteConfigEnabled('desktop.libsignalNet.shadowAuthChatWithNoise')) { log.info('libsignal net will shadow auth chat connections'); libsignalRemoteConfig.set('shadowAuthChatWithNoise', 'true'); } const perMessageDeflateConfigKey = isProduction(version) ? 'desktop.libsignalNet.chatPermessageDeflate.prod' : 'desktop.libsignalNet.chatPermessageDeflate'; - if (window.Signal.RemoteConfig.isEnabled(perMessageDeflateConfigKey)) { + if (isRemoteConfigEnabled(perMessageDeflateConfigKey)) { libsignalRemoteConfig.set('chatPermessageDeflate', 'true'); } libsignalNet.setRemoteConfig(libsignalRemoteConfig); diff --git a/ts/textsecure/downloadAttachment.ts b/ts/textsecure/downloadAttachment.ts index d33fc98d25..0b7fb78786 100644 --- a/ts/textsecure/downloadAttachment.ts +++ b/ts/textsecure/downloadAttachment.ts @@ -249,7 +249,7 @@ export async function downloadAttachment( }); } catch (error) { if (error instanceof HTTPError && error.code === 401) { - window.Signal.Services.backups.credentials.onCdnCredentialError(); + backupsService.credentials.onCdnCredentialError(); } throw error; } diff --git a/ts/types/Attachment.ts b/ts/types/Attachment.ts index db34d7a7a7..5d4af1c169 100644 --- a/ts/types/Attachment.ts +++ b/ts/types/Attachment.ts @@ -24,7 +24,7 @@ import type { import { ThemeType } from './Util.js'; import * as GoogleChrome from '../util/GoogleChrome.js'; import { ReadStatus } from '../messages/MessageReadStatus.js'; -import type { MessageStatusType } from '../components/conversation/Message.js'; +import type { MessageStatusType } from './message/MessageStatus.js'; import type { SignalService as Proto } from '../protobuf/index.js'; import { isMoreRecentThan } from '../util/timestamp.js'; import { DAY } from '../util/durations/index.js'; diff --git a/ts/types/BodyRange.ts b/ts/types/BodyRange.ts index 010ffcb852..61cbe7133c 100644 --- a/ts/types/BodyRange.ts +++ b/ts/types/BodyRange.ts @@ -9,7 +9,6 @@ import { SignalService as Proto } from '../protobuf/index.js'; import { createLogger } from '../logging/log.js'; import { missingCaseError } from '../util/missingCaseError.js'; import { isNotNil } from '../util/isNotNil.js'; -import type { ConversationType } from '../state/ducks/conversations.js'; import { SNIPPET_LEFT_PLACEHOLDER, SNIPPET_RIGHT_PLACEHOLDER, @@ -219,7 +218,7 @@ export function filterAndClean( export function hydrateRanges( ranges: ReadonlyArray> | undefined, - conversationSelector: (id: string) => ConversationType + conversationSelector: (id: string) => { id: string; title: string } ): Array | undefined { if (!ranges) { return undefined; diff --git a/ts/types/PhoneNumberSharingMode.ts b/ts/types/PhoneNumberSharingMode.ts new file mode 100644 index 0000000000..86af8810f0 --- /dev/null +++ b/ts/types/PhoneNumberSharingMode.ts @@ -0,0 +1,16 @@ +// Copyright 2025 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +import { makeEnumParser } from '../util/enum.js'; + +// These strings are saved to disk, so be careful when changing them. +export enum PhoneNumberSharingMode { + Everybody = 'Everybody', + ContactsOnly = 'ContactsOnly', + Nobody = 'Nobody', +} + +export const parsePhoneNumberSharingMode = makeEnumParser( + PhoneNumberSharingMode, + PhoneNumberSharingMode.Nobody +); diff --git a/ts/types/Stickers.ts b/ts/types/Stickers.ts index b711db67a1..d300231506 100644 --- a/ts/types/Stickers.ts +++ b/ts/types/Stickers.ts @@ -34,6 +34,7 @@ import { drop } from '../util/drop.js'; import { isNotNil } from '../util/isNotNil.js'; import { encryptLegacyAttachment } from '../util/encryptLegacyAttachment.js'; import { AttachmentDisposition } from '../util/getLocalAttachmentUrl.js'; +import { isPackIdValid, redactPackId } from '../util/Stickers.js'; import { getPlaintextHashForInMemoryAttachment } from '../AttachmentCrypto.js'; const { isNumber, reject, groupBy, values, chunk } = lodash; @@ -133,8 +134,6 @@ const STICKER_PACK_DEFAULTS: StickerPackType = { storageNeedsSync: false, }; -const VALID_PACK_ID_REGEXP = /^[0-9a-f]{32}$/i; - const DOWNLOAD_PRIORITY_NORMAL = 0; const DOWNLOAD_PRIORITY_HIGH = 1; @@ -397,14 +396,6 @@ export function getInitialState(): StickersStateType { return initialState; } -export function isPackIdValid(packId: unknown): packId is string { - return typeof packId === 'string' && VALID_PACK_ID_REGEXP.test(packId); -} - -export function redactPackId(packId: string): string { - return `[REDACTED]${packId.slice(-3)}`; -} - function getReduxStickerActions() { const actions = window.reduxActions; strictAssert(actions && actions.stickers, 'Redux not ready'); diff --git a/ts/types/Storage.d.ts b/ts/types/Storage.d.ts index 1414c89fbd..854ae6ad5b 100644 --- a/ts/types/Storage.d.ts +++ b/ts/types/Storage.d.ts @@ -7,7 +7,6 @@ import type { DefaultConversationColorType, } from './Colors.js'; import type { PhoneNumberDiscoverability } from '../util/phoneNumberDiscoverability.js'; -import type { PhoneNumberSharingMode } from '../util/phoneNumberSharingMode.js'; import type { RetryItemType } from '../util/retryPlaceholders.js'; import type { ConfigMapType as RemoteConfigType } from '../RemoteConfig.js'; import type { ExtendedStorageID, UnknownRecord } from './StorageService.d.ts'; @@ -26,6 +25,7 @@ import type { ServiceIdString } from './ServiceId.js'; import type { RegisteredChallengeType } from '../challenge.js'; import type { ServerAlertsType } from '../util/handleServerAlerts.js'; import type { NotificationProfileOverride } from './NotificationProfile.js'; +import type { PhoneNumberSharingMode } from './PhoneNumberSharingMode.js'; export type AutoDownloadAttachmentType = { photos: boolean; diff --git a/ts/types/groups.ts b/ts/types/groups.ts new file mode 100644 index 0000000000..addb7e6077 --- /dev/null +++ b/ts/types/groups.ts @@ -0,0 +1,156 @@ +// Copyright 2025 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +import type { ServiceIdString, AciString, PniString } from './ServiceId.js'; + +export const ID_V1_LENGTH = 16; +export const ID_LENGTH = 32; + +type GroupV2AccessCreateChangeType = { + type: 'create'; +}; +type GroupV2AccessAttributesChangeType = { + type: 'access-attributes'; + newPrivilege: number; +}; +type GroupV2AccessMembersChangeType = { + type: 'access-members'; + newPrivilege: number; +}; +type GroupV2AccessInviteLinkChangeType = { + type: 'access-invite-link'; + newPrivilege: number; +}; +type GroupV2AnnouncementsOnlyChangeType = { + type: 'announcements-only'; + announcementsOnly: boolean; +}; +type GroupV2AvatarChangeType = { + type: 'avatar'; + removed: boolean; +}; +type GroupV2TitleChangeType = { + type: 'title'; + // Allow for null, because the title could be removed entirely + newTitle?: string; +}; +type GroupV2GroupLinkAddChangeType = { + type: 'group-link-add'; + privilege: number; +}; +type GroupV2GroupLinkResetChangeType = { + type: 'group-link-reset'; +}; +type GroupV2GroupLinkRemoveChangeType = { + type: 'group-link-remove'; +}; + +// No disappearing messages timer change type - message.expirationTimerUpdate used instead + +type GroupV2MemberAddChangeType = { + type: 'member-add'; + aci: AciString; +}; +type GroupV2MemberAddFromInviteChangeType = { + type: 'member-add-from-invite'; + aci: AciString; + pni?: PniString; + inviter?: AciString; +}; +type GroupV2MemberAddFromLinkChangeType = { + type: 'member-add-from-link'; + aci: AciString; +}; +type GroupV2MemberAddFromAdminApprovalChangeType = { + type: 'member-add-from-admin-approval'; + aci: AciString; +}; +type GroupV2MemberPrivilegeChangeType = { + type: 'member-privilege'; + aci: AciString; + newPrivilege: number; +}; +type GroupV2MemberRemoveChangeType = { + type: 'member-remove'; + aci: AciString; +}; + +type GroupV2PendingAddOneChangeType = { + type: 'pending-add-one'; + serviceId: ServiceIdString; +}; +type GroupV2PendingAddManyChangeType = { + type: 'pending-add-many'; + count: number; +}; +// Note: pending-remove is only used if user didn't also join the group at the same time +type GroupV2PendingRemoveOneChangeType = { + type: 'pending-remove-one'; + serviceId?: ServiceIdString; + inviter?: AciString; +}; +// Note: pending-remove is only used if user didn't also join the group at the same time +type GroupV2PendingRemoveManyChangeType = { + type: 'pending-remove-many'; + count: number; + inviter?: AciString; +}; + +type GroupV2AdminApprovalAddOneChangeType = { + type: 'admin-approval-add-one'; + aci: AciString; +}; +// Note: admin-approval-remove-one is only used if user didn't also join the group at +// the same time +type GroupV2AdminApprovalRemoveOneChangeType = { + type: 'admin-approval-remove-one'; + aci: AciString; + inviter?: AciString; +}; +type GroupV2AdminApprovalBounceChangeType = { + type: 'admin-approval-bounce'; + times: number; + isApprovalPending: boolean; + aci: AciString; +}; +export type GroupV2DescriptionChangeType = { + type: 'description'; + removed?: boolean; + // Adding this field; cannot remove previous field for backwards compatibility + description?: string; +}; +export type GroupV2SummaryType = { + type: 'summary'; +}; + +export type GroupV2ChangeDetailType = + | GroupV2AccessAttributesChangeType + | GroupV2AccessCreateChangeType + | GroupV2AccessInviteLinkChangeType + | GroupV2AccessMembersChangeType + | GroupV2AdminApprovalAddOneChangeType + | GroupV2AdminApprovalRemoveOneChangeType + | GroupV2AdminApprovalBounceChangeType + | GroupV2AnnouncementsOnlyChangeType + | GroupV2AvatarChangeType + | GroupV2DescriptionChangeType + | GroupV2GroupLinkAddChangeType + | GroupV2GroupLinkRemoveChangeType + | GroupV2GroupLinkResetChangeType + | GroupV2MemberAddChangeType + | GroupV2MemberAddFromAdminApprovalChangeType + | GroupV2MemberAddFromInviteChangeType + | GroupV2MemberAddFromLinkChangeType + | GroupV2MemberPrivilegeChangeType + | GroupV2MemberRemoveChangeType + | GroupV2PendingAddManyChangeType + | GroupV2PendingAddOneChangeType + | GroupV2PendingRemoveManyChangeType + | GroupV2PendingRemoveOneChangeType + | GroupV2SummaryType + | GroupV2TitleChangeType; + +export type GroupV2ChangeType = { + from?: ServiceIdString; + details: ReadonlyArray; +}; diff --git a/ts/types/message/MessageStatus.ts b/ts/types/message/MessageStatus.ts new file mode 100644 index 0000000000..8395d96bd9 --- /dev/null +++ b/ts/types/message/MessageStatus.ts @@ -0,0 +1,14 @@ +// Copyright 2025 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +export const MessageStatuses = [ + 'delivered', + 'error', + 'paused', + 'partial-sent', + 'read', + 'sending', + 'sent', + 'viewed', +] as const; +export type MessageStatusType = (typeof MessageStatuses)[number]; diff --git a/ts/util/Stickers.ts b/ts/util/Stickers.ts new file mode 100644 index 0000000000..4d2bda986d --- /dev/null +++ b/ts/util/Stickers.ts @@ -0,0 +1,12 @@ +// Copyright 2025 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +const VALID_PACK_ID_REGEXP = /^[0-9a-f]{32}$/i; + +export function isPackIdValid(packId: unknown): packId is string { + return typeof packId === 'string' && VALID_PACK_ID_REGEXP.test(packId); +} + +export function redactPackId(packId: string): string { + return `[REDACTED]${packId.slice(-3)}`; +} diff --git a/ts/util/backupSubscriptionData.ts b/ts/util/backupSubscriptionData.ts index a41d34a4ee..68c3803476 100644 --- a/ts/util/backupSubscriptionData.ts +++ b/ts/util/backupSubscriptionData.ts @@ -4,6 +4,7 @@ import Long from 'long'; import type { Backups, SignalService } from '../protobuf/index.js'; import * as Bytes from '../Bytes.js'; +import { backupsService } from '../services/backups/index.js'; import { drop } from './drop.js'; import { createLogger } from '../logging/log.js'; import { resetBackupMediaDownloadStats } from './backupMediaDownload.js'; @@ -23,7 +24,7 @@ export async function saveBackupsSubscriberData( const previousSubscriberId = window.storage.get('backupsSubscriberId'); if (previousSubscriberId !== backupsSubscriberData?.subscriberId) { - drop(window.Signal.Services.backups.refreshBackupAndSubscriptionStatus()); + drop(backupsService.refreshBackupAndSubscriptionStatus()); } if (backupsSubscriberData == null) { @@ -66,7 +67,7 @@ export async function saveBackupTier( if (backupTier !== previousBackupTier) { log.info('backup tier has changed', { previousBackupTier, backupTier }); await resetBackupMediaDownloadStats(); - drop(window.Signal.Services.backups.resetCachedData()); + drop(backupsService.resetCachedData()); } } diff --git a/ts/util/createIPCEvents.ts b/ts/util/createIPCEvents.ts index ed17a6a0c0..55863073ec 100644 --- a/ts/util/createIPCEvents.ts +++ b/ts/util/createIPCEvents.ts @@ -21,6 +21,7 @@ import { type NotificationClickData, notificationService, } from '../services/notifications.js'; +import { joinViaLink } from '../groups.js'; import { StoryViewModeType, StoryViewTargetType } from '../types/Stories.js'; import { isValidE164 } from './isValidE164.js'; import { fromWebSafeBase64 } from './webSafeBase64.js'; @@ -394,7 +395,7 @@ export function createIPCEvents( return; } try { - await window.Signal.Groups.joinViaLink(value); + await joinViaLink(value); } catch (error) { log.error( 'showGroupViaLink: Ran into an error!', diff --git a/ts/util/denyPendingApprovalRequest.ts b/ts/util/denyPendingApprovalRequest.ts index 6a3bde110d..28d8c452b4 100644 --- a/ts/util/denyPendingApprovalRequest.ts +++ b/ts/util/denyPendingApprovalRequest.ts @@ -5,6 +5,7 @@ import type { ConversationAttributesType } from '../model-types.d.ts'; import type { SignalService as Proto } from '../protobuf/index.js'; import type { AciString } from '../types/ServiceId.js'; import { createLogger } from '../logging/log.js'; +import { buildDeletePendingAdminApprovalMemberChange } from '../groups.js'; import { getConversationIdForLogging } from './idForLogging.js'; import { isMemberRequestingToJoin } from './groupMembershipUtils.js'; @@ -29,7 +30,7 @@ export async function denyPendingApprovalRequest( const ourAci = window.textsecure.storage.user.getCheckedAci(); - return window.Signal.Groups.buildDeletePendingAdminApprovalMemberChange({ + return buildDeletePendingAdminApprovalMemberChange({ group: conversationAttributes, ourAci, aci, diff --git a/ts/util/handleRetry.ts b/ts/util/handleRetry.ts index 04a2d9f10c..8aaed03850 100644 --- a/ts/util/handleRetry.ts +++ b/ts/util/handleRetry.ts @@ -12,6 +12,7 @@ import * as Bytes from '../Bytes.js'; import { DataReader, DataWriter } from '../sql/Client.js'; import { isProduction } from './version.js'; import { strictAssert } from './assert.js'; +import { lightSessionResetQueue } from './lightSessionResetQueue.js'; import { isGroupV2 } from './whatTypeOfConversation.js'; import { isOlderThan } from './timestamp.js'; import { parseIntOrThrow } from './parseIntOrThrow.js'; @@ -714,13 +715,6 @@ async function requestResend(decryptionError: DecryptionErrorEventData) { function scheduleSessionReset(senderAci: AciString, senderDevice: number) { // Postpone sending light session resets until the queue is empty - const { lightSessionResetQueue } = window.Signal.Services; - - if (!lightSessionResetQueue) { - throw new Error( - 'scheduleSessionReset: lightSessionResetQueue is not available!' - ); - } drop( lightSessionResetQueue.add(async () => { diff --git a/ts/util/lightSessionResetQueue.ts b/ts/util/lightSessionResetQueue.ts new file mode 100644 index 0000000000..f871dcd0b8 --- /dev/null +++ b/ts/util/lightSessionResetQueue.ts @@ -0,0 +1,8 @@ +// Copyright 2025 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +import PQueue from 'p-queue'; + +export const lightSessionResetQueue = new PQueue({ concurrency: 1 }); + +lightSessionResetQueue.pause(); diff --git a/ts/util/phoneNumberSharingMode.ts b/ts/util/phoneNumberSharingMode.ts index 762dfffacb..43f532550a 100644 --- a/ts/util/phoneNumberSharingMode.ts +++ b/ts/util/phoneNumberSharingMode.ts @@ -3,22 +3,13 @@ import type { ConversationAttributesType } from '../model-types.d.ts'; -import { makeEnumParser } from './enum.js'; +import { + PhoneNumberSharingMode, + parsePhoneNumberSharingMode, +} from '../types/PhoneNumberSharingMode.js'; import { missingCaseError } from './missingCaseError.js'; import { isDirectConversation, isMe } from './whatTypeOfConversation.js'; -// These strings are saved to disk, so be careful when changing them. -export enum PhoneNumberSharingMode { - Everybody = 'Everybody', - ContactsOnly = 'ContactsOnly', - Nobody = 'Nobody', -} - -export const parsePhoneNumberSharingMode = makeEnumParser( - PhoneNumberSharingMode, - PhoneNumberSharingMode.Nobody -); - export const isSharingPhoneNumberWithEverybody = (): boolean => { const phoneNumberSharingMode = parsePhoneNumberSharingMode( window.storage.get('phoneNumberSharingMode') diff --git a/ts/util/removePendingMember.ts b/ts/util/removePendingMember.ts index c94d2b8e25..bc73c5ea56 100644 --- a/ts/util/removePendingMember.ts +++ b/ts/util/removePendingMember.ts @@ -4,6 +4,7 @@ import type { ConversationAttributesType } from '../model-types.d.ts'; import type { SignalService as Proto } from '../protobuf/index.js'; import type { ServiceIdString } from '../types/ServiceId.js'; +import { buildDeletePendingMemberChange } from '../groups.js'; import { createLogger } from '../logging/log.js'; import { getConversationIdForLogging } from './idForLogging.js'; import { isMemberPending } from './groupMembershipUtils.js'; @@ -37,7 +38,7 @@ export async function removePendingMember( return undefined; } - return window.Signal.Groups.buildDeletePendingMemberChange({ + return buildDeletePendingMemberChange({ group: conversationAttributes, serviceIds: pendingServiceIds, }); diff --git a/ts/util/whatTypeOfConversation.ts b/ts/util/whatTypeOfConversation.ts index 225bacdbec..b71e3da75e 100644 --- a/ts/util/whatTypeOfConversation.ts +++ b/ts/util/whatTypeOfConversation.ts @@ -5,6 +5,7 @@ import type { ConversationAttributesType } from '../model-types.d.ts'; import type { ConversationType } from '../state/ducks/conversations.js'; import * as Bytes from '../Bytes.js'; import { createLogger } from '../logging/log.js'; +import { ID_V1_LENGTH, ID_LENGTH } from '../types/groups.js'; const log = createLogger('whatTypeOfConversation'); @@ -54,7 +55,7 @@ export function isGroupV1( } const buffer = Bytes.fromBinary(groupId); - return buffer.byteLength === window.Signal.Groups.ID_V1_LENGTH; + return buffer.byteLength === ID_V1_LENGTH; } export function isGroupV2( @@ -70,8 +71,7 @@ export function isGroupV2( try { return ( - groupVersion === 2 && - Bytes.fromBase64(groupId).byteLength === window.Signal.Groups.ID_LENGTH + groupVersion === 2 && Bytes.fromBase64(groupId).byteLength === ID_LENGTH ); } catch (error) { log.error('isGroupV2: Failed to process groupId in base64!'); diff --git a/ts/window.d.ts b/ts/window.d.ts index 6488334d56..34151e3208 100644 --- a/ts/window.d.ts +++ b/ts/window.d.ts @@ -19,14 +19,6 @@ import type { } from './challenge.js'; import type AccountManager from './textsecure/AccountManager.js'; import type { WebAPIConnectType } from './textsecure/WebAPI.js'; -import type { CallingClass } from './services/calling.js'; -import type * as Donations from './services/donations.js'; -import type * as StorageService from './services/storage.js'; -import type { BackupsService } from './services/backups/index.js'; -import type * as Groups from './groups.js'; -import type * as Crypto from './Crypto.js'; -import type * as Curve from './Curve.js'; -import type * as RemoteConfig from './RemoteConfig.js'; import type { OSType } from './util/os/shared.js'; import type { LocalizerType, @@ -36,11 +28,8 @@ import type { import type { Receipt } from './types/Receipt.js'; import type { ConversationController } from './ConversationController.js'; import type { ReduxActions } from './state/types.js'; -import type { createApp } from './state/roots/createApp.js'; import type { BatcherType } from './util/batcher.js'; -import type { ConfirmationDialog } from './components/ConfirmationDialog.js'; import type { SignalProtocolStore } from './SignalProtocolStore.js'; -import type { SocketStatus } from './types/SocketStatus.js'; import type { ScreenShareStatus } from './types/Calling.js'; import type { MessageCache } from './services/MessageCache.js'; import type { StateType } from './state/reducer.js'; @@ -51,12 +40,10 @@ import type { IPCEventsType } from './util/createIPCEvents.js'; import type { SignalContextType } from './windows/context.js'; import type * as Message2 from './types/Message2.js'; import type { initializeMigrations } from './signal.js'; -import type { RetryPlaceholders } from './util/retryPlaceholders.js'; import type { PropsPreloadType as PreferencesPropsType } from './components/Preferences.js'; import type { WindowsNotificationData } from './services/notifications.js'; import type { QueryStatsOptions } from './sql/main.js'; import type { SocketStatuses } from './textsecure/SocketManager.js'; -import type { BeforeNavigateService } from './services/BeforeNavigate.js'; const { PhoneNumber, PhoneNumberFormat } = googleLibphonenumber; @@ -149,27 +136,14 @@ type SettingsWindowPropsType = { export type SignalCoreType = { AboutWindowProps?: AboutWindowPropsType; - Crypto: typeof Crypto; - Curve: typeof Curve; DebugLogWindowProps?: DebugLogWindowPropsType; - Groups: typeof Groups; PermissionsWindowProps?: PermissionsWindowPropsType; - RemoteConfig: typeof RemoteConfig; ScreenShareWindowProps?: ScreenShareWindowPropsType; Services: { - backups: BackupsService; - beforeNavigate: BeforeNavigateService; - calling: CallingClass; - initializeGroupCredentialFetcher: () => Promise; - initializeNetworkObserver: ( - network: ReduxActions['network'], - getAuthSocketStatus: () => SocketStatus - ) => void; - initializeUpdateListener: (updates: ReduxActions['updates']) => void; - lightSessionResetQueue?: PQueue; - retryPlaceholders?: RetryPlaceholders; - storage: typeof StorageService; - donations: typeof Donations; + // Only for development + backups: unknown; + calling: unknown; + donations: unknown; }; SettingsWindowProps?: SettingsWindowPropsType; Migrations: ReturnType; @@ -178,15 +152,7 @@ export type SignalCoreType = { Address: typeof Address; QualifiedAddress: typeof QualifiedAddress; }; - Components: { - ConfirmationDialog: typeof ConfirmationDialog; - }; OS: OSType; - State: { - Roots: { - createApp: typeof createApp; - }; - }; challengeHandler?: ChallengeHandler; // Only for debugging in Dev Tools diff --git a/ts/windows/main/phase1-ipc.ts b/ts/windows/main/phase1-ipc.ts index 375ce91775..627a309dee 100644 --- a/ts/windows/main/phase1-ipc.ts +++ b/ts/windows/main/phase1-ipc.ts @@ -22,6 +22,7 @@ import { drop } from '../../util/drop.js'; import { explodePromise } from '../../util/explodePromise.js'; import { DataReader } from '../../sql/Client.js'; import type { WindowsNotificationData } from '../../services/notifications.js'; +import { finish3dsValidation } from '../../services/donations.js'; import { AggregatedStats } from '../../textsecure/WebsocketResources.js'; import { UNAUTHENTICATED_CHANNEL_NAME } from '../../textsecure/SocketManager.js'; import { isProduction } from '../../util/version.js'; @@ -415,7 +416,7 @@ ipc.on('cancel-presenting', () => { }); ipc.on('donation-validation-complete', (_event, { token }) => { - drop(window.Signal.Services.donations.finish3dsValidation(token)); + drop(finish3dsValidation(token)); }); ipc.on('show-conversation-via-token', (_event, token: string) => { diff --git a/ts/windows/main/start.ts b/ts/windows/main/start.ts index 8deffacd7b..af72f332f3 100644 --- a/ts/windows/main/start.ts +++ b/ts/windows/main/start.ts @@ -25,6 +25,7 @@ import type { import type { FeatureFlagType } from '../../window.d.ts'; import type { StorageAccessType } from '../../types/Storage.d.ts'; import { initMessageCleanup } from '../../services/messageStateCleanup.js'; +import { calling } from '../../services/calling.js'; import { Environment, getEnvironment } from '../../environment.js'; import { isProduction } from '../../util/version.js'; import { benchmarkConversationOpen } from '../../CI/benchmarkConversationOpen.js'; @@ -83,9 +84,8 @@ if ( return message?.attributes; }, getReduxState: () => window.reduxStore.getState(), - getSfuUrl: () => window.Signal.Services.calling._sfuUrl, - getIceServerOverride: () => - window.Signal.Services.calling._iceServerOverride, + getSfuUrl: () => calling._sfuUrl, + getIceServerOverride: () => calling._iceServerOverride, getSocketStatus: () => window.textsecure.server?.getSocketStatus(), getStorageItem: (name: keyof StorageAccessType) => window.storage.get(name), putStorageItem: ( @@ -99,7 +99,7 @@ if ( window.Flags[name] = value; }, setSfuUrl: (url: string) => { - window.Signal.Services.calling._sfuUrl = url; + calling._sfuUrl = url; }, setIceServerOverride: ( override: GetIceServersResultType | string | undefined @@ -112,10 +112,10 @@ if ( } } - window.Signal.Services.calling._iceServerOverride = override; + calling._iceServerOverride = override; }, setRtcStatsInterval: (intervalMillis: number) => - window.Signal.Services.calling.setAllRtcStatsInterval(intervalMillis), + calling.setAllRtcStatsInterval(intervalMillis), ...(window.SignalContext.config.ciMode === 'benchmark' ? { benchmarkConversationOpen,