1- import React , { useCallback , useEffect , useState } from "react"
1+ import React from "react"
22import ReactGA from "react-ga4"
33import packageJson from "../../../package.json"
44import { useLocation } from "react-router"
55import configStore from "../config"
6- import { confirmAlert } from "react-confirm-alert" // Import
7- import { reactotronAnalytics } from "../images"
8- import "react-confirm-alert/src/react-confirm-alert.css" // Import css
96
107// This is the Google Analytics 4 key for Reactotron
118// TODO: Change this to the correct key for production.
129const GA4_KEY = "G-WZE3E5XCQ7"
1310
1411type IAnalyticsEventCategory =
12+ | { category : "opt-out" ; actions : [ "opt-out" ] }
1513 | { category : "android" ; actions : [ "settings" , "reverse-tunnel" , "reload-app" , "shake-device" ] }
1614 | { category : "navigation" ; actions : [ "keyboard_shortcut" ] }
1715 | { category : "external_link" ; actions : [ "click" ] }
@@ -43,10 +41,10 @@ type IOptOutStatus = "unknown" | true | false
4341// It also handles the user's opt-out status.
4442// We use a custom alert to ask the user if they want to opt-in to analytics.
4543export const useAnalytics = ( ) => {
46- const [ initialized , setInitialized ] = useState ( false )
47- const [ optedOut , setOptedOut ] = useState < IOptOutStatus > ( "unknown" )
44+ const [ initialized , setInitialized ] = React . useState ( false )
45+ const [ optedOut , setOptedOut ] = React . useState < IOptOutStatus > ( "unknown" )
4846
49- useEffect ( ( ) => {
47+ React . useEffect ( ( ) => {
5048 const storeWatcher = configStore . onDidChange ( "analyticsOptOut" , ( newValue ) => {
5149 console . log ( "[analytics] user has changed opt-out status" , newValue )
5250 setOptedOut ( newValue as IOptOutStatus )
@@ -62,24 +60,21 @@ export const useAnalytics = () => {
6260
6361 if ( status === "unknown" ) {
6462 console . log ( `[analytics] user has not opted in or out` )
65- confirmAlert ( {
66- closeOnEscape : false ,
67- closeOnClickOutside : false ,
68- customUI : CustomAlert ,
69- } )
7063 } else {
7164 // If the user has opted out, we'll disable analytics
7265 setOptedOut ( status )
7366 setInitialized ( false )
7467 console . log ( `[analytics] user has opted ${ status ? "out" : "in" } ` )
7568 }
69+
70+ return status
7671 }
7772
7873 // Initialize analytics and set some system data like the app version and platform
7974 // as well as the mode we are running in. We don't want to send analytics events
8075 // during tests, so we disable them if we are running in test mode.
8176 // We also disable analytics if the user has opted out.
82- useEffect ( ( ) => {
77+ React . useEffect ( ( ) => {
8378 const initialize = ( ) => {
8479 const testMode = process . env . NODE_ENV === "test" // we don't want to send analytics events during tests
8580 ReactGA . initialize ( GA4_KEY , { testMode : testMode || optedOut === true } )
@@ -102,7 +97,7 @@ export const useAnalytics = () => {
10297 // This is the main function we use to send events throughout the app.
10398 // See documentation here for how to use react-ga4:
10499 // https://github.com/codler/react-ga4
105- const sendAnalyticsEvent = useCallback (
100+ const sendAnalyticsEvent = React . useCallback (
106101 ( event : UaEventOptions ) => {
107102 if ( ! optedOut ) {
108103 console . log ( "[analytics] Sending event" , event )
@@ -113,7 +108,7 @@ export const useAnalytics = () => {
113108 )
114109
115110 // Send a page view event
116- const sendPageViewAnalyticsEvent = useCallback (
111+ const sendPageViewAnalyticsEvent = React . useCallback (
117112 ( page : string ) => {
118113 if ( ! optedOut ) {
119114 console . log ( "[analytics] Sending page view event" , page )
@@ -124,7 +119,7 @@ export const useAnalytics = () => {
124119 )
125120
126121 // Send a keyboard shortcut event
127- const sendKeyboardShortcutAnalyticsEvent = useCallback (
122+ const sendKeyboardShortcutAnalyticsEvent = React . useCallback (
128123 ( label : string ) => {
129124 sendAnalyticsEvent ( {
130125 category : "navigation" ,
@@ -137,7 +132,7 @@ export const useAnalytics = () => {
137132 )
138133
139134 // Send a custom command event
140- const sendCustomCommandAnalyticsEvent = useCallback (
135+ const sendCustomCommandAnalyticsEvent = React . useCallback (
141136 ( command : string ) => {
142137 sendAnalyticsEvent ( {
143138 category : "custom_command" ,
@@ -150,7 +145,7 @@ export const useAnalytics = () => {
150145 )
151146
152147 // Send an external link event
153- const sendExternalLinkAnalyticsEvent = useCallback (
148+ const sendExternalLinkAnalyticsEvent = React . useCallback (
154149 ( label : string ) => {
155150 sendAnalyticsEvent ( {
156151 category : "external_link" ,
@@ -162,13 +157,24 @@ export const useAnalytics = () => {
162157 [ sendAnalyticsEvent ]
163158 )
164159
160+ const sendOptOutAnalyticsEvent = React . useCallback ( ( ) => {
161+ const event = {
162+ category : "opt-out" ,
163+ action : "opt-out" ,
164+ nonInteraction : false ,
165+ }
166+ console . log ( "[analytics] Sending opt-out event" , event )
167+ ReactGA . event ( event ) // this is the only time we send an event without checking the optedOut status
168+ } , [ ] )
169+
165170 return {
166171 initializeAnalytics,
167172 sendAnalyticsEvent,
168173 sendPageViewAnalyticsEvent,
169174 sendKeyboardShortcutAnalyticsEvent,
170175 sendCustomCommandAnalyticsEvent,
171176 sendExternalLinkAnalyticsEvent,
177+ sendOptOutAnalyticsEvent,
172178 }
173179}
174180
@@ -179,96 +185,7 @@ export const usePageTracking = () => {
179185 const location = useLocation ( )
180186 const { sendPageViewAnalyticsEvent } = useAnalytics ( )
181187
182- useEffect ( ( ) => {
188+ React . useEffect ( ( ) => {
183189 sendPageViewAnalyticsEvent ( location . pathname )
184190 } , [ location , sendPageViewAnalyticsEvent ] )
185191}
186-
187- // This is a custom alert that we use to ask the user if they want to opt-in to analytics
188- // We use this instead of the default alert because we want to style it to match our app.
189- // We inherit the styles from react-confirm-alert and override them as needed.
190- // Unfortunately, we can't use styled-components here because react-confirm-alert doesn't support it.
191- // This also means we have to hard-code the colors here instead of using our theme, which is not ideal.
192- const CustomAlert = ( { onClose } ) => {
193- return (
194- < div
195- className = "react-confirm-alert-overlay"
196- style = { {
197- background : "#1e1e1e" ,
198- } }
199- >
200- < div
201- className = "react-confirm-alert-body"
202- style = { {
203- background : "#1e1e1e" ,
204- color : "#c3c3c3" ,
205- boxShadow : "0 20px 75px rgba(255, 255, 255, 0.13)" ,
206- width : "80%" ,
207- maxWidth : "500px" ,
208- } }
209- >
210- < div
211- style = { {
212- display : "flex" ,
213- justifyContent : "center" ,
214- marginBottom : 20 ,
215- } }
216- >
217- < img
218- src = { reactotronAnalytics }
219- style = { {
220- height : 128 ,
221- } }
222- />
223- </ div >
224- < h1 > Opt in to Reactotron analytics?</ h1 >
225- < p > Help us improve Reactotron!</ p >
226- < p >
227- We'd like to collect anonymous usage data to enhance Reactotron's performance
228- and features. This data includes general usage patterns and interactions. No personal
229- information will be collected.
230- </ p >
231- < p >
232- You can change this setting at any time and by opting in, you can contribute to making
233- Reactotron better for everyone!
234- </ p >
235- < p > Would you like to participate?</ p >
236- < div
237- className = "react-confirm-alert-button-group"
238- style = { {
239- flexDirection : "column" ,
240- } }
241- >
242- < button
243- onClick = { ( ) => {
244- configStore . set ( "analyticsOptOut" , true )
245- onClose ( )
246- } }
247- style = { {
248- fontSize : 16 ,
249- backgroundColor : "#cf6a4c" ,
250- color : "#1e1e1e" ,
251- } }
252- >
253- No, don't collect any data
254- </ button >
255- < button
256- onClick = { ( ) => {
257- configStore . set ( "analyticsOptOut" , false )
258- onClose ( )
259- } }
260- style = { {
261- fontSize : 16 ,
262- marginTop : 20 ,
263- backgroundColor : "#8f9d6a" ,
264- color : "#1e1e1e" ,
265- fontWeight : "bold" ,
266- } }
267- >
268- Yes, I understand no personal information will be collected
269- </ button >
270- </ div >
271- </ div >
272- </ div >
273- )
274- }
0 commit comments