205 lines
5.5 KiB
JavaScript

import { useContext, useEffect, useRef, useState } from 'react'
import { useLocation } from 'react-router-dom'
import { Flex, Card, Alert, Button } from 'antd'
import { LoadingOutlined } from '@ant-design/icons'
import { customAlphabet } from 'nanoid'
import AuthParticles from './AppParticles'
import FarmControlLogo from '../Logos/FarmControlLogo'
import ExclamationOctagonIcon from '../Icons/ExclamationOctagonIcon'
import CheckIcon from '../Icons/CheckIcon'
import ReloadIcon from '../Icons/ReloadIcon'
import { ApiServerContext } from '../Dashboard/context/ApiServerContext'
const createLaunchSession = customAlphabet(
'01abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
32
)
const AuthLaunch = () => {
const location = useLocation()
const hasRedirected = useRef(false)
const startTimeoutRef = useRef(null)
const pollTimeoutRef = useRef(null)
const { getAppLaunchSession } = useContext(ApiServerContext)
const [launchError, setLaunchError] = useState(false)
const [launchErrorMessage, setLaunchErrorMessage] = useState('')
const [launchSuccess, setLaunchSuccess] = useState(false)
const handleRefresh = () => {
window.location.reload()
}
useEffect(() => {
let cancelled = false
const redirect = new URLSearchParams(location.search).get('redirect')
const redirectType = new URLSearchParams(location.search).get(
'redirectType'
)
if (!redirect) {
setLaunchError(true)
setLaunchErrorMessage('No redirect provided!')
return
}
if (!redirect || hasRedirected.current) {
return
}
startTimeoutRef.current = setTimeout(() => {
if (cancelled) {
return
}
hasRedirected.current = true
const launchSession = createLaunchSession()
let launchCheckCount = 0
setLaunchError(false)
setLaunchErrorMessage('')
setLaunchSuccess(false)
let redirectWithLaunchSession = redirect
try {
const redirectUrl = new URL(redirect, window.location.origin)
redirectUrl.searchParams.set('launchSession', launchSession)
redirectWithLaunchSession = redirectUrl.toString()
} catch {
const hasQuery = redirect.includes('?')
const separator = hasQuery ? '&' : '?'
redirectWithLaunchSession = `${redirect}${separator}launchSession=${encodeURIComponent(
launchSession
)}`
}
const link = document.createElement('a')
link.href = redirectWithLaunchSession
link.style.display = 'none'
if (redirectType === 'app-localhost') {
link.addEventListener('click', (event) => {
event.preventDefault()
window.open(
redirectWithLaunchSession,
'farmcontrol-launch',
'width=480,height=640,menubar=no,toolbar=no,location=yes,status=no,resizable=yes,scrollbars=yes'
)
})
}
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
const checkLaunchSession = async () => {
launchCheckCount += 1
let launchComplete = false
try {
const launchStatus = await getAppLaunchSession(launchSession)
launchComplete = launchStatus?.complete === true
} catch {
launchComplete = false
}
if (cancelled) {
return
}
if (launchComplete) {
setLaunchSuccess(true)
return
}
if (launchCheckCount >= 10) {
setLaunchError(true)
setLaunchErrorMessage('Failed to open Farm Control.')
return
}
pollTimeoutRef.current = setTimeout(() => {
checkLaunchSession()
}, 1000)
}
checkLaunchSession()
}, 0)
return () => {
cancelled = true
hasRedirected.current = false
if (startTimeoutRef.current) {
clearTimeout(startTimeoutRef.current)
}
if (pollTimeoutRef.current) {
clearTimeout(pollTimeoutRef.current)
}
}
}, [getAppLaunchSession, location.search])
return (
<div
style={{
backgroundColor: 'black'
}}
>
<div
style={{
backgroundColor: 'black',
minHeight: '100vh',
transition: 'opacity 0.5s ease-in-out',
opacity: 1
}}
>
<AuthParticles />
<Flex
align='center'
justify='center'
vertical
style={{ height: '100vh' }}
gap={'large'}
>
<Card style={{ borderRadius: 20 }}>
<Flex vertical align='center'>
<FarmControlLogo style={{ fontSize: '500px', height: '40px' }} />
</Flex>
</Card>
{!launchError && !launchSuccess && (
<Alert
message='Launching Farm Control please wait...'
icon={<LoadingOutlined />}
showIcon
/>
)}
{launchError && (
<>
<Alert
message={launchErrorMessage}
icon={<ExclamationOctagonIcon />}
type='error'
showIcon
/>
<Button
icon={<ReloadIcon />}
onClick={handleRefresh}
size='large'
/>
</>
)}
{launchSuccess && (
<Alert
message='Launch successful! You may now close this window.'
icon={<CheckIcon />}
type='success'
showIcon
/>
)}
</Flex>
</div>
</div>
)
}
export default AuthLaunch