import React, { useState, useEffect, useCallback, useMemo } from 'react'; import Button from '@dailyjs/shared/components/Button'; import { TextInput } from '@dailyjs/shared/components/Input'; import Loader from '@dailyjs/shared/components/Loader'; import { MuteButton } from '@dailyjs/shared/components/MuteButtons'; import { Tile } from '@dailyjs/shared/components/Tile'; import { ACCESS_STATE_LOBBY } from '@dailyjs/shared/constants'; 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 { DEVICE_STATE_BLOCKED, DEVICE_STATE_NOT_FOUND, DEVICE_STATE_IN_USE, DEVICE_STATE_PENDING, DEVICE_STATE_LOADING, DEVICE_STATE_GRANTED, } from '@dailyjs/shared/contexts/useDevices'; import IconSettings from '@dailyjs/shared/icons/settings-sm.svg'; import { useDeepCompareMemo } from 'use-deep-compare'; export const HairCheck = () => { const { callObject } = useCallState(); const { localParticipant } = useParticipants(); const { deviceState, camError, micError, isCamMuted, isMicMuted } = useMediaDevices(); const { showDeviceModal, setShowDeviceModal } = useUIState(); const [waiting, setWaiting] = useState(false); const [joining, setJoining] = useState(false); const [userName, setUserName] = useState(''); // Tell Daily to initialise devices (even through we're not yet in a call) useEffect(() => { if (!callObject) return; callObject.startCamera(); }, [callObject]); const joinCall = useCallback(async () => { if (!callObject) return; // Disable join controls setJoining(true); // Set the local participants name await callObject.setUserName(userName); // Attempt to jin the call 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: localParticipant?.name, access: { level: 'full', }, }); if (granted) { console.log('👋 Access granted'); } else { console.log('❌ Access denied'); } } }, [callObject, userName, localParticipant]); // Memoize the to prevent unnecassary re-renders const tileMemo = useDeepCompareMemo( () => ( ), [localParticipant] ); const isLoading = useMemo( () => deviceState === DEVICE_STATE_LOADING, [deviceState] ); const hasError = useMemo(() => { if ( !deviceState || [ DEVICE_STATE_LOADING, DEVICE_STATE_PENDING, DEVICE_STATE_GRANTED, ].includes(deviceState) ) { return false; } return true; }, [deviceState]); const camErrorVerbose = useMemo(() => { switch (camError) { 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'; } }, [camError]); return ( <> Ready to join? setShowDeviceModal(!showDeviceModal)} > {isLoading && ( Loading devices, please wait... )} {hasError && ( <> {camError && ( {camErrorVerbose} )} {micError && ( {micError} )} > )} {tileMemo} > ); }; export default HairCheck;