Group calling: add speaker view
This commit is contained in:
parent
fbfcdbf84e
commit
b281420a40
16 changed files with 174 additions and 19 deletions
|
@ -3147,6 +3147,14 @@
|
|||
"message": "Fullscreen call",
|
||||
"description": "Title for picture-in-picture toggle"
|
||||
},
|
||||
"calling__switch-view--to-grid": {
|
||||
"message": "Switch to grid view",
|
||||
"description": "Title for grid/speaker view toggle when on a call"
|
||||
},
|
||||
"calling__switch-view--to-speaker": {
|
||||
"message": "Switch to speaker view",
|
||||
"description": "Title for grid/speaker view toggle when on a call"
|
||||
},
|
||||
"calling__hangup": {
|
||||
"message": "Leave call",
|
||||
"description": "Title for hang up button"
|
||||
|
|
1
images/icons/v2/grid-view-solid-24.svg
Normal file
1
images/icons/v2/grid-view-solid-24.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="m21.5 8.5h-4a1 1 0 0 1 -1-1v-2a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2a1 1 0 0 1 -1 1zm-6.5-1v-2a1 1 0 0 0 -1-1h-4a1 1 0 0 0 -1 1v2a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1zm-7.5 0v-2a1 1 0 0 0 -1-1h-4a1 1 0 0 0 -1 1v2a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1zm7.5 5.5v-2a1 1 0 0 0 -1-1h-4a1 1 0 0 0 -1 1v2a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1zm-7.5 0v-2a1 1 0 0 0 -1-1h-4a1 1 0 0 0 -1 1v2a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1zm7.5 5.5v-2a1 1 0 0 0 -1-1h-4a1 1 0 0 0 -1 1v2a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1zm-7.5 0v-2a1 1 0 0 0 -1-1h-4a1 1 0 0 0 -1 1v2a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1zm15-5.5v-2a1 1 0 0 0 -1-1h-4a1 1 0 0 0 -1 1v2a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1zm0 5.5v-2a1 1 0 0 0 -1-1h-4a1 1 0 0 0 -1 1v2a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1z"/></svg>
|
After Width: | Height: | Size: 788 B |
1
images/icons/v2/speaker-view-solid-24.svg
Normal file
1
images/icons/v2/speaker-view-solid-24.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="m15 19.5h-12.5a1 1 0 0 1 -1-1v-13a1 1 0 0 1 1-1h12.5a1 1 0 0 1 1 1v13a1 1 0 0 1 -1 1zm7.5-12v-2a1 1 0 0 0 -1-1h-3a1 1 0 0 0 -1 1v2a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1zm0 5.5v-2a1 1 0 0 0 -1-1h-3a1 1 0 0 0 -1 1v2a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1zm0 5.5v-2a1 1 0 0 0 -1-1h-3a1 1 0 0 0 -1 1v2a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1z"/></svg>
|
After Width: | Height: | Size: 412 B |
|
@ -6090,11 +6090,13 @@ button.module-image__border-overlay:focus {
|
|||
}
|
||||
|
||||
.module-calling-button {
|
||||
$size: 22px;
|
||||
|
||||
&__participants {
|
||||
@include color-svg('../images/icons/v2/group-solid-24.svg', $color-white);
|
||||
display: inline-block;
|
||||
height: 22px;
|
||||
width: 22px;
|
||||
height: $size;
|
||||
width: $size;
|
||||
|
||||
&--container {
|
||||
@include button-reset;
|
||||
|
@ -6123,14 +6125,32 @@ button.module-image__border-overlay:focus {
|
|||
'../images/icons/v2/settings-solid-16.svg',
|
||||
$color-white
|
||||
);
|
||||
height: 22px;
|
||||
width: 22px;
|
||||
height: $size;
|
||||
width: $size;
|
||||
}
|
||||
|
||||
&__grid-view {
|
||||
@include color-svg(
|
||||
'../images/icons/v2/grid-view-solid-24.svg',
|
||||
$color-white
|
||||
);
|
||||
height: $size;
|
||||
width: $size;
|
||||
}
|
||||
|
||||
&__speaker-view {
|
||||
@include color-svg(
|
||||
'../images/icons/v2/speaker-view-solid-24.svg',
|
||||
$color-white
|
||||
);
|
||||
height: $size;
|
||||
width: $size;
|
||||
}
|
||||
|
||||
&__pip {
|
||||
@include color-svg('../images/icons/v2/pip-minimize-24.svg', $color-white);
|
||||
height: 22px;
|
||||
width: 22px;
|
||||
height: $size;
|
||||
width: $size;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@ const getCommonActiveCallData = () => ({
|
|||
joinedAt: Date.now(),
|
||||
hasLocalAudio: boolean('hasLocalAudio', true),
|
||||
hasLocalVideo: boolean('hasLocalVideo', false),
|
||||
isInSpeakerView: boolean('isInSpeakerView', false),
|
||||
pip: boolean('pip', false),
|
||||
settingsDialogOpen: boolean('settingsDialogOpen', false),
|
||||
showParticipantsList: boolean('showParticipantsList', false),
|
||||
|
@ -87,6 +88,7 @@ const createProps = (storyProps: Partial<PropsType> = {}): PropsType => ({
|
|||
toggleParticipants: action('toggle-participants'),
|
||||
togglePip: action('toggle-pip'),
|
||||
toggleSettings: action('toggle-settings'),
|
||||
toggleSpeakerView: action('toggle-speaker-view'),
|
||||
});
|
||||
|
||||
const story = storiesOf('Components/CallManager', module);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2020 Signal Messenger, LLC
|
||||
// Copyright 2020-2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React, { useCallback } from 'react';
|
||||
|
@ -73,6 +73,7 @@ export interface PropsType {
|
|||
hangUp: (_: HangUpType) => void;
|
||||
togglePip: () => void;
|
||||
toggleSettings: () => void;
|
||||
toggleSpeakerView: () => void;
|
||||
}
|
||||
|
||||
interface ActiveCallManagerPropsType extends PropsType {
|
||||
|
@ -100,6 +101,7 @@ const ActiveCallManager: React.FC<ActiveCallManagerPropsType> = ({
|
|||
toggleParticipants,
|
||||
togglePip,
|
||||
toggleSettings,
|
||||
toggleSpeakerView,
|
||||
}) => {
|
||||
const {
|
||||
conversation,
|
||||
|
@ -265,6 +267,7 @@ const ActiveCallManager: React.FC<ActiveCallManagerPropsType> = ({
|
|||
toggleParticipants={toggleParticipants}
|
||||
togglePip={togglePip}
|
||||
toggleSettings={toggleSettings}
|
||||
toggleSpeakerView={toggleSpeakerView}
|
||||
/>
|
||||
{settingsDialogOpen && renderDeviceSelection()}
|
||||
{showParticipantsList && activeCall.callMode === CallMode.Group ? (
|
||||
|
|
|
@ -44,6 +44,7 @@ const conversation = {
|
|||
interface OverridePropsBase {
|
||||
hasLocalAudio?: boolean;
|
||||
hasLocalVideo?: boolean;
|
||||
isInSpeakerView?: boolean;
|
||||
}
|
||||
|
||||
interface DirectCallOverrideProps extends OverridePropsBase {
|
||||
|
@ -113,6 +114,10 @@ const createActiveCallProp = (
|
|||
'hasLocalVideo',
|
||||
overrideProps.hasLocalVideo || false
|
||||
),
|
||||
isInSpeakerView: boolean(
|
||||
'isInSpeakerView',
|
||||
overrideProps.isInSpeakerView || false
|
||||
),
|
||||
pip: false,
|
||||
settingsDialogOpen: false,
|
||||
showParticipantsList: false,
|
||||
|
@ -152,6 +157,7 @@ const createProps = (
|
|||
toggleParticipants: action('toggle-participants'),
|
||||
togglePip: action('toggle-pip'),
|
||||
toggleSettings: action('toggle-settings'),
|
||||
toggleSpeakerView: action('toggle-speaker-view'),
|
||||
});
|
||||
|
||||
const story = storiesOf('Components/CallScreen', module);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2020 Signal Messenger, LLC
|
||||
// Copyright 2020-2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React, { useState, useRef, useEffect, useCallback } from 'react';
|
||||
|
@ -53,6 +53,7 @@ export type PropsType = {
|
|||
toggleParticipants: () => void;
|
||||
togglePip: () => void;
|
||||
toggleSettings: () => void;
|
||||
toggleSpeakerView: () => void;
|
||||
};
|
||||
|
||||
export const CallScreen: React.FC<PropsType> = ({
|
||||
|
@ -71,6 +72,7 @@ export const CallScreen: React.FC<PropsType> = ({
|
|||
toggleParticipants,
|
||||
togglePip,
|
||||
toggleSettings,
|
||||
toggleSpeakerView,
|
||||
}) => {
|
||||
const {
|
||||
conversation,
|
||||
|
@ -190,6 +192,7 @@ export const CallScreen: React.FC<PropsType> = ({
|
|||
<GroupCallRemoteParticipants
|
||||
getGroupCallVideoFrameSource={getGroupCallVideoFrameSource}
|
||||
i18n={i18n}
|
||||
isInSpeakerView={activeCall.isInSpeakerView}
|
||||
remoteParticipants={activeCall.remoteParticipants}
|
||||
setGroupCallVideoRequest={setGroupCallVideoRequest}
|
||||
/>
|
||||
|
@ -244,6 +247,7 @@ export const CallScreen: React.FC<PropsType> = ({
|
|||
<CallingHeader
|
||||
canPip
|
||||
i18n={i18n}
|
||||
isInSpeakerView={activeCall.isInSpeakerView}
|
||||
isGroupCall={activeCall.callMode === CallMode.Group}
|
||||
message={headerMessage}
|
||||
participantCount={participantCount}
|
||||
|
@ -252,6 +256,7 @@ export const CallScreen: React.FC<PropsType> = ({
|
|||
toggleParticipants={toggleParticipants}
|
||||
togglePip={togglePip}
|
||||
toggleSettings={toggleSettings}
|
||||
toggleSpeakerView={toggleSpeakerView}
|
||||
/>
|
||||
</div>
|
||||
{remoteParticipantsElement}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2020 Signal Messenger, LLC
|
||||
// Copyright 2020-2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
|
@ -10,6 +10,7 @@ import { Theme } from '../util/theme';
|
|||
export type PropsType = {
|
||||
canPip?: boolean;
|
||||
i18n: LocalizerType;
|
||||
isInSpeakerView?: boolean;
|
||||
isGroupCall?: boolean;
|
||||
message?: string;
|
||||
participantCount: number;
|
||||
|
@ -18,11 +19,13 @@ export type PropsType = {
|
|||
toggleParticipants?: () => void;
|
||||
togglePip?: () => void;
|
||||
toggleSettings: () => void;
|
||||
toggleSpeakerView?: () => void;
|
||||
};
|
||||
|
||||
export const CallingHeader = ({
|
||||
canPip = false,
|
||||
i18n,
|
||||
isInSpeakerView,
|
||||
isGroupCall = false,
|
||||
message,
|
||||
participantCount,
|
||||
|
@ -31,6 +34,7 @@ export const CallingHeader = ({
|
|||
toggleParticipants,
|
||||
togglePip,
|
||||
toggleSettings,
|
||||
toggleSpeakerView,
|
||||
}: PropsType): JSX.Element => (
|
||||
<div className="module-calling__header">
|
||||
{title ? (
|
||||
|
@ -80,6 +84,33 @@ export const CallingHeader = ({
|
|||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
{isGroupCall && participantCount > 2 && toggleSpeakerView && (
|
||||
<div className="module-calling-tools__button">
|
||||
<Tooltip
|
||||
content={i18n(
|
||||
isInSpeakerView
|
||||
? 'calling__switch-view--to-grid'
|
||||
: 'calling__switch-view--to-speaker'
|
||||
)}
|
||||
theme={Theme.Dark}
|
||||
>
|
||||
<button
|
||||
aria-label={i18n(
|
||||
isInSpeakerView
|
||||
? 'calling__switch-view--to-grid'
|
||||
: 'calling__switch-view--to-speaker'
|
||||
)}
|
||||
className={
|
||||
isInSpeakerView
|
||||
? 'module-calling-button__grid-view'
|
||||
: 'module-calling-button__speaker-view'
|
||||
}
|
||||
onClick={toggleSpeakerView}
|
||||
type="button"
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
)}
|
||||
{canPip && (
|
||||
<div className="module-calling-tools__button">
|
||||
<Tooltip content={i18n('calling__pip--on')} theme={Theme.Dark}>
|
||||
|
|
|
@ -39,6 +39,7 @@ const getCommonActiveCallData = () => ({
|
|||
conversation,
|
||||
hasLocalAudio: boolean('hasLocalAudio', true),
|
||||
hasLocalVideo: boolean('hasLocalVideo', false),
|
||||
isInSpeakerView: boolean('isInSpeakerView', false),
|
||||
joinedAt: Date.now(),
|
||||
pip: true,
|
||||
settingsDialogOpen: false,
|
||||
|
|
|
@ -38,6 +38,7 @@ interface GridArrangement {
|
|||
interface PropsType {
|
||||
getGroupCallVideoFrameSource: (demuxId: number) => VideoFrameSource;
|
||||
i18n: LocalizerType;
|
||||
isInSpeakerView: boolean;
|
||||
remoteParticipants: ReadonlyArray<GroupCallRemoteParticipantType>;
|
||||
setGroupCallVideoRequest: (_: Array<GroupCallVideoRequest>) => void;
|
||||
}
|
||||
|
@ -68,6 +69,7 @@ interface PropsType {
|
|||
export const GroupCallRemoteParticipants: React.FC<PropsType> = ({
|
||||
getGroupCallVideoFrameSource,
|
||||
i18n,
|
||||
isInSpeakerView,
|
||||
remoteParticipants,
|
||||
setGroupCallVideoRequest,
|
||||
}) => {
|
||||
|
@ -122,6 +124,14 @@ export const GroupCallRemoteParticipants: React.FC<PropsType> = ({
|
|||
[remoteParticipants]
|
||||
);
|
||||
const gridParticipants: Array<GroupCallRemoteParticipantType> = useMemo(() => {
|
||||
if (!sortedParticipants.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const candidateParticipants = isInSpeakerView
|
||||
? [sortedParticipants[0]]
|
||||
: sortedParticipants;
|
||||
|
||||
// Imagine that we laid out all of the rows end-to-end. That's the maximum total
|
||||
// width. So if there were 5 rows and the container was 100px wide, then we can't
|
||||
// possibly fit more than 500px of participants.
|
||||
|
@ -130,11 +140,16 @@ export const GroupCallRemoteParticipants: React.FC<PropsType> = ({
|
|||
// We do the same thing for participants, "laying them out end-to-end" until they
|
||||
// exceed the maximum total width.
|
||||
let totalWidth = 0;
|
||||
return takeWhile(sortedParticipants, remoteParticipant => {
|
||||
return takeWhile(candidateParticipants, remoteParticipant => {
|
||||
totalWidth += remoteParticipant.videoAspectRatio * MIN_RENDERED_HEIGHT;
|
||||
return totalWidth < maxTotalWidth;
|
||||
}).sort(stableParticipantComparator);
|
||||
}, [maxRowCount, containerDimensions.width, sortedParticipants]);
|
||||
}, [
|
||||
containerDimensions.width,
|
||||
isInSpeakerView,
|
||||
maxRowCount,
|
||||
sortedParticipants,
|
||||
]);
|
||||
const overflowedParticipants: Array<GroupCallRemoteParticipantType> = useMemo(
|
||||
() =>
|
||||
sortedParticipants
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2020 Signal Messenger, LLC
|
||||
// Copyright 2020-2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { ThunkAction } from 'redux-thunk';
|
||||
|
@ -68,12 +68,13 @@ export interface GroupCallStateType {
|
|||
|
||||
export interface ActiveCallStateType {
|
||||
conversationId: string;
|
||||
joinedAt?: number;
|
||||
hasLocalAudio: boolean;
|
||||
hasLocalVideo: boolean;
|
||||
isInSpeakerView: boolean;
|
||||
joinedAt?: number;
|
||||
pip: boolean;
|
||||
settingsDialogOpen: boolean;
|
||||
safetyNumberChangedUuids: Array<string>;
|
||||
settingsDialogOpen: boolean;
|
||||
showParticipantsList: boolean;
|
||||
}
|
||||
|
||||
|
@ -243,6 +244,7 @@ const START_DIRECT_CALL = 'calling/START_DIRECT_CALL';
|
|||
const TOGGLE_PARTICIPANTS = 'calling/TOGGLE_PARTICIPANTS';
|
||||
const TOGGLE_PIP = 'calling/TOGGLE_PIP';
|
||||
const TOGGLE_SETTINGS = 'calling/TOGGLE_SETTINGS';
|
||||
const TOGGLE_SPEAKER_VIEW = 'calling/TOGGLE_SPEAKER_VIEW';
|
||||
|
||||
type AcceptCallPendingActionType = {
|
||||
type: 'calling/ACCEPT_CALL_PENDING';
|
||||
|
@ -365,6 +367,10 @@ type ToggleSettingsActionType = {
|
|||
type: 'calling/TOGGLE_SETTINGS';
|
||||
};
|
||||
|
||||
type ToggleSpeakerViewActionType = {
|
||||
type: 'calling/TOGGLE_SPEAKER_VIEW';
|
||||
};
|
||||
|
||||
export type CallingActionType =
|
||||
| AcceptCallPendingActionType
|
||||
| CancelCallActionType
|
||||
|
@ -389,7 +395,8 @@ export type CallingActionType =
|
|||
| StartDirectCallActionType
|
||||
| ToggleParticipantsActionType
|
||||
| TogglePipActionType
|
||||
| ToggleSettingsActionType;
|
||||
| ToggleSettingsActionType
|
||||
| ToggleSpeakerViewActionType;
|
||||
|
||||
// Action Creators
|
||||
|
||||
|
@ -856,6 +863,12 @@ function toggleSettings(): ToggleSettingsActionType {
|
|||
};
|
||||
}
|
||||
|
||||
function toggleSpeakerView(): ToggleSpeakerViewActionType {
|
||||
return {
|
||||
type: TOGGLE_SPEAKER_VIEW,
|
||||
};
|
||||
}
|
||||
|
||||
export const actions = {
|
||||
acceptCall,
|
||||
cancelCall,
|
||||
|
@ -884,6 +897,7 @@ export const actions = {
|
|||
toggleParticipants,
|
||||
togglePip,
|
||||
toggleSettings,
|
||||
toggleSpeakerView,
|
||||
};
|
||||
|
||||
export type ActionsType = typeof actions;
|
||||
|
@ -974,6 +988,7 @@ export function reducer(
|
|||
conversationId: action.payload.conversationId,
|
||||
hasLocalAudio: action.payload.hasLocalAudio,
|
||||
hasLocalVideo: action.payload.hasLocalVideo,
|
||||
isInSpeakerView: false,
|
||||
pip: false,
|
||||
safetyNumberChangedUuids: [],
|
||||
settingsDialogOpen: false,
|
||||
|
@ -999,6 +1014,7 @@ export function reducer(
|
|||
conversationId: action.payload.conversationId,
|
||||
hasLocalAudio: action.payload.hasLocalAudio,
|
||||
hasLocalVideo: action.payload.hasLocalVideo,
|
||||
isInSpeakerView: false,
|
||||
pip: false,
|
||||
safetyNumberChangedUuids: [],
|
||||
settingsDialogOpen: false,
|
||||
|
@ -1019,6 +1035,7 @@ export function reducer(
|
|||
conversationId: action.payload.conversationId,
|
||||
hasLocalAudio: true,
|
||||
hasLocalVideo: action.payload.asVideoCall,
|
||||
isInSpeakerView: false,
|
||||
pip: false,
|
||||
safetyNumberChangedUuids: [],
|
||||
settingsDialogOpen: false,
|
||||
|
@ -1084,6 +1101,7 @@ export function reducer(
|
|||
conversationId: action.payload.conversationId,
|
||||
hasLocalAudio: action.payload.hasLocalAudio,
|
||||
hasLocalVideo: action.payload.hasLocalVideo,
|
||||
isInSpeakerView: false,
|
||||
pip: false,
|
||||
safetyNumberChangedUuids: [],
|
||||
settingsDialogOpen: false,
|
||||
|
@ -1409,6 +1427,24 @@ export function reducer(
|
|||
};
|
||||
}
|
||||
|
||||
if (action.type === TOGGLE_SPEAKER_VIEW) {
|
||||
const { activeCallState } = state;
|
||||
if (!activeCallState) {
|
||||
window.log.warn(
|
||||
'Cannot toggle speaker view when there is no active call'
|
||||
);
|
||||
return state;
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
activeCallState: {
|
||||
...activeCallState,
|
||||
isInSpeakerView: !activeCallState.isInSpeakerView,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (action.type === MARK_CALL_UNTRUSTED) {
|
||||
const { activeCallState } = state;
|
||||
if (!activeCallState) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2020 Signal Messenger, LLC
|
||||
// Copyright 2020-2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
|
@ -75,6 +75,7 @@ const mapStateToActiveCallProp = (
|
|||
conversation,
|
||||
hasLocalAudio: activeCallState.hasLocalAudio,
|
||||
hasLocalVideo: activeCallState.hasLocalVideo,
|
||||
isInSpeakerView: activeCallState.isInSpeakerView,
|
||||
joinedAt: activeCallState.joinedAt,
|
||||
pip: activeCallState.pip,
|
||||
settingsDialogOpen: activeCallState.settingsDialogOpen,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2020 Signal Messenger, LLC
|
||||
// Copyright 2020-2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { assert } from 'chai';
|
||||
|
@ -43,6 +43,7 @@ describe('calling duck', () => {
|
|||
conversationId: 'fake-direct-call-conversation-id',
|
||||
hasLocalAudio: true,
|
||||
hasLocalVideo: false,
|
||||
isInSpeakerView: false,
|
||||
showParticipantsList: false,
|
||||
safetyNumberChangedUuids: [],
|
||||
pip: false,
|
||||
|
@ -98,6 +99,7 @@ describe('calling duck', () => {
|
|||
conversationId: 'fake-group-call-conversation-id',
|
||||
hasLocalAudio: true,
|
||||
hasLocalVideo: false,
|
||||
isInSpeakerView: false,
|
||||
showParticipantsList: false,
|
||||
safetyNumberChangedUuids: [],
|
||||
pip: false,
|
||||
|
@ -202,6 +204,7 @@ describe('calling duck', () => {
|
|||
conversationId: 'fake-direct-call-conversation-id',
|
||||
hasLocalAudio: true,
|
||||
hasLocalVideo: true,
|
||||
isInSpeakerView: false,
|
||||
showParticipantsList: false,
|
||||
safetyNumberChangedUuids: [],
|
||||
pip: false,
|
||||
|
@ -578,6 +581,7 @@ describe('calling duck', () => {
|
|||
conversationId: 'fake-group-call-conversation-id',
|
||||
hasLocalAudio: true,
|
||||
hasLocalVideo: false,
|
||||
isInSpeakerView: false,
|
||||
showParticipantsList: false,
|
||||
safetyNumberChangedUuids: [],
|
||||
pip: false,
|
||||
|
@ -815,6 +819,7 @@ describe('calling duck', () => {
|
|||
conversationId: 'fake-conversation-id',
|
||||
hasLocalAudio: true,
|
||||
hasLocalVideo: true,
|
||||
isInSpeakerView: false,
|
||||
showParticipantsList: false,
|
||||
safetyNumberChangedUuids: [],
|
||||
pip: false,
|
||||
|
@ -1050,6 +1055,7 @@ describe('calling duck', () => {
|
|||
conversationId: 'fake-conversation-id',
|
||||
hasLocalAudio: true,
|
||||
hasLocalVideo: false,
|
||||
isInSpeakerView: false,
|
||||
showParticipantsList: false,
|
||||
safetyNumberChangedUuids: [],
|
||||
pip: false,
|
||||
|
@ -1120,6 +1126,23 @@ describe('calling duck', () => {
|
|||
assert.isTrue(afterThreeToggles.activeCallState?.pip);
|
||||
});
|
||||
});
|
||||
|
||||
describe('toggleSpeakerView', () => {
|
||||
const { toggleSpeakerView } = actions;
|
||||
|
||||
it('toggles speaker view', () => {
|
||||
const afterOneToggle = reducer(
|
||||
stateWithActiveGroupCall,
|
||||
toggleSpeakerView()
|
||||
);
|
||||
const afterTwoToggles = reducer(afterOneToggle, toggleSpeakerView());
|
||||
const afterThreeToggles = reducer(afterTwoToggles, toggleSpeakerView());
|
||||
|
||||
assert.isTrue(afterOneToggle.activeCallState?.isInSpeakerView);
|
||||
assert.isFalse(afterTwoToggles.activeCallState?.isInSpeakerView);
|
||||
assert.isTrue(afterThreeToggles.activeCallState?.isInSpeakerView);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('helpers', () => {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2019-2020 Signal Messenger, LLC
|
||||
// Copyright 2019-2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { assert } from 'chai';
|
||||
|
@ -40,6 +40,7 @@ describe('state/selectors/calling', () => {
|
|||
conversationId: 'fake-direct-call-conversation-id',
|
||||
hasLocalAudio: true,
|
||||
hasLocalVideo: false,
|
||||
isInSpeakerView: false,
|
||||
showParticipantsList: false,
|
||||
safetyNumberChangedUuids: [],
|
||||
pip: false,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2020 Signal Messenger, LLC
|
||||
// Copyright 2020-2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { ConversationType } from '../state/ducks/conversations';
|
||||
|
@ -14,6 +14,7 @@ interface ActiveCallBaseType {
|
|||
conversation: ConversationType;
|
||||
hasLocalAudio: boolean;
|
||||
hasLocalVideo: boolean;
|
||||
isInSpeakerView: boolean;
|
||||
joinedAt?: number;
|
||||
pip: boolean;
|
||||
settingsDialogOpen: boolean;
|
||||
|
|
Loading…
Reference in a new issue