Send call messages with conversationJobQueue
Co-authored-by: trevor-signal <trevor@signal.org>
This commit is contained in:
		
					parent
					
						
							
								72169820eb
							
						
					
				
			
			
				commit
				
					
						783c71999a
					
				
			
		
					 18 changed files with 457 additions and 392 deletions
				
			
		| 
						 | 
					@ -23,9 +23,7 @@ import { generateAci } from '../types/ServiceId';
 | 
				
			||||||
import { getDefaultConversation } from '../test-both/helpers/getDefaultConversation';
 | 
					import { getDefaultConversation } from '../test-both/helpers/getDefaultConversation';
 | 
				
			||||||
import { fakeGetGroupCallVideoFrameSource } from '../test-both/helpers/fakeGetGroupCallVideoFrameSource';
 | 
					import { fakeGetGroupCallVideoFrameSource } from '../test-both/helpers/fakeGetGroupCallVideoFrameSource';
 | 
				
			||||||
import { setupI18n } from '../util/setupI18n';
 | 
					import { setupI18n } from '../util/setupI18n';
 | 
				
			||||||
import type { SafetyNumberProps } from './SafetyNumberChangeDialog';
 | 
					 | 
				
			||||||
import enMessages from '../../_locales/en/messages.json';
 | 
					import enMessages from '../../_locales/en/messages.json';
 | 
				
			||||||
import { ThemeType } from '../types/Util';
 | 
					 | 
				
			||||||
import { StorySendMode } from '../types/Stories';
 | 
					import { StorySendMode } from '../types/Stories';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const i18n = setupI18n('en', enMessages);
 | 
					const i18n = setupI18n('en', enMessages);
 | 
				
			||||||
