Merge branch 'main' of github.com:daily-demos/examples into dailyjs/basic-call/devrel-feedback
This commit is contained in:
commit
4e1d032c96
|
|
@ -1,7 +1,17 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
WaitingRoomModal,
|
||||||
|
WaitingRoomNotification,
|
||||||
|
} from '@dailyjs/shared/components/WaitingRoom';
|
||||||
import { useCallState } from '@dailyjs/shared/contexts/CallProvider';
|
import { useCallState } from '@dailyjs/shared/contexts/CallProvider';
|
||||||
import { useMediaDevices } from '@dailyjs/shared/contexts/MediaDeviceProvider';
|
import { useMediaDevices } from '@dailyjs/shared/contexts/MediaDeviceProvider';
|
||||||
|
import { useParticipants } from '@dailyjs/shared/contexts/ParticipantsProvider';
|
||||||
import { useUIState } from '@dailyjs/shared/contexts/UIStateProvider';
|
import { useUIState } from '@dailyjs/shared/contexts/UIStateProvider';
|
||||||
|
<<<<<<< HEAD
|
||||||
|
import { useWaitingRoom } from '@dailyjs/shared/contexts/WaitingRoomProvider';
|
||||||
|
|
||||||
|
=======
|
||||||
|
>>>>>>> e47ada8fa4389bbfbeb7c97a6d80731a33d24b01
|
||||||
import { ReactComponent as IconCameraOff } from '@dailyjs/shared/icons/camera-off-md.svg';
|
import { ReactComponent as IconCameraOff } from '@dailyjs/shared/icons/camera-off-md.svg';
|
||||||
import { ReactComponent as IconCameraOn } from '@dailyjs/shared/icons/camera-on-md.svg';
|
import { ReactComponent as IconCameraOn } from '@dailyjs/shared/icons/camera-on-md.svg';
|
||||||
import { ReactComponent as IconLeave } from '@dailyjs/shared/icons/leave-md.svg';
|
import { ReactComponent as IconLeave } from '@dailyjs/shared/icons/leave-md.svg';
|
||||||
|
|
@ -19,6 +29,7 @@ export const Room = ({ onLeave }) => {
|
||||||
const { callObject } = useCallState();
|
const { callObject } = useCallState();
|
||||||
const { setShowDeviceModal } = useUIState();
|
const { setShowDeviceModal } = useUIState();
|
||||||
const { isCamMuted, isMicMuted } = useMediaDevices();
|
const { isCamMuted, isMicMuted } = useMediaDevices();
|
||||||
|
const { setShowModal, showModal } = useWaitingRoom();
|
||||||
|
|
||||||
const toggleCamera = (newState) => {
|
const toggleCamera = (newState) => {
|
||||||
if (!callObject) return false;
|
if (!callObject) return false;
|
||||||
|
|
@ -38,6 +49,16 @@ export const Room = ({ onLeave }) => {
|
||||||
<VideoGrid />
|
<VideoGrid />
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
{/* Show waiting room notification & modal if call owner */}
|
||||||
|
{localParticipant?.isOwner && (
|
||||||
|
<>
|
||||||
|
<WaitingRoomNotification />
|
||||||
|
{showModal && (
|
||||||
|
<WaitingRoomModal onClose={() => setShowModal(false)} />
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
<Tray>
|
<Tray>
|
||||||
<TrayButton
|
<TrayButton
|
||||||
label="Camera"
|
label="Camera"
|
||||||
|
|
@ -58,6 +79,7 @@ export const Room = ({ onLeave }) => {
|
||||||
</TrayButton>
|
</TrayButton>
|
||||||
|
|
||||||
<span className="divider" />
|
<span className="divider" />
|
||||||
|
|
||||||
<TrayButton label="Leave" onClick={onLeave} orange>
|
<TrayButton label="Leave" onClick={onLeave} orange>
|
||||||
<IconLeave />
|
<IconLeave />
|
||||||
</TrayButton>
|
</TrayButton>
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { MediaDeviceProvider } from '@dailyjs/shared/contexts/MediaDeviceProvide
|
||||||
import { ParticipantsProvider } from '@dailyjs/shared/contexts/ParticipantsProvider';
|
import { ParticipantsProvider } from '@dailyjs/shared/contexts/ParticipantsProvider';
|
||||||
import { TracksProvider } from '@dailyjs/shared/contexts/TracksProvider';
|
import { TracksProvider } from '@dailyjs/shared/contexts/TracksProvider';
|
||||||
import { UIStateProvider } from '@dailyjs/shared/contexts/UIStateProvider';
|
import { UIStateProvider } from '@dailyjs/shared/contexts/UIStateProvider';
|
||||||
|
import { WaitingRoomProvider } from '@dailyjs/shared/contexts/WaitingRoomProvider';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import App from '../components/App';
|
import App from '../components/App';
|
||||||
import { Intro, NotConfigured } from '../components/Intro';
|
import { Intro, NotConfigured } from '../components/Intro';
|
||||||
|
|
@ -95,7 +96,9 @@ export default function Index({ domain, isConfigured = false }) {
|
||||||
<ParticipantsProvider>
|
<ParticipantsProvider>
|
||||||
<TracksProvider>
|
<TracksProvider>
|
||||||
<MediaDeviceProvider>
|
<MediaDeviceProvider>
|
||||||
<App />
|
<WaitingRoomProvider>
|
||||||
|
<App />
|
||||||
|
</WaitingRoomProvider>
|
||||||
</MediaDeviceProvider>
|
</MediaDeviceProvider>
|
||||||
</TracksProvider>
|
</TracksProvider>
|
||||||
</ParticipantsProvider>
|
</ParticipantsProvider>
|
||||||
|
|
|
||||||
|
|
@ -121,6 +121,28 @@ export const Button = forwardRef(
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.button.error {
|
||||||
|
background: var(--secondary-default);
|
||||||
|
border-color: var(--secondary-default);
|
||||||
|
}
|
||||||
|
.button.error:focus {
|
||||||
|
box-shadow: 0 0 0px 3px ${hexa(theme.secondary.default, 0.35)};
|
||||||
|
}
|
||||||
|
.button.error:hover {
|
||||||
|
border-color: var(--secondary-dark);
|
||||||
|
}
|
||||||
|
|
||||||
|
.button.success {
|
||||||
|
background: var(--green-default);
|
||||||
|
border-color: var(--green-default);
|
||||||
|
}
|
||||||
|
.button.success:focus {
|
||||||
|
box-shadow: 0 0 0px 3px ${hexa(theme.green.default, 0.35)};
|
||||||
|
}
|
||||||
|
.button.success:hover {
|
||||||
|
border-color: var(--green-dark);
|
||||||
|
}
|
||||||
|
|
||||||
.button.shadow {
|
.button.shadow {
|
||||||
box-shadow: 0 0 4px 0 rgb(0 0 0 / 8%), 0 4px 4px 0 rgb(0 0 0 / 4%);
|
box-shadow: 0 0 4px 0 rgb(0 0 0 / 8%), 0 4px 4px 0 rgb(0 0 0 / 4%);
|
||||||
}
|
}
|
||||||
|
|
@ -129,6 +151,10 @@ export const Button = forwardRef(
|
||||||
box-shadow: 0 0 4px 0 rgb(0 0 0 / 8%), 0 4px 4px 0 rgb(0 0 0 / 12%);
|
box-shadow: 0 0 4px 0 rgb(0 0 0 / 8%), 0 4px 4px 0 rgb(0 0 0 / 12%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.button.small {
|
||||||
|
height: 42px;
|
||||||
|
}
|
||||||
|
|
||||||
.button.medium-square {
|
.button.medium-square {
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
height: 48px;
|
height: 48px;
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ import React from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
export const Card = ({ children }) => (
|
export const Card = ({ children, className }) => (
|
||||||
<div className="card">
|
<div className={classNames('card', className)}>
|
||||||
{children}
|
{children}
|
||||||
<style jsx>{`
|
<style jsx>{`
|
||||||
background: white;
|
background: white;
|
||||||
|
|
@ -16,6 +16,7 @@ export const Card = ({ children }) => (
|
||||||
|
|
||||||
Card.propTypes = {
|
Card.propTypes = {
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
|
className: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CardHeader = ({ children }) => (
|
export const CardHeader = ({ children }) => (
|
||||||
|
|
@ -53,14 +54,19 @@ CardBody.propTypes = {
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CardFooter = ({ children, divider = false }) => (
|
export const CardFooter = ({ children, divider = false, flex = false }) => (
|
||||||
<footer className={classNames('card-footer', { divider })}>
|
<footer className={classNames('card-footer', { divider, flex })}>
|
||||||
{children}
|
{children}
|
||||||
<style jsx>{`
|
<style jsx>{`
|
||||||
display: flex;
|
.card-footer {
|
||||||
margin: 0;
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
&.divider {
|
:global(.card-footer.flex > *) {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-footer.divider {
|
||||||
border-top: 1px solid var(--gray-light);
|
border-top: 1px solid var(--gray-light);
|
||||||
padding-top: var(--spacing-md);
|
padding-top: var(--spacing-md);
|
||||||
}
|
}
|
||||||
|
|
@ -70,6 +76,7 @@ export const CardFooter = ({ children, divider = false }) => (
|
||||||
CardFooter.propTypes = {
|
CardFooter.propTypes = {
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
divider: PropTypes.bool,
|
divider: PropTypes.bool,
|
||||||
|
flex: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Card;
|
export default Card;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useState, useEffect, useCallback, useMemo } from 'react';
|
import React, { useState, useEffect, useMemo } from 'react';
|
||||||
import Button from '@dailyjs/shared/components/Button';
|
import Button from '@dailyjs/shared/components/Button';
|
||||||
import { TextInput } from '@dailyjs/shared/components/Input';
|
import { TextInput } from '@dailyjs/shared/components/Input';
|
||||||
import Loader from '@dailyjs/shared/components/Loader';
|
import Loader from '@dailyjs/shared/components/Loader';
|
||||||
|
|
@ -37,7 +37,7 @@ export const HairCheck = () => {
|
||||||
callObject.startCamera();
|
callObject.startCamera();
|
||||||
}, [callObject]);
|
}, [callObject]);
|
||||||
|
|
||||||
const joinCall = useCallback(async () => {
|
const joinCall = async () => {
|
||||||
if (!callObject) return;
|
if (!callObject) return;
|
||||||
|
|
||||||
// Disable join controls
|
// Disable join controls
|
||||||
|
|
@ -54,7 +54,7 @@ export const HairCheck = () => {
|
||||||
if (access?.level === ACCESS_STATE_LOBBY) {
|
if (access?.level === ACCESS_STATE_LOBBY) {
|
||||||
setWaiting(true);
|
setWaiting(true);
|
||||||
const { granted } = await callObject.requestAccess({
|
const { granted } = await callObject.requestAccess({
|
||||||
name: localParticipant?.name,
|
name: userName,
|
||||||
access: {
|
access: {
|
||||||
level: 'full',
|
level: 'full',
|
||||||
},
|
},
|
||||||
|
|
@ -66,7 +66,7 @@ export const HairCheck = () => {
|
||||||
console.log('❌ Access denied');
|
console.log('❌ Access denied');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [callObject, userName, localParticipant]);
|
};
|
||||||
|
|
||||||
// Memoize the to prevent unnecassary re-renders
|
// Memoize the to prevent unnecassary re-renders
|
||||||
const tileMemo = useDeepCompareMemo(
|
const tileMemo = useDeepCompareMemo(
|
||||||
|
|
|
||||||
|
|
@ -121,9 +121,9 @@ export const Modal = ({
|
||||||
|
|
||||||
.backdrop .modal :global(.card-footer) {
|
.backdrop .modal :global(.card-footer) {
|
||||||
border-top: 0px;
|
border-top: 0px;
|
||||||
display: grid;
|
display: flex;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
|
column-gap: var(--spacing-xs);
|
||||||
grid-column-gap: var(--spacing-sm);
|
margin-top: var(--spacing-sm);
|
||||||
}
|
}
|
||||||
|
|
||||||
.isVisible {
|
.isVisible {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { useWaitingRoom } from '../../contexts/WaitingRoomProvider';
|
||||||
|
import { Button } from '../Button';
|
||||||
|
|
||||||
|
export const WaitingParticipantRow = ({ participant }) => {
|
||||||
|
const { grantAccess, denyAccess } = useWaitingRoom();
|
||||||
|
|
||||||
|
const handleAllowClick = () => {
|
||||||
|
grantAccess(participant.id);
|
||||||
|
};
|
||||||
|
const handleDenyClick = () => {
|
||||||
|
denyAccess(participant.id);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="waiting-room-row">
|
||||||
|
{participant.name}
|
||||||
|
<div className="actions">
|
||||||
|
<Button onClick={handleAllowClick} size="small" variant="success">
|
||||||
|
Allow
|
||||||
|
</Button>
|
||||||
|
<Button onClick={handleDenyClick} size="small" variant="error">
|
||||||
|
Deny
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style jsx>{`
|
||||||
|
.waiting-room-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
border-bottom: 1px solid var(--gray-light);
|
||||||
|
padding-bottom: var(--spacing-xxs);
|
||||||
|
margin-bottom: var(--spacing-xxs);
|
||||||
|
}
|
||||||
|
|
||||||
|
.waiting-room-row:last-child {
|
||||||
|
border-bottom: 0px;
|
||||||
|
padding-bottom: 0px;
|
||||||
|
margin-bottom: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--spacing-xxs);
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
WaitingParticipantRow.propTypes = {
|
||||||
|
participant: PropTypes.object,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default WaitingParticipantRow;
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
import React from 'react';
|
||||||
|
import Modal from '@dailyjs/shared/components/Modal';
|
||||||
|
import { useWaitingRoom } from '@dailyjs/shared/contexts/WaitingRoomProvider';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Button } from '../Button';
|
||||||
|
import { WaitingParticipantRow } from './WaitingParticipantRow';
|
||||||
|
|
||||||
|
export const WaitingRoomModal = ({ onClose }) => {
|
||||||
|
const { denyAccess, grantAccess, waitingParticipants } = useWaitingRoom();
|
||||||
|
|
||||||
|
const handleAllowAllClick = (close) => {
|
||||||
|
grantAccess('all');
|
||||||
|
close();
|
||||||
|
};
|
||||||
|
const handleDenyAllClick = (close) => {
|
||||||
|
denyAccess('all');
|
||||||
|
close();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
title="Waiting room"
|
||||||
|
isOpen
|
||||||
|
onClose={() => onClose()}
|
||||||
|
actions={[
|
||||||
|
<Button fullWidth onClick={handleAllowAllClick} variant="success">
|
||||||
|
Allow all
|
||||||
|
</Button>,
|
||||||
|
<Button fullWidth onClick={handleDenyAllClick} variant="error">
|
||||||
|
Deny all
|
||||||
|
</Button>,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{waitingParticipants.map((p) => (
|
||||||
|
<WaitingParticipantRow participant={p} />
|
||||||
|
))}
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
WaitingRoomModal.propTypes = {
|
||||||
|
onClose: PropTypes.func,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default WaitingRoomModal;
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
import { useCallState } from '../../contexts/CallProvider';
|
||||||
|
import { useWaitingRoom } from '../../contexts/WaitingRoomProvider';
|
||||||
|
import { Button } from '../Button';
|
||||||
|
import { Card, CardBody, CardFooter } from '../Card';
|
||||||
|
|
||||||
|
export const WaitingRoomNotification = () => {
|
||||||
|
const { callObject } = useCallState();
|
||||||
|
const {
|
||||||
|
denyAccess,
|
||||||
|
grantAccess,
|
||||||
|
showModal,
|
||||||
|
setShowModal,
|
||||||
|
waitingParticipants,
|
||||||
|
} = useWaitingRoom();
|
||||||
|
const [showNotification, setShowNotification] = useState(false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show notification when waiting participants change.
|
||||||
|
*/
|
||||||
|
useEffect(() => {
|
||||||
|
if (showModal) return false;
|
||||||
|
|
||||||
|
const handleWaitingParticipantAdded = () => {
|
||||||
|
setShowNotification(
|
||||||
|
Object.keys(callObject.waitingParticipants()).length > 0
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
callObject.on('waiting-participant-added', handleWaitingParticipantAdded);
|
||||||
|
return () => {
|
||||||
|
callObject.off(
|
||||||
|
'waiting-participant-added',
|
||||||
|
handleWaitingParticipantAdded
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}, [callObject, showModal]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide notification when people panel is opened.
|
||||||
|
*/
|
||||||
|
useEffect(() => {
|
||||||
|
if (showModal) setShowNotification(false);
|
||||||
|
}, [showModal]);
|
||||||
|
|
||||||
|
if (!showNotification || waitingParticipants.length === 0) return null;
|
||||||
|
|
||||||
|
const hasMultiplePeopleWaiting = waitingParticipants.length > 1;
|
||||||
|
|
||||||
|
const handleViewAllClick = () => {
|
||||||
|
setShowModal(true);
|
||||||
|
setShowNotification(false);
|
||||||
|
};
|
||||||
|
const handleAllowClick = () => {
|
||||||
|
grantAccess(waitingParticipants[0].id);
|
||||||
|
};
|
||||||
|
const handleDenyClick = () => {
|
||||||
|
denyAccess(hasMultiplePeopleWaiting ? 'all' : waitingParticipants[0].id);
|
||||||
|
};
|
||||||
|
// const handleClose = () => setShowNotification(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card className="waiting-room-notification">
|
||||||
|
<CardBody>
|
||||||
|
{hasMultiplePeopleWaiting
|
||||||
|
? `${waitingParticipants.length} people would like to join the call`
|
||||||
|
: `${waitingParticipants[0].name} would like to join the call`}
|
||||||
|
</CardBody>
|
||||||
|
<CardFooter>
|
||||||
|
{hasMultiplePeopleWaiting ? (
|
||||||
|
<Button onClick={handleViewAllClick} size="small" variant="success">
|
||||||
|
View all
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
<Button onClick={handleAllowClick} size="small" variant="success">
|
||||||
|
Allow
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<Button onClick={handleDenyClick} size="small" variant="error">
|
||||||
|
{hasMultiplePeopleWaiting ? 'Deny All' : 'Deny'}
|
||||||
|
</Button>
|
||||||
|
</CardFooter>
|
||||||
|
<style jsx>{`
|
||||||
|
:global(.waiting-room-notification) {
|
||||||
|
position: absolute;
|
||||||
|
right: var(--spacing-sm);
|
||||||
|
top: var(--spacing-sm);
|
||||||
|
z-index: 999;
|
||||||
|
box-shadow: var(--shadow-depth-2);
|
||||||
|
}
|
||||||
|
:global(.waiting-room-notification .card-footer) {
|
||||||
|
display: flex;
|
||||||
|
column-gap: var(--spacing-xxs);
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default WaitingRoomNotification;
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
export { WaitingRoomModal } from './WaitingRoomModal';
|
||||||
|
export { WaitingRoomNotification } from './WaitingRoomNotification';
|
||||||
|
export { WaitingParticipantRow } from './WaitingParticipantRow';
|
||||||
|
|
@ -0,0 +1,111 @@
|
||||||
|
import React, {
|
||||||
|
createContext,
|
||||||
|
useCallback,
|
||||||
|
useContext,
|
||||||
|
useEffect,
|
||||||
|
useState,
|
||||||
|
} from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { useCallState } from './CallProvider';
|
||||||
|
|
||||||
|
const WaitingRoomContext = createContext(null);
|
||||||
|
|
||||||
|
export const WaitingRoomProvider = ({ children }) => {
|
||||||
|
const { callObject } = useCallState();
|
||||||
|
const [waitingParticipants, setWaitingParticipants] = useState([]);
|
||||||
|
const [showModal, setShowModal] = useState(false);
|
||||||
|
|
||||||
|
const handleWaitingParticipantEvent = useCallback(() => {
|
||||||
|
if (!callObject) return;
|
||||||
|
|
||||||
|
const waiting = Object.entries(callObject.waitingParticipants());
|
||||||
|
|
||||||
|
console.log(`🚪 ${waiting.length} participant(s) waiting for access`);
|
||||||
|
|
||||||
|
setWaitingParticipants((wp) =>
|
||||||
|
waiting.map(([pid, p]) => {
|
||||||
|
const prevWP = wp.find(({ id }) => id === pid);
|
||||||
|
return {
|
||||||
|
...p,
|
||||||
|
joinDate: prevWP?.joinDate ?? new Date(),
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}, [callObject]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (waitingParticipants.length === 0) {
|
||||||
|
setShowModal(false);
|
||||||
|
}
|
||||||
|
}, [waitingParticipants]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!callObject) return false;
|
||||||
|
|
||||||
|
console.log('🚪 Waiting room provider listening for requests');
|
||||||
|
|
||||||
|
const events = [
|
||||||
|
'waiting-participant-added',
|
||||||
|
'waiting-participant-updated',
|
||||||
|
'waiting-participant-removed',
|
||||||
|
];
|
||||||
|
|
||||||
|
events.forEach((e) => callObject.on(e, handleWaitingParticipantEvent));
|
||||||
|
|
||||||
|
return () =>
|
||||||
|
events.forEach((e) => callObject.off(e, handleWaitingParticipantEvent));
|
||||||
|
}, [callObject, handleWaitingParticipantEvent]);
|
||||||
|
|
||||||
|
const updateWaitingParticipant = (id, grantRequestedAccess) => {
|
||||||
|
if (!waitingParticipants.some((p) => p.id === id)) return;
|
||||||
|
callObject.updateWaitingParticipant(id, {
|
||||||
|
grantRequestedAccess,
|
||||||
|
});
|
||||||
|
setWaitingParticipants((wp) => wp.filter((p) => p.id !== id));
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateAllWaitingParticipants = (grantRequestedAccess) => {
|
||||||
|
if (!waitingParticipants.length) return;
|
||||||
|
callObject.updateWaitingParticipants({
|
||||||
|
'*': {
|
||||||
|
grantRequestedAccess,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
setWaitingParticipants([]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const grantAccess = (id = 'all') => {
|
||||||
|
if (id === 'all') {
|
||||||
|
updateAllWaitingParticipants(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
updateWaitingParticipant(id, true);
|
||||||
|
};
|
||||||
|
const denyAccess = (id = 'all') => {
|
||||||
|
if (id === 'all') {
|
||||||
|
updateAllWaitingParticipants(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
updateWaitingParticipant(id, false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WaitingRoomContext.Provider
|
||||||
|
value={{
|
||||||
|
denyAccess,
|
||||||
|
grantAccess,
|
||||||
|
setShowModal,
|
||||||
|
showModal,
|
||||||
|
waitingParticipants,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</WaitingRoomContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
WaitingRoomProvider.propTypes = {
|
||||||
|
children: PropTypes.node,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useWaitingRoom = () => useContext(WaitingRoomContext);
|
||||||
Loading…
Reference in New Issue