fix(presence) : count visitors per browser
This commit is contained in:
+34
-10
@@ -14,22 +14,39 @@ export type EditorsByGuide = Record<
|
||||
Array<{ userId: string; name: string; sections: string[] }>
|
||||
>;
|
||||
|
||||
function tabId(storageKey: string): string {
|
||||
const existing = window.sessionStorage.getItem(storageKey);
|
||||
if (existing) return existing;
|
||||
const created = crypto.randomUUID();
|
||||
window.sessionStorage.setItem(storageKey, created);
|
||||
return created;
|
||||
const visitorIdStorageKey = "buzz-sheet:visitor-id";
|
||||
|
||||
let currentTabId: string | undefined;
|
||||
let fallbackVisitorId: string | undefined;
|
||||
|
||||
function tabId(): string {
|
||||
currentTabId ??= crypto.randomUUID();
|
||||
return currentTabId;
|
||||
}
|
||||
|
||||
function visitorId(): string {
|
||||
try {
|
||||
const existing = window.localStorage.getItem(visitorIdStorageKey);
|
||||
if (existing) return existing;
|
||||
const created = crypto.randomUUID();
|
||||
window.localStorage.setItem(visitorIdStorageKey, created);
|
||||
return created;
|
||||
} catch {
|
||||
fallbackVisitorId ??= crypto.randomUUID();
|
||||
return fallbackVisitorId;
|
||||
}
|
||||
}
|
||||
|
||||
function useHeartbeat<T>({
|
||||
endpoint,
|
||||
guideId,
|
||||
section,
|
||||
identifyBy,
|
||||
}: {
|
||||
endpoint: string;
|
||||
guideId: string | null;
|
||||
section?: string | null;
|
||||
identifyBy: "browser" | "tab";
|
||||
}) {
|
||||
const [value, setValue] = useState<T | null>(null);
|
||||
const send = useEffectEvent(async (active = true) => {
|
||||
@@ -38,7 +55,7 @@ function useHeartbeat<T>({
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
tabId: tabId(`buzz-sheet:${endpoint}:tab`),
|
||||
tabId: identifyBy === "browser" ? visitorId() : tabId(),
|
||||
guideId,
|
||||
...(section !== undefined ? { section } : {}),
|
||||
active,
|
||||
@@ -59,7 +76,9 @@ function useHeartbeat<T>({
|
||||
const refresh = () => {
|
||||
if (document.visibilityState === "visible") void send();
|
||||
};
|
||||
const leave = () => void send(false);
|
||||
const leave = () => {
|
||||
if (identifyBy === "tab") void send(false);
|
||||
};
|
||||
window.addEventListener("focus", refresh);
|
||||
window.addEventListener("pagehide", leave);
|
||||
document.addEventListener("visibilitychange", refresh);
|
||||
@@ -70,13 +89,17 @@ function useHeartbeat<T>({
|
||||
window.removeEventListener("pagehide", leave);
|
||||
document.removeEventListener("visibilitychange", refresh);
|
||||
};
|
||||
}, [endpoint, guideId, section]);
|
||||
}, [endpoint, guideId, identifyBy, section]);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
export function useVisitorPresence(guideId: string | null) {
|
||||
return useHeartbeat<VisitorCounts>({ endpoint: "/api/presence/visitors", guideId });
|
||||
return useHeartbeat<VisitorCounts>({
|
||||
endpoint: "/api/presence/visitors",
|
||||
guideId,
|
||||
identifyBy: "browser",
|
||||
});
|
||||
}
|
||||
|
||||
export function useAdminPresence(guideId: string | null, section: string | null = null) {
|
||||
@@ -84,5 +107,6 @@ export function useAdminPresence(guideId: string | null, section: string | null
|
||||
endpoint: "/api/presence/admin",
|
||||
guideId,
|
||||
section,
|
||||
identifyBy: "tab",
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user