Merge pull request #8 from daily-demos/dailyjs/basic-call/devrel-feedback

created useCallUI hook to simplify cross demo call loop
This commit is contained in:
Jon Taylor 2021-06-16 14:11:09 +01:00 committed by GitHub
commit 32bc7de155
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 169 additions and 98 deletions

View File

@ -1,77 +1,23 @@
import React, { useCallback, useEffect, useMemo } from 'react';
import Loader from '@dailyjs/shared/components/Loader';
import React, { useMemo } from 'react';
import { useCallState } from '@dailyjs/shared/contexts/CallProvider';
import {
CALL_STATE_ENDED,
CALL_STATE_JOINED,
CALL_STATE_JOINING,
CALL_STATE_LOBBY,
CALL_STATE_NOT_FOUND,
CALL_STATE_NOT_BEFORE,
CALL_STATE_READY,
CALL_STATE_REDIRECTING,
} from '@dailyjs/shared/contexts/useCallMachine';
import { useCallUI } from '@dailyjs/shared/hooks/useCallUI';
import { useRouter } from 'next/router';
import HairCheck from '../HairCheck';
import MessageCard from '../MessageCard';
import Room from '../Room';
import { Modals } from './Modals';
export const App = () => {
const { state, leave } = useCallState();
const router = useRouter();
useEffect(() => {
console.log(`%c🔀 App state changed: ${state}`, `color: gray;`);
}, [state]);
const renderState = useCallback(() => {
// Show loader when state is undefined or ready to join
if (!state || [CALL_STATE_READY, CALL_STATE_JOINING].includes(state)) {
return <Loader />;
}
// Update the UI based on the state of our call
switch (state) {
case CALL_STATE_NOT_FOUND:
router.replace('/not-found');
return null;
case CALL_STATE_NOT_BEFORE:
return (
<MessageCard error header="Cannot join before owner">
This room has `nbf` set, meaning you cannot join the call before the
owner
</MessageCard>
);
case CALL_STATE_LOBBY:
return <HairCheck />;
case CALL_STATE_JOINED:
return <Room onLeave={() => leave()} />;
case CALL_STATE_REDIRECTING:
case CALL_STATE_ENDED:
// Note: you could set a manual redirect here but we'll show just an exit screen
return (
<MessageCard onBack={() => window.location.reload()}>
You have left the call. We hope you had fun!
</MessageCard>
);
default:
break;
}
return (
<MessageCard error header="An error occured">
An unknown error has occured in the call loop. This should not happen!
</MessageCard>
);
}, [leave, router, state]);
const componentForState = useCallUI({
state,
room: () => <Room onLeave={() => leave()} />,
});
// Memoize children to avoid unnecassary renders from HOC
return useMemo(
() => (
<div className="app">
{renderState()}
{componentForState()}
<Modals />
<style jsx>{`
color: white;
@ -86,7 +32,7 @@ export const App = () => {
`}</style>
</div>
),
[renderState]
[componentForState]
);
};

View File

