From 0f1187fb084caf621e35b3aad78c4fdeb3742cdf Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 14 Jun 2021 14:14:58 +0100 Subject: [PATCH] added initial waiting room components and contexts --- dailyjs/basic-call/components/Room/Room.js | 27 ++++- dailyjs/basic-call/pages/index.js | 5 +- .../components/WaitingRoom/WaitingRoom.js | 28 +++++ .../WaitingRoom/WaitingRoomModal.js | 16 +++ .../WaitingRoom/WaitingRoomNotification.js | 92 +++++++++++++++ .../shared/components/WaitingRoom/index.js | 4 + .../shared/contexts/WaitingRoomProvider.js | 111 ++++++++++++++++++ 7 files changed, 277 insertions(+), 6 deletions(-) create mode 100644 dailyjs/shared/components/WaitingRoom/WaitingRoom.js create mode 100644 dailyjs/shared/components/WaitingRoom/WaitingRoomModal.js create mode 100644 dailyjs/shared/components/WaitingRoom/WaitingRoomNotification.js create mode 100644 dailyjs/shared/components/WaitingRoom/index.js create mode 100644 dailyjs/shared/contexts/WaitingRoomProvider.js diff --git a/dailyjs/basic-call/components/Room/Room.js b/dailyjs/basic-call/components/Room/Room.js index ec98886..2e966fd 100644 --- a/dailyjs/basic-call/components/Room/Room.js +++ b/dailyjs/basic-call/components/Room/Room.js @@ -1,8 +1,14 @@ import React from 'react'; +import { + WaitingRoomModal, + WaitingRoomNotification, +} from '@dailyjs/shared/components/WaitingRoom'; import { useCallState } from '@dailyjs/shared/contexts/CallProvider'; import { useMediaDevices } from '@dailyjs/shared/contexts/MediaDeviceProvider'; +import { useParticipants } from '@dailyjs/shared/contexts/ParticipantsProvider'; import { useUIState } from '@dailyjs/shared/contexts/UIStateProvider'; -import { ReactComponent as IconAdd } from '@dailyjs/shared/icons/add-md.svg'; +import { useWaitingRoom } from '@dailyjs/shared/contexts/WaitingRoomProvider'; + 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 IconLeave } from '@dailyjs/shared/icons/leave-md.svg'; @@ -17,9 +23,11 @@ import { Header } from './Header'; import { Tray, TrayButton } from './Tray'; export const Room = ({ onLeave }) => { - const { callObject, addFakeParticipant } = useCallState(); + const { callObject } = useCallState(); + const { localParticipant } = useParticipants(); const { setShowDeviceModal } = useUIState(); const { isCamMuted, isMicMuted } = useMediaDevices(); + const { setShowModal, showModal } = useWaitingRoom(); const toggleCamera = (newState) => { if (!callObject) return false; @@ -39,6 +47,16 @@ export const Room = ({ onLeave }) => { + {/* Show waiting room notification & modal if call owner */} + {localParticipant?.isOwner && ( + <> + + {showModal && ( + setShowModal(false)} /> + )} + + )} + { setShowDeviceModal(true)}> - addFakeParticipant()}> - - + + diff --git a/dailyjs/basic-call/pages/index.js b/dailyjs/basic-call/pages/index.js index 3f0250e..b55c5ce 100644 --- a/dailyjs/basic-call/pages/index.js +++ b/dailyjs/basic-call/pages/index.js @@ -4,6 +4,7 @@ import { MediaDeviceProvider } from '@dailyjs/shared/contexts/MediaDeviceProvide import { ParticipantsProvider } from '@dailyjs/shared/contexts/ParticipantsProvider'; import { TracksProvider } from '@dailyjs/shared/contexts/TracksProvider'; import { UIStateProvider } from '@dailyjs/shared/contexts/UIStateProvider'; +import { WaitingRoomProvider } from '@dailyjs/shared/contexts/WaitingRoomProvider'; import PropTypes from 'prop-types'; import App from '../components/App'; import { Intro, NotConfigured } from '../components/Intro'; @@ -95,7 +96,9 @@ export default function Index({ domain, isConfigured = false }) { - + + + diff --git a/dailyjs/shared/components/WaitingRoom/WaitingRoom.js b/dailyjs/shared/components/WaitingRoom/WaitingRoom.js new file mode 100644 index 0000000..5452545 --- /dev/null +++ b/dailyjs/shared/components/WaitingRoom/WaitingRoom.js @@ -0,0 +1,28 @@ +import React, { useEffect } from 'react'; +import { useCallState } from '@dailyjs/shared/contexts/CallProvider'; + +export const WaitingRoom = () => { + const { callObject } = useCallState(); + + /** + * Show notification when waiting participants change. + */ + useEffect(() => { + const handleWaitingParticipantAdded = () => { + console.log('people are waiting'); + // setShowNotification(Object.keys(daily.waitingParticipants()).length > 0); + }; + + callObject.on('waiting-participant-added', handleWaitingParticipantAdded); + return () => { + callObject.off( + 'waiting-participant-added', + handleWaitingParticipantAdded + ); + }; + }, [callObject]); + + return
Waiting Room
; +}; + +export default WaitingRoom; diff --git a/dailyjs/shared/components/WaitingRoom/WaitingRoomModal.js b/dailyjs/shared/components/WaitingRoom/WaitingRoomModal.js new file mode 100644 index 0000000..6873d9d --- /dev/null +++ b/dailyjs/shared/components/WaitingRoom/WaitingRoomModal.js @@ -0,0 +1,16 @@ +import React from 'react'; +import Modal from '@dailyjs/shared/components/Modal'; + +import PropTypes from 'prop-types'; + +export const WaitingRoomModal = ({ onClose }) => ( + onClose()} actions={[]}> + Hello + +); + +WaitingRoomModal.propTypes = { + onClose: PropTypes.func, +}; + +export default WaitingRoomModal; diff --git a/dailyjs/shared/components/WaitingRoom/WaitingRoomNotification.js b/dailyjs/shared/components/WaitingRoom/WaitingRoomNotification.js new file mode 100644 index 0000000..06e8688 --- /dev/null +++ b/dailyjs/shared/components/WaitingRoom/WaitingRoomNotification.js @@ -0,0 +1,92 @@ +import React, { useEffect, useState } from 'react'; + +import { useCallState } from '../../contexts/CallProvider'; +import { useWaitingRoom } from '../../contexts/WaitingRoomProvider'; +import { Button } from '../Button'; + +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 ( +
+ <> + {hasMultiplePeopleWaiting ? ( + + ) : ( + + )} + + + + + +
+ ); +}; + +export default WaitingRoomNotification; diff --git a/dailyjs/shared/components/WaitingRoom/index.js b/dailyjs/shared/components/WaitingRoom/index.js new file mode 100644 index 0000000..d7affb2 --- /dev/null +++ b/dailyjs/shared/components/WaitingRoom/index.js @@ -0,0 +1,4 @@ +export { WaitingRoom as default } from './WaitingRoom'; +export { WaitingRoom } from './WaitingRoom'; +export { WaitingRoomModal } from './WaitingRoomModal'; +export { WaitingRoomNotification } from './WaitingRoomNotification'; diff --git a/dailyjs/shared/contexts/WaitingRoomProvider.js b/dailyjs/shared/contexts/WaitingRoomProvider.js new file mode 100644 index 0000000..978036a --- /dev/null +++ b/dailyjs/shared/contexts/WaitingRoomProvider.js @@ -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 ( + + {children} + + ); +}; + +WaitingRoomProvider.propTypes = { + children: PropTypes.node, +}; + +export const useWaitingRoom = () => useContext(WaitingRoomContext);