import {
Avatar,
AvatarFallback,
AvatarImage,
Button,
ContextMenu,
ContextMenuContent,
ContextMenuItem,
ContextMenuTrigger,
} from "@tensamin/ui";
import {
focusParticipant,
getRoomMetadata,
setCallView,
startWatchingStream,
useCall,
} from "../../store";
import { Track, type Participant } from "livekit-client";
import { useEffect, useRef, useState } from "react";
import { useUser, type User } from "@tensamin/user/context";
import { useIsSpeaking } from "../../speakingState";
import VideoViewer from "../videoViewer";
import { HeadphoneOff, MicOff, Monitor, Plus, Shield } from "lucide-react";
function getTrackPublicationBySource(
participant: Participant | undefined,
source: Track.Source,
) {
if (!participant) {
return undefined;
}
return [...participant.trackPublications.values()].find(
(publication) => publication.source === source,
);
}
function TransparentButton({ children }: { children: React.ReactNode }) {
return (
{children}
);
}
function Overlay({
type,
user,
participant,
}: {
type: "user" | "stream";
user: User;
participant: Participant;
}) {
const isAdmin = getRoomMetadata()?.admins.includes(user.user_id) === true;
const isDeafened = participant.attributes["deafened"] === "true";
return (
{type === "user" && (
<>
{!participant.isMicrophoneEnabled && (
)}
{isDeafened && (
)}
{isAdmin && (
)}
>
)}
{type === "stream" && (
)}
{user.display}
);
}
export default function Base({
participant,
type,
fill = false,
flush = false,
}: {
participant: Participant | undefined;
type: "user" | "stream";
fill?: boolean;
flush?: boolean;
}) {
const { get } = useUser();
const focusedParticipantId = useCall((state) => state.focusedParticipantId);
const view = useCall((state) => state.view);
const [user, setUser] = useState(null);
const isSpeaking = useIsSpeaking(user?.user_id ?? -1);
const screenSharePublication = getTrackPublicationBySource(
participant,
Track.Source.ScreenShare,
);
const screenSharePreview = participant?.attributes["screenSharePreview"];
useEffect(() => {
const participantId = Number(participant?.identity);
if (
!participant ||
!Number.isInteger(participantId) ||
participantId <= 0
) {
// eslint-disable-next-line
setUser(null);
return;
}
let active = true;
void get(participantId).then((nextUser) => {
if (active) {
setUser(nextUser);
}
});
return () => {
active = false;
};
}, [participant, get]);
// Avatar calc
const currentCard = useRef(null);
if (!participant || !user) {
return (
);
}
const onClick = () => {
if (view === "grid") {
focusParticipant(user.user_id, type);
} else {
if (user.user_id === focusedParticipantId) {
setCallView("grid");
} else {
focusParticipant(user.user_id, type);
}
}
};
const showStream =
screenSharePublication?.isSubscribed && screenSharePublication.track;
const isFocusedInFocusedView =
view === "focused" && user.user_id === focusedParticipantId;
return (
<>
{type === "stream" && !showStream && (
{!isFocusedInFocusedView && (
)}
)}
{view === "grid" ||
(view === "focused" &&
user.user_id !== focusedParticipantId) ? (
) : null}
>
{/* Detect video / user and place here */}
{type === "stream" &&
(showStream ? (
) : screenSharePreview ? (
) : null)}
{type === "user" && (
{user.display.slice(0, 2).toUpperCase()}
)}
}
/>
Stop Watching
);
}