feat: add shared analytics types, events, consent logic, and i18n strings

This commit is contained in:
ashim-hq
2026-04-22 18:58:16 +08:00
parent 5a45bcbc8f
commit 03df555e10
6 changed files with 219 additions and 0 deletions
+14
View File
@@ -0,0 +1,14 @@
import type { ConsentState } from "./types.js";
export function shouldShowConsent(consent: ConsentState, serverEnabled: boolean): boolean {
if (!serverEnabled) return false;
if (consent.analyticsEnabled !== null) return false;
if (consent.analyticsConsentShownAt === null) return true;
if (consent.analyticsConsentRemindAt === null) return false;
return Date.now() >= consent.analyticsConsentRemindAt;
}
export function isConsentEnabled(consent: ConsentState, serverEnabled: boolean): boolean {
if (!serverEnabled) return false;
return consent.analyticsEnabled === true;
}
+38
View File
@@ -0,0 +1,38 @@
export const ANALYTICS_EVENTS = {
TOOL_USED: "tool_used",
SEARCH: "search",
PIPELINE_EXECUTED: "pipeline_executed",
AI_BUNDLE_ACTION: "ai_bundle_action",
} as const;
export type AnalyticsEvent = (typeof ANALYTICS_EVENTS)[keyof typeof ANALYTICS_EVENTS];
export interface ToolUsedProperties {
tool_id: string;
status: "completed" | "failed";
duration_ms: number;
category: string;
is_ai_tool: boolean;
params?: Record<string, string | number | boolean>;
}
export interface SearchProperties {
query: string;
results_count: number;
clicked_tool_id?: string;
}
export interface PipelineExecutedProperties {
step_count: number;
tool_ids: string[];
is_batch: boolean;
file_count?: number;
duration_ms: number;
status: "completed" | "failed";
}
export interface AiBundleActionProperties {
bundle_id: string;
action: "installed" | "uninstalled";
duration_ms: number;
}
+14
View File
@@ -0,0 +1,14 @@
export interface AnalyticsConfig {
enabled: boolean;
posthogApiKey: string;
posthogHost: string;
sentryDsn: string;
sampleRate: number;
instanceId: string;
}
export interface ConsentState {
analyticsEnabled: boolean | null;
analyticsConsentShownAt: number | null;
analyticsConsentRemindAt: number | null;
}
+27
View File
@@ -301,6 +301,33 @@ export const en = {
help: "Help",
settings: "Settings",
},
analytics: {
consentTitle: "Make ashim better for you",
consentDescription:
'Anonymous product analytics — like "crop tool used" or "an error occurred" — help us fix bugs faster and build the tools you actually want.',
consentPrivacy: "Your images never leave your machine. Ever.",
whatShared: "What's shared:",
whatSharedItems: [
"Which tools you use",
"Error reports (no file data)",
"App version and performance",
],
whatNever: "What NEVER leaves your machine:",
whatNeverItems: [
"Your images, PDFs, and files",
"File names and contents",
"Any personal information",
],
consentProviders: "Data is sent to PostHog (analytics) and Sentry (errors) — both open-source.",
consentChangeable: "You can change this anytime in Settings.",
acceptButton: "Sure, sounds good",
declineButton: "Maybe later",
settingsTitle: "Product Analytics",
settingsDescription: "Share anonymous usage data to help improve ashim.",
settingsPrivacy: "Your images never leave your machine.",
settingsDisabledByAdmin: "Product analytics has been disabled by the server administrator.",
learnMore: "Learn more",
},
} as const;
export type TranslationKeys = typeof en;
+3
View File
@@ -1,3 +1,6 @@
export * from "./analytics/consent.js";
export * from "./analytics/events.js";
export * from "./analytics/types.js";
export * from "./constants.js";
export * from "./features.js";
export * from "./i18n/index.js";