Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 48 additions & 1 deletion configs/nimbus.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,53 @@ features:
value: { "enabled": false, "emoji": ";)" }
- channel: production
value: { "enabled": false, "emoji": "❤️" }
promo-qr-mobile:
description: >
Value proposition A/B test for the QR mobile download promo shown in the
authentication flow. The branch slug selects both the heading string and
the matching QR code, because each QR points at a distinct Bitly link.
variables:
enabled:
description: If the experiment is enabled. When false the control ships.
type: Boolean
default: false
branch:
description: >
The branch slug. An unknown slug falls back to the control, but the
BranchSlug enum means Experimenter rejects one before that can happen.
type: Option<BranchSlug>
default: null
defaults:
- channel: developer
value: { "enabled": false, "branch": "control" }
- channel: staging
value: { "enabled": false, "branch": "control" }
- channel: production
value: { "enabled": false, "branch": "control" }
types:
objects: {}
enums: {}
enums:
BranchSlug:
description: >
The arms of the QR promo value proposition test. Each slug selects a
heading and its matching QR code in PromoQrMobile/branches.ts, so a slug
added here must be added there too.
variants:
control:
description: Control.
treatment-a:
description: Continue Across Devices.
treatment-b:
description: Continue Across Devices.
treatment-c:
description: One Browser You Trust.
treatment-d:
description: One Browser You Trust.
treatment-e:
description: Protect Your Privacy Everywhere.
treatment-f:
description: Protect Your Privacy Everywhere.
treatment-g:
description: Calmer Focus.
treatment-h:
description: Calmer Focus.
23 changes: 14 additions & 9 deletions packages/fxa-settings/src/components/App/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,6 @@ const PairAuthorityTimeoutAndCancel = lazy(
() => import('../../pages/Pair2/Authority/TimeoutAndCancel/container')
);


const PairSupplicantApproveSignIn = lazy(
() => import('../../pages/Pair2/Supplicant/ApproveSignIn/container')
);
Expand All @@ -215,8 +214,6 @@ const PairSupplicantTimeoutAndCancel = lazy(
() => import('../../pages/Pair2/Supplicant/TimeoutAndCancel')
);



