diff --git a/ts/components/CallManager.stories.tsx b/ts/components/CallManager.stories.tsx index f8322d1fef..6c675f9076 100644 --- a/ts/components/CallManager.stories.tsx +++ b/ts/components/CallManager.stories.tsx @@ -23,9 +23,7 @@ import { generateAci } from '../types/ServiceId'; import { getDefaultConversation } from '../test-both/helpers/getDefaultConversation'; import { fakeGetGroupCallVideoFrameSource } from '../test-both/helpers/fakeGetGroupCallVideoFrameSource'; import { setupI18n } from '../util/setupI18n'; -import type { SafetyNumberProps } from './SafetyNumberChangeDialog'; import enMessages from '../../_locales/en/messages.json'; -import { ThemeType } from '../types/Util'; import { StorySendMode } from '../types/Stories'; const i18n = setupI18n('en', enMessages); @@ -69,7 +67,6 @@ const createProps = (storyProps: Partial = {}): PropsType => ({ declineCall: action('decline-call'), getGroupCallVideoFrameSource: (_: string, demuxId: number) => fakeGetGroupCallVideoFrameSource(demuxId), - getPreferredBadge: () => undefined, getPresentingSources: action('get-presenting-sources'), hangUpActiveCall: action('hang-up-active-call'), hasInitialLoadCompleted: true, @@ -78,7 +75,6 @@ const createProps = (storyProps: Partial = {}): PropsType => ({ callLink: undefined, isGroupCallRaiseHandEnabled: true, isGroupCallReactionsEnabled: true, - keyChangeOk: action('key-change-ok'), me: { ...getDefaultConversation({ color: AvatarColors[0], @@ -92,7 +88,6 @@ const createProps = (storyProps: Partial = {}): PropsType => ({ renderDeviceSelection: () =>
, renderEmojiPicker: () => <>EmojiPicker, renderReactionPicker: () =>
, - renderSafetyNumberViewer: (_: SafetyNumberProps) =>
, sendGroupCallRaiseHand: action('send-group-call-raise-hand'), sendGroupCallReaction: action('send-group-call-reaction'), setGroupCallVideoRequest: action('set-group-call-video-request'), @@ -108,7 +103,6 @@ const createProps = (storyProps: Partial = {}): PropsType => ({ stopRingtone: action('stop-ringtone'), switchToPresentationView: action('switch-to-presentation-view'), switchFromPresentationView: action('switch-from-presentation-view'), - theme: ThemeType.light, toggleParticipants: action('toggle-participants'), togglePip: action('toggle-pip'), toggleScreenRecordingPermissionsDialog: action( @@ -155,7 +149,6 @@ export function OngoingGroupCall(): JSX.Element { ...getCommonActiveCallData(), callMode: CallMode.Group, connectionState: GroupCallConnectionState.Connected, - conversationsWithSafetyNumberChanges: [], conversationsByDemuxId: new Map(), deviceCount: 0, joinState: GroupCallJoinState.Joined, @@ -232,35 +225,3 @@ export function CallRequestNeeded(): JSX.Element { /> ); } - -export function GroupCallSafetyNumberChanged(): JSX.Element { - return ( - (), - deviceCount: 0, - joinState: GroupCallJoinState.Joined, - localDemuxId: 1, - maxDevices: 5, - groupMembers: [], - isConversationTooBigToRing: false, - peekedParticipants: [], - raisedHands: new Set(), - remoteParticipants: [], - remoteAudioLevels: new Map(), - }, - })} - /> - ); -} diff --git a/ts/components/CallManager.tsx b/ts/components/CallManager.tsx index bc219608e2..0d05df7d31 100644 --- a/ts/components/CallManager.tsx +++ b/ts/components/CallManager.tsx @@ -11,8 +11,6 @@ import { CallingParticipantsList } from './CallingParticipantsList'; import { CallingSelectPresentingSourcesModal } from './CallingSelectPresentingSourcesModal'; import { CallingPip } from './CallingPip'; import { IncomingCallBar } from './IncomingCallBar'; -import type { SafetyNumberProps } from './SafetyNumberChangeDialog'; -import { SafetyNumberChangeDialog } from './SafetyNumberChangeDialog'; import type { ActiveCallType, CallingConversationType, @@ -28,13 +26,11 @@ import { GroupCallJoinState, } from '../types/Calling'; import type { ConversationType } from '../state/ducks/conversations'; -import type { PreferredBadgeSelectorType } from '../state/selectors/badges'; import type { AcceptCallType, CancelCallType, DeclineCallType, GroupCallParticipantInfoType, - KeyChangeOkType, SendGroupCallRaiseHandType, SendGroupCallReactionType, SetGroupCallVideoRequestType, @@ -46,7 +42,7 @@ import type { } from '../state/ducks/calling'; import { CallLinkRestrictions } from '../types/CallLink'; import type { CallLinkType } from '../types/CallLink'; -import type { LocalizerType, ThemeType } from '../types/Util'; +import type { LocalizerType } from '../types/Util'; import { missingCaseError } from '../util/missingCaseError'; import { CallingToastProvider } from './CallingToast'; import type { SmartReactionPicker } from '../state/smart/ReactionPicker'; @@ -89,15 +85,12 @@ export type PropsType = { conversationId: string, demuxId: number ) => VideoFrameSource; - getPreferredBadge: PreferredBadgeSelectorType; getPresentingSources: () => void; incomingCall: DirectIncomingCall | GroupIncomingCall | null; - keyChangeOk: (_: KeyChangeOkType) => void; renderDeviceSelection: () => JSX.Element; renderReactionPicker: ( props: React.ComponentProps ) => JSX.Element; - renderSafetyNumberViewer: (props: SafetyNumberProps) => JSX.Element; startCall: (payload: StartCallType) => void; toggleParticipants: () => void; acceptCall: (_: AcceptCallType) => void; @@ -131,7 +124,6 @@ export type PropsType = { switchToPresentationView: () => void; switchFromPresentationView: () => void; hangUpActiveCall: (reason: string) => void; - theme: ThemeType; togglePip: () => void; toggleScreenRecordingPermissionsDialog: () => unknown; toggleSettings: () => void; @@ -168,16 +160,13 @@ function ActiveCallManager({ i18n, isGroupCallRaiseHandEnabled, isGroupCallReactionsEnabled, - keyChangeOk, getGroupCallVideoFrameSource, - getPreferredBadge, getPresentingSources, me, openSystemPreferencesAction, renderDeviceSelection, renderEmojiPicker, renderReactionPicker, - renderSafetyNumberViewer, sendGroupCallRaiseHand, sendGroupCallReaction, setGroupCallVideoRequest, @@ -191,7 +180,6 @@ function ActiveCallManager({ startCall, switchToPresentationView, switchFromPresentationView, - theme, toggleParticipants, togglePip, toggleScreenRecordingPermissionsDialog, @@ -263,10 +251,6 @@ function ActiveCallManager({ } }, [callLink, showToast]); - const onSafetyNumberDialogCancel = useCallback(() => { - hangUpActiveCall('safety number dialog cancel'); - }, [hangUpActiveCall]); - let isCallFull: boolean; let showCallLobby: boolean; let groupMembers: @@ -463,26 +447,6 @@ function ActiveCallManager({ participants={groupCallParticipantsForParticipantsList} /> ))} - {isGroupOrAdhocActiveCall(activeCall) && - activeCall.conversationsWithSafetyNumberChanges.length ? ( - { - keyChangeOk({ conversationId: activeCall.conversation.id }); - }} - renderSafetyNumber={renderSafetyNumberViewer} - theme={theme} - /> - ) : null} ); } @@ -499,7 +463,6 @@ export function CallManager({ closeNeedPermissionScreen, declineCall, getGroupCallVideoFrameSource, - getPreferredBadge, getPresentingSources, hangUpActiveCall, hasInitialLoadCompleted, @@ -508,7 +471,6 @@ export function CallManager({ isConversationTooBigToRing, isGroupCallRaiseHandEnabled, isGroupCallReactionsEnabled, - keyChangeOk, me, notifyForCall, openSystemPreferencesAction, @@ -517,7 +479,6 @@ export function CallManager({ renderDeviceSelection, renderEmojiPicker, renderReactionPicker, - renderSafetyNumberViewer, sendGroupCallRaiseHand, sendGroupCallReaction, setGroupCallVideoRequest, @@ -533,7 +494,6 @@ export function CallManager({ stopRingtone, switchFromPresentationView, switchToPresentationView, - theme, toggleParticipants, togglePip, toggleScreenRecordingPermissionsDialog, @@ -594,20 +554,17 @@ export function CallManager({ changeCallView={changeCallView} closeNeedPermissionScreen={closeNeedPermissionScreen} getGroupCallVideoFrameSource={getGroupCallVideoFrameSource} - getPreferredBadge={getPreferredBadge} getPresentingSources={getPresentingSources} hangUpActiveCall={hangUpActiveCall} i18n={i18n} isGroupCallRaiseHandEnabled={isGroupCallRaiseHandEnabled} isGroupCallReactionsEnabled={isGroupCallReactionsEnabled} - keyChangeOk={keyChangeOk} me={me} openSystemPreferencesAction={openSystemPreferencesAction} pauseVoiceNotePlayer={pauseVoiceNotePlayer} renderDeviceSelection={renderDeviceSelection} renderEmojiPicker={renderEmojiPicker} renderReactionPicker={renderReactionPicker} - renderSafetyNumberViewer={renderSafetyNumberViewer} sendGroupCallRaiseHand={sendGroupCallRaiseHand} sendGroupCallReaction={sendGroupCallReaction} setGroupCallVideoRequest={setGroupCallVideoRequest} @@ -621,7 +578,6 @@ export function CallManager({ startCall={startCall} switchFromPresentationView={switchFromPresentationView} switchToPresentationView={switchToPresentationView} - theme={theme} toggleParticipants={toggleParticipants} togglePip={togglePip} toggleScreenRecordingPermissionsDialog={ diff --git a/ts/components/CallScreen.stories.tsx b/ts/components/CallScreen.stories.tsx index 390e2376ed..72386d1e04 100644 --- a/ts/components/CallScreen.stories.tsx +++ b/ts/components/CallScreen.stories.tsx @@ -124,7 +124,6 @@ const createActiveGroupCallProp = (overrideProps: GroupCallOverrideProps) => ({ callMode: CallMode.Group as CallMode.Group, connectionState: overrideProps.connectionState || GroupCallConnectionState.Connected, - conversationsWithSafetyNumberChanges: [], conversationsByDemuxId: getConversationsByDemuxId(overrideProps), joinState: GroupCallJoinState.Joined, localDemuxId: LOCAL_DEMUX_ID, diff --git a/ts/components/CallingPip.stories.tsx b/ts/components/CallingPip.stories.tsx index 4e412f9301..29c595ee59 100644 --- a/ts/components/CallingPip.stories.tsx +++ b/ts/components/CallingPip.stories.tsx @@ -131,7 +131,6 @@ export function GroupCall(args: PropsType): JSX.Element { ...getCommonActiveCallData({}), callMode: CallMode.Group as CallMode.Group, connectionState: GroupCallConnectionState.Connected, - conversationsWithSafetyNumberChanges: [], conversationsByDemuxId: new Map(), groupMembers: times(3, () => getDefaultConversation()), isConversationTooBigToRing: false, diff --git a/ts/jobs/conversationJobQueue.ts b/ts/jobs/conversationJobQueue.ts index 9480b7ccc8..47a5dab825 100644 --- a/ts/jobs/conversationJobQueue.ts +++ b/ts/jobs/conversationJobQueue.ts @@ -12,7 +12,9 @@ import { jobQueueDatabaseStore } from './JobQueueDatabaseStore'; import { JOB_STATUS, JobQueue } from './JobQueue'; import { sendNormalMessage } from './helpers/sendNormalMessage'; +import { sendCallingMessage } from './helpers/sendCallingMessage'; import { sendDirectExpirationTimerUpdate } from './helpers/sendDirectExpirationTimerUpdate'; +import { sendGroupCallUpdate } from './helpers/sendGroupCallUpdate'; import { sendGroupUpdate } from './helpers/sendGroupUpdate'; import { sendDeleteForEveryone } from './helpers/sendDeleteForEveryone'; import { sendDeleteStoryForEveryone } from './helpers/sendDeleteStoryForEveryone'; @@ -51,9 +53,11 @@ import { clearTimeoutIfNecessary } from '../util/clearTimeoutIfNecessary'; // Note: generally, we only want to add to this list. If you do need to change one of // these values, you'll likely need to write a database migration. export const conversationQueueJobEnum = z.enum([ + 'CallingMessage', 'DeleteForEveryone', 'DeleteStoryForEveryone', 'DirectExpirationTimerUpdate', + 'GroupCallUpdate', 'GroupUpdate', 'NormalMessage', 'NullMessage', @@ -67,6 +71,17 @@ export const conversationQueueJobEnum = z.enum([ ]); type ConversationQueueJobEnum = z.infer; +const callingMessageJobDataSchema = z.object({ + type: z.literal(conversationQueueJobEnum.enum.CallingMessage), + conversationId: z.string(), + protoBase64: z.string(), + urgent: z.boolean(), + // These two are group-only + recipients: z.array(serviceIdSchema).optional(), + isPartialSend: z.boolean().optional(), +}); +export type CallingMessageJobData = z.infer; + const deleteForEveryoneJobDataSchema = z.object({ type: z.literal(conversationQueueJobEnum.enum.DeleteForEveryone), conversationId: z.string(), @@ -108,6 +123,16 @@ export type ExpirationTimerUpdateJobData = z.infer< typeof expirationTimerUpdateJobDataSchema >; +const groupCallUpdateJobDataSchema = z.object({ + type: z.literal(conversationQueueJobEnum.enum.GroupCallUpdate), + conversationId: z.string(), + eraId: z.string(), + urgent: z.boolean(), +}); +export type GroupCallUpdateJobData = z.infer< + typeof groupCallUpdateJobDataSchema +>; + const groupUpdateJobDataSchema = z.object({ type: z.literal(conversationQueueJobEnum.enum.GroupUpdate), conversationId: z.string(), @@ -208,9 +233,11 @@ const receiptsJobDataSchema = z.object({ export type ReceiptsJobData = z.infer; export const conversationQueueJobDataSchema = z.union([ + callingMessageJobDataSchema, deleteForEveryoneJobDataSchema, deleteStoryForEveryoneJobDataSchema, expirationTimerUpdateJobDataSchema, + groupCallUpdateJobDataSchema, groupUpdateJobDataSchema, normalMessageSendJobDataSchema, nullMessageJobDataSchema, @@ -239,6 +266,9 @@ const MAX_RETRY_TIME = durations.DAY; const MAX_ATTEMPTS = exponentialBackoffMaxAttempts(MAX_RETRY_TIME); function shouldSendShowCaptcha(type: ConversationQueueJobEnum): boolean { + if (type === 'CallingMessage') { + return true; + } if (type === 'DeleteForEveryone') { return true; } @@ -248,6 +278,9 @@ function shouldSendShowCaptcha(type: ConversationQueueJobEnum): boolean { if (type === 'DirectExpirationTimerUpdate') { return true; } + if (type === 'GroupCallUpdate') { + return true; + } if (type === 'GroupUpdate') { return true; } @@ -263,6 +296,9 @@ function shouldSendShowCaptcha(type: ConversationQueueJobEnum): boolean { if (type === 'Reaction') { return false; } + if (type === 'Receipts') { + return false; + } if (type === 'ResendRequest') { return false; } @@ -277,9 +313,6 @@ function shouldSendShowCaptcha(type: ConversationQueueJobEnum): boolean { if (type === 'Story') { return true; } - if (type === 'Receipts') { - return false; - } throw missingCaseError(type); } @@ -785,6 +818,9 @@ export class ConversationJobQueue extends JobQueue { try { switch (type) { + case jobSet.CallingMessage: + await sendCallingMessage(conversation, jobBundle, data); + break; case jobSet.DeleteForEveryone: await sendDeleteForEveryone(conversation, jobBundle, data); break; @@ -794,6 +830,9 @@ export class ConversationJobQueue extends JobQueue { case jobSet.DirectExpirationTimerUpdate: await sendDirectExpirationTimerUpdate(conversation, jobBundle, data); break; + case jobSet.GroupCallUpdate: + await sendGroupCallUpdate(conversation, jobBundle, data); + break; case jobSet.GroupUpdate: await sendGroupUpdate(conversation, jobBundle, data); break; diff --git a/ts/jobs/helpers/getValidRecipients.ts b/ts/jobs/helpers/getValidRecipients.ts new file mode 100644 index 0000000000..2e2a9054b8 --- /dev/null +++ b/ts/jobs/helpers/getValidRecipients.ts @@ -0,0 +1,40 @@ +// Copyright 2022 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +import { isNotNil } from '../../util/isNotNil'; + +import type { LoggerType } from '../../types/Logging'; +import type { ServiceIdString } from '../../types/ServiceId'; + +export function getValidRecipients( + recipients: Array, + options: { + logId: string; + log: LoggerType; + } +): Array { + const { log, logId } = options; + + return recipients + .map(id => { + const recipient = window.ConversationController.get(id); + if (!recipient) { + return undefined; + } + if (recipient.isUnregistered()) { + log.warn( + `${logId}: dropping unregistered recipient ${recipient.idForLogging()}` + ); + return undefined; + } + if (recipient.isBlocked()) { + log.warn( + `${logId}: dropping blocked recipient ${recipient.idForLogging()}` + ); + return undefined; + } + + return recipient.getSendTarget(); + }) + .filter(isNotNil); +} diff --git a/ts/jobs/helpers/sendCallingMessage.ts b/ts/jobs/helpers/sendCallingMessage.ts new file mode 100644 index 0000000000..b39ba5d698 --- /dev/null +++ b/ts/jobs/helpers/sendCallingMessage.ts @@ -0,0 +1,149 @@ +// Copyright 2022 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +import { handleMessageSend } from '../../util/handleMessageSend'; +import { getSendOptions } from '../../util/getSendOptions'; +import { + isDirectConversation, + isGroup, +} from '../../util/whatTypeOfConversation'; +import { SignalService as Proto } from '../../protobuf'; +import { + handleMultipleSendErrors, + maybeExpandErrors, +} from './handleMultipleSendErrors'; + +import type { ConversationModel } from '../../models/conversations'; +import type { + ConversationQueueJobBundle, + CallingMessageJobData, +} from '../conversationJobQueue'; +import { isConversationUnregistered } from '../../util/isConversationUnregistered'; +import { + OutgoingIdentityKeyError, + UnregisteredUserError, +} from '../../textsecure/Errors'; +import { getUntrustedConversationServiceIds } from './getUntrustedConversationServiceIds'; +import { sendContentMessageToGroup } from '../../util/sendToGroup'; +import * as Bytes from '../../Bytes'; +import { getValidRecipients } from './getValidRecipients'; + +export async function sendCallingMessage( + conversation: ConversationModel, + { + isFinalAttempt, + messaging, + shouldContinue, + timestamp, + timeRemaining, + log, + }: ConversationQueueJobBundle, + data: CallingMessageJobData +): Promise { + const logId = `sendCallingMessage(${conversation.idForLogging()}.${timestamp})`; + if (!shouldContinue) { + log.info(`${logId}: Ran out of time. Giving up.`); + return; + } + + log.info(`${logId}: Starting send`); + + if ( + isDirectConversation(conversation.attributes) && + isConversationUnregistered(conversation.attributes) + ) { + log.warn(`${logId}: Direct conversation is unregistered; refusing to send`); + return; + } + + const { + protoBase64, + urgent, + recipients: jobRecipients, + isPartialSend, + } = data; + + const recipients = getValidRecipients( + jobRecipients || conversation.getRecipients(), + { log, logId } + ); + + const untrustedServiceIds = getUntrustedConversationServiceIds(recipients); + if (untrustedServiceIds.length) { + window.reduxActions.conversations.conversationStoppedByMissingVerification({ + conversationId: conversation.id, + untrustedServiceIds, + }); + throw new Error( + `${logId}: Blocked because ${untrustedServiceIds.length} conversation(s) were untrusted. Failing this attempt.` + ); + } + + if (recipients.length === 0) { + log.warn(`${logId}: Giving up because there are no valid recipients.`); + return; + } + + const sendType = 'callingMessage'; + const sendOptions = await getSendOptions(conversation.attributes); + + const callingMessage = Proto.CallingMessage.decode( + Bytes.fromBase64(protoBase64) + ); + + const { ContentHint } = Proto.UnidentifiedSenderMessage.Message; + + try { + if (isGroup(conversation.attributes)) { + await handleMessageSend( + sendContentMessageToGroup({ + contentHint: ContentHint.DEFAULT, + contentMessage: new Proto.Content({ callingMessage }), + isPartialSend, + messageId: undefined, + recipients, + sendOptions, + sendTarget: conversation.toSenderKeyTarget(), + sendType, + timestamp, + urgent, + }), + { messageIds: [], sendType } + ); + } else { + const sendTarget = conversation.getSendTarget(); + if (!sendTarget) { + log.error(`${logId}: Direct conversation send target is falsy`); + return; + } + await handleMessageSend( + messaging.sendCallingMessage( + sendTarget, + callingMessage, + timestamp, + urgent, + sendOptions + ), + { messageIds: [], sendType } + ); + } + } catch (error: unknown) { + if ( + error instanceof OutgoingIdentityKeyError || + error instanceof UnregisteredUserError + ) { + log.info( + `${logId}: Send failure was OutgoingIdentityKeyError or UnregisteredUserError. Cancelling job.` + ); + return; + } + + await handleMultipleSendErrors({ + errors: maybeExpandErrors(error), + isFinalAttempt, + log, + timeRemaining, + toThrow: error, + }); + } +} diff --git a/ts/jobs/helpers/sendGroupCallUpdate.ts b/ts/jobs/helpers/sendGroupCallUpdate.ts new file mode 100644 index 0000000000..d5b682e773 --- /dev/null +++ b/ts/jobs/helpers/sendGroupCallUpdate.ts @@ -0,0 +1,110 @@ +// Copyright 2022 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +import { getSendOptions } from '../../util/getSendOptions'; +import { isGroup } from '../../util/whatTypeOfConversation'; +import { SignalService as Proto } from '../../protobuf'; +import { + handleMultipleSendErrors, + maybeExpandErrors, +} from './handleMultipleSendErrors'; + +import type { ConversationModel } from '../../models/conversations'; +import type { + ConversationQueueJobBundle, + GroupCallUpdateJobData, +} from '../conversationJobQueue'; +import { getUntrustedConversationServiceIds } from './getUntrustedConversationServiceIds'; +import { sendToGroup } from '../../util/sendToGroup'; +import { wrapWithSyncMessageSend } from '../../util/wrapWithSyncMessageSend'; +import { getValidRecipients } from './getValidRecipients'; + +export async function sendGroupCallUpdate( + conversation: ConversationModel, + { + isFinalAttempt, + shouldContinue, + timestamp, + timeRemaining, + log, + }: ConversationQueueJobBundle, + data: GroupCallUpdateJobData +): Promise { + const { eraId, urgent } = data; + const logId = `sendCallUpdate(${conversation.idForLogging()}.${eraId})`; + if (!shouldContinue) { + log.info(`${logId}: Ran out of time. Giving up.`); + return; + } + + log.info(`${logId}: Starting send`); + + if (!isGroup(conversation.attributes)) { + log.warn(`${logId}: Conversation is not a group; refusing to send`); + return; + } + + const recipients = getValidRecipients(conversation.getRecipients(), { + log, + logId, + }); + + const untrustedServiceIds = getUntrustedConversationServiceIds(recipients); + if (untrustedServiceIds.length) { + window.reduxActions.conversations.conversationStoppedByMissingVerification({ + conversationId: conversation.id, + untrustedServiceIds, + }); + throw new Error( + `${logId}: Blocked because ${untrustedServiceIds.length} conversation(s) were untrusted. Failing this attempt.` + ); + } + + if (recipients.length === 0) { + log.warn(`${logId}: Giving up because there are no valid recipients.`); + return; + } + + const sendType = 'callingMessage'; + const { ContentHint } = Proto.UnidentifiedSenderMessage.Message; + const groupV2 = conversation.getGroupV2Info(); + const sendOptions = await getSendOptions(conversation.attributes); + if (!groupV2) { + log.error(`${logId}: Conversation lacks groupV2 info!`); + return; + } + + try { + await wrapWithSyncMessageSend({ + conversation, + logId, + messageIds: [], + send: () => + conversation.queueJob(logId, () => + sendToGroup({ + contentHint: ContentHint.DEFAULT, + groupSendOptions: { + groupCallUpdate: { eraId }, + groupV2, + timestamp, + }, + messageId: undefined, + sendOptions, + sendTarget: conversation.toSenderKeyTarget(), + sendType, + urgent, + }) + ), + sendType, + timestamp, + }); + } catch (error: unknown) { + await handleMultipleSendErrors({ + errors: maybeExpandErrors(error), + isFinalAttempt, + log, + timeRemaining, + toThrow: error, + }); + } +} diff --git a/ts/jobs/helpers/sendGroupUpdate.ts b/ts/jobs/helpers/sendGroupUpdate.ts index 7b61d49334..57c12bcaa9 100644 --- a/ts/jobs/helpers/sendGroupUpdate.ts +++ b/ts/jobs/helpers/sendGroupUpdate.ts @@ -11,7 +11,6 @@ import { import { wrapWithSyncMessageSend } from '../../util/wrapWithSyncMessageSend'; import * as Bytes from '../../Bytes'; import { strictAssert } from '../../util/assert'; -import { isNotNil } from '../../util/isNotNil'; import { ourProfileKeyService } from '../../services/ourProfileKey'; import type { ConversationModel } from '../../models/conversations'; @@ -22,6 +21,7 @@ import type { } from '../conversationJobQueue'; import { getUntrustedConversationServiceIds } from './getUntrustedConversationServiceIds'; import { sendToGroup } from '../../util/sendToGroup'; +import { getValidRecipients } from './getValidRecipients'; // Note: because we don't have a recipient map, if some sends fail, we will resend this // message to folks that got it on the first go-round. This is okay, because receivers @@ -55,28 +55,7 @@ export async function sendGroupUpdate( const { groupChangeBase64, recipients: jobRecipients, revision } = data; - const recipients = jobRecipients - .map(id => { - const recipient = window.ConversationController.get(id); - if (!recipient) { - return undefined; - } - if (recipient.isUnregistered()) { - log.warn( - `${logId}: dropping unregistered recipient ${recipient.idForLogging()}` - ); - return undefined; - } - if (recipient.isBlocked()) { - log.warn( - `${logId}: dropping blocked recipient ${recipient.idForLogging()}` - ); - return undefined; - } - - return recipient.getSendTarget(); - }) - .filter(isNotNil); + const recipients = getValidRecipients(jobRecipients, { log, logId }); const untrustedServiceIds = getUntrustedConversationServiceIds(recipients); if (untrustedServiceIds.length) { diff --git a/ts/models/conversations.ts b/ts/models/conversations.ts index f63fb25471..a2639bfde7 100644 --- a/ts/models/conversations.ts +++ b/ts/models/conversations.ts @@ -3008,15 +3008,9 @@ export class ConversationModel extends window.Backbone 'addKeyChange' ); - const isUntrusted = await this.isUntrusted(); - this.trigger('newmessage', model); const serviceId = this.getServiceId(); - // Group calls are always with folks that have a serviceId - if (isUntrusted && isAciString(serviceId)) { - window.reduxActions.calling.keyChanged({ aci: serviceId }); - } if (isDirectConversation(this.attributes)) { window.reduxActions?.safetyNumber.clearSafetyNumber(this.id); diff --git a/ts/services/calling.ts b/ts/services/calling.ts index 3ba10910af..18d4468cd5 100644 --- a/ts/services/calling.ts +++ b/ts/services/calling.ts @@ -81,9 +81,7 @@ import { dropNull } from '../util/dropNull'; import { getOwn } from '../util/getOwn'; import * as durations from '../util/durations'; import { clearTimeoutIfNecessary } from '../util/clearTimeoutIfNecessary'; -import { handleMessageSend } from '../util/handleMessageSend'; import { fetchMembershipProof, getMembershipList } from '../groups'; -import { wrapWithSyncMessageSend } from '../util/wrapWithSyncMessageSend'; import type { ProcessedEnvelope } from '../textsecure/Types.d'; import { missingCaseError } from '../util/missingCaseError'; import { normalizeGroupCallTimestamp } from '../util/ringrtc/normalizeGroupCallTimestamp'; @@ -94,7 +92,6 @@ import { REQUESTED_VIDEO_FRAMERATE, } from '../calling/constants'; import { callingMessageToProto } from '../util/callingMessageToProto'; -import { getSendOptions } from '../util/getSendOptions'; import { requestMicrophonePermissions } from '../util/requestMicrophonePermissions'; import OS from '../util/os/osMain'; import { SignalService as Proto } from '../protobuf'; @@ -107,7 +104,6 @@ import { } from './notifications'; import * as log from '../logging/log'; import { assertDev, strictAssert } from '../util/assert'; -import { sendContentMessageToGroup, sendToGroup } from '../util/sendToGroup'; import { formatLocalDeviceState, formatPeekInfo, @@ -130,13 +126,14 @@ import { } from '../util/callDisposition'; import { isNormalNumber } from '../util/isNormalNumber'; import { LocalCallEvent } from '../types/CallDisposition'; -import { isServiceIdString } from '../types/ServiceId'; +import { isServiceIdString, type ServiceIdString } from '../types/ServiceId'; import { isInSystemContacts } from '../util/isInSystemContacts'; import { getRoomIdFromRootKey, getCallLinkAuthCredentialPresentation, } from '../util/callLinks'; import { isAdhocCallingEnabled } from '../util/isAdhocCallingEnabled'; +import { conversationJobQueue } from '../jobs/conversationJobQueue'; const { processGroupCallRingCancellation, @@ -1430,53 +1427,36 @@ export class CallingClass { private async sendGroupCallUpdateMessage( conversationId: string, eraId: string - ): Promise { + ): Promise { const conversation = window.ConversationController.get(conversationId); if (!conversation) { - log.error( - 'Unable to send group call update message for non-existent conversation' - ); - return; + log.error('sendGroupCallUpdateMessage: Conversation not found!'); + return false; } + const logId = `sendGroupCallUpdateMessage/${conversation.idForLogging()}`; + const groupV2 = conversation.getGroupV2Info(); - const sendOptions = await getSendOptions(conversation.attributes); if (!groupV2) { - log.error( - 'Unable to send group call update message for conversation that lacks groupV2 info' - ); - return; + log.error(`${logId}: Conversation lacks groupV2 info!`); + return false; } - const timestamp = Date.now(); - - // We "fire and forget" because sending this message is non-essential. - const { ContentHint } = Proto.UnidentifiedSenderMessage.Message; - wrapWithSyncMessageSend({ - conversation, - logId: `sendToGroup/groupCallUpdate/${conversationId}-${eraId}`, - messageIds: [], - send: () => - conversation.queueJob('sendGroupCallUpdateMessage', () => - sendToGroup({ - contentHint: ContentHint.DEFAULT, - groupSendOptions: { - groupCallUpdate: { eraId }, - groupV2, - timestamp, - }, - messageId: undefined, - sendOptions, - sendTarget: conversation.toSenderKeyTarget(), - sendType: 'callingMessage', - urgent: true, - }) - ), - sendType: 'callingMessage', - timestamp, - }).catch(err => { - log.error('Failed to send group call update:', Errors.toLogFormat(err)); - }); + try { + await conversationJobQueue.add({ + type: 'GroupCallUpdate', + conversationId: conversation.id, + eraId, + urgent: true, + }); + return true; + } catch (err) { + log.error( + `${logId}: Failed to queue call update:`, + Errors.toLogFormat(err) + ); + return false; + } } async acceptDirectCall( @@ -2109,50 +2089,71 @@ export class CallingClass { private async handleSendCallMessageToGroup( groupIdBytes: Buffer, data: Buffer, - urgency: CallMessageUrgency - ): Promise { + urgency: CallMessageUrgency, + overrideRecipients: Array = [] + ): Promise { const groupId = groupIdBytes.toString('base64'); const conversation = window.ConversationController.get(groupId); if (!conversation) { log.error('handleSendCallMessageToGroup(): could not find conversation'); - return; + return false; } - const timestamp = Date.now(); - - const callingMessage = new CallingMessage(); - callingMessage.opaque = new OpaqueMessage(); - callingMessage.opaque.data = data; - const contentMessage = new Proto.Content(); - contentMessage.callingMessage = callingMessageToProto( - callingMessage, - urgency - ); - // If this message isn't droppable, we'll wake up recipient devices. The important one // is the first message to start the call. const urgent = urgency === CallMessageUrgency.HandleImmediately; - // We "fire and forget" because sending this message is non-essential. - // We also don't sync this message. - const { ContentHint } = Proto.UnidentifiedSenderMessage.Message; - await conversation.queueJob('handleSendCallMessageToGroup', async () => - handleMessageSend( - sendContentMessageToGroup({ - contentHint: ContentHint.DEFAULT, - contentMessage, - isPartialSend: false, - messageId: undefined, - recipients: conversation.getRecipients(), - sendOptions: await getSendOptions(conversation.attributes), - sendTarget: conversation.toSenderKeyTarget(), - sendType: 'callingMessage', - timestamp, - urgent, - }), - { messageIds: [], sendType: 'callingMessage' } - ) - ); + try { + let recipients: Array = []; + let isPartialSend = false; + if (overrideRecipients.length > 0) { + // Send only to the overriding recipients. + overrideRecipients.forEach(recipient => { + const serviceId = bytesToUuid(recipient); + if (!serviceId) { + log.error( + 'handleSendCallMessageToGroup(): missing recipient serviceId' + ); + } else { + assertDev( + isServiceIdString(serviceId), + 'remoteServiceId is not a serviceId' + ); + recipients.push(serviceId); + } + }); + isPartialSend = true; + } else { + // Send to all members in the group. + recipients = conversation.getRecipients(); + } + + const callingMessage = new CallingMessage(); + callingMessage.opaque = new OpaqueMessage(); + callingMessage.opaque.data = data; + + const proto = callingMessageToProto(callingMessage, urgency); + const protoBytes = Proto.CallingMessage.encode(proto).finish(); + const protoBase64 = Bytes.toBase64(protoBytes); + + await conversationJobQueue.add({ + type: 'CallingMessage', + conversationId: conversation.id, + protoBase64, + urgent, + isPartialSend, + recipients, + }); + + log.info('handleSendCallMessageToGroup() completed successfully'); + return true; + } catch (err) { + const errorString = Errors.toLogFormat(err); + log.error( + `handleSendCallMessageToGroup() failed to queue job: ${errorString}` + ); + return false; + } } private async handleGroupCallRingUpdate( @@ -2275,15 +2276,14 @@ export class CallingClass { message: CallingMessage, urgency?: CallMessageUrgency ): Promise { - const conversation = window.ConversationController.get(remoteUserId); - const sendOptions = conversation - ? await getSendOptions(conversation.attributes) - : undefined; - - if (!window.textsecure.messaging) { - log.warn('handleOutgoingSignaling() returning false; offline'); - return false; - } + assertDev( + isServiceIdString(remoteUserId), + 'remoteUserId is not a service id' + ); + const conversation = window.ConversationController.getOrCreate( + remoteUserId, + 'private' + ); // We want 1:1 call initiate messages to wake up recipient devices, but not others const urgent = @@ -2291,32 +2291,23 @@ export class CallingClass { Boolean(message.offer); try { - assertDev( - isServiceIdString(remoteUserId), - 'remoteUserId is not a service id' - ); - const result = await handleMessageSend( - window.textsecure.messaging.sendCallingMessage( - remoteUserId, - callingMessageToProto(message, urgency), - urgent, - sendOptions - ), - { messageIds: [], sendType: 'callingMessage' } - ); + const proto = callingMessageToProto(message, urgency); + const protoBytes = Proto.CallingMessage.encode(proto).finish(); + const protoBase64 = Bytes.toBase64(protoBytes); - if (result && result.errors && result.errors.length) { - throw result.errors[0]; - } + await conversationJobQueue.add({ + type: 'CallingMessage', + conversationId: conversation.id, + protoBase64, + urgent, + }); - log.info('handleOutgoingSignaling() completed successfully'); return true; } catch (err) { - if (err && err.errors && err.errors.length > 0) { - log.error(`handleOutgoingSignaling() failed: ${err.errors[0].reason}`); - } else { - log.error('handleOutgoingSignaling() failed'); - } + const errorString = Errors.toLogFormat(err); + log.error( + `handleOutgoingSignaling() failed to queue job: ${errorString}` + ); return false; } } diff --git a/ts/state/ducks/calling.ts b/ts/state/ducks/calling.ts index fb2c0a1d4e..261558291e 100644 --- a/ts/state/ducks/calling.ts +++ b/ts/state/ducks/calling.ts @@ -152,7 +152,6 @@ export type ActiveCallStateType = { pip: boolean; presentingSource?: PresentedSource; presentingSourcesAvailable?: Array; - safetyNumberChangedAcis: Array; settingsDialogOpen: boolean; showNeedsScreenRecordingPermissionsWarning?: boolean; showParticipantsList: boolean; @@ -249,14 +248,6 @@ type HangUpActionPayloadType = ReadonlyDeep<{ conversationId: string; }>; -type KeyChangedType = ReadonlyDeep<{ - aci: AciString; -}>; - -export type KeyChangeOkType = ReadonlyDeep<{ - conversationId: string; -}>; - export type IncomingDirectCallType = ReadonlyDeep<{ conversationId: string; isVideoCall: boolean; @@ -579,8 +570,6 @@ const GROUP_CALL_REACTIONS_EXPIRED = 'calling/GROUP_CALL_REACTIONS_EXPIRED'; const HANG_UP = 'calling/HANG_UP'; const INCOMING_DIRECT_CALL = 'calling/INCOMING_DIRECT_CALL'; const INCOMING_GROUP_CALL = 'calling/INCOMING_GROUP_CALL'; -const MARK_CALL_TRUSTED = 'calling/MARK_CALL_TRUSTED'; -const MARK_CALL_UNTRUSTED = 'calling/MARK_CALL_UNTRUSTED'; const OUTGOING_CALL = 'calling/OUTGOING_CALL'; const PEEK_GROUP_CALL_FULFILLED = 'calling/PEEK_GROUP_CALL_FULFILLED'; const RAISE_HAND_GROUP_CALL = 'calling/RAISE_HAND_GROUP_CALL'; @@ -725,19 +714,6 @@ type IncomingGroupCallActionType = ReadonlyDeep<{ payload: IncomingGroupCallType; }>; -// eslint-disable-next-line local-rules/type-alias-readonlydeep -type KeyChangedActionType = { - type: 'calling/MARK_CALL_UNTRUSTED'; - payload: { - safetyNumberChangedAcis: Array; - }; -}; - -type KeyChangeOkActionType = ReadonlyDeep<{ - type: 'calling/MARK_CALL_TRUSTED'; - payload: null; -}>; - type SendGroupCallRaiseHandActionType = ReadonlyDeep<{ type: 'calling/RAISE_HAND_GROUP_CALL'; payload: SendGroupCallRaiseHandType; @@ -865,8 +841,6 @@ export type CallingActionType = | HangUpActionType | IncomingDirectCallActionType | IncomingGroupCallActionType - | KeyChangedActionType - | KeyChangeOkActionType | OutgoingCallActionType | PeekGroupCallFulfilledActionType | RefreshIODevicesActionType @@ -1267,56 +1241,6 @@ function hangUpActiveCall( }; } -function keyChanged( - payload: KeyChangedType -): ThunkAction { - return (dispatch, getState) => { - const state = getState(); - const { activeCallState } = state.calling; - - const activeCall = getActiveCall(state.calling); - if (!activeCall || !activeCallState) { - return; - } - - if (isGroupOrAdhocCallState(activeCall)) { - const acisChanged = new Set(activeCallState.safetyNumberChangedAcis); - - // Iterate over each participant to ensure that the service id passed in - // matches one of the participants in the group call. - activeCall.remoteParticipants.forEach(participant => { - if (participant.aci === payload.aci) { - acisChanged.add(participant.aci); - } - }); - - const safetyNumberChangedAcis = Array.from(acisChanged); - - if (safetyNumberChangedAcis.length) { - dispatch({ - type: MARK_CALL_UNTRUSTED, - payload: { - safetyNumberChangedAcis, - }, - }); - } - } - }; -} - -function keyChangeOk( - payload: KeyChangeOkType -): ThunkAction { - return dispatch => { - calling.resendGroupCallMediaKeys(payload.conversationId); - - dispatch({ - type: MARK_CALL_TRUSTED, - payload: null, - }); - }; -} - function sendGroupCallRaiseHand( payload: SendGroupCallRaiseHandType ): ThunkAction { @@ -2059,8 +1983,6 @@ export const actions = { groupCallRaisedHandsChange, groupCallStateChange, hangUpActiveCall, - keyChangeOk, - keyChanged, onOutgoingVideoCallInConversation, onOutgoingAudioCallInConversation, openSystemPreferencesAction, @@ -2284,7 +2206,6 @@ export function reducer( localAudioLevel: 0, viewMode: CallViewMode.Paginated, pip: false, - safetyNumberChangedAcis: [], settingsDialogOpen: false, showParticipantsList: false, outgoingRing, @@ -2314,7 +2235,6 @@ export function reducer( localAudioLevel: 0, viewMode: CallViewMode.Paginated, pip: false, - safetyNumberChangedAcis: [], settingsDialogOpen: false, showParticipantsList: false, outgoingRing: true, @@ -2343,7 +2263,6 @@ export function reducer( localAudioLevel: 0, viewMode: CallViewMode.Paginated, pip: false, - safetyNumberChangedAcis: [], settingsDialogOpen: false, showParticipantsList: false, outgoingRing: false, @@ -2505,7 +2424,6 @@ export function reducer( localAudioLevel: 0, viewMode: CallViewMode.Paginated, pip: false, - safetyNumberChangedAcis: [], settingsDialogOpen: false, showParticipantsList: false, outgoingRing: true, @@ -3182,42 +3100,5 @@ export function reducer( }; } - if (action.type === MARK_CALL_UNTRUSTED) { - const { activeCallState } = state; - if (!activeCallState) { - log.warn('Cannot mark call as untrusted when there is no active call'); - return state; - } - - const { safetyNumberChangedAcis } = action.payload; - - return { - ...state, - activeCallState: { - ...activeCallState, - pip: false, - safetyNumberChangedAcis, - settingsDialogOpen: false, - showParticipantsList: false, - }, - }; - } - - if (action.type === MARK_CALL_TRUSTED) { - const { activeCallState } = state; - if (!activeCallState) { - log.warn('Cannot mark call as trusted when there is no active call'); - return state; - } - - return { - ...state, - activeCallState: { - ...activeCallState, - safetyNumberChangedAcis: [], - }, - }; - } - return state; } diff --git a/ts/state/ducks/conversations.ts b/ts/state/ducks/conversations.ts index 47fd0a56b3..0a940d8ec8 100644 --- a/ts/state/ducks/conversations.ts +++ b/ts/state/ducks/conversations.ts @@ -127,7 +127,7 @@ import { missingCaseError } from '../../util/missingCaseError'; import { viewSyncJobQueue } from '../../jobs/viewSyncJobQueue'; import { ReadStatus } from '../../messages/MessageReadStatus'; import { isIncoming, processBodyRanges } from '../selectors/message'; -import { getActiveCallState } from '../selectors/calling'; +import { getActiveCall, getActiveCallState } from '../selectors/calling'; import { sendDeleteForEveryoneMessage } from '../../util/sendDeleteForEveryoneMessage'; import type { ShowToastActionType } from './toast'; import { SHOW_TOAST } from './toast'; @@ -2433,7 +2433,15 @@ export function cancelConversationVerification( }); // Start the blocked conversation queues up again + const activeCall = getActiveCall(state); conversationIdsBlocked.forEach(conversationId => { + if ( + activeCall && + activeCall.conversationId === conversationId && + activeCall.callMode === CallMode.Direct + ) { + calling.hangup(conversationId, 'canceled conversation verification'); + } conversationJobQueue.resolveVerificationWaiter(conversationId); }); }; diff --git a/ts/state/smart/CallManager.tsx b/ts/state/smart/CallManager.tsx index 082b6b7a33..6c56ed9f2d 100644 --- a/ts/state/smart/CallManager.tsx +++ b/ts/state/smart/CallManager.tsx @@ -9,7 +9,6 @@ import type { GroupIncomingCall, } from '../../components/CallManager'; import { CallManager } from '../../components/CallManager'; -import type { SafetyNumberProps } from '../../components/SafetyNumberChangeDialog'; import { isConversationTooBigToRing as getIsConversationTooBigToRing } from '../../conversations/isConversationTooBigToRing'; import * as log from '../../logging/log'; import { calling as callingService } from '../../services/calling'; @@ -47,16 +46,14 @@ import type { ConversationType } from '../ducks/conversations'; import { useToastActions } from '../ducks/toast'; import type { StateType } from '../reducer'; import { getHasInitialLoadCompleted } from '../selectors/app'; -import { getPreferredBadgeSelector } from '../selectors/badges'; import { getAvailableCameras, getCallLinkSelector, getIncomingCall, } from '../selectors/calling'; import { getConversationSelector, getMe } from '../selectors/conversations'; -import { getIntl, getTheme } from '../selectors/user'; +import { getIntl } from '../selectors/user'; import { SmartCallingDeviceSelection } from './CallingDeviceSelection'; -import { SmartSafetyNumberViewer } from './SafetyNumberViewer'; import { renderEmojiPicker } from './renderEmojiPicker'; import { renderReactionPicker } from './renderReactionPicker'; @@ -64,10 +61,6 @@ function renderDeviceSelection(): JSX.Element { return ; } -function renderSafetyNumberViewer(props: SafetyNumberProps): JSX.Element { - return ; -} - const getGroupCallVideoFrameSource = callingService.getGroupCallVideoFrameSource.bind(callingService); @@ -216,7 +209,6 @@ const mapStateToActiveCallProp = ( } satisfies ActiveDirectCallType; case CallMode.Group: case CallMode.Adhoc: { - const conversationsWithSafetyNumberChanges: Array = []; const groupMembers: Array = []; const remoteParticipants: Array = []; const peekedParticipants: Array = []; @@ -290,22 +282,6 @@ const mapStateToActiveCallProp = ( } }); - for ( - let i = 0; - i < activeCallState.safetyNumberChangedAcis.length; - i += 1 - ) { - const aci = activeCallState.safetyNumberChangedAcis[i]; - - const remoteConversation = conversationSelectorByAci(aci); - if (!remoteConversation) { - log.error('Remote participant has no corresponding conversation'); - continue; - } - - conversationsWithSafetyNumberChanges.push(remoteConversation); - } - for (let i = 0; i < peekInfo.acis.length; i += 1) { const peekedParticipantAci = peekInfo.acis[i]; @@ -323,7 +299,6 @@ const mapStateToActiveCallProp = ( ...baseResult, callMode: call.callMode, connectionState: call.connectionState, - conversationsWithSafetyNumberChanges, conversationsByDemuxId, deviceCount: peekInfo.deviceCount, groupMembers, @@ -422,11 +397,9 @@ const mapStateToIncomingCallProp = ( export const SmartCallManager = memo(function SmartCallManager() { const i18n = useSelector(getIntl); - const theme = useSelector(getTheme); const activeCall = useSelector(mapStateToActiveCallProp); const callLink = useSelector(mapStateToCallLinkProp); const incomingCall = useSelector(mapStateToIncomingCallProp); - const getPreferredBadge = useSelector(getPreferredBadgeSelector); const availableCameras = useSelector(getAvailableCameras); const hasInitialLoadCompleted = useSelector(getHasInitialLoadCompleted); const me = useSelector(getMe); @@ -439,7 +412,6 @@ export const SmartCallManager = memo(function SmartCallManager() { closeNeedPermissionScreen, getPresentingSources, cancelCall, - keyChangeOk, startCall, toggleParticipants, acceptCall, @@ -478,7 +450,6 @@ export const SmartCallManager = memo(function SmartCallManager() { closeNeedPermissionScreen={closeNeedPermissionScreen} declineCall={declineCall} getGroupCallVideoFrameSource={getGroupCallVideoFrameSource} - getPreferredBadge={getPreferredBadge} getPresentingSources={getPresentingSources} hangUpActiveCall={hangUpActiveCall} hasInitialLoadCompleted={hasInitialLoadCompleted} @@ -487,7 +458,6 @@ export const SmartCallManager = memo(function SmartCallManager() { isConversationTooBigToRing={isConversationTooBigToRing} isGroupCallRaiseHandEnabled={isGroupCallRaiseHandEnabled()} isGroupCallReactionsEnabled={isGroupCallReactionsEnabled()} - keyChangeOk={keyChangeOk} me={me} notifyForCall={notifyForCall} openSystemPreferencesAction={openSystemPreferencesAction} @@ -496,7 +466,6 @@ export const SmartCallManager = memo(function SmartCallManager() { renderDeviceSelection={renderDeviceSelection} renderEmojiPicker={renderEmojiPicker} renderReactionPicker={renderReactionPicker} - renderSafetyNumberViewer={renderSafetyNumberViewer} sendGroupCallRaiseHand={sendGroupCallRaiseHand} sendGroupCallReaction={sendGroupCallReaction} setGroupCallVideoRequest={setGroupCallVideoRequest} @@ -512,7 +481,6 @@ export const SmartCallManager = memo(function SmartCallManager() { stopRingtone={stopRingtone} switchFromPresentationView={switchFromPresentationView} switchToPresentationView={switchToPresentationView} - theme={theme} toggleParticipants={toggleParticipants} togglePip={togglePip} toggleScreenRecordingPermissionsDialog={ diff --git a/ts/test-electron/state/ducks/calling_test.ts b/ts/test-electron/state/ducks/calling_test.ts index 471196f08a..72c691d489 100644 --- a/ts/test-electron/state/ducks/calling_test.ts +++ b/ts/test-electron/state/ducks/calling_test.ts @@ -70,7 +70,6 @@ describe('calling duck', () => { localAudioLevel: 0, viewMode: CallViewMode.Paginated, showParticipantsList: false, - safetyNumberChangedAcis: [], outgoingRing: true, pip: false, settingsDialogOpen: false, @@ -153,7 +152,6 @@ describe('calling duck', () => { localAudioLevel: 0, viewMode: CallViewMode.Paginated, showParticipantsList: false, - safetyNumberChangedAcis: [], outgoingRing: false, pip: false, settingsDialogOpen: false, @@ -482,7 +480,6 @@ describe('calling duck', () => { localAudioLevel: 0, viewMode: CallViewMode.Paginated, showParticipantsList: false, - safetyNumberChangedAcis: [], outgoingRing: false, pip: false, settingsDialogOpen: false, @@ -577,7 +574,6 @@ describe('calling duck', () => { localAudioLevel: 0, viewMode: CallViewMode.Paginated, showParticipantsList: false, - safetyNumberChangedAcis: [], outgoingRing: false, pip: false, settingsDialogOpen: false, @@ -1198,7 +1194,6 @@ describe('calling duck', () => { localAudioLevel: 0, viewMode: CallViewMode.Paginated, showParticipantsList: false, - safetyNumberChangedAcis: [], outgoingRing: false, pip: false, settingsDialogOpen: false, @@ -1887,7 +1882,6 @@ describe('calling duck', () => { localAudioLevel: 0, viewMode: CallViewMode.Paginated, showParticipantsList: false, - safetyNumberChangedAcis: [], pip: false, settingsDialogOpen: false, outgoingRing: true, @@ -2195,7 +2189,6 @@ describe('calling duck', () => { localAudioLevel: 0, viewMode: CallViewMode.Paginated, showParticipantsList: false, - safetyNumberChangedAcis: [], pip: false, settingsDialogOpen: false, outgoingRing: true, diff --git a/ts/test-electron/state/selectors/calling_test.ts b/ts/test-electron/state/selectors/calling_test.ts index 8f28346211..d9cad0e964 100644 --- a/ts/test-electron/state/selectors/calling_test.ts +++ b/ts/test-electron/state/selectors/calling_test.ts @@ -69,7 +69,6 @@ describe('state/selectors/calling', () => { localAudioLevel: 0, viewMode: CallViewMode.Paginated, showParticipantsList: false, - safetyNumberChangedAcis: [], outgoingRing: true, pip: false, settingsDialogOpen: false, diff --git a/ts/textsecure/SendMessage.ts b/ts/textsecure/SendMessage.ts index 600aa858ab..69eb1aaae3 100644 --- a/ts/textsecure/SendMessage.ts +++ b/ts/textsecure/SendMessage.ts @@ -1722,11 +1722,11 @@ export default class MessageSender { async sendCallingMessage( serviceId: ServiceIdString, callingMessage: Readonly, + timestamp: number, urgent: boolean, options?: Readonly ): Promise { const recipients = [serviceId]; - const finalTimestamp = Date.now(); const contentMessage = new Proto.Content(); contentMessage.callingMessage = callingMessage; @@ -1736,13 +1736,13 @@ export default class MessageSender { addPniSignatureMessageToProto({ conversation, proto: contentMessage, - reason: `sendCallingMessage(${finalTimestamp})`, + reason: `sendCallingMessage(${timestamp})`, }); const { ContentHint } = Proto.UnidentifiedSenderMessage.Message; return this.sendMessageProtoAndWait({ - timestamp: finalTimestamp, + timestamp, recipients, proto: contentMessage, contentHint: ContentHint.DEFAULT, diff --git a/ts/types/Calling.ts b/ts/types/Calling.ts index 15b5599156..577ae7cdd1 100644 --- a/ts/types/Calling.ts +++ b/ts/types/Calling.ts @@ -90,7 +90,6 @@ export type ActiveGroupCallType = ActiveCallBaseType & { callMode: CallMode.Group | CallMode.Adhoc; connectionState: GroupCallConnectionState; conversationsByDemuxId: ConversationsByDemuxIdType; - conversationsWithSafetyNumberChanges: Array; joinState: GroupCallJoinState; localDemuxId: number | undefined; maxDevices: number;