61 lines
1.6 KiB
TypeScript
61 lines
1.6 KiB
TypeScript
import { type Signal, useComputed, useSignal } from '@preact/signals'
|
|
import { useEffect } from 'preact/hooks'
|
|
import { IS_BROWSER } from '$fresh/runtime.ts'
|
|
|
|
type NetworkConnection = {
|
|
addEventListener: (
|
|
type: 'change',
|
|
listener: (event: Event) => void | Promise<void>,
|
|
) => void
|
|
}
|
|
|
|
export default function IsOnline(
|
|
{ online, offline }: { online?: string; offline: string },
|
|
) {
|
|
if (!IS_BROWSER) {
|
|
return online
|
|
? <span style={{ display: 'none' }}></span>
|
|
: <span>{online}</span>
|
|
}
|
|
|
|
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>
|
|
})
|
|
const connection = 'connection' in navigator
|
|
? navigator.connection as NetworkConnection
|
|
: null
|
|
useEffect(() => {
|
|
openSocket(status, { id: undefined })
|
|
connection?.addEventListener('change', () => {
|
|
fetch('/api/serviceworker/is-online')
|
|
.then(() => status.value = true)
|
|
.catch(() => status.value = false)
|
|
})
|
|
}, [])
|
|
|
|
return <>{displayed}</>
|
|
}
|
|
|
|
function openSocket(status: Signal<boolean>, ref: { id: number | undefined }) {
|
|
const socket = new WebSocket(
|
|
`wss://${location.host}/api/serviceworker/is-online`,
|
|
)
|
|
socket.addEventListener('open', () => {
|
|
status.value = true
|
|
clearInterval(ref.id)
|
|
})
|
|
socket.addEventListener('close', () => {
|
|
status.value = false
|
|
// Try reconnect every 5s
|
|
const id = setInterval(() => openSocket(status, ref), 5_000)
|
|
ref.id = id
|
|
})
|
|
}
|