const AuthorizationContainer = lazy(
() => import('../../pages/Authorization/container')
);
Expand Down Expand Up @@ -652,7 +649,10 @@ const AuthAndAccountSetupRoutes = ({
<Route path="/web_channel_example/*" element={<WebChannelExample />} />
<Route path="/poc_deep_link/*" element={<PocDeepLink />} />
<Route path="/poc_pair_init/*" element={<PocPairInit />} />
<Route path="/poc_pair_start/*" element={<PocPairStart {...{integration}} />} />
<Route
path="/poc_pair_start/*"
element={<PocPairStart {...{ integration }} />}
/>
<Route path="/cookies_disabled" element={<CookiesDisabled />} />

{/* Post verify */}
Expand Down Expand Up @@ -1074,15 +1074,19 @@ const AuthAndAccountSetupRoutes = ({
<Route path="/pair/unsupported/*" element={<PairUnsupported />} />
<Route
path="/pair/*"
element={<PairIndex {...{integration, fxaStatusResult: useFxAStatusResult}} />}
element={
<PairIndex
{...{ integration, fxaStatusResult: useFxAStatusResult }}
/>
}
/>
<Route
path="/pair/authority/approve_signin/*"
element={<PairAuthorityApproveSignIn {...{integration}} />}
element={<PairAuthorityApproveSignIn {...{ integration }} />}
/>
<Route
path="/pair/authority/continue_on_mobile/*"
element={<PairAuthorityContinueOnMobile {...{integration}} />}
element={<PairAuthorityContinueOnMobile {...{ integration }} />}
/>
<Route
path="/pair/authority/download_firefox/*"
Expand All @@ -1102,11 +1106,11 @@ const AuthAndAccountSetupRoutes = ({
/>
<Route
path="/pair/supplicant/approve_signin/*"
element={<PairSupplicantApproveSignIn {...{integration}} />}
element={<PairSupplicantApproveSignIn {...{ integration }} />}
/>
<Route
path="/pair/supplicant/connect_this_device/*"
element={<PairSupplicantConnectThisDevice {...{integration}} />}
element={<PairSupplicantConnectThisDevice {...{ integration }} />}
/>
<Route
path="/pair/supplicant/download_firefox/*"
Expand All @@ -1130,6 +1134,7 @@ const AuthAndAccountSetupRoutes = ({
<PromoQrMobile
integration={integration}
promoQrImageUrl={defaultCmsState.data?.defaultCms?.promoQrImageUrl}
cmsLoading={defaultCmsState.loading}
/>
</>
);
Expand Down
84 changes: 84 additions & 0 deletions packages/fxa-settings/src/components/PromoQrMobile/branches.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import controlQr from './qr/control.svg';
import treatmentAQr from './qr/treatment-a.svg';
import treatmentBQr from './qr/treatment-b.svg';
import treatmentCQr from './qr/treatment-c.svg';
import treatmentDQr from './qr/treatment-d.svg';
import treatmentEQr from './qr/treatment-e.svg';
import treatmentFQr from './qr/treatment-f.svg';
import treatmentGQr from './qr/treatment-g.svg';
import treatmentHQr from './qr/treatment-h.svg';

export const CONTROL_BRANCH = 'control';

export type PromoQrBranch = {
/** Literal FTL id. Declared in en.ftl so the l10n extraction finds it. */
ftlId: string;
/** English fallback, kept in sync with en.ftl. */
heading: string;
/** Each branch scans to its own Bitly link, so the QR identifies the branch. */
qr: string;
};

export const BRANCHES: Record<string, PromoQrBranch> = {
[CONTROL_BRANCH]: {
ftlId: 'promo-qr-mobile-heading',
heading: 'Your phone. Your rules.',
qr: controlQr,
},
'treatment-a': {
ftlId: 'promo-qr-mobile-heading-treatment-a',
heading: 'Pick up where you left off, wherever you go',
qr: treatmentAQr,
},
'treatment-b': {
ftlId: 'promo-qr-mobile-heading-treatment-b',
heading: 'Your tabs and more, ready on your phone',
qr: treatmentBQr,
},
'treatment-c': {
ftlId: 'promo-qr-mobile-heading-treatment-c',
heading: 'The browser you trust, on your phone',
qr: treatmentCQr,
},
'treatment-d': {
ftlId: 'promo-qr-mobile-heading-treatment-d',
heading: 'Same Firefox. Different screen.',
qr: treatmentDQr,
},
'treatment-e': {
ftlId: 'promo-qr-mobile-heading-treatment-e',
heading: 'Your privacy shouldn’t stop here',
qr: treatmentEQr,
},
'treatment-f': {
ftlId: 'promo-qr-mobile-heading-treatment-f',
heading: 'Keep more of your browsing to yourself',
qr: treatmentFQr,
},
'treatment-g': {
ftlId: 'promo-qr-mobile-heading-treatment-g',
heading: 'Your phone could use a little less noise',
qr: treatmentGQr,
},
'treatment-h': {
ftlId: 'promo-qr-mobile-heading-treatment-h',
heading: 'Take a calmer way to browse with you',
qr: treatmentHQr,
},
};

export type ResolvedBranch = PromoQrBranch & { slug: string };

/**
* Resolve a branch slug to its copy and QR code. An unknown slug falls back to
* the control, so a typo in Experimenter cannot break the promo. The returned
* slug is what the user actually saw, which is what telemetry reports.
*/
export function resolveBranch(slug?: string | null): ResolvedBranch {
const key = slug && BRANCHES[slug] ? slug : CONTROL_BRANCH;
return { ...BRANCHES[key], slug: key };
}
20 changes: 18 additions & 2 deletions packages/fxa-settings/src/components/PromoQrMobile/en.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,24 @@

# "Your phone. Your rules." refers to the user being able to control what browser they use on their own phone.
promo-qr-mobile-heading = Your phone. Your rules.
# Appears next to a QR code that a user can scan to download the Firefox mobile app
promo-qr-mobile-description = Scan to get the app
# Value proposition variant. Refers to resuming browsing on another device.
promo-qr-mobile-heading-treatment-a = Pick up where you left off, wherever you go
# Value proposition variant. "tabs" are the open pages in a browser.
promo-qr-mobile-heading-treatment-b = Your tabs and more, ready on your phone
# Value proposition variant. Refers to using the same trusted browser on a phone.
promo-qr-mobile-heading-treatment-c = The browser you trust, on your phone
# Value proposition variant. "Different screen" refers to the phone rather than the desktop.
promo-qr-mobile-heading-treatment-d = Same { -brand-firefox }. Different screen.
# Value proposition variant. "stop here" means privacy protection should continue onto the phone.
promo-qr-mobile-heading-treatment-e = Your privacy shouldn’t stop here
# Value proposition variant. Refers to keeping browsing activity private.
promo-qr-mobile-heading-treatment-f = Keep more of your browsing to yourself
# Value proposition variant. "noise" refers to distractions and clutter.
promo-qr-mobile-heading-treatment-g = Your phone could use a little less noise
# Value proposition variant. Refers to a calmer browsing experience on the phone.
promo-qr-mobile-heading-treatment-h = Take a calmer way to browse with you
# Appears below a QR code that a user can scan to download the Firefox mobile app
promo-qr-mobile-description-v2 = Scan to download mobile app
# Note that for RTL languages, this should be translated as "the lower-left corner of your screen," instead of "the lower-right corner."
promo-qr-mobile-qr-alt =
.alt = QR code to download the { -brand-firefox } mobile app. Position your phone’s camera on the lower-right corner of your screen to scan it.
Loading