From 0f1187fb084caf621e35b3aad78c4fdeb3742cdf Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 14 Jun 2021 14:14:58 +0100 Subject: [PATCH 1/7] 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); From d4636a9450d59a56810d238d276846b71c3ae27c Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 14 Jun 2021 16:08:14 +0100 Subject: [PATCH 2/7] added notification styles --- dailyjs/shared/components/Button/Button.js | 26 +++++++++++ dailyjs/shared/components/Card/Card.js | 21 ++++++--- dailyjs/shared/components/Modal/Modal.js | 6 +-- .../WaitingRoom/WaitingRoomNotification.js | 43 +++++++++++-------- 4 files changed, 69 insertions(+), 27 deletions(-) diff --git a/dailyjs/shared/components/Button/Button.js b/dailyjs/shared/components/Button/Button.js index 64187d2..d4374b5 100644 --- a/dailyjs/shared/components/Button/Button.js +++ b/dailyjs/shared/components/Button/Button.js @@ -121,6 +121,28 @@ export const Button = forwardRef( 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 { 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%); } + .button.small { + height: 42px; + } + .button.medium-square { padding: 0px; height: 48px; diff --git a/dailyjs/shared/components/Card/Card.js b/dailyjs/shared/components/Card/Card.js index 607d6a5..2d66eee 100644 --- a/dailyjs/shared/components/Card/Card.js +++ b/dailyjs/shared/components/Card/Card.js @@ -2,8 +2,8 @@ import React from 'react'; import classNames from 'classnames'; import PropTypes from 'prop-types'; -export const Card = ({ children }) => ( -
+export const Card = ({ children, className }) => ( +
{children} -
+ ); }; From 22330eda7ef4366b90f47b5bd16ef6f02f3448cf Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 14 Jun 2021 16:20:01 +0100 Subject: [PATCH 3/7] styled modal --- .../WaitingRoom/WaitingParticipantRow.js | 57 +++++++++++++++++++ .../WaitingRoom/WaitingRoomModal.js | 41 +++++++++++-- .../shared/components/WaitingRoom/index.js | 1 + 3 files changed, 93 insertions(+), 6 deletions(-) create mode 100644 dailyjs/shared/components/WaitingRoom/WaitingParticipantRow.js diff --git a/dailyjs/shared/components/WaitingRoom/WaitingParticipantRow.js b/dailyjs/shared/components/WaitingRoom/WaitingParticipantRow.js new file mode 100644 index 0000000..f28f6ab --- /dev/null +++ b/dailyjs/shared/components/WaitingRoom/WaitingParticipantRow.js @@ -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 ( +
+ {participant.name} +
+ + +
+ + +
+ ); +}; + +WaitingParticipantRow.propTypes = { + participant: PropTypes.object, +}; + +export default WaitingParticipantRow; diff --git a/dailyjs/shared/components/WaitingRoom/WaitingRoomModal.js b/dailyjs/shared/components/WaitingRoom/WaitingRoomModal.js index 6873d9d..266898f 100644 --- a/dailyjs/shared/components/WaitingRoom/WaitingRoomModal.js +++ b/dailyjs/shared/components/WaitingRoom/WaitingRoomModal.js @@ -1,13 +1,42 @@ 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 '.'; -export const WaitingRoomModal = ({ onClose }) => ( - onClose()} actions={[]}> - Hello - -); +export const WaitingRoomModal = ({ onClose }) => { + const { denyAccess, grantAccess, waitingParticipants } = useWaitingRoom(); + + const handleAllowAllClick = (close) => { + grantAccess('all'); + close(); + }; + const handleDenyAllClick = (close) => { + denyAccess('all'); + close(); + }; + + return ( + onClose()} + actions={[ + , + , + ]} + > + {waitingParticipants.map((p) => ( + + ))} + + ); +}; WaitingRoomModal.propTypes = { onClose: PropTypes.func, diff --git a/dailyjs/shared/components/WaitingRoom/index.js b/dailyjs/shared/components/WaitingRoom/index.js index d7affb2..9deaecc 100644 --- a/dailyjs/shared/components/WaitingRoom/index.js +++ b/dailyjs/shared/components/WaitingRoom/index.js @@ -2,3 +2,4 @@ export { WaitingRoom as default } from './WaitingRoom'; export { WaitingRoom } from './WaitingRoom'; export { WaitingRoomModal } from './WaitingRoomModal'; export { WaitingRoomNotification } from './WaitingRoomNotification'; +export { WaitingParticipantRow } from './WaitingParticipantRow'; From 33fcde6d7f78eaeecc8ad763d340e5d06dd1ec42 Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 14 Jun 2021 19:45:57 +0100 Subject: [PATCH 4/7] fixed bug where previous username would stick --- dailyjs/basic-call/components/HairCheck/HairCheck.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dailyjs/basic-call/components/HairCheck/HairCheck.js b/dailyjs/basic-call/components/HairCheck/HairCheck.js index fc8a711..68d1fe1 100644 --- a/dailyjs/basic-call/components/HairCheck/HairCheck.js +++ b/dailyjs/basic-call/components/HairCheck/HairCheck.js @@ -54,7 +54,7 @@ export const HairCheck = () => { if (access?.level === ACCESS_STATE_LOBBY) { setWaiting(true); const { granted } = await callObject.requestAccess({ - name: localParticipant?.name, + name: userName, access: { level: 'full', }, From a1fd415c8dc36346e64317177d1b76275acbd049 Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 14 Jun 2021 19:50:52 +0100 Subject: [PATCH 5/7] removed localparticipant dep from haircheck joinCall --- dailyjs/basic-call/components/HairCheck/HairCheck.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dailyjs/basic-call/components/HairCheck/HairCheck.js b/dailyjs/basic-call/components/HairCheck/HairCheck.js index 68d1fe1..f29dab3 100644 --- a/dailyjs/basic-call/components/HairCheck/HairCheck.js +++ b/dailyjs/basic-call/components/HairCheck/HairCheck.js @@ -66,7 +66,7 @@ export const HairCheck = () => { console.log('❌ Access denied'); } } - }, [callObject, userName, localParticipant]); + }, [callObject, userName]); // Memoize the to prevent unnecassary re-renders const tileMemo = useDeepCompareMemo( From 61a6557e069b80c1c5f21284f3fff5895441c4b0 Mon Sep 17 00:00:00 2001 From: Jon Date: Tue, 15 Jun 2021 12:33:42 +0100 Subject: [PATCH 6/7] removed unnecassary callback --- dailyjs/basic-call/components/HairCheck/HairCheck.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dailyjs/basic-call/components/HairCheck/HairCheck.js b/dailyjs/basic-call/components/HairCheck/HairCheck.js index f29dab3..e1aabc5 100644 --- a/dailyjs/basic-call/components/HairCheck/HairCheck.js +++ b/dailyjs/basic-call/components/HairCheck/HairCheck.js @@ -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 { TextInput } from '@dailyjs/shared/components/Input'; import Loader from '@dailyjs/shared/components/Loader'; @@ -37,7 +37,7 @@ export const HairCheck = () => { callObject.startCamera(); }, [callObject]); - const joinCall = useCallback(async () => { + const joinCall = async () => { if (!callObject) return; // Disable join controls @@ -66,7 +66,7 @@ export const HairCheck = () => { console.log('❌ Access denied'); } } - }, [callObject, userName]); + }; // Memoize the to prevent unnecassary re-renders const tileMemo = useDeepCompareMemo( From 47dc85315cbd31e7818a6c96f3a2ba682f2afc06 Mon Sep 17 00:00:00 2001 From: Jon Date: Tue, 15 Jun 2021 12:36:39 +0100 Subject: [PATCH 7/7] removed unnecassary waiting room component --- .../components/WaitingRoom/WaitingRoom.js | 28 ------------------- .../WaitingRoom/WaitingRoomModal.js | 2 +- .../shared/components/WaitingRoom/index.js | 2 -- 3 files changed, 1 insertion(+), 31 deletions(-) delete mode 100644 dailyjs/shared/components/WaitingRoom/WaitingRoom.js diff --git a/dailyjs/shared/components/WaitingRoom/WaitingRoom.js b/dailyjs/shared/components/WaitingRoom/WaitingRoom.js deleted file mode 100644 index 5452545..0000000 --- a/dailyjs/shared/components/WaitingRoom/WaitingRoom.js +++ /dev/null @@ -1,28 +0,0 @@ -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 index 266898f..ce65263 100644 --- a/dailyjs/shared/components/WaitingRoom/WaitingRoomModal.js +++ b/dailyjs/shared/components/WaitingRoom/WaitingRoomModal.js @@ -3,7 +3,7 @@ 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 '.'; +import { WaitingParticipantRow } from './WaitingParticipantRow'; export const WaitingRoomModal = ({ onClose }) => { const { denyAccess, grantAccess, waitingParticipants } = useWaitingRoom(); diff --git a/dailyjs/shared/components/WaitingRoom/index.js b/dailyjs/shared/components/WaitingRoom/index.js index 9deaecc..4041549 100644 --- a/dailyjs/shared/components/WaitingRoom/index.js +++ b/dailyjs/shared/components/WaitingRoom/index.js @@ -1,5 +1,3 @@ -export { WaitingRoom as default } from './WaitingRoom'; -export { WaitingRoom } from './WaitingRoom'; export { WaitingRoomModal } from './WaitingRoomModal'; export { WaitingRoomNotification } from './WaitingRoomNotification'; export { WaitingParticipantRow } from './WaitingParticipantRow';