// site-shell.jsx — hash router that ties the public site together.
// ── Stable visitor / session IDs ──────────────────────────────────────────
function getOrCreate(key, storage, ttlDays) {
try {
const raw = storage.getItem(key);
if (raw) {
const obj = JSON.parse(raw);
if (!obj.exp || Date.now() < obj.exp) return obj.id;
}
const id = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
const r = Math.random() * 16 | 0;
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
});
const exp = ttlDays ? Date.now() + ttlDays * 864e5 : null;
storage.setItem(key, JSON.stringify({ id, exp }));
return id;
} catch (_) { return 'anon'; }
}
function getVisitorIds() {
const visitor_id = getOrCreate('ljmu_vid', localStorage, 90); // 90-day persistent
const session_id = getOrCreate('ljmu_sid', sessionStorage, null); // browser-session
return { visitor_id, session_id };
}
function useRoute() {
const [route, setRoute] = React.useState(() => window.location.hash.slice(1) || "/");
const track = (page) => {
const ids = getVisitorIds();
fetch((window.__API_BASE__ || "/api") + "/track.php", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ page, ...ids }),
}).catch(() => {});
};
React.useEffect(() => {
track(window.location.hash.slice(1) || "/");
const h = () => {
const page = window.location.hash.slice(1) || "/";
setRoute(page);
track(page);
};
window.addEventListener("hashchange", h);
return () => window.removeEventListener("hashchange", h);
}, []);
const navigate = React.useCallback((to) => {
if (("#" + to) === window.location.hash) return;
window.location.hash = to;
window.scrollTo({ top: 0, behavior: "auto" });
}, []);
return [route, navigate];
}
function SiteShell() {
const [route, navigate] = useRoute();
const d = useSite();
const seg = route.split("/").filter(Boolean);
const top = seg[0] || "home";
const awardsLive = !!(d.festival && d.festival.awardsPage && d.festival.awardsPage.live);
React.useEffect(() => { window.scrollTo({ top: 0 }); }, [route]);
let page;
if (!top || top === "home") page = ;
else if (top === "programme") page = ;
else if (top === "film") page = ;
else if (top === "about") page = ;
else if (top === "jury") page = ;
else if (top === "archive") page = ;
else if (top === "news") page = seg[1] ? : ;
else if (top === "tickets") page = ;
else if (top === "submit") page = ;
else if (top === "contact") page = ;
else if (top === "awards") page = awardsLive ? : ;
else page = ;
return (
<>
{page}
>
);
}
function SiteApp() {
return (
);
}
Object.assign(window, { SiteApp });