import React, { useState, useEffect, useMemo } from 'react'; import Button from '@custom/shared/components/Button'; import { DEVICE_MODAL } from '@custom/shared/components/DeviceSelectModal/DeviceSelectModal'; import { TextInput } from '@custom/shared/components/Input'; import Loader from '@custom/shared/components/Loader'; import MuteButton from '@custom/shared/components/MuteButton'; import Tile from '@custom/shared/components/Tile'; import { ACCESS_STATE_LOBBY } from '@custom/shared/constants'; import { useCallState } from '@custom/shared/contexts/CallProvider'; import { DEVICE_STATE_BLOCKED, DEVICE_STATE_NOT_FOUND, DEVICE_STATE_IN_USE, DEVICE_STATE_PENDING, useMediaDevices, } from '@custom/shared/contexts/MediaDeviceProvider'; import { useParticipants } from '@custom/shared/contexts/ParticipantsProvider'; import { useUIState } from '@custom/shared/contexts/UIStateProvider'; import IconSettings from '@custom/shared/icons/settings-sm.svg'; import { useDeepCompareMemo } from 'use-deep-compare'; /** * Hair check * --- * - Setup local media devices to see how you look / sound * - Toggle mute state of camera and mic * - Set user name and join call / request access */ export const HairCheck = () => { const { callObject } = useCallState(); const { localParticipant } = useParticipants(); const { camState, micState, camError, micError, isCamMuted, isMicMuted, } = useMediaDevices(); const { openModal } = useUIState(); const [waiting, setWaiting] = useState(false); const [joining, setJoining] = useState(false); const [denied, setDenied] = useState(); const [userName, setUserName] = useState( localStorage.getItem('PLUOT_PARTICIPANT_NAME') || '' ); // Initialise devices (even though we're not yet in a call) useEffect(() => { if (!callObject) return; callObject.startCamera(); }, [callObject]); const joinCall = async () => { if (!callObject) return; // Disable join controls setJoining(true); // Set the local participants name await callObject.setUserName(userName); // Async request access (this will block until the call owner responds to the knock) const { access } = callObject.accessState(); await callObject.join(); // If we we're in the lobby, wait for the owner to let us in if (access?.level === ACCESS_STATE_LOBBY) { setWaiting(true); const { granted } = await callObject.requestAccess({ name: userName, access: { level: 'full', }, }); if (granted) { // Note: we don't have to do any thing here as the call state will mutate console.log('👋 Access granted'); localStorage.setItem('PLUOT_PARTICIPANT_NAME', userName); } else { console.log('❌ Access denied'); setDenied(true); } } }; // Memoize the to prevent unnecessary re-renders const tileMemo = useDeepCompareMemo( () => ( ), [localParticipant] ); const isLoading = useMemo(() => camState === DEVICE_STATE_PENDING || micState === DEVICE_STATE_PENDING, [ camState, micState, ]); const hasError = useMemo(() => camError || micError, [camError, micError]); const camErrorVerbose = useMemo(() => { switch (camState) { case DEVICE_STATE_BLOCKED: return 'Camera blocked by user'; case DEVICE_STATE_NOT_FOUND: return 'Camera not found'; case DEVICE_STATE_IN_USE: return 'Device in use'; default: return 'unknown'; } }, [camState]); const micErrorVerbose = useMemo(() => { switch (micState) { case DEVICE_STATE_BLOCKED: return 'Microphone blocked by user'; case DEVICE_STATE_NOT_FOUND: return 'Microphone not found'; case DEVICE_STATE_IN_USE: return 'Microphone in use'; default: return 'unknown'; } }, [micState]); const showWaitingMessage = useMemo(() => { return (
{denied ? ( Call owner denied request ) : ( Waiting for host to grant access )}
); }, [denied]); const showUsernameInput = useMemo(() => { return ( <> setUserName(e.target.value)} /> ); }, [userName, joining, setUserName]); return ( <>
Daily.co

Ready to join?

{isLoading && (
Loading devices, please wait...
)} {hasError && ( <> {camError && (
{camErrorVerbose}
)} {micError && (
{micErrorVerbose}
)} )}
{tileMemo}
{ e.preventDefault(); joinCall(userName); }}>
{waiting ? showWaitingMessage : showUsernameInput}
); }; export default HairCheck;