website/islands/IsOnline.tsx

59 lines
1.4 KiB
TypeScript

import { type Signal, useComputed, useSignal } from '@preact/signals'
import { useEffect } from 'preact/hooks'
type NetworkConnection = {
addEventListener: (
type: 'change',
listener: (event: Event) => void | Promise<void>,
) => void
}
declare global {
interface Navigator {
connection?: NetworkConnection
}
}
export default function IsOnline(
{ online, offline }: { online?: string; offline: string },
) {
const status = useSignal(true)
const displayed = useComputed(() => {
if (status.value && online) {
return <span>{online}</span>
}
if (status.value) {
return <span style={{ display: 'none' }}></span>
}
return <span class='island__is_online'>{offline}</span>
})
useEffect(() => {
openSocket(status)
navigator.connection?.addEventListener('change', () => {
fetch('/api/serviceworker/is-online')
.then(() => status.value = true)
.catch(() => status.value = false)
})
}, [])
return <>{displayed}</>
}
function openSocket(status: Signal<boolean>, timeout = 5_000) {
const socket = new WebSocket(
`wss://${location.host}/api/serviceworker/is-online`,
)
setTimeout(() => {
if (socket.OPEN || socket.CONNECTING) return
socket.close()
}, timeout)
socket.addEventListener('open', () => status.value = true)
socket.addEventListener('close', () => {
status.value = false
setTimeout(() => openSocket(status), timeout)
})
}