Skip to content

Commit ef28439

Browse files
authored
feat(PostInstall): add consent screen about data sharing (#1191)
1 parent b33416c commit ef28439

20 files changed

Lines changed: 440 additions & 9 deletions

File tree

src/_locales/en/messages.json

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,5 +308,80 @@
308308
},
309309
"postInstall_text_wallet_connected_2": {
310310
"message": "Access the extension from the browser toolbar."
311+
},
312+
"postInstallConsent_text_title": {
313+
"message": "Welcome to the Web Monetization Extension"
314+
},
315+
"postInstallConsent_text_header1": {
316+
"message": "We’re transparent about what data is shared and how it’s used."
317+
},
318+
"postInstallConsent_text_header2": {
319+
"message": "By continuing, you consent to share the information below."
320+
},
321+
"postInstallConsent_text_dataShared_title": {
322+
"message": "What’s shared"
323+
},
324+
"postInstallConsent_text_dataShared_yourWallet_title": {
325+
"message": "With your wallet provider:"
326+
},
327+
"postInstallConsent_text_dataShared_yourWallet_keyName": {
328+
"message": " If you choose automatic key addition, the extension shares:"
329+
},
330+
"postInstallConsent_text_dataShared_yourWallet_keyConsent": {
331+
"message": "Note: You will always be asked for consent before any connection."
332+
},
333+
"postInstallConsent_text_dataShared_yourWallet_headers": {
334+
"message": "Your IP address, language, and browser version while you use the extension."
335+
},
336+
"postInstallConsent_text_dataShared_websiteWallets_title": {
337+
"message": "With the wallets used on websites you visit that use Web Monetization:"
338+
},
339+
"postInstallConsent_text_dataShared_websiteWallets_headers": {
340+
"message": "Your IP address, language and browser version information."
341+
},
342+
"postInstallConsent_text_dataShared_websiteWallets_wa": {
343+
"message": "Your wallet address."
344+
},
345+
"postInstallConsent_text_dataShared_headers": {
346+
"message": "Browsers send certain HTTP headers by default each request that inform the servers about your language (`Accept-Language` header), browser version (`User-Agent` header) and more. Your IP address is also sent by default."
347+
},
348+
"postInstallConsent_text_dataNotShared_title": {
349+
"message": "What’s not shared"
350+
},
351+
"postInstallConsent_text_dataNotShared_walletDetails": {
352+
"message": "Your wallet address, balance, or currency, are never shared with websites you visit."
353+
},
354+
"postInstallConsent_text_dataNotShared_browsingHistory": {
355+
"message": "Your browsing history. It stays private in your browser."
356+
},
357+
"postInstallConsent_text_permissions_title": {
358+
"message": "Extension Permissions"
359+
},
360+
"postInstallConsent_text_permissions_text": {
361+
"message": "The extension needs basic permissions to work."
362+
},
363+
"postInstallConsent_text_permissions_linkText": {
364+
"message": "See details."
365+
},
366+
"postInstallConsent_state_consentProvided": {
367+
"message": "You have provided your consent to the above. Access the extension from the browser toolbar."
368+
},
369+
"postInstallConsent_text_confirmation": {
370+
"message": "I confirm that I understand and consent to this data usage."
371+
},
372+
"postInstallConsent_action_confirm": {
373+
"message": "Confirm and continue"
374+
},
375+
"consentRequired_text_title": {
376+
"message": "Consent Required: Data Collection & Transmission"
377+
},
378+
"consentRequired_text_subtitle": {
379+
"message": "We want to be transparent about what data is shared and how it’s used."
380+
},
381+
"consentRequired_text_msg": {
382+
"message": "To use the the Web Monetization Extension, we require your consent to our data collection & transmission policy."
383+
},
384+
"consentRequired_action_primary": {
385+
"message": "View Policy"
311386
}
312387
}

