File size: 4,817 Bytes
abc1805
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
"use client";

import { useEffect, useRef, useState } from "react";
import { Button } from "@/components/ui/button";
import { Mic, MicOff, Video, VideoOff, PhoneOff } from "lucide-react";
import { Card } from "@/components/ui/card";

interface CallInterfaceProps {
    localStream: MediaStream | null;
    remoteStreams: Map<string, MediaStream>;
    onLeave: () => void;
    nicknames: Map<string, string>;
}

export default function CallInterface({ localStream, remoteStreams, onLeave, nicknames }: CallInterfaceProps) {
    const localVideoRef = useRef<HTMLVideoElement>(null);
    const [isMuted, setIsMuted] = useState(false);
    const [isVideoOff, setIsVideoOff] = useState(false);

    useEffect(() => {
        if (localVideoRef.current && localStream) {
            localVideoRef.current.srcObject = localStream;
        }
    }, [localStream]);

    const toggleMute = () => {
        if (localStream) {
            localStream.getAudioTracks().forEach(track => track.enabled = !track.enabled);
            setIsMuted(!isMuted);
        }
    };

    const toggleVideo = () => {
        if (localStream) {
            localStream.getVideoTracks().forEach(track => track.enabled = !track.enabled);
            setIsVideoOff(!isVideoOff);
        }
    };

    return (
        <div className="fixed inset-0 bg-background/95 z-50 flex flex-col p-4">

            <div className="flex-1 grid grid-cols-2 md:grid-cols-3 gap-4 p-4 overflow-y-auto">

                {/* Local Video */}

                <Card className="relative overflow-hidden bg-black/50 aspect-video flex items-center justify-center">

                    <video

                        ref={localVideoRef}

                        autoPlay

                        muted

                        playsInline

                        className={`w-full h-full object-cover ${isVideoOff ? "hidden" : ""}`}

                    />

                    {isVideoOff && (

                        <div className="absolute inset-0 flex items-center justify-center text-muted-foreground">

                            Video Off

                        </div>

                    )}

                    <div className="absolute bottom-2 left-2 bg-black/50 px-2 py-1 rounded text-xs text-white">

                        You {isMuted && "(Muted)"}

                    </div>

                </Card>



                {/* Remote Videos */}

                {Array.from(remoteStreams.entries()).map(([socketId, stream]) => (

                    <RemoteVideo

                        key={socketId}

                        stream={stream}

                        nickname={nicknames.get(socketId) || `User ${socketId.slice(0, 4)}`}

                    />

                ))}

            </div>



            {/* Controls */}

            <div className="h-20 flex items-center justify-center gap-4 bg-card border-t border-border rounded-t-xl">

                <Button

                    variant={isMuted ? "destructive" : "secondary"}

                    size="icon"

                    className="rounded-full h-12 w-12"

                    onClick={toggleMute}

                >

                    {isMuted ? <MicOff className="w-5 h-5" /> : <Mic className="w-5 h-5" />}

                </Button>

                <Button

                    variant="destructive"

                    size="icon"

                    className="rounded-full h-14 w-14"

                    onClick={onLeave}

                >

                    <PhoneOff className="w-6 h-6" />

                </Button>

                <Button

                    variant={isVideoOff ? "destructive" : "secondary"}

                    size="icon"

                    className="rounded-full h-12 w-12"

                    onClick={toggleVideo}

                >

                    {isVideoOff ? <VideoOff className="w-5 h-5" /> : <Video className="w-5 h-5" />}

                </Button>

            </div>

        </div>
    );
}

function RemoteVideo({ stream, nickname }: { stream: MediaStream; nickname: string }) {
    const videoRef = useRef<HTMLVideoElement>(null);

    useEffect(() => {
        if (videoRef.current) {
            videoRef.current.srcObject = stream;
        }
    }, [stream]);

    return (
        <Card className="relative overflow-hidden bg-black/50 aspect-video flex items-center justify-center">

            <video

                ref={videoRef}

                autoPlay

                playsInline

                className="w-full h-full object-cover"

            />

            <div className="absolute bottom-2 left-2 bg-black/50 px-2 py-1 rounded text-xs text-white">

                {nickname}

            </div>

        </Card>
    );
}