feat(pwa): add network status banner

This commit is contained in:
Julien Oculi 2024-07-17 13:50:37 +02:00
parent 5716284c73
commit 00c40f2569
5 changed files with 79 additions and 0 deletions

13
islands/IsOnline.css Normal file
View file

@ -0,0 +1,13 @@
.island__is_online {
position: sticky;
top: 0;
width: 100%;
text-align: center;
padding: var(--_gap-half);
/* invert accent color */
background: hsl(from #82c91e calc(h + 180) s l / 1);
font-family: var(--_font-family-accent);
color: white;
font-weight: bold;
z-index: 9999;
}

52
islands/IsOnline.tsx Normal file
View file

@ -0,0 +1,52 @@
import { type Signal, 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
}
export default function IsOnline(
{ online, offline }: { online?: string; offline: string },
) {
const status = useSignal(offline)
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<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

@ -3,6 +3,7 @@ import { type PageProps } from '$fresh/server.ts'
import { Footer } from ':components/Footer.tsx'
import { Header } from ':components/Header.tsx'
import { ProgressiveWebApp } from ':components/ProgressiveWebApp.tsx'
import IsOnline from ':islands/IsOnline.tsx'
export default function App({ Component }: PageProps) {
return (
@ -42,6 +43,9 @@ export default function App({ Component }: PageProps) {
</Head>
<body>
<Header />
<IsOnline
offline={'Vous êtes hors ligne / Serveur inaccessible'}
/>
<main f-client-nav>
<Partial name='main'>
<Component />

View file

@ -0,0 +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)
},
}

View file

@ -13,3 +13,4 @@
@import url('../../islands/MoreBox.css');
@import url('../../islands/AiChatBox.css');
@import url('../../islands/LoginForm.css');
@import url('../../islands/IsOnline.css');