added demo mode

This commit is contained in:
Jon 2021-07-15 13:16:23 +01:00
parent c716c455a3
commit 1068a9f753
9 changed files with 210 additions and 24 deletions

View File

@ -12,6 +12,10 @@ Send messages to other participants using sendAppMessage
Broadcast call to a custom RTMP endpoint using a variety of different layout modes Broadcast call to a custom RTMP endpoint using a variety of different layout modes
### [⏺️ Recording](./recording)
Record a call video and audio using both cloud and local modes
### [🔥 Flying emojis](./flying-emojis) ### [🔥 Flying emojis](./flying-emojis)
Send emoji reactions to all clients using sendAppMessage Send emoji reactions to all clients using sendAppMessage

View File

@ -1,14 +1,26 @@
import React, { useMemo } from 'react'; import React, { useState, useEffect, useMemo } from 'react';
import { useCallState } from '@dailyjs/shared/contexts/CallProvider'; import { useCallState } from '@dailyjs/shared/contexts/CallProvider';
import { useCallUI } from '@dailyjs/shared/hooks/useCallUI'; import { useCallUI } from '@dailyjs/shared/hooks/useCallUI';
import PropTypes from 'prop-types';
import Room from '../Room'; import Room from '../Room';
import { Asides } from './Asides'; import { Asides } from './Asides';
import { Modals } from './Modals'; import { Modals } from './Modals';
export const App = () => { export const App = () => {
const { state } = useCallState(); const { roomExp, state } = useCallState();
const [secs, setSecs] = useState();
// If room has an expiry time, we'll calculate how many seconds until expiry
useEffect(() => {
if (!roomExp) {
return false;
}
const i = setInterval(() => {
const timeLeft = Math.round((roomExp - Date.now()) / 1000);
setSecs(`${Math.floor(timeLeft / 60)}:${`0${timeLeft % 60}`.slice(-2)}`);
}, 1000);
return () => clearInterval(i);
}, [roomExp]);
const componentForState = useCallUI({ const componentForState = useCallUI({
state, state,
@ -16,7 +28,7 @@ export const App = () => {
}); });
// Memoize children to avoid unnecassary renders from HOC // Memoize children to avoid unnecassary renders from HOC
return useMemo( const memoizedApp = useMemo(
() => ( () => (
<div className="app"> <div className="app">
{componentForState()} {componentForState()}
@ -37,10 +49,28 @@ export const App = () => {
), ),
[componentForState] [componentForState]
); );
};
App.propTypes = { return (
asides: PropTypes.arrayOf(PropTypes.func), <>
{roomExp && <div className="countdown">{secs}</div>} {memoizedApp}
<style jsx>{`
.countdown {
position: fixed;
top: 0px;
right: 0px;
width: 48px;
text-align: center;
padding: 4px 0;
font-size: 0.875rem;
font-weight: var(--weight-medium);
border-radius: 0 0 0 var(--radius-sm);
background: var(--blue-dark);
color: white;
z-index: 999;
}
`}</style>
</>
);
}; };
export default App; export default App;

View File

@ -0,0 +1,100 @@
import React, { useState, useEffect } from 'react';
import { Card, CardHeader, CardBody } from '@dailyjs/shared/components/Card';
import Loader from '@dailyjs/shared/components/Loader';
import { Well } from '@dailyjs/shared/components/Well';
import PropTypes from 'prop-types';
export const CreatingRoom = ({ onCreated }) => {
const [room, setRoom] = useState();
const [fetching, setFetching] = useState(false);
const [error, setError] = useState(false);
useEffect(() => {
if (room) return;
async function createRoom() {
setError(false);
setFetching(true);
console.log(`🚪 Creating new demo room...`);
// Create a room server side (using Next JS serverless)
const res = await fetch('/api/createRoom', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
});
const resJson = await res.json();
if (resJson.name) {
setFetching(false);
setRoom(resJson.name);
return;
}
setError(resJson.error || 'An unknown error occured');
setFetching(false);
}
createRoom();
}, [room]);
useEffect(() => {
if (!room || !onCreated) return;
console.log(`🚪 Room created: ${room}, joining now`);
onCreated(room, true);
}, [room, onCreated]);
return (
<div className="creating-room">
{fetching && (
<div className="creating">
<Loader /> Creating new demo room...
</div>
)}
{error && (
<Card error>
<CardHeader>An error occured</CardHeader>
<CardBody>
<Well variant="error">{error}</Well>
An error occured when trying to create a demo room. Please check
that your environmental variables are correct and try again.
</CardBody>
</Card>
)}
<style jsx>
{`
.creating-room {
width: 100vw;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
color: white;
max-width: 420px;
margin: 0 auto;
}
.creating-room .creating {
display: flex;
}
.creating-room :global(.loader) {
margin-right: var(--spacing-xxxs);
}
`}
</style>
</div>
);
};
CreatingRoom.propTypes = {
onCreated: PropTypes.func.isRequired,
};
export default CreatingRoom;

View File

@ -0,0 +1,2 @@
export { CreatingRoom as default } from './CreatingRoom';
export { CreatingRoom } from './CreatingRoom';

View File

@ -0,0 +1,36 @@
export default async function handler(req, res) {
if (req.method === 'POST') {
console.log(`Creating room on domain ${process.env.DAILY_DOMAIN}`);
const options = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.DAILY_API_KEY}`,
},
body: JSON.stringify({
properties: {
exp: Math.round(Date.now() / 1000) + 5 * 60, // expire in 5 minutes
eject_at_room_exp: true,
},
}),
};
const dailyRes = await fetch(
`${process.env.DAILY_REST_DOMAIN || 'https://api.daily.co/v1'}/rooms`,
options
);
const { name, url, error } = await dailyRes.json();
if (error) {
return res.status(500).json({ error });
}
return res
.status(200)
.json({ name, url, domain: process.env.DAILY_DOMAIN });
}
return res.status(500);
}

View File

@ -6,9 +6,9 @@ import { TracksProvider } from '@dailyjs/shared/contexts/TracksProvider';
import { UIStateProvider } from '@dailyjs/shared/contexts/UIStateProvider'; import { UIStateProvider } from '@dailyjs/shared/contexts/UIStateProvider';
import { WaitingRoomProvider } from '@dailyjs/shared/contexts/WaitingRoomProvider'; import { WaitingRoomProvider } from '@dailyjs/shared/contexts/WaitingRoomProvider';
import getDemoProps from '@dailyjs/shared/lib/demoProps'; import getDemoProps from '@dailyjs/shared/lib/demoProps';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import App from '../components/App'; import App from '../components/App';
import { CreatingRoom } from '../components/CreatingRoom';
import { Intro, NotConfigured } from '../components/Intro'; import { Intro, NotConfigured } from '../components/Intro';
/** /**
@ -25,6 +25,7 @@ export default function Index({
predefinedRoom = '', predefinedRoom = '',
forceFetchToken = false, forceFetchToken = false,
forceOwner = false, forceOwner = false,
demoMode = false,
asides, asides,
modals, modals,
customTrayComponent, customTrayComponent,
@ -74,22 +75,24 @@ export default function Index({
if (!isReady) { if (!isReady) {
return ( return (
<main> <main>
{!isConfigured ? ( {(() => {
<NotConfigured /> if (!isConfigured) return <NotConfigured />;
) : ( if (demoMode) return <CreatingRoom onCreated={getMeetingToken} />;
<Intro return (
forceFetchToken={forceFetchToken} <Intro
forceOwner={forceOwner} forceFetchToken={forceFetchToken}
title={process.env.PROJECT_TITLE} forceOwner={forceOwner}
room={roomName} title={process.env.PROJECT_TITLE}
error={tokenError} room={roomName}
fetching={fetchingToken} error={tokenError}
domain={domain} fetching={fetchingToken}
onJoin={(room, isOwner, fetchToken) => domain={domain}
fetchToken ? getMeetingToken(room, isOwner) : setRoomName(room) onJoin={(room, isOwner, fetchToken) =>
} fetchToken ? getMeetingToken(room, isOwner) : setRoomName(room)
/> }
)} />
);
})()}
<style jsx>{` <style jsx>{`
height: 100vh; height: 100vh;
@ -136,6 +139,7 @@ Index.propTypes = {
customAppComponent: PropTypes.node, customAppComponent: PropTypes.node,
forceFetchToken: PropTypes.bool, forceFetchToken: PropTypes.bool,
forceOwner: PropTypes.bool, forceOwner: PropTypes.bool,
demoMode: PropTypes.bool,
}; };
export async function getStaticProps() { export async function getStaticProps() {

View File

@ -34,6 +34,7 @@ export const CallProvider = ({
const [preJoinNonAuthorized, setPreJoinNonAuthorized] = useState(false); const [preJoinNonAuthorized, setPreJoinNonAuthorized] = useState(false);
const [enableRecording, setEnableRecording] = useState(null); const [enableRecording, setEnableRecording] = useState(null);
const [startCloudRecording, setStartCloudRecording] = useState(false); const [startCloudRecording, setStartCloudRecording] = useState(false);
const [roomExp, setRoomExp] = useState(null);
// Daily CallMachine hook (primarily handles status of the call) // Daily CallMachine hook (primarily handles status of the call)
const { daily, leave, state, setRedirectOnLeave } = useCallMachine({ const { daily, leave, state, setRedirectOnLeave } = useCallMachine({
@ -50,6 +51,11 @@ export const CallProvider = ({
const roomConfig = await daily.room(); const roomConfig = await daily.room();
if (!('config' in roomConfig)) return; if (!('config' in roomConfig)) return;
if (roomConfig?.config?.exp) {
setRoomExp(
roomConfig?.config?.exp * 1000 || Date.now() + 1 * 60 * 1000
);
}
const browser = Bowser.parse(window.navigator.userAgent); const browser = Bowser.parse(window.navigator.userAgent);
const supportsRecording = const supportsRecording =
browser.platform.type === 'desktop' && browser.engine.name === 'Blink'; browser.platform.type === 'desktop' && browser.engine.name === 'Blink';
@ -100,6 +106,7 @@ export const CallProvider = ({
addFakeParticipant, addFakeParticipant,
preJoinNonAuthorized, preJoinNonAuthorized,
leave, leave,
roomExp,
videoQuality, videoQuality,
enableRecording, enableRecording,
setVideoQuality, setVideoQuality,

View File

@ -57,6 +57,7 @@ export const UIStateProvider = ({
UIStateProvider.propTypes = { UIStateProvider.propTypes = {
children: PropTypes.node, children: PropTypes.node,
demoMode: PropTypes.bool,
asides: PropTypes.arrayOf(PropTypes.func), asides: PropTypes.arrayOf(PropTypes.func),
modals: PropTypes.arrayOf(PropTypes.func), modals: PropTypes.arrayOf(PropTypes.func),
customTrayComponent: PropTypes.node, customTrayComponent: PropTypes.node,

View File

@ -5,5 +5,7 @@ export default function getDemoProps() {
isConfigured: !!process.env.DAILY_DOMAIN && !!process.env.DAILY_API_KEY, isConfigured: !!process.env.DAILY_DOMAIN && !!process.env.DAILY_API_KEY,
// Have we predefined a room to use? // Have we predefined a room to use?
predefinedRoom: process.env.DAILY_ROOM || '', predefinedRoom: process.env.DAILY_ROOM || '',
// Are we running in demo mode? (automatically creates a short-expiry room)
demoMode: !!process.env.DAILY_DEMO_MODE,
}; };
} }