39 lines
1.2 KiB
TypeScript
39 lines
1.2 KiB
TypeScript
import { useState } from "react";
|
|||
|
|
|
||
|
|
const STORAGE_KEY = "verse-quote-app.visitor-id";
|
||
|
|
|
||
|
|
function generateId(): string {
|
||
|
|
if (typeof crypto !== "undefined" && "randomUUID" in crypto) {
|
||
|
|
return crypto.randomUUID();
|
||
|
|
}
|
||
|
|
return `v-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* A stable anonymous id for this browser, used to scope favorites without
|
||
|
|
* requiring a login. Not a real user identity — just enough to remember
|
||
|
|
* "your" favorites on this device.
|
||
|
|
*/
|
||
|
|
export function getVisitorId(): string {
|
||
|
|
if (typeof window === "undefined") return "";
|
||
|
|
try {
|
||
|
|
const existing = window.localStorage.getItem(STORAGE_KEY);
|
||
|
|
if (existing) return existing;
|
||
|
|
const created = generateId();
|
||
|
|
window.localStorage.setItem(STORAGE_KEY, created);
|
||
|
|
return created;
|
||
|
|
} catch {
|
||
|
|
return generateId();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* React hook wrapper around getVisitorId(). Routes that use this render only
|
||
|
|
* an SSR loading shell (see DEVELOPING.md), so reading localStorage during
|
||
|
|
* the initial client render here does not cause a hydration mismatch.
|
||
|
|
*/
|
||
|
|
export function useVisitorId(): string {
|
||
|
|
const [visitorId] = useState(() => getVisitorId());
|
||
|
|
return visitorId;
|
||
|
|
}
|