tahamajs/IE / Projects /front /src /components /messages /VideoCallModal.tsx
tahamajs's picture
download
raw
6.05 kB
// src/components/messages/VideoCallModal.tsx
import { useEffect } from "react";
import {
Phone, PhoneOff, Mic, MicOff, Video, VideoOff, Minimize2,
User, Clock,
} from "lucide-react";
import { toast } from "react-toastify";
import type { UseVideoCallReturn } from "@/hooks/useVideoCall";
interface VideoCallModalProps {
isOpen: boolean;
onClose: () => void;
remoteUsername: string;
currentUsername: string;
mode: "outgoing" | "incoming" | "active";
onAccept?: () => void;
onReject?: () => void;
videoCall: UseVideoCallReturn;
}
export function VideoCallModal({
isOpen,
onClose,
remoteUsername,
currentUsername,
mode,
onAccept,
onReject,
videoCall,
}: VideoCallModalProps) {
const {
localVideoRef,
remoteVideoRef,
isMuted,
isVideoOff,
callDuration,
toggleMute,
toggleVideo,
endCall,
formatDuration,
} = videoCall;
// When modal closes, end the call (if active)
useEffect(() => {
if (!isOpen && mode === "active") {
endCall();
}
}, [isOpen, mode, endCall]);
// Prevent accidental background scroll
useEffect(() => {
if (isOpen) {
document.body.style.overflow = "hidden";
return () => {
document.body.style.overflow = "";
};
}
}, [isOpen]);
if (!isOpen) return null;
const handleEndCall = () => {
endCall();
onClose();
};
return (
<div className="fixed inset-0 z-[100] flex items-center justify-center bg-black">
{/* Remote video (full screen) */}
<video
ref={remoteVideoRef}
autoPlay
playsInline
className="absolute inset-0 w-full h-full object-cover"
/>
{/* Local video (picture‑in‑picture) */}
<div className="absolute bottom-6 right-6 w-40 h-28 sm:w-52 sm:h-36 rounded-2xl overflow-hidden border-2 border-white/30 shadow-2xl z-10 bg-gray-800">
<video
ref={localVideoRef}
autoPlay
playsInline
muted
className="w-full h-full object-cover"
/>
{isVideoOff && (
<div className="absolute inset-0 bg-gray-900 flex items-center justify-center">
<User className="w-10 h-10 text-gray-500" />
</div>
)}
</div>
{/* Status overlay */}
<div className="absolute top-6 left-1/2 -translate-x-1/2 z-10">
{mode === "outgoing" && (
<div className="bg-gray-900/80 text-white px-6 py-3 rounded-2xl flex items-center gap-3 text-sm">
<div className="w-3 h-3 rounded-full bg-yellow-400 animate-pulse" />
Calling {remoteUsername}...
</div>
)}
{mode === "incoming" && (
<div className="bg-gray-900/80 text-white px-6 py-3 rounded-2xl text-center">
<p className="text-lg font-semibold">{remoteUsername}</p>
<p className="text-sm text-gray-300">is calling you...</p>
</div>
)}
{mode === "active" && (
<div className="bg-gray-900/70 text-white px-4 py-2 rounded-full flex items-center gap-2 text-sm">
<Clock className="w-4 h-4" />
{formatDuration(callDuration)}
</div>
)}
</div>
{/* Caller info (when ringing) */}
{(mode === "outgoing" || mode === "incoming") && (
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 z-10 text-center">
<div className="w-24 h-24 rounded-full bg-linear-to-br from-primary to-secondary flex items-center justify-center text-white text-3xl font-bold mx-auto mb-4 shadow-2xl">
{(remoteUsername[0] || "?").toUpperCase()}
</div>
<h2 className="text-white text-xl font-semibold">{remoteUsername}</h2>
</div>
)}
{/* Controls */}
<div className="absolute bottom-24 left-1/2 -translate-x-1/2 flex gap-4 z-10">
{mode === "incoming" ? (
<>
<button
onClick={() => {
onReject?.();
onClose();
}}
className="w-16 h-16 rounded-full bg-red-500 hover:bg-red-600 flex items-center justify-center text-white shadow-xl transition"
>
<PhoneOff className="w-7 h-7" />
</button>
<button
onClick={() => onAccept?.()}
className="w-16 h-16 rounded-full bg-green-500 hover:bg-green-600 flex items-center justify-center text-white shadow-xl transition animate-pulse"
>
<Phone className="w-7 h-7" />
</button>
</>
) : (
<>
<button
onClick={toggleMute}
className={`w-12 h-12 rounded-full flex items-center justify-center text-white shadow-lg transition ${
isMuted ? "bg-red-500" : "bg-gray-700 hover:bg-gray-600"
}`}
>
{isMuted ? <MicOff className="w-5 h-5" /> : <Mic className="w-5 h-5" />}
</button>
<button
onClick={handleEndCall}
className="w-16 h-16 rounded-full bg-red-500 hover:bg-red-600 flex items-center justify-center text-white shadow-xl transition"
>
<PhoneOff className="w-7 h-7" />
</button>
<button
onClick={toggleVideo}
className={`w-12 h-12 rounded-full flex items-center justify-center text-white shadow-lg transition ${
isVideoOff ? "bg-red-500" : "bg-gray-700 hover:bg-gray-600"
}`}
>
{isVideoOff ? <VideoOff className="w-5 h-5" /> : <Video className="w-5 h-5" />}
</button>
</>
)}
</div>
{/* Minimize button (when call is active) */}
{mode === "active" && (
<button
onClick={onClose}
className="absolute top-6 left-6 z-10 bg-gray-800/70 text-white p-2 rounded-full hover:bg-gray-700"
>
<Minimize2 className="w-5 h-5" />
</button>
)}
</div>
);
}

Xet Storage Details

Size:
6.05 kB
·
Xet hash:
c3d8bc6b36c7378aaf775094ee39709607214bc3c4e27bbed6724bed540c2451

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.