Skip to content

Commit f7d3172

Browse files
shakyShaneShane Osbourne
andauthored
ntp: add telemetry events for show/hide (#1280)
* ntp: add telemetry events for show/hide * some renaming --------- Co-authored-by: Shane Osbourne <[email protected]>
1 parent 2b0f898 commit f7d3172

File tree

12 files changed

+144
-13
lines changed

12 files changed

+144
-13
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"title": "NTP TelemetryEvent",
4+
"type": "object",
5+
"required": ["attributes"],
6+
"properties": {
7+
"attributes": {
8+
"oneOf": [
9+
{
10+
"type": "object",
11+
"title": "Stats Show More",
12+
"required": ["name", "value"],
13+
"properties": {
14+
"name": {
15+
"const": "stats_toggle"
16+
},
17+
"value": {
18+
"type": "string",
19+
"enum": ["show_more", "show_less"]
20+
}
21+
}
22+
},
23+
{
24+
"type": "object",
25+
"title": "Example Telemetry Event",
26+
"required": ["name"],
27+
"properties": {
28+
"name": {
29+
"const": "ntp_example"
30+
}
31+
}
32+
}
33+
]
34+
}
35+
}
36+
}

special-pages/pages/new-tab/app/favorites/favorites.service.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ export class FavoritesService {
2929
});
3030
}
3131

32+
name() {
33+
return 'FavoritesService';
34+
}
35+
3236
/**
3337
* @returns {Promise<{data: FavoritesData; config: FavoritesConfig}>}
3438
* @internal

special-pages/pages/new-tab/app/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import { callWithRetry } from '../../../shared/call-with-retry.js';
2424
* @throws Error
2525
*/
2626
export async function init(root, messaging, telemetry, baseEnvironment) {
27-
const result = await callWithRetry(() => messaging.init());
27+
const result = await callWithRetry(() => messaging.initialSetup());
2828

2929
// handle fatal exceptions, the following things prevent anything from starting.
3030
if ('error' in result) {

special-pages/pages/new-tab/app/new-tab.md

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ children:
2020

2121
## Notifications
2222

23-
- {@link "NewTab Messages".ContextMenuNotification `contextMenu`}
23+
### {@link "NewTab Messages".ContextMenuNotification `contextMenu`}
2424
- Sent when the user right-clicks in the page
2525
- Note: Other widgets might prevent this (and send their own, eg: favorites)
2626
- Sends: {@link "NewTab Messages".ContextMenuNotify}
@@ -41,10 +41,24 @@ children:
4141
}
4242
```
4343

44-
- {@link "NewTab Messages".ReportInitExceptionNotification `reportInitException`}
44+
### {@link "NewTab Messages".TelemetryEventNotification `telemetryEvent`}
45+
- These are generic events that might be useful to observe. For example, you can use these to decide when to send pixels.
46+
- Sends a standard format `{ attributes: { name: string', value?: any } }` - see {@link "NewTab Messages".TelemetryEventNotification `telemetryEvent`}
47+
- Example:
48+
49+
```json
50+
{
51+
"attributes": {
52+
"name": "stats_toggle",
53+
"value": "show_more"
54+
}
55+
}
56+
```
57+
58+
### {@link "NewTab Messages".ReportInitExceptionNotification `reportInitException`}
4559
- Sent when the application fails to initialize (for example, a JavaScript exception prevented it)
4660
- Sends: `{ message: string }` - see {@link "NewTab Messages".ReportInitExceptionNotify}
4761

48-
- {@link "NewTab Messages".ReportPageExceptionNotification `reportPageException`}
62+
### {@link "NewTab Messages".ReportPageExceptionNotification `reportPageException`}
4963
- Sent when the application failed after initialization (for example, a JavaScript exception prevented it)
5064
- Sends: `{ message: string }` - see {@link "NewTab Messages".ReportPageExceptionNotify}

special-pages/pages/new-tab/app/next-steps/next-steps.service.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ export class NextStepsService {
2525
});
2626
}
2727

28+
name() {
29+
return 'NextStepsService';
30+
}
31+
2832
/**
2933
* @returns {Promise<{data: NextStepsData; config: NextStepsConfig}>}
3034
* @internal

special-pages/pages/new-tab/app/privacy-stats/components/PrivacyStats.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Fragment, h } from 'preact';
22
import cn from 'classnames';
33
import styles from './PrivacyStats.module.css';
4-
import { useTypedTranslationWith } from '../../types.js';
4+
import { useMessaging, useTypedTranslationWith } from '../../types.js';
55
import { useContext, useState, useId, useCallback } from 'preact/hooks';
66
import { PrivacyStatsContext, PrivacyStatsProvider } from '../PrivacyStatsProvider.js';
77
import { useVisibility } from '../../widget-list/widget-config.provider.js';
@@ -133,6 +133,7 @@ export function Heading({ expansion, trackerCompanies, onToggle, buttonAttrs = {
133133

134134
export function PrivacyStatsBody({ trackerCompanies, listAttrs = {} }) {
135135
const { t } = useTypedTranslationWith(/** @type {enStrings} */ ({}));
136+
const messaging = useMessaging();
136137
const [formatter] = useState(() => new Intl.NumberFormat());
137138
const defaultRowMax = 5;
138139
const sorted = sortStatsForDisplay(trackerCompanies);
@@ -141,6 +142,11 @@ export function PrivacyStatsBody({ trackerCompanies, listAttrs = {} }) {
141142
const hasmore = sorted.length > visible;
142143

143144
const toggleListExpansion = () => {
145+
if (hasmore) {
146+
messaging.telemetryEvent({ attributes: { name: 'stats_toggle', value: 'show_more' } });
147+
} else {
148+
messaging.telemetryEvent({ attributes: { name: 'stats_toggle', value: 'show_less' } });
149+
}
144150
if (visible === defaultRowMax) {
145151
setVisible(sorted.length);
146152
}

special-pages/pages/new-tab/app/privacy-stats/integration-tests/privacy-stats.spec.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,33 @@ test.describe('newtab privacy stats', () => {
2727
await page.getByLabel('Hide recent activity').click();
2828
await page.getByLabel('Show recent activity').click();
2929
});
30+
test('sending a pixel when show more is clicked', async ({ page }, workerInfo) => {
31+
const ntp = NewtabPage.create(page, workerInfo);
32+
await ntp.reducedMotion();
33+
await ntp.openPage({ additional: { stats: 'many' } });
34+
await page.getByLabel('Show More', { exact: true }).click();
35+
await page.getByLabel('Show Less').click();
36+
const calls1 = await ntp.mocks.waitForCallCount({ method: 'telemetryEvent', count: 2 });
37+
expect(calls1.length).toBe(2);
38+
expect(calls1).toStrictEqual([
39+
{
40+
payload: {
41+
context: 'specialPages',
42+
featureName: 'newTabPage',
43+
method: 'telemetryEvent',
44+
params: { attributes: { name: 'stats_toggle', value: 'show_more' } },
45+
},
46+
},
47+
{
48+
payload: {
49+
context: 'specialPages',
50+
featureName: 'newTabPage',
51+
method: 'telemetryEvent',
52+
params: { attributes: { name: 'stats_toggle', value: 'show_less' } },
53+
},
54+
},
55+
]);
56+
});
3057
test(
3158
'hiding the expander when empty',
3259
{

special-pages/pages/new-tab/app/privacy-stats/privacy-stats.service.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ export class PrivacyStatsService {
2424
});
2525
}
2626

27+
name() {
28+
return 'PrivacyStatsService';
29+
}
30+
2731
/**
2832
* @returns {Promise<{data: PrivacyStatsData; config: StatsConfig}>}
2933
* @internal

special-pages/pages/new-tab/app/remote-messaging-framework/rmf.service.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ export class RMFService {
1717
});
1818
}
1919

20+
name() {
21+
return 'RMFService';
22+
}
23+
2024
/**
2125
* @returns {Promise<RMFData>}
2226
* @internal

special-pages/pages/new-tab/app/service.hooks.js

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
*/
2424

2525
import { useCallback, useEffect } from 'preact/hooks';
26+
import { useMessaging } from './types.js';
2627

2728
/**
2829
* @template D
@@ -86,14 +87,16 @@ export function reducer(state, event) {
8687
* @param {import("preact").RefObject<{
8788
* getInitial: () => Promise<{data: D, config: C}>;
8889
* destroy: () => void;
90+
* name: () => string;
8991
* }>} params.service
9092
*/
9193
export function useInitialDataAndConfig({ dispatch, service }) {
94+
const messaging = useMessaging();
9295
useEffect(() => {
9396
if (!service.current) return console.warn('missing service');
94-
const stats = service.current;
97+
const currentService = service.current;
9598
async function init() {
96-
const { config, data } = await stats.getInitial();
99+
const { config, data } = await currentService.getInitial();
97100
if (data) {
98101
dispatch({ kind: 'initial-data', data, config });
99102
} else {
@@ -107,12 +110,13 @@ export function useInitialDataAndConfig({ dispatch, service }) {
107110
init().catch((e) => {
108111
console.error('uncaught error', e);
109112
dispatch({ kind: 'error', error: e });
113+
messaging.reportPageException({ message: `${currentService.name()}: failed to fetch initial data+config: ` + e.message });
110114
});
111115

112116
return () => {
113-
stats.destroy();
117+
currentService.destroy();
114118
};
115-
}, []);
119+
}, [messaging]);
116120
}
117121

118122
/**
@@ -122,14 +126,16 @@ export function useInitialDataAndConfig({ dispatch, service }) {
122126
* @param {import("preact").RefObject<{
123127
* getInitial: () => Promise<D>;
124128
* destroy: () => void;
129+
* name: () => string;
125130
* }>} params.service
126131
*/
127132
export function useInitialData({ dispatch, service }) {
133+
const messaging = useMessaging();
128134
useEffect(() => {
129135
if (!service.current) return console.warn('missing service');
130-
const stats = service.current;
136+
const currentService = service.current;
131137
async function init() {
132-
const data = await stats.getInitial();
138+
const data = await currentService.getInitial();
133139
if (data) {
134140
dispatch({ kind: 'initial-data', data });
135141
} else {
@@ -143,10 +149,11 @@ export function useInitialData({ dispatch, service }) {
143149
init().catch((e) => {
144150
console.error('uncaught error', e);
145151
dispatch({ kind: 'error', error: e });
152+
messaging.reportPageException({ message: `${currentService.name()}: failed to fetch initial data: ` + e.message });
146153
});
147154

148155
return () => {
149-
stats.destroy();
156+
currentService.destroy();
150157
};
151158
}, []);
152159
}

0 commit comments

Comments
 (0)