48d52ea898
Fixes #4339. PostHog telemetry was previously enabled by default for every install, which surprised privacy-conscious users on self-hosted setups. Behavior for new users only (existing users are migrated to a decision that preserves their current `telemetryEnabled` setting): - 90% are silently opted out on first launch. - 10% see a one-time consent prompt; accepting opts in, declining opts out. Decision is persisted via a new `readest-telemetry-decision` localStorage key so subsequent boots don't re-roll. PostHog now inits with `opt_out_capturing_by_default` so brand-new users never ping before the decision is finalized. New `TelemetryConsentDialog` uses the project's `btn-contrast` (theme-neutral) CTA and `eink-bordered` chassis so it renders correctly under `[data-eink]` without color-mode-only assumptions. Strings translated across all 33 locales. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
47 lines
1.6 KiB
TypeScript
47 lines
1.6 KiB
TypeScript
import posthog from 'posthog-js';
|
|
|
|
export const TELEMETRY_OPT_OUT_KEY = 'readest-telemetry-opt-out';
|
|
export const TELEMETRY_DECISION_KEY = 'readest-telemetry-decision';
|
|
|
|
export type TelemetryDecision = 'opt-in' | 'opt-out' | 'pending';
|
|
|
|
/** Fraction of new users shown the consent prompt; the rest are opted out silently. */
|
|
export const TELEMETRY_PROMPT_BUCKET_RATE = 0.1;
|
|
|
|
export const hasOptedOutTelemetry = () => {
|
|
return localStorage.getItem(TELEMETRY_OPT_OUT_KEY) === 'true';
|
|
};
|
|
|
|
export const getTelemetryDecision = (): TelemetryDecision | null => {
|
|
if (typeof window === 'undefined') return null;
|
|
const value = localStorage.getItem(TELEMETRY_DECISION_KEY);
|
|
if (value === 'opt-in' || value === 'opt-out' || value === 'pending') return value;
|
|
return null;
|
|
};
|
|
|
|
export const setTelemetryDecision = (decision: TelemetryDecision) => {
|
|
localStorage.setItem(TELEMETRY_DECISION_KEY, decision);
|
|
};
|
|
|
|
/** Returns true with probability TELEMETRY_PROMPT_BUCKET_RATE. */
|
|
export const rollIntoTelemetryPromptBucket = (rng: () => number = Math.random) => {
|
|
return rng() < TELEMETRY_PROMPT_BUCKET_RATE;
|
|
};
|
|
|
|
export const captureEvent = (event: string, properties?: Record<string, unknown>) => {
|
|
if (!hasOptedOutTelemetry()) {
|
|
posthog.capture(event, properties);
|
|
}
|
|
};
|
|
|
|
export const optInTelemetry = () => {
|
|
localStorage.setItem(TELEMETRY_OPT_OUT_KEY, 'false');
|
|
setTelemetryDecision('opt-in');
|
|
posthog.opt_in_capturing();
|
|
};
|
|
export const optOutTelemetry = () => {
|
|
localStorage.setItem(TELEMETRY_OPT_OUT_KEY, 'true');
|
|
setTelemetryDecision('opt-out');
|
|
posthog.opt_out_capturing();
|
|
};
|