perf(island): switch to websocket to check connection status changes

This commit is contained in:
Julien Oculi 2024-07-19 13:43:41 +02:00
parent 01f5986e0b
commit 8f223d34da
3 changed files with 42 additions and 49 deletions

View file

@ -1,52 +1,41 @@
import { type Signal, useSignal } from '@preact/signals'
import { type Signal, useComputed, useSignal } from '@preact/signals'
import { useEffect } from 'preact/hooks'
import { JSX } from 'preact'
import { requestApi } from ':src/utils.ts'
type NetworkConnection = {
addEventListener: (
type: 'change',
listener: (event: Event) => void | Promise<void>,
) => void
}
import { IS_BROWSER } from '$fresh/runtime.ts'
export default function IsOnline(
{ online, offline }: { online?: string; offline: string },
) {
const status = useSignal(online)
const connection = 'connection' in navigator
? navigator.connection as NetworkConnection
: null
if (!IS_BROWSER) {
return <span class={'island__is_online'}>{offline}</span>
}
useEffect(() => {
// Update connection status on network changes
connection?.addEventListener('change', () => {
updateNetworkStatus(status, online, offline)
const status = useSignal(false)
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>
})
// Update connection status on 1st load
updateNetworkStatus(status, online, offline)
// Update connection status on interval (10s)
setInterval(() => updateNetworkStatus(status, online, offline), 10_000)
useEffect(() => openSocket(status, { id: undefined }), [])
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
})
return <>{status}</>
}
async function updateNetworkStatus(
status: Signal<JSX.Element>,
online: string | undefined,
offline: string,
) {
const isOnline = await requestApi<void, boolean>(
'/serviceworker/is-online',
'POST',
).catch(() => false)
if (isOnline) {
status.value = online === undefined
? <span style={{ display: 'none' }}></span>
: <span>{online}</span>
} else {
status.value = <span class={'island__is_online'}>{offline}</span>
}
}

View file

@ -13,6 +13,10 @@ export async function handler(request: Request, ctx: FreshContext) {
// Get response
const response = await ctx.next()
if (ctx.state?.skipMiddlewares) {
return response
}
// Use custom middleware hooks
useSecurityHeaders(request, response, ctx)
await useCsp(request, response, ctx)

View file

@ -1,9 +1,9 @@
import { SessionHandlers } from ':src/session/mod.ts'
import { respondApi } from ':src/utils.ts'
export const handler: SessionHandlers = {
POST() {
// Check if server is online
return respondApi<'success', true>('success', true)
GET(req: Request, ctx) {
const { response } = Deno.upgradeWebSocket(req)
ctx.state.skipMiddlewares = true
return response
},
}