Skip to content

Commit 20ee90d

Browse files
committed
feat: prefer real user ID in analytics tracking
When a user is authenticated, cache their real user ID in personal settings and use it as the distinct ID for PostHog events instead of the anonymous UUID. This improves analytics accuracy by linking events to actual user accounts.
1 parent 090deec commit 20ee90d

3 files changed

Lines changed: 29 additions & 1 deletion

File tree

pkg/analytics/posthog.go

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@ func SetAnalyticsPreference(enabled bool) error {
100100
return nil
101101
}
102102

103-
// GetOrCreateAnalyticsID returns a stable anonymous UUID for tracking, creating one if needed.
103+
// GetOrCreateAnalyticsID returns the user's distinct ID for tracking.
104+
// It prefers the real user ID if available, falling back to a stable anonymous UUID.
104105
func GetOrCreateAnalyticsID() string {
105106
fs := files.AppFs
106107
home, err := getHomeDir()
@@ -111,6 +112,9 @@ func GetOrCreateAnalyticsID() string {
111112
if err != nil {
112113
return ""
113114
}
115+
if settings.UserID != "" {
116+
return settings.UserID
117+
}
114118
if settings.AnalyticsID != "" {
115119
return settings.AnalyticsID
116120
}
@@ -119,6 +123,28 @@ func GetOrCreateAnalyticsID() string {
119123
return settings.AnalyticsID
120124
}
121125

126+
// SetUserID persists the real user ID into personal settings so that
127+
// future analytics events use it as the distinct ID.
128+
func SetUserID(userID string) {
129+
if userID == "" {
130+
return
131+
}
132+
fs := files.AppFs
133+
home, err := getHomeDir()
134+
if err != nil {
135+
return
136+
}
137+
settings, err := files.ReadPersonalSettings(fs, home)
138+
if err != nil {
139+
return
140+
}
141+
if settings.UserID == userID {
142+
return
143+
}
144+
settings.UserID = userID
145+
_ = files.WritePersonalSettings(fs, home, settings)
146+
}
147+
122148
// CaptureAnalyticsOptIn sends an event recording the user's analytics consent choice.
123149
// This is sent regardless of the user's choice so we can measure opt-in rates.
124150
func CaptureAnalyticsOptIn(optedIn bool) {

pkg/cmd/cmd.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ func NewBrevCommand() *cobra.Command { //nolint:funlen,gocognit,gocyclo // defin
169169
user, err := noLoginCmdStore.GetCurrentUser()
170170
if err == nil && user != nil {
171171
userID = user.ID
172+
analytics.SetUserID(userID)
172173
}
173174
if userID == "" {
174175
userID = analytics.GetOrCreateAnalyticsID()

pkg/files/files.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ type PersonalSettings struct {
1919
DefaultEditor string `json:"default_editor"`
2020
AnalyticsEnabled *bool `json:"analytics_enabled,omitempty"` // nil = never asked, true = opted in, false = opted out
2121
AnalyticsID string `json:"analytics_id,omitempty"` // stable anonymous ID for analytics
22+
UserID string `json:"user_id,omitempty"` // real user ID from the API, cached locally
2223
}
2324

2425
const (

0 commit comments

Comments
 (0)