fix(presence) : count visible browsers accurately
This commit is contained in:
+24
-9
@@ -50,10 +50,19 @@ function useHeartbeat<T>({
|
|||||||
}) {
|
}) {
|
||||||
const [value, setValue] = useState<T | null>(null);
|
const [value, setValue] = useState<T | null>(null);
|
||||||
const pending = useRef(false);
|
const pending = useRef(false);
|
||||||
|
const queuedActive = useRef<boolean | null>(null);
|
||||||
const send = useEffectEvent(async (active = true) => {
|
const send = useEffectEvent(async (active = true) => {
|
||||||
if (active && (document.visibilityState !== "visible" || pending.current)) return;
|
if (active && document.visibilityState !== "visible") return;
|
||||||
|
if (pending.current) {
|
||||||
|
queuedActive.current = active;
|
||||||
|
return;
|
||||||
|
}
|
||||||
pending.current = true;
|
pending.current = true;
|
||||||
|
let nextActive: boolean | null = active;
|
||||||
try {
|
try {
|
||||||
|
while (nextActive !== null) {
|
||||||
|
const requestActive = nextActive;
|
||||||
|
queuedActive.current = null;
|
||||||
const response = await fetch(endpoint, {
|
const response = await fetch(endpoint, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
@@ -62,38 +71,44 @@ function useHeartbeat<T>({
|
|||||||
...(identifyBy === "browser" ? { browserId: visitorId() } : {}),
|
...(identifyBy === "browser" ? { browserId: visitorId() } : {}),
|
||||||
guideId,
|
guideId,
|
||||||
...(section !== undefined ? { section } : {}),
|
...(section !== undefined ? { section } : {}),
|
||||||
active,
|
active: requestActive,
|
||||||
}),
|
}),
|
||||||
cache: "no-store",
|
cache: "no-store",
|
||||||
keepalive: !active,
|
keepalive: !requestActive,
|
||||||
});
|
});
|
||||||
if (!response.ok) throw new Error("presence unavailable");
|
if (!response.ok) throw new Error("presence unavailable");
|
||||||
if (active) setValue(await response.json() as T);
|
if (requestActive) setValue(await response.json() as T);
|
||||||
|
nextActive = queuedActive.current;
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
if (active) setValue(null);
|
if (active) setValue(null);
|
||||||
} finally {
|
} finally {
|
||||||
pending.current = false;
|
pending.current = false;
|
||||||
|
if (queuedActive.current !== null) {
|
||||||
|
const queued = queuedActive.current;
|
||||||
|
queuedActive.current = null;
|
||||||
|
void send(queued);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const immediate = window.setTimeout(() => void send(), 0);
|
const immediate = window.setTimeout(() => void send(), 0);
|
||||||
const interval = window.setInterval(() => void send(), PRESENCE_HEARTBEAT_MS);
|
const interval = window.setInterval(() => void send(), PRESENCE_HEARTBEAT_MS);
|
||||||
const refresh = () => {
|
const refresh = () => void send();
|
||||||
if (document.visibilityState === "visible") void send();
|
const visibilityChange = () => void send(document.visibilityState === "visible");
|
||||||
};
|
|
||||||
const leave = () => {
|
const leave = () => {
|
||||||
void send(false);
|
void send(false);
|
||||||
};
|
};
|
||||||
window.addEventListener("focus", refresh);
|
window.addEventListener("focus", refresh);
|
||||||
window.addEventListener("pagehide", leave);
|
window.addEventListener("pagehide", leave);
|
||||||
document.addEventListener("visibilitychange", refresh);
|
document.addEventListener("visibilitychange", visibilityChange);
|
||||||
return () => {
|
return () => {
|
||||||
window.clearInterval(interval);
|
window.clearInterval(interval);
|
||||||
window.clearTimeout(immediate);
|
window.clearTimeout(immediate);
|
||||||
window.removeEventListener("focus", refresh);
|
window.removeEventListener("focus", refresh);
|
||||||
window.removeEventListener("pagehide", leave);
|
window.removeEventListener("pagehide", leave);
|
||||||
document.removeEventListener("visibilitychange", refresh);
|
document.removeEventListener("visibilitychange", visibilityChange);
|
||||||
};
|
};
|
||||||
}, [endpoint, guideId, identifyBy, section]);
|
}, [endpoint, guideId, identifyBy, section]);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user