Skip to content

Commit 06670d2

Browse files
noisysocksmgurgel
authored andcommitted
Add ad blocking variants to v3 onboarding
- Add optional 'Enhanced Ad Blocking' row to the settings step. - Add optional 'YouTube Ad Blocking' row to the settings step. - Update Duck Player step copy when YouTube Ad Blocking is enabled.
1 parent d2877f7 commit 06670d2

File tree

11 files changed

+308
-6
lines changed

11 files changed

+308
-6
lines changed

special-pages/pages/onboarding/app/components/ListItem.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ export const availableIcons = /** @type {const} */ ([
2121
'v3/home.svg',
2222
'v3/import.svg',
2323
'v3/session-restore.svg',
24+
'v3/ads.svg',
25+
'v3/video-player.svg',
2426
]);
2527

2628
const prefix = 'assets/img/steps/';

special-pages/pages/onboarding/app/components/v3/data.js

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,10 @@ export const stepsConfig = {
8383
content: <SettingsStep data={settingsRowItems} />,
8484
};
8585
},
86-
duckPlayerSingle: ({ t, advance, beforeAfter }) => {
86+
duckPlayerSingle: ({ t, globalState, advance, beforeAfter }) => {
87+
const isYouTubeAdBlockingEnabled = globalState.values['youtube-ad-blocking']?.enabled ?? false;
88+
const title = isYouTubeAdBlockingEnabled ? t('duckPlayer_alt_title') : t('duckPlayer_title');
89+
const subtitle = isYouTubeAdBlockingEnabled ? t('duckPlayer_alt_subtitle') : t('duckPlayer_subtitle');
8790
const beforeAfterState = beforeAfter.get();
8891
const longestText = [t('beforeAfter_duckPlayer_show'), t('beforeAfter_duckPlayer_hide')].reduce((acc, cur) => {
8992
return cur.length > acc.length ? cur : acc;
@@ -92,8 +95,8 @@ export const stepsConfig = {
9295
return {
9396
variant: 'box',
9497
heading: {
95-
title: t('duckPlayer_title'),
96-
subtitle: t('duckPlayer_subtitle'),
98+
title: title,
99+
subtitle: subtitle,
97100
speechBubble: true,
98101
},
99102
dismissButton: {
@@ -204,6 +207,24 @@ export const settingsRowItems = {
204207
acceptText: t('row_home-shortcut_accept'),
205208
accepButtonVariant: 'secondary',
206209
}),
210+
'ad-blocking': (t) => ({
211+
id: 'ad-blocking',
212+
icon: 'v3/ads.svg',
213+
title: t('row_ad-blocking_title_v3'),
214+
secondaryText: t('row_ad-blocking_desc_v3'),
215+
kind: 'one-time',
216+
acceptText: t('row_ad-blocking_accept_v3'),
217+
accepButtonVariant: 'primary',
218+
}),
219+
'youtube-ad-blocking': (t) => ({
220+
id: 'youtube-ad-blocking',
221+
icon: 'v3/video-player.svg',
222+
title: t('row_youtube-ad-blocking_title_v3'),
223+
secondaryText: t('row_youtube-ad-blocking_desc_v3'),
224+
kind: 'one-time',
225+
acceptText: t('row_youtube-ad-blocking_accept_v3'),
226+
accepButtonVariant: 'primary',
227+
}),
207228
};
208229

209230
export const stepDefinitions = {
@@ -218,3 +239,19 @@ export const stepDefinitions = {
218239
rows: ['bookmarks', 'session-restore', 'home-shortcut'],
219240
},
220241
};
242+
243+
export const stepDefinitionsAdBlocking = {
244+
systemSettings: {
245+
id: 'systemSettings',
246+
kind: 'settings',
247+
rows: ['dock', 'ad-blocking', 'import'],
248+
},
249+
};
250+
251+
export const stepDefinitionsYouTubeAdBlocking = {
252+
systemSettings: {
253+
id: 'systemSettings',
254+
kind: 'settings',
255+
rows: ['dock', 'youtube-ad-blocking', 'import'],
256+
},
257+
};

special-pages/pages/onboarding/app/data.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,28 @@ export const settingsRowItems = {
217217
kind: 'toggle',
218218
acceptText: t('row_home-shortcut_accept'),
219219
}),
220+
// Intended only for use with v3
221+
'ad-blocking': (t) => ({
222+
id: 'ad-blocking',
223+
icon: 'v3/ads.svg',
224+
title: t('row_ad-blocking_title_v3'),
225+
secondaryText: t('row_ad-blocking_desc_v3'),
226+
summary: t('row_ad-blocking_title_v3'),
227+
kind: 'one-time',
228+
acceptText: t('row_ad-blocking_accept_v3'),
229+
accepButtonVariant: 'primary',
230+
}),
231+
// Intended only for use with v3
232+
'youtube-ad-blocking': (t) => ({
233+
id: 'youtube-ad-blocking',
234+
icon: 'v3/video-player.svg',
235+
title: t('row_youtube-ad-blocking_title_v3'),
236+
secondaryText: t('row_youtube-ad-blocking_desc_v3'),
237+
summary: t('row_youtube-ad-blocking_title_v3'),
238+
kind: 'one-time',
239+
acceptText: t('row_youtube-ad-blocking_accept_v3'),
240+
accepButtonVariant: 'primary',
241+
}),
220242
};
221243

222244
/**

special-pages/pages/onboarding/app/global.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,8 @@ export function GlobalProvider({ order, children, stepDefinitions, messaging, fi
143143
bookmarks: 'idle',
144144
'session-restore': 'idle',
145145
'home-shortcut': 'idle',
146+
'ad-blocking': 'idle',
147+
'youtube-ad-blocking': 'idle',
146148
},
147149
});
148150

@@ -278,6 +280,17 @@ async function handleSystemSettingUpdate(action, messaging, platform) {
278280
}
279281
break;
280282
}
283+
case 'ad-blocking':
284+
case 'youtube-ad-blocking': {
285+
if (!current) {
286+
messaging.setAdBlocking(payload);
287+
} else {
288+
if (payload.enabled) {
289+
messaging.setAdBlocking(payload);
290+
}
291+
}
292+
return payload;
293+
}
281294
}
282295
if ('value' in payload) {
283296
return { enabled: payload.enabled, value: payload.value };

special-pages/pages/onboarding/app/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import { callWithRetry } from '../../../shared/call-with-retry';
1313
import { TranslationProvider } from '../../../shared/components/TranslationsProvider';
1414
import { SettingsProvider } from './components/SettingsProvider';
1515
import enStrings from '../public/locales/en/onboarding.json';
16-
import { stepDefinitions as stepDefinitionsV3 } from './components/v3/data';
1716
import { mockTransport } from '../src/mock-transport.js';
1817

1918
const baseEnvironment = new Environment().withInjectName(document.documentElement.dataset.platform).withEnv(import.meta.env);
@@ -65,8 +64,9 @@ async function init() {
6564
.withPlatformName(baseEnvironment.injectName)
6665
.withPlatformName(init.platform?.name)
6766
.withPlatformName(baseEnvironment.urlParams.get('platform'))
68-
.withStepDefinitions(init.order === 'v3' ? stepDefinitionsV3 : null)
67+
.withNamedStepDefinitions(init.order)
6968
.withStepDefinitions(init.stepDefinitions)
69+
.withNamedStepDefinitions(environment.urlParams.get('steps'))
7070
.withNamedOrder(init.order)
7171
.withNamedOrder(environment.urlParams.get('order'))
7272
.withExcludedScreens(init.exclude)

special-pages/pages/onboarding/app/messages.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,4 +158,13 @@ export class OnboardingMessages {
158158
reportInitException(params) {
159159
this.messaging.notify('reportInitException', params);
160160
}
161+
162+
/**
163+
* Sent when the user wants to enable or disable ad blocking.
164+
*
165+
* @param {import('./types').BooleanSystemValue} params
166+
*/
167+
setAdBlocking(params) {
168+
this.messaging.notify('setAdBlocking', params);
169+
}
161170
}

special-pages/pages/onboarding/app/settings.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { ALT_ORDER, DEFAULT_ORDER, EVERY_PAGE_ID, ORDER_V3 } from './types';
22
import { stepDefinitions as defaultStepDefinitions } from './data';
3+
import { stepDefinitions as stepDefinitionsV3, stepDefinitionsAdBlocking, stepDefinitionsYouTubeAdBlocking } from './components/v3/data';
34

45
/**
56
* Settings that affect the Application, such as running order
@@ -148,4 +149,17 @@ export class Settings {
148149
stepDefinitions: nextSteps,
149150
});
150151
}
152+
153+
/**
154+
* @param {string|null|undefined} named
155+
* @returns {Settings}
156+
*/
157+
withNamedStepDefinitions(named) {
158+
if (!named) return this;
159+
if (named === 'v3') return this.withStepDefinitions(stepDefinitionsV3);
160+
if (named === 'adBlocking') return this.withStepDefinitions(stepDefinitionsAdBlocking);
161+
if (named === 'youTubeAdBlocking') return this.withStepDefinitions(stepDefinitionsYouTubeAdBlocking);
162+
console.warn('ignoring named step definitions:', named);
163+
return this;
164+
}
151165
}

special-pages/pages/onboarding/app/types.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import { useContext } from 'preact/hooks';
1010
* | 'bookmarks'
1111
* | 'session-restore'
1212
* | 'home-shortcut'
13+
* | 'ad-blocking'
14+
* | 'youtube-ad-blocking'
1315
* } SystemValueId - Each setting that can be updated should have a unique ID listed here.
1416
*/
1517

special-pages/pages/onboarding/integration-tests/onboarding.js

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export class OnboardingPage {
2929
requestDockOptIn: {},
3030
requestImport: { enabled: true },
3131
stepCompleted: {},
32+
setAdBlocking: {},
3233
reportPageException: {},
3334
init: {
3435
stepDefinitions: {
@@ -382,6 +383,34 @@ export class OnboardingPage {
382383
]);
383384
}
384385

386+
async enableEnhancedAdBlocking() {
387+
const { page } = this;
388+
await page.getByRole('button', { name: 'Turn on Enhanced Ad Blocking' }).click();
389+
await expect(page.getByRole('img', { name: 'Completed Action' })).toBeVisible();
390+
await this.didSetAdBlocking();
391+
}
392+
393+
async enableYouTubeAdBlocking() {
394+
const { page } = this;
395+
await page.getByRole('button', { name: 'Block Ads' }).click();
396+
await expect(page.getByRole('img', { name: 'Completed Action' })).toBeVisible();
397+
await this.didSetAdBlocking();
398+
}
399+
400+
async didSetAdBlocking() {
401+
const calls = await this.mocks.outgoing({ names: ['setAdBlocking'] });
402+
expect(calls).toMatchObject([
403+
{
404+
payload: {
405+
context: 'specialPages',
406+
featureName: 'onboarding',
407+
method: 'setAdBlocking',
408+
params: { enabled: true },
409+
},
410+
},
411+
]);
412+
}
413+
385414
async startBrowsing() {
386415
const { page } = this;
387416
await page.getByRole('button', { name: 'Start Browsing' }).click();
@@ -514,6 +543,7 @@ export class OnboardingPage {
514543

515544
async completesOrderV3() {
516545
const { page } = this;
546+
517547
/* Welcome */
518548
await page.getByText('Welcome to DuckDuckGo').nth(1).waitFor({ timeout: 1000 });
519549

@@ -580,4 +610,86 @@ export class OnboardingPage {
580610
await page.getByRole('button', { name: 'Show Home Button' }).click();
581611
await this.startBrowsing();
582612
}
613+
614+
async completesOrderV3WithAdBlocking() {
615+
const { page } = this;
616+
617+
/* Welcome */
618+
await page.getByText('Welcome to DuckDuckGo').nth(1).waitFor({ timeout: 1000 });
619+
620+
/* Get started */
621+
await page.getByText('Hi there').nth(1).waitFor({ timeout: 1500 });
622+
await page.getByRole('button', { name: 'Let’s Do It' }).click();
623+
624+
/* Make default */
625+
await page.getByText('Protections activated').nth(1).waitFor({ timeout: 1000 });
626+
await page.getByRole('button', { name: 'Make DuckDuckGo Your Default' }).click();
627+
await page.getByText('Excellent!').nth(1).waitFor({ timeout: 1000 });
628+
await page.getByRole('button', { name: 'Next' }).click();
629+
630+
/* System settings (with ad-blocking) */
631+
await page.getByText('Let’s get you set up!').nth(1).waitFor({ timeout: 1000 });
632+
const dockButton = this.build.switch({
633+
windows: () => page.getByRole('button', { name: 'Pin to Taskbar' }),
634+
apple: () => page.getByRole('button', { name: 'Keep in Dock' }),
635+
});
636+
await dockButton.click();
637+
await page.getByRole('button', { name: 'Turn on Enhanced Ad Blocking', exact: true }).click();
638+
await page.getByRole('button', { name: 'Import Now', exact: true }).click();
639+
await page.getByRole('button', { name: 'Next' }).click();
640+
641+
/* Duckplayer */
642+
await page.getByText('Drowning in ads').nth(1).waitFor({ timeout: 1000 });
643+
await page.getByLabel('See Without Duck Player').click();
644+
await page.getByLabel('See With Duck Player').click();
645+
await page.getByRole('button', { name: 'Next' }).click();
646+
647+
/* Customize */
648+
await page.getByText('Let’s customize a few things').nth(1).waitFor({ timeout: 1000 });
649+
await page.getByRole('button', { name: 'Show Bookmarks Bar' }).click();
650+
await page.getByRole('button', { name: 'Enable Session Restore' }).click();
651+
await page.getByRole('button', { name: 'Show Home Button' }).click();
652+
await this.startBrowsing();
653+
}
654+
655+
async completesOrderV3WithYouTubeAdBlocking() {
656+
const { page } = this;
657+
/* Welcome */
658+
await page.getByText('Welcome to DuckDuckGo').nth(1).waitFor({ timeout: 1000 });
659+
660+
/* Get started */
661+
await page.getByText('Hi there').nth(1).waitFor({ timeout: 1500 });
662+
await page.getByRole('button', { name: 'Let’s Do It' }).click();
663+
664+
/* Make default */
665+
await page.getByText('Protections activated').nth(1).waitFor({ timeout: 1000 });
666+
await page.getByRole('button', { name: 'Make DuckDuckGo Your Default' }).click();
667+
await page.getByText('Excellent!').nth(1).waitFor({ timeout: 1000 });
668+
await page.getByRole('button', { name: 'Next' }).click();
669+
670+
/* System settings (with youtube ad-blocking) */
671+
await page.getByText('Let’s get you set up!').nth(1).waitFor({ timeout: 1000 });
672+
const dockButton = this.build.switch({
673+
windows: () => page.getByRole('button', { name: 'Pin to Taskbar' }),
674+
apple: () => page.getByRole('button', { name: 'Keep in Dock' }),
675+
});
676+
await dockButton.click();
677+
await page.getByRole('button', { name: 'Block Ads', exact: true }).click();
678+
await page.getByRole('button', { name: 'Import Now', exact: true }).click();
679+
await page.getByRole('button', { name: 'Next' }).click();
680+
681+
/* Duckplayer - alternate title/subtitle */
682+
await page.getByText('Watch YouTube privately with Duck Player').nth(1).waitFor({ timeout: 1000 });
683+
await expect(page.locator('h2')).toContainText("Watching videos in Duck Player won't influence your YouTube recommendations.");
684+
await page.getByLabel('See Without Duck Player').click();
685+
await page.getByLabel('See With Duck Player').click();
686+
await page.getByRole('button', { name: 'Next' }).click();
687+
688+
/* Customize */
689+
await page.getByText('Let’s customize a few things').nth(1).waitFor({ timeout: 1000 });
690+
await page.getByRole('button', { name: 'Show Bookmarks Bar' }).click();
691+
await page.getByRole('button', { name: 'Enable Session Restore' }).click();
692+
await page.getByRole('button', { name: 'Show Home Button' }).click();
693+
await this.startBrowsing();
694+
}
583695
}

0 commit comments

Comments
 (0)