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:
commit
32bc7de155
|
|
@ -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]
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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 |
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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 |
Loading…
Reference in New Issue