172 lines
6.1 KiB
TypeScript
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
|