diff --git a/islands/IsOnline.tsx b/islands/IsOnline.tsx index b68a5bb..ed322cf 100644 --- a/islands/IsOnline.tsx +++ b/islands/IsOnline.tsx @@ -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 -} +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 - - useEffect(() => { - // Update connection status on network changes - connection?.addEventListener('change', () => { - updateNetworkStatus(status, online, offline) - }) - // Update connection status on 1st load - updateNetworkStatus(status, online, offline) - // Update connection status on interval (10s) - setInterval(() => updateNetworkStatus(status, online, offline), 10_000) - }) - - return <>{status} -} - -async function updateNetworkStatus( - status: Signal, - online: string | undefined, - offline: string, -) { - const isOnline = await requestApi( - '/serviceworker/is-online', - 'POST', - ).catch(() => false) - - if (isOnline) { - status.value = online === undefined - ? - : {online} - } else { - status.value = {offline} + if (!IS_BROWSER) { + return {offline} } + + const status = useSignal(false) + const displayed = useComputed(() => { + if (status.value && online) { + return {online} + } + if (status.value) { + return + } + return {offline} + }) + useEffect(() => openSocket(status, { id: undefined }), []) + + return <>{displayed} +} + +function openSocket(status: Signal, 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 + }) } diff --git a/routes/_middleware.ts b/routes/_middleware.ts index 99b7e64..cfa4bcd 100644 --- a/routes/_middleware.ts +++ b/routes/_middleware.ts @@ -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) diff --git a/routes/api/serviceworker/is-online.tsx b/routes/api/serviceworker/is-online.tsx index 35fb742..1772bb6 100644 --- a/routes/api/serviceworker/is-online.tsx +++ b/routes/api/serviceworker/is-online.tsx @@ -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 }, }