Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(reactotron-app): Add basic analytics to Reactotron app with react-ga4 #1406

Draft
wants to merge 10 commits into
base: master
Choose a base branch
from

Conversation

markrickert
Copy link
Member

@markrickert markrickert commented Jan 18, 2024

This adds google analytics to the reactotron app for basic feature and usage metrics. It's implemented using react-ga4 but the codebase can easily be switched to another service is we want.

The first time the user opens the app after upgrading to this version, they'll be presented with this screen:

Screenshot 2024-01-17 at 4 02 39 PM

Clicking the red "NO" button changes their user setting to opt-out of anonymous analytics. A user that opens the app and clicks "NO" should have zero analytics calls send to google-analytics.

Clicking the green "YES" button saves the user setting and starts allowing tracking calls to be sent to google analytics.

We are tracking the following things:

  1. Screen views (with a hook that just listens to the path of react-router)
  2. Interface button click events
  3. Keyboard shortcut usage
  4. Custom command usage statistics including the title of the custom command that was run.

Users can opt in or out of this feature by going to Reactotron -> Settings (which opens the settings json file in their default editor.

Screenshot 2024-01-17 at 4 16 41 PM

Editing this file to have the analyticsOptOut be true or false and then restarting the app will turn this setting on and off.

Screenshot 2024-01-17 at 5 15 38 PM

Things that we need to do before this can move out of draft status:

  • Create a new google analytics property for Reactotron in our master InfiniteRed account and change the GA4_KEY variable.
  • Look for other things that could benefit from usage tracking
  • Brainstorm a way to track plugin usage (redux, mst, mmkv, etc)
  • Validate verbiage for the Opt-in/out screen.

@markrickert markrickert changed the title Add basic analytics to Reactotron app with react-ga4 feat(reactotron-app): Add basic analytics to Reactotron app with react-ga4 Jan 18, 2024
Copy link
Contributor

@Jpoliachik Jpoliachik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code looks good, nice work! 👍

I think its worth a UX discussion on the impact of showing that alert on startup.
I suppose some other dev tools do something similar - like this setting in Android Studio: (I don't remember if you get prompted this on first startup or not)
image

IMO the alert could also use a UI pass if Justin has some bandwidth

// We use this instead of the default alert because we want to style it to match our app.
// We inherit the styles from react-confirm-alert and override them as needed.
// Unfortunately, we can't use styled-components here because react-confirm-alert doesn't support it.
// This also means we have to hard-code the colors here instead of using our theme, which is not ideal.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just curious - how much is react-confirm-alert doing for us here?
wondering if its worth the dependency if its difficult to style. could be nice to have full control of the UI, but might not be worth the effort.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, when i put it in the app, i was thinking it would be useful as-is, but the more i played with it, the more customizations I wound up doing. I'll probably rip this out before i change this pr to no longer be a draft.

@markrickert markrickert mentioned this pull request Jan 24, 2024
Copy link
Contributor

@jonmajorc jonmajorc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good! just left a suggestion 💯

Comment on lines +135 to +139
sendAnalyticsEvent({
category: "overlay",
action: "OverlayDropImage",
label: "Success",
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed that each sendAnalyticsEvent has string values for category, action, and label. It may be worth using an enum for these constants. Then it's type-safe and easier to see what is available per key! Thoughts?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion @jonmajorc! Yeah, i need to clean up the types for sending events so we know exactly what we're going to send to the analytics server.

Copy link
Contributor

@morganick morganick left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a couple of comments. Appears to work as expected.

Low GAFO: I find myself parsing this statement incorrectly half the time:

[analytics] user has changed opt-out status false

The status could be unknown | opted-in | opted-out. Then the statement could be "user has changed status to opted-in"


// This is the Google Analytics 4 key for Reactotron
// TODO: Change this to the correct key for production.
const GA4_KEY = "G-WZE3E5XCQ7"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be an environment var?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think google analytics keys are considered private or sensitive information

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was less about privacy and more about being able to use CI to build it for the production key and have a dev key locally.

const status = configStore.get("analyticsOptOut") as IOptOutStatus

if (status === "unknown") {
console.log(`[analytics] user has not opted in or out`)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to keep this logging in?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's safe to leave in. It'll only ever console log once at the beginning of the app boot. It's useful for development to leave it in.

// We also disable analytics if the user has opted out.
useEffect(() => {
const initialize = () => {
const testMode = process.env.NODE_ENV === "test" // we don't want to send analytics events during tests
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about development? We probably don't care about our usage patterns while developing Reactotron.

Is there another environment besides prod where we would want it enabled?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, i did want it to log during development so i could see that it was working 😄

Good call to turn it off in DEV mode before we merge.

Screenshot 2024-01-24 at 9 57 53 AM

information will be collected.
</p>
<p>
You can change this setting at any time and by opting in, you can contribute to making
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do I opt back in or opt out again?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This verbiage isn't final yet, but basically you go to the Electron Menu and "Settings" - it opens a json file in your default editor and you change the json for analyticsOptOut to true. Safe the file and reboot reactotron. VIOLLA! no more logging.

@@ -1,3 +1,4 @@
export const reactotronLogo: string = require("./Reactotron-128.png").default
export const reactotronAnalytics: string = require("./Reactotron-analytics.png").default
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's throw this over to Justin and see what he'd like graphic wise.

useEffect(() => {
const timer = setTimeout(() => {
initializeAnalytics()
}, 250)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are we waiting for here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While developing, I found that there was the possibility of this component rendering a couple times. This delay with the clearTimeout is meant to only initialize the analytics once the electron renderer process has completely opened the app and react has stopped reacting. Not sure if there's a better way to handle this other than some memoizing

@markrickert
Copy link
Member Author

I also found this discussion from a VERY long time ago #723 where @jamonholmgren had already mocked up a screen and verbiage.

@morganick
Copy link
Contributor

@markrickert I rebased this on top of master. One thing I noticed when I was working with it was that setting the analyticsOptOut: "unknown" causes the application to break. It appears that's the default value and what should trigger the dialog to appear. Instead I get errors in console and a blank screen. I tested this both before and after rebasing master into this branch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants