Update environment configuration for local development, add new dependencies for WebRTC functionality, and refactor user session management components. Remove deprecated SessionUsersPanel and enhance SessionUsersPanel2 with improved user handling and controls. Integrate socket.io for real-time communication in the server.
This commit is contained in:
@@ -1,190 +0,0 @@
|
||||
import { useState, useRef, useEffect, useCallback } from "react";
|
||||
import UserCamera from "./ui/UserCamera";
|
||||
import UserDevicesControls from "./ui/UserDevicesControls";
|
||||
import clsx from "clsx";
|
||||
|
||||
const DRAG_THRESHOLD = 15;
|
||||
const OFFSET = 0.01111; // 1.111vw
|
||||
const TRANSITION = "all 0.5s cubic-bezier(.63,.08,.37,.89)";
|
||||
|
||||
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,
|
||||
},
|
||||
];
|
||||
|
||||
const [corner, setCorner] = useState({ top: false, left: false });
|
||||
const [dragState, setDragState] = useState<"idle" | "dragging" | "snapping" | "released">("idle");
|
||||
const [position, setPosition] = useState({ x: 0, y: 0 });
|
||||
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const dragDataRef = useRef({ offsetX: 0, offsetY: 0, startX: 0, startY: 0, hasStarted: false });
|
||||
|
||||
const getPointerPos = (e: MouseEvent | TouchEvent | React.MouseEvent | React.TouchEvent) => ({
|
||||
x: "touches" in e ? e.touches[0].clientX : e.clientX,
|
||||
y: "touches" in e ? e.touches[0].clientY : e.clientY,
|
||||
});
|
||||
|
||||
const handleMove = useCallback((e: MouseEvent | TouchEvent) => {
|
||||
if (!containerRef.current) return;
|
||||
|
||||
const pos = getPointerPos(e);
|
||||
const { startX, startY, offsetX, offsetY, hasStarted } = dragDataRef.current;
|
||||
|
||||
if (!hasStarted) {
|
||||
const distance = Math.hypot(pos.x - startX, pos.y - startY);
|
||||
if (distance < DRAG_THRESHOLD) return;
|
||||
|
||||
dragDataRef.current.hasStarted = true;
|
||||
setDragState("dragging");
|
||||
}
|
||||
|
||||
setPosition({ x: pos.x - offsetX, y: pos.y - offsetY });
|
||||
}, []);
|
||||
|
||||
const handleEnd = useCallback(() => {
|
||||
if (!containerRef.current) return;
|
||||
|
||||
const rect = containerRef.current.getBoundingClientRect();
|
||||
const centerX = rect.left + rect.width / 2;
|
||||
const centerY = rect.top + rect.height / 2;
|
||||
const shouldBeTop = centerY < window.innerHeight / 2;
|
||||
const shouldBeLeft = centerX < window.innerWidth / 2;
|
||||
|
||||
if (dragDataRef.current.hasStarted) {
|
||||
// Фиксируем текущую позицию без transition
|
||||
setPosition({ x: rect.left, y: rect.top });
|
||||
setDragState("released");
|
||||
|
||||
// Запускаем анимацию к углу
|
||||
requestAnimationFrame(() => {
|
||||
requestAnimationFrame(() => {
|
||||
setDragState("snapping");
|
||||
setCorner({ top: shouldBeTop, left: shouldBeLeft });
|
||||
setTimeout(() => setDragState("idle"), 500);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
setDragState("idle");
|
||||
}
|
||||
|
||||
dragDataRef.current.hasStarted = false;
|
||||
window.removeEventListener("mousemove", handleMove);
|
||||
window.removeEventListener("touchmove", handleMove);
|
||||
window.removeEventListener("mouseup", handleEnd);
|
||||
window.removeEventListener("touchend", handleEnd);
|
||||
}, [handleMove]);
|
||||
|
||||
const handleStart = (e: React.MouseEvent | React.TouchEvent) => {
|
||||
if (!containerRef.current) return;
|
||||
|
||||
const rect = containerRef.current.getBoundingClientRect();
|
||||
const pos = getPointerPos(e);
|
||||
|
||||
dragDataRef.current = {
|
||||
startX: pos.x,
|
||||
startY: pos.y,
|
||||
offsetX: pos.x - rect.left,
|
||||
offsetY: pos.y - rect.top,
|
||||
hasStarted: false,
|
||||
};
|
||||
|
||||
setPosition({ x: rect.left, y: rect.top });
|
||||
|
||||
window.addEventListener("mousemove", handleMove);
|
||||
window.addEventListener("touchmove", handleMove);
|
||||
window.addEventListener("mouseup", handleEnd);
|
||||
window.addEventListener("touchend", handleEnd);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
window.removeEventListener("mousemove", handleMove);
|
||||
window.removeEventListener("touchmove", handleMove);
|
||||
window.removeEventListener("mouseup", handleEnd);
|
||||
window.removeEventListener("touchend", handleEnd);
|
||||
};
|
||||
}, [handleMove, handleEnd]);
|
||||
|
||||
const offset = window.innerWidth * OFFSET;
|
||||
|
||||
// Вычисляем финальные координаты угла
|
||||
const getCornerPosition = () => {
|
||||
if (!containerRef.current) return { x: offset, y: offset };
|
||||
const rect = containerRef.current.getBoundingClientRect();
|
||||
return {
|
||||
x: corner.left ? offset : window.innerWidth - offset - rect.width,
|
||||
y: corner.top ? offset : window.innerHeight - offset - rect.height,
|
||||
};
|
||||
};
|
||||
|
||||
let style: React.CSSProperties;
|
||||
if (dragState === "dragging" || dragState === "released") {
|
||||
// Во время перетаскивания или сразу после отпускания
|
||||
style = {
|
||||
left: position.x,
|
||||
top: position.y,
|
||||
transition: "none"
|
||||
};
|
||||
} else {
|
||||
// Анимация к углу или покой в углу
|
||||
const cornerPos = getCornerPosition();
|
||||
style = {
|
||||
left: cornerPos.x,
|
||||
top: cornerPos.y,
|
||||
transition: dragState === "snapping" ? TRANSITION : "none",
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={containerRef}
|
||||
onMouseDown={handleStart}
|
||||
onTouchStart={handleStart}
|
||||
className="flex absolute gap-4 active:cursor-grabbing cursor-grab"
|
||||
style={style}
|
||||
>
|
||||
<div
|
||||
className={clsx(
|
||||
"flex gap-4 w-max",
|
||||
corner.left ? "flex-row-reverse" : "flex-row",
|
||||
corner.top ? "items-start" : "items-end"
|
||||
)}
|
||||
>
|
||||
{users.map((user) => (
|
||||
<UserCamera
|
||||
key={user.id}
|
||||
onMute={() => console.log(`Mute user ${user.id}`)}
|
||||
onVideoOff={() => console.log(`Video off user ${user.id}`)}
|
||||
onCanControl={() => console.log(`Can control user ${user.id}`)}
|
||||
{...user}
|
||||
/>
|
||||
))}
|
||||
<UserDevicesControls />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user