meet/src/components/Pages/ConnectToWorld/ConnectToWorld.tsx

172 lines
6.1 KiB
TypeScript

import React, { ChangeEvent, useCallback, useEffect, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { AuthIdentity } from '@dcl/crypto'
import { t } from 'decentraland-dapps/dist/modules/translation/utils'
import { Button, Loader, SelectField, Field, DropdownProps, Form } from 'decentraland-ui'
import meetOnDecentralandImg from '../../../assets/images/meet-on-decentraland.png'
import { locations } from '../../../modules/routing/locations'
import { signedFetch } from '../../../utils/auth'
import { isErrorMessage } from '../../../utils/errors'
import { flatFetch } from '../../../utils/flat-fetch'
import { addServerToPreviouslyLoaded } from '../../../utils/worldServers'
import { PageLayout } from '../../PageLayout'
import { Props } from './ConnectToWorld.types'
import styles from './ConnectToWorld.module.css'
function ConnectToWorld(props: Props) {
const [selectedServer, setSelectedServer] = useState('')
const [error, setError] = useState<string>('')
const [availableServers, setAvailableServers] = useState<string[]>([])
const [isConnectingToServer] = useState(false)
const { isLoading, loggedInAddress, identity, previouslyLoadedServers, worldsContentServerUrl, onSubmitConnectForm } = props
const navigate = useNavigate()
useEffect(() => {
if (!loggedInAddress && !isLoading) {
navigate(locations.signIn(locations.root(worldsContentServerUrl)))
}
}, [isLoading, loggedInAddress])
useEffect(() => {
if (previouslyLoadedServers) {
setAvailableServers(previouslyLoadedServers)
setSelectedServer(previouslyLoadedServers[0])
}
}, [previouslyLoadedServers, setAvailableServers])
const handleChange = useCallback(
(e: ChangeEvent<HTMLInputElement>) => {
setError('')
setSelectedServer(e.target.value)
},
[setSelectedServer]
)
const handleSelectChange = useCallback(
(_event: React.SyntheticEvent<HTMLElement>, { value }: DropdownProps) => {
setError('')
const newOption = value as string
if (!availableServers.includes(newOption)) setAvailableServers([...availableServers, newOption])
setSelectedServer(newOption)
},
[availableServers, setAvailableServers, setSelectedServer]
)
async function livekitConnect(identity: AuthIdentity, worldServer: string, worldName: string) {
const aboutResponse = await flatFetch(`${worldServer}/world/${worldName}/about`)
console.log(aboutResponse.text)
if (aboutResponse.status === 200) {
const url = JSON.parse(aboutResponse.text!)
['comms']['fixedAdapter'].replace('signed-login:', '')
.replace('get-comms-adapter', 'cast-adapter')
const response = await signedFetch(
url,
identity,
{
method: 'POST'
},
{
signer: 'dcl:explorer',
intent: 'dcl:explorer:comms-handshake'
}
)
if (response.status === 200) {
console.log(response.text)
return JSON.parse(response.text!)
} else {
let message = ''
try {
message = JSON.parse(response.text || '')?.message
} catch (e) {
message = response.text || ''
}
throw Error(message)
}
// throw Error(`Failed to connect to LiveKit: ${JSON.stringify(response.text || response.json?.message)}`)
} else if (aboutResponse.status === 404) {
throw Error(`World ${worldName} not found`)
}
throw Error('An error has occurred')
}
const handleClick = useCallback(
async (event: React.MouseEvent<HTMLButtonElement>) => {
event.preventDefault()
setError('')
try {
if (!identity) return
const response: { url: string; token: string } = await livekitConnect(identity, worldsContentServerUrl, selectedServer)
onSubmitConnectForm(response.url, response.token)
addServerToPreviouslyLoaded(selectedServer)
navigate(`/meet/${encodeURIComponent(response.url)}?token=${encodeURIComponent(response.token)}`)
} catch (error) {
console.error('ERROR livekit connect', error)
if (isErrorMessage(error)) setError(error.message)
}
},
[identity, selectedServer, onSubmitConnectForm]
)
return (
<PageLayout>
{isLoading || isConnectingToServer ? (
<Loader active />
) : (
<div className={styles.ConnectToWorld}>
<div className={styles.content}>
<h4 className={styles.title}>{t('connect_to_world.title')}</h4>
<p className={styles.description}>{t('connect_to_world.description')}</p>
<img
className={styles.img}
src={meetOnDecentralandImg}
alt={t('connect_to_world.image_alt')}
aria-label={t('connect_to_world.image_alt')}
/>
<Form className={styles.form}>
<div className={styles.inputContainer}>
<label className={styles.label} htmlFor="server">
{t('connect_to_world.input_label')}
</label>
{availableServers.length > 0 ? (
<SelectField
value={selectedServer}
options={availableServers.map(server => ({
value: server,
text: server
}))}
onAddItem={handleSelectChange}
onChange={handleSelectChange}
allowAdditions
error={!!error}
message={error}
/>
) : (
<Field
name="server"
value={selectedServer}
onChange={handleChange}
placeholder={t('connect_to_world.input_placeholder')}
error={!!error}
message={error}
onEnter={handleClick}
/>
)}
</div>
<Button primary onClick={handleClick} fluid disabled={!selectedServer} type="submit">
{t('connect_to_world.cta')}
</Button>
</Form>
</div>
</div>
)}
</PageLayout>
)
}
export default ConnectToWorld