-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added video calling support
- Loading branch information
Showing
8 changed files
with
338 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import asyncHandler from "express-async-handler"; | ||
import { Request, Response } from "express"; | ||
import axios from "axios"; | ||
import prisma from "../lib/prisma"; | ||
|
||
export const createRoom = asyncHandler(async (req: Request, res: Response) => { | ||
//@ts-ignore | ||
const username = req.user.username; | ||
const video = await prisma.video.findUnique({ | ||
where: { | ||
video_id: username, | ||
}, | ||
}); | ||
if (video) { | ||
res.status(205).json({ message: "Room already exists" }); | ||
return; | ||
} | ||
const response = await axios.post( | ||
"https://api.daily.co/v1/rooms", | ||
{ | ||
properties: { enable_chat: true, enable_screenshare: true }, | ||
name: username, | ||
}, | ||
{ headers: { Authorization: `Bearer ${process.env.DAILY_API_KEY}` } } | ||
); | ||
if (!response.data) { | ||
res.status(400); | ||
throw new Error("Invalid data"); | ||
} | ||
const url = response.data.url; | ||
const video_id = response.data.name; | ||
//@ts-ignore | ||
const user_id = req.user.user_id; | ||
await prisma.video.create({ | ||
data: { | ||
video_id, | ||
url, | ||
user_id, | ||
}, | ||
}); | ||
res.status(201).json(response.data); | ||
}); | ||
|
||
export const deleteRoom = asyncHandler(async (req: Request, res: Response) => { | ||
const { video_id } = req.body; | ||
if (!video_id) { | ||
res.status(400); | ||
throw new Error("Invalid data"); | ||
} | ||
const video = await prisma.video.findUnique({ | ||
where: { | ||
video_id, | ||
}, | ||
select: { | ||
user_id: true, | ||
}, | ||
}); | ||
//@ts-ignore | ||
const user_id = req.user.user_id; | ||
if (video?.user_id !== user_id) { | ||
res.status(401).json({ message: "Unauthorized" }); | ||
return; | ||
} | ||
await axios.delete(`https://api.daily.co/v1/rooms/${video_id}`, { | ||
headers: { Authorization: `Bearer ${process.env.DAILY_API_KEY}` }, | ||
}); | ||
await prisma.video.delete({ | ||
where: { | ||
video_id, | ||
}, | ||
}); | ||
res.status(200).json({ message: "Room deleted" }); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import express from "express"; | ||
import checkAuth from "../middleware/checkAuth"; | ||
import { createRoom, deleteRoom } from "../controllers/videoController"; | ||
import rateLimiter from "../middleware/rateLimit"; | ||
|
||
const router = express.Router(); | ||
|
||
router.post("/createroom", checkAuth, rateLimiter, createRoom); | ||
router.delete("/deleteroom", checkAuth, rateLimiter, deleteRoom); | ||
|
||
export default router; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
import { useState, useRef, useEffect } from "react"; | ||
import { | ||
Box, | ||
Button, | ||
Center, | ||
Heading, | ||
VStack, | ||
useToast, | ||
Flex, | ||
Input, | ||
} from "@chakra-ui/react"; | ||
import { gsap } from "gsap"; | ||
import { useNavigate } from "react-router-dom"; | ||
import VideoCall from "./VideoCall"; | ||
import { useUser } from "../hook/useUser"; | ||
import axios from "axios"; | ||
import "../styles/loader.css"; | ||
// import { useNavigate } from "react-router-dom"; | ||
|
||
const StartVideoCall = () => { | ||
const toast = useToast(); | ||
const navigate = useNavigate(); | ||
const { userDetails, loadingUser } = useUser(); | ||
|
||
const [isLoading, setIsLoading] = useState(loadingUser); | ||
const [roomUrl, setRoomUrl] = useState(""); // URL for the created room | ||
const [joinRoomUrl, setJoinRoomUrl] = useState(""); // URL for joining a room | ||
const buttonRef = useRef(); | ||
|
||
useEffect(() => { | ||
if (loadingUser) { | ||
setIsLoading(true); | ||
} else { | ||
setIsLoading(false); | ||
if (!userDetails) { | ||
toast({ | ||
title: "Please sign in first", | ||
status: "error", | ||
duration: 5000, | ||
isClosable: true, | ||
}); | ||
navigate("/login"); | ||
} | ||
} | ||
}, [loadingUser, userDetails]); | ||
|
||
const createRoom = async () => { | ||
try { | ||
const response = await axios.post("/api/video/createroom"); | ||
if (response.status == 205) { | ||
navigate(`/video/${userDetails.username}`); | ||
return; | ||
} | ||
setRoomUrl(response.data.url); | ||
setJoinRoomUrl(""); | ||
navigate(`/video/${response.data.url.split("/").pop()}`); | ||
} catch (error) { | ||
console.error("Failed to create room", error); | ||
toast({ | ||
title: "Error creating room", | ||
status: "error", | ||
duration: 5000, | ||
isClosable: true, | ||
}); | ||
} | ||
}; | ||
|
||
const handleUrlChange = (e) => { | ||
setJoinRoomUrl(e.target.value); | ||
}; | ||
|
||
const handleJoinRoom = () => { | ||
if (joinRoomUrl) { | ||
setRoomUrl(joinRoomUrl); | ||
setJoinRoomUrl(""); | ||
navigate(`/video/${joinRoomUrl.split("/").pop()}`); | ||
} else { | ||
toast({ | ||
title: "Please enter a valid room URL", | ||
status: "warning", | ||
duration: 5000, | ||
isClosable: true, | ||
}); | ||
} | ||
}; | ||
|
||
useEffect(() => { | ||
if (!isLoading) { | ||
gsap.from(buttonRef.current, { | ||
y: -50, | ||
opacity: 0, | ||
duration: 0.5, | ||
ease: "bounce", | ||
}); | ||
} | ||
}, [isLoading]); | ||
|
||
if (isLoading) { | ||
return ( | ||
<Flex minH="100vh" align="center" justify="center" bg="black"> | ||
<div className="loader"></div> | ||
</Flex> | ||
); | ||
} | ||
|
||
return ( | ||
<Center height="100vh" bgGradient="linear(to-r, teal.500, blue.500)"> | ||
<VStack spacing={8}> | ||
<Heading color="white" size="2xl"> | ||
{roomUrl ? "Join the Video Call" : "Ready to Start a Video Call?"} | ||
</Heading> | ||
<Button | ||
ref={buttonRef} | ||
colorScheme="teal" | ||
size="lg" | ||
onClick={createRoom} | ||
> | ||
Start Video Call | ||
</Button> | ||
<Input | ||
placeholder="Enter Room URL" | ||
value={joinRoomUrl} | ||
onChange={handleUrlChange} // Use handleUrlChange to update state | ||
/> | ||
<Button | ||
colorScheme="teal" | ||
size="lg" | ||
onClick={handleJoinRoom} // Call handleJoinRoom when joining | ||
> | ||
Join Video Call | ||
</Button> | ||
</VStack> | ||
</Center> | ||
); | ||
}; | ||
|
||
export default StartVideoCall; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
import DailyIframe from "@daily-co/daily-js"; | ||
import { useEffect, useRef, useState } from "react"; | ||
import { Box, useToast, Flex } from "@chakra-ui/react"; | ||
import { useParams, useNavigate } from "react-router-dom"; | ||
import { useUser } from "../hook/useUser"; | ||
import axios from "axios"; | ||
import "../styles/loader.css"; | ||
|
||
const VideoCall = () => { | ||
const videoRef = useRef(); | ||
const callFrame = useRef(); | ||
const { id } = useParams(); | ||
const navigate = useNavigate(); | ||
const roomUrl = "https://campusify.daily.co/" + id; | ||
const { userDetails, loadingUser } = useUser(); | ||
const toast = useToast(); | ||
const [isLoading, setIsLoading] = useState(true); | ||
|
||
useEffect(() => { | ||
if (loadingUser) { | ||
setIsLoading(true); | ||
} else { | ||
setIsLoading(false); | ||
if (!userDetails) { | ||
toast({ | ||
title: "Please sign in first", | ||
status: "error", | ||
duration: 5000, | ||
isClosable: true, | ||
}); | ||
navigate("/login"); | ||
} | ||
} | ||
}, [loadingUser, userDetails, navigate, toast]); | ||
|
||
const username = userDetails?.username; | ||
|
||
useEffect(() => { | ||
if (!isLoading && username) { | ||
callFrame.current = DailyIframe.createFrame(videoRef.current, { | ||
showLeaveButton: true, | ||
iframeStyle: { | ||
position: "absolute", | ||
top: 0, | ||
left: 0, | ||
width: "100%", | ||
height: "100%", | ||
borderRadius: "0", | ||
}, | ||
}); | ||
const handleLeave = async () => { | ||
const videoId = roomUrl.split("/").pop(); | ||
try { | ||
const response = await axios.delete("/api/video/deleteroom", { | ||
data: { video_id: videoId }, | ||
}); | ||
console.log(response.data.message); | ||
} catch (error) { | ||
console.error("Failed to delete room:", error); | ||
} | ||
}; | ||
callFrame.current.on("left-meeting", handleLeave); | ||
|
||
// console.log(username, roomUrl); | ||
callFrame.current.join({ url: roomUrl, userName: username }); | ||
|
||
return () => { | ||
callFrame.current.leave(); | ||
callFrame.current.destroy(); | ||
}; | ||
} | ||
}, [roomUrl, username, isLoading]); | ||
|
||
if (isLoading) { | ||
return ( | ||
<Flex minH="100vh" align="center" justify="center" bg="black"> | ||
<div className="loader"></div> | ||
</Flex> | ||
); | ||
} | ||
|
||
return ( | ||
<Box | ||
ref={videoRef} | ||
width="100vw" | ||
height="100vh" | ||
position="absolute" | ||
top="0" | ||
left="0" | ||
bg="black" | ||
/> | ||
); | ||
}; | ||
|
||
export default VideoCall; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters