expiry timer
This commit is contained in:
parent
242b6154f1
commit
1f7ebf8e22
|
|
@ -1,4 +1,5 @@
|
|||
import React, { useState, useEffect, useMemo } from 'react';
|
||||
import ExpiryTimer from '@dailyjs/shared/components/ExpiryTimer';
|
||||
import { useCallState } from '@dailyjs/shared/contexts/CallProvider';
|
||||
import { useCallUI } from '@dailyjs/shared/hooks/useCallUI';
|
||||
|
||||
|
|
@ -8,19 +9,6 @@ import { Modals } from './Modals';
|
|||
|
||||
export const App = () => {
|
||||
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({
|
||||
state,
|
||||
|
|
@ -28,48 +16,29 @@ export const App = () => {
|
|||
});
|
||||
|
||||
// Memoize children to avoid unnecassary renders from HOC
|
||||
const memoizedApp = useMemo(
|
||||
return useMemo(
|
||||
() => (
|
||||
<div className="app">
|
||||
{componentForState()}
|
||||
<Modals />
|
||||
<Asides />
|
||||
<style jsx>{`
|
||||
color: white;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
<>
|
||||
{roomExp && <ExpiryTimer expiry={roomExp} />}
|
||||
<div className="app">
|
||||
{componentForState()}
|
||||
<Modals />
|
||||
<Asides />
|
||||
<style jsx>{`
|
||||
color: white;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.loader {
|
||||
margin: 0 auto;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
.loader {
|
||||
margin: 0 auto;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
</>
|
||||
),
|
||||
[componentForState]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{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>
|
||||
</>
|
||||
[componentForState, roomExp]
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
export default async function handler(req, res) {
|
||||
const { privacy, expiryMinutes } = req.body;
|
||||
|
||||
if (req.method === 'POST') {
|
||||
console.log(`Creating room on domain ${process.env.DAILY_DOMAIN}`);
|
||||
|
||||
|
|
@ -9,8 +11,9 @@ export default async function handler(req, res) {
|
|||
Authorization: `Bearer ${process.env.DAILY_API_KEY}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
privacy: privacy || 'public',
|
||||
properties: {
|
||||
exp: Math.round(Date.now() / 1000) + 5 * 60, // expire in 5 minutes
|
||||
exp: Math.round(Date.now() / 1000) + (expiryMinutes || 5) * 60, // expire in x minutes
|
||||
eject_at_room_exp: true,
|
||||
},
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -1,17 +1,23 @@
|
|||
import React, { useState } from 'react';
|
||||
|
||||
import Button from '@dailyjs/shared/components/Button';
|
||||
import Loader from '@dailyjs/shared/components/Loader';
|
||||
import { Well } from '@dailyjs/shared/components/Well';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
export const Splash = ({ domain, onJoin, isConfigured }) => {
|
||||
// const [joinAsInstructor, setJoinAsInstructor] = useState(false);
|
||||
/**
|
||||
* Splash
|
||||
* ---
|
||||
* - Checks our app is configured properly
|
||||
* - Creates a new Daily room for this session
|
||||
* - Calls the onJoin method with the room name and instructor (owner) status
|
||||
*/
|
||||
export const Splash = ({ onJoin, isConfigured }) => {
|
||||
const [fetching, setFetching] = useState(false);
|
||||
const [error, setError] = useState(false);
|
||||
const [room, setRoom] = useState('');
|
||||
|
||||
async function createRoom() {
|
||||
// Create a room
|
||||
|
||||
async function createRoom(asInstructor) {
|
||||
// Create a new room for this class
|
||||
setError(false);
|
||||
setFetching(true);
|
||||
|
||||
|
|
@ -23,13 +29,16 @@ export const Splash = ({ domain, onJoin, isConfigured }) => {
|
|||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
privacy: 'private',
|
||||
expiryMinutes: 10,
|
||||
}),
|
||||
});
|
||||
|
||||
const resJson = await res.json();
|
||||
|
||||
if (resJson.name) {
|
||||
setFetching(false);
|
||||
setRoom(resJson.name);
|
||||
onJoin(resJson.name, asInstructor);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -39,25 +48,111 @@ export const Splash = ({ domain, onJoin, isConfigured }) => {
|
|||
|
||||
return (
|
||||
<div className="container">
|
||||
<aside />
|
||||
<aside>
|
||||
<a href="https://unsplash.com/@jordannix?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">
|
||||
Photo by Jordan Nix on Unsplash
|
||||
</a>
|
||||
</aside>
|
||||
|
||||
<main>
|
||||
<header className="branding">
|
||||
<img src="/assets/daily-logo-dark.svg" alt="Daily" />
|
||||
</header>
|
||||
<img
|
||||
src="/assets/daily-logo-dark.svg"
|
||||
alt="Daily"
|
||||
className="branding"
|
||||
/>
|
||||
|
||||
<div className="inner">
|
||||
{!isConfigured ? (
|
||||
<div>
|
||||
You must set <code>Stuff</code>
|
||||
</div>
|
||||
) : (
|
||||
<Button onClick={() => createRoom()}>
|
||||
{fetching ? 'Creating room...' : 'Join'}
|
||||
</Button>
|
||||
)}
|
||||
{(() => {
|
||||
// Application is not yet configured (there are missing globals, such as domain and dev key)
|
||||
if (!isConfigured)
|
||||
return (
|
||||
<>
|
||||
<h2>Not configured</h2>
|
||||
<p>
|
||||
Please ensure you have set both the{' '}
|
||||
<code>DAILY_API_KEY</code> and <code>DAILY_DOMAIN</code>{' '}
|
||||
environmental variables. An example can be found in the
|
||||
provided <code>env.example</code> file.
|
||||
</p>
|
||||
<p>
|
||||
If you do not yet have a Daily developer account, please{' '}
|
||||
<a
|
||||
href="https://dashboard.daily.co/signup"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
create one now
|
||||
</a>{' '}
|
||||
. You can find your Daily API key on the{' '}
|
||||
<a
|
||||
href="https://dashboard.daily.co/developers"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
developer page
|
||||
</a>
|
||||
of the dashboard.
|
||||
</p>
|
||||
</>
|
||||
);
|
||||
|
||||
// There was an error creating the room
|
||||
if (error)
|
||||
return (
|
||||
<div>
|
||||
<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.
|
||||
</div>
|
||||
);
|
||||
|
||||
// Loader whilst we create the room
|
||||
if (fetching)
|
||||
return (
|
||||
<>
|
||||
<Loader /> <p>Creating room, please wait...</p>
|
||||
</>
|
||||
);
|
||||
|
||||
// Introductory splash screen
|
||||
return (
|
||||
<>
|
||||
<h2>Live fitness example</h2>
|
||||
<p>
|
||||
This example demonstrates how to use Daily JS to create a live
|
||||
class experience. Please be sure to reference the project
|
||||
readme first.
|
||||
</p>
|
||||
<p>
|
||||
Note: all rooms created with this demo will have a 5 minute
|
||||
expiry time. If you would like to create a longer running
|
||||
room, please set the <code>DAILY_ROOM</code> environmental
|
||||
variable to use your own custom room.
|
||||
</p>
|
||||
<hr />
|
||||
<footer>
|
||||
<Button
|
||||
fullWidth
|
||||
onClick={() => createRoom(true)}
|
||||
disabled={fetching}
|
||||
>
|
||||
Join as instructor
|
||||
</Button>
|
||||
<Button
|
||||
fullWidth
|
||||
onClick={() => createRoom(false)}
|
||||
variant="outline-gray"
|
||||
disabled={fetching}
|
||||
>
|
||||
Join as student
|
||||
</Button>
|
||||
</footer>
|
||||
</>
|
||||
);
|
||||
})()}
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<style jsx>
|
||||
{`
|
||||
.container {
|
||||
|
|
@ -74,19 +169,43 @@ export const Splash = ({ domain, onJoin, isConfigured }) => {
|
|||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
p {
|
||||
color: var(--text-mid);
|
||||
}
|
||||
|
||||
main .inner {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
flex: 0;
|
||||
margin: auto 0;
|
||||
}
|
||||
|
||||
.branding {
|
||||
flex: 0;
|
||||
width: 108px;
|
||||
}
|
||||
|
||||
hr {
|
||||
margin: var(--spacing-md) 0;
|
||||
}
|
||||
|
||||
aside {
|
||||
background: url(/images/fitness-bg.jpg) no-repeat;
|
||||
background-size: cover;
|
||||
color: white;
|
||||
font-size: 0.875rem;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
padding: var(--spacing-xs);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
aside a {
|
||||
color: white;
|
||||
opacity: 0.65;
|
||||
}
|
||||
|
||||
footer {
|
||||
display: flex;
|
||||
gap: var(--spacing-xxs);
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
|
|
@ -95,7 +214,6 @@ export const Splash = ({ domain, onJoin, isConfigured }) => {
|
|||
};
|
||||
|
||||
Splash.propTypes = {
|
||||
domain: PropTypes.string,
|
||||
onJoin: PropTypes.func,
|
||||
isConfigured: PropTypes.bool,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,101 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import App from '@dailyjs/basic-call/components/App';
|
||||
import Loader from '@dailyjs/shared/components/Loader';
|
||||
import { CallProvider } from '@dailyjs/shared/contexts/CallProvider';
|
||||
import { MediaDeviceProvider } from '@dailyjs/shared/contexts/MediaDeviceProvider';
|
||||
import { ParticipantsProvider } from '@dailyjs/shared/contexts/ParticipantsProvider';
|
||||
import { TracksProvider } from '@dailyjs/shared/contexts/TracksProvider';
|
||||
import { UIStateProvider } from '@dailyjs/shared/contexts/UIStateProvider';
|
||||
import { WaitingRoomProvider } from '@dailyjs/shared/contexts/WaitingRoomProvider';
|
||||
import getDemoProps from '@dailyjs/shared/lib/demoProps';
|
||||
import { useRouter } from 'next/router';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
/**
|
||||
* Room page
|
||||
* ---
|
||||
*/
|
||||
export default function Room({ room, instructor, domain }) {
|
||||
const [token, setToken] = useState();
|
||||
const [tokenError, setTokenError] = useState();
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
// Redirect to a 404 if we do not have a room
|
||||
useEffect(
|
||||
() => (!room || !domain) && router.replace('/not-found'),
|
||||
[room, domain, router]
|
||||
);
|
||||
|
||||
// Fetch a meeting token
|
||||
useEffect(() => {
|
||||
if (token || !room) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We're using a simple Next serverless function to generate meeting tokens
|
||||
// which could be replaced with your own serverside method that authenticates
|
||||
// users / sets room owner status, user ID, user names etc
|
||||
async function getToken() {
|
||||
console.log(`🪙 Fetching meeting token for room '${room}'`);
|
||||
|
||||
const res = await fetch('/api/token', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ roomName: room, isOwner: !!instructor }),
|
||||
});
|
||||
|
||||
const resJson = await res.json();
|
||||
|
||||
if (!resJson?.token) {
|
||||
setTokenError(resJson?.error || true);
|
||||
}
|
||||
|
||||
console.log(`🪙 Meeting token received`);
|
||||
|
||||
setToken(resJson.token);
|
||||
}
|
||||
|
||||
getToken();
|
||||
}, [token, room, instructor]);
|
||||
|
||||
if (!token) {
|
||||
return <div>Fetching token...</div>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main call UI
|
||||
*/
|
||||
return (
|
||||
<UIStateProvider>
|
||||
<CallProvider domain={domain} room={room} token={token}>
|
||||
<ParticipantsProvider>
|
||||
<TracksProvider>
|
||||
<MediaDeviceProvider>
|
||||
<WaitingRoomProvider>
|
||||
<App />
|
||||
</WaitingRoomProvider>
|
||||
</MediaDeviceProvider>
|
||||
</TracksProvider>
|
||||
</ParticipantsProvider>
|
||||
</CallProvider>
|
||||
</UIStateProvider>
|
||||
);
|
||||
}
|
||||
|
||||
Room.propTypes = {
|
||||
room: PropTypes.string.isRequired,
|
||||
domain: PropTypes.string.isRequired,
|
||||
instructor: PropTypes.bool,
|
||||
};
|
||||
|
||||
export async function getServerSideProps(context) {
|
||||
const { room, instructor } = context.query;
|
||||
const defaultProps = getDemoProps();
|
||||
|
||||
return {
|
||||
props: { room, instructor: !!instructor, ...defaultProps },
|
||||
};
|
||||
}
|
||||
|
|
@ -8,25 +8,22 @@ import Splash from '../components/Splash';
|
|||
* Index page
|
||||
* ---
|
||||
*/
|
||||
export default function Index({ domain, isConfigured = false }) {
|
||||
export default function Index({ isConfigured = false }) {
|
||||
const router = useRouter();
|
||||
|
||||
function joinRoom(room, joinAsInstructor) {
|
||||
// redirect to room....
|
||||
console.log(room);
|
||||
console.log(joinAsInstructor);
|
||||
|
||||
router.replace(`/${room}/`);
|
||||
// Redirect to room page
|
||||
router.replace({
|
||||
pathname: `/${room}`,
|
||||
query: { instructor: !!joinAsInstructor },
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<Splash domain={domain} onJoin={joinRoom} isConfigured={!!isConfigured} />
|
||||
);
|
||||
return <Splash onJoin={joinRoom} isConfigured={!!isConfigured} />;
|
||||
}
|
||||
|
||||
Index.propTypes = {
|
||||
isConfigured: PropTypes.bool.isRequired,
|
||||
domain: PropTypes.string,
|
||||
};
|
||||
|
||||
export async function getStaticProps() {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
import React from 'react';
|
||||
import MessageCard from '@dailyjs/shared/components/MessageCard';
|
||||
|
||||
export default function RoomNotFound() {
|
||||
return (
|
||||
<div className="not-found">
|
||||
<MessageCard error header="Room not found">
|
||||
The room you are trying to join does not exist. Have you created the
|
||||
room using the Daily REST API or the dashboard?
|
||||
</MessageCard>
|
||||
<style jsx>{`
|
||||
display: grid;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
grid-template-columns: 620px;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
`}</style>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
export const ExpiryTimer = ({ expiry }) => {
|
||||
const [secs, setSecs] = useState('--:--');
|
||||
|
||||
// If room has an expiry time, we'll calculate how many seconds until expiry
|
||||
useEffect(() => {
|
||||
if (!expiry) {
|
||||
return false;
|
||||
}
|
||||
const i = setInterval(() => {
|
||||
const timeLeft = Math.round((expiry - Date.now()) / 1000);
|
||||
setSecs(`${Math.floor(timeLeft / 60)}:${`0${timeLeft % 60}`.slice(-2)}`);
|
||||
}, 1000);
|
||||
|
||||
return () => clearInterval(i);
|
||||
}, [expiry]);
|
||||
|
||||
return (
|
||||
<div className="countdown">
|
||||
{secs}
|
||||
<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(--secondary-dark);
|
||||
color: white;
|
||||
z-index: 999;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
ExpiryTimer.propTypes = {
|
||||
expiry: PropTypes.number,
|
||||
};
|
||||
|
||||
export default ExpiryTimer;
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { ExpiryTimer as default } from './ExpiryTimer';
|
||||
|
|
@ -213,7 +213,7 @@ export const HairCheck = () => {
|
|||
}
|
||||
|
||||
.haircheck .panel {
|
||||
width: 720px;
|
||||
width: 580px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
|
@ -228,7 +228,7 @@ export const HairCheck = () => {
|
|||
position: relative;
|
||||
color: white;
|
||||
border: 3px solid rgba(255, 255, 255, 0.1);
|
||||
max-width: 520px;
|
||||
max-width: 480px;
|
||||
margin: 0 auto;
|
||||
border-radius: var(--radius-md) var(--radius-md) 0 0;
|
||||
border-bottom: 0px;
|
||||
|
|
@ -279,7 +279,7 @@ export const HairCheck = () => {
|
|||
left: 0px;
|
||||
right: 0px;
|
||||
z-index: 99;
|
||||
padding: var(--spacing-sm);
|
||||
padding: var(--spacing-xs);
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
@ -289,8 +289,8 @@ export const HairCheck = () => {
|
|||
|
||||
.haircheck .content :global(.device-button) {
|
||||
position: absolute;
|
||||
top: var(--spacing-sm);
|
||||
right: var(--spacing-sm);
|
||||
top: var(--spacing-xxs);
|
||||
right: var(--spacing-xxs);
|
||||
}
|
||||
|
||||
.haircheck .overlay-message {
|
||||
|
|
@ -303,7 +303,7 @@ export const HairCheck = () => {
|
|||
.haircheck footer {
|
||||
position: relative;
|
||||
border: 3px solid rgba(255, 255, 255, 0.1);
|
||||
max-width: 520px;
|
||||
max-width: 480px;
|
||||
margin: 0 auto;
|
||||
border-radius: 0 0 var(--radius-md) var(--radius-md);
|
||||
padding: calc(6px + var(--spacing-md)) var(--spacing-sm)
|
||||
|
|
@ -312,7 +312,7 @@ export const HairCheck = () => {
|
|||
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
grid-column-gap: var(--spacing-sm);
|
||||
grid-column-gap: var(--spacing-xs);
|
||||
}
|
||||
|
||||
.waiting {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,15 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
export const Loader = ({ color = 'currentColor', size = 24 }) => (
|
||||
export const Loader = ({
|
||||
color = 'currentColor',
|
||||
size = 24,
|
||||
centered = false,
|
||||
}) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 44 44"
|
||||
className="loader"
|
||||
className={centered ? 'loader centered' : 'loader'}
|
||||
>
|
||||
<g fill="none" fillRule="evenodd" strokeWidth="2">
|
||||
<circle cx="22" cy="22" r="19.4775">
|
||||
|
|
@ -60,6 +64,13 @@ export const Loader = ({ color = 'currentColor', size = 24 }) => (
|
|||
stroke: ${color};
|
||||
width: ${size}px;
|
||||
}
|
||||
.centered {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
margin-top: ${size / 2}px;
|
||||
left: 50%;
|
||||
margin-left: ${size / 2}px;
|
||||
}
|
||||
`}</style>
|
||||
</svg>
|
||||
);
|
||||
|
|
@ -67,6 +78,7 @@ export const Loader = ({ color = 'currentColor', size = 24 }) => (
|
|||
Loader.propTypes = {
|
||||
size: PropTypes.number,
|
||||
color: PropTypes.string,
|
||||
centered: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default Loader;
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ import { useDeepCompareMemo } from 'use-deep-compare';
|
|||
export const UIStateContext = createContext();
|
||||
|
||||
export const UIStateProvider = ({
|
||||
asides,
|
||||
modals,
|
||||
asides = [],
|
||||
modals = [],
|
||||
customTrayComponent,
|
||||
children,
|
||||
}) => {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ import {
|
|||
CALL_STATE_NOT_BEFORE,
|
||||
CALL_STATE_READY,
|
||||
CALL_STATE_REDIRECTING,
|
||||
CALL_STATE_NOT_ALLOWED,
|
||||
CALL_STATE_EXPIRED,
|
||||
} from '@dailyjs/shared/contexts/useCallMachine';
|
||||
import { useRouter } from 'next/router';
|
||||
import HairCheck from '../components/HairCheck';
|
||||
|
|
@ -39,6 +41,13 @@ export const useCallUI = ({
|
|||
case CALL_STATE_NOT_FOUND:
|
||||
router.replace(notFoundRedirect);
|
||||
return null;
|
||||
case CALL_STATE_NOT_ALLOWED:
|
||||
return (
|
||||
<MessageCard error header="Access denied">
|
||||
You are not allowed to join this meeting. Please make sure you have
|
||||
a valid meeting token.
|
||||
</MessageCard>
|
||||
);
|
||||
case CALL_STATE_NOT_BEFORE:
|
||||
return (
|
||||
<MessageCard error header="Cannot join before owner">
|
||||
|
|
@ -46,6 +55,13 @@ export const useCallUI = ({
|
|||
owner
|
||||
</MessageCard>
|
||||
);
|
||||
case CALL_STATE_EXPIRED:
|
||||
return (
|
||||
<MessageCard error header="Room expired">
|
||||
The room you are trying to join has expired. Please create or join
|
||||
another room.
|
||||
</MessageCard>
|
||||
);
|
||||
case CALL_STATE_LOBBY:
|
||||
return haircheck ? haircheck() : <HairCheck />;
|
||||
case CALL_STATE_JOINED:
|
||||
|
|
|
|||
Loading…
Reference in New Issue