Consider own join time for group call missing media key check
Co-authored-by: Jamie Kyle <113370520+jamiebuilds-signal@users.noreply.github.com>
This commit is contained in:
parent
f9b2261783
commit
ec9041937f
11 changed files with 82 additions and 15 deletions
|
@ -34,6 +34,7 @@ import { fakeGetGroupCallVideoFrameSource } from '../test-both/helpers/fakeGetGr
|
|||
import enMessages from '../../_locales/en/messages.json';
|
||||
import { CallingToastProvider, useCallingToasts } from './CallingToast';
|
||||
import type { CallingImageDataCache } from './CallManager';
|
||||
import { MINUTE } from '../util/durations';
|
||||
|
||||
const MAX_PARTICIPANTS = 75;
|
||||
const LOCAL_DEMUX_ID = 1;
|
||||
|
@ -158,7 +159,7 @@ const createActiveCallProp = (
|
|||
overrideProps: DirectCallOverrideProps | GroupCallOverrideProps
|
||||
) => {
|
||||
const baseResult = {
|
||||
joinedAt: Date.now(),
|
||||
joinedAt: Date.now() - MINUTE,
|
||||
conversation,
|
||||
hasLocalAudio: overrideProps.hasLocalAudio ?? false,
|
||||
hasLocalVideo: overrideProps.hasLocalVideo ?? false,
|
||||
|
|
|
@ -660,8 +660,7 @@ export function CallScreen({
|
|||
/>
|
||||
);
|
||||
}
|
||||
// joinedAt is only available for direct calls
|
||||
if (isConnected) {
|
||||
if (isConnected && activeCall.callMode === CallMode.Direct) {
|
||||
return <CallDuration joinedAt={activeCall.joinedAt} />;
|
||||
}
|
||||
if (hasLocalVideo) {
|
||||
|
@ -713,6 +712,7 @@ export function CallScreen({
|
|||
getGroupCallVideoFrameSource={getGroupCallVideoFrameSource}
|
||||
imageDataCache={imageDataCache}
|
||||
i18n={i18n}
|
||||
joinedAt={activeCall.joinedAt}
|
||||
remoteParticipants={activeCall.remoteParticipants}
|
||||
setGroupCallVideoRequest={setGroupCallVideoRequest}
|
||||
remoteAudioLevels={activeCall.remoteAudioLevels}
|
||||
|
|
|
@ -21,6 +21,7 @@ import { getDefaultConversation } from '../test-both/helpers/getDefaultConversat
|
|||
import { fakeGetGroupCallVideoFrameSource } from '../test-both/helpers/fakeGetGroupCallVideoFrameSource';
|
||||
import { setupI18n } from '../util/setupI18n';
|
||||
import enMessages from '../../_locales/en/messages.json';
|
||||
import { MINUTE } from '../util/durations';
|
||||
|
||||
const i18n = setupI18n('en', enMessages);
|
||||
|
||||
|
@ -47,7 +48,7 @@ const getCommonActiveCallData = (overrides: Overrides) => ({
|
|||
hasLocalVideo: overrides.hasLocalVideo ?? false,
|
||||
localAudioLevel: overrides.localAudioLevel ?? 0,
|
||||
viewMode: overrides.viewMode ?? CallViewMode.Paginated,
|
||||
joinedAt: Date.now(),
|
||||
joinedAt: Date.now() - MINUTE,
|
||||
outgoingRing: true,
|
||||
pip: true,
|
||||
settingsDialogOpen: false,
|
||||
|
|
|
@ -188,6 +188,7 @@ export function CallingPipRemoteVideo({
|
|||
imageDataCache={imageDataCache}
|
||||
i18n={i18n}
|
||||
isInPip
|
||||
joinedAt={activeCall.joinedAt}
|
||||
remoteParticipant={activeGroupCallSpeaker}
|
||||
remoteParticipantsCount={activeCall.remoteParticipants.length}
|
||||
isActiveSpeakerInSpeakerView={false}
|
||||
|
|
|
@ -14,6 +14,7 @@ import { FRAME_BUFFER_SIZE } from '../calling/constants';
|
|||
import enMessages from '../../_locales/en/messages.json';
|
||||
import { generateAci } from '../types/ServiceId';
|
||||
import type { CallingImageDataCache } from './CallManager';
|
||||
import { MINUTE } from '../util/durations';
|
||||
|
||||
const MAX_PARTICIPANTS = 32;
|
||||
|
||||
|
@ -48,6 +49,7 @@ const defaultProps = {
|
|||
imageDataCache: React.createRef<CallingImageDataCache>(),
|
||||
i18n,
|
||||
isCallReconnecting: false,
|
||||
joinedAt: new Date().getTime() - MINUTE,
|
||||
onParticipantVisibilityChanged: action('onParticipantVisibilityChanged'),
|
||||
remoteAudioLevels: new Map<number, number>(),
|
||||
remoteParticipantsCount: 1,
|
||||
|
|
|
@ -22,6 +22,7 @@ export type PropsType = {
|
|||
i18n: LocalizerType;
|
||||
imageDataCache: React.RefObject<CallingImageDataCache>;
|
||||
isCallReconnecting: boolean;
|
||||
joinedAt: number | null;
|
||||
onClickRaisedHand?: () => void;
|
||||
onParticipantVisibilityChanged: (
|
||||
demuxId: number,
|
||||
|
@ -38,6 +39,7 @@ export function GroupCallOverflowArea({
|
|||
imageDataCache,
|
||||
i18n,
|
||||
isCallReconnecting,
|
||||
joinedAt,
|
||||
onClickRaisedHand,
|
||||
onParticipantVisibilityChanged,
|
||||
overflowedParticipants,
|
||||
|
@ -138,6 +140,7 @@ export function GroupCallOverflowArea({
|
|||
isActiveSpeakerInSpeakerView={false}
|
||||
isCallReconnecting={isCallReconnecting}
|
||||
isInOverflow
|
||||
joinedAt={joinedAt}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
@ -12,6 +12,7 @@ import { setupI18n } from '../util/setupI18n';
|
|||
import { generateAci } from '../types/ServiceId';
|
||||
import enMessages from '../../_locales/en/messages.json';
|
||||
import type { CallingImageDataCache } from './CallManager';
|
||||
import { MINUTE } from '../util/durations';
|
||||
|
||||
const i18n = setupI18n('en', enMessages);
|
||||
|
||||
|
@ -79,6 +80,7 @@ const createProps = (
|
|||
remoteParticipantsCount: 1,
|
||||
isActiveSpeakerInSpeakerView: false,
|
||||
isCallReconnecting: false,
|
||||
joinedAt: new Date().getTime() - MINUTE,
|
||||
...overrideProps,
|
||||
});
|
||||
|
||||
|
@ -186,7 +188,7 @@ export function NoMediaKeys(): JSX.Element {
|
|||
width: 120,
|
||||
},
|
||||
{
|
||||
addedTime: Date.now() - 60 * 1000,
|
||||
addedTime: Date.now() - MINUTE,
|
||||
hasRemoteAudio: true,
|
||||
mediaKeysReceived: false,
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ type BasePropsType = {
|
|||
isActiveSpeakerInSpeakerView: boolean;
|
||||
isCallReconnecting: boolean;
|
||||
isInOverflow?: boolean;
|
||||
joinedAt: number | null;
|
||||
onClickRaisedHand?: () => void;
|
||||
onVisibilityChanged?: (demuxId: number, isVisible: boolean) => unknown;
|
||||
remoteParticipant: GroupCallRemoteParticipantType;
|
||||
|
@ -82,6 +83,7 @@ export const GroupCallRemoteParticipant: React.FC<PropsType> = React.memo(
|
|||
isActiveSpeakerInSpeakerView,
|
||||
isCallReconnecting,
|
||||
isInOverflow,
|
||||
joinedAt,
|
||||
} = props;
|
||||
|
||||
const {
|
||||
|
@ -150,10 +152,17 @@ export const GroupCallRemoteParticipant: React.FC<PropsType> = React.memo(
|
|||
|
||||
const wantsToShowVideo = hasRemoteVideo && !isBlocked && isVisible;
|
||||
const hasVideoToShow = wantsToShowVideo && hasReceivedVideoRecently;
|
||||
|
||||
// Use the later of participant join time (addedTime) vs your join time (joinedAt)
|
||||
const timeForMissingMediaKeysCheck =
|
||||
addedTime && joinedAt && addedTime > joinedAt ? addedTime : joinedAt;
|
||||
const showMissingMediaKeys = Boolean(
|
||||
!mediaKeysReceived &&
|
||||
addedTime &&
|
||||
isOlderThan(addedTime, DELAY_TO_SHOW_MISSING_MEDIA_KEYS)
|
||||
timeForMissingMediaKeysCheck &&
|
||||
isOlderThan(
|
||||
timeForMissingMediaKeysCheck,
|
||||
DELAY_TO_SHOW_MISSING_MEDIA_KEYS
|
||||
)
|
||||
);
|
||||
|
||||
const videoFrameSource = useMemo(
|
||||
|
|
|
@ -63,6 +63,7 @@ type PropsType = {
|
|||
i18n: LocalizerType;
|
||||
imageDataCache: React.RefObject<CallingImageDataCache>;
|
||||
isCallReconnecting: boolean;
|
||||
joinedAt: number | null;
|
||||
remoteParticipants: ReadonlyArray<GroupCallRemoteParticipantType>;
|
||||
setGroupCallVideoRequest: (
|
||||
_: Array<GroupCallVideoRequest>,
|
||||
|
@ -115,6 +116,7 @@ export function GroupCallRemoteParticipants({
|
|||
imageDataCache,
|
||||
i18n,
|
||||
isCallReconnecting,
|
||||
joinedAt,
|
||||
remoteParticipants,
|
||||
setGroupCallVideoRequest,
|
||||
remoteAudioLevels,
|
||||
|
@ -359,6 +361,7 @@ export function GroupCallRemoteParticipants({
|
|||
remoteParticipantsCount={remoteParticipants.length}
|
||||
isActiveSpeakerInSpeakerView={isInSpeakerView}
|
||||
isCallReconnecting={isCallReconnecting}
|
||||
joinedAt={joinedAt}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
@ -517,6 +520,7 @@ export function GroupCallRemoteParticipants({
|
|||
imageDataCache={imageDataCache}
|
||||
i18n={i18n}
|
||||
isCallReconnecting={isCallReconnecting}
|
||||
joinedAt={joinedAt}
|
||||
onClickRaisedHand={onClickRaisedHand}
|
||||
onParticipantVisibilityChanged={onParticipantVisibilityChanged}
|
||||
overflowedParticipants={overflowedParticipants}
|
||||
|
|
|
@ -3408,14 +3408,22 @@ export function reducer(
|
|||
state.activeCallState?.state === 'Active' &&
|
||||
state.activeCallState?.conversationId === conversationId
|
||||
) {
|
||||
newActiveCallState =
|
||||
connectionState === GroupCallConnectionState.NotConnected
|
||||
? undefined
|
||||
: {
|
||||
...state.activeCallState,
|
||||
hasLocalAudio,
|
||||
hasLocalVideo,
|
||||
};
|
||||
if (connectionState === GroupCallConnectionState.NotConnected) {
|
||||
newActiveCallState = undefined;
|
||||
} else {
|
||||
const joinedAt =
|
||||
state.activeCallState.joinedAt ??
|
||||
(connectionState === GroupCallConnectionState.Connected
|
||||
? new Date().getTime()
|
||||
: null);
|
||||
|
||||
newActiveCallState = {
|
||||
...state.activeCallState,
|
||||
hasLocalAudio,
|
||||
hasLocalVideo,
|
||||
joinedAt,
|
||||
};
|
||||
}
|
||||
|
||||
// The first time we detect call participants in the lobby, check participant count
|
||||
// and mute ourselves if over the threshold.
|
||||
|
|
|
@ -1265,6 +1265,42 @@ describe('calling duck', () => {
|
|||
);
|
||||
assert.isTrue(result.activeCallState?.hasLocalAudio);
|
||||
assert.isTrue(result.activeCallState?.hasLocalVideo);
|
||||
assert.isNumber(result.activeCallState?.joinedAt);
|
||||
});
|
||||
|
||||
it('keeps existing activeCallState.joinedAt', () => {
|
||||
const joinedAt = new Date().getTime() - 1000;
|
||||
const result = reducer(
|
||||
{
|
||||
...stateWithActiveGroupCall,
|
||||
activeCallState: {
|
||||
...stateWithActiveDirectCall.activeCallState,
|
||||
joinedAt,
|
||||
},
|
||||
},
|
||||
getAction({
|
||||
callMode: CallMode.Group,
|
||||
conversationId: 'fake-group-call-conversation-id',
|
||||
connectionState: GroupCallConnectionState.Connected,
|
||||
joinState: GroupCallJoinState.Joined,
|
||||
localDemuxId: 1,
|
||||
hasLocalAudio: true,
|
||||
hasLocalVideo: true,
|
||||
peekInfo: {
|
||||
acis: [],
|
||||
pendingAcis: [],
|
||||
maxDevices: 16,
|
||||
deviceCount: 0,
|
||||
},
|
||||
remoteParticipants: [],
|
||||
})
|
||||
);
|
||||
|
||||
strictAssert(
|
||||
result.activeCallState?.state === 'Active',
|
||||
'state is active'
|
||||
);
|
||||
assert.equal(result.activeCallState?.joinedAt, joinedAt);
|
||||
});
|
||||
|
||||
it("doesn't stop ringing if nobody is in the call", () => {
|
||||
|
|
Loading…
Reference in a new issue