4bf05f6784
Instrument the play flow with 9 content-free custom events (game_start, art_style_select, style_image_upload, scene_reached, choice_select, vision_click, tts_toggle, fullscreen_toggle, play_heartbeat) to measure retention, engagement depth and session duration. Privacy is enforced by construction, not convention: - lib/analytics.ts types each event with a discriminated union, so a payload has no slot for free text — prompts, world guides, uploaded images and vision output can never reach analytics (compile-time guarantee, not a comment). - track() no-ops without window.umami and never throws into the app. - coarse 30s heartbeat fires only while the tab is visible. - script stays gated on NEXT_PUBLIC_UMAMI_* env (blank → no script), honours Do-Not-Track, and locks to an exact data-domains allowlist. - one-line on-site disclosure with a link, shown only when tracking is on. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
26 lines
947 B
TypeScript
26 lines
947 B
TypeScript
import Script from "next/script";
|
|
|
|
// Privacy-friendly, cookieless analytics (Umami). Both env vars unset →
|
|
// render nothing, so local dev and forks never report to our instance.
|
|
// - data-do-not-track: honour the visitor's browser Do Not Track setting.
|
|
// - data-domains (NEXT_PUBLIC_UMAMI_DOMAINS): extra guard — the tracker only
|
|
// fires when the live hostname matches, so even a fork that copied our env
|
|
// vars stays silent on a different domain. Unset → run on all hosts.
|
|
export function Analytics() {
|
|
const src = process.env.NEXT_PUBLIC_UMAMI_SRC;
|
|
const websiteId = process.env.NEXT_PUBLIC_UMAMI_WEBSITE_ID;
|
|
const domains = process.env.NEXT_PUBLIC_UMAMI_DOMAINS;
|
|
if (!src || !websiteId) return null;
|
|
|
|
return (
|
|
<Script
|
|
src={src}
|
|
data-website-id={websiteId}
|
|
data-do-not-track="true"
|
|
{...(domains ? { "data-domains": domains } : {})}
|
|
strategy="afterInteractive"
|
|
defer
|
|
/>
|
|
);
|
|
}
|