Commit b188752b authored by Thisara Kavinda's avatar Thisara Kavinda

feat: implemented lobby & loading screens

parent ecc165b0
......@@ -35,8 +35,9 @@
"@typescript-eslint/no-confusing-void-expression": "off",
"@typescript-eslint/consistent-indexed-object-style": "warn",
"@typescript-eslint/await-thenable": "off",
"@typescript-eslint/consistent-type-imports": "warn"
"@typescript-eslint/consistent-type-imports": "warn",
// "no-restricted-imports": ["warn", { "patterns": ["../*"] }]
"@typescript-eslint/no-unused-vars": "warn"
},
"settings": {
"react": {
......
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.formatOnPaste": false
"editor.formatOnPaste": false,
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}
import React from 'react'
import './App.css'
import { ThemeProvider } from '@mui/material/styles'
import { Routes, Route } from 'react-router-dom'
......@@ -6,15 +5,16 @@ import { Routes, Route } from 'react-router-dom'
import VideoCall from './Pages/VideoCall/VideoCall'
import { theme } from './Services/Utils/theme'
import { Box } from '@mui/material'
function App() {
return (
<ThemeProvider theme={theme}>
<div className='App'>
<Box className='App'>
<Routes>
<Route path='/' element={<VideoCall />} />
</Routes>
</div>
</Box>
</ThemeProvider>
)
}
......
import {
Accordion,
AccordionDetails,
AccordionSummary,
Box,
Button,
Divider,
IconButton,
TextField,
Typography,
} from '@mui/material'
import ChevronRightIcon from '@mui/icons-material/ChevronRight'
import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'
import { useTheme, type Theme } from '@mui/material/styles'
interface Props {
handleJoinClick: () => void
}
const Lobby = ({ handleJoinClick }: Props) => {
const theme: Theme = useTheme()
return (
<Box
sx={{
backgroundColor: theme.palette.background.default,
height: '100%',
width: '100%',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
}}
>
<Box
width={'40%'}
borderRadius={'20px'}
sx={{ backgroundColor: '#262625', display: 'flex', flexDirection: 'column' }}
>
<Box sx={{ paddingY: '20px', backgroundColor: '#363739', borderRadius: '20px 20px 0 0' }}>
<Typography
variant='h4'
sx={{
color: theme.palette.common.white,
textAlign: 'center',
fontSize: '18px',
fontWeight: '600',
}}
>
Create or Join Room 👋
</Typography>
</Box>
<Box padding={'30px'} display={'flex'} flexDirection={'column'}>
<TextField
id='username'
label='Your Name'
variant='outlined'
sx={{
backgroundColor: '#3f434a',
borderRadius: '10px',
color: theme.palette.common.white,
marginBottom: '30px',
}}
inputProps={{
style: { color: theme.palette.common.white, borderColor: theme.palette.common.white },
}}
InputLabelProps={{ style: { color: theme.palette.common.white } }}
/>
<Box sx={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-between' }}>
<Box
sx={{
width: '50%',
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
}}
>
<TextField
id='room'
label='Room Name'
variant='outlined'
sx={{
backgroundColor: '#3f434a',
borderRadius: '10px',
color: theme.palette.common.white,
width: '75%',
}}
inputProps={{ style: { color: theme.palette.common.white, borderColor: 'white' } }}
InputLabelProps={{ style: { color: theme.palette.common.white } }}
/>
<IconButton
sx={{
backgroundColor: theme.palette.primary.main,
color: theme.palette.common.white,
width: '18%',
borderRadius: '10px',
height: '50px',
}}
aria-label='join room'
>
<ChevronRightIcon />
</IconButton>
</Box>
<Divider
orientation='vertical'
variant='middle'
sx={{ backgroundColor: theme.palette.common.white }}
flexItem
/>
<Box
sx={{ display: 'flex', justifyContent: 'end', alignItems: 'center', width: '35%' }}
>
<Button
sx={{
backgroundColor: theme.palette.primary.main,
color: theme.palette.common.white,
width: '90%',
borderRadius: '10px',
height: '50px',
}}
onClick={handleJoinClick}
>
Create Room
</Button>
</Box>
</Box>
<Box sx={{ marginTop: '30px' }}>
<Accordion sx={{ backgroundColor: '#3f434a' }}>
<AccordionSummary
expandIcon={<ArrowDropDownIcon sx={{ color: theme.palette.common.white }} />}
aria-controls='accord-content'
id='accord-header'
>
<Typography
variant='h6'
sx={{ color: theme.palette.common.white, fontSize: '15px' }}
>
Are you a differently-abled person? 🦸
</Typography>
</AccordionSummary>
<AccordionDetails>
<Box>sdsd</Box>
</AccordionDetails>
</Accordion>
</Box>
</Box>
</Box>
</Box>
)
}
export default Lobby
......@@ -2,6 +2,8 @@ import { useState } from 'react'
import AgoraRTC from 'agora-rtc-sdk-ng'
import { AgoraRTCProvider, useRTCClient } from 'agora-rtc-react'
import { AgoraManager } from '../../Services/AgoraManager/AgoraManager'
import Lobby from '../../Components/Lobby/Lobby'
import { Box } from '@mui/material'
const VideoCall = () => {
const agoraEngine = useRTCClient(AgoraRTC.createClient({ codec: 'vp8', mode: 'rtc' }))
......@@ -11,30 +13,22 @@ const VideoCall = () => {
setJoined(true)
}
const handleLeaveClick = () => {
setJoined(false)
}
const renderActionButton = () => {
return joined ? (
<button onClick={handleLeaveClick}>Leave</button>
) : (
<button onClick={handleJoinClick}>Join</button>
)
}
// const handleLeaveClick = () => {
// setJoined(false)
// }
return (
<div>
<h1>Get Started with Video Calling</h1>
{renderActionButton()}
{joined && (
<Box sx={{ height: '100vh', width: '100vw', overflow: 'hidden' }}>
{joined ? (
<AgoraRTCProvider client={agoraEngine}>
<AgoraManager>
<div>asas</div>
</AgoraManager>
</AgoraRTCProvider>
) : (
<Lobby handleJoinClick={handleJoinClick} />
)}
</div>
</Box>
)
}
......
......@@ -12,6 +12,8 @@ import {
import React, { createContext, useContext, useEffect } from 'react'
import type { IMicrophoneAudioTrack, ICameraVideoTrack } from 'agora-rtc-sdk-ng'
import { Box, Button, CircularProgress, Typography } from '@mui/material'
import { useTheme, type Theme } from '@mui/material/styles'
interface AgoraContextType {
localCameraTrack: ICameraVideoTrack | null
......@@ -38,19 +40,25 @@ export const useAgoraContext = () => {
}
export const AgoraManager = ({ children }: { children: React.ReactNode }) => {
const theme: Theme = useTheme()
const agoraEngine = useRTCClient()
const { isLoading: isLoadingCam, localCameraTrack } = useLocalCameraTrack()
const { isLoading: isLoadingMic, localMicrophoneTrack } = useLocalMicrophoneTrack()
const remoteUsers = useRemoteUsers()
usePublish([localMicrophoneTrack, localCameraTrack])
useJoin({
const {
data: uid,
error: joinError,
isConnected: isJoined,
isLoading: isJoining,
} = useJoin({
appid: '3af4648782de46ddbf90005f7a68f206',
channel: 'test',
token: null,
uid: null,
})
const { isLoading: isLoadingCam, localCameraTrack } = useLocalCameraTrack()
const { isLoading: isLoadingMic, localMicrophoneTrack } = useLocalMicrophoneTrack()
const remoteUsers = useRemoteUsers()
usePublish([localMicrophoneTrack, localCameraTrack])
useClientEvent(agoraEngine, 'user-joined', (user) => {
console.log('The user', user.uid, ' has joined the channel')
......@@ -71,27 +79,132 @@ export const AgoraManager = ({ children }: { children: React.ReactNode }) => {
}
}, [])
// Check if devices are still loading
const deviceLoading = isLoadingMic || isLoadingCam
if (deviceLoading) return <div>Loading devices...</div>
// Render the AgoraProvider and associated UI components
return (
<AgoraProvider localCameraTrack={localCameraTrack} localMicrophoneTrack={localMicrophoneTrack}>
{children}
<div id='videos'>
{/* Render the local video track */}
<div className='vid' style={{ height: 300, width: 600 }}>
<LocalVideoTrack track={localCameraTrack} play={true} />
</div>
{/* Render remote users' video and audio tracks */}
{remoteUsers.map((remoteUser) => (
<div className='vid' style={{ height: 300, width: 600 }} key={remoteUser.uid}>
<RemoteUser user={remoteUser} playVideo={true} playAudio={true} />
<Box sx={{ width: '100%', height: '100%', backgroundColor: theme.palette.background.default }}>
{isJoining ? (
<Box
sx={{
height: '100%',
width: '100%',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
}}
>
<Typography
variant='h3'
textAlign={'center'}
sx={{ fontSize: '18px', color: theme.palette.common.white, fontWeight: '600' }}
>
Joining the meeting...
</Typography>
</Box>
) : isLoadingCam || isLoadingMic ? (
<Box
sx={{
height: '100%',
width: '100%',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
}}
>
<Box
sx={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
}}
>
<CircularProgress
size={'md'}
sx={{
color: theme.palette.common.white,
height: '20px',
width: '20px',
marginRight: '20px',
}}
/>
<Typography
variant='h3'
textAlign={'center'}
sx={{ fontSize: '18px', color: theme.palette.common.white, fontWeight: '600' }}
>
Loading devices
</Typography>
</Box>
</Box>
) : isJoined ? (
<AgoraProvider
localCameraTrack={localCameraTrack}
localMicrophoneTrack={localMicrophoneTrack}
>
{children}
<div id='videos'>
{/* Render the local video track */}
<div className='vid' style={{ height: 300, width: 600 }}>
<LocalVideoTrack track={localCameraTrack} play={true} />
</div>
{/* Render remote users' video and audio tracks */}
{remoteUsers.map((remoteUser) => (
<div className='vid' style={{ height: 300, width: 600 }} key={remoteUser.uid}>
<RemoteUser user={remoteUser} playVideo={true} playAudio={true} />
</div>
))}
</div>
))}
</div>
</AgoraProvider>
</AgoraProvider>
) : joinError ? (
<Box
sx={{
height: '100%',
width: '100%',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
}}
>
<Box
sx={{
backgroundColor: '#262625',
borderRadius: '20px',
padding: '20px 40px',
width: '30%',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
}}
>
<Typography
variant='h3'
textAlign={'center'}
sx={{
fontSize: '18px',
color: theme.palette.common.white,
fontWeight: '600',
marginTop: '10px',
}}
>
Unexpected error occurred. Please try reloading the page. 😞
</Typography>
<Button
variant='text'
sx={{
color: theme.palette.primary.main,
borderRadius: '10px',
padding: '10px 20px',
marginTop: '10px',
fontWeight: '600',
}}
onClick={() => window.location.reload()}
>
Reload
</Button>
</Box>
</Box>
) : null}
</Box>
)
}
......
......@@ -2,6 +2,10 @@ import { createTheme } from '@mui/material/styles'
export const theme = createTheme({
palette: {
common: {
white: '#fff',
black: '#000',
},
primary: {
main: '#845695',
dark: '#19857b',
......@@ -14,7 +18,7 @@ export const theme = createTheme({
main: '#f44336',
},
background: {
default: '#fff',
default: '#1a1a1a',
},
},
})
body,
html {
margin: 0;
padding: 0;
}
body {
margin: 0;
padding: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu',
'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
-webkit-font-smoothing: antialiased;
......@@ -9,3 +16,10 @@ body {
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
}
#wrapper {
max-width: 940px;
margin: 0 auto;
padding: 0 5%;
clear: both;
}
......@@ -1314,10 +1314,10 @@
resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.8.1.tgz#c1ddb040429c6d21d38cc945fe75c818cfb68e17"
integrity sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==
"@emotion/react@^11.11.3":
version "11.11.3"
resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.11.3.tgz#96b855dc40a2a55f52a72f518a41db4f69c31a25"
integrity sha512-Cnn0kuq4DoONOMcnoVsTOR8E+AdnKFf//6kUWc4LCdnxj31pZWn7rIULd6Y7/Js1PiPHzn7SKCM9vB/jBni8eA==
"@emotion/react@^11.11.4":
version "11.11.4"
resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.11.4.tgz#3a829cac25c1f00e126408fab7f891f00ecc3c1d"
integrity sha512-t8AjMlF0gHpvvxk5mAtCqR4vmxiGHCeJBaQO6gncUSdklELOgtwjerNY2yuJNfwnc6vi16U/+uMF+afIawJ9iw==
dependencies:
"@babel/runtime" "^7.18.3"
"@emotion/babel-plugin" "^11.11.0"
......@@ -1763,35 +1763,42 @@
resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b"
integrity sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==
"@mui/base@5.0.0-beta.37":
version "5.0.0-beta.37"
resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-beta.37.tgz#0e7e0f28402391fcfbb05476d5acc6c4f2d817b1"
integrity sha512-/o3anbb+DeCng8jNsd3704XtmmLDZju1Fo8R2o7ugrVtPQ/QpcqddwKNzKPZwa0J5T8YNW3ZVuHyQgbTnQLisQ==
"@mui/base@5.0.0-beta.39":
version "5.0.0-beta.39"
resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-beta.39.tgz#9b8bab9d292e78721565197bead5050a79881194"
integrity sha512-puyUptF7VJ+9/dMIRLF+DLR21cWfvejsA6OnatfJfqFp8aMhya7xQtvYLEfCch6ahvFZvNC9FFEGGR+qkgFjUg==
dependencies:
"@babel/runtime" "^7.23.9"
"@floating-ui/react-dom" "^2.0.8"
"@mui/types" "^7.2.13"
"@mui/utils" "^5.15.11"
"@mui/utils" "^5.15.13"
"@popperjs/core" "^2.11.8"
clsx "^2.1.0"
prop-types "^15.8.1"
"@mui/core-downloads-tracker@^5.15.11":
version "5.15.11"
resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.11.tgz#dcaf6156880e81e4547237fb781700485453e964"
integrity sha512-JVrJ9Jo4gyU707ujnRzmE8ABBWpXd6FwL9GYULmwZRtfPg89ggXs/S3MStQkpJ1JRWfdLL6S5syXmgQGq5EDAw==
"@mui/core-downloads-tracker@^5.15.13":
version "5.15.13"
resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.13.tgz#f753bec8994b5defe4f62832a8a9ed14b5cb2d16"
integrity sha512-ERsk9EWpiitSiKnmUdFJGshtFk647l4p7r+mjRWe/F1l5kT1NTTKkaeDLcK3/lsy0udXjMgcG0bNwzbYBdDdhQ==
"@mui/material@^5.15.11":
version "5.15.11"
resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.15.11.tgz#4f42ee30443699ffb5836029c6d8464154eca603"
integrity sha512-FA3eEuEZaDaxgN3CgfXezMWbCZ4VCeU/sv0F0/PK5n42qIgsPVD6q+j71qS7/62sp6wRFMHtDMpXRlN+tT/7NA==
"@mui/icons-material@^5.15.13":
version "5.15.13"
resolved "https://registry.yarnpkg.com/@mui/icons-material/-/icons-material-5.15.13.tgz#8eabb372e64cb4dd5ef4f02df670543fa34bf360"
integrity sha512-I7CioMQKBPaKyGgcE9i8+1dgzAmox5a/0wZ0E9sIxm7PzG5KJZRRJkdK4oDT4HfYRGv61KjcHEeqH48pht1dvQ==
dependencies:
"@babel/runtime" "^7.23.9"
"@mui/material@^5.15.13":
version "5.15.13"
resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.15.13.tgz#ba4414d90075321d631a6ecfaad69b34b995cea0"
integrity sha512-E+QisOJcIzTTyeJ0o3lgYMcyrmCydb2S4cn9vTtGpIB9uR6fQ6La3dIGsXgYEGyeOB9YkWzQbNzYzvyODGEWKA==
dependencies:
"@babel/runtime" "^7.23.9"
"@mui/base" "5.0.0-beta.37"
"@mui/core-downloads-tracker" "^5.15.11"
"@mui/system" "^5.15.11"
"@mui/base" "5.0.0-beta.39"
"@mui/core-downloads-tracker" "^5.15.13"
"@mui/system" "^5.15.13"
"@mui/types" "^7.2.13"
"@mui/utils" "^5.15.11"
"@mui/utils" "^5.15.13"
"@types/react-transition-group" "^4.4.10"
clsx "^2.1.0"
csstype "^3.1.3"
......@@ -1799,13 +1806,13 @@
react-is "^18.2.0"
react-transition-group "^4.4.5"
"@mui/private-theming@^5.15.11":
version "5.15.11"
resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-5.15.11.tgz#4b9289b56b1ae0beb84e47bc9952f927b6e175ae"
integrity sha512-jY/696SnSxSzO1u86Thym7ky5T9CgfidU3NFJjguldqK4f3Z5S97amZ6nffg8gTD0HBjY9scB+4ekqDEUmxZOA==
"@mui/private-theming@^5.15.13":
version "5.15.13"
resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-5.15.13.tgz#04c8c8a6f2e6a67e4cc3aecb9375cc23df1a6f23"
integrity sha512-j5Z2pRi6talCunIRIzpQERSaHwLd5EPdHMwIKDVCszro1RAzRZl7WmH68IMCgQmJMeglr+FalqNuq048qptGAg==
dependencies:
"@babel/runtime" "^7.23.9"
"@mui/utils" "^5.15.11"
"@mui/utils" "^5.15.13"
prop-types "^15.8.1"
"@mui/styled-engine@^5.15.11":
......@@ -1818,16 +1825,16 @@
csstype "^3.1.3"
prop-types "^15.8.1"
"@mui/system@^5.15.11":
version "5.15.11"
resolved "https://registry.yarnpkg.com/@mui/system/-/system-5.15.11.tgz#19cf1974f82f1dd38be1f162034efecadd765733"
integrity sha512-9j35suLFq+MgJo5ktVSHPbkjDLRMBCV17NMBdEQurh6oWyGnLM4uhU4QGZZQ75o0vuhjJghOCA1jkO3+79wKsA==
"@mui/system@^5.15.13":
version "5.15.13"
resolved "https://registry.yarnpkg.com/@mui/system/-/system-5.15.13.tgz#dd86dbbebf92e4afdf0fa01afdae28598745ba4c"
integrity sha512-eHaX3sniZXNWkxX0lmcLxROhQ5La0HkOuF7zxbSdAoHUOk07gboQYmF6hSJ/VBFx/GLanIw67FMTn88vc8niLg==
dependencies:
"@babel/runtime" "^7.23.9"
"@mui/private-theming" "^5.15.11"
"@mui/private-theming" "^5.15.13"
"@mui/styled-engine" "^5.15.11"
"@mui/types" "^7.2.13"
"@mui/utils" "^5.15.11"
"@mui/utils" "^5.15.13"
clsx "^2.1.0"
csstype "^3.1.3"
prop-types "^15.8.1"
......@@ -1837,10 +1844,10 @@
resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.2.13.tgz#d1584912942f9dc042441ecc2d1452be39c666b8"
integrity sha512-qP9OgacN62s+l8rdDhSFRe05HWtLLJ5TGclC9I1+tQngbssu0m2dmFZs+Px53AcOs9fD7TbYd4gc9AXzVqO/+g==
"@mui/utils@^5.15.11":
version "5.15.11"
resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-5.15.11.tgz#a71804d6d6025783478fd1aca9afbf83d9b789c7"
integrity sha512-D6bwqprUa9Stf8ft0dcMqWyWDKEo7D+6pB1k8WajbqlYIRA8J8Kw9Ra7PSZKKePGBGWO+/xxrX1U8HpG/aXQCw==
"@mui/utils@^5.15.13":
version "5.15.13"
resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-5.15.13.tgz#4adfed6c585a6787f1f0d7d1fadb9ff0f7ddb2bd"
integrity sha512-qNlR9FLEhORC4zVZ3fzF48213EhP/92N71AcFbhHN73lPJjAbq9lUv+71P7uEdRHdrrOlm8+1zE8/OBy6MUqdg==
dependencies:
"@babel/runtime" "^7.23.9"
"@types/prop-types" "^15.7.11"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment