11'use client'
22
3- import { useState , useEffect , useRef } from 'react'
4- import {
5- AlertDialog ,
6- AlertDialogAction ,
7- AlertDialogCancel ,
8- AlertDialogContent ,
9- AlertDialogFooter ,
10- AlertDialogHeader ,
11- AlertDialogTitle
3+ import { useEffect , useRef , useState } from 'react'
4+ import {
5+ AlertDialog ,
6+ AlertDialogAction ,
7+ AlertDialogCancel ,
8+ AlertDialogContent ,
9+ AlertDialogFooter ,
10+ AlertDialogHeader ,
11+ AlertDialogTitle ,
1212} from '@/components/ui/alert-dialog'
1313import { Button } from '@/components/ui/button'
14- import { useGeneralStore } from '@/stores/settings/general/store'
1514import { createLogger } from '@/lib/logs/console-logger'
15+ import { useGeneralStore } from '@/stores/settings/general/store'
1616
1717declare global {
1818 interface Window {
@@ -23,6 +23,10 @@ declare global {
2323
2424const logger = createLogger ( 'TelemetryConsentDialog' )
2525
26+ // LocalStorage key for telemetry preferences
27+ const TELEMETRY_NOTIFIED_KEY = 'sim_telemetry_notified'
28+ const TELEMETRY_ENABLED_KEY = 'sim_telemetry_enabled'
29+
2630const trackEvent = ( eventName : string , properties ?: Record < string , any > ) => {
2731 if ( typeof window !== 'undefined' && window . __SIM_TELEMETRY_ENABLED ) {
2832 try {
@@ -38,15 +42,35 @@ const trackEvent = (eventName: string, properties?: Record<string, any>) => {
3842export function TelemetryConsentDialog ( ) {
3943 const [ open , setOpen ] = useState ( false )
4044 const [ settingsLoaded , setSettingsLoaded ] = useState ( false )
41- const telemetryEnabled = useGeneralStore ( state => state . telemetryEnabled )
42- const telemetryNotifiedUser = useGeneralStore ( state => state . telemetryNotifiedUser )
43- const setTelemetryEnabled = useGeneralStore ( state => state . setTelemetryEnabled )
44- const setTelemetryNotifiedUser = useGeneralStore ( state => state . setTelemetryNotifiedUser )
45- const loadSettings = useGeneralStore ( state => state . loadSettings )
46-
45+ const telemetryEnabled = useGeneralStore ( ( state ) => state . telemetryEnabled )
46+ const telemetryNotifiedUser = useGeneralStore ( ( state ) => state . telemetryNotifiedUser )
47+ const setTelemetryEnabled = useGeneralStore ( ( state ) => state . setTelemetryEnabled )
48+ const setTelemetryNotifiedUser = useGeneralStore ( ( state ) => state . setTelemetryNotifiedUser )
49+ const loadSettings = useGeneralStore ( ( state ) => state . loadSettings )
50+
4751 const hasShownDialogThisSession = useRef ( false )
4852 const isDevelopment = process . env . NODE_ENV === 'development'
4953
54+ // Check localStorage for saved preferences
55+ useEffect ( ( ) => {
56+ if ( typeof window === 'undefined' ) return
57+
58+ try {
59+ const notified = localStorage . getItem ( TELEMETRY_NOTIFIED_KEY ) === 'true'
60+ const enabled = localStorage . getItem ( TELEMETRY_ENABLED_KEY )
61+
62+ if ( notified ) {
63+ setTelemetryNotifiedUser ( true )
64+ }
65+
66+ if ( enabled !== null ) {
67+ setTelemetryEnabled ( enabled === 'true' )
68+ }
69+ } catch ( error ) {
70+ logger . error ( 'Error reading telemetry preferences from localStorage:' , error )
71+ }
72+ } , [ setTelemetryNotifiedUser , setTelemetryEnabled ] )
73+
5074 useEffect ( ( ) => {
5175 let isMounted = true
5276 const fetchSettings = async ( ) => {
@@ -62,57 +86,95 @@ export function TelemetryConsentDialog() {
6286 }
6387 }
6488 }
65-
89+
6690 fetchSettings ( )
67-
91+
6892 return ( ) => {
6993 isMounted = false
7094 }
7195 } , [ loadSettings ] )
7296
7397 useEffect ( ( ) => {
7498 if ( ! settingsLoaded ) return
75-
76- logger . debug ( 'Settings loaded state:' , {
77- telemetryNotifiedUser,
78- telemetryEnabled,
99+
100+ logger . debug ( 'Settings loaded state:' , {
101+ telemetryNotifiedUser,
102+ telemetryEnabled,
79103 hasShownInSession : hasShownDialogThisSession . current ,
80- environment : process . env . NODE_ENV
104+ environment : process . env . NODE_ENV ,
81105 } )
82-
106+
107+ const localStorageNotified =
108+ typeof window !== 'undefined' && localStorage . getItem ( TELEMETRY_NOTIFIED_KEY ) === 'true'
109+
83110 // Only show dialog if:
84111 // 1. Settings are fully loaded from the database
85- // 2. User has not been notified yet (according to database)
112+ // 2. User has not been notified yet (according to database AND localStorage )
86113 // 3. Telemetry is currently enabled (default)
87114 // 4. Dialog hasn't been shown in this session already (extra protection)
88115 // 5. We're in development environment
89- if ( settingsLoaded && ! telemetryNotifiedUser && telemetryEnabled && ! hasShownDialogThisSession . current && isDevelopment ) {
116+ if (
117+ settingsLoaded &&
118+ ! telemetryNotifiedUser &&
119+ ! localStorageNotified &&
120+ telemetryEnabled &&
121+ ! hasShownDialogThisSession . current &&
122+ isDevelopment
123+ ) {
90124 setOpen ( true )
91125 hasShownDialogThisSession . current = true
92126 } else if ( settingsLoaded && ! telemetryNotifiedUser && ! isDevelopment ) {
127+ // Auto-notify in non-development environments
93128 setTelemetryNotifiedUser ( true )
129+ if ( typeof window !== 'undefined' ) {
130+ try {
131+ localStorage . setItem ( TELEMETRY_NOTIFIED_KEY , 'true' )
132+ } catch ( error ) {
133+ logger . error ( 'Error saving telemetry notification to localStorage:' , error )
134+ }
135+ }
94136 }
95137 } , [ settingsLoaded , telemetryNotifiedUser , telemetryEnabled , setTelemetryNotifiedUser ] )
96138
97139 const handleAccept = ( ) => {
98140 trackEvent ( 'telemetry_consent_accepted' , {
99141 source : 'consent_dialog' ,
100- defaultEnabled : true
142+ defaultEnabled : true ,
101143 } )
102-
144+
103145 setTelemetryNotifiedUser ( true )
104146 setOpen ( false )
147+
148+ // Save preference to localStorage
149+ if ( typeof window !== 'undefined' ) {
150+ try {
151+ localStorage . setItem ( TELEMETRY_NOTIFIED_KEY , 'true' )
152+ localStorage . setItem ( TELEMETRY_ENABLED_KEY , 'true' )
153+ } catch ( error ) {
154+ logger . error ( 'Error saving telemetry preferences to localStorage:' , error )
155+ }
156+ }
105157 }
106158
107159 const handleDecline = ( ) => {
108160 trackEvent ( 'telemetry_consent_declined' , {
109161 source : 'consent_dialog' ,
110- defaultEnabled : false
162+ defaultEnabled : false ,
111163 } )
112-
164+
113165 setTelemetryEnabled ( false )
114166 setTelemetryNotifiedUser ( true )
115167 setOpen ( false )
168+
169+ // Save preference to localStorage
170+ if ( typeof window !== 'undefined' ) {
171+ try {
172+ localStorage . setItem ( TELEMETRY_NOTIFIED_KEY , 'true' )
173+ localStorage . setItem ( TELEMETRY_ENABLED_KEY , 'false' )
174+ } catch ( error ) {
175+ logger . error ( 'Error saving telemetry preferences to localStorage:' , error )
176+ }
177+ }
116178 }
117179
118180 return (
@@ -121,14 +183,13 @@ export function TelemetryConsentDialog() {
121183 < AlertDialogHeader >
122184 < AlertDialogTitle className = "text-2xl font-bold mb-2" > Telemetry</ AlertDialogTitle >
123185 </ AlertDialogHeader >
124-
186+
125187 < div className = "space-y-4 text-base text-muted-foreground" >
126188 < div >
127- To help us improve Sim Studio, we collect anonymous usage
128- data by default. This helps us understand which features are
129- most useful and identify areas for improvement.
189+ To help us improve Sim Studio, we collect anonymous usage data by default. This helps us
190+ understand which features are most useful and identify areas for improvement.
130191 </ div >
131-
192+
132193 < div className = "py-2" >
133194 < div className = "font-semibold text-foreground mb-2" > We only collect:</ div >
134195 < ul className = "list-disc pl-6 space-y-1" >
@@ -137,7 +198,7 @@ export function TelemetryConsentDialog() {
137198 < li > Performance metrics</ li >
138199 </ ul >
139200 </ div >
140-
201+
141202 < div className = "py-2" >
142203 < div className = "font-semibold text-foreground mb-2" > We never collect:</ div >
143204 < ul className = "list-disc pl-6 space-y-1" >
@@ -147,7 +208,7 @@ export function TelemetryConsentDialog() {
147208 < li > IP addresses or location data</ li >
148209 </ ul >
149210 </ div >
150-
211+
151212 < div className = "text-sm text-muted-foreground pt-2" >
152213 You can change this setting anytime in{ ' ' }
153214 < span className = "font-medium" > Settings → Privacy</ span > .
@@ -161,12 +222,10 @@ export function TelemetryConsentDialog() {
161222 </ Button >
162223 </ AlertDialogCancel >
163224 < AlertDialogAction asChild onClick = { handleAccept } >
164- < Button className = "flex-1" >
165- Continue with telemetry
166- </ Button >
225+ < Button className = "flex-1" > Continue with telemetry</ Button >
167226 </ AlertDialogAction >
168227 </ AlertDialogFooter >
169228 </ AlertDialogContent >
170229 </ AlertDialog >
171230 )
172- }
231+ }
0 commit comments