| 
						 | 
					@ -69,7 +67,6 @@ const createProps = (storyProps: Partial<PropsType> = {}): PropsType => ({
 | 
				
			||||||
  declineCall: action('decline-call'),
 | 
					  declineCall: action('decline-call'),
 | 
				
			||||||
  getGroupCallVideoFrameSource: (_: string, demuxId: number) =>
 | 
					  getGroupCallVideoFrameSource: (_: string, demuxId: number) =>
 | 
				
			||||||
    fakeGetGroupCallVideoFrameSource(demuxId),
 | 
					    fakeGetGroupCallVideoFrameSource(demuxId),
 | 
				
			||||||
  getPreferredBadge: () => undefined,
 | 
					 | 
				
			||||||
  getPresentingSources: action('get-presenting-sources'),
 | 
					  getPresentingSources: action('get-presenting-sources'),
 | 
				
			||||||
  hangUpActiveCall: action('hang-up-active-call'),
 | 
					  hangUpActiveCall: action('hang-up-active-call'),
 | 
				
			||||||
  hasInitialLoadCompleted: true,
 | 
					  hasInitialLoadCompleted: true,
 | 
				
			||||||
| 
						 | 
					@ -78,7 +75,6 @@ const createProps = (storyProps: Partial<PropsType> = {}): PropsType => ({
 | 
				
			||||||
  callLink: undefined,
 | 
					  callLink: undefined,
 | 
				
			||||||
  isGroupCallRaiseHandEnabled: true,
 | 
					  isGroupCallRaiseHandEnabled: true,
 | 
				
			||||||
  isGroupCallReactionsEnabled: true,
 | 
					  isGroupCallReactionsEnabled: true,
 | 
				
			||||||
  keyChangeOk: action('key-change-ok'),
 | 
					 | 
				
			||||||
  me: {
 | 
					  me: {
 | 
				
			||||||
    ...getDefaultConversation({
 | 
					    ...getDefaultConversation({
 | 
				
			||||||
      color: AvatarColors[0],
 | 
					      color: AvatarColors[0],
 | 
				
			||||||
| 
						 | 
					@ -92,7 +88,6 @@ const createProps = (storyProps: Partial<PropsType> = {}): PropsType => ({
 | 
				
			||||||
  renderDeviceSelection: () => <div />,
 | 
					  renderDeviceSelection: () => <div />,
 | 
				
			||||||
  renderEmojiPicker: () => <>EmojiPicker</>,
 | 
					  renderEmojiPicker: () => <>EmojiPicker</>,
 | 
				
			||||||
  renderReactionPicker: () => <div />,
 | 
					  renderReactionPicker: () => <div />,
 | 
				
			||||||
  renderSafetyNumberViewer: (_: SafetyNumberProps) => <div />,
 | 
					 | 
				
			||||||
  sendGroupCallRaiseHand: action('send-group-call-raise-hand'),
 | 
					  sendGroupCallRaiseHand: action('send-group-call-raise-hand'),
 | 
				
			||||||
  sendGroupCallReaction: action('send-group-call-reaction'),
 | 
					  sendGroupCallReaction: action('send-group-call-reaction'),
 | 
				
			||||||
  setGroupCallVideoRequest: action('set-group-call-video-request'),
 | 
					  setGroupCallVideoRequest: action('set-group-call-video-request'),
 | 
				
			||||||
| 
						 | 
					@ -108,7 +103,6 @@ const createProps = (storyProps: Partial<PropsType> = {}): PropsType => ({
 | 
				
			||||||
  stopRingtone: action('stop-ringtone'),
 | 
					  stopRingtone: action('stop-ringtone'),
 | 
				
			||||||
  switchToPresentationView: action('switch-to-presentation-view'),
 | 
					  switchToPresentationView: action('switch-to-presentation-view'),
 | 
				
			||||||
  switchFromPresentationView: action('switch-from-presentation-view'),
 | 
					  switchFromPresentationView: action('switch-from-presentation-view'),
 | 
				
			||||||
  theme: ThemeType.light,
 | 
					 | 
				
			||||||
  toggleParticipants: action('toggle-participants'),
 | 
					  toggleParticipants: action('toggle-participants'),
 | 
				
			||||||
  togglePip: action('toggle-pip'),
 | 
					  togglePip: action('toggle-pip'),
 | 
				
			||||||
  toggleScreenRecordingPermissionsDialog: action(
 | 
					  toggleScreenRecordingPermissionsDialog: action(
 | 
				
			||||||
| 
						 | 
					@ -155,7 +149,6 @@ export function OngoingGroupCall(): JSX.Element {
 | 
				
			||||||
          ...getCommonActiveCallData(),
 | 
					          ...getCommonActiveCallData(),
 | 
				
			||||||
          callMode: CallMode.Group,
 | 
					          callMode: CallMode.Group,
 | 
				
			||||||
          connectionState: GroupCallConnectionState.Connected,
 | 
					          connectionState: GroupCallConnectionState.Connected,
 | 
				
			||||||
          conversationsWithSafetyNumberChanges: [],
 | 
					 | 
				
			||||||
          conversationsByDemuxId: new Map<number, ConversationType>(),
 | 
					          conversationsByDemuxId: new Map<number, ConversationType>(),
 | 
				
			||||||
          deviceCount: 0,
 | 
					          deviceCount: 0,
 | 
				
			||||||
          joinState: GroupCallJoinState.Joined,
 | 
					          joinState: GroupCallJoinState.Joined,
 | 
				
			||||||
| 
						 | 
					@ -232,35 +225,3 @@ export function CallRequestNeeded(): JSX.Element {
 | 
				
			||||||
    />
 | 
					    />
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
export function GroupCallSafetyNumberChanged(): JSX.Element {
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <CallManager
 | 
					 | 
				
			||||||
      {...createProps({
 | 
					 | 
				
			||||||
        activeCall: {
 | 
					 | 
				
			||||||
          ...getCommonActiveCallData(),
 | 
					 | 
				
			||||||
          callMode: CallMode.Group,
 | 
					 | 
				
			||||||
          connectionState: GroupCallConnectionState.Connected,
 | 
					 | 
				
			||||||
          conversationsWithSafetyNumberChanges: [
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
              ...getDefaultConversation({
 | 
					 | 
				
			||||||
                title: 'Aaron',
 | 
					 | 
				
			||||||
              }),
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
          ],
 | 
					 | 
				
			||||||
          conversationsByDemuxId: new Map<number, ConversationType>(),
 | 
					 | 
				
			||||||
          deviceCount: 0,
 | 
					 | 
				
			||||||
          joinState: GroupCallJoinState.Joined,
 | 
					 | 
				
			||||||
          localDemuxId: 1,
 | 
					 | 
				
			||||||
          maxDevices: 5,
 | 
					 | 
				
			||||||
          groupMembers: [],
 | 
					 | 
				
			||||||
          isConversationTooBigToRing: false,
 | 
					 | 
				
			||||||
          peekedParticipants: [],
 | 
					 | 
				
			||||||
          raisedHands: new Set<number>(),
 | 
					 | 
				
			||||||
          remoteParticipants: [],
 | 
					 | 
				
			||||||
          remoteAudioLevels: new Map<number, number>(),
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
      })}
 | 
					 | 
				
			||||||
    />
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,8 +11,6 @@ import { CallingParticipantsList } from './CallingParticipantsList';
 | 
				
			||||||
import { CallingSelectPresentingSourcesModal } from './CallingSelectPresentingSourcesModal';
 | 
					import { CallingSelectPresentingSourcesModal } from './CallingSelectPresentingSourcesModal';
 | 
				
			||||||
import { CallingPip } from './CallingPip';
 | 
					import { CallingPip } from './CallingPip';
 | 
				
			||||||
import { IncomingCallBar } from './IncomingCallBar';
 | 
					import { IncomingCallBar } from './IncomingCallBar';
 | 
				
			||||||
import type { SafetyNumberProps } from './SafetyNumberChangeDialog';
 | 
					 | 
				
			||||||
import { SafetyNumberChangeDialog } from './SafetyNumberChangeDialog';
 | 
					 | 
				
			||||||
import type {
 | 
					import type {
 | 
				
			||||||
  ActiveCallType,
 | 
					  ActiveCallType,
 | 
				
			||||||
  CallingConversationType,
 | 
					  CallingConversationType,
 | 
				
			||||||
| 
						 | 
					@ -28,13 +26,11 @@ import {
 | 
				
			||||||
  GroupCallJoinState,
 | 
					  GroupCallJoinState,
 | 
				
			||||||
} from '../types/Calling';
 | 
					} from '../types/Calling';
 | 
				
			||||||
import type { ConversationType } from '../state/ducks/conversations';
 | 
					import type { ConversationType } from '../state/ducks/conversations';
 | 
				
			||||||
import type { PreferredBadgeSelectorType } from '../state/selectors/badges';
 | 
					 | 
				
			||||||
import type {
 | 
					import type {
 | 
				
			||||||
  AcceptCallType,
 | 
					  AcceptCallType,
 | 
				
			||||||
  CancelCallType,
 | 
					  CancelCallType,
 | 
				
			||||||
  DeclineCallType,
 | 
					  DeclineCallType,
 | 
				
			||||||
  GroupCallParticipantInfoType,
 | 
					  GroupCallParticipantInfoType,
 | 
				
			||||||
  KeyChangeOkType,
 | 
					 | 
				
			||||||
  SendGroupCallRaiseHandType,
 | 
					  SendGroupCallRaiseHandType,
 | 
				
			||||||
  SendGroupCallReactionType,
 | 
					  SendGroupCallReactionType,
 | 
				
			||||||
  SetGroupCallVideoRequestType,
 | 
					  SetGroupCallVideoRequestType,
 | 
				
			||||||
| 
						 | 
					@ -46,7 +42,7 @@ import type {
 | 
				
			||||||
} from '../state/ducks/calling';
 | 
					} from '../state/ducks/calling';
 | 
				
			||||||
import { CallLinkRestrictions } from '../types/CallLink';
 | 
					import { CallLinkRestrictions } from '../types/CallLink';
 | 
				
			||||||
import type { CallLinkType } 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 { missingCaseError } from '../util/missingCaseError';
 | 
				
			||||||
import { CallingToastProvider } from './CallingToast';
 | 
					import { CallingToastProvider } from './CallingToast';
 | 
				
			||||||
import type { SmartReactionPicker } from '../state/smart/ReactionPicker';
 | 
					import type { SmartReactionPicker } from '../state/smart/ReactionPicker';
 | 
				
			||||||
| 
						 | 
					@ -89,15 +85,12 @@ export type PropsType = {
 | 
				
			||||||
    conversationId: string,
 | 
					    conversationId: string,
 | 
				
			||||||
    demuxId: number
 | 
					    demuxId: number
 | 
				
			||||||
  ) => VideoFrameSource;
 | 
					  ) => VideoFrameSource;
 | 
				
			||||||
  getPreferredBadge: PreferredBadgeSelectorType;
 | 
					 | 
				
			||||||
  getPresentingSources: () => void;
 | 
					  getPresentingSources: () => void;
 | 
				
			||||||
  incomingCall: DirectIncomingCall | GroupIncomingCall | null;
 | 
					  incomingCall: DirectIncomingCall | GroupIncomingCall | null;
 | 
				
			||||||
  keyChangeOk: (_: KeyChangeOkType) => void;
 | 
					 | 
				
			||||||
  renderDeviceSelection: () => JSX.Element;
 | 
					  renderDeviceSelection: () => JSX.Element;
 | 
				
			||||||
  renderReactionPicker: (
 | 
					  renderReactionPicker: (
 | 
				
			||||||
    props: React.ComponentProps<typeof SmartReactionPicker>
 | 
					    props: React.ComponentProps<typeof SmartReactionPicker>
 | 
				
			||||||
  ) => JSX.Element;
 | 
					  ) => JSX.Element;
 | 
				
			||||||
  renderSafetyNumberViewer: (props: SafetyNumberProps) => JSX.Element;
 | 
					 | 
				
			||||||
  startCall: (payload: StartCallType) => void;
 | 
					  startCall: (payload: StartCallType) => void;
 | 
				
			||||||
  toggleParticipants: () => void;
 | 
					  toggleParticipants: () => void;
 | 
				
			||||||
  acceptCall: (_: AcceptCallType) => void;
 | 
					  acceptCall: (_: AcceptCallType) => void;
 | 
				
			||||||
| 
						 | 
					@ -131,7 +124,6 @@ export type PropsType = {
 | 
				
			||||||
  switchToPresentationView: () => void;
 | 
					  switchToPresentationView: () => void;
 | 
				
			||||||
  switchFromPresentationView: () => void;
 | 
					  switchFromPresentationView: () => void;
 | 
				
			||||||
  hangUpActiveCall: (reason: string) => void;
 | 
					  hangUpActiveCall: (reason: string) => void;
 | 
				
			||||||
  theme: ThemeType;
 | 
					 | 
				
			||||||
  togglePip: () => void;
 | 
					  togglePip: () => void;
 | 
				
			||||||
  toggleScreenRecordingPermissionsDialog: () => unknown;
 | 
					  toggleScreenRecordingPermissionsDialog: () => unknown;
 | 
				
			||||||
  toggleSettings: () => void;
 | 
					  toggleSettings: () => void;
 | 
				
			||||||
| 
						 | 
					@ -168,16 +160,13 @@ function ActiveCallManager({
 | 
				
			||||||
  i18n,
 | 
					  i18n,
 | 
				
			||||||
  isGroupCallRaiseHandEnabled,
 | 
					  isGroupCallRaiseHandEnabled,
 | 
				
			||||||
  isGroupCallReactionsEnabled,
 | 
					  isGroupCallReactionsEnabled,
 | 
				
			||||||
  keyChangeOk,
 | 
					 | 
				
			||||||
  getGroupCallVideoFrameSource,
 | 
					  getGroupCallVideoFrameSource,
 | 
				
			||||||
  getPreferredBadge,
 | 
					 | 
				
			||||||
  getPresentingSources,
 | 
					  getPresentingSources,
 | 
				
			||||||
  me,
 | 
					  me,
 | 
				
			||||||
  openSystemPreferencesAction,
 | 
					  openSystemPreferencesAction,
 | 
				
			||||||
  renderDeviceSelection,
 | 
					  renderDeviceSelection,
 | 
				
			||||||
  renderEmojiPicker,
 | 
					  renderEmojiPicker,
 | 
				
			||||||
  renderReactionPicker,
 | 
					  renderReactionPicker,
 | 
				
			||||||
  renderSafetyNumberViewer,
 | 
					 | 
				
			||||||
  sendGroupCallRaiseHand,
 | 
					  sendGroupCallRaiseHand,
 | 
				
			||||||
  sendGroupCallReaction,
 | 
					  sendGroupCallReaction,
 | 
				
			||||||
  setGroupCallVideoRequest,
 | 
					  setGroupCallVideoRequest,
 | 
				
			||||||
| 
						 | 
					@ -191,7 +180,6 @@ function ActiveCallManager({
 | 
				
			||||||
  startCall,
 | 
					  startCall,
 | 
				
			||||||
  switchToPresentationView,
 | 
					  switchToPresentationView,
 | 
				
			||||||
  switchFromPresentationView,
 | 
					  switchFromPresentationView,
 | 
				
			||||||
  theme,
 | 
					 | 
				
			||||||
  toggleParticipants,
 | 
					  toggleParticipants,
 | 
				
			||||||
  togglePip,
 | 
					  togglePip,
 | 
				
			||||||
  toggleScreenRecordingPermissionsDialog,
 | 
					  toggleScreenRecordingPermissionsDialog,
 | 
				
			||||||
| 
						 | 
					@ -263,10 +251,6 @@ function ActiveCallManager({
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }, [callLink, showToast]);
 | 
					  }, [callLink, showToast]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const onSafetyNumberDialogCancel = useCallback(() => {
 | 
					 | 
				
			||||||
    hangUpActiveCall('safety number dialog cancel');
 | 
					 | 
				
			||||||
  }, [hangUpActiveCall]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  let isCallFull: boolean;
 | 
					  let isCallFull: boolean;
 | 
				
			||||||
  let showCallLobby: boolean;
 | 
					  let showCallLobby: boolean;
 | 
				
			||||||
  let groupMembers:
 | 
					  let groupMembers:
 | 
				
			||||||
| 
						 | 
					@ -463,26 +447,6 @@ function ActiveCallManager({
 | 
				
			||||||
            participants={groupCallParticipantsForParticipantsList}
 | 
					            participants={groupCallParticipantsForParticipantsList}
 | 
				
			||||||
          />
 | 
					          />
 | 
				
			||||||
        ))}
 | 
					        ))}
 | 
				
			||||||
      {isGroupOrAdhocActiveCall(activeCall) &&
 | 
					 | 
				
			||||||
      activeCall.conversationsWithSafetyNumberChanges.length ? (
 | 
					 | 
				
			||||||
        <SafetyNumberChangeDialog
 | 
					 | 
				
			||||||
          confirmText={i18n('icu:continueCall')}
 | 
					 | 
				
			||||||
          contacts={[
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
              story: undefined,
 | 
					 | 
				
			||||||
              contacts: activeCall.conversationsWithSafetyNumberChanges,
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
          ]}
 | 
					 | 
				
			||||||
          getPreferredBadge={getPreferredBadge}
 | 
					 | 
				
			||||||
          i18n={i18n}
 | 
					 | 
				
			||||||
          onCancel={onSafetyNumberDialogCancel}
 | 
					 | 
				
			||||||
          onConfirm={() => {
 | 
					 | 
				
			||||||
            keyChangeOk({ conversationId: activeCall.conversation.id });
 | 
					 | 
				
			||||||
          }}
 | 
					 | 
				
			||||||
          renderSafetyNumber={renderSafetyNumberViewer}
 | 
					 | 
				
			||||||
          theme={theme}
 | 
					 | 
				
			||||||
        />
 | 
					 | 
				
			||||||
      ) : null}
 | 
					 | 
				
			||||||
    </>
 | 
					    </>
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -499,7 +463,6 @@ export function CallManager({
 | 
				
			||||||
  closeNeedPermissionScreen,
 | 
					  closeNeedPermissionScreen,
 | 
				
			||||||
  declineCall,
 | 
					  declineCall,
 | 
				
			||||||
  getGroupCallVideoFrameSource,
 | 
					  getGroupCallVideoFrameSource,
 | 
				
			||||||
  getPreferredBadge,
 | 
					 | 
				
			||||||
  getPresentingSources,
 | 
					  getPresentingSources,
 | 
				
			||||||
  hangUpActiveCall,
 | 
					  hangUpActiveCall,
 | 
				
			||||||
  hasInitialLoadCompleted,
 | 
					  hasInitialLoadCompleted,
 | 
				
			||||||
| 
						 | 
					@ -508,7 +471,6 @@ export function CallManager({
 | 
				
			||||||
  isConversationTooBigToRing,
 | 
					  isConversationTooBigToRing,
 | 
				
			||||||
  isGroupCallRaiseHandEnabled,
 | 
					  isGroupCallRaiseHandEnabled,
 | 
				
			||||||
  isGroupCallReactionsEnabled,
 | 
					  isGroupCallReactionsEnabled,
 | 
				
			||||||
  keyChangeOk,
 | 
					 | 
				
			||||||
  me,
 | 
					  me,
 | 
				
			||||||
  notifyForCall,
 | 
					  notifyForCall,
 | 
				
			||||||
  openSystemPreferencesAction,
 | 
					  openSystemPreferencesAction,
 | 
				
			||||||
| 
						 | 
					@ -517,7 +479,6 @@ export function CallManager({
 | 
				
			||||||
  renderDeviceSelection,
 | 
					  renderDeviceSelection,
 | 
				
			||||||
  renderEmojiPicker,
 | 
					  renderEmojiPicker,
 | 
				
			||||||
  renderReactionPicker,
 | 
					  renderReactionPicker,
 | 
				
			||||||
  renderSafetyNumberViewer,
 | 
					 | 
				
			||||||
  sendGroupCallRaiseHand,
 | 
					  sendGroupCallRaiseHand,
 | 
				
			||||||
  sendGroupCallReaction,
 | 
					  sendGroupCallReaction,
 | 
				
			||||||
  setGroupCallVideoRequest,
 | 
					  setGroupCallVideoRequest,
 | 
				
			||||||
| 
						 | 
					@ -533,7 +494,6 @@ export function CallManager({
 | 
				
			||||||
  stopRingtone,
 | 
					  stopRingtone,
 | 
				
			||||||
  switchFromPresentationView,
 | 
					  switchFromPresentationView,
 | 
				
			||||||
  switchToPresentationView,
 | 
					  switchToPresentationView,
 | 
				
			||||||
  theme,
 | 
					 | 
				
			||||||
  toggleParticipants,
 | 
					  toggleParticipants,
 | 
				
			||||||
  togglePip,
 | 
					  togglePip,
 | 
				
			||||||
  toggleScreenRecordingPermissionsDialog,
 | 
					  toggleScreenRecordingPermissionsDialog,
 | 
				
			||||||
| 
						 | 
					@ -594,20 +554,17 @@ export function CallManager({
 | 
				
			||||||
          changeCallView={changeCallView}
 | 
					          changeCallView={changeCallView}
 | 
				
			||||||
          closeNeedPermissionScreen={closeNeedPermissionScreen}
 | 
					          closeNeedPermissionScreen={closeNeedPermissionScreen}
 | 
				
			||||||
          getGroupCallVideoFrameSource={getGroupCallVideoFrameSource}
 | 
					          getGroupCallVideoFrameSource={getGroupCallVideoFrameSource}
 | 
				
			||||||
          getPreferredBadge={getPreferredBadge}
 | 
					 | 
				
			||||||
          getPresentingSources={getPresentingSources}
 | 
					          getPresentingSources={getPresentingSources}
 | 
				
			||||||
          hangUpActiveCall={hangUpActiveCall}
 | 
					          hangUpActiveCall={hangUpActiveCall}
 | 
				
			||||||
          i18n={i18n}
 | 
					          i18n={i18n}
 | 
				
			||||||
          isGroupCallRaiseHandEnabled={isGroupCallRaiseHandEnabled}
 | 
					          isGroupCallRaiseHandEnabled={isGroupCallRaiseHandEnabled}
 | 
				
			||||||
          isGroupCallReactionsEnabled={isGroupCallReactionsEnabled}
 | 
					          isGroupCallReactionsEnabled={isGroupCallReactionsEnabled}
 | 
				
			||||||
          keyChangeOk={keyChangeOk}
 | 
					 | 
				
			||||||
          me={me}
 | 
					          me={me}
 | 
				
			||||||
          openSystemPreferencesAction={openSystemPreferencesAction}
 | 
					          openSystemPreferencesAction={openSystemPreferencesAction}
 | 
				
			||||||
          pauseVoiceNotePlayer={pauseVoiceNotePlayer}
 | 
					          pauseVoiceNotePlayer={pauseVoiceNotePlayer}
 | 
				
			||||||
          renderDeviceSelection={renderDeviceSelection}
 | 
					          renderDeviceSelection={renderDeviceSelection}
 | 
				
			||||||
          renderEmojiPicker={renderEmojiPicker}
 | 
					          renderEmojiPicker={renderEmojiPicker}
 | 
				
			||||||
          renderReactionPicker={renderReactionPicker}
 | 
					          renderReactionPicker={renderReactionPicker}
 | 
				
			||||||
          renderSafetyNumberViewer={renderSafetyNumberViewer}
 | 
					 | 
				
			||||||
          sendGroupCallRaiseHand={sendGroupCallRaiseHand}
 | 
					          sendGroupCallRaiseHand={sendGroupCallRaiseHand}
 | 
				
			||||||
          sendGroupCallReaction={sendGroupCallReaction}
 | 
					          sendGroupCallReaction={sendGroupCallReaction}
 | 
				
			||||||
          setGroupCallVideoRequest={setGroupCallVideoRequest}
 | 
					          setGroupCallVideoRequest={setGroupCallVideoRequest}
 | 
				
			||||||
| 
						 | 
					@ -621,7 +578,6 @@ export function CallManager({
 | 
				
			||||||
          startCall={startCall}
 | 
					          startCall={startCall}
 | 
				
			||||||
          switchFromPresentationView={switchFromPresentationView}
 | 
					          switchFromPresentationView={switchFromPresentationView}
 | 
				
			||||||
          switchToPresentationView={switchToPresentationView}
 | 
					          switchToPresentationView={switchToPresentationView}
 | 
				
			||||||
          theme={theme}
 | 
					 | 
				
			||||||
          toggleParticipants={toggleParticipants}
 | 
					          toggleParticipants={toggleParticipants}
 | 
				
			||||||
          togglePip={togglePip}
 | 
					          togglePip={togglePip}
 | 
				
			||||||
          toggleScreenRecordingPermissionsDialog={
 | 
					          toggleScreenRecordingPermissionsDialog={
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -124,7 +124,6 @@ const createActiveGroupCallProp = (overrideProps: GroupCallOverrideProps) => ({
 | 
				
			||||||
  callMode: CallMode.Group as CallMode.Group,
 | 
					  callMode: CallMode.Group as CallMode.Group,
 | 
				
			||||||
  connectionState:
 | 
					  connectionState:
 | 
				
			||||||
    overrideProps.connectionState || GroupCallConnectionState.Connected,
 | 
					    overrideProps.connectionState || GroupCallConnectionState.Connected,
 | 
				
			||||||
  conversationsWithSafetyNumberChanges: [],
 | 
					 | 
				
			||||||
  conversationsByDemuxId: getConversationsByDemuxId(overrideProps),
 | 
					  conversationsByDemuxId: getConversationsByDemuxId(overrideProps),
 | 
				
			||||||
  joinState: GroupCallJoinState.Joined,
 | 
					  joinState: GroupCallJoinState.Joined,
 | 
				
			||||||
  localDemuxId: LOCAL_DEMUX_ID,
 | 
					  localDemuxId: LOCAL_DEMUX_ID,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -131,7 +131,6 @@ export function GroupCall(args: PropsType): JSX.Element {
 | 
				
			||||||
        ...getCommonActiveCallData({}),
 | 
					        ...getCommonActiveCallData({}),
 | 
				
			||||||
        callMode: CallMode.Group as CallMode.Group,
 | 
					        callMode: CallMode.Group as CallMode.Group,
 | 
				
			||||||
        connectionState: GroupCallConnectionState.Connected,
 | 
					        connectionState: GroupCallConnectionState.Connected,
 | 
				
			||||||
        conversationsWithSafetyNumberChanges: [],
 | 
					 | 
				
			||||||
        conversationsByDemuxId: new Map<number, ConversationType>(),
 | 
					        conversationsByDemuxId: new Map<number, ConversationType>(),
 | 
				
			||||||
        groupMembers: times(3, () => getDefaultConversation()),
 | 
					        groupMembers: times(3, () => getDefaultConversation()),
 | 
				
			||||||
        isConversationTooBigToRing: false,
 | 
					        isConversationTooBigToRing: false,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,7 +12,9 @@ import { jobQueueDatabaseStore } from './JobQueueDatabaseStore';
 | 
				
			||||||
import { JOB_STATUS, JobQueue } from './JobQueue';
 | 
					import { JOB_STATUS, JobQueue } from './JobQueue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { sendNormalMessage } from './helpers/sendNormalMessage';
 | 
					import { sendNormalMessage } from './helpers/sendNormalMessage';
 | 
				
			||||||
 | 
					import { sendCallingMessage } from './helpers/sendCallingMessage';
 | 
				
			||||||
import { sendDirectExpirationTimerUpdate } from './helpers/sendDirectExpirationTimerUpdate';
 | 
					import { sendDirectExpirationTimerUpdate } from './helpers/sendDirectExpirationTimerUpdate';
 | 
				
			||||||
 | 
					import { sendGroupCallUpdate } from './helpers/sendGroupCallUpdate';
 | 
				
			||||||
import { sendGroupUpdate } from './helpers/sendGroupUpdate';
 | 
					import { sendGroupUpdate } from './helpers/sendGroupUpdate';
 | 
				
			||||||
import { sendDeleteForEveryone } from './helpers/sendDeleteForEveryone';
 | 
					import { sendDeleteForEveryone } from './helpers/sendDeleteForEveryone';
 | 
				
			||||||
import { sendDeleteStoryForEveryone } from './helpers/sendDeleteStoryForEveryone';
 | 
					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
 | 
					// 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.
 | 
					//   these values, you'll likely need to write a database migration.
 | 
				
			||||||
export const conversationQueueJobEnum = z.enum([
 | 
					export const conversationQueueJobEnum = z.enum([
 | 
				
			||||||
 | 
					  'CallingMessage',
 | 
				
			||||||
  'DeleteForEveryone',
 | 
					  'DeleteForEveryone',
 | 
				
			||||||
  'DeleteStoryForEveryone',
 | 
					  'DeleteStoryForEveryone',
 | 
				
			||||||
  'DirectExpirationTimerUpdate',
 | 
					  'DirectExpirationTimerUpdate',
 | 
				
			||||||
 | 
					  'GroupCallUpdate',
 | 
				
			||||||
  'GroupUpdate',
 | 
					  'GroupUpdate',
 | 
				
			||||||
  'NormalMessage',
 | 
					  'NormalMessage',
 | 
				
			||||||
  'NullMessage',
 | 
					  'NullMessage',
 | 
				
			||||||
| 
						 | 
					@ -67,6 +71,17 @@ export const conversationQueueJobEnum = z.enum([
 | 
				
			||||||
]);
 | 
					]);
 | 
				
			||||||
type ConversationQueueJobEnum = z.infer<typeof conversationQueueJobEnum>;
 | 
					type ConversationQueueJobEnum = z.infer<typeof conversationQueueJobEnum>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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<typeof callingMessageJobDataSchema>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const deleteForEveryoneJobDataSchema = z.object({
 | 
					const deleteForEveryoneJobDataSchema = z.object({
 | 
				
			||||||
  type: z.literal(conversationQueueJobEnum.enum.DeleteForEveryone),
 | 
					  type: z.literal(conversationQueueJobEnum.enum.DeleteForEveryone),
 | 
				
			||||||
  conversationId: z.string(),
 | 
					  conversationId: z.string(),
 | 
				
			||||||
| 
						 | 
					@ -108,6 +123,16 @@ export type ExpirationTimerUpdateJobData = z.infer<
 | 
				
			||||||
  typeof expirationTimerUpdateJobDataSchema
 | 
					  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({
 | 
					const groupUpdateJobDataSchema = z.object({
 | 
				
			||||||
  type: z.literal(conversationQueueJobEnum.enum.GroupUpdate),
 | 
					  type: z.literal(conversationQueueJobEnum.enum.GroupUpdate),
 | 
				
			||||||
  conversationId: z.string(),
 | 
					  conversationId: z.string(),
 | 
				
			||||||
| 
						 | 
					@ -208,9 +233,11 @@ const receiptsJobDataSchema = z.object({
 | 
				
			||||||
export type ReceiptsJobData = z.infer<typeof receiptsJobDataSchema>;
 | 
					export type ReceiptsJobData = z.infer<typeof receiptsJobDataSchema>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const conversationQueueJobDataSchema = z.union([
 | 
					export const conversationQueueJobDataSchema = z.union([
 | 
				
			||||||
 | 
					  callingMessageJobDataSchema,
 | 
				
			||||||
  deleteForEveryoneJobDataSchema,
 | 
					  deleteForEveryoneJobDataSchema,
 | 
				
			||||||
  deleteStoryForEveryoneJobDataSchema,
 | 
					  deleteStoryForEveryoneJobDataSchema,
 | 
				
			||||||
  expirationTimerUpdateJobDataSchema,
 | 
					  expirationTimerUpdateJobDataSchema,
 | 
				
			||||||
 | 
					  groupCallUpdateJobDataSchema,
 | 
				
			||||||
  groupUpdateJobDataSchema,
 | 
					  groupUpdateJobDataSchema,
 | 
				
			||||||
  normalMessageSendJobDataSchema,
 | 
					  normalMessageSendJobDataSchema,
 | 
				
			||||||
  nullMessageJobDataSchema,
 | 
					  nullMessageJobDataSchema,
 | 
				
			||||||
| 
						 | 
					@ -239,6 +266,9 @@ const MAX_RETRY_TIME = durations.DAY;
 | 
				
			||||||
const MAX_ATTEMPTS = exponentialBackoffMaxAttempts(MAX_RETRY_TIME);
 | 
					const MAX_ATTEMPTS = exponentialBackoffMaxAttempts(MAX_RETRY_TIME);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function shouldSendShowCaptcha(type: ConversationQueueJobEnum): boolean {
 | 
					function shouldSendShowCaptcha(type: ConversationQueueJobEnum): boolean {
 | 
				
			||||||
 | 
					  if (type === 'CallingMessage') {
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
  if (type === 'DeleteForEveryone') {
 | 
					  if (type === 'DeleteForEveryone') {
 | 
				
			||||||
    return true;
 | 
					    return true;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					@ -248,6 +278,9 @@ function shouldSendShowCaptcha(type: ConversationQueueJobEnum): boolean {
 | 
				
			||||||
  if (type === 'DirectExpirationTimerUpdate') {
 | 
					  if (type === 'DirectExpirationTimerUpdate') {
 | 
				
			||||||
    return true;
 | 
					    return true;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  if (type === 'GroupCallUpdate') {
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
  if (type === 'GroupUpdate') {
 | 
					  if (type === 'GroupUpdate') {
 | 
				
			||||||
    return true;
 | 
					    return true;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					@ -263,6 +296,9 @@ function shouldSendShowCaptcha(type: ConversationQueueJobEnum): boolean {
 | 
				
			||||||
  if (type === 'Reaction') {
 | 
					  if (type === 'Reaction') {
 | 
				
			||||||
    return false;
 | 
					    return false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  if (type === 'Receipts') {
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
  if (type === 'ResendRequest') {
 | 
					  if (type === 'ResendRequest') {
 | 
				
			||||||
    return false;
 | 
					    return false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					@ -277,9 +313,6 @@ function shouldSendShowCaptcha(type: ConversationQueueJobEnum): boolean {
 | 
				
			||||||
  if (type === 'Story') {
 | 
					  if (type === 'Story') {
 | 
				
			||||||
    return true;
 | 
					    return true;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  if (type === 'Receipts') {
 | 
					 | 
				
			||||||
    return false;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  throw missingCaseError(type);
 | 
					  throw missingCaseError(type);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -785,6 +818,9 @@ export class ConversationJobQueue extends JobQueue<ConversationQueueJobData> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      switch (type) {
 | 
					      switch (type) {
 | 
				
			||||||
 | 
					        case jobSet.CallingMessage:
 | 
				
			||||||
 | 
					          await sendCallingMessage(conversation, jobBundle, data);
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
        case jobSet.DeleteForEveryone:
 | 
					        case jobSet.DeleteForEveryone:
 | 
				
			||||||
          await sendDeleteForEveryone(conversation, jobBundle, data);
 | 
					          await sendDeleteForEveryone(conversation, jobBundle, data);
 | 
				
			||||||
          break;
 | 
					          break;
 | 
				
			||||||
| 
						 | 
					@ -794,6 +830,9 @@ export class ConversationJobQueue extends JobQueue<ConversationQueueJobData> {
 | 
				
			||||||
        case jobSet.DirectExpirationTimerUpdate:
 | 
					        case jobSet.DirectExpirationTimerUpdate:
 | 
				
			||||||
          await sendDirectExpirationTimerUpdate(conversation, jobBundle, data);
 | 
					          await sendDirectExpirationTimerUpdate(conversation, jobBundle, data);
 | 
				
			||||||
          break;
 | 
					          break;
 | 
				
			||||||
 | 
					        case jobSet.GroupCallUpdate:
 | 
				
			||||||
 | 
					          await sendGroupCallUpdate(conversation, jobBundle, data);
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
        case jobSet.GroupUpdate:
 | 
					        case jobSet.GroupUpdate:
 | 
				
			||||||
          await sendGroupUpdate(conversation, jobBundle, data);
 | 
					          await sendGroupUpdate(conversation, jobBundle, data);
 | 
				
			||||||
          break;
 | 
					          break;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										40
									
								
								ts/jobs/helpers/getValidRecipients.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								ts/jobs/helpers/getValidRecipients.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -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<string>,
 | 
				
			||||||
 | 
					  options: {
 | 
				
			||||||
 | 
					    logId: string;
 | 
				
			||||||
 | 
					    log: LoggerType;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					): Array<ServiceIdString> {
 | 
				
			||||||
 | 
					  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);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										149
									
								
								ts/jobs/helpers/sendCallingMessage.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										149
									
								
								ts/jobs/helpers/sendCallingMessage.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -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<void> {
 | 
				
			||||||
 | 
					  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,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										110
									
								
								ts/jobs/helpers/sendGroupCallUpdate.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								ts/jobs/helpers/sendGroupCallUpdate.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -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<void> {
 | 
				
			||||||
 | 
					  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,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -11,7 +11,6 @@ import {
 | 
				
			||||||
import { wrapWithSyncMessageSend } from '../../util/wrapWithSyncMessageSend';
 | 
					import { wrapWithSyncMessageSend } from '../../util/wrapWithSyncMessageSend';
 | 
				
			||||||
import * as Bytes from '../../Bytes';
 | 
					import * as Bytes from '../../Bytes';
 | 
				
			||||||
import { strictAssert } from '../../util/assert';
 | 
					import { strictAssert } from '../../util/assert';
 | 
				
			||||||
import { isNotNil } from '../../util/isNotNil';
 | 
					 | 
				
			||||||
import { ourProfileKeyService } from '../../services/ourProfileKey';
 | 
					import { ourProfileKeyService } from '../../services/ourProfileKey';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import type { ConversationModel } from '../../models/conversations';
 | 
					import type { ConversationModel } from '../../models/conversations';
 | 
				
			||||||
| 
						 | 
					@ -22,6 +21,7 @@ import type {
 | 
				
			||||||
} from '../conversationJobQueue';
 | 
					} from '../conversationJobQueue';
 | 
				
			||||||
import { getUntrustedConversationServiceIds } from './getUntrustedConversationServiceIds';
 | 
					import { getUntrustedConversationServiceIds } from './getUntrustedConversationServiceIds';
 | 
				
			||||||
import { sendToGroup } from '../../util/sendToGroup';
 | 
					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
 | 
					// 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
 | 
					//   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 { groupChangeBase64, recipients: jobRecipients, revision } = data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const recipients = jobRecipients
 | 
					  const recipients = getValidRecipients(jobRecipients, { log, logId });
 | 
				
			||||||
    .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 untrustedServiceIds = getUntrustedConversationServiceIds(recipients);
 | 
					  const untrustedServiceIds = getUntrustedConversationServiceIds(recipients);
 | 
				
			||||||
  if (untrustedServiceIds.length) {
 | 
					  if (untrustedServiceIds.length) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3008,15 +3008,9 @@ export class ConversationModel extends window.Backbone
 | 
				
			||||||
        'addKeyChange'
 | 
					        'addKeyChange'
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const isUntrusted = await this.isUntrusted();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      this.trigger('newmessage', model);
 | 
					      this.trigger('newmessage', model);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const serviceId = this.getServiceId();
 | 
					      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)) {
 | 
					      if (isDirectConversation(this.attributes)) {
 | 
				
			||||||
        window.reduxActions?.safetyNumber.clearSafetyNumber(this.id);
 | 
					        window.reduxActions?.safetyNumber.clearSafetyNumber(this.id);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -81,9 +81,7 @@ import { dropNull } from '../util/dropNull';
 | 
				
			||||||
import { getOwn } from '../util/getOwn';
 | 
					import { getOwn } from '../util/getOwn';
 | 
				
			||||||
import * as durations from '../util/durations';
 | 
					import * as durations from '../util/durations';
 | 
				
			||||||
import { clearTimeoutIfNecessary } from '../util/clearTimeoutIfNecessary';
 | 
					import { clearTimeoutIfNecessary } from '../util/clearTimeoutIfNecessary';
 | 
				
			||||||
import { handleMessageSend } from '../util/handleMessageSend';
 | 
					 | 
				
			||||||
import { fetchMembershipProof, getMembershipList } from '../groups';
 | 
					import { fetchMembershipProof, getMembershipList } from '../groups';
 | 
				
			||||||
import { wrapWithSyncMessageSend } from '../util/wrapWithSyncMessageSend';
 | 
					 | 
				
			||||||
import type { ProcessedEnvelope } from '../textsecure/Types.d';
 | 
					import type { ProcessedEnvelope } from '../textsecure/Types.d';
 | 
				
			||||||
import { missingCaseError } from '../util/missingCaseError';
 | 
					import { missingCaseError } from '../util/missingCaseError';
 | 
				
			||||||
import { normalizeGroupCallTimestamp } from '../util/ringrtc/normalizeGroupCallTimestamp';
 | 
					import { normalizeGroupCallTimestamp } from '../util/ringrtc/normalizeGroupCallTimestamp';
 | 
				
			||||||
| 
						 | 
					@ -94,7 +92,6 @@ import {
 | 
				
			||||||
  REQUESTED_VIDEO_FRAMERATE,
 | 
					  REQUESTED_VIDEO_FRAMERATE,
 | 
				
			||||||
} from '../calling/constants';
 | 
					} from '../calling/constants';
 | 
				
			||||||
import { callingMessageToProto } from '../util/callingMessageToProto';
 | 
					import { callingMessageToProto } from '../util/callingMessageToProto';
 | 
				
			||||||
import { getSendOptions } from '../util/getSendOptions';
 | 
					 | 
				
			||||||
import { requestMicrophonePermissions } from '../util/requestMicrophonePermissions';
 | 
					import { requestMicrophonePermissions } from '../util/requestMicrophonePermissions';
 | 
				
			||||||
import OS from '../util/os/osMain';
 | 
					import OS from '../util/os/osMain';
 | 
				
			||||||
import { SignalService as Proto } from '../protobuf';
 | 
					import { SignalService as Proto } from '../protobuf';
 | 
				
			||||||
| 
						 | 
					@ -107,7 +104,6 @@ import {
 | 
				
			||||||
} from './notifications';
 | 
					} from './notifications';
 | 
				
			||||||
import * as log from '../logging/log';
 | 
					import * as log from '../logging/log';
 | 
				
			||||||
import { assertDev, strictAssert } from '../util/assert';
 | 
					import { assertDev, strictAssert } from '../util/assert';
 | 
				
			||||||
import { sendContentMessageToGroup, sendToGroup } from '../util/sendToGroup';
 | 
					 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  formatLocalDeviceState,
 | 
					  formatLocalDeviceState,
 | 
				
			||||||
  formatPeekInfo,
 | 
					  formatPeekInfo,
 | 
				
			||||||
| 
						 | 
					@ -130,13 +126,14 @@ import {
 | 
				
			||||||
} from '../util/callDisposition';
 | 
					} from '../util/callDisposition';
 | 
				
			||||||
import { isNormalNumber } from '../util/isNormalNumber';
 | 
					import { isNormalNumber } from '../util/isNormalNumber';
 | 
				
			||||||
import { LocalCallEvent } from '../types/CallDisposition';
 | 
					import { LocalCallEvent } from '../types/CallDisposition';
 | 
				
			||||||
import { isServiceIdString } from '../types/ServiceId';
 | 
					import { isServiceIdString, type ServiceIdString } from '../types/ServiceId';
 | 
				
			||||||
import { isInSystemContacts } from '../util/isInSystemContacts';
 | 
					import { isInSystemContacts } from '../util/isInSystemContacts';
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  getRoomIdFromRootKey,
 | 
					  getRoomIdFromRootKey,
 | 
				
			||||||
  getCallLinkAuthCredentialPresentation,
 | 
					  getCallLinkAuthCredentialPresentation,
 | 
				
			||||||
} from '../util/callLinks';
 | 
					} from '../util/callLinks';
 | 
				
			||||||
import { isAdhocCallingEnabled } from '../util/isAdhocCallingEnabled';
 | 
					import { isAdhocCallingEnabled } from '../util/isAdhocCallingEnabled';
 | 
				
			||||||
 | 
					import { conversationJobQueue } from '../jobs/conversationJobQueue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const {
 | 
					const {
 | 
				
			||||||
  processGroupCallRingCancellation,
 | 
					  processGroupCallRingCancellation,
 | 
				
			||||||
| 
						 | 
					@ -1430,53 +1427,36 @@ export class CallingClass {
 | 
				
			||||||
  private async sendGroupCallUpdateMessage(
 | 
					  private async sendGroupCallUpdateMessage(
 | 
				
			||||||
    conversationId: string,
 | 
					    conversationId: string,
 | 
				
			||||||
    eraId: string
 | 
					    eraId: string
 | 
				
			||||||
  ): Promise<void> {
 | 
					  ): Promise<boolean> {
 | 
				
			||||||
    const conversation = window.ConversationController.get(conversationId);
 | 
					    const conversation = window.ConversationController.get(conversationId);
 | 
				
			||||||
    if (!conversation) {
 | 
					    if (!conversation) {
 | 
				
			||||||
      log.error(
 | 
					      log.error('sendGroupCallUpdateMessage: Conversation not found!');
 | 
				
			||||||
        'Unable to send group call update message for non-existent conversation'
 | 
					      return false;
 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
      return;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const logId = `sendGroupCallUpdateMessage/${conversation.idForLogging()}`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const groupV2 = conversation.getGroupV2Info();
 | 
					    const groupV2 = conversation.getGroupV2Info();
 | 
				
			||||||
    const sendOptions = await getSendOptions(conversation.attributes);
 | 
					 | 
				
			||||||
    if (!groupV2) {
 | 
					    if (!groupV2) {
 | 
				
			||||||
      log.error(
 | 
					      log.error(`${logId}: Conversation lacks groupV2 info!`);
 | 
				
			||||||
        'Unable to send group call update message for conversation that lacks groupV2 info'
 | 
					      return false;
 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
      return;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const timestamp = Date.now();
 | 
					    try {
 | 
				
			||||||
 | 
					      await conversationJobQueue.add({
 | 
				
			||||||
    // We "fire and forget" because sending this message is non-essential.
 | 
					        type: 'GroupCallUpdate',
 | 
				
			||||||
    const { ContentHint } = Proto.UnidentifiedSenderMessage.Message;
 | 
					        conversationId: conversation.id,
 | 
				
			||||||
    wrapWithSyncMessageSend({
 | 
					        eraId,
 | 
				
			||||||
      conversation,
 | 
					        urgent: true,
 | 
				
			||||||
      logId: `sendToGroup/groupCallUpdate/${conversationId}-${eraId}`,
 | 
					      });
 | 
				
			||||||
      messageIds: [],
 | 
					      return true;
 | 
				
			||||||
      send: () =>
 | 
					    } catch (err) {
 | 
				
			||||||
        conversation.queueJob('sendGroupCallUpdateMessage', () =>
 | 
					      log.error(
 | 
				
			||||||
          sendToGroup({
 | 
					        `${logId}: Failed to queue call update:`,
 | 
				
			||||||
            contentHint: ContentHint.DEFAULT,
 | 
					        Errors.toLogFormat(err)
 | 
				
			||||||
            groupSendOptions: {
 | 
					      );
 | 
				
			||||||
              groupCallUpdate: { eraId },
 | 
					      return false;
 | 
				
			||||||
              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));
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async acceptDirectCall(
 | 
					  async acceptDirectCall(
 | 
				
			||||||
| 
						 | 
					@ -2109,50 +2089,71 @@ export class CallingClass {
 | 
				
			||||||
  private async handleSendCallMessageToGroup(
 | 
					  private async handleSendCallMessageToGroup(
 | 
				
			||||||
    groupIdBytes: Buffer,
 | 
					    groupIdBytes: Buffer,
 | 
				
			||||||
    data: Buffer,
 | 
					    data: Buffer,
 | 
				
			||||||
    urgency: CallMessageUrgency
 | 
					    urgency: CallMessageUrgency,
 | 
				
			||||||
  ): Promise<void> {
 | 
					    overrideRecipients: Array<Buffer> = []
 | 
				
			||||||
 | 
					  ): Promise<boolean> {
 | 
				
			||||||
    const groupId = groupIdBytes.toString('base64');
 | 
					    const groupId = groupIdBytes.toString('base64');
 | 
				
			||||||
    const conversation = window.ConversationController.get(groupId);
 | 
					    const conversation = window.ConversationController.get(groupId);
 | 
				
			||||||
    if (!conversation) {
 | 
					    if (!conversation) {
 | 
				
			||||||
      log.error('handleSendCallMessageToGroup(): could not find 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
 | 
					    // If this message isn't droppable, we'll wake up recipient devices. The important one
 | 
				
			||||||
    //   is the first message to start the call.
 | 
					    //   is the first message to start the call.
 | 
				
			||||||
    const urgent = urgency === CallMessageUrgency.HandleImmediately;
 | 
					    const urgent = urgency === CallMessageUrgency.HandleImmediately;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // We "fire and forget" because sending this message is non-essential.
 | 
					    try {
 | 
				
			||||||
    // We also don't sync this message.
 | 
					      let recipients: Array<ServiceIdString> = [];
 | 
				
			||||||
    const { ContentHint } = Proto.UnidentifiedSenderMessage.Message;
 | 
					      let isPartialSend = false;
 | 
				
			||||||
    await conversation.queueJob('handleSendCallMessageToGroup', async () =>
 | 
					      if (overrideRecipients.length > 0) {
 | 
				
			||||||
      handleMessageSend(
 | 
					        // Send only to the overriding recipients.
 | 
				
			||||||
        sendContentMessageToGroup({
 | 
					        overrideRecipients.forEach(recipient => {
 | 
				
			||||||
          contentHint: ContentHint.DEFAULT,
 | 
					          const serviceId = bytesToUuid(recipient);
 | 
				
			||||||
          contentMessage,
 | 
					          if (!serviceId) {
 | 
				
			||||||
          isPartialSend: false,
 | 
					            log.error(
 | 
				
			||||||
          messageId: undefined,
 | 
					              'handleSendCallMessageToGroup(): missing recipient serviceId'
 | 
				
			||||||
          recipients: conversation.getRecipients(),
 | 
					            );
 | 
				
			||||||
          sendOptions: await getSendOptions(conversation.attributes),
 | 
					          } else {
 | 
				
			||||||
          sendTarget: conversation.toSenderKeyTarget(),
 | 
					            assertDev(
 | 
				
			||||||
          sendType: 'callingMessage',
 | 
					              isServiceIdString(serviceId),
 | 
				
			||||||
          timestamp,
 | 
					              'remoteServiceId is not a serviceId'
 | 
				
			||||||
          urgent,
 | 
					            );
 | 
				
			||||||
        }),
 | 
					            recipients.push(serviceId);
 | 
				
			||||||
        { messageIds: [], sendType: 'callingMessage' }
 | 
					          }
 | 
				
			||||||
      )
 | 
					        });
 | 
				
			||||||
    );
 | 
					        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(
 | 
					  private async handleGroupCallRingUpdate(
 | 
				
			||||||
| 
						 | 
					@ -2275,15 +2276,14 @@ export class CallingClass {
 | 
				
			||||||
    message: CallingMessage,
 | 
					    message: CallingMessage,
 | 
				
			||||||
    urgency?: CallMessageUrgency
 | 
					    urgency?: CallMessageUrgency
 | 
				
			||||||
  ): Promise<boolean> {
 | 
					  ): Promise<boolean> {
 | 
				
			||||||
    const conversation = window.ConversationController.get(remoteUserId);
 | 
					    assertDev(
 | 
				
			||||||
    const sendOptions = conversation
 | 
					      isServiceIdString(remoteUserId),
 | 
				
			||||||
      ? await getSendOptions(conversation.attributes)
 | 
					      'remoteUserId is not a service id'
 | 
				
			||||||
      : undefined;
 | 
					    );
 | 
				
			||||||
 | 
					    const conversation = window.ConversationController.getOrCreate(
 | 
				
			||||||
    if (!window.textsecure.messaging) {
 | 
					      remoteUserId,
 | 
				
			||||||
      log.warn('handleOutgoingSignaling() returning false; offline');
 | 
					      'private'
 | 
				
			||||||
      return false;
 | 
					    );
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // We want 1:1 call initiate messages to wake up recipient devices, but not others
 | 
					    // We want 1:1 call initiate messages to wake up recipient devices, but not others
 | 
				
			||||||
    const urgent =
 | 
					    const urgent =
 | 
				
			||||||
| 
						 | 
					@ -2291,32 +2291,23 @@ export class CallingClass {
 | 
				
			||||||
      Boolean(message.offer);
 | 
					      Boolean(message.offer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      assertDev(
 | 
					      const proto = callingMessageToProto(message, urgency);
 | 
				
			||||||
        isServiceIdString(remoteUserId),
 | 
					      const protoBytes = Proto.CallingMessage.encode(proto).finish();
 | 
				
			||||||
        'remoteUserId is not a service id'
 | 
					      const protoBase64 = Bytes.toBase64(protoBytes);
 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
      const result = await handleMessageSend(
 | 
					 | 
				
			||||||
        window.textsecure.messaging.sendCallingMessage(
 | 
					 | 
				
			||||||
          remoteUserId,
 | 
					 | 
				
			||||||
          callingMessageToProto(message, urgency),
 | 
					 | 
				
			||||||
          urgent,
 | 
					 | 
				
			||||||
          sendOptions
 | 
					 | 
				
			||||||
        ),
 | 
					 | 
				
			||||||
        { messageIds: [], sendType: 'callingMessage' }
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (result && result.errors && result.errors.length) {
 | 
					      await conversationJobQueue.add({
 | 
				
			||||||
        throw result.errors[0];
 | 
					        type: 'CallingMessage',
 | 
				
			||||||
      }
 | 
					        conversationId: conversation.id,
 | 
				
			||||||
 | 
					        protoBase64,
 | 
				
			||||||
 | 
					        urgent,
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      log.info('handleOutgoingSignaling() completed successfully');
 | 
					 | 
				
			||||||
      return true;
 | 
					      return true;
 | 
				
			||||||
    } catch (err) {
 | 
					    } catch (err) {
 | 
				
			||||||
      if (err && err.errors && err.errors.length > 0) {
 | 
					      const errorString = Errors.toLogFormat(err);
 | 
				
			||||||
        log.error(`handleOutgoingSignaling() failed: ${err.errors[0].reason}`);
 | 
					      log.error(
 | 
				
			||||||
      } else {
 | 
					        `handleOutgoingSignaling() failed to queue job: ${errorString}`
 | 
				
			||||||
        log.error('handleOutgoingSignaling() failed');
 | 
					      );
 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      return false;
 | 
					      return false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -152,7 +152,6 @@ export type ActiveCallStateType = {
 | 
				
			||||||
  pip: boolean;
 | 
					  pip: boolean;
 | 
				
			||||||
  presentingSource?: PresentedSource;
 | 
					  presentingSource?: PresentedSource;
 | 
				
			||||||
  presentingSourcesAvailable?: Array<PresentableSource>;
 | 
					  presentingSourcesAvailable?: Array<PresentableSource>;
 | 
				
			||||||
  safetyNumberChangedAcis: Array<AciString>;
 | 
					 | 
				
			||||||
  settingsDialogOpen: boolean;
 | 
					  settingsDialogOpen: boolean;
 | 
				
			||||||
  showNeedsScreenRecordingPermissionsWarning?: boolean;
 | 
					  showNeedsScreenRecordingPermissionsWarning?: boolean;
 | 
				
			||||||
  showParticipantsList: boolean;
 | 
					  showParticipantsList: boolean;
 | 
				
			||||||
| 
						 | 
					@ -249,14 +248,6 @@ type HangUpActionPayloadType = ReadonlyDeep<{
 | 
				
			||||||
  conversationId: string;
 | 
					  conversationId: string;
 | 
				
			||||||
}>;
 | 
					}>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type KeyChangedType = ReadonlyDeep<{
 | 
					 | 
				
			||||||
  aci: AciString;
 | 
					 | 
				
			||||||
}>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export type KeyChangeOkType = ReadonlyDeep<{
 | 
					 | 
				
			||||||
  conversationId: string;
 | 
					 | 
				
			||||||
}>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export type IncomingDirectCallType = ReadonlyDeep<{
 | 
					export type IncomingDirectCallType = ReadonlyDeep<{
 | 
				
			||||||
  conversationId: string;
 | 
					  conversationId: string;
 | 
				
			||||||
  isVideoCall: boolean;
 | 
					  isVideoCall: boolean;
 | 
				
			||||||
| 
						 | 
					@ -579,8 +570,6 @@ const GROUP_CALL_REACTIONS_EXPIRED = 'calling/GROUP_CALL_REACTIONS_EXPIRED';
 | 
				
			||||||
const HANG_UP = 'calling/HANG_UP';
 | 
					const HANG_UP = 'calling/HANG_UP';
 | 
				
			||||||
const INCOMING_DIRECT_CALL = 'calling/INCOMING_DIRECT_CALL';
 | 
					const INCOMING_DIRECT_CALL = 'calling/INCOMING_DIRECT_CALL';
 | 
				
			||||||
const INCOMING_GROUP_CALL = 'calling/INCOMING_GROUP_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 OUTGOING_CALL = 'calling/OUTGOING_CALL';
 | 
				
			||||||
const PEEK_GROUP_CALL_FULFILLED = 'calling/PEEK_GROUP_CALL_FULFILLED';
 | 
					const PEEK_GROUP_CALL_FULFILLED = 'calling/PEEK_GROUP_CALL_FULFILLED';
 | 
				
			||||||
const RAISE_HAND_GROUP_CALL = 'calling/RAISE_HAND_GROUP_CALL';
 | 
					const RAISE_HAND_GROUP_CALL = 'calling/RAISE_HAND_GROUP_CALL';
 | 
				
			||||||
| 
						 | 
					@ -725,19 +714,6 @@ type IncomingGroupCallActionType = ReadonlyDeep<{
 | 
				
			||||||
  payload: IncomingGroupCallType;
 | 
					  payload: IncomingGroupCallType;
 | 
				
			||||||
}>;
 | 
					}>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// eslint-disable-next-line local-rules/type-alias-readonlydeep
 | 
					 | 
				
			||||||
type KeyChangedActionType = {
 | 
					 | 
				
			||||||
  type: 'calling/MARK_CALL_UNTRUSTED';
 | 
					 | 
				
			||||||
  payload: {
 | 
					 | 
				
			||||||
    safetyNumberChangedAcis: Array<AciString>;
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type KeyChangeOkActionType = ReadonlyDeep<{
 | 
					 | 
				
			||||||
  type: 'calling/MARK_CALL_TRUSTED';
 | 
					 | 
				
			||||||
  payload: null;
 | 
					 | 
				
			||||||
}>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type SendGroupCallRaiseHandActionType = ReadonlyDeep<{
 | 
					type SendGroupCallRaiseHandActionType = ReadonlyDeep<{
 | 
				
			||||||
  type: 'calling/RAISE_HAND_GROUP_CALL';
 | 
					  type: 'calling/RAISE_HAND_GROUP_CALL';
 | 
				
			||||||
  payload: SendGroupCallRaiseHandType;
 | 
					  payload: SendGroupCallRaiseHandType;
 | 
				
			||||||
| 
						 | 
					@ -865,8 +841,6 @@ export type CallingActionType =
 | 
				
			||||||
  | HangUpActionType
 | 
					  | HangUpActionType
 | 
				
			||||||
  | IncomingDirectCallActionType
 | 
					  | IncomingDirectCallActionType
 | 
				
			||||||
  | IncomingGroupCallActionType
 | 
					  | IncomingGroupCallActionType
 | 
				
			||||||
  | KeyChangedActionType
 | 
					 | 
				
			||||||
  | KeyChangeOkActionType
 | 
					 | 
				
			||||||
  | OutgoingCallActionType
 | 
					  | OutgoingCallActionType
 | 
				
			||||||
  | PeekGroupCallFulfilledActionType
 | 
					  | PeekGroupCallFulfilledActionType
 | 
				
			||||||
  | RefreshIODevicesActionType
 | 
					  | RefreshIODevicesActionType
 | 
				
			||||||
| 
						 | 
					@ -1267,56 +1241,6 @@ function hangUpActiveCall(
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function keyChanged(
 | 
					 | 
				
			||||||
  payload: KeyChangedType
 | 
					 | 
				
			||||||
): ThunkAction<void, RootStateType, unknown, KeyChangedActionType> {
 | 
					 | 
				
			||||||
  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<void, RootStateType, unknown, KeyChangeOkActionType> {
 | 
					 | 
				
			||||||
  return dispatch => {
 | 
					 | 
				
			||||||
    calling.resendGroupCallMediaKeys(payload.conversationId);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    dispatch({
 | 
					 | 
				
			||||||
      type: MARK_CALL_TRUSTED,
 | 
					 | 
				
			||||||
      payload: null,
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function sendGroupCallRaiseHand(
 | 
					function sendGroupCallRaiseHand(
 | 
				
			||||||
  payload: SendGroupCallRaiseHandType
 | 
					  payload: SendGroupCallRaiseHandType
 | 
				
			||||||
): ThunkAction<void, RootStateType, unknown, SendGroupCallRaiseHandActionType> {
 | 
					): ThunkAction<void, RootStateType, unknown, SendGroupCallRaiseHandActionType> {
 | 
				
			||||||
| 
						 | 
					@ -2059,8 +1983,6 @@ export const actions = {
 | 
				
			||||||
  groupCallRaisedHandsChange,
 | 
					  groupCallRaisedHandsChange,
 | 
				
			||||||
  groupCallStateChange,
 | 
					  groupCallStateChange,
 | 
				
			||||||
  hangUpActiveCall,
 | 
					  hangUpActiveCall,
 | 
				
			||||||
  keyChangeOk,
 | 
					 | 
				
			||||||
  keyChanged,
 | 
					 | 
				
			||||||
  onOutgoingVideoCallInConversation,
 | 
					  onOutgoingVideoCallInConversation,
 | 
				
			||||||
  onOutgoingAudioCallInConversation,
 | 
					  onOutgoingAudioCallInConversation,
 | 
				
			||||||
  openSystemPreferencesAction,
 | 
					  openSystemPreferencesAction,
 | 
				
			||||||
| 
						 | 
					@ -2284,7 +2206,6 @@ export function reducer(
 | 
				
			||||||
        localAudioLevel: 0,
 | 
					        localAudioLevel: 0,
 | 
				
			||||||
        viewMode: CallViewMode.Paginated,
 | 
					        viewMode: CallViewMode.Paginated,
 | 
				
			||||||
        pip: false,
 | 
					        pip: false,
 | 
				
			||||||
        safetyNumberChangedAcis: [],
 | 
					 | 
				
			||||||
        settingsDialogOpen: false,
 | 
					        settingsDialogOpen: false,
 | 
				
			||||||
        showParticipantsList: false,
 | 
					        showParticipantsList: false,
 | 
				
			||||||
        outgoingRing,
 | 
					        outgoingRing,
 | 
				
			||||||
| 
						 | 
					@ -2314,7 +2235,6 @@ export function reducer(
 | 
				
			||||||
        localAudioLevel: 0,
 | 
					        localAudioLevel: 0,
 | 
				
			||||||
        viewMode: CallViewMode.Paginated,
 | 
					        viewMode: CallViewMode.Paginated,
 | 
				
			||||||
        pip: false,
 | 
					        pip: false,
 | 
				
			||||||
        safetyNumberChangedAcis: [],
 | 
					 | 
				
			||||||
        settingsDialogOpen: false,
 | 
					        settingsDialogOpen: false,
 | 
				
			||||||
        showParticipantsList: false,
 | 
					        showParticipantsList: false,
 | 
				
			||||||
        outgoingRing: true,
 | 
					        outgoingRing: true,
 | 
				
			||||||
| 
						 | 
					@ -2343,7 +2263,6 @@ export function reducer(
 | 
				
			||||||
        localAudioLevel: 0,
 | 
					        localAudioLevel: 0,
 | 
				
			||||||
        viewMode: CallViewMode.Paginated,
 | 
					        viewMode: CallViewMode.Paginated,
 | 
				
			||||||
        pip: false,
 | 
					        pip: false,
 | 
				
			||||||
        safetyNumberChangedAcis: [],
 | 
					 | 
				
			||||||
        settingsDialogOpen: false,
 | 
					        settingsDialogOpen: false,
 | 
				
			||||||
        showParticipantsList: false,
 | 
					        showParticipantsList: false,
 | 
				
			||||||
        outgoingRing: false,
 | 
					        outgoingRing: false,
 | 
				
			||||||
| 
						 | 
					@ -2505,7 +2424,6 @@ export function reducer(
 | 
				
			||||||
        localAudioLevel: 0,
 | 
					        localAudioLevel: 0,
 | 
				
			||||||
        viewMode: CallViewMode.Paginated,
 | 
					        viewMode: CallViewMode.Paginated,
 | 
				
			||||||
        pip: false,
 | 
					        pip: false,
 | 
				
			||||||
        safetyNumberChangedAcis: [],
 | 
					 | 
				
			||||||
        settingsDialogOpen: false,
 | 
					        settingsDialogOpen: false,
 | 
				
			||||||
        showParticipantsList: false,
 | 
					        showParticipantsList: false,
 | 
				
			||||||
        outgoingRing: true,
 | 
					        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;
 | 
					  return state;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -127,7 +127,7 @@ import { missingCaseError } from '../../util/missingCaseError';
 | 
				
			||||||
import { viewSyncJobQueue } from '../../jobs/viewSyncJobQueue';
 | 
					import { viewSyncJobQueue } from '../../jobs/viewSyncJobQueue';
 | 
				
			||||||
import { ReadStatus } from '../../messages/MessageReadStatus';
 | 
					import { ReadStatus } from '../../messages/MessageReadStatus';
 | 
				
			||||||
import { isIncoming, processBodyRanges } from '../selectors/message';
 | 
					import { isIncoming, processBodyRanges } from '../selectors/message';
 | 
				
			||||||
import { getActiveCallState } from '../selectors/calling';
 | 
					import { getActiveCall, getActiveCallState } from '../selectors/calling';
 | 
				
			||||||
import { sendDeleteForEveryoneMessage } from '../../util/sendDeleteForEveryoneMessage';
 | 
					import { sendDeleteForEveryoneMessage } from '../../util/sendDeleteForEveryoneMessage';
 | 
				
			||||||
import type { ShowToastActionType } from './toast';
 | 
					import type { ShowToastActionType } from './toast';
 | 
				
			||||||
import { SHOW_TOAST } from './toast';
 | 
					import { SHOW_TOAST } from './toast';
 | 
				
			||||||
| 
						 | 
					@ -2433,7 +2433,15 @@ export function cancelConversationVerification(
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Start the blocked conversation queues up again
 | 
					    // Start the blocked conversation queues up again
 | 
				
			||||||
 | 
					    const activeCall = getActiveCall(state);
 | 
				
			||||||
    conversationIdsBlocked.forEach(conversationId => {
 | 
					    conversationIdsBlocked.forEach(conversationId => {
 | 
				
			||||||
 | 
					      if (
 | 
				
			||||||
 | 
					        activeCall &&
 | 
				
			||||||
 | 
					        activeCall.conversationId === conversationId &&
 | 
				
			||||||
 | 
					        activeCall.callMode === CallMode.Direct
 | 
				
			||||||
 | 
					      ) {
 | 
				
			||||||
 | 
					        calling.hangup(conversationId, 'canceled conversation verification');
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
      conversationJobQueue.resolveVerificationWaiter(conversationId);
 | 
					      conversationJobQueue.resolveVerificationWaiter(conversationId);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,7 +9,6 @@ import type {
 | 
				
			||||||
  GroupIncomingCall,
 | 
					  GroupIncomingCall,
 | 
				
			||||||
} from '../../components/CallManager';
 | 
					} from '../../components/CallManager';
 | 
				
			||||||
import { CallManager } from '../../components/CallManager';
 | 
					import { CallManager } from '../../components/CallManager';
 | 
				
			||||||
import type { SafetyNumberProps } from '../../components/SafetyNumberChangeDialog';
 | 
					 | 
				
			||||||
import { isConversationTooBigToRing as getIsConversationTooBigToRing } from '../../conversations/isConversationTooBigToRing';
 | 
					import { isConversationTooBigToRing as getIsConversationTooBigToRing } from '../../conversations/isConversationTooBigToRing';
 | 
				
			||||||
import * as log from '../../logging/log';
 | 
					import * as log from '../../logging/log';
 | 
				
			||||||
import { calling as callingService } from '../../services/calling';
 | 
					import { calling as callingService } from '../../services/calling';
 | 
				
			||||||
| 
						 | 
					@ -47,16 +46,14 @@ import type { ConversationType } from '../ducks/conversations';
 | 
				
			||||||
import { useToastActions } from '../ducks/toast';
 | 
					import { useToastActions } from '../ducks/toast';
 | 
				
			||||||
import type { StateType } from '../reducer';
 | 
					import type { StateType } from '../reducer';
 | 
				
			||||||
import { getHasInitialLoadCompleted } from '../selectors/app';
 | 
					import { getHasInitialLoadCompleted } from '../selectors/app';
 | 
				
			||||||
import { getPreferredBadgeSelector } from '../selectors/badges';
 | 
					 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  getAvailableCameras,
 | 
					  getAvailableCameras,
 | 
				
			||||||
  getCallLinkSelector,
 | 
					  getCallLinkSelector,
 | 
				
			||||||
  getIncomingCall,
 | 
					  getIncomingCall,
 | 
				
			||||||
} from '../selectors/calling';
 | 
					} from '../selectors/calling';
 | 
				
			||||||
import { getConversationSelector, getMe } from '../selectors/conversations';
 | 
					import { getConversationSelector, getMe } from '../selectors/conversations';
 | 
				
			||||||
import { getIntl, getTheme } from '../selectors/user';
 | 
					import { getIntl } from '../selectors/user';
 | 
				
			||||||
import { SmartCallingDeviceSelection } from './CallingDeviceSelection';
 | 
					import { SmartCallingDeviceSelection } from './CallingDeviceSelection';
 | 
				
			||||||
import { SmartSafetyNumberViewer } from './SafetyNumberViewer';
 | 
					 | 
				
			||||||
import { renderEmojiPicker } from './renderEmojiPicker';
 | 
					import { renderEmojiPicker } from './renderEmojiPicker';
 | 
				
			||||||
import { renderReactionPicker } from './renderReactionPicker';
 | 
					import { renderReactionPicker } from './renderReactionPicker';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -64,10 +61,6 @@ function renderDeviceSelection(): JSX.Element {
 | 
				
			||||||
  return <SmartCallingDeviceSelection />;
 | 
					  return <SmartCallingDeviceSelection />;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function renderSafetyNumberViewer(props: SafetyNumberProps): JSX.Element {
 | 
					 | 
				
			||||||
  return <SmartSafetyNumberViewer {...props} />;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const getGroupCallVideoFrameSource =
 | 
					const getGroupCallVideoFrameSource =
 | 
				
			||||||
  callingService.getGroupCallVideoFrameSource.bind(callingService);
 | 
					  callingService.getGroupCallVideoFrameSource.bind(callingService);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -216,7 +209,6 @@ const mapStateToActiveCallProp = (
 | 
				
			||||||
      } satisfies ActiveDirectCallType;
 | 
					      } satisfies ActiveDirectCallType;
 | 
				
			||||||
    case CallMode.Group:
 | 
					    case CallMode.Group:
 | 
				
			||||||
    case CallMode.Adhoc: {
 | 
					    case CallMode.Adhoc: {
 | 
				
			||||||
      const conversationsWithSafetyNumberChanges: Array<ConversationType> = [];
 | 
					 | 
				
			||||||
      const groupMembers: Array<ConversationType> = [];
 | 
					      const groupMembers: Array<ConversationType> = [];
 | 
				
			||||||
      const remoteParticipants: Array<GroupCallRemoteParticipantType> = [];
 | 
					      const remoteParticipants: Array<GroupCallRemoteParticipantType> = [];
 | 
				
			||||||
      const peekedParticipants: Array<ConversationType> = [];
 | 
					      const peekedParticipants: Array<ConversationType> = [];
 | 
				
			||||||
| 
						 | 
					@ -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) {
 | 
					      for (let i = 0; i < peekInfo.acis.length; i += 1) {
 | 
				
			||||||
        const peekedParticipantAci = peekInfo.acis[i];
 | 
					        const peekedParticipantAci = peekInfo.acis[i];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -323,7 +299,6 @@ const mapStateToActiveCallProp = (
 | 
				
			||||||
        ...baseResult,
 | 
					        ...baseResult,
 | 
				
			||||||
        callMode: call.callMode,
 | 
					        callMode: call.callMode,
 | 
				
			||||||
        connectionState: call.connectionState,
 | 
					        connectionState: call.connectionState,
 | 
				
			||||||
        conversationsWithSafetyNumberChanges,
 | 
					 | 
				
			||||||
        conversationsByDemuxId,
 | 
					        conversationsByDemuxId,
 | 
				
			||||||
        deviceCount: peekInfo.deviceCount,
 | 
					        deviceCount: peekInfo.deviceCount,
 | 
				
			||||||
        groupMembers,
 | 
					        groupMembers,
 | 
				
			||||||
| 
						 | 
					@ -422,11 +397,9 @@ const mapStateToIncomingCallProp = (
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const SmartCallManager = memo(function SmartCallManager() {
 | 
					export const SmartCallManager = memo(function SmartCallManager() {
 | 
				
			||||||
  const i18n = useSelector(getIntl);
 | 
					  const i18n = useSelector(getIntl);
 | 
				
			||||||
  const theme = useSelector(getTheme);
 | 
					 | 
				
			||||||
  const activeCall = useSelector(mapStateToActiveCallProp);
 | 
					  const activeCall = useSelector(mapStateToActiveCallProp);
 | 
				
			||||||
  const callLink = useSelector(mapStateToCallLinkProp);
 | 
					  const callLink = useSelector(mapStateToCallLinkProp);
 | 
				
			||||||
  const incomingCall = useSelector(mapStateToIncomingCallProp);
 | 
					  const incomingCall = useSelector(mapStateToIncomingCallProp);
 | 
				
			||||||
  const getPreferredBadge = useSelector(getPreferredBadgeSelector);
 | 
					 | 
				
			||||||
  const availableCameras = useSelector(getAvailableCameras);
 | 
					  const availableCameras = useSelector(getAvailableCameras);
 | 
				
			||||||
  const hasInitialLoadCompleted = useSelector(getHasInitialLoadCompleted);
 | 
					  const hasInitialLoadCompleted = useSelector(getHasInitialLoadCompleted);
 | 
				
			||||||
  const me = useSelector(getMe);
 | 
					  const me = useSelector(getMe);
 | 
				
			||||||
| 
						 | 
					@ -439,7 +412,6 @@ export const SmartCallManager = memo(function SmartCallManager() {
 | 
				
			||||||
    closeNeedPermissionScreen,
 | 
					    closeNeedPermissionScreen,
 | 
				
			||||||
    getPresentingSources,
 | 
					    getPresentingSources,
 | 
				
			||||||
    cancelCall,
 | 
					    cancelCall,
 | 
				
			||||||
    keyChangeOk,
 | 
					 | 
				
			||||||
    startCall,
 | 
					    startCall,
 | 
				
			||||||
    toggleParticipants,
 | 
					    toggleParticipants,
 | 
				
			||||||
    acceptCall,
 | 
					    acceptCall,
 | 
				
			||||||
| 
						 | 
					@ -478,7 +450,6 @@ export const SmartCallManager = memo(function SmartCallManager() {
 | 
				
			||||||
      closeNeedPermissionScreen={closeNeedPermissionScreen}
 | 
					      closeNeedPermissionScreen={closeNeedPermissionScreen}
 | 
				
			||||||
      declineCall={declineCall}
 | 
					      declineCall={declineCall}
 | 
				
			||||||
      getGroupCallVideoFrameSource={getGroupCallVideoFrameSource}
 | 
					      getGroupCallVideoFrameSource={getGroupCallVideoFrameSource}
 | 
				
			||||||
      getPreferredBadge={getPreferredBadge}
 | 
					 | 
				
			||||||
      getPresentingSources={getPresentingSources}
 | 
					      getPresentingSources={getPresentingSources}
 | 
				
			||||||
      hangUpActiveCall={hangUpActiveCall}
 | 
					      hangUpActiveCall={hangUpActiveCall}
 | 
				
			||||||
      hasInitialLoadCompleted={hasInitialLoadCompleted}
 | 
					      hasInitialLoadCompleted={hasInitialLoadCompleted}
 | 
				
			||||||
| 
						 | 
					@ -487,7 +458,6 @@ export const SmartCallManager = memo(function SmartCallManager() {
 | 
				
			||||||
      isConversationTooBigToRing={isConversationTooBigToRing}
 | 
					      isConversationTooBigToRing={isConversationTooBigToRing}
 | 
				
			||||||
      isGroupCallRaiseHandEnabled={isGroupCallRaiseHandEnabled()}
 | 
					      isGroupCallRaiseHandEnabled={isGroupCallRaiseHandEnabled()}
 | 
				
			||||||
      isGroupCallReactionsEnabled={isGroupCallReactionsEnabled()}
 | 
					      isGroupCallReactionsEnabled={isGroupCallReactionsEnabled()}
 | 
				
			||||||
      keyChangeOk={keyChangeOk}
 | 
					 | 
				
			||||||
      me={me}
 | 
					      me={me}
 | 
				
			||||||
      notifyForCall={notifyForCall}
 | 
					      notifyForCall={notifyForCall}
 | 
				
			||||||
      openSystemPreferencesAction={openSystemPreferencesAction}
 | 
					      openSystemPreferencesAction={openSystemPreferencesAction}
 | 
				
			||||||
| 
						 | 
					@ -496,7 +466,6 @@ export const SmartCallManager = memo(function SmartCallManager() {
 | 
				
			||||||
      renderDeviceSelection={renderDeviceSelection}
 | 
					      renderDeviceSelection={renderDeviceSelection}
 | 
				
			||||||
      renderEmojiPicker={renderEmojiPicker}
 | 
					      renderEmojiPicker={renderEmojiPicker}
 | 
				
			||||||
      renderReactionPicker={renderReactionPicker}
 | 
					      renderReactionPicker={renderReactionPicker}
 | 
				
			||||||
      renderSafetyNumberViewer={renderSafetyNumberViewer}
 | 
					 | 
				
			||||||
      sendGroupCallRaiseHand={sendGroupCallRaiseHand}
 | 
					      sendGroupCallRaiseHand={sendGroupCallRaiseHand}
 | 
				
			||||||
      sendGroupCallReaction={sendGroupCallReaction}
 | 
					      sendGroupCallReaction={sendGroupCallReaction}
 | 
				
			||||||
      setGroupCallVideoRequest={setGroupCallVideoRequest}
 | 
					      setGroupCallVideoRequest={setGroupCallVideoRequest}
 | 
				
			||||||
| 
						 | 
					@ -512,7 +481,6 @@ export const SmartCallManager = memo(function SmartCallManager() {
 | 
				
			||||||
      stopRingtone={stopRingtone}
 | 
					      stopRingtone={stopRingtone}
 | 
				
			||||||
      switchFromPresentationView={switchFromPresentationView}
 | 
					      switchFromPresentationView={switchFromPresentationView}
 | 
				
			||||||
      switchToPresentationView={switchToPresentationView}
 | 
					      switchToPresentationView={switchToPresentationView}
 | 
				
			||||||
      theme={theme}
 | 
					 | 
				
			||||||
      toggleParticipants={toggleParticipants}
 | 
					      toggleParticipants={toggleParticipants}
 | 
				
			||||||
      togglePip={togglePip}
 | 
					      togglePip={togglePip}
 | 
				
			||||||
      toggleScreenRecordingPermissionsDialog={
 | 
					      toggleScreenRecordingPermissionsDialog={
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -70,7 +70,6 @@ describe('calling duck', () => {
 | 
				
			||||||
      localAudioLevel: 0,
 | 
					      localAudioLevel: 0,
 | 
				
			||||||
      viewMode: CallViewMode.Paginated,
 | 
					      viewMode: CallViewMode.Paginated,
 | 
				
			||||||
      showParticipantsList: false,
 | 
					      showParticipantsList: false,
 | 
				
			||||||
      safetyNumberChangedAcis: [],
 | 
					 | 
				
			||||||
      outgoingRing: true,
 | 
					      outgoingRing: true,
 | 
				
			||||||
      pip: false,
 | 
					      pip: false,
 | 
				
			||||||
      settingsDialogOpen: false,
 | 
					      settingsDialogOpen: false,
 | 
				
			||||||
| 
						 | 
					@ -153,7 +152,6 @@ describe('calling duck', () => {
 | 
				
			||||||
      localAudioLevel: 0,
 | 
					      localAudioLevel: 0,
 | 
				
			||||||
      viewMode: CallViewMode.Paginated,
 | 
					      viewMode: CallViewMode.Paginated,
 | 
				
			||||||
      showParticipantsList: false,
 | 
					      showParticipantsList: false,
 | 
				
			||||||
      safetyNumberChangedAcis: [],
 | 
					 | 
				
			||||||
      outgoingRing: false,
 | 
					      outgoingRing: false,
 | 
				
			||||||
      pip: false,
 | 
					      pip: false,
 | 
				
			||||||
      settingsDialogOpen: false,
 | 
					      settingsDialogOpen: false,
 | 
				
			||||||
| 
						 | 
					@ -482,7 +480,6 @@ describe('calling duck', () => {
 | 
				
			||||||
            localAudioLevel: 0,
 | 
					            localAudioLevel: 0,
 | 
				
			||||||
            viewMode: CallViewMode.Paginated,
 | 
					            viewMode: CallViewMode.Paginated,
 | 
				
			||||||
            showParticipantsList: false,
 | 
					            showParticipantsList: false,
 | 
				
			||||||
            safetyNumberChangedAcis: [],
 | 
					 | 
				
			||||||
            outgoingRing: false,
 | 
					            outgoingRing: false,
 | 
				
			||||||
            pip: false,
 | 
					            pip: false,
 | 
				
			||||||
            settingsDialogOpen: false,
 | 
					            settingsDialogOpen: false,
 | 
				
			||||||
| 
						 | 
					@ -577,7 +574,6 @@ describe('calling duck', () => {
 | 
				
			||||||
            localAudioLevel: 0,
 | 
					            localAudioLevel: 0,
 | 
				
			||||||
            viewMode: CallViewMode.Paginated,
 | 
					            viewMode: CallViewMode.Paginated,
 | 
				
			||||||
            showParticipantsList: false,
 | 
					            showParticipantsList: false,
 | 
				
			||||||
            safetyNumberChangedAcis: [],
 | 
					 | 
				
			||||||
            outgoingRing: false,
 | 
					            outgoingRing: false,
 | 
				
			||||||
            pip: false,
 | 
					            pip: false,
 | 
				
			||||||
            settingsDialogOpen: false,
 | 
					            settingsDialogOpen: false,
 | 
				
			||||||
| 
						 | 
					@ -1198,7 +1194,6 @@ describe('calling duck', () => {
 | 
				
			||||||
          localAudioLevel: 0,
 | 
					          localAudioLevel: 0,
 | 
				
			||||||
          viewMode: CallViewMode.Paginated,
 | 
					          viewMode: CallViewMode.Paginated,
 | 
				
			||||||
          showParticipantsList: false,
 | 
					          showParticipantsList: false,
 | 
				
			||||||
          safetyNumberChangedAcis: [],
 | 
					 | 
				
			||||||
          outgoingRing: false,
 | 
					          outgoingRing: false,
 | 
				
			||||||
          pip: false,
 | 
					          pip: false,
 | 
				
			||||||
          settingsDialogOpen: false,
 | 
					          settingsDialogOpen: false,
 | 
				
			||||||
| 
						 | 
					@ -1887,7 +1882,6 @@ describe('calling duck', () => {
 | 
				
			||||||
            localAudioLevel: 0,
 | 
					            localAudioLevel: 0,
 | 
				
			||||||
            viewMode: CallViewMode.Paginated,
 | 
					            viewMode: CallViewMode.Paginated,
 | 
				
			||||||
            showParticipantsList: false,
 | 
					            showParticipantsList: false,
 | 
				
			||||||
            safetyNumberChangedAcis: [],
 | 
					 | 
				
			||||||
            pip: false,
 | 
					            pip: false,
 | 
				
			||||||
            settingsDialogOpen: false,
 | 
					            settingsDialogOpen: false,
 | 
				
			||||||
            outgoingRing: true,
 | 
					            outgoingRing: true,
 | 
				
			||||||
| 
						 | 
					@ -2195,7 +2189,6 @@ describe('calling duck', () => {
 | 
				
			||||||
          localAudioLevel: 0,
 | 
					          localAudioLevel: 0,
 | 
				
			||||||
          viewMode: CallViewMode.Paginated,
 | 
					          viewMode: CallViewMode.Paginated,
 | 
				
			||||||
          showParticipantsList: false,
 | 
					          showParticipantsList: false,
 | 
				
			||||||
          safetyNumberChangedAcis: [],
 | 
					 | 
				
			||||||
          pip: false,
 | 
					          pip: false,
 | 
				
			||||||
          settingsDialogOpen: false,
 | 
					          settingsDialogOpen: false,
 | 
				
			||||||
          outgoingRing: true,
 | 
					          outgoingRing: true,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -69,7 +69,6 @@ describe('state/selectors/calling', () => {
 | 
				
			||||||
      localAudioLevel: 0,
 | 
					      localAudioLevel: 0,
 | 
				
			||||||
      viewMode: CallViewMode.Paginated,
 | 
					      viewMode: CallViewMode.Paginated,
 | 
				
			||||||
      showParticipantsList: false,
 | 
					      showParticipantsList: false,
 | 
				
			||||||
      safetyNumberChangedAcis: [],
 | 
					 | 
				
			||||||
      outgoingRing: true,
 | 
					      outgoingRing: true,
 | 
				
			||||||
      pip: false,
 | 
					      pip: false,
 | 
				
			||||||
      settingsDialogOpen: false,
 | 
					      settingsDialogOpen: false,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1722,11 +1722,11 @@ export default class MessageSender {
 | 
				
			||||||
  async sendCallingMessage(
 | 
					  async sendCallingMessage(
 | 
				
			||||||
    serviceId: ServiceIdString,
 | 
					    serviceId: ServiceIdString,
 | 
				
			||||||
    callingMessage: Readonly<Proto.ICallingMessage>,
 | 
					    callingMessage: Readonly<Proto.ICallingMessage>,
 | 
				
			||||||
 | 
					    timestamp: number,
 | 
				
			||||||
    urgent: boolean,
 | 
					    urgent: boolean,
 | 
				
			||||||
    options?: Readonly<SendOptionsType>
 | 
					    options?: Readonly<SendOptionsType>
 | 
				
			||||||
  ): Promise<CallbackResultType> {
 | 
					  ): Promise<CallbackResultType> {
 | 
				
			||||||
    const recipients = [serviceId];
 | 
					    const recipients = [serviceId];
 | 
				
			||||||
    const finalTimestamp = Date.now();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const contentMessage = new Proto.Content();
 | 
					    const contentMessage = new Proto.Content();
 | 
				
			||||||
    contentMessage.callingMessage = callingMessage;
 | 
					    contentMessage.callingMessage = callingMessage;
 | 
				
			||||||
| 
						 | 
					@ -1736,13 +1736,13 @@ export default class MessageSender {
 | 
				
			||||||
    addPniSignatureMessageToProto({
 | 
					    addPniSignatureMessageToProto({
 | 
				
			||||||
      conversation,
 | 
					      conversation,
 | 
				
			||||||
      proto: contentMessage,
 | 
					      proto: contentMessage,
 | 
				
			||||||
      reason: `sendCallingMessage(${finalTimestamp})`,
 | 
					      reason: `sendCallingMessage(${timestamp})`,
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const { ContentHint } = Proto.UnidentifiedSenderMessage.Message;
 | 
					    const { ContentHint } = Proto.UnidentifiedSenderMessage.Message;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return this.sendMessageProtoAndWait({
 | 
					    return this.sendMessageProtoAndWait({
 | 
				
			||||||
      timestamp: finalTimestamp,
 | 
					      timestamp,
 | 
				
			||||||
      recipients,
 | 
					      recipients,
 | 
				
			||||||
      proto: contentMessage,
 | 
					      proto: contentMessage,
 | 
				
			||||||
      contentHint: ContentHint.DEFAULT,
 | 
					      contentHint: ContentHint.DEFAULT,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -90,7 +90,6 @@ export type ActiveGroupCallType = ActiveCallBaseType & {
 | 
				
			||||||
  callMode: CallMode.Group | CallMode.Adhoc;
 | 
					  callMode: CallMode.Group | CallMode.Adhoc;
 | 
				
			||||||
  connectionState: GroupCallConnectionState;
 | 
					  connectionState: GroupCallConnectionState;
 | 
				
			||||||
  conversationsByDemuxId: ConversationsByDemuxIdType;
 | 
					  conversationsByDemuxId: ConversationsByDemuxIdType;
 | 
				
			||||||
  conversationsWithSafetyNumberChanges: Array<ConversationType>;
 | 
					 | 
				
			||||||
  joinState: GroupCallJoinState;
 | 
					  joinState: GroupCallJoinState;
 | 
				
			||||||
  localDemuxId: number | undefined;
 | 
					  localDemuxId: number | undefined;
 | 
				
			||||||
  maxDevices: number;
 | 
					  maxDevices: number;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue