Skip to content

Commit

Permalink
Send network speed to Amplitude analytics and Sentry (#358)
Browse files Browse the repository at this point in the history
  • Loading branch information
Vadorequest committed Jun 9, 2021
1 parent f5cce0d commit 681b3fe
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 1 deletion.
12 changes: 12 additions & 0 deletions src/app/components/BrowserPageBootstrap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ import useDataset from '@/modules/core/data/hooks/useDataset';
import { Customer } from '@/modules/core/data/types/Customer';
import { detectLightHouse } from '@/modules/core/lightHouse/lighthouse';
import { createLogger } from '@/modules/core/logging/logger';
import {
ClientNetworkConnectionType,
ClientNetworkInformationSpeed,
getClientNetworkConnectionType,
getClientNetworkInformationSpeed,
} from '@/modules/core/networkInformation/networkInformation';
import { configureSentryUser } from '@/modules/core/sentry/sentry';
import { cypressContext } from '@/modules/core/testing/contexts/cypressContext';
import {
Expand Down Expand Up @@ -81,6 +87,8 @@ const BrowserPageBootstrap = (props: BrowserPageBootstrapProps): JSX.Element =>
const theme = useTheme();
const isCypressRunning = detectCypress();
const isLightHouseRunning = detectLightHouse();
const networkSpeed: ClientNetworkInformationSpeed = getClientNetworkInformationSpeed();
const networkConnectionType: ClientNetworkConnectionType = getClientNetworkConnectionType();

// Configure Sentry user and track navigation through breadcrumb
configureSentryUser(userSession);
Expand All @@ -103,6 +111,8 @@ const BrowserPageBootstrap = (props: BrowserPageBootstrapProps): JSX.Element =>
locale,
userId,
userConsent,
networkSpeed,
networkConnectionType,
});

// Init the Cookie Consent popup, which will open on the browser
Expand Down Expand Up @@ -175,6 +185,8 @@ const BrowserPageBootstrap = (props: BrowserPageBootstrapProps): JSX.Element =>
hasUserGivenAnyCookieConsent: hasUserGivenAnyCookieConsent,
isCypressRunning,
isLightHouseRunning,
networkSpeed,
networkConnectionType,
}}
// XXX Do not use "userProperties" here, add default user-related properties in getAmplitudeInstance instead
// Because "event" had priority over "user event" and will be executed before
Expand Down
23 changes: 22 additions & 1 deletion src/modules/core/amplitude/amplitude.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { createLogger } from '@/modules/core/logging/logger';
import {
getClientNetworkInformationSpeed,
ClientNetworkInformationSpeed,
ClientNetworkConnectionType,
getClientNetworkConnectionType,
} from '@/modules/core/networkInformation/networkInformation';
import * as Sentry from '@sentry/node';
import { isBrowser } from '@unly/utils';
import {
Expand Down Expand Up @@ -54,6 +60,8 @@ type GetAmplitudeInstanceProps = {
locale: string;
userId: string;
userConsent: UserConsent;
networkSpeed: ClientNetworkInformationSpeed;
networkConnectionType: ClientNetworkConnectionType;
}

export const getAmplitudeInstance = (props: GetAmplitudeInstanceProps): AmplitudeClient | null => {
Expand All @@ -68,13 +76,17 @@ export const getAmplitudeInstance = (props: GetAmplitudeInstanceProps): Amplitud
locale,
userId,
userConsent,
networkSpeed,
networkConnectionType,
} = props;
const {
isUserOptedOutOfAnalytics,
hasUserGivenAnyCookieConsent,
} = userConsent;

Sentry.configureScope((scope) => { // See https://www.npmjs.com/package/@sentry/node
scope.setTag('networkSpeed', networkSpeed);
scope.setTag('networkConnectionType', networkConnectionType);
scope.setTag('iframe', `${isInIframe}`);
scope.setExtra('iframe', isInIframe);
scope.setExtra('iframeReferrer', iframeReferrer);
Expand Down Expand Up @@ -123,6 +135,8 @@ export const getAmplitudeInstance = (props: GetAmplitudeInstanceProps): Amplitud
// XXX Learn more about "setOnce" at https://github.com/amplitude/Amplitude-JavaScript/issues/223
visitor.setOnce('initial_lang', lang); // DA Helps figuring out if the initial language (auto-detected) is changed afterwards
visitor.setOnce('initial_locale', locale);
visitor.setOnce('initial_networkSpeed', networkSpeed);
visitor.setOnce('initial_networkConnectionType', networkConnectionType);
// DA This will help track down the users who discovered our platform because of an iframe
visitor.setOnce('initial_iframe', isInIframe);
visitor.setOnce('initial_iframeReferrer', iframeReferrer);
Expand All @@ -131,6 +145,8 @@ export const getAmplitudeInstance = (props: GetAmplitudeInstanceProps): Amplitud
visitor.setOnce('customer.ref', customerRef);
visitor.setOnce('lang', lang);
visitor.setOnce('locale', locale);
visitor.setOnce('networkSpeed', networkSpeed);
visitor.setOnce('networkConnectionType', networkConnectionType);
visitor.setOnce('iframe', isInIframe);
visitor.setOnce('iframeReferrer', iframeReferrer);

Expand Down Expand Up @@ -160,11 +176,14 @@ export const sendWebVitals = (report: NextWebVitalsMetricsReport): void => {
const amplitudeInstance: AmplitudeClient = amplitude.getInstance();
const universalCookiesManager = new UniversalCookiesManager();
const userData: UserSemiPersistentSession = universalCookiesManager.getUserData();
const networkSpeed: ClientNetworkInformationSpeed = getClientNetworkInformationSpeed();
const networkConnectionType: ClientNetworkConnectionType = getClientNetworkConnectionType();
console.log('networkConnectionType', networkConnectionType)

// https://help.amplitude.com/hc/en-us/articles/115001361248#settings-configuration-options
amplitudeInstance.init(process.env.NEXT_PUBLIC_AMPLITUDE_API_KEY, null, {
// userId: null,
userId: userData.id,
userId: userData?.id,
logLevel: process.env.NEXT_PUBLIC_APP_STAGE === 'production' ? 'DISABLE' : 'WARN',
includeGclid: false, // GDPR Enabling this is not GDPR compliant and must not be enabled without explicit user consent - See https://croud.com/blog/news/10-point-gdpr-checklist-digital-advertising/
includeReferrer: true, // https://help.amplitude.com/hc/en-us/articles/215131888#track-referrers
Expand Down Expand Up @@ -197,6 +216,8 @@ export const sendWebVitals = (report: NextWebVitalsMetricsReport): void => {
ref: process.env.NEXT_PUBLIC_CUSTOMER_REF,
},
report,
networkSpeed,
networkConnectionType,
});
// eslint-disable-next-line no-console
console.debug('report-web-vitals report sent to Amplitude');
Expand Down
77 changes: 77 additions & 0 deletions src/modules/core/networkInformation/networkInformation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { isBrowser } from '@unly/utils';

/**
* Connection speed used by the client (browser).
*
* Universal, will return "not-applicable" if executed on the server.
* Not available on all browsers, only a few of them provide such API.
*
* Experimental feature.
*
* @see https://developer.mozilla.org/fr/docs/Web/API/Navigator/connection
* @see https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation/effectiveType
*/
export type ClientNetworkInformationSpeed =
'2g' | '3g' | '4g' | 'slow-2g' // Native types
| 'unknown' // When the browser doesn't provide a "Connection" feature
| 'not-applicable'; // Connection speed isn't applicable on the server

/**
* Connection type used by the client (browser).
*
* Universal, will return "not-applicable" if executed on the server.
* Not available on all browsers, only a few of them provide such API.
*
* Experimental feature. Only supported by Chrome OS as of June 2021.
*
* @see https://developer.mozilla.org/fr/docs/Web/API/Navigator/connection
* @seehttps://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation/type
*/
export type ClientNetworkConnectionType =
'bluetooth' | 'cellular' | 'ethernet' | 'mixed' | 'none' | 'other' | 'unknown' | 'wifi' | 'wimax'
| 'not-applicable'; // Connection type isn't applicable on the server

/**
* Returns the device's network connection speed.
*
* Meant to be used outside of React components.
*
* XXX If you want to use this in a React component and react to network changes, you should rather use the "useNetworkInformation" hook from "web-api-hooks" library.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation/effectiveType
*/
export const getClientNetworkInformationSpeed = (): ClientNetworkInformationSpeed => {
let networkInformation;

if (isBrowser()) {
// @ts-ignore Experimental feature, not described in TypeScript at the moment
networkInformation = navigator?.connection || navigator?.mozConnection || navigator?.webkitConnection;
} else {
return 'not-applicable';
}

return networkInformation?.effectiveType || 'unknown';
};

/**
* Returns the device's network connection type.
*
* Meant to be used outside of React components.
*
* XXX If you want to use this in a React component and react to network changes, you should rather use the "useNetworkInformation" hook from "web-api-hooks" library.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation/type
*/
export const getClientNetworkConnectionType = (): ClientNetworkConnectionType => {
let networkInformation;

if (isBrowser()) {
// @ts-ignore Experimental feature, not described in TypeScript at the moment
networkInformation = navigator?.connection || navigator?.mozConnection || navigator?.webkitConnection;
} else {
return 'not-applicable';
}
console.log('networkInformation', networkInformation);

return networkInformation?.type;
};
12 changes: 12 additions & 0 deletions src/pages/[locale]/demo/built-in-utilities/hooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,18 @@ const HooksPage: NextPage<Props> = (props): JSX.Element => {

<hr />

<Alert color={'info'}>
If you want to add more utility hooks, here are some famous open-source projects you might want to check out:
<ul>
<li>
<ExternalLink href={'https://github.com/kripod/react-hooks/tree/master/packages/state-hooks'}><code>state-hooks</code></ExternalLink>
</li>
<li>
<ExternalLink href={'https://github.com/kripod/react-hooks/tree/master/packages/web-api-hooks'}><code>web-api-hooks</code></ExternalLink>
</li>
</ul>
</Alert>

</DemoPage>
</DemoLayout>
);
Expand Down

0 comments on commit 681b3fe

Please sign in to comment.