Compare commits

..

1 Commits

Author SHA1 Message Date
22aee0b915 Improve live indications (Smaller, but maybe marginally less user friendly?) Shorten poll interval for boards due to backend optimizations.
ensure data is reloaded when tab/page becomes visible.
2026-05-21 20:06:33 +01:00
2 changed files with 47 additions and 61 deletions

View File

@@ -1,49 +0,0 @@
<div class="cont">
<div class="flash"></div>
<span class="txt">Live</span>
</div>
<style>
.cont {
display: inline-flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
font-family: 'URW Gothic', sans-serif;
font-weight: 600;
letter-spacing: 0.15ch;
}
.flash {
height: 0.6rem;
width: 0.6rem;
margin: 0 0.35rem;
border-radius: 50%;
background-color: forestgreen;
animation: live-pulse 1s ease-in-out infinite;
}
.txt {
animation: live-text-pulse 1s ease-in-out infinite;
}
@keyframes live-pulse {
0%, 100% {
box-shadow: 0 0 14px forestgreen;
filter: opacity(1) saturate(1);
}
50% {
box-shadow: 0 0 2px forestgreen;
filter: opacity(0.2) saturate(0.95);
}
}
@keyframes live-text-pulse {
0%, 100% {
opacity: 1;
}
50% {
opacity: 0.65;
}
}
</style>

View File

@@ -6,12 +6,14 @@
import { formatUkDateTime, formatUkTime } from '$lib/utils/time'; import { formatUkDateTime, formatUkTime } from '$lib/utils/time';
import StaffServicesTable from '$lib/components/ui/station-board/StaffServicesTable.svelte'; import StaffServicesTable from '$lib/components/ui/station-board/StaffServicesTable.svelte';
import LiveIndicator from '$lib/components/ui/LiveIndicator.svelte';
import StaffServicesGrid from '$lib/components/ui/station-board/StaffServicesGrid.svelte'; import StaffServicesGrid from '$lib/components/ui/station-board/StaffServicesGrid.svelte';
let { data } = $props(); let { data } = $props();
let now = $state(new Date()); let now = $state(new Date());
let live = $state(false); let dataAgeInSeconds = $derived(
Math.floor((now.getTime() - new Date(data.boardData.producedAt).getTime()) / 1000)
);
let live = $derived(dataAgeInSeconds < 45);
// Handle auto-refreshing & Wake-lock // Handle auto-refreshing & Wake-lock
$effect(() => { $effect(() => {
@@ -50,10 +52,8 @@
await invalidateAll(); await invalidateAll();
} catch (error) { } catch (error) {
console.error("Background data fetch failed: ", error) console.error("Background data fetch failed: ", error)
live = false;
} }
}, 60100); }, 20000);
live = true;
console.log("Polling started") console.log("Polling started")
}; };
@@ -66,11 +66,12 @@
await releaseWakeLock(); await releaseWakeLock();
}; };
const handleVisibilityChange = () => { const handleVisibilityChange = async () => {
if (document.hidden) { if (document.hidden) {
stopPolling(); stopPolling();
} else { } else {
startPolling(); startPolling();
await invalidateAll();
} }
}; };
@@ -115,17 +116,15 @@ $effect(() => {
</script> </script>
<section class="board-wrapper"> <section class="board-wrapper">
<div class="time-data"> <div class="time-data">
<span class="time-loaded">Updated: {formatUkDateTime(data.boardData.producedAt, true)}</span> <div class="loadedlive">
<div class="liveflash {live ? 'isLive' : 'notLive'}"></div>
<span class="time-loaded">Updated: {formatUkDateTime(data.boardData.producedAt, true)}</span>
</div>
<span class="time-now">{formatUkTime(now, true)}</span> <span class="time-now">{formatUkTime(now, true)}</span>
</div> </div>
{#if data.boardData.data.m?.length} {#if data.boardData.data.m?.length}
<StationAlertCard messages={data.boardData.data.m} /> <StationAlertCard messages={data.boardData.data.m} />
{/if} {/if}
{#if live}
<div class="live-indicator">
<LiveIndicator />
</div>
{/if}
{#if data.boardData.data.s?.length} {#if data.boardData.data.s?.length}
<div class="service-list-wrapper"> <div class="service-list-wrapper">
<StaffServicesGrid services={data.boardData.data.s} /> <StaffServicesGrid services={data.boardData.data.s} />
@@ -155,6 +154,42 @@ $effect(() => {
font-family: 'URW Gothic', sans-serif; font-family: 'URW Gothic', sans-serif;
} }
.loadedlive {
display: inline-flex;
justify-content: center;
align-items: center;
gap: 1rem;
}
.liveflash {
width: 12px;
height: 12px;
border-radius: 50%;
transition: all 0.3s ease;
}
div.notLive {
background-color: #ef4444;
animation:
led-blink 1s steps(1, start) infinite,
led-halo-expand 1s steps(1, start) infinite;
}
div.isLive {
background-color: #10b981;
animation:
led-pulse 2.5s infinite ease-in-out;
}
@keyframes led-blink {
0%,100%{opacity:1;}
50%{opacity:0.2;filter:saturate(0.2)}
}
@keyframes led-pulse{
0%,100%{opacity: 1;filter:saturate(1);box-shadow: 0 0 6px 1px rgba(16,185,129,0.4)}
50%{opacity: 0.2;filter:saturate(0.2);box-shadow:0 0 2px 0px rgba(16,185,129,0.1)}
}
.time-loaded, .time-loaded,
.time-now { .time-now {
font-variant-numeric: tabular-nums; font-variant-numeric: tabular-nums;