Maximise the active speaker when the window is small

This commit is contained in:
Robin Townsend 2022-09-14 19:05:05 -04:00
commit 9e367db324
6 changed files with 86 additions and 52 deletions

View file

@ -16,6 +16,8 @@ limitations under the License.
import React, { useEffect, useCallback, useMemo, useRef } from "react";
import { usePreventScroll } from "@react-aria/overlays";
import useMeasure from "react-use-measure";
import { ResizeObserver } from "@juggle/resize-observer";
import { MatrixClient } from "matrix-js-sdk/src/client";
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
import { GroupCall } from "matrix-js-sdk/src/webrtc/groupCall";
@ -111,9 +113,20 @@ export function InCallView({
hideHeader,
}: Props) {
usePreventScroll();
const elementRef = useRef<HTMLDivElement>();
const containerRef1 = useRef<HTMLDivElement | null>(null);
const [containerRef2, bounds] = useMeasure({ polyfill: ResizeObserver });
// Merge the refs so they can attach to the same element
const containerRef = useCallback(
(el: HTMLDivElement) => {
containerRef1.current = el;
containerRef2(el);
},
[containerRef1, containerRef2]
);
const { layout, setLayout } = useVideoGridLayout(screenshareFeeds.length > 0);
const { toggleFullscreen, fullscreenParticipant } = useFullscreen(elementRef);
const { toggleFullscreen, fullscreenParticipant } =
useFullscreen(containerRef1);
const [spatialAudio] = useSpatialAudio();
@ -170,9 +183,7 @@ export function InCallView({
id: callFeed.stream.id,
callFeed,
focused:
screenshareFeeds.length === 0 && layout === "spotlight"
? callFeed.userId === activeSpeaker
: false,
screenshareFeeds.length === 0 && callFeed.userId === activeSpeaker,
isLocal: callFeed.isLocal(),
presenter: false,
});
@ -197,7 +208,20 @@ export function InCallView({
}
return participants;
}, [userMediaFeeds, activeSpeaker, screenshareFeeds, layout]);
}, [userMediaFeeds, activeSpeaker, screenshareFeeds]);
// The maximised participant: either the participant that the user has
// manually put in fullscreen, or the focused (active) participant if the
// window is too small to show everyone
const maximisedParticipant = useMemo(
() =>
fullscreenParticipant ?? bounds.height <= 500
? items.find((item) => item.focused) ??
items.find((item) => item.callFeed) ??
null
: null,
[fullscreenParticipant, bounds, items]
);
const renderAvatar = useCallback(
(roomMember: RoomMember, width: number, height: number) => {
@ -217,7 +241,7 @@ export function InCallView({
[]
);
const renderContent = useCallback((): JSX.Element => {
const renderContent = (): JSX.Element => {
if (items.length === 0) {
return (
<div className={styles.centerMessage}>
@ -225,16 +249,19 @@ export function InCallView({
</div>
);
}
if (fullscreenParticipant) {
if (maximisedParticipant) {
return (
<VideoTileContainer
key={fullscreenParticipant.id}
item={fullscreenParticipant}
height={bounds.height}
width={bounds.width}
key={maximisedParticipant.id}
item={maximisedParticipant}
getAvatar={renderAvatar}
audioContext={audioContext}
audioDestination={audioDestination}
disableSpeakingIndicator={true}
isFullscreen={!!fullscreenParticipant}
maximised={Boolean(maximisedParticipant)}
fullscreen={maximisedParticipant === fullscreenParticipant}
onFullscreen={toggleFullscreen}
/>
);
@ -250,43 +277,36 @@ export function InCallView({
audioContext={audioContext}
audioDestination={audioDestination}
disableSpeakingIndicator={items.length < 3}
isFullscreen={!!fullscreenParticipant}
maximised={false}
fullscreen={false}
onFullscreen={toggleFullscreen}
{...rest}
/>
)}
</VideoGrid>
);
}, [
fullscreenParticipant,
items,
audioContext,
audioDestination,
layout,
renderAvatar,
toggleFullscreen,
]);
};
const {
modalState: rageshakeRequestModalState,
modalProps: rageshakeRequestModalProps,
} = useRageshakeRequestModal(groupCall.room.roomId);
const footerClassNames = classNames(styles.footer, {
[styles.footerFullscreen]: fullscreenParticipant,
const containerClasses = classNames(styles.inRoom, {
[styles.maximised]: maximisedParticipant,
});
return (
<div className={styles.inRoom} ref={elementRef}>
<div className={containerClasses} ref={containerRef}>
<audio ref={audioRef} />
{(!spatialAudio || fullscreenParticipant) && (
{(!spatialAudio || maximisedParticipant) && (
<AudioContainer
items={items}
audioContext={audioContext}
audioDestination={audioDestination}
/>
)}
{!hideHeader && !fullscreenParticipant && (
{!hideHeader && !maximisedParticipant && (
<Header>
<LeftNav>
<RoomHeaderInfo roomName={roomName} avatarUrl={avatarUrl} />
@ -302,16 +322,16 @@ export function InCallView({
</Header>
)}
{renderContent()}
<div className={footerClassNames}>
<div className={styles.footer}>
<MicButton muted={microphoneMuted} onPress={toggleMicrophoneMuted} />
<VideoButton muted={localVideoMuted} onPress={toggleLocalVideoMuted} />
{canScreenshare && !isSafari && !fullscreenParticipant && (
{canScreenshare && !isSafari && !maximisedParticipant && (
<ScreenshareButton
enabled={isScreensharing}
onPress={toggleScreensharing}
/>
)}
{!fullscreenParticipant && (
{!maximisedParticipant && (
<OverflowMenu
inCall
roomIdOrAlias={roomIdOrAlias}