The bolts in the list keep asking who is reachable
Two things kept the charger list showing whatever was true when the tab opened. The map of who is online was replaced wholesale on every read, so a service that would not answer took every charger it knows about grey with it — and then the read marked the question asked, and the guard above it meant nothing asked again. One failed call and the list sat colourless until somebody pressed Refresh. It merges now, and only when something actually answered: a provider that answered overwrites its own entries, a provider that could not be reached leaves its last word standing. "We could not ask" is not an answer, and it is not "unknown" either. And it only ever asked once. There is a thirty-second poll now, running only while the tab is being looked at — not on the public tab, not while the page is hidden, and asking straight away on the way back rather than waiting out an interval that was never going to fire. The poll takes the reachability half alone, which is what the second argument is for. That is one call per connected service, back in about a quarter of a second. The per-charger views behind it are a dozen cloud endpoints each, and a background poll has no business spending those. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
dd636bbe03
commit
4bcf1305be
@@ -1,5 +1,5 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, onMounted, watch } from "vue";
|
import { ref, computed, onMounted, onUnmounted, watch } from "vue";
|
||||||
import { t } from "../i18n";
|
import { t } from "../i18n";
|
||||||
import { prefs } from "../prefs";
|
import { prefs } from "../prefs";
|
||||||
import { askConfirm } from "../lib/confirm.js";
|
import { askConfirm } from "../lib/confirm.js";
|
||||||
@@ -1010,29 +1010,74 @@ const chargerLive = ref({}); // provider charger id → the service's own record
|
|||||||
const chargerLiveLoading = ref(false);
|
const chargerLiveLoading = ref(false);
|
||||||
const chargerLiveLoaded = ref(false);
|
const chargerLiveLoaded = ref(false);
|
||||||
|
|
||||||
async function loadChargerLive(force = false) {
|
async function loadChargerLive(force = false, withDetails = true) {
|
||||||
const connected = chargerProviders.value.filter((p) => p.connected);
|
const connected = chargerProviders.value.filter((p) => p.connected);
|
||||||
if (chargerLiveLoading.value || connected.length === 0) return;
|
if (chargerLiveLoading.value || connected.length === 0) return;
|
||||||
if (chargerLiveLoaded.value && !force) return; // switching tabs is not a new question
|
if (chargerLiveLoaded.value && !force) return; // switching tabs is not a new question
|
||||||
chargerLiveLoading.value = true;
|
chargerLiveLoading.value = true;
|
||||||
const live = {};
|
const live = {};
|
||||||
|
let answered = false;
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
connected.map(async (p) => {
|
connected.map(async (p) => {
|
||||||
try {
|
try {
|
||||||
const res = await api.listProviderChargers(p.id);
|
const res = await api.listProviderChargers(p.id);
|
||||||
for (const c of res?.chargers || []) live[c.id] = c;
|
for (const c of res?.chargers || []) live[c.id] = c;
|
||||||
|
answered = true;
|
||||||
} catch {
|
} catch {
|
||||||
// A service that will not answer leaves its chargers unknown rather
|
// A service that will not answer leaves its chargers unknown rather
|
||||||
// than offline — this page cannot tell those two apart.
|
// than offline — this page cannot tell those two apart.
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
chargerLive.value = live;
|
// Merged rather than replaced, and only when something actually answered. A
|
||||||
chargerLiveLoaded.value = true;
|
// service that could not be reached used to take every charger it knows about
|
||||||
|
// grey with it and then mark the question asked, so the list sat colourless
|
||||||
|
// until somebody pressed Refresh. "We could not ask" is not an answer, and it
|
||||||
|
// is certainly not "unknown" — the last thing the service did say still
|
||||||
|
// stands, and a provider that did answer overwrites its own entries here.
|
||||||
|
if (answered) {
|
||||||
|
chargerLive.value = { ...chargerLive.value, ...live };
|
||||||
|
chargerLiveLoaded.value = true;
|
||||||
|
}
|
||||||
chargerLiveLoading.value = false;
|
chargerLiveLoading.value = false;
|
||||||
// The card's other half: the views that answer per charger rather than per
|
// The card's other half: the views that answer per charger rather than per
|
||||||
// account. Refreshing the card refreshes both.
|
// account. Skipped by the poll below — those are a dozen cloud endpoints per
|
||||||
loadChargerDetails(force);
|
// charger, and reachability is one call per service.
|
||||||
|
if (withDetails) loadChargerDetails(force);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Keeping the list's reachability current ---------------------------------
|
||||||
|
//
|
||||||
|
// Who is reachable changes on its own, so asking once when the tab opens left
|
||||||
|
// the bolts saying whatever was true when it did. This re-asks while the tab is
|
||||||
|
// actually being looked at: not while the page is hidden, not on the public tab,
|
||||||
|
// and not the per-charger views — one call per connected service, which came
|
||||||
|
// back in about a quarter of a second.
|
||||||
|
const LIVE_POLL_MS = 30000;
|
||||||
|
let livePoll = null;
|
||||||
|
|
||||||
|
function stopLivePoll() {
|
||||||
|
if (livePoll) {
|
||||||
|
clearInterval(livePoll);
|
||||||
|
livePoll = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function startLivePoll() {
|
||||||
|
stopLivePoll();
|
||||||
|
if (chargerTab.value !== "home" || document.hidden) return;
|
||||||
|
livePoll = setInterval(() => loadChargerLive(true, false), LIVE_POLL_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Coming back to a tab that has been hidden asks straight away rather than
|
||||||
|
// waiting out the rest of an interval that was never going to fire.
|
||||||
|
function onPageVisibility() {
|
||||||
|
if (document.hidden) {
|
||||||
|
stopLivePoll();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
loadChargerLive(true, false);
|
||||||
|
startLivePoll();
|
||||||
}
|
}
|
||||||
|
|
||||||
// The live half for one imported charger, or null when the service it came from
|
// The live half for one imported charger, or null when the service it came from
|
||||||
@@ -1838,16 +1883,24 @@ function cancelReset() {
|
|||||||
// public half never needs it.
|
// public half never needs it.
|
||||||
watch(chargerTab, (tab) => {
|
watch(chargerTab, (tab) => {
|
||||||
if (tab === "home") loadChargerLive();
|
if (tab === "home") loadChargerLive();
|
||||||
|
startLivePoll();
|
||||||
});
|
});
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
document.addEventListener("visibilitychange", onPageVisibility);
|
||||||
await loadHomeChargers();
|
await loadHomeChargers();
|
||||||
await loadChargerProviders();
|
await loadChargerProviders();
|
||||||
if (chargerTab.value === "home") loadChargerLive();
|
if (chargerTab.value === "home") loadChargerLive();
|
||||||
|
startLivePoll();
|
||||||
await loadCtlMode();
|
await loadCtlMode();
|
||||||
if (ctlActive.value) await loadChargers();
|
if (ctlActive.value) await loadChargers();
|
||||||
await refreshCtl();
|
await refreshCtl();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
stopLivePoll();
|
||||||
|
document.removeEventListener("visibilitychange", onPageVisibility);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
Reference in New Issue
Block a user