fix: resolve analytics data gaps and resize validation failures

- Fix resize 20% failure rate: add Zod refine requiring at least one
  dimension, enforce integer/max constraints, clamp percentage scaling
  to minimum 1px, and guard against missing metadata in withoutEnlargement
- Fix PostHog init race condition: move consent check before async import
  so frontend events (search, pageview) are no longer silently dropped
- Fix identify() passing nested $set/$set_once wrappers instead of flat
  properties, so version person property now appears on PostHog profiles
- Add error_code and error_message to failed tool_used analytics events
  for debugging tool failures from PostHog
This commit is contained in:
SnapOtter
2026-05-06 23:21:45 +08:00
parent c9b24ada78
commit 979a833978
9 changed files with 50 additions and 41 deletions
+7 -5
View File
@@ -5,7 +5,7 @@ import { Toaster } from "sonner";
import { ConnectionMonitor } from "./components/common/connection-monitor";
import { KeyboardShortcutProvider } from "./components/common/keyboard-shortcut-provider";
import { useAuth } from "./hooks/use-auth";
import { identify, initAnalytics } from "./lib/analytics";
import { identify, initAnalytics, setAnalyticsConsent } from "./lib/analytics";
import { useAnalyticsStore } from "./stores/analytics-store";
// Lazy-load all pages so each page's JS (and its icons/deps) is only
@@ -190,11 +190,13 @@ export function App() {
)
return;
void (async () => {
setAnalyticsConsent(true);
await initAnalytics(analyticsConfig);
identify(analyticsConfig.instanceId, {
$set: { version: APP_VERSION },
$set_once: { instance_id: analyticsConfig.instanceId },
});
identify(
analyticsConfig.instanceId,
{ version: APP_VERSION },
{ instance_id: analyticsConfig.instanceId },
);
})();
}, [analyticsConfigLoaded, analyticsConfig, analyticsConsent.analyticsEnabled]);
+7 -9
View File
@@ -15,13 +15,10 @@ function scrubString(str: string): string {
}
export async function initAnalytics(config: AnalyticsConfig): Promise<void> {
if (initialized || !config.enabled) return;
if (initialized || !config.enabled || !consentGranted) return;
try {
const posthogJs = (await import("posthog-js")).default;
if (!consentGranted) {
return;
}
posthog =
posthogJs.init(config.posthogApiKey, {
api_host: config.posthogHost,
@@ -45,9 +42,6 @@ export async function initAnalytics(config: AnalyticsConfig): Promise<void> {
try {
if (config.sentryDsn) {
const Sentry = await import("@sentry/react");
if (!consentGranted) {
return;
}
Sentry.init({
dsn: config.sentryDsn,
sendDefaultPii: false,
@@ -110,10 +104,14 @@ export function setAnalyticsConsent(enabled: boolean): void {
}
}
export function identify(instanceId: string, properties: Record<string, unknown>): void {
export function identify(
instanceId: string,
properties: Record<string, unknown>,
propertiesSetOnce?: Record<string, unknown>,
): void {
if (!posthog || !consentGranted) return;
try {
posthog.identify(instanceId, properties);
posthog.identify(instanceId, properties, propertiesSetOnce);
} catch {
// never throw
}