added demo mode
This commit is contained in:
parent
c716c455a3
commit
1068a9f753
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
export { CreatingRoom as default } from './CreatingRoom';
|
||||||
|
export { CreatingRoom } from './CreatingRoom';
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
@ -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() {
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue