mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-27 13:06:20 +02:00
* feat(analytics): add daily client usage reporting (MUL-5125) Co-authored-by: multica-agent <github@multica.ai> * fix(analytics): clarify daily usage semantics (MUL-5125) Co-authored-by: multica-agent <github@multica.ai> * fix(analytics): resolve usage review blockers (MUL-5125) Co-authored-by: multica-agent <github@multica.ai> --------- Co-authored-by: Eve <eve@multica-ai.local> Co-authored-by: multica-agent <github@multica.ai>
77 lines
2.3 KiB
TypeScript
77 lines
2.3 KiB
TypeScript
"use client";
|
|
|
|
import { useCallback, useEffect, useRef } from "react";
|
|
import { getApi } from "../api";
|
|
import { useAuthStore } from "../auth";
|
|
import type { StorageAdapter } from "../types/storage";
|
|
import type { ClientIdentity } from "../platform/types";
|
|
import { getOrCreateInstallId } from "./install-id";
|
|
|
|
const LAST_REPORTED_PREFIX = "multica_client_usage_last_reported";
|
|
|
|
export function utcDay(date = new Date()): string {
|
|
return date.toISOString().slice(0, 10);
|
|
}
|
|
|
|
export function ClientUsageReporter({
|
|
storage,
|
|
identity,
|
|
}: {
|
|
storage: StorageAdapter;
|
|
identity?: ClientIdentity;
|
|
}) {
|
|
const userID = useAuthStore((state) => state.user?.id ?? null);
|
|
const userIDRef = useRef(userID);
|
|
userIDRef.current = userID;
|
|
const inFlight = useRef(false);
|
|
const rerun = useRef(false);
|
|
|
|
const reportIfNeeded = useCallback(async () => {
|
|
const platform = identity?.platform;
|
|
const activeUserID = userIDRef.current;
|
|
if (!activeUserID || (platform !== "web" && platform !== "desktop"))
|
|
return;
|
|
if (inFlight.current) {
|
|
rerun.current = true;
|
|
return;
|
|
}
|
|
|
|
const day = utcDay();
|
|
try {
|
|
const installID = getOrCreateInstallId(storage);
|
|
const reportedKey = `${LAST_REPORTED_PREFIX}:${activeUserID}:${platform}:${installID}`;
|
|
if (storage.getItem(reportedKey) === day) return;
|
|
inFlight.current = true;
|
|
await getApi().upsertClientUsage({ install_id: installID });
|
|
storage.setItem(reportedKey, day);
|
|
} catch {
|
|
// Usage reporting is best-effort and must never interrupt app startup.
|
|
} finally {
|
|
inFlight.current = false;
|
|
if (rerun.current) {
|
|
rerun.current = false;
|
|
void reportIfNeeded();
|
|
}
|
|
}
|
|
}, [identity?.platform, storage]);
|
|
|
|
useEffect(() => {
|
|
void reportIfNeeded();
|
|
}, [reportIfNeeded, userID]);
|
|
|
|
useEffect(() => {
|
|
const onFocus = () => void reportIfNeeded();
|
|
const onVisibility = () => {
|
|
if (document.visibilityState === "visible") void reportIfNeeded();
|
|
};
|
|
window.addEventListener("focus", onFocus);
|
|
document.addEventListener("visibilitychange", onVisibility);
|
|
return () => {
|
|
window.removeEventListener("focus", onFocus);
|
|
document.removeEventListener("visibilitychange", onVisibility);
|
|
};
|
|
}, [reportIfNeeded]);
|
|
|
|
return null;
|
|
}
|