Handles safety number changes while in a call
This commit is contained in:
parent
561baf6309
commit
318013e83d
26 changed files with 387 additions and 162 deletions
|
@ -425,6 +425,10 @@
|
||||||
"message": "Call Anyway",
|
"message": "Call Anyway",
|
||||||
"description": "Used on a warning dialog to make it clear that it might be risky to call the conversation."
|
"description": "Used on a warning dialog to make it clear that it might be risky to call the conversation."
|
||||||
},
|
},
|
||||||
|
"continueCall": {
|
||||||
|
"message": "Continue Call",
|
||||||
|
"description": "Used on a warning dialog to make it clear that it might be risky to continue the group call."
|
||||||
|
},
|
||||||
"noLongerVerified": {
|
"noLongerVerified": {
|
||||||
"message": "Your safety number with $name$ has changed and is no longer verified. Click to show.",
|
"message": "Your safety number with $name$ has changed and is no longer verified. Click to show.",
|
||||||
"description": "Shown in conversation banner when user's safety number has changed, but they were previously verified.",
|
"description": "Shown in conversation banner when user's safety number has changed, but they were previously verified.",
|
||||||
|
|
|
@ -904,7 +904,7 @@
|
||||||
// state we had before.
|
// state we had before.
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
async isUntrusted(identifier) {
|
isUntrusted(identifier) {
|
||||||
if (identifier === null || identifier === undefined) {
|
if (identifier === null || identifier === undefined) {
|
||||||
throw new Error('Tried to set verified for undefined/null key');
|
throw new Error('Tried to set verified for undefined/null key');
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,9 @@ import {
|
||||||
} from '../types/Calling';
|
} from '../types/Calling';
|
||||||
import { ConversationTypeType } from '../state/ducks/conversations';
|
import { ConversationTypeType } from '../state/ducks/conversations';
|
||||||
import { Colors, ColorType } from '../types/Colors';
|
import { Colors, ColorType } from '../types/Colors';
|
||||||
|
import { getDefaultConversation } from '../util/getDefaultConversation';
|
||||||
import { setup as setupI18n } from '../../js/modules/i18n';
|
import { setup as setupI18n } from '../../js/modules/i18n';
|
||||||
|
import { Props as SafetyNumberViewerProps } from '../state/smart/SafetyNumberViewer';
|
||||||
import enMessages from '../../_locales/en/messages.json';
|
import enMessages from '../../_locales/en/messages.json';
|
||||||
|
|
||||||
const i18n = setupI18n('en', enMessages);
|
const i18n = setupI18n('en', enMessages);
|
||||||
|
@ -68,12 +70,16 @@ const createProps = (storyProps: Partial<PropsType> = {}): PropsType => ({
|
||||||
getGroupCallVideoFrameSource: noop as any,
|
getGroupCallVideoFrameSource: noop as any,
|
||||||
hangUp: action('hang-up'),
|
hangUp: action('hang-up'),
|
||||||
i18n,
|
i18n,
|
||||||
|
keyChangeOk: action('key-change-ok'),
|
||||||
me: {
|
me: {
|
||||||
|
...getDefaultConversation({
|
||||||
|
color: select('Caller color', Colors, 'ultramarine' as ColorType),
|
||||||
|
title: text('Caller Title', 'Morty Smith'),
|
||||||
|
}),
|
||||||
uuid: 'cb0dd0c8-7393-41e9-a0aa-d631c4109541',
|
uuid: 'cb0dd0c8-7393-41e9-a0aa-d631c4109541',
|
||||||
color: select('Caller color', Colors, 'ultramarine' as ColorType),
|
|
||||||
title: text('Caller Title', 'Morty Smith'),
|
|
||||||
},
|
},
|
||||||
renderDeviceSelection: () => <div />,
|
renderDeviceSelection: () => <div />,
|
||||||
|
renderSafetyNumberViewer: (_: SafetyNumberViewerProps) => <div />,
|
||||||
setGroupCallVideoRequest: action('set-group-call-video-request'),
|
setGroupCallVideoRequest: action('set-group-call-video-request'),
|
||||||
setLocalAudio: action('set-local-audio'),
|
setLocalAudio: action('set-local-audio'),
|
||||||
setLocalPreview: action('set-local-preview'),
|
setLocalPreview: action('set-local-preview'),
|
||||||
|
@ -110,6 +116,7 @@ story.add('Ongoing Group Call', () => (
|
||||||
...getCommonActiveCallData(),
|
...getCommonActiveCallData(),
|
||||||
callMode: CallMode.Group,
|
callMode: CallMode.Group,
|
||||||
connectionState: GroupCallConnectionState.Connected,
|
connectionState: GroupCallConnectionState.Connected,
|
||||||
|
conversationsWithSafetyNumberChanges: [],
|
||||||
deviceCount: 0,
|
deviceCount: 0,
|
||||||
joinState: GroupCallJoinState.Joined,
|
joinState: GroupCallJoinState.Joined,
|
||||||
maxDevices: 5,
|
maxDevices: 5,
|
||||||
|
@ -145,3 +152,27 @@ story.add('Call Request Needed', () => (
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
|
|
||||||
|
story.add('Group call - Safety Number Changed', () => (
|
||||||
|
<CallManager
|
||||||
|
{...createProps({
|
||||||
|
activeCall: {
|
||||||
|
...getCommonActiveCallData(),
|
||||||
|
callMode: CallMode.Group,
|
||||||
|
connectionState: GroupCallConnectionState.Connected,
|
||||||
|
conversationsWithSafetyNumberChanges: [
|
||||||
|
{
|
||||||
|
...getDefaultConversation({
|
||||||
|
title: 'Aaron',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
deviceCount: 0,
|
||||||
|
joinState: GroupCallJoinState.Joined,
|
||||||
|
maxDevices: 5,
|
||||||
|
peekedParticipants: [],
|
||||||
|
remoteParticipants: [],
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
|
|
@ -8,6 +8,10 @@ import { CallingLobby } from './CallingLobby';
|
||||||
import { CallingParticipantsList } from './CallingParticipantsList';
|
import { CallingParticipantsList } from './CallingParticipantsList';
|
||||||
import { CallingPip } from './CallingPip';
|
import { CallingPip } from './CallingPip';
|
||||||
import { IncomingCallBar } from './IncomingCallBar';
|
import { IncomingCallBar } from './IncomingCallBar';
|
||||||
|
import {
|
||||||
|
SafetyNumberChangeDialog,
|
||||||
|
SafetyNumberProps,
|
||||||
|
} from './SafetyNumberChangeDialog';
|
||||||
import {
|
import {
|
||||||
ActiveCallType,
|
ActiveCallType,
|
||||||
CallEndedReason,
|
CallEndedReason,
|
||||||
|
@ -24,6 +28,7 @@ import {
|
||||||
DeclineCallType,
|
DeclineCallType,
|
||||||
DirectCallStateType,
|
DirectCallStateType,
|
||||||
HangUpType,
|
HangUpType,
|
||||||
|
KeyChangeOkType,
|
||||||
SetGroupCallVideoRequestType,
|
SetGroupCallVideoRequestType,
|
||||||
SetLocalAudioType,
|
SetLocalAudioType,
|
||||||
SetLocalPreviewType,
|
SetLocalPreviewType,
|
||||||
|
@ -32,9 +37,12 @@ import {
|
||||||
StartCallType,
|
StartCallType,
|
||||||
} from '../state/ducks/calling';
|
} from '../state/ducks/calling';
|
||||||
import { LocalizerType } from '../types/Util';
|
import { LocalizerType } from '../types/Util';
|
||||||
import { ColorType } from '../types/Colors';
|
|
||||||
import { missingCaseError } from '../util/missingCaseError';
|
import { missingCaseError } from '../util/missingCaseError';
|
||||||
|
|
||||||
|
interface MeType extends ConversationType {
|
||||||
|
uuid: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface PropsType {
|
export interface PropsType {
|
||||||
activeCall?: ActiveCallType;
|
activeCall?: ActiveCallType;
|
||||||
availableCameras: Array<MediaDeviceInfo>;
|
availableCameras: Array<MediaDeviceInfo>;
|
||||||
|
@ -48,21 +56,15 @@ export interface PropsType {
|
||||||
call: DirectCallStateType;
|
call: DirectCallStateType;
|
||||||
conversation: ConversationType;
|
conversation: ConversationType;
|
||||||
};
|
};
|
||||||
|
keyChangeOk: (_: KeyChangeOkType) => void;
|
||||||
renderDeviceSelection: () => JSX.Element;
|
renderDeviceSelection: () => 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;
|
||||||
declineCall: (_: DeclineCallType) => void;
|
declineCall: (_: DeclineCallType) => void;
|
||||||
i18n: LocalizerType;
|
i18n: LocalizerType;
|
||||||
me: {
|
me: MeType;
|
||||||
avatarPath?: string;
|
|
||||||
color?: ColorType;
|
|
||||||
name?: string;
|
|
||||||
phoneNumber?: string;
|
|
||||||
profileName?: string;
|
|
||||||
title: string;
|
|
||||||
uuid: string;
|
|
||||||
};
|
|
||||||
setGroupCallVideoRequest: (_: SetGroupCallVideoRequestType) => void;
|
setGroupCallVideoRequest: (_: SetGroupCallVideoRequestType) => void;
|
||||||
setLocalAudio: (_: SetLocalAudioType) => void;
|
setLocalAudio: (_: SetLocalAudioType) => void;
|
||||||
setLocalVideo: (_: SetLocalVideoType) => void;
|
setLocalVideo: (_: SetLocalVideoType) => void;
|
||||||
|
@ -84,9 +86,11 @@ const ActiveCallManager: React.FC<ActiveCallManagerPropsType> = ({
|
||||||
closeNeedPermissionScreen,
|
closeNeedPermissionScreen,
|
||||||
hangUp,
|
hangUp,
|
||||||
i18n,
|
i18n,
|
||||||
|
keyChangeOk,
|
||||||
getGroupCallVideoFrameSource,
|
getGroupCallVideoFrameSource,
|
||||||
me,
|
me,
|
||||||
renderDeviceSelection,
|
renderDeviceSelection,
|
||||||
|
renderSafetyNumberViewer,
|
||||||
setGroupCallVideoRequest,
|
setGroupCallVideoRequest,
|
||||||
setLocalAudio,
|
setLocalAudio,
|
||||||
setLocalPreview,
|
setLocalPreview,
|
||||||
|
@ -203,6 +207,7 @@ const ActiveCallManager: React.FC<ActiveCallManagerPropsType> = ({
|
||||||
<CallingParticipantsList
|
<CallingParticipantsList
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
onClose={toggleParticipants}
|
onClose={toggleParticipants}
|
||||||
|
ourUuid={me.uuid}
|
||||||
participants={peekedParticipants}
|
participants={peekedParticipants}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
@ -233,13 +238,11 @@ const ActiveCallManager: React.FC<ActiveCallManagerPropsType> = ({
|
||||||
...participant,
|
...participant,
|
||||||
hasAudio: participant.hasRemoteAudio,
|
hasAudio: participant.hasRemoteAudio,
|
||||||
hasVideo: participant.hasRemoteVideo,
|
hasVideo: participant.hasRemoteVideo,
|
||||||
isSelf: false,
|
|
||||||
})),
|
})),
|
||||||
{
|
{
|
||||||
...me,
|
...me,
|
||||||
hasAudio: hasLocalAudio,
|
hasAudio: hasLocalAudio,
|
||||||
hasVideo: hasLocalVideo,
|
hasVideo: hasLocalVideo,
|
||||||
isSelf: true,
|
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
: [];
|
: [];
|
||||||
|
@ -268,9 +271,25 @@ const ActiveCallManager: React.FC<ActiveCallManagerPropsType> = ({
|
||||||
<CallingParticipantsList
|
<CallingParticipantsList
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
onClose={toggleParticipants}
|
onClose={toggleParticipants}
|
||||||
|
ourUuid={me.uuid}
|
||||||
participants={groupCallParticipantsForParticipantsList}
|
participants={groupCallParticipantsForParticipantsList}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
{activeCall.callMode === CallMode.Group &&
|
||||||
|
activeCall.conversationsWithSafetyNumberChanges.length ? (
|
||||||
|
<SafetyNumberChangeDialog
|
||||||
|
confirmText={i18n('continueCall')}
|
||||||
|
contacts={activeCall.conversationsWithSafetyNumberChanges}
|
||||||
|
i18n={i18n}
|
||||||
|
onCancel={() => {
|
||||||
|
hangUp({ conversationId: activeCall.conversation.id });
|
||||||
|
}}
|
||||||
|
onConfirm={() => {
|
||||||
|
keyChangeOk({ conversationId: activeCall.conversation.id });
|
||||||
|
}}
|
||||||
|
renderSafetyNumber={renderSafetyNumberViewer}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,13 +12,14 @@ import {
|
||||||
CallState,
|
CallState,
|
||||||
GroupCallConnectionState,
|
GroupCallConnectionState,
|
||||||
GroupCallJoinState,
|
GroupCallJoinState,
|
||||||
GroupCallPeekedParticipantType,
|
|
||||||
GroupCallRemoteParticipantType,
|
GroupCallRemoteParticipantType,
|
||||||
} from '../types/Calling';
|
} from '../types/Calling';
|
||||||
|
import { ConversationType } from '../state/ducks/conversations';
|
||||||
import { Colors } from '../types/Colors';
|
import { Colors } from '../types/Colors';
|
||||||
import { CallScreen, PropsType } from './CallScreen';
|
import { CallScreen, PropsType } from './CallScreen';
|
||||||
import { setup as setupI18n } from '../../js/modules/i18n';
|
import { setup as setupI18n } from '../../js/modules/i18n';
|
||||||
import { missingCaseError } from '../util/missingCaseError';
|
import { missingCaseError } from '../util/missingCaseError';
|
||||||
|
import { getDefaultConversation } from '../util/getDefaultConversation';
|
||||||
import enMessages from '../../_locales/en/messages.json';
|
import enMessages from '../../_locales/en/messages.json';
|
||||||
|
|
||||||
const i18n = setupI18n('en', enMessages);
|
const i18n = setupI18n('en', enMessages);
|
||||||
|
@ -50,7 +51,7 @@ interface DirectCallOverrideProps extends OverridePropsBase {
|
||||||
interface GroupCallOverrideProps extends OverridePropsBase {
|
interface GroupCallOverrideProps extends OverridePropsBase {
|
||||||
callMode: CallMode.Group;
|
callMode: CallMode.Group;
|
||||||
connectionState?: GroupCallConnectionState;
|
connectionState?: GroupCallConnectionState;
|
||||||
peekedParticipants?: Array<GroupCallPeekedParticipantType>;
|
peekedParticipants?: Array<ConversationType>;
|
||||||
remoteParticipants?: Array<GroupCallRemoteParticipantType>;
|
remoteParticipants?: Array<GroupCallRemoteParticipantType>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,6 +84,7 @@ 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: [],
|
||||||
joinState: GroupCallJoinState.Joined,
|
joinState: GroupCallJoinState.Joined,
|
||||||
maxDevices: 5,
|
maxDevices: 5,
|
||||||
deviceCount: (overrideProps.remoteParticipants || []).length,
|
deviceCount: (overrideProps.remoteParticipants || []).length,
|
||||||
|
@ -240,14 +242,15 @@ story.add('Group call - 1', () => (
|
||||||
callMode: CallMode.Group,
|
callMode: CallMode.Group,
|
||||||
remoteParticipants: [
|
remoteParticipants: [
|
||||||
{
|
{
|
||||||
uuid: '72fa60e5-25fb-472d-8a56-e56867c57dda',
|
|
||||||
demuxId: 0,
|
demuxId: 0,
|
||||||
hasRemoteAudio: true,
|
hasRemoteAudio: true,
|
||||||
hasRemoteVideo: true,
|
hasRemoteVideo: true,
|
||||||
isBlocked: false,
|
|
||||||
isSelf: false,
|
|
||||||
title: 'Tyler',
|
|
||||||
videoAspectRatio: 1.3,
|
videoAspectRatio: 1.3,
|
||||||
|
...getDefaultConversation({
|
||||||
|
isBlocked: false,
|
||||||
|
uuid: '72fa60e5-25fb-472d-8a56-e56867c57dda',
|
||||||
|
title: 'Tyler',
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})}
|
})}
|
||||||
|
@ -260,34 +263,37 @@ story.add('Group call - Many', () => (
|
||||||
callMode: CallMode.Group,
|
callMode: CallMode.Group,
|
||||||
remoteParticipants: [
|
remoteParticipants: [
|
||||||
{
|
{
|
||||||
uuid: '094586f5-8fc2-4ce2-a152-2dfcc99f4630',
|
|
||||||
demuxId: 0,
|
demuxId: 0,
|
||||||
hasRemoteAudio: true,
|
hasRemoteAudio: true,
|
||||||
hasRemoteVideo: true,
|
hasRemoteVideo: true,
|
||||||
isBlocked: false,
|
|
||||||
isSelf: false,
|
|
||||||
title: 'Amy',
|
|
||||||
videoAspectRatio: 1.3,
|
videoAspectRatio: 1.3,
|
||||||
|
...getDefaultConversation({
|
||||||
|
isBlocked: false,
|
||||||
|
title: 'Amy',
|
||||||
|
uuid: '094586f5-8fc2-4ce2-a152-2dfcc99f4630',
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
uuid: 'cb5bdb24-4cbb-4650-8a7a-1a2807051e74',
|
|
||||||
demuxId: 1,
|
demuxId: 1,
|
||||||
hasRemoteAudio: true,
|
hasRemoteAudio: true,
|
||||||
hasRemoteVideo: true,
|
hasRemoteVideo: true,
|
||||||
isBlocked: false,
|
|
||||||
isSelf: true,
|
|
||||||
title: 'Bob',
|
|
||||||
videoAspectRatio: 1.3,
|
videoAspectRatio: 1.3,
|
||||||
|
...getDefaultConversation({
|
||||||
|
isBlocked: false,
|
||||||
|
title: 'Bob',
|
||||||
|
uuid: 'cb5bdb24-4cbb-4650-8a7a-1a2807051e74',
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
uuid: '2d7d13ae-53dc-4a51-8dc7-976cd85e0b57',
|
|
||||||
demuxId: 2,
|
demuxId: 2,
|
||||||
hasRemoteAudio: true,
|
hasRemoteAudio: true,
|
||||||
hasRemoteVideo: true,
|
hasRemoteVideo: true,
|
||||||
isBlocked: true,
|
|
||||||
isSelf: false,
|
|
||||||
title: 'Alice',
|
|
||||||
videoAspectRatio: 1.3,
|
videoAspectRatio: 1.3,
|
||||||
|
...getDefaultConversation({
|
||||||
|
isBlocked: true,
|
||||||
|
title: 'Alice',
|
||||||
|
uuid: '2d7d13ae-53dc-4a51-8dc7-976cd85e0b57',
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})}
|
})}
|
||||||
|
@ -301,14 +307,15 @@ story.add('Group call - reconnecting', () => (
|
||||||
connectionState: GroupCallConnectionState.Reconnecting,
|
connectionState: GroupCallConnectionState.Reconnecting,
|
||||||
remoteParticipants: [
|
remoteParticipants: [
|
||||||
{
|
{
|
||||||
uuid: '33871c64-0c22-45ce-8aa4-0ec237ac4a31',
|
|
||||||
demuxId: 0,
|
demuxId: 0,
|
||||||
hasRemoteAudio: true,
|
hasRemoteAudio: true,
|
||||||
hasRemoteVideo: true,
|
hasRemoteVideo: true,
|
||||||
isBlocked: false,
|
|
||||||
isSelf: false,
|
|
||||||
title: 'Tyler',
|
|
||||||
videoAspectRatio: 1.3,
|
videoAspectRatio: 1.3,
|
||||||
|
...getDefaultConversation({
|
||||||
|
isBlocked: false,
|
||||||
|
title: 'Tyler',
|
||||||
|
uuid: '33871c64-0c22-45ce-8aa4-0ec237ac4a31',
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})}
|
})}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import { ColorType } from '../types/Colors';
|
||||||
import { CallingLobby, PropsType } from './CallingLobby';
|
import { CallingLobby, PropsType } from './CallingLobby';
|
||||||
import { setup as setupI18n } from '../../js/modules/i18n';
|
import { setup as setupI18n } from '../../js/modules/i18n';
|
||||||
import enMessages from '../../_locales/en/messages.json';
|
import enMessages from '../../_locales/en/messages.json';
|
||||||
|
import { getDefaultConversation } from '../util/getDefaultConversation';
|
||||||
|
|
||||||
const i18n = setupI18n('en', enMessages);
|
const i18n = setupI18n('en', enMessages);
|
||||||
|
|
||||||
|
@ -33,7 +34,10 @@ const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
|
||||||
hasLocalVideo: boolean('hasLocalVideo', overrideProps.hasLocalVideo || false),
|
hasLocalVideo: boolean('hasLocalVideo', overrideProps.hasLocalVideo || false),
|
||||||
i18n,
|
i18n,
|
||||||
isGroupCall: boolean('isGroupCall', overrideProps.isGroupCall || false),
|
isGroupCall: boolean('isGroupCall', overrideProps.isGroupCall || false),
|
||||||
me: overrideProps.me || { color: 'ultramarine' as ColorType },
|
me: overrideProps.me || {
|
||||||
|
color: 'ultramarine' as ColorType,
|
||||||
|
uuid: generateUuid(),
|
||||||
|
},
|
||||||
onCallCanceled: action('on-call-canceled'),
|
onCallCanceled: action('on-call-canceled'),
|
||||||
onJoinCall: action('on-join-call'),
|
onJoinCall: action('on-join-call'),
|
||||||
peekedParticipants: overrideProps.peekedParticipants || [],
|
peekedParticipants: overrideProps.peekedParticipants || [],
|
||||||
|
@ -48,11 +52,11 @@ const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
|
||||||
toggleSettings: action('toggle-settings'),
|
toggleSettings: action('toggle-settings'),
|
||||||
});
|
});
|
||||||
|
|
||||||
const fakePeekedParticipant = (title: string) => ({
|
const fakePeekedParticipant = (title: string) =>
|
||||||
isSelf: false,
|
getDefaultConversation({
|
||||||
title,
|
title,
|
||||||
uuid: generateUuid(),
|
uuid: generateUuid(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const story = storiesOf('Components/CallingLobby', module);
|
const story = storiesOf('Components/CallingLobby', module);
|
||||||
|
|
||||||
|
@ -72,8 +76,9 @@ story.add('No Camera, local avatar', () => {
|
||||||
const props = createProps({
|
const props = createProps({
|
||||||
availableCameras: [],
|
availableCameras: [],
|
||||||
me: {
|
me: {
|
||||||
color: 'ultramarine' as ColorType,
|
|
||||||
avatarPath: '/fixtures/kitten-4-112-112.jpg',
|
avatarPath: '/fixtures/kitten-4-112-112.jpg',
|
||||||
|
color: 'ultramarine' as ColorType,
|
||||||
|
uuid: generateUuid(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return <CallingLobby {...props} />;
|
return <CallingLobby {...props} />;
|
||||||
|
|
|
@ -14,6 +14,7 @@ import { CallingHeader } from './CallingHeader';
|
||||||
import { Spinner } from './Spinner';
|
import { Spinner } from './Spinner';
|
||||||
import { ColorType } from '../types/Colors';
|
import { ColorType } from '../types/Colors';
|
||||||
import { LocalizerType } from '../types/Util';
|
import { LocalizerType } from '../types/Util';
|
||||||
|
import { ConversationType } from '../state/ducks/conversations';
|
||||||
|
|
||||||
export type PropsType = {
|
export type PropsType = {
|
||||||
availableCameras: Array<MediaDeviceInfo>;
|
availableCameras: Array<MediaDeviceInfo>;
|
||||||
|
@ -28,15 +29,11 @@ export type PropsType = {
|
||||||
me: {
|
me: {
|
||||||
avatarPath?: string;
|
avatarPath?: string;
|
||||||
color?: ColorType;
|
color?: ColorType;
|
||||||
|
uuid: string;
|
||||||
};
|
};
|
||||||
onCallCanceled: () => void;
|
onCallCanceled: () => void;
|
||||||
onJoinCall: () => void;
|
onJoinCall: () => void;
|
||||||
peekedParticipants: Array<{
|
peekedParticipants: Array<ConversationType>;
|
||||||
firstName?: string;
|
|
||||||
isSelf: boolean;
|
|
||||||
title: string;
|
|
||||||
uuid: string;
|
|
||||||
}>;
|
|
||||||
setLocalAudio: (_: SetLocalAudioType) => void;
|
setLocalAudio: (_: SetLocalAudioType) => void;
|
||||||
setLocalVideo: (_: SetLocalVideoType) => void;
|
setLocalVideo: (_: SetLocalVideoType) => void;
|
||||||
setLocalPreview: (_: SetLocalPreviewType) => void;
|
setLocalPreview: (_: SetLocalPreviewType) => void;
|
||||||
|
@ -124,7 +121,7 @@ export const CallingLobby = ({
|
||||||
// device.
|
// device.
|
||||||
// TODO: Improve the "it's you" case; see DESKTOP-926.
|
// TODO: Improve the "it's you" case; see DESKTOP-926.
|
||||||
const participantNames = peekedParticipants.map(participant =>
|
const participantNames = peekedParticipants.map(participant =>
|
||||||
participant.isSelf
|
participant.uuid === me.uuid
|
||||||
? i18n('you')
|
? i18n('you')
|
||||||
: participant.firstName || participant.title
|
: participant.firstName || participant.title
|
||||||
);
|
);
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { v4 as generateUuid } from 'uuid';
|
||||||
import { CallingParticipantsList, PropsType } from './CallingParticipantsList';
|
import { CallingParticipantsList, PropsType } from './CallingParticipantsList';
|
||||||
import { Colors } from '../types/Colors';
|
import { Colors } from '../types/Colors';
|
||||||
import { GroupCallRemoteParticipantType } from '../types/Calling';
|
import { GroupCallRemoteParticipantType } from '../types/Calling';
|
||||||
|
import { getDefaultConversation } from '../util/getDefaultConversation';
|
||||||
import { setup as setupI18n } from '../../js/modules/i18n';
|
import { setup as setupI18n } from '../../js/modules/i18n';
|
||||||
import enMessages from '../../_locales/en/messages.json';
|
import enMessages from '../../_locales/en/messages.json';
|
||||||
|
|
||||||
|
@ -19,24 +20,26 @@ function createParticipant(
|
||||||
): GroupCallRemoteParticipantType {
|
): GroupCallRemoteParticipantType {
|
||||||
const randomColor = Math.floor(Math.random() * Colors.length - 1);
|
const randomColor = Math.floor(Math.random() * Colors.length - 1);
|
||||||
return {
|
return {
|
||||||
avatarPath: participantProps.avatarPath,
|
|
||||||
color: Colors[randomColor],
|
|
||||||
demuxId: 2,
|
demuxId: 2,
|
||||||
hasRemoteAudio: Boolean(participantProps.hasRemoteAudio),
|
hasRemoteAudio: Boolean(participantProps.hasRemoteAudio),
|
||||||
hasRemoteVideo: Boolean(participantProps.hasRemoteVideo),
|
hasRemoteVideo: Boolean(participantProps.hasRemoteVideo),
|
||||||
isBlocked: Boolean(participantProps.isBlocked),
|
|
||||||
isSelf: Boolean(participantProps.isSelf),
|
|
||||||
name: participantProps.name,
|
|
||||||
profileName: participantProps.title,
|
|
||||||
title: String(participantProps.title),
|
|
||||||
videoAspectRatio: 1.3,
|
videoAspectRatio: 1.3,
|
||||||
uuid: generateUuid(),
|
...getDefaultConversation({
|
||||||
|
avatarPath: participantProps.avatarPath,
|
||||||
|
color: Colors[randomColor],
|
||||||
|
isBlocked: Boolean(participantProps.isBlocked),
|
||||||
|
name: participantProps.name,
|
||||||
|
profileName: participantProps.title,
|
||||||
|
title: String(participantProps.title),
|
||||||
|
uuid: generateUuid(),
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
|
const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
|
||||||
i18n,
|
i18n,
|
||||||
onClose: action('on-close'),
|
onClose: action('on-close'),
|
||||||
|
ourUuid: 'cf085e6a-e70b-41ec-a310-c198248af13f',
|
||||||
participants: overrideProps.participants || [],
|
participants: overrideProps.participants || [],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -62,7 +65,6 @@ story.add('Many Participants', () => {
|
||||||
const props = createProps({
|
const props = createProps({
|
||||||
participants: [
|
participants: [
|
||||||
createParticipant({
|
createParticipant({
|
||||||
isSelf: true,
|
|
||||||
title: 'Son Goku',
|
title: 'Son Goku',
|
||||||
}),
|
}),
|
||||||
createParticipant({
|
createParticipant({
|
||||||
|
|
|
@ -9,10 +9,10 @@ import { Avatar } from './Avatar';
|
||||||
import { ContactName } from './conversation/ContactName';
|
import { ContactName } from './conversation/ContactName';
|
||||||
import { InContactsIcon } from './InContactsIcon';
|
import { InContactsIcon } from './InContactsIcon';
|
||||||
import { LocalizerType } from '../types/Util';
|
import { LocalizerType } from '../types/Util';
|
||||||
import { GroupCallPeekedParticipantType } from '../types/Calling';
|
|
||||||
import { sortByTitle } from '../util/sortByTitle';
|
import { sortByTitle } from '../util/sortByTitle';
|
||||||
|
import { ConversationType } from '../state/ducks/conversations';
|
||||||
|
|
||||||
interface ParticipantType extends GroupCallPeekedParticipantType {
|
interface ParticipantType extends ConversationType {
|
||||||
hasAudio?: boolean;
|
hasAudio?: boolean;
|
||||||
hasVideo?: boolean;
|
hasVideo?: boolean;
|
||||||
}
|
}
|
||||||
|
@ -20,11 +20,12 @@ interface ParticipantType extends GroupCallPeekedParticipantType {
|
||||||
export type PropsType = {
|
export type PropsType = {
|
||||||
readonly i18n: LocalizerType;
|
readonly i18n: LocalizerType;
|
||||||
readonly onClose: () => void;
|
readonly onClose: () => void;
|
||||||
|
readonly ourUuid: string;
|
||||||
readonly participants: Array<ParticipantType>;
|
readonly participants: Array<ParticipantType>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CallingParticipantsList = React.memo(
|
export const CallingParticipantsList = React.memo(
|
||||||
({ i18n, onClose, participants }: PropsType) => {
|
({ i18n, onClose, ourUuid, participants }: PropsType) => {
|
||||||
const [root, setRoot] = React.useState<HTMLElement | null>(null);
|
const [root, setRoot] = React.useState<HTMLElement | null>(null);
|
||||||
|
|
||||||
const sortedParticipants = React.useMemo<Array<ParticipantType>>(
|
const sortedParticipants = React.useMemo<Array<ParticipantType>>(
|
||||||
|
@ -100,7 +101,7 @@ export const CallingParticipantsList = React.memo(
|
||||||
title={participant.title}
|
title={participant.title}
|
||||||
size={32}
|
size={32}
|
||||||
/>
|
/>
|
||||||
{participant.isSelf ? (
|
{participant.uuid === ourUuid ? (
|
||||||
<span className="module-calling-participants-list__name">
|
<span className="module-calling-participants-list__name">
|
||||||
{i18n('you')}
|
{i18n('you')}
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -105,6 +105,7 @@ story.add('Group Call', () => {
|
||||||
...getCommonActiveCallData(),
|
...getCommonActiveCallData(),
|
||||||
callMode: CallMode.Group as CallMode.Group,
|
callMode: CallMode.Group as CallMode.Group,
|
||||||
connectionState: GroupCallConnectionState.Connected,
|
connectionState: GroupCallConnectionState.Connected,
|
||||||
|
conversationsWithSafetyNumberChanges: [],
|
||||||
joinState: GroupCallJoinState.Joined,
|
joinState: GroupCallJoinState.Joined,
|
||||||
maxDevices: 5,
|
maxDevices: 5,
|
||||||
deviceCount: 0,
|
deviceCount: 0,
|
||||||
|
|
|
@ -9,6 +9,7 @@ import {
|
||||||
GroupCallRemoteParticipant,
|
GroupCallRemoteParticipant,
|
||||||
PropsType,
|
PropsType,
|
||||||
} from './GroupCallRemoteParticipant';
|
} from './GroupCallRemoteParticipant';
|
||||||
|
import { getDefaultConversation } from '../util/getDefaultConversation';
|
||||||
import { setup as setupI18n } from '../../js/modules/i18n';
|
import { setup as setupI18n } from '../../js/modules/i18n';
|
||||||
import enMessages from '../../_locales/en/messages.json';
|
import enMessages from '../../_locales/en/messages.json';
|
||||||
|
|
||||||
|
@ -37,12 +38,13 @@ const createProps = (
|
||||||
demuxId: 123,
|
demuxId: 123,
|
||||||
hasRemoteAudio: false,
|
hasRemoteAudio: false,
|
||||||
hasRemoteVideo: true,
|
hasRemoteVideo: true,
|
||||||
isBlocked: Boolean(isBlocked),
|
|
||||||
isSelf: false,
|
|
||||||
title:
|
|
||||||
'Pablo Diego José Francisco de Paula Juan Nepomuceno María de los Remedios Cipriano de la Santísima Trinidad Ruiz y Picasso',
|
|
||||||
videoAspectRatio: 1.3,
|
videoAspectRatio: 1.3,
|
||||||
uuid: '992ed3b9-fc9b-47a9-bdb4-e0c7cbb0fda5',
|
...getDefaultConversation({
|
||||||
|
isBlocked: Boolean(isBlocked),
|
||||||
|
title:
|
||||||
|
'Pablo Diego José Francisco de Paula Juan Nepomuceno María de los Remedios Cipriano de la Santísima Trinidad Ruiz y Picasso',
|
||||||
|
uuid: '992ed3b9-fc9b-47a9-bdb4-e0c7cbb0fda5',
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
...overrideProps,
|
...overrideProps,
|
||||||
});
|
});
|
||||||
|
|
|
@ -10,7 +10,7 @@ import { InContactsIcon } from './InContactsIcon';
|
||||||
import { ConversationType } from '../state/ducks/conversations';
|
import { ConversationType } from '../state/ducks/conversations';
|
||||||
import { LocalizerType } from '../types/Util';
|
import { LocalizerType } from '../types/Util';
|
||||||
|
|
||||||
type SafetyNumberProps = {
|
export type SafetyNumberProps = {
|
||||||
contactID: string;
|
contactID: string;
|
||||||
onClose?: () => void;
|
onClose?: () => void;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1194,6 +1194,7 @@ export class ConversationModel extends window.Backbone.Model<
|
||||||
isGroupV1AndDisabled: this.isGroupV1AndDisabled(),
|
isGroupV1AndDisabled: this.isGroupV1AndDisabled(),
|
||||||
isPinned: this.get('isPinned'),
|
isPinned: this.get('isPinned'),
|
||||||
isMissingMandatoryProfileSharing: this.isMissingRequiredProfileSharing(),
|
isMissingMandatoryProfileSharing: this.isMissingRequiredProfileSharing(),
|
||||||
|
isUntrusted: this.isUntrusted(),
|
||||||
isVerified: this.isVerified(),
|
isVerified: this.isVerified(),
|
||||||
lastMessage: {
|
lastMessage: {
|
||||||
status: this.get('lastMessageStatus')!,
|
status: this.get('lastMessageStatus')!,
|
||||||
|
@ -1809,64 +1810,50 @@ export class ConversationModel extends window.Backbone.Model<
|
||||||
return window.textsecure.storage.protocol.setApproval(this.id, true);
|
return window.textsecure.storage.protocol.setApproval(this.id, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
async safeIsUntrusted(): Promise<boolean> {
|
safeIsUntrusted(): boolean {
|
||||||
return window.textsecure.storage.protocol
|
try {
|
||||||
.isUntrusted(this.id)
|
return window.textsecure.storage.protocol.isUntrusted(this.id);
|
||||||
.catch(() => false);
|
} catch (err) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async isUntrusted(): Promise<boolean> {
|
isUntrusted(): boolean {
|
||||||
if (this.isPrivate()) {
|
if (this.isPrivate()) {
|
||||||
return this.safeIsUntrusted();
|
return this.safeIsUntrusted();
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
if (!this.contactCollection!.length) {
|
if (!this.contactCollection!.length) {
|
||||||
return Promise.resolve(false);
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.all(
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
return this.contactCollection!.any(contact => {
|
||||||
this.contactCollection!.map(contact => {
|
if (contact.isMe()) {
|
||||||
if (contact.isMe()) {
|
return false;
|
||||||
return false;
|
}
|
||||||
}
|
return contact.safeIsUntrusted();
|
||||||
return contact.safeIsUntrusted();
|
});
|
||||||
})
|
|
||||||
).then(results => window._.any(results, result => result));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getUntrusted(): Promise<Backbone.Collection> {
|
getUntrusted(): Backbone.Collection {
|
||||||
// This is a bit ugly because isUntrusted() is async. Could do the work to cache
|
|
||||||
// it locally, but we really only need it for this call.
|
|
||||||
if (this.isPrivate()) {
|
if (this.isPrivate()) {
|
||||||
return this.isUntrusted().then(untrusted => {
|
if (this.isUntrusted()) {
|
||||||
if (untrusted) {
|
return new window.Backbone.Collection([this]);
|
||||||
return new window.Backbone.Collection([this]);
|
}
|
||||||
}
|
return new window.Backbone.Collection();
|
||||||
|
|
||||||
return new window.Backbone.Collection();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
return Promise.all(
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
const results = this.contactCollection!.map(contact => {
|
||||||
this.contactCollection!.map(contact => {
|
if (contact.isMe()) {
|
||||||
if (contact.isMe()) {
|
return [false, contact];
|
||||||
return [false, contact];
|
}
|
||||||
}
|
return [contact.isUntrusted(), contact];
|
||||||
return Promise.all([contact.isUntrusted(), contact]);
|
|
||||||
})
|
|
||||||
).then(results => {
|
|
||||||
const filtered = window._.filter(results, result => {
|
|
||||||
const untrusted = result[0];
|
|
||||||
return untrusted;
|
|
||||||
});
|
|
||||||
return new window.Backbone.Collection(
|
|
||||||
window._.map(filtered, result => {
|
|
||||||
const contact = result[1];
|
|
||||||
return contact;
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return new window.Backbone.Collection(
|
||||||
|
results.filter(result => result[0]).map(result => result[1])
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getSentMessageCount(): number {
|
getSentMessageCount(): number {
|
||||||
|
@ -1983,7 +1970,15 @@ export class ConversationModel extends window.Backbone.Model<
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const isUntrusted = await this.isUntrusted();
|
||||||
|
|
||||||
this.trigger('newmessage', model);
|
this.trigger('newmessage', model);
|
||||||
|
|
||||||
|
const uuid = this.get('uuid');
|
||||||
|
// Group calls are always with folks that have a UUID
|
||||||
|
if (isUntrusted && uuid) {
|
||||||
|
window.reduxActions.calling.keyChanged({ uuid });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async addVerifiedChange(
|
async addVerifiedChange(
|
||||||
|
|
|
@ -667,6 +667,14 @@ export class CallingClass {
|
||||||
return groupCall.getVideoSource(demuxId);
|
return groupCall.getVideoSource(demuxId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public resendGroupCallMediaKeys(conversationId: string): void {
|
||||||
|
const groupCall = this.getGroupCall(conversationId);
|
||||||
|
if (!groupCall) {
|
||||||
|
throw new Error('Could not find matching call');
|
||||||
|
}
|
||||||
|
groupCall.resendMediaKeys();
|
||||||
|
}
|
||||||
|
|
||||||
private syncGroupCallToRedux(
|
private syncGroupCallToRedux(
|
||||||
conversationId: string,
|
conversationId: string,
|
||||||
groupCall: GroupCall
|
groupCall: GroupCall
|
||||||
|
|
|
@ -71,9 +71,10 @@ export interface ActiveCallStateType {
|
||||||
joinedAt?: number;
|
joinedAt?: number;
|
||||||
hasLocalAudio: boolean;
|
hasLocalAudio: boolean;
|
||||||
hasLocalVideo: boolean;
|
hasLocalVideo: boolean;
|
||||||
showParticipantsList: boolean;
|
|
||||||
pip: boolean;
|
pip: boolean;
|
||||||
settingsDialogOpen: boolean;
|
settingsDialogOpen: boolean;
|
||||||
|
safetyNumberChangedUuids: Array<string>;
|
||||||
|
showParticipantsList: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CallsByConversationType {
|
export interface CallsByConversationType {
|
||||||
|
@ -126,6 +127,14 @@ export type HangUpType = {
|
||||||
conversationId: string;
|
conversationId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type KeyChangedType = {
|
||||||
|
uuid: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type KeyChangeOkType = {
|
||||||
|
conversationId: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type IncomingCallType = {
|
export type IncomingCallType = {
|
||||||
conversationId: string;
|
conversationId: string;
|
||||||
isVideoCall: boolean;
|
isVideoCall: boolean;
|
||||||
|
@ -220,6 +229,8 @@ const DECLINE_CALL = 'calling/DECLINE_CALL';
|
||||||
const GROUP_CALL_STATE_CHANGE = 'calling/GROUP_CALL_STATE_CHANGE';
|
const GROUP_CALL_STATE_CHANGE = 'calling/GROUP_CALL_STATE_CHANGE';
|
||||||
const HANG_UP = 'calling/HANG_UP';
|
const HANG_UP = 'calling/HANG_UP';
|
||||||
const INCOMING_CALL = 'calling/INCOMING_CALL';
|
const INCOMING_CALL = 'calling/INCOMING_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_NOT_CONNECTED_GROUP_CALL_FULFILLED =
|
const PEEK_NOT_CONNECTED_GROUP_CALL_FULFILLED =
|
||||||
'calling/PEEK_NOT_CONNECTED_GROUP_CALL_FULFILLED';
|
'calling/PEEK_NOT_CONNECTED_GROUP_CALL_FULFILLED';
|
||||||
|
@ -282,6 +293,18 @@ type IncomingCallActionType = {
|
||||||
payload: IncomingCallType;
|
payload: IncomingCallType;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type KeyChangedActionType = {
|
||||||
|
type: 'calling/MARK_CALL_UNTRUSTED';
|
||||||
|
payload: {
|
||||||
|
safetyNumberChangedUuids: Array<string>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
type KeyChangeOkActionType = {
|
||||||
|
type: 'calling/MARK_CALL_TRUSTED';
|
||||||
|
payload: null;
|
||||||
|
};
|
||||||
|
|
||||||
type OutgoingCallActionType = {
|
type OutgoingCallActionType = {
|
||||||
type: 'calling/OUTGOING_CALL';
|
type: 'calling/OUTGOING_CALL';
|
||||||
payload: StartDirectCallType;
|
payload: StartDirectCallType;
|
||||||
|
@ -353,6 +376,8 @@ export type CallingActionType =
|
||||||
| GroupCallStateChangeActionType
|
| GroupCallStateChangeActionType
|
||||||
| HangUpActionType
|
| HangUpActionType
|
||||||
| IncomingCallActionType
|
| IncomingCallActionType
|
||||||
|
| KeyChangedActionType
|
||||||
|
| KeyChangeOkActionType
|
||||||
| OutgoingCallActionType
|
| OutgoingCallActionType
|
||||||
| PeekNotConnectedGroupCallFulfilledActionType
|
| PeekNotConnectedGroupCallFulfilledActionType
|
||||||
| RefreshIODevicesActionType
|
| RefreshIODevicesActionType
|
||||||
|
@ -509,6 +534,56 @@ function hangUp(payload: HangUpType): HangUpActionType {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 (activeCall.callMode === CallMode.Group) {
|
||||||
|
const uuidsChanged = new Set(activeCallState.safetyNumberChangedUuids);
|
||||||
|
|
||||||
|
// Iterate over each participant to ensure that the uuid passed in
|
||||||
|
// matches one of the participants in the group call.
|
||||||
|
activeCall.remoteParticipants.forEach(participant => {
|
||||||
|
if (participant.uuid === payload.uuid) {
|
||||||
|
uuidsChanged.add(participant.uuid);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const safetyNumberChangedUuids = Array.from(uuidsChanged);
|
||||||
|
|
||||||
|
if (safetyNumberChangedUuids.length) {
|
||||||
|
dispatch({
|
||||||
|
type: MARK_CALL_UNTRUSTED,
|
||||||
|
payload: {
|
||||||
|
safetyNumberChangedUuids,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function keyChangeOk(
|
||||||
|
payload: KeyChangeOkType
|
||||||
|
): ThunkAction<void, RootStateType, unknown, KeyChangeOkActionType> {
|
||||||
|
return dispatch => {
|
||||||
|
calling.resendGroupCallMediaKeys(payload.conversationId);
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: MARK_CALL_TRUSTED,
|
||||||
|
payload: null,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function receiveIncomingCall(
|
function receiveIncomingCall(
|
||||||
payload: IncomingCallType
|
payload: IncomingCallType
|
||||||
): IncomingCallActionType {
|
): IncomingCallActionType {
|
||||||
|
@ -789,6 +864,8 @@ export const actions = {
|
||||||
declineCall,
|
declineCall,
|
||||||
groupCallStateChange,
|
groupCallStateChange,
|
||||||
hangUp,
|
hangUp,
|
||||||
|
keyChanged,
|
||||||
|
keyChangeOk,
|
||||||
receiveIncomingCall,
|
receiveIncomingCall,
|
||||||
outgoingCall,
|
outgoingCall,
|
||||||
peekNotConnectedGroupCall,
|
peekNotConnectedGroupCall,
|
||||||
|
@ -896,9 +973,10 @@ export function reducer(
|
||||||
conversationId: action.payload.conversationId,
|
conversationId: action.payload.conversationId,
|
||||||
hasLocalAudio: action.payload.hasLocalAudio,
|
hasLocalAudio: action.payload.hasLocalAudio,
|
||||||
hasLocalVideo: action.payload.hasLocalVideo,
|
hasLocalVideo: action.payload.hasLocalVideo,
|
||||||
showParticipantsList: false,
|
|
||||||
pip: false,
|
pip: false,
|
||||||
|
safetyNumberChangedUuids: [],
|
||||||
settingsDialogOpen: false,
|
settingsDialogOpen: false,
|
||||||
|
showParticipantsList: false,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -920,9 +998,10 @@ export function reducer(
|
||||||
conversationId: action.payload.conversationId,
|
conversationId: action.payload.conversationId,
|
||||||
hasLocalAudio: action.payload.hasLocalAudio,
|
hasLocalAudio: action.payload.hasLocalAudio,
|
||||||
hasLocalVideo: action.payload.hasLocalVideo,
|
hasLocalVideo: action.payload.hasLocalVideo,
|
||||||
showParticipantsList: false,
|
|
||||||
pip: false,
|
pip: false,
|
||||||
|
safetyNumberChangedUuids: [],
|
||||||
settingsDialogOpen: false,
|
settingsDialogOpen: false,
|
||||||
|
showParticipantsList: false,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -939,9 +1018,10 @@ export function reducer(
|
||||||
conversationId: action.payload.conversationId,
|
conversationId: action.payload.conversationId,
|
||||||
hasLocalAudio: true,
|
hasLocalAudio: true,
|
||||||
hasLocalVideo: action.payload.asVideoCall,
|
hasLocalVideo: action.payload.asVideoCall,
|
||||||
showParticipantsList: false,
|
|
||||||
pip: false,
|
pip: false,
|
||||||
|
safetyNumberChangedUuids: [],
|
||||||
settingsDialogOpen: false,
|
settingsDialogOpen: false,
|
||||||
|
showParticipantsList: false,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1003,9 +1083,10 @@ export function reducer(
|
||||||
conversationId: action.payload.conversationId,
|
conversationId: action.payload.conversationId,
|
||||||
hasLocalAudio: action.payload.hasLocalAudio,
|
hasLocalAudio: action.payload.hasLocalAudio,
|
||||||
hasLocalVideo: action.payload.hasLocalVideo,
|
hasLocalVideo: action.payload.hasLocalVideo,
|
||||||
showParticipantsList: false,
|
|
||||||
pip: false,
|
pip: false,
|
||||||
|
safetyNumberChangedUuids: [],
|
||||||
settingsDialogOpen: false,
|
settingsDialogOpen: false,
|
||||||
|
showParticipantsList: false,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1327,5 +1408,46 @@ export function reducer(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (action.type === MARK_CALL_UNTRUSTED) {
|
||||||
|
const { activeCallState } = state;
|
||||||
|
if (!activeCallState) {
|
||||||
|
window.log.warn(
|
||||||
|
'Cannot mark call as untrusted when there is no active call'
|
||||||
|
);
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { safetyNumberChangedUuids } = action.payload;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
activeCallState: {
|
||||||
|
...activeCallState,
|
||||||
|
pip: false,
|
||||||
|
safetyNumberChangedUuids,
|
||||||
|
settingsDialogOpen: false,
|
||||||
|
showParticipantsList: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action.type === MARK_CALL_TRUSTED) {
|
||||||
|
const { activeCallState } = state;
|
||||||
|
if (!activeCallState) {
|
||||||
|
window.log.warn(
|
||||||
|
'Cannot mark call as trusted when there is no active call'
|
||||||
|
);
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
activeCallState: {
|
||||||
|
...activeCallState,
|
||||||
|
safetyNumberChangedUuids: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,6 +58,7 @@ export type ConversationType = {
|
||||||
isBlocked?: boolean;
|
isBlocked?: boolean;
|
||||||
isGroupV1AndDisabled?: boolean;
|
isGroupV1AndDisabled?: boolean;
|
||||||
isPinned?: boolean;
|
isPinned?: boolean;
|
||||||
|
isUntrusted?: boolean;
|
||||||
isVerified?: boolean;
|
isVerified?: boolean;
|
||||||
activeAt?: number;
|
activeAt?: number;
|
||||||
timestamp?: number;
|
timestamp?: number;
|
||||||
|
|
|
@ -15,17 +15,24 @@ import { getIncomingCall } from '../selectors/calling';
|
||||||
import {
|
import {
|
||||||
ActiveCallType,
|
ActiveCallType,
|
||||||
CallMode,
|
CallMode,
|
||||||
GroupCallPeekedParticipantType,
|
|
||||||
GroupCallRemoteParticipantType,
|
GroupCallRemoteParticipantType,
|
||||||
} from '../../types/Calling';
|
} from '../../types/Calling';
|
||||||
import { StateType } from '../reducer';
|
import { StateType } from '../reducer';
|
||||||
import { missingCaseError } from '../../util/missingCaseError';
|
import { missingCaseError } from '../../util/missingCaseError';
|
||||||
import { SmartCallingDeviceSelection } from './CallingDeviceSelection';
|
import { SmartCallingDeviceSelection } from './CallingDeviceSelection';
|
||||||
|
import {
|
||||||
|
SmartSafetyNumberViewer,
|
||||||
|
Props as SafetyNumberViewerProps,
|
||||||
|
} from './SafetyNumberViewer';
|
||||||
|
|
||||||
function renderDeviceSelection(): JSX.Element {
|
function renderDeviceSelection(): JSX.Element {
|
||||||
return <SmartCallingDeviceSelection />;
|
return <SmartCallingDeviceSelection />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderSafetyNumberViewer(props: SafetyNumberViewerProps): JSX.Element {
|
||||||
|
return <SmartSafetyNumberViewer {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
const getGroupCallVideoFrameSource = callingService.getGroupCallVideoFrameSource.bind(
|
const getGroupCallVideoFrameSource = callingService.getGroupCallVideoFrameSource.bind(
|
||||||
callingService
|
callingService
|
||||||
);
|
);
|
||||||
|
@ -89,10 +96,9 @@ const mapStateToActiveCallProp = (
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
case CallMode.Group: {
|
case CallMode.Group: {
|
||||||
const ourUuid = getUserUuid(state);
|
const conversationsWithSafetyNumberChanges: Array<ConversationType> = [];
|
||||||
|
|
||||||
const remoteParticipants: Array<GroupCallRemoteParticipantType> = [];
|
const remoteParticipants: Array<GroupCallRemoteParticipantType> = [];
|
||||||
const peekedParticipants: Array<GroupCallPeekedParticipantType> = [];
|
const peekedParticipants: Array<ConversationType> = [];
|
||||||
|
|
||||||
for (let i = 0; i < call.remoteParticipants.length; i += 1) {
|
for (let i = 0; i < call.remoteParticipants.length; i += 1) {
|
||||||
const remoteParticipant = call.remoteParticipants[i];
|
const remoteParticipant = call.remoteParticipants[i];
|
||||||
|
@ -108,23 +114,33 @@ const mapStateToActiveCallProp = (
|
||||||
}
|
}
|
||||||
|
|
||||||
remoteParticipants.push({
|
remoteParticipants.push({
|
||||||
avatarPath: remoteConversation.avatarPath,
|
...remoteConversation,
|
||||||
color: remoteConversation.color,
|
|
||||||
demuxId: remoteParticipant.demuxId,
|
demuxId: remoteParticipant.demuxId,
|
||||||
firstName: remoteConversation.firstName,
|
|
||||||
hasRemoteAudio: remoteParticipant.hasRemoteAudio,
|
hasRemoteAudio: remoteParticipant.hasRemoteAudio,
|
||||||
hasRemoteVideo: remoteParticipant.hasRemoteVideo,
|
hasRemoteVideo: remoteParticipant.hasRemoteVideo,
|
||||||
isBlocked: Boolean(remoteConversation.isBlocked),
|
|
||||||
isSelf: remoteParticipant.uuid === ourUuid,
|
|
||||||
name: remoteConversation.name,
|
|
||||||
profileName: remoteConversation.profileName,
|
|
||||||
speakerTime: remoteParticipant.speakerTime,
|
speakerTime: remoteParticipant.speakerTime,
|
||||||
title: remoteConversation.title,
|
|
||||||
uuid: remoteParticipant.uuid,
|
|
||||||
videoAspectRatio: remoteParticipant.videoAspectRatio,
|
videoAspectRatio: remoteParticipant.videoAspectRatio,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (
|
||||||
|
let i = 0;
|
||||||
|
i < activeCallState.safetyNumberChangedUuids.length;
|
||||||
|
i += 1
|
||||||
|
) {
|
||||||
|
const uuid = activeCallState.safetyNumberChangedUuids[i];
|
||||||
|
|
||||||
|
const remoteConversation = conversationSelectorByUuid(uuid);
|
||||||
|
if (!remoteConversation) {
|
||||||
|
window.log.error(
|
||||||
|
'Remote participant has no corresponding conversation'
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
conversationsWithSafetyNumberChanges.push(remoteConversation);
|
||||||
|
}
|
||||||
|
|
||||||
for (let i = 0; i < call.peekInfo.uuids.length; i += 1) {
|
for (let i = 0; i < call.peekInfo.uuids.length; i += 1) {
|
||||||
const peekedParticipantUuid = call.peekInfo.uuids[i];
|
const peekedParticipantUuid = call.peekInfo.uuids[i];
|
||||||
|
|
||||||
|
@ -138,22 +154,14 @@ const mapStateToActiveCallProp = (
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
peekedParticipants.push({
|
peekedParticipants.push(peekedConversation);
|
||||||
avatarPath: peekedConversation.avatarPath,
|
|
||||||
color: peekedConversation.color,
|
|
||||||
firstName: peekedConversation.firstName,
|
|
||||||
isSelf: peekedParticipantUuid === ourUuid,
|
|
||||||
name: peekedConversation.name,
|
|
||||||
profileName: peekedConversation.profileName,
|
|
||||||
title: peekedConversation.title,
|
|
||||||
uuid: peekedParticipantUuid,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...baseResult,
|
...baseResult,
|
||||||
callMode: CallMode.Group,
|
callMode: CallMode.Group,
|
||||||
connectionState: call.connectionState,
|
connectionState: call.connectionState,
|
||||||
|
conversationsWithSafetyNumberChanges,
|
||||||
deviceCount: call.peekInfo.deviceCount,
|
deviceCount: call.peekInfo.deviceCount,
|
||||||
joinState: call.joinState,
|
joinState: call.joinState,
|
||||||
maxDevices: call.peekInfo.maxDevices,
|
maxDevices: call.peekInfo.maxDevices,
|
||||||
|
@ -197,6 +205,7 @@ const mapStateToProps = (state: StateType) => ({
|
||||||
uuid: getUserUuid(state),
|
uuid: getUserUuid(state),
|
||||||
},
|
},
|
||||||
renderDeviceSelection,
|
renderDeviceSelection,
|
||||||
|
renderSafetyNumberViewer,
|
||||||
});
|
});
|
||||||
|
|
||||||
const smart = connect(mapStateToProps, mapDispatchToProps);
|
const smart = connect(mapStateToProps, mapDispatchToProps);
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { getContactSafetyNumber } from '../selectors/safetyNumber';
|
||||||
import { getConversationSelector } from '../selectors/conversations';
|
import { getConversationSelector } from '../selectors/conversations';
|
||||||
import { getIntl } from '../selectors/user';
|
import { getIntl } from '../selectors/user';
|
||||||
|
|
||||||
type Props = {
|
export type Props = {
|
||||||
contactID: string;
|
contactID: string;
|
||||||
onClose?: () => void;
|
onClose?: () => void;
|
||||||
};
|
};
|
||||||
|
|
|
@ -44,6 +44,7 @@ describe('calling duck', () => {
|
||||||
hasLocalAudio: true,
|
hasLocalAudio: true,
|
||||||
hasLocalVideo: false,
|
hasLocalVideo: false,
|
||||||
showParticipantsList: false,
|
showParticipantsList: false,
|
||||||
|
safetyNumberChangedUuids: [],
|
||||||
pip: false,
|
pip: false,
|
||||||
settingsDialogOpen: false,
|
settingsDialogOpen: false,
|
||||||
},
|
},
|
||||||
|
@ -98,6 +99,7 @@ describe('calling duck', () => {
|
||||||
hasLocalAudio: true,
|
hasLocalAudio: true,
|
||||||
hasLocalVideo: false,
|
hasLocalVideo: false,
|
||||||
showParticipantsList: false,
|
showParticipantsList: false,
|
||||||
|
safetyNumberChangedUuids: [],
|
||||||
pip: false,
|
pip: false,
|
||||||
settingsDialogOpen: false,
|
settingsDialogOpen: false,
|
||||||
},
|
},
|
||||||
|
@ -201,6 +203,7 @@ describe('calling duck', () => {
|
||||||
hasLocalAudio: true,
|
hasLocalAudio: true,
|
||||||
hasLocalVideo: true,
|
hasLocalVideo: true,
|
||||||
showParticipantsList: false,
|
showParticipantsList: false,
|
||||||
|
safetyNumberChangedUuids: [],
|
||||||
pip: false,
|
pip: false,
|
||||||
settingsDialogOpen: false,
|
settingsDialogOpen: false,
|
||||||
});
|
});
|
||||||
|
@ -576,6 +579,7 @@ describe('calling duck', () => {
|
||||||
hasLocalAudio: true,
|
hasLocalAudio: true,
|
||||||
hasLocalVideo: false,
|
hasLocalVideo: false,
|
||||||
showParticipantsList: false,
|
showParticipantsList: false,
|
||||||
|
safetyNumberChangedUuids: [],
|
||||||
pip: false,
|
pip: false,
|
||||||
settingsDialogOpen: false,
|
settingsDialogOpen: false,
|
||||||
});
|
});
|
||||||
|
@ -812,6 +816,7 @@ describe('calling duck', () => {
|
||||||
hasLocalAudio: true,
|
hasLocalAudio: true,
|
||||||
hasLocalVideo: true,
|
hasLocalVideo: true,
|
||||||
showParticipantsList: false,
|
showParticipantsList: false,
|
||||||
|
safetyNumberChangedUuids: [],
|
||||||
pip: false,
|
pip: false,
|
||||||
settingsDialogOpen: false,
|
settingsDialogOpen: false,
|
||||||
});
|
});
|
||||||
|
@ -1046,6 +1051,7 @@ describe('calling duck', () => {
|
||||||
hasLocalAudio: true,
|
hasLocalAudio: true,
|
||||||
hasLocalVideo: false,
|
hasLocalVideo: false,
|
||||||
showParticipantsList: false,
|
showParticipantsList: false,
|
||||||
|
safetyNumberChangedUuids: [],
|
||||||
pip: false,
|
pip: false,
|
||||||
settingsDialogOpen: false,
|
settingsDialogOpen: false,
|
||||||
});
|
});
|
||||||
|
|
|
@ -41,6 +41,7 @@ describe('state/selectors/calling', () => {
|
||||||
hasLocalAudio: true,
|
hasLocalAudio: true,
|
||||||
hasLocalVideo: false,
|
hasLocalVideo: false,
|
||||||
showParticipantsList: false,
|
showParticipantsList: false,
|
||||||
|
safetyNumberChangedUuids: [],
|
||||||
pip: false,
|
pip: false,
|
||||||
settingsDialogOpen: false,
|
settingsDialogOpen: false,
|
||||||
},
|
},
|
||||||
|
|
2
ts/textsecure.d.ts
vendored
2
ts/textsecure.d.ts
vendored
|
@ -123,7 +123,7 @@ export type StorageProtocolType = StorageType & {
|
||||||
clearSignedPreKeysStore: () => Promise<void>;
|
clearSignedPreKeysStore: () => Promise<void>;
|
||||||
clearSessionStore: () => Promise<void>;
|
clearSessionStore: () => Promise<void>;
|
||||||
isTrustedIdentity: () => void;
|
isTrustedIdentity: () => void;
|
||||||
isUntrusted: (id: string) => Promise<boolean>;
|
isUntrusted: (id: string) => boolean;
|
||||||
storePreKey: (keyId: number, keyPair: KeyPairType) => Promise<void>;
|
storePreKey: (keyId: number, keyPair: KeyPairType) => Promise<void>;
|
||||||
storeSignedPreKey: (
|
storeSignedPreKey: (
|
||||||
keyId: number,
|
keyId: number,
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
// Copyright 2020 Signal Messenger, LLC
|
// Copyright 2020 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import { ColorType } from './Colors';
|
|
||||||
import { ConversationType } from '../state/ducks/conversations';
|
import { ConversationType } from '../state/ducks/conversations';
|
||||||
|
|
||||||
// These are strings (1) for the database (2) for Storybook.
|
// These are strings (1) for the database (2) for Storybook.
|
||||||
|
@ -19,6 +18,7 @@ interface ActiveCallBaseType {
|
||||||
pip: boolean;
|
pip: boolean;
|
||||||
settingsDialogOpen: boolean;
|
settingsDialogOpen: boolean;
|
||||||
showParticipantsList: boolean;
|
showParticipantsList: boolean;
|
||||||
|
showSafetyNumberDialog?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ActiveDirectCallType extends ActiveCallBaseType {
|
interface ActiveDirectCallType extends ActiveCallBaseType {
|
||||||
|
@ -36,10 +36,11 @@ interface ActiveDirectCallType extends ActiveCallBaseType {
|
||||||
interface ActiveGroupCallType extends ActiveCallBaseType {
|
interface ActiveGroupCallType extends ActiveCallBaseType {
|
||||||
callMode: CallMode.Group;
|
callMode: CallMode.Group;
|
||||||
connectionState: GroupCallConnectionState;
|
connectionState: GroupCallConnectionState;
|
||||||
|
conversationsWithSafetyNumberChanges: Array<ConversationType>;
|
||||||
joinState: GroupCallJoinState;
|
joinState: GroupCallJoinState;
|
||||||
maxDevices: number;
|
maxDevices: number;
|
||||||
deviceCount: number;
|
deviceCount: number;
|
||||||
peekedParticipants: Array<GroupCallPeekedParticipantType>;
|
peekedParticipants: Array<ConversationType>;
|
||||||
remoteParticipants: Array<GroupCallRemoteParticipantType>;
|
remoteParticipants: Array<GroupCallRemoteParticipantType>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,23 +95,10 @@ export enum GroupCallJoinState {
|
||||||
Joined = 2,
|
Joined = 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GroupCallPeekedParticipantType {
|
export interface GroupCallRemoteParticipantType extends ConversationType {
|
||||||
avatarPath?: string;
|
|
||||||
color?: ColorType;
|
|
||||||
firstName?: string;
|
|
||||||
isSelf: boolean;
|
|
||||||
name?: string;
|
|
||||||
profileName?: string;
|
|
||||||
title: string;
|
|
||||||
uuid: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GroupCallRemoteParticipantType
|
|
||||||
extends GroupCallPeekedParticipantType {
|
|
||||||
demuxId: number;
|
demuxId: number;
|
||||||
hasRemoteAudio: boolean;
|
hasRemoteAudio: boolean;
|
||||||
hasRemoteVideo: boolean;
|
hasRemoteVideo: boolean;
|
||||||
isBlocked: boolean;
|
|
||||||
speakerTime?: number;
|
speakerTime?: number;
|
||||||
videoAspectRatio: number;
|
videoAspectRatio: number;
|
||||||
}
|
}
|
||||||
|
|
24
ts/util/getDefaultConversation.ts
Normal file
24
ts/util/getDefaultConversation.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
// Copyright 2020 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import { v4 as generateUuid } from 'uuid';
|
||||||
|
import { ConversationType } from '../state/ducks/conversations';
|
||||||
|
|
||||||
|
export function getDefaultConversation(
|
||||||
|
overrideProps: Partial<ConversationType>
|
||||||
|
): ConversationType {
|
||||||
|
if (window.STORYBOOK_ENV !== 'react') {
|
||||||
|
throw new Error('getDefaultConversation is for storybook only');
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: 'guid-1',
|
||||||
|
lastUpdated: Date.now(),
|
||||||
|
markedUnread: Boolean(overrideProps.markedUnread),
|
||||||
|
e164: '+1300555000',
|
||||||
|
title: 'Alice',
|
||||||
|
type: 'direct' as const,
|
||||||
|
uuid: generateUuid(),
|
||||||
|
...overrideProps,
|
||||||
|
};
|
||||||
|
}
|
|
@ -14400,7 +14400,7 @@
|
||||||
"rule": "React-useRef",
|
"rule": "React-useRef",
|
||||||
"path": "ts/components/CallingLobby.tsx",
|
"path": "ts/components/CallingLobby.tsx",
|
||||||
"line": " const localVideoRef = React.useRef(null);",
|
"line": " const localVideoRef = React.useRef(null);",
|
||||||
"lineNumber": 67,
|
"lineNumber": 64,
|
||||||
"reasonCategory": "usageTrusted",
|
"reasonCategory": "usageTrusted",
|
||||||
"updated": "2020-10-26T19:12:24.410Z",
|
"updated": "2020-10-26T19:12:24.410Z",
|
||||||
"reasonDetail": "Used to get the local video element for rendering."
|
"reasonDetail": "Used to get the local video element for rendering."
|
||||||
|
|
|
@ -2992,7 +2992,7 @@ Whisper.ConversationView = Whisper.View.extend({
|
||||||
return unverifiedContacts;
|
return unverifiedContacts;
|
||||||
}
|
}
|
||||||
|
|
||||||
const untrustedContacts = await this.model.getUntrusted();
|
const untrustedContacts = this.model.getUntrusted();
|
||||||
|
|
||||||
if (options.force) {
|
if (options.force) {
|
||||||
if (untrustedContacts.length) {
|
if (untrustedContacts.length) {
|
||||||
|
|
2
ts/window.d.ts
vendored
2
ts/window.d.ts
vendored
|
@ -479,6 +479,8 @@ declare global {
|
||||||
getServerTrustRoot: () => WhatIsThis;
|
getServerTrustRoot: () => WhatIsThis;
|
||||||
readyForUpdates: () => void;
|
readyForUpdates: () => void;
|
||||||
|
|
||||||
|
STORYBOOK_ENV?: string;
|
||||||
|
|
||||||
// Flags
|
// Flags
|
||||||
isGroupCallingEnabled: () => boolean;
|
isGroupCallingEnabled: () => boolean;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue