Fix call animation from speaker to grid view so animating speaker is on top

This commit is contained in:
ayumi-signal
2025-02-19 09:13:27 -08:00
committed by GitHub
parent f7f1052246
commit 78cfa7eca3
2 changed files with 39 additions and 2 deletions

View File

@@ -4352,6 +4352,8 @@ button.module-image__border-overlay:focus {
line-height: 0; line-height: 0;
overflow: hidden; overflow: hidden;
border-radius: 10px; border-radius: 10px;
// Should match GroupCallRemoteParticipant CONTAINER_TRANSITION_TIME
transition: transition:
top 200ms linear, top 200ms linear,
inset-inline-start 200ms linear, inset-inline-start 200ms linear,
@@ -4531,6 +4533,10 @@ button.module-image__border-overlay:focus {
} }
} }
&--is-on-top {
z-index: variables.$z-index-above-above-base;
}
&:hover { &:hover {
.module-ongoing-call__group-call-remote-participant__info__contact-name { .module-ongoing-call__group-call-remote-participant__info__contact-name {
display: block; display: block;

View File

@@ -10,7 +10,7 @@ import React, {
useEffect, useEffect,
} from 'react'; } from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import { noop } from 'lodash'; import { debounce, noop } from 'lodash';
import type { VideoFrameSource } from '@signalapp/ringrtc'; import type { VideoFrameSource } from '@signalapp/ringrtc';
import type { GroupCallRemoteParticipantType } from '../types/Calling'; import type { GroupCallRemoteParticipantType } from '../types/Calling';
import type { LocalizerType } from '../types/Util'; import type { LocalizerType } from '../types/Util';
@@ -36,6 +36,9 @@ const MAX_TIME_TO_SHOW_STALE_VIDEO_FRAMES = 10000;
const MAX_TIME_TO_SHOW_STALE_SCREENSHARE_FRAMES = 60000; const MAX_TIME_TO_SHOW_STALE_SCREENSHARE_FRAMES = 60000;
const DELAY_TO_SHOW_MISSING_MEDIA_KEYS = 5000; const DELAY_TO_SHOW_MISSING_MEDIA_KEYS = 5000;
// Should match transition time in .module-ongoing-call__group-call-remote-participant
const CONTAINER_TRANSITION_TIME = 200;
type BasePropsType = { type BasePropsType = {
getFrameBuffer: () => Buffer; getFrameBuffer: () => Buffer;
getGroupCallVideoFrameSource: (demuxId: number) => VideoFrameSource; getGroupCallVideoFrameSource: (demuxId: number) => VideoFrameSource;
@@ -111,6 +114,10 @@ export const GroupCallRemoteParticipant: React.FC<PropsType> = React.memo(
SPEAKING_LINGER_MS SPEAKING_LINGER_MS
); );
const previousSharingScreen = usePrevious(sharingScreen, sharingScreen); const previousSharingScreen = usePrevious(sharingScreen, sharingScreen);
const prevIsActiveSpeakerInSpeakerView = usePrevious(
isActiveSpeakerInSpeakerView,
isActiveSpeakerInSpeakerView
);
const isImageDataCached = const isImageDataCached =
sharingScreen && imageDataCache.current?.has(demuxId); sharingScreen && imageDataCache.current?.has(demuxId);
@@ -120,6 +127,7 @@ export const GroupCallRemoteParticipant: React.FC<PropsType> = React.memo(
videoAspectRatio ? videoAspectRatio >= 1 : true videoAspectRatio ? videoAspectRatio >= 1 : true
); );
const [showErrorDialog, setShowErrorDialog] = useState(false); const [showErrorDialog, setShowErrorDialog] = useState(false);
const [isOnTop, setIsOnTop] = useState(false);
// We have some state (`hasReceivedVideoRecently`) and this ref. We can't have a // We have some state (`hasReceivedVideoRecently`) and this ref. We can't have a
// single state value like `lastReceivedVideoAt` because (1) it won't automatically // single state value like `lastReceivedVideoAt` because (1) it won't automatically
@@ -290,6 +298,27 @@ export const GroupCallRemoteParticipant: React.FC<PropsType> = React.memo(
}; };
}, [hasRemoteVideo, isVisible, renderVideoFrame, videoFrameSource]); }, [hasRemoteVideo, isVisible, renderVideoFrame, videoFrameSource]);
const setIsOnTopDebounced = useMemo(
() => debounce(setIsOnTop, CONTAINER_TRANSITION_TIME),
[setIsOnTop]
);
// When in speaker view or while transitioning out of it, keep the main speaker
// z-indexed above all other participants
useEffect(() => {
if (isActiveSpeakerInSpeakerView !== prevIsActiveSpeakerInSpeakerView) {
if (isActiveSpeakerInSpeakerView) {
setIsOnTop(true);
} else {
setIsOnTopDebounced(false);
}
}
}, [
prevIsActiveSpeakerInSpeakerView,
isActiveSpeakerInSpeakerView,
setIsOnTopDebounced,
]);
let canvasStyles: CSSProperties; let canvasStyles: CSSProperties;
let containerStyles: CSSProperties; let containerStyles: CSSProperties;
@@ -521,7 +550,9 @@ export const GroupCallRemoteParticipant: React.FC<PropsType> = React.memo(
remoteParticipantsCount > 1 && remoteParticipantsCount > 1 &&
'module-ongoing-call__group-call-remote-participant--speaking', 'module-ongoing-call__group-call-remote-participant--speaking',
isHandRaised && isHandRaised &&
'module-ongoing-call__group-call-remote-participant--hand-raised' 'module-ongoing-call__group-call-remote-participant--hand-raised',
isOnTop &&
'module-ongoing-call__group-call-remote-participant--is-on-top'
)} )}
ref={intersectionRef} ref={intersectionRef}
style={containerStyles} style={containerStyles}