feat: add PostHog analytics with full event tracking across app
Integrate self-hosted PostHog (SPM) with AnalyticsManager singleton wrapping all SDK calls. Adds ~40 type-safe events covering trip planning, schedule, progress, IAP, settings, polls, export, and share flows. Includes session replay, autocapture, network telemetry, privacy opt-out toggle in Settings, and super properties (app version, device, pro status, selected sports). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -39,6 +39,9 @@ struct SettingsView: View {
|
||||
// Travel Preferences
|
||||
travelSection
|
||||
|
||||
// Privacy
|
||||
privacySection
|
||||
|
||||
// Icon Generator
|
||||
iconGeneratorSection
|
||||
|
||||
@@ -79,6 +82,7 @@ struct SettingsView: View {
|
||||
Button {
|
||||
withAnimation(.easeInOut(duration: 0.2)) {
|
||||
AppearanceManager.shared.currentMode = mode
|
||||
AnalyticsManager.shared.track(.appearanceChanged(mode: mode.displayName))
|
||||
}
|
||||
} label: {
|
||||
HStack(spacing: 12) {
|
||||
@@ -181,7 +185,10 @@ struct SettingsView: View {
|
||||
Section {
|
||||
Toggle(isOn: Binding(
|
||||
get: { DesignStyleManager.shared.animationsEnabled },
|
||||
set: { DesignStyleManager.shared.animationsEnabled = $0 }
|
||||
set: {
|
||||
DesignStyleManager.shared.animationsEnabled = $0
|
||||
AnalyticsManager.shared.track(.animationsToggled(enabled: $0))
|
||||
}
|
||||
)) {
|
||||
Label {
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
@@ -257,6 +264,42 @@ struct SettingsView: View {
|
||||
.listRowBackground(Theme.cardBackground(colorScheme))
|
||||
}
|
||||
|
||||
// MARK: - Privacy Section
|
||||
|
||||
private var privacySection: some View {
|
||||
Section {
|
||||
Toggle(isOn: Binding(
|
||||
get: { !AnalyticsManager.shared.isOptedOut },
|
||||
set: { enabled in
|
||||
if enabled {
|
||||
AnalyticsManager.shared.optIn()
|
||||
} else {
|
||||
AnalyticsManager.shared.optOut()
|
||||
}
|
||||
}
|
||||
)) {
|
||||
Label {
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text("Share Analytics")
|
||||
.font(.body)
|
||||
.foregroundStyle(.primary)
|
||||
Text("Help improve SportsTime by sharing anonymous usage data")
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
} icon: {
|
||||
Image(systemName: "chart.bar.xaxis")
|
||||
.foregroundStyle(Theme.warmOrange)
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
Text("Privacy")
|
||||
} footer: {
|
||||
Text("No personal data is collected. Analytics are fully anonymous.")
|
||||
}
|
||||
.listRowBackground(Theme.cardBackground(colorScheme))
|
||||
}
|
||||
|
||||
// MARK: - About Section
|
||||
|
||||
private var aboutSection: some View {
|
||||
@@ -573,7 +616,7 @@ struct SettingsView: View {
|
||||
|
||||
Button {
|
||||
Task {
|
||||
await StoreManager.shared.restorePurchases()
|
||||
await StoreManager.shared.restorePurchases(source: "settings")
|
||||
}
|
||||
} label: {
|
||||
Label("Restore Purchases", systemImage: "arrow.clockwise")
|
||||
@@ -584,7 +627,7 @@ struct SettingsView: View {
|
||||
}
|
||||
.listRowBackground(Theme.cardBackground(colorScheme))
|
||||
.sheet(isPresented: $showPaywall) {
|
||||
PaywallView()
|
||||
PaywallView(source: "settings")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user