diff --git a/client/src/components/SessionUsersPanel.tsx b/client/src/components/SessionUsersPanel.tsx
new file mode 100644
index 0000000..6df2a05
--- /dev/null
+++ b/client/src/components/SessionUsersPanel.tsx
@@ -0,0 +1,59 @@
+import UserCamera from "./ui/UserCamera";
+import UserDevicesControls from "./ui/UserDevicesControls";
+
+export default function SessionUsersPanel() {
+ const users = [
+ {
+ id: 1,
+ name: "John Doe",
+ isSpeaking: true,
+ isMuted: false,
+ isVideoOff: false,
+ isControlDisabled: false,
+ isAdmin: true,
+ },
+ {
+ id: 2,
+ name: "Jane Doe",
+ isSpeaking: false,
+ isMuted: true,
+ isVideoOff: true,
+ isControlDisabled: true,
+ },
+ {
+ id: 3,
+ name: "Jim Doe",
+ isSpeaking: false,
+ isMuted: false,
+ isVideoOff: false,
+ isControlDisabled: false,
+ },
+ ];
+
+ function handleMute(id: number) {
+ console.log(`Mute user ${id}`);
+ }
+ function handleVideoOff(id: number) {
+ console.log(`Video off user ${id}`);
+ }
+ function handleCanControl(id: number) {
+ console.log(`Can control user ${id}`);
+ }
+
+ return (
+
+
+ {users.map((user) => (
+ handleMute(user.id)}
+ onVideoOff={() => handleVideoOff(user.id)}
+ onCanControl={() => handleCanControl(user.id)}
+ {...user}
+ />
+ ))}
+
+
+
+ );
+}
diff --git a/client/src/components/indicators/Admin.tsx b/client/src/components/indicators/Admin.tsx
index b4c517c..adfed1d 100644
--- a/client/src/components/indicators/Admin.tsx
+++ b/client/src/components/indicators/Admin.tsx
@@ -9,7 +9,7 @@ export default function Admin({ className }: { className?: string }) {
className
)}
>
-
diff --git a/client/src/components/ui/Tooltip.tsx b/client/src/components/ui/Tooltip.tsx
index 022b4cb..82354d6 100644
--- a/client/src/components/ui/Tooltip.tsx
+++ b/client/src/components/ui/Tooltip.tsx
@@ -23,18 +23,14 @@ export default function Tooltip({
showDelay = 500,
}: TooltipProps) {
const [isVisible, setIsVisible] = useState(false);
- const [tooltipPosition, setTooltipPosition] = useState({
- top: 0,
- left: 0,
- transform: "none",
- });
+ const [tooltipPosition, setTooltipPosition] = useState({});
const tooltipWrapperRef = useRef(null);
const tooltipRef = useRef(null);
useEffect(() => {
if (!tooltipWrapperRef.current) return;
const current = tooltipWrapperRef.current;
-
+
current.addEventListener("mouseenter", () => setIsVisible(true));
current.addEventListener("mouseleave", () => setIsVisible(false));
return () => {
diff --git a/client/src/components/ui/UserCamera.tsx b/client/src/components/ui/UserCamera.tsx
new file mode 100644
index 0000000..557423a
--- /dev/null
+++ b/client/src/components/ui/UserCamera.tsx
@@ -0,0 +1,160 @@
+import { AnimatePresence, motion } from "motion/react";
+import { useState } from "react";
+
+import HandRaisedOffFilledIcon from "../icons/HandRaisedOffFilledIcon";
+import HandRaisedFilledIcon from "../icons/HandRaisedFilledIcon";
+import MicrophoneFilledIcon from "../icons/MicrophoneFilledIcon";
+import VideoOffFilledIcon from "../icons/VideoOffFilledIcon";
+import MicrophoneOffIcon from "../icons/MicrophoneOffIcon";
+import VideoFilledIcon from "../icons/VideoFilledIcon";
+import ControlButton from "./ControlButton";
+import Admin from "../indicators/Admin";
+import clsx from "clsx";
+
+interface UserCameraControlsProps {
+ isMuted: boolean;
+ isVideoOff: boolean;
+ isControlDisabled: boolean;
+ onMute: () => void;
+ onVideoOff: () => void;
+ onCanControl: () => void;
+}
+
+interface UserCameraProps extends UserCameraControlsProps {
+ isAdmin?: boolean;
+ name?: string;
+ mediaStream?: string;
+ isSpeaking?: boolean;
+}
+
+export default function UserCamera({
+ isMuted,
+ isVideoOff,
+ isControlDisabled,
+ onMute,
+ onVideoOff,
+ onCanControl,
+
+ isSpeaking = false,
+ isAdmin = false,
+ name = "Гость",
+ mediaStream = "",
+}: UserCameraProps) {
+ const [isHover, setIsHover] = useState(false);
+
+ return (
+ setIsHover(true)}
+ onMouseLeave={() => setIsHover(false)}
+ animate={{
+ width: isHover ? "10.833vw" : "6.944vw",
+ border: isSpeaking
+ ? "0.139vw solid #7B60F3"
+ : "0.139vw solid #FFFFFF4D",
+ }}
+ className={clsx(
+ "aspect-square rounded-[1.667vw] bg-yellow-500 relative flex-shrink-0",
+ isAdmin && "order-last"
+ )}
+ >
+ {isAdmin && }
+
+ {isHover && (
+
+ {name}
+
+ )}
+
+
+
+
+
+
+ );
+}
+
+function UserCameraControls({
+ isMuted,
+ isVideoOff,
+ isControlDisabled,
+ isHover,
+ onMute,
+ onVideoOff,
+ onCanControl,
+}: UserCameraControlsProps & { isHover: boolean }) {
+ return (
+
+
+ {isHover ? (
+
+ : }
+ size={"small"}
+ enabled={!isMuted}
+ onClick={onMute}
+ />
+ : }
+ size={"small"}
+ enabled={!isVideoOff}
+ onClick={onVideoOff}
+ />
+
+ ) : (
+
+ )
+ }
+ size={"small"}
+ enabled={!isControlDisabled}
+ onClick={onCanControl}
+ />
+
+ ) : (
+
+
+
+
+
+ )}
+
+
+ );
+}
diff --git a/client/src/components/ui/UserDevicesControls.tsx b/client/src/components/ui/UserDevicesControls.tsx
new file mode 100644
index 0000000..5068667
--- /dev/null
+++ b/client/src/components/ui/UserDevicesControls.tsx
@@ -0,0 +1,52 @@
+import MicrophoneFilledIcon from "../icons/MicrophoneFilledIcon";
+import ControlButton from "./ControlButton";
+import VideoFilledIcon from "../icons/VideoFilledIcon";
+import HandRaisedFilledIcon from "../icons/HandRaisedFilledIcon";
+import CogFilledIcon from "../icons/CogFilledIcon";
+import useModalStore from "../../store/modalStore";
+import SettingsModal from "../modals/SettingsModal";
+
+export default function UserDevicesControls() {
+ const { setModal } = useModalStore();
+ function ToggleAudioDevice() {
+ console.log("Mute device");
+ }
+ function ToggleVideoDevice() {
+ console.log("Video device");
+ }
+ function ToggleCanControl() {
+ console.log("Can control device");
+ }
+ function ToggleSettings() {
+ setModal();
+ }
+
+ return (
+
+ }
+ onClick={ToggleAudioDevice}
+ enabled={true}
+ />
+ }
+ enabled={true}
+ onClick={ToggleVideoDevice}
+ />
+ }
+ onClick={ToggleCanControl}
+ enabled={true}
+ />
+ }
+ enabled={true}
+ onClick={ToggleSettings}
+ />
+
+ );
+}
diff --git a/client/src/pages/HomePage.tsx b/client/src/pages/HomePage.tsx
index ab75ce3..1c9ecfd 100644
--- a/client/src/pages/HomePage.tsx
+++ b/client/src/pages/HomePage.tsx
@@ -8,6 +8,7 @@ import SettingsModal from "../components/modals/SettingsModal";
import useModalStore from "../store/modalStore";
import CogFilledIcon from "../components/icons/CogFilledIcon";
import ParticipantsPopup from "../components/popups/ParticipantsPopup";
+import SessionUsersPanel from "../components/SessionUsersPanel";
function HomePage() {
const { data: user } = useMe();
@@ -38,6 +39,7 @@ function HomePage() {
+