Skip to content

Commit

Permalink
Add the UI for the hair check component
Browse files Browse the repository at this point in the history
  • Loading branch information
nienkedekker committed May 11, 2022
1 parent 7a03fcc commit b85cff1
Show file tree
Hide file tree
Showing 8 changed files with 351 additions and 118 deletions.
145 changes: 90 additions & 55 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
import './App.css';

import React, { useEffect, useState, useCallback } from 'react';
import DailyIframe from '@daily-co/daily-js';
import { DailyProvider } from '@daily-co/daily-react-hooks';

import './App.css';
import api from './api';
import { roomUrlFromPageUrl, pageUrlFromRoomUrl } from './utils';

import HomeScreen from './components/HomeScreen/HomeScreen';
import Call from './components/Call/Call';
import Header from './components/Header/Header';
import Tray from './components/Tray/Tray';
import HairCheck from './components/HairCheck/HairCheck';

/* We decide what UI to show to users based on the state of the app, which is dependent on the state of the call object: see line 137. */
/* We decide what UI to show to users based on the state of the app, which is dependent on the state of the call object. */
const STATE_IDLE = 'STATE_IDLE';
const STATE_CREATING = 'STATE_CREATING';
const STATE_JOINING = 'STATE_JOINING';
const STATE_JOINED = 'STATE_JOINED';
const STATE_LEAVING = 'STATE_LEAVING';
const STATE_ERROR = 'STATE_ERROR';
const STATE_HAIRCHECK = 'STATE_HAIRCHECK';

export default function App() {
const [appState, setAppState] = useState(STATE_IDLE);
Expand All @@ -27,40 +28,46 @@ export default function App() {
const [apiError, setApiError] = useState(false);

/**
* Show the call UI if we're either joining, already joined, or are showing
* an error.
*/
const showCall = [STATE_JOINING, STATE_JOINED, STATE_ERROR].includes(
appState,
);

/**
* Creates a new call room.
* Create a new call room. This function will return the newly created room URL.
* We'll need this URL when pre-authorizing (https://docs.daily.co/reference/rn-daily-js/instance-methods/pre-auth)
* or joining (https://docs.daily.co/reference/rn-daily-js/instance-methods/join) a call.
*/
const createCall = useCallback(() => {
setAppState(STATE_CREATING);
return api
.createRoom()
.then((room) => room.url)
.catch((error) => {
console.error('Error creating room', error);
setRoomUrl(null);
setAppState(STATE_IDLE);
setApiError(true);
});
.createRoom()
.then((room) => room.url)
.catch((error) => {
console.error('Error creating room', error);
setRoomUrl(null);
setAppState(STATE_IDLE);
setApiError(true);
});
}, []);

/**
* Starts joining an existing call.
* We've created a room, so let's start the hair check. We won't be joining the call yet.
*/
const startJoiningCall = useCallback((url) => {
const startHairCheck = useCallback(async (url) => {
const newCallObject = DailyIframe.createCallObject();
setRoomUrl(url);
setCallObject(newCallObject);
setAppState(STATE_JOINING);
newCallObject.join({ url });
setAppState(STATE_HAIRCHECK);
await newCallObject.preAuth({ url });
await newCallObject.startCamera();
}, []);

/**
* Once we pass the hair check, we can actually join the call.
*/
const joinCall = useCallback(
() => {
console.log("joinCall: Hair looks good! Let's go to the call.");
callObject.join({ url: roomUrl });
},
[callObject, roomUrl],
);

/**
* Starts leaving the current call.
*/
Expand All @@ -74,7 +81,9 @@ export default function App() {
setAppState(STATE_IDLE);
});
} else {
/* This will trigger a `left-meeting` event, which in turn will trigger the full clean-up as seen in handleNewMeetingState() below. */
// This will trigger a `left-meeting` event, which in turn will trigger
// the full clean-up as seen in handleNewMeetingState() below.
// This will trigger a `left-meeting` event, which in turn will trigger the full clean-up as seen in handleNewMeetingState() below.
setAppState(STATE_LEAVING);
callObject.leave();
}
Expand All @@ -86,8 +95,8 @@ export default function App() {
*/
useEffect(() => {
const url = roomUrlFromPageUrl();
url && startJoiningCall(url);
}, [startJoiningCall]);
url && startHairCheck(url);
}, [startHairCheck]);

/**
* Update the page's URL to reflect the active call when roomUrl changes.
Expand Down Expand Up @@ -139,7 +148,7 @@ export default function App() {
/*
We can't use the useDailyEvent hook (https://docs.daily.co/reference/daily-react-hooks/use-daily-event) for this
because right now, we're not inside a <DailyProvider/> (https://docs.daily.co/reference/daily-react-hooks/daily-provider)
context yet. We can't access the call object via daily-react-hooks just yet, but we will later in Call.js.
context yet. We can't access the call object via daily-react-hooks just yet, but we will later in Call.js and HairCheck.js!
*/
callObject.on(event, handleNewMeetingState);
}
Expand All @@ -152,32 +161,58 @@ export default function App() {
};
}, [callObject]);

