Merge pull request #1410 from vector-im/dbkr/smooth_focus_switch
Smooth Focus Switching
This commit is contained in:
@@ -27,7 +27,7 @@ import { ConnectionState, Room, Track } from "livekit-client";
|
||||
import { MatrixClient } from "matrix-js-sdk/src/client";
|
||||
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
|
||||
import { Room as MatrixRoom } from "matrix-js-sdk/src/models/room";
|
||||
import { Ref, useCallback, useEffect, useMemo, useRef } from "react";
|
||||
import { Ref, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import useMeasure from "react-use-measure";
|
||||
import { OverlayTriggerState } from "@react-stately/overlays";
|
||||
@@ -73,7 +73,10 @@ import { MuteStates } from "./MuteStates";
|
||||
import { MatrixInfo } from "./VideoPreview";
|
||||
import { ShareButton } from "../button/ShareButton";
|
||||
import { LayoutToggle } from "./LayoutToggle";
|
||||
import { ECConnectionState } from "../livekit/useECConnectionState";
|
||||
import {
|
||||
ECAddonConnectionState,
|
||||
ECConnectionState,
|
||||
} from "../livekit/useECConnectionState";
|
||||
import { useOpenIDSFU } from "../livekit/openIDSFU";
|
||||
|
||||
const canScreenshare = "getDisplayMedia" in (navigator.mediaDevices ?? {});
|
||||
@@ -82,6 +85,9 @@ const canScreenshare = "getDisplayMedia" in (navigator.mediaDevices ?? {});
|
||||
// For now we can disable screensharing in Safari.
|
||||
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
|
||||
|
||||
// How long we wait after a focus switch before showing the real participant list again
|
||||
const POST_FOCUS_PARTICIPANT_UPDATE_DELAY_MS = 3000;
|
||||
|
||||
export interface ActiveCallProps
|
||||
extends Omit<InCallViewProps, "livekitRoom" | "connState"> {
|
||||
e2eeConfig?: E2EEConfig;
|
||||
@@ -236,7 +242,7 @@ export function InCallView({
|
||||
const reducedControls = boundsValid && bounds.width <= 340;
|
||||
const noControls = reducedControls && bounds.height <= 400;
|
||||
|
||||
const items = useParticipantTiles(livekitRoom, rtcSession.room);
|
||||
const items = useParticipantTiles(livekitRoom, rtcSession.room, connState);
|
||||
const { fullscreenItem, toggleFullscreen, exitFullscreen } =
|
||||
useFullscreen(items);
|
||||
|
||||
@@ -476,8 +482,11 @@ function findMatrixMember(
|
||||
|
||||
function useParticipantTiles(
|
||||
livekitRoom: Room,
|
||||
matrixRoom: MatrixRoom
|
||||
matrixRoom: MatrixRoom,
|
||||
connState: ECConnectionState
|
||||
): TileDescriptor<ItemData>[] {
|
||||
const previousTiles = useRef<TileDescriptor<ItemData>[]>([]);
|
||||
|
||||
const sfuParticipants = useParticipants({
|
||||
room: livekitRoom,
|
||||
});
|
||||
@@ -559,5 +568,44 @@ function useParticipantTiles(
|
||||
return allGhosts ? [] : tiles;
|
||||
}, [matrixRoom, sfuParticipants]);
|
||||
|
||||
return items;
|
||||
// We carry over old tiles from the previous focus for some time after a focus switch
|
||||
// so that the video tiles don't all disappear and reappear.
|
||||
// This is set to true when the state transitions to Switching Focus and remains
|
||||
// true for a short time after it changes (ie. connState is only switching focus for
|
||||
// the time it takes us to reconnect to the conference).
|
||||
// If there are still members that haven't reconnected after that time, they'll just
|
||||
// appear to disconnect and will reappear once they reconnect.
|
||||
const [isSwitchingFocus, setIsSwitchingFocus] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (connState === ECAddonConnectionState.ECSwitchingFocus) {
|
||||
setIsSwitchingFocus(true);
|
||||
} else if (isSwitchingFocus) {
|
||||
setTimeout(() => {
|
||||
setIsSwitchingFocus(false);
|
||||
}, POST_FOCUS_PARTICIPANT_UPDATE_DELAY_MS);
|
||||
}
|
||||
}, [connState, setIsSwitchingFocus, isSwitchingFocus]);
|
||||
|
||||
if (
|
||||
connState === ECAddonConnectionState.ECSwitchingFocus ||
|
||||
isSwitchingFocus
|
||||
) {
|
||||
logger.debug("Switching focus: injecting previous tiles");
|
||||
|
||||
// inject the previous tile for members that haven't rejoined yet
|
||||
const newItems = items.slice(0);
|
||||
const rejoined = new Set(newItems.map((p) => p.id));
|
||||
|
||||
for (const prevTile of previousTiles.current) {
|
||||
if (!rejoined.has(prevTile.id)) {
|
||||
newItems.push(prevTile);
|
||||
}
|
||||
}
|
||||
|
||||
return newItems;
|
||||
} else {
|
||||
previousTiles.current = items;
|
||||
return items;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user