@ -10,15 +10,15 @@ const AudioItem = React.memo(({ participant }) => {
useEffect(() => {
if (!audioTrack || !audioRef.current) return;
// sanity check to make sure this is an audio track
if (audioTrack && audioTrack !== 'audio') return;
// quick sanity to check to make sure this is an audio track...
if (audioTrack.kind !== 'audio') return;
audioRef.current.srcObject = new MediaStream([audioTrack]);
}, [audioTrack]);
return (
<>
<audio autoPlay playsInline ref={audioRef}>
<audio autoPlay playsInline ref={audioRef} id={participant.name}>
<track kind="captions" />
</audio>
</>

View File

@ -7,11 +7,7 @@ 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';
<<<<<<< HEAD
import { useWaitingRoom } from '@dailyjs/shared/contexts/WaitingRoomProvider';
=======
>>>>>>> e47ada8fa4389bbfbeb7c97a6d80731a33d24b01
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';
@ -30,6 +26,7 @@ export const Room = ({ onLeave }) => {
const { setShowDeviceModal } = useUIState();
const { isCamMuted, isMicMuted } = useMediaDevices();
const { setShowModal, showModal } = useWaitingRoom();
const { localParticipant } = useParticipants();
const toggleCamera = (newState) => {
if (!callObject) return false;

View File

@ -22,6 +22,7 @@ export const Tile = React.memo(
const cx = classNames('tile', {
mirrored,
avatar: showAvatar && !videoTrack,
active: participant.isActiveSpeaker,
});
return (
@ -60,6 +61,11 @@ export const Tile = React.memo(
min-width: 1px;
position: relative;
width: 100%;
box-sizing: border-box;
}
.tile.active {
border: 2px solid var(--primary-default);
}
.tile.mirrored :global(video) {

View File

@ -1,12 +1,7 @@
<svg width="100%" viewBox="0 0 87 87" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="43.75" cy="43.5" r="43" fill="#1F2D3D"/>
<g clip-path="url(#clip0)">
<g>
<path d="M43.75 59.5C39.5188 59.6114 35.3061 58.9031 31.344 57.414C31.1672 57.3354 31.0169 57.2073 30.9115 57.0451C30.8061 56.8828 30.75 56.6935 30.75 56.5C30.7529 53.8487 31.8074 51.3069 33.6821 49.4321C35.5569 47.5574 38.0987 46.5029 40.75 46.5H46.75C49.4013 46.5029 51.9431 47.5574 53.8179 49.4321C55.6926 51.3069 56.7471 53.8487 56.75 56.5C56.75 56.6935 56.6939 56.8828 56.5885 57.0451C56.4831 57.2073 56.3328 57.3354 56.156 57.414C52.1939 58.9031 47.9812 59.6114 43.75 59.5Z" fill="#7B848F"/>
<path d="M43.75 44.5C39.171 44.5 35.75 39.749 35.75 35.5C35.75 33.3783 36.5929 31.3434 38.0931 29.8431C39.5934 28.3429 41.6283 27.5 43.75 27.5C45.8717 27.5 47.9066 28.3429 49.4069 29.8431C50.9071 31.3434 51.75 33.3783 51.75 35.5C51.75 39.749 48.329 44.5 43.75 44.5Z" fill="#7B848F"/>
</g>
<defs>
<clipPath id="clip0">
<rect width="32" height="32" fill="white" transform="translate(27.75 27.5)"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 937 B

View File

@ -2,6 +2,7 @@ import React, { useEffect, useState } from 'react';
import { useCallState } from '../../contexts/CallProvider';
import { useWaitingRoom } from '../../contexts/WaitingRoomProvider';
import { ReactComponent as IconWaiting } from '../../icons/add-person-lg.svg';
import { Button } from '../Button';
import { Card, CardBody, CardFooter } from '../Card';
@ -58,40 +59,70 @@ export const WaitingRoomNotification = () => {
const handleDenyClick = () => {
denyAccess(hasMultiplePeopleWaiting ? 'all' : waitingParticipants[0].id);
};
// const handleClose = () => setShowNotification(false);
return (
<Card className="waiting-room-notification">
<aside>
<IconWaiting />
</aside>
<CardBody>
<strong>
{hasMultiplePeopleWaiting
? waitingParticipants.length
: waitingParticipants[0].name}
</strong>
{hasMultiplePeopleWaiting
? `${waitingParticipants.length} people would like to join the call`
: `${waitingParticipants[0].name} would like to join the call`}
? ` people would like to join the call`
: ` would like to join the call`}
<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>
</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) {
:global(.card.waiting-room-notification) {
position: absolute;
right: var(--spacing-sm);
top: var(--spacing-sm);
z-index: 999;
padding: 0px;
display: grid;
align-items: center;
grid-template-columns: auto auto;
overflow: hidden;
box-shadow: var(--shadow-depth-2);
}
strong {
color: var(--text-default);
}
aside {
background: var(--gray-wash);
display: flex;
padding: var(--spacing-md);
height: 100%;
align-items: center;
color: var(--gray-default);
}
:global(.waiting-room-notification .card-footer) {
display: flex;
column-gap: var(--spacing-xxs);
margin-top: var(--spacing-xs);
}
:global(.waiting-room-notification .card-body) {
padding: var(--spacing-md);
}
`}</style>
</Card>

View File

@ -33,7 +33,7 @@ export const CallProvider = ({
const [preJoinNonAuthorized, setPreJoinNonAuthorized] = useState(false);
// Daily CallMachine hook (primarily handles status of the call)
const { daily, leave, join, state } = useCallMachine({
const { daily, leave, state, setRedirectOnLeave } = useCallMachine({
domain,
room,
token,
@ -71,10 +71,10 @@ export const CallProvider = ({
addFakeParticipant,
preJoinNonAuthorized,
leave,
join,
videoQuality,
setVideoQuality,
setBandwidth,
setRedirectOnLeave,
subscribeToTracksAutomatically,
}}
>

View File

@ -38,7 +38,7 @@ export const useCallMachine = ({
}) => {
const [daily, setDaily] = useState(null);
const [state, setState] = useState(CALL_STATE_READY);
const [redirectOnLeave, setRedirectOnLeave] = useState(true);
const [redirectOnLeave, setRedirectOnLeave] = useState(false);
const url = useMemo(
() => (domain && room ? `https://${domain}.daily.co/${room}` : null),
@ -245,8 +245,9 @@ export const useCallMachine = ({
break;
case 'left-meeting':
daily.destroy();
if (!redirectOnLeave) return;
setState(CALL_STATE_REDIRECTING);
setState(
!redirectOnLeave ? CALL_STATE_ENDED : CALL_STATE_REDIRECTING
);
break;
case 'error':
switch (ev?.error?.type) {

View File

@ -0,0 +1,94 @@
import React, { useCallback, useEffect } from 'react';
import Loader from '@dailyjs/shared/components/Loader';
import MessageCard from '@dailyjs/shared/components/MessageCard';
import {
CALL_STATE_ENDED,
CALL_STATE_JOINED,
CALL_STATE_JOINING,
CALL_STATE_LOBBY,
CALL_STATE_NOT_FOUND,
CALL_STATE_NOT_BEFORE,
CALL_STATE_READY,
CALL_STATE_REDIRECTING,
} from '@dailyjs/shared/contexts/useCallMachine';
import { useRouter } from 'next/router';
import HairCheck from '../components/HairCheck';
export const useCallUI = ({
state,
room,
haircheck,
redirectUrl,
callEnded,
notFoundRedirect = 'not-found',
}) => {
const router = useRouter();
useEffect(() => {
console.log(`%c🔀 App state changed: ${state}`, `color: gray;`);
}, [state]);
const renderByState = useCallback(() => {
// Show loader when state is undefined or ready to join
if (!state || [CALL_STATE_READY, CALL_STATE_JOINING].includes(state)) {
return <Loader />;
}
// Update the UI based on the state of our call
switch (state) {
case CALL_STATE_NOT_FOUND:
router.replace(notFoundRedirect);
return null;
case CALL_STATE_NOT_BEFORE:
return (
<MessageCard error header="Cannot join before owner">
This room has `nbf` set, meaning you cannot join the call before the
owner
</MessageCard>
);
case CALL_STATE_LOBBY:
return haircheck ? haircheck() : <HairCheck />;
case CALL_STATE_JOINED:
return room ? (
room()
) : (
<MessageCard error header="No room component declared" />
);
case CALL_STATE_REDIRECTING:
if (!redirectUrl) {
break;
}
window.location = redirectUrl;
break;
case CALL_STATE_ENDED:
return callEnded ? (
callEnded()
) : (
<MessageCard onBack={() => window.location.reload()}>
You have left the call. We hope you had fun!
</MessageCard>
);
default:
break;
}
return (
<MessageCard error header="An error occured">
An unknown error has occured in the call loop. This should not happen!
</MessageCard>
);
}, [
state,
notFoundRedirect,
redirectUrl,
haircheck,
room,
callEnded,
router,
]);
return renderByState;
};
export default useCallUI;

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><title>add-27</title><g stroke-linecap="square" stroke-linejoin="miter" stroke-width="2" fill="none" stroke="currentColor" stroke-miterlimit="10"><line x1="8" y1="14" x2="8" y2="26" stroke="currentColor"></line> <line x1="2" y1="20" x2="14" y2="20" stroke="currentColor"></line> <path d="M30,22L30,22 c-4.418,0-8-3.582-8-8v-4c0-4.418,3.582-8,8-8h0c4.418,0,8,3.582,8,8v4C38,18.418,34.418,22,30,22z"></path> <path d="M46,46H14v-8.229 c0-2.493,1.53-4.727,3.862-5.61C20.635,31.11,24.797,30,30,30s9.365,1.11,12.138,2.161C44.47,33.044,46,35.278,46,37.771V46z"></path></g></svg>

After

Width:  |  Height:  |  Size: 654 B