Skip to content

Commit 460bff5

Browse files
added GA4 detection of events PWA_DISMISSED and PWA_INSTALLED (#1479)
Co-authored-by: Kylee Fields <43586156+kyleecodes@users.noreply.github.com>
1 parent b3e0921 commit 460bff5

File tree

2 files changed

+44
-5
lines changed

2 files changed

+44
-5
lines changed

lib/hooks/usePWA.test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@ import { setPwaDismissed } from '../store/userSlice';
55
import { useAppDispatch, useTypedSelector } from './store';
66
import usePWA from './usePwa';
77

8+
// Mocking track functions for vercel and GA4
9+
jest.mock('@vercel/analytics/react', () => ({
10+
track: jest.fn(),
11+
}));
12+
jest.mock('@next/third-parties/google', () => ({
13+
sendGAEvent: jest.fn(),
14+
}));
15+
816
jest.mock('js-cookie');
917
jest.mock('./store', () => ({
1018
useTypedSelector: jest.fn(),

lib/hooks/usePwa.ts

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
'use client';
22

3+
import { useAppDispatch, useTypedSelector } from '@/lib/hooks/store';
4+
import { setPwaDismissed } from '@/lib/store/userSlice';
5+
import logEvent from '@/lib/utils/logEvent';
36
import Cookies from 'js-cookie';
47
import { useEffect, useMemo, useState } from 'react';
5-
import { setPwaDismissed } from '@/lib/store/userSlice';
6-
import { useAppDispatch, useTypedSelector } from '@/lib/hooks/store';
8+
import { PWA_DISMISSED, PWA_INSTALLED } from '../constants/events';
79

810
type UserChoice = Promise<{
911
outcome: 'accepted' | 'dismissed';
@@ -17,7 +19,7 @@ interface BeforeInstallPromptEvent extends Event {
1719
prompt(): Promise<void>;
1820
}
1921

20-
const PWA_DISMISSED = 'pwaBannerDismissed';
22+
const PWA_BANNER_DISMISSED = 'pwaBannerDismissed';
2123

2224
export default function usePWA() {
2325
const [bannerState, setBannerState] = useState<PwaBannerState>('Generic');
@@ -31,9 +33,35 @@ export default function usePWA() {
3133
[],
3234
);
3335

36+
const getPwaMetaData = useMemo(() => {
37+
const userAgent = window.navigator.userAgent;
38+
const platform = userAgent.includes('Win')
39+
? 'Windows'
40+
: userAgent.includes('Mac')
41+
? 'MacOS'
42+
: userAgent.includes('Linux')
43+
? 'Linux'
44+
: 'Unknown OS';
45+
46+
const browser = userAgent.includes('Chrome')
47+
? 'Chrome'
48+
: userAgent.includes('Firefox')
49+
? 'Firefox'
50+
: userAgent.includes('Safari')
51+
? 'Safari'
52+
: userAgent.includes('Edge')
53+
? 'Edge'
54+
: userAgent.includes('Edge')
55+
? 'Opera'
56+
: 'Unknown Browser';
57+
58+
return { browser, platform };
59+
}, []);
60+
3461
const declineInstallation = async () => {
3562
if (userCookiesAccepted) {
36-
Cookies.set(PWA_DISMISSED, 'true');
63+
Cookies.set(PWA_BANNER_DISMISSED, 'true');
64+
logEvent(PWA_DISMISSED, getPwaMetaData);
3765
}
3866
setBannerState('Hidden');
3967
await dispatch(setPwaDismissed(true));
@@ -50,12 +78,15 @@ export default function usePWA() {
5078
* immediately after installation. In some cases, isStandalone might
5179
* still return false momentarily, causing the banner to reappear.
5280
*/
81+
if (userCookiesAccepted) {
82+
logEvent(PWA_INSTALLED, getPwaMetaData);
83+
}
5384
window.beforeinstallpromptEvent = undefined;
5485
setBannerState('Hidden');
5586
};
5687

5788
useEffect(() => {
58-
const pwaBannerDismissedCookie = Boolean(Cookies.get(PWA_DISMISSED));
89+
const pwaBannerDismissedCookie = Boolean(Cookies.get(PWA_BANNER_DISMISSED));
5990
const isStandalone =
6091
typeof window !== 'undefined' && window.matchMedia('(display-mode: standalone)').matches;
6192
const isHidden =

0 commit comments

Comments
 (0)