/**
* Show the call UI if we're either joining, already joined, or are showing
* an error that is _not_ a room API error.
*/
const showCall = !apiError && [STATE_JOINING, STATE_JOINED, STATE_ERROR].includes(appState);
const showHairCheck = !apiError && appState === STATE_HAIRCHECK;

const renderApp = () => {
/* If something goes wrong with creating the room. */
if (apiError) {
return (
<div className="api-error">
<h1>Error</h1>
<p>
Room could not be created. Please check your local configuration in `api.js`. For more
information, check out the{' '}
<a href="https://github.com/daily-demos/call-object-react-daily-hooks/blob/main/README.md">
readme
</a>{' '}
:)
</p>
</div>
);
}

/* No API errors? Let's check our hair then. */
if (showHairCheck) {
return (
<DailyProvider callObject={callObject}>
<HairCheck joinCall={joinCall} cancelCall={startLeavingCall} />
</DailyProvider>
);
}

/* No API errors, we passed the hair check, and we've joined the call? Then show the call !*/
if (showCall) {
return (
<DailyProvider callObject={callObject}>
<Call />
<Tray leaveCall={startLeavingCall} />
</DailyProvider>
);
}

// The default view is the HomeScreen, from where we start the demo.
return <HomeScreen createCall={createCall} startHairCheck={startHairCheck} />;
};

return (
<div className="app">
<Header />
{apiError ? (
<div className="api-error">
<h1>Error</h1>
<p>
Room could not be created. Please check your local configuration in
`api.js`. For more information, check out the{' '}
<a href="https://github.com/daily-demos/call-object-react-daily-hooks/blob/main/README.md">
readme
</a>{' '}
:)
</p>
</div>
) : showCall ? (
<DailyProvider callObject={callObject}>
<Call />
<Tray leaveCall={startLeavingCall} />
</DailyProvider>
) : (
<HomeScreen
createCall={createCall}
startJoiningCall={startJoiningCall}
/>
)}
</div>
<div className="app">
<Header />
{renderApp()}
</div>
);
}
}
13 changes: 13 additions & 0 deletions src/components/Call/Call.css
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
grid-row-gap: 30px;
width: 100%;
box-sizing: border-box;
position: relative;
}

.is-screenshare {
Expand All @@ -27,6 +28,7 @@
.self-view {
max-width: 480px;
max-height: 270px;
position: relative;
}

/* Hard-code the self-view size if there's no one else in the call, just to make it align with the info-box */
Expand All @@ -38,8 +40,19 @@
.self-view video {
/* Mirror the self-view: it's weird not seeing yourself mirrored! */
transform: rotate3d(0, 1, 0, 180deg);
position: relative;
}

.username {
position: absolute;
z-index: 2;
color: var(--grey);
background-color: var(--dark-blue);
bottom: 0;
left: 0;
font-size: 14px;
padding: 0.2em 0.5em;
}
/*
When someone is sharing their screen, we want to resize the participants' videos,
so the biggest screen in our grid is the screen share.
Expand Down
93 changes: 47 additions & 46 deletions src/components/Call/Call.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,27 @@ import './Call.css';
import Tile from '../Tile/Tile';
import UserMediaError from '../UserMediaError/UserMediaError';

export default function Call({ apiError }) {
export default function Call() {
/* If a participant runs into a getUserMedia() error, we need to warn them. */
const [getUserMediaError, setGetUserMediaError] = useState(false);

/* We can use the useDailyEvent() hook to listen for daily-js events. Here's a full list
* of all events: https://docs.daily.co/reference/daily-js/events */
useDailyEvent(
'camera-error',
useCallback((ev) => {
setGetUserMediaError(true);
}, []),
'camera-error',
useCallback((ev) => {
setGetUserMediaError(true);
}, []),
);

/* This is for displaying our self-view. */
const localParticipant = useLocalParticipant();
const localParticipantVideoTrack = useVideoTrack(
localParticipant?.session_id,
);
const localParticipantVideoTrack = useVideoTrack(localParticipant?.session_id);
const localVideoElement = useRef(null);

useEffect(() => {
localVideoElement.current &&
(localVideoElement.current.srcObject =
localVideoElement?.current &&
(localVideoElement.current.srcObject =
localParticipantVideoTrack &&
new MediaStream([localParticipantVideoTrack?.persistentTrack]));
// eslint-disable-next-line react-hooks/exhaustive-deps
Expand All @@ -45,43 +43,46 @@ export default function Call({ apiError }) {

const renderCallScreen = () => {
return (
<div className={`${screens.length > 0 ? 'is-screenshare' : 'call'}`}>
{/*Your self view*/}
{localParticipant && (
<div
className={
remoteParticipantIds?.length > 0 || screens?.length > 0
? 'self-view'
: 'self-view alone'
}>
<video autoPlay muted playsInline ref={localVideoElement} />
</div>
)}
{/*Videos of remote participants and screen shares*/}
{remoteParticipantIds?.length > 0 || screens?.length > 0 ? (
<>
{remoteParticipantIds.map((id) => (
<Tile key={id} id={id} />
))}
{screens.map((screen) => (
<Tile
key={screen.screenId}
id={screen.session_id}
isScreenShare
/>
))}
</>
) : (
// When there are no remote participants or screen shares
<div className="info-box">
<h1>Waiting for others</h1>
<p>Invite someone by sharing this link:</p>
<span className="room-url">{window.location.href}</span>
</div>
)}
</div>
<div className={`${screens.length > 0 ? 'is-screenshare' : 'call'}`}>
{/*Your self view*/}
{localParticipant && (
<div
className={
remoteParticipantIds?.length > 0 || screens?.length > 0
? 'self-view'
: 'self-view alone'
}>
<video autoPlay muted playsInline ref={localVideoElement} />
<div className="username">
You (
{localParticipant?.user_name
? localParticipant?.user_name
: localParticipant?.user_id}
)
</div>
</div>
)}
{/*Videos of remote participants and screen shares*/}
{remoteParticipantIds?.length > 0 || screens?.length > 0 ? (
<>
{remoteParticipantIds.map((id) => (
<Tile key={id} id={id} />
))}
{screens.map((screen) => (
<Tile key={screen.screenId} id={screen.session_id} isScreenShare />
))}
</>
) : (
// When there are no remote participants or screen shares
<div className="info-box">
<h1>Waiting for others</h1>
<p>Invite someone by sharing this link:</p>
<span className="room-url">{window.location.href}</span>
</div>
)}
</div>
);
};

return <>{getUserMediaError ? <UserMediaError /> : renderCallScreen()}</>;
}
}
Loading

0 comments on commit b85cff1

Please sign in to comment.