src/background/services/background.ts

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import {
77
getWalletInformation,
88
isErrorWithKey,
99
moveToFront,
10+
CURRENT_DATA_CONSENT_VERSION,
11+
isConsentRequired,
1012
} from '@/shared/helpers';
1113
import { KeyAutoAddService } from '@/background/services/keyAutoAdd';
1214
import { OpenPaymentsClientError } from '@interledger/open-payments/dist/client/error';
@@ -124,7 +126,12 @@ export class Background {
124126
}
125127
await this.storage.populate();
126128
await this.checkPermissions();
129+
const { consent } = await this.storage.get(['consent']);
130+
if (isConsentRequired(consent)) {
131+
await this.storage.setState({ consent_required: true });
132+
}
127133
await this.scheduleResetOutOfFundsState();
134+
await this.updateVisualIndicatorsForCurrentTab().catch(() => {});
128135
}
129136

130137
async scheduleResetOutOfFundsState() {
@@ -147,12 +154,14 @@ export class Background {
147154
}
148155

149156
async getAppData(): Promise<AppStore> {
150-
const { connected, publicKey } = await this.storage.get([
157+
const { connected, publicKey, consent } = await this.storage.get([
151158
'connected',
152159
'publicKey',
160+
'consent',
153161
]);
154162

155163
return {
164+
consent,
156165
connected,
157166
publicKey,
158167
transientState: this.storage.getPopupTransientState(),
@@ -295,6 +304,10 @@ export class Background {
295304
await this.monetizationService.pay(message.payload),
296305
);
297306

307+
case 'OPEN_APP':
308+
await this.openAppPage(message.payload.path);
309+
return success(undefined);
310+
298311
// endregion
299312

300313
// region Content
@@ -337,6 +350,13 @@ export class Background {
337350
// region App
338351
case 'GET_DATA_APP':
339352
return success(await this.getAppData());
353+
354+
case 'PROVIDE_CONSENT': {
355+
await this.storage.set({ consent: CURRENT_DATA_CONSENT_VERSION });
356+
await this.storage.setState({ consent_required: false });
357+
return success(CURRENT_DATA_CONSENT_VERSION);
358+
}
359+
340360
// endregion
341361

342362
default:
@@ -406,6 +426,9 @@ export class Background {
406426
this.events.on('storage.state_update', async ({ state, prevState }) => {
407427
this.sendToPopup.send('SET_STATE', { state, prevState });
408428
await this.updateVisualIndicatorsForCurrentTab();
429+
if (state.consent_required) {
430+
await this.openAppPage('/post-install/consent');
431+
}
409432
});
410433

411434
this.events.on('monetization.state_update', async (tabId) => {
@@ -446,6 +469,9 @@ export class Background {
446469
);
447470
}
448471
}
472+
if (isConsentRequired(data.consent)) {
473+
await this.storage.setState({ consent_required: true });
474+
}
449475
});
450476
}
451477

@@ -460,4 +486,20 @@ export class Background {
460486
this.logger.error(error);
461487
}
462488
};
489+
490+
async openAppPage(path: string) {
491+
const appUrl = this.browser.runtime.getURL(APP_URL);
492+
493+
const allTabs = await this.browser.tabs.query({});
494+
const appTab = allTabs.find((t) => t.url?.startsWith(appUrl));
495+
496+
const url = `${appUrl}#${path}`;
497+
if (appTab?.id) {
498+
await this.browser.tabs.update(appTab.id, { url });
499+
await this.sendToPopup.send('CLOSE_POPUP', undefined);
500+
return appTab;
501+
} else {
502+
return await this.browser.tabs.create({ url });
503+
}
504+
}
463505
}

src/background/services/monetization.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,7 @@ export class MonetizationService {
410410

411411
async getPopupData(tab: Pick<Tabs.Tab, 'id' | 'url'>): Promise<PopupStore> {
412412
const storedData = await this.storage.get([
413+
'consent',
413414
'enabled',
414415
'continuousPaymentsEnabled',
415416
'connected',

src/background/services/storage.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,13 @@ import type {
77
StorageKey,
88
WalletAmount,
99
} from '@/shared/types';
10-
import { objectEquals, ThrottleBatch } from '@/shared/helpers';
11-
import { bigIntMax, computeBalance } from '../utils';
12-
import type { Cradle } from '../container';
10+
import {
11+
isConsentRequired,
12+
objectEquals,
13+
ThrottleBatch,
14+
} from '@/shared/helpers';
15+
import { bigIntMax, computeBalance } from '@/background/utils';
16+
import type { Cradle } from '@/background/container';
1317

1418
const defaultStorage = {
1519
/**
@@ -20,6 +24,7 @@ const defaultStorage = {
2024
* existing installations.
2125
*/
2226
version: 5,
27+
consent: 0,
2328
state: {},
2429
connected: false,
2530
enabled: true,
@@ -76,8 +81,12 @@ export class StorageService {
7681
}
7782

7883
async clear(): Promise<void> {
79-
await this.set(defaultStorage);
80-
this.currentState = { ...defaultStorage.state };
84+
const preservedValues = await this.get(['consent']);
85+
await this.set({ ...defaultStorage, ...preservedValues });
86+
this.currentState = {
87+
...defaultStorage.state,
88+
consent_required: isConsentRequired(preservedValues.consent),
89+
};
8190
}
8291

8392
/**

src/pages/app/App.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@ import * as PAGES from './pages/index';
1111

1212
export const ROUTES = {
1313
DEFAULT: '/',
14+
CONSENT: '/consent',
1415
} as const;
1516

1617
const P = ROUTES;
1718
const C = PAGES;
1819
const Routes = () => (
1920
<Switch>
2021
<Route path={P.DEFAULT} component={C.PostInstall} />
22+
<Route path={P.CONSENT} component={C.Consent} />
2123
</Switch>
2224
);
2325

src/pages/app/lib/store.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,15 @@ export const dispatch = async ({ type, data }: Actions) => {
2525
case 'SET_TRANSIENT_STATE':
2626
store.transientState = data;
2727
break;
28+
case 'SET_CONSENT':
29+
store.consent = data;
30+
break;
2831
default:
2932
throw new Error('Unknown action');
3033
}
3134
};
3235

3336
type Actions =
3437
| { type: 'SET_TRANSIENT_STATE'; data: PopupTransientState }
35-
| { type: 'SET_DATA_APP'; data: Pick<Storage, 'connected' | 'publicKey'> };
38+
| { type: 'SET_CONSENT'; data: NonNullable<AppStore['consent']> }
39+
| { type: 'SET_DATA_APP'; data: Pick<AppStore, 'connected' | 'publicKey'> };

src/pages/app/pages/PostInstall.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,22 @@
11
import React from 'react';
2+
import { Redirect } from 'wouter';
23
import {
34
ArrowBack,
45
CaretDownIcon,
56
ExternalIcon,
67
} from '@/pages/shared/components/Icons';
7-
import { getBrowserName, type BrowserName } from '@/shared/helpers';
8+
import {
9+
getBrowserName,
10+
isConsentRequired,
11+
type BrowserName,
12+
} from '@/shared/helpers';
813
import { getResponseOrThrow } from '@/shared/messages';
914
import { useBrowser, useTranslation } from '@/app/lib/context';
1015
import { ConnectWalletForm } from '@/popup/components/ConnectWalletForm';
1116
import { cn } from '@/pages/shared/lib/utils';
1217
import { useMessage } from '@/app/lib/context';
1318
import { useAppState } from '@/app/lib/store';
19+
import { ROUTES } from '../App';
1420

1521
export default () => {
1622
return (
@@ -52,6 +58,12 @@ const Header = () => {
5258

5359
const Main = () => {
5460
const t = useTranslation();
61+
const { consent } = useAppState();
62+
63+
if (isConsentRequired(consent)) {
64+
return <Redirect to={ROUTES.CONSENT} />;
65+
}
66+
5567
return (
5668
<div className="mx-auto flex w-full max-w-2xl flex-col gap-6 rounded-lg border border-gray-200 bg-gray-50/75 p-3 shadow-md backdrop-blur-0 sm:p-8">
5769
<h2 className="rounded-sm bg-gray-100 p-2 text-center text-base font-medium sm:rounded-2xl sm:p-4 sm:text-lg">

0 commit comments

Comments
 (0)