Skip to content
This repository was archived by the owner on Oct 22, 2024. It is now read-only.

feat: send events saved before users opt-in#472

Merged
vinistevam merged 6 commits intomainfrom
455-flush-events-saved-before-users-opt-in-out
Feb 9, 2023
Merged

feat: send events saved before users opt-in#472
vinistevam merged 6 commits intomainfrom
455-flush-events-saved-before-users-opt-in-out

Conversation

@vinistevam
Copy link
Copy Markdown
Contributor

@vinistevam vinistevam commented Jan 27, 2023

Overview

The aim of this PR is to implement a method to flush events saved before users opt-in/opt-out and as a prerequisite of this ticket a mechanism to get the option chosen by the users on the metrics page.

Changes

  • Exposed track method on the electron bridge and handle it in the main process.
  • Removed participateInDesktopMetrics in metrics-service.ts and used readPersistedSettingFromAppState to get metametricsOptIn.
  • create sendPendingEvents() to send events saved before the user opt-in.
  • added event whenever the user submits OTP and an error is triggered because of timeout.

Others

Issues

resolves: #455 and #464

@vinistevam vinistevam marked this pull request as ready for review January 30, 2023 08:45
@vinistevam vinistevam requested a review from a team January 30, 2023 08:45
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think we don't need to check the type of value anymore, I fixed it last week and will return either true or false

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'm removing this part. 👍
Just to confirm, Is it going to return false if a user has not opt-in/opt-out yet?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

For the initial start of the app(first time running app) it will not return false
Because there won't be any stored Redux state, this is the reason we've introduced defaultValue property to this method.

But on the second time when user execute the app, it will definitely return false

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Amazing that is exactly what we will need.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Would it be better if we also set stored events to the empty array?
this.store.set('eventsBeforeMetricsOptIn', []);
It could get overloaded if the user switch back and forth

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

100% I'm changing it now.

@vinistevam vinistevam force-pushed the 455-flush-events-saved-before-users-opt-in-out branch 3 times, most recently from 9eb0ca7 to a6dbdc8 Compare February 1, 2023 10:06
@vinistevam vinistevam force-pushed the 455-flush-events-saved-before-users-opt-in-out branch 4 times, most recently from 8ebb535 to f59367f Compare February 1, 2023 16:02
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Would this be more useful after constructing eventToTrack as it would contain more info?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Given we explicitly want to use the three states here, I think this is a little dangerous as we have to be exact with all our conditions despite it returning a boolean.

Perhaps it would be safer and more obvious if we return an enum such as MetricsDecision with values like ENABLED, DISABLED, and PENDING?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Great point, I'm pushing the changes you suggested for this method.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

getMetricsDecision?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do we not need the inverse condition here also to clear the array if we get a confirmed negative? Otherwise the pending events will just sit in memory until the app is restarted?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

yeap, I just changed it while I was refactoring this method. 💜

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

sendPendingEvents?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why are we saving every event? Is this part of the first time checks? Won't we only need to save unique event names?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This was actually one of the behaviours that I've mimed from the extensions. Initially, I thought would be complementary to the first-time check solution but turns out it end up being much more simple the solution PR I can remove it for simplicity in the PR 483.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Will we ever have more than one analytics method or are we just matching the format of the store bridges?

If so, shall we push for simplicity and just have a track method directly on the electronBridge?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

In the PR to track first-time events I'm including identify. There are also page and screen that the extension uses and we might need to integrate them depending on how we move further with the metrics.

Copy link
Copy Markdown
Member

@matthewwalsh0 matthewwalsh0 Feb 2, 2023

Choose a reason for hiding this comment

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

If we think of the UI as purely a display of the background state, then we don't want to couple our metrics to the UI, but to the underlying background logic whenever possible. This ensures we're tracking events as low as possible in case we remove it from the UI and forget, or the UI failures for whatever reason.

This invalid OTP event for example should be fired from: packages/app/src/app/pairing.ts.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Totally, I'm removing it from the UI this was more to test the bridge.

@vinistevam vinistevam force-pushed the 455-flush-events-saved-before-users-opt-in-out branch from abb6d50 to 919ced6 Compare February 2, 2023 21:54
@vinistevam vinistevam changed the title feat: flush events saved before users opt-in feat: send events saved before users opt-in Feb 3, 2023
@vinistevam vinistevam force-pushed the 455-flush-events-saved-before-users-opt-in-out branch from bb2db6c to 544efea Compare February 6, 2023 18:11
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Doesn't analytics.identify also send a request to Segment?

If so, shouldn't we also maintain an array of these identify requests while pending to be sent later?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, It sends to Segment but only when users agree. In the PR infrastructure for the first time event I'm saving the traits locally because traits are attached whenever we send an event using track method. Currently, that is the important part for us, but we still need to discuss the usage of identity with the Data & Analytics team.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Sorry if i'm missing something but won't the current code send requests to Segment if the decision is still pending?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@vinistevam what @matthewwalsh0 is saying is that even on that PR we are sending an identity event to Segment even if the MetricsDecision.PENDING.. we should only send when MetricsDecision.ENABLED.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Sorry guys, totally my fault. I pushed a fix for that only allowing identify to send to segment when MetricsDecision.ENABLED

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We should validate right at the top of this method. There's no point in setting a desktopMetricsId, or building the context on the event to track, if the user does not want to be tracked.
Plus having early returns makes the code much easier to read.

if (metricsDecision === MetricsDecision.DISABLED) {
    return;
}
... code ...

if (metricsDecision === MetricsDecision.PENDING) {
    this.eventsSavedBeforeMetricsDecision.push(eventToTrack);
    return;
}

...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Great point, I'm pushing this change.

Comment on lines 103 to 115
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This seems overly complex and has some side effects. getMetricsDecision should not be responsible for sendPendingEvents or cleanPendingEvents..
Those are consequences of the decision. We should move those out of this method (probably to the place where the desktopMetricsOptIn value is stored.. like when user opts in/out we should get an event on the main process notifying it to send or clean pending events.. Probably the event is already getting to the main process on ipcMain.handle(${name}-store-set`. Not sure if reusing that would be the best though or if we should add another specific event for it.)

Removing those would also make this method much much simpler. Could be something like this

const defaultValue = undefined;

...

if (desktopMetricsOptIn === defaultValue) {
  return MetricsDecision.PENDING;
}

return desktopMetricsOptIn === true ? MetricsDecision.ENABLED : MetricsDecision.DISABLED;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agree, I created a new event in the electron bridge to be invoked when users take the metrics decision and that will be responsible to send or clean the events saved before the user's decision.

@vinistevam vinistevam force-pushed the 455-flush-events-saved-before-users-opt-in-out branch 2 times, most recently from 32df9e1 to 634f27c Compare February 8, 2023 05:20
@vinistevam vinistevam force-pushed the 455-flush-events-saved-before-users-opt-in-out branch from 634f27c to 0bbaf10 Compare February 8, 2023 19:30
const metricsDecision = this.getMetricsDecision();
if (metricsDecision === MetricsDecision.DISABLED) {
return;
} else if (metricsDecision === MetricsDecision.PENDING) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Remove else if return. We want to enable no-else-return (https://eslint.org/docs/latest/rules/no-else-return). (I though that we had it enabled.. but apparently not).

if (...) {
    return;
}

if (...) {
    return
}

return

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

thanks for pointing that out.
Just to keep documented here as well, it was fixed on commit: dc0c381

@vinistevam vinistevam merged commit fbd57b1 into main Feb 9, 2023
@vinistevam vinistevam deleted the 455-flush-events-saved-before-users-opt-in-out branch February 10, 2023 08:35
@cryptotavares cryptotavares mentioned this pull request Mar 1, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Segment] Implement flush events that are saved before users OptIn/out

4 participants