(fix): sharing screen and then stopping screenshare causes empty gray box to remain
This commit is contained in:
parent
fbd8ef4fa0
commit
1df48c55c1
3 changed files with 61 additions and 28 deletions
|
|
@ -7,7 +7,7 @@ import {
|
|||
ContextMenuTrigger,
|
||||
} from "@tensamin/ui";
|
||||
import { focusParticipant, setCallView, useCall } from "../../store";
|
||||
import { type Participant } from "livekit-client";
|
||||
import { Track, type Participant } from "livekit-client";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useUser, type User } from "@tensamin/user/context";
|
||||
import VideoViewer from "../videoViewer";
|
||||
|
|
@ -67,6 +67,9 @@ export default function Base({
|
|||
const focusedParticipantId = useCall((state) => state.focusedParticipantId);
|
||||
const view = useCall((state) => state.view);
|
||||
const [user, setUser] = useState<User | null>(null);
|
||||
const screenSharePublication = participant?.getTrackPublication(
|
||||
Track.Source.ScreenShare,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const participantId = Number(participant?.identity);
|
||||
|
|
@ -139,18 +142,15 @@ export default function Base({
|
|||
{/* Detect video / user and place here */}
|
||||
|
||||
{type === "stream" &&
|
||||
Array.from(participant.videoTrackPublications.values()).map(
|
||||
(publication) =>
|
||||
publication.isSubscribed && publication.track ? (
|
||||
(screenSharePublication?.isSubscribed && screenSharePublication.track ? (
|
||||
<VideoViewer
|
||||
flush={flush}
|
||||
participantId={participant.identity}
|
||||
publication={publication}
|
||||
publication={screenSharePublication}
|
||||
/>
|
||||
) : (
|
||||
<Loader2 className="animate-spin" />
|
||||
),
|
||||
)}
|
||||
))}
|
||||
|
||||
{type === "user" && <p>{user.display}</p>}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ function getActiveScreenShareParticipantIds(): number[] {
|
|||
.map((participant) => ({
|
||||
participantId: getParticipantId(participant.identity),
|
||||
hasScreenShare:
|
||||
participant.getTrackPublication(Track.Source.ScreenShare) != null,
|
||||
participant.getTrackPublication(Track.Source.ScreenShare)?.track != null,
|
||||
}))
|
||||
.filter(
|
||||
(entry): entry is { participantId: number; hasScreenShare: true } =>
|
||||
|
|
@ -841,6 +841,8 @@ export function useInitializeCall() {
|
|||
room.on(RoomEvent.Disconnected, onDisconnected);
|
||||
room.on(RoomEvent.TrackSubscribed, onTrackSubscribed);
|
||||
room.on(RoomEvent.TrackUnsubscribed, onTrackUnsubscribed);
|
||||
room.on(RoomEvent.TrackPublished, onParticipantStateChange);
|
||||
room.on(RoomEvent.TrackUnpublished, onParticipantStateChange);
|
||||
room.on(RoomEvent.ParticipantConnected, onParticipantConnected);
|
||||
room.on(RoomEvent.ParticipantDisconnected, onParticipantDisconnected);
|
||||
room.on(RoomEvent.TrackMuted, onParticipantStateChange);
|
||||
|
|
@ -860,6 +862,8 @@ export function useInitializeCall() {
|
|||
room.off(RoomEvent.Disconnected, onDisconnected);
|
||||
room.off(RoomEvent.TrackSubscribed, onTrackSubscribed);
|
||||
room.off(RoomEvent.TrackUnsubscribed, onTrackUnsubscribed);
|
||||
room.off(RoomEvent.TrackPublished, onParticipantStateChange);
|
||||
room.off(RoomEvent.TrackUnpublished, onParticipantStateChange);
|
||||
room.off(RoomEvent.ParticipantConnected, onParticipantConnected);
|
||||
room.off(RoomEvent.ParticipantDisconnected, onParticipantDisconnected);
|
||||
room.off(RoomEvent.TrackMuted, onParticipantStateChange);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { useLayoutEffect, useMemo, useRef, useState } from "react";
|
||||
import { RoomEvent } from "livekit-client";
|
||||
import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
||||
import { useCall } from "../../store";
|
||||
import Base from "../../components/modals/base";
|
||||
|
||||
|
|
@ -16,6 +17,20 @@ export default function View() {
|
|||
const containerRef = useRef<HTMLDivElement | null>(null);
|
||||
const [focusedTileSize, setFocusedTileSize] = useState({ width: 0, height: 0 });
|
||||
const [isFocusedTileFlush, setIsFocusedTileFlush] = useState(false);
|
||||
const [participantVersion, setParticipantVersion] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
const syncParticipants = () => {
|
||||
setParticipantVersion((version) => version + 1);
|
||||
};
|
||||
room.on(RoomEvent.ParticipantConnected, syncParticipants);
|
||||
room.on(RoomEvent.ParticipantDisconnected, syncParticipants);
|
||||
|
||||
return () => {
|
||||
room.off(RoomEvent.ParticipantConnected, syncParticipants);
|
||||
room.off(RoomEvent.ParticipantDisconnected, syncParticipants);
|
||||
};
|
||||
}, [room]);
|
||||
|
||||
const users = useMemo(() => {
|
||||
const participants = [
|
||||
|
|
@ -27,12 +42,17 @@ export default function View() {
|
|||
const participantId = Number(participant.identity);
|
||||
return Number.isInteger(participantId) && participantId > 0;
|
||||
});
|
||||
}, [room]);
|
||||
// eslint-disable-next-line
|
||||
}, [participantVersion, room]);
|
||||
|
||||
const userIds = useMemo(
|
||||
() => users.map((participant) => Number(participant.identity)),
|
||||
[users],
|
||||
);
|
||||
const activeScreenShareParticipantIdSet = useMemo(
|
||||
() => new Set(activeScreenShareParticipantIds),
|
||||
[activeScreenShareParticipantIds],
|
||||
);
|
||||
|
||||
const tiles = useMemo(
|
||||
() => [
|
||||
|
|
@ -45,14 +65,19 @@ export default function View() {
|
|||
})),
|
||||
...userIds
|
||||
.filter((id) => id !== focusedParticipantId)
|
||||
.filter((id) => !activeScreenShareParticipantIds.includes(id))
|
||||
.filter((id) => !activeScreenShareParticipantIdSet.has(id))
|
||||
.map((participantId) => ({
|
||||
key: `user:${participantId}`,
|
||||
kind: "user" as const,
|
||||
participantId,
|
||||
})),
|
||||
],
|
||||
[activeScreenShareParticipantIds, userIds, focusedParticipantId],
|
||||
[
|
||||
activeScreenShareParticipantIds,
|
||||
activeScreenShareParticipantIdSet,
|
||||
userIds,
|
||||
focusedParticipantId,
|
||||
],
|
||||
);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
|
|
@ -103,9 +128,17 @@ export default function View() {
|
|||
return null;
|
||||
}
|
||||
|
||||
const focusedParticipant = room.getParticipantByIdentity(
|
||||
String(focusedParticipantId),
|
||||
);
|
||||
function getParticipantById(participantId: number) {
|
||||
if (Number(room.localParticipant.identity) === participantId) {
|
||||
return room.localParticipant;
|
||||
}
|
||||
|
||||
return room.getParticipantByIdentity(String(participantId));
|
||||
}
|
||||
|
||||
const focusedParticipant = getParticipantById(focusedParticipantId);
|
||||
const focusedParticipantHasActiveScreenShare =
|
||||
activeScreenShareParticipantIdSet.has(focusedParticipantId);
|
||||
|
||||
return (
|
||||
<div ref={containerRef} className="flex h-full w-full items-center justify-center overflow-hidden">
|
||||
|
|
@ -117,10 +150,8 @@ export default function View() {
|
|||
<div className="overflow-hidden" style={focusedTileSize}>
|
||||
<Base
|
||||
flush={isFocusedTileFlush}
|
||||
type={focusedParticipant?.isScreenShareEnabled ? "stream" : "user"}
|
||||
participant={room.getParticipantByIdentity(
|
||||
String(focusedParticipantId),
|
||||
)}
|
||||
type={focusedParticipantHasActiveScreenShare ? "stream" : "user"}
|
||||
participant={focusedParticipant}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -133,9 +164,7 @@ export default function View() {
|
|||
<div key={tile.key} className="h-full shrink-0 aspect-video">
|
||||
<Base
|
||||
type={tile.kind}
|
||||
participant={room.getParticipantByIdentity(
|
||||
String(tile.participantId),
|
||||
)}
|
||||
participant={getParticipantById(tile.participantId)}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
|
|
|
|||
Loading…
Reference in a new issue