Bug 1938253 - Implement Callout 3 for integrated version of Review Checker - r=kpatenio,shopping-reviewers,fluent-reviewers,settings-reviewers,omc-reviewers,aminomancer,bolsson,mossop

Differential Revision: https://phabricator.services.mozilla.com/D233053
This commit is contained in:
Rebecca King
2025-02-24 18:15:36 +00:00
parent 8ad4c34540
commit b3a22ac978
12 changed files with 634 additions and 6 deletions

View File

@@ -863,6 +863,8 @@ const SHOPPING_MICROSURVEY = {
};
const OPTED_IN_TIME_PREF = "browser.shopping.experience2023.survey.optedInTime";
const ONBOARDING_FIRST_IMPRESSION_TIME_PREF =
"browser.shopping.experience2023.firstImpressionTime";
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
@@ -968,6 +970,19 @@ export class AboutWelcomeShoppingChild extends AboutWelcomeChild {
});
}
setOnBoardingImpressionTime() {
const now = Date.now() / 1000;
this.AWSendToParent("SPECIAL_ACTION", {
type: "SET_PREF",
data: {
pref: {
name: ONBOARDING_FIRST_IMPRESSION_TIME_PREF,
value: now,
},
},
});
}
handleEvent(event) {
// Decide when to show/hide onboarding and survey message
const { productUrl, showOnboarding, data, isSupportedSite, isProductPage } =
@@ -1042,6 +1057,8 @@ export class AboutWelcomeShoppingChild extends AboutWelcomeChild {
if (this.showMicroSurvey && !this.showOnboarding) {
messageContent = SHOPPING_MICROSURVEY;
this.setShoppingSurveySeen();
} else {
this.setOnBoardingImpressionTime();
}
return Cu.cloneInto(messageContent, this.contentWindow);
}

View File

@@ -407,6 +407,9 @@ div#feature-callout.hidden {
#feature-callout .screen[pos=callout] .section-main .main-content .main-content-inner {
gap: 12px;
}
#feature-callout .screen[pos=callout] .section-main .main-content .main-content-inner .legal-paragraph .text-link {
color: var(--fc-color);
}
#feature-callout .screen[pos=callout] .section-main .main-content .steps {
height: auto;
position: absolute;

View File

@@ -310,6 +310,12 @@
.main-content-inner {
gap: 12px;
.legal-paragraph {
.text-link {
color: var(--fc-color);
}
}
}
.steps {

View File

@@ -624,7 +624,7 @@ export class FeatureCallout {
// example, if we're anchoring directly to the trigger tab, our selector
// might look like `%triggerTab%[visuallyselected]`. In this case,
// querySelector() will return nothing, but matches() will return true.
if (!element && scope.matches(selector)) {
if (!element && scope.matches?.(selector)) {
element = scope;
}
if (!element) {

View File

@@ -1782,6 +1782,516 @@ const MESSAGES = () => {
skip_in_tests:
"not tested in automation and might pop up unexpectedly during review checker tests",
},
{
// "Callout 3" in the Review Checker Integrated Sidebar Migration Figma spec
// For non-opted in users
// Triggered if the Review Checker is panel is closed and user visits a product page
// Explains why you should use Review Checker and prompts to opt in
// Horizontal tabs
id: "REVIEW_CHECKER_CALLOUT_PDP_NOT_OPTED_IN_REMINDER",
template: "feature_callout",
content: {
id: "REVIEW_CHECKER_CALLOUT_PDP_NOT_OPTED_IN_REMINDER",
template: "multistage",
backdrop: "transparent",
transitions: false,
disableHistoryUpdates: true,
screens: [
{
id: "REVIEW_CHECKER_CALLOUT_PDP_NOT_OPTED_IN_REMINDER_HORIZONTAL",
anchors: [
{
selector:
"#sidebar-main:not([positionend]) > sidebar-main::%shadow% .tools-and-extensions::%shadow% moz-button[view='viewReviewCheckerSidebar']",
panel_position: {
anchor_attachment: "rightcenter",
callout_attachment: "topleft",
},
no_open_on_anchor: true,
},
{
selector:
"#sidebar-main[positionend] > sidebar-main::%shadow% .tools-and-extensions::%shadow% moz-button[view='viewReviewCheckerSidebar']",
panel_position: {
anchor_attachment: "leftcenter",
callout_attachment: "topright",
},
no_open_on_anchor: true,
},
],
content: {
position: "callout",
width: "401px",
title: {
string_id: "shopping-opt-in-integrated-headline",
fontSize: "20px",
letterSpacing: "0",
},
subtitle: {
string_id:
"shopping-callout-not-opted-in-integrated-paragraph1",
letterSpacing: "0",
},
above_button_content: [
{
type: "text",
text: {
string_id:
"shopping-callout-not-opted-in-integrated-paragraph2",
letterSpacing: "0",
textAlign: "start",
fontSize: "0.831em",
marginBlock: "0",
marginInline: "0",
},
link_keys: ["privacy_policy", "terms_of_use"],
font_styles: "legal",
},
],
privacy_policy: {
action: {
type: "OPEN_URL",
data: {
args: "https://www.mozilla.org/privacy/firefox?utm_source=review-checker&utm_campaign=privacy-policy&utm_medium=in-product&utm_term=opt-in-screen",
where: "tab",
},
},
},
terms_of_use: {
action: {
type: "OPEN_URL",
data: {
args: "https://www.fakespot.com/terms?utm_source=review-checker&utm_campaign=terms-of-use&utm_medium=in-product",
where: "tab",
},
},
},
logo: {
imageURL:
"chrome://browser/content/shopping/assets/reviewsVisualCallout.svg",
alt: {
string_id: "shopping-callout-not-opted-in-reminder-img-alt",
},
height: "214px",
},
dismiss_button: {
action: { dismiss: true },
size: "small",
marginBlock: "28px 0",
marginInline: "0 28px",
},
secondary_button: {
label: {
string_id:
"shopping-callout-not-opted-in-integrated-reminder-accept-button",
marginBlock: "0 -8px",
},
style: "primary",
action: {
type: "MULTI_ACTION",
collectSelect: true,
data: {
actions: [
{
type: "SET_PREF",
data: {
pref: {
name: "browser.shopping.experience2023.optedIn",
value: 1,
},
},
},
{
type: "OPEN_SIDEBAR",
data: "viewReviewCheckerSidebar",
},
],
},
dismiss: true,
},
},
additional_button: {
label: {
string_id:
"shopping-callout-not-opted-in-integrated-reminder-dismiss-button",
marginBlock: "0 -8px",
},
style: "secondary",
action: { dismiss: true },
},
submenu_button: {
submenu: [
{
type: "action",
label: {
raw: {
string_id:
"shopping-callout-not-opted-in-integrated-reminder-do-not-show",
},
},
action: {
type: "SET_PREF",
data: {
pref: {
name: "messaging-system-action.shopping-block-review-checker-callout-3",
value: true,
},
},
dismiss: true,
},
id: "shopping-callout-not-opted-in-integrated-reminder-do-not-show",
},
{
type: "action",
label: {
raw: {
string_id:
"shopping-callout-not-opted-in-integrated-reminder-show-fewer",
},
},
action: {
type: "MULTI_ACTION",
collectSelect: true,
data: {
actions: [
{
type: "SET_PREF",
data: {
pref: {
name: "messaging-system-action.shopping-block-review-checker-callouts",
value: true,
},
},
},
{
type: "SET_PREF",
data: {
pref: {
name: "messaging-system-action.shopping-block-review-checker-callout-3",
value: true,
},
},
},
],
},
dismiss: true,
},
id: "shopping-callout-not-opted-in-integrated-reminder-show-fewer",
},
{
type: "separator",
},
{
type: "action",
label: {
raw: {
string_id:
"shopping-callout-not-opted-in-integrated-reminder-manage-settings",
},
},
action: {
type: "OPEN_ABOUT_PAGE",
data: {
args: "settings#general-cfrfeatures",
where: "tab",
},
dismiss: true,
},
id: "shopping-callout-not-opted-in-integrated-reminder-manage-settings",
},
],
attached_to: "additional_button",
style: "secondary",
label: {
marginBlock: "0 -8px",
},
},
tiles: {
type: "multiselect",
style: {
flexDirection: "column",
alignItems: "flex-start",
},
data: [],
},
},
},
],
},
priority: 2,
// Review checker is added to the sidebar; Sidebar is closed; Review checker callouts have not been disabled; Integrated Sidebar is enabled; User is not opted in; Has not opted out of CFRs; Onboarding impression was at least 24 hr ago; Frequency of 5 days;
targeting:
"'sidebar.main.tools' | preferenceValue | regExpMatch('reviewchecker') && !'messaging-system-action.shopping-block-review-checker-callout-3' | preferenceValue && !'messaging-system-action.shopping-block-review-checker-callouts' | preferenceValue && isReviewCheckerInSidebarClosed && 'browser.shopping.experience2023.integratedSidebar' | preferenceValue && 'sidebar.revamp' | preferenceValue && 'browser.shopping.experience2023.optedIn' | preferenceValue == 0 && 'browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features' | preferenceValue != false && 'browser.shopping.experience2023.firstImpressionTime' | preferenceValue && ((currentDate | date - ('browser.shopping.experience2023.firstImpressionTime' | preferenceValue * 1000)) / 3600000) > 24 && !'sidebar.verticalTabs' | preferenceValue",
trigger: {
id: "shoppingProductPageWithIntegratedRCSidebarClosed",
},
frequency: {
custom: [
{
cap: 1,
period: 432000000,
},
],
lifetime: 3,
},
skip_in_tests:
"not tested in automation and might pop up unexpectedly during review checker tests",
},
{
// "Callout 3" in the Review Checker Integrated Sidebar Migration Figma spec
// For non-opted in users
// Triggered if the Review Checker is panel is closed and user visits a product page
// Explains why you should use Review Checker and prompts to opt in
// Vertical tabs
id: "REVIEW_CHECKER_CALLOUT_PDP_NOT_OPTED_IN_REMINDER",
template: "feature_callout",
content: {
id: "REVIEW_CHECKER_CALLOUT_PDP_NOT_OPTED_IN_REMINDER",
template: "multistage",
backdrop: "transparent",
transitions: false,
disableHistoryUpdates: true,
screens: [
{
id: "REVIEW_CHECKER_CALLOUT_PDP_NOT_OPTED_IN_REMINDER_VERTICAL",
anchors: [
{
selector:
"#sidebar-main:not([positionend]) > sidebar-main::%shadow% .tools-and-extensions::%shadow% moz-button[view='viewReviewCheckerSidebar']",
panel_position: {
anchor_attachment: "rightcenter",
callout_attachment: "bottomleft",
},
no_open_on_anchor: true,
},
{
selector:
"#sidebar-main[positionend] > sidebar-main::%shadow% .tools-and-extensions::%shadow% moz-button[view='viewReviewCheckerSidebar']",
panel_position: {
anchor_attachment: "leftcenter",
callout_attachment: "bottomright",
},
no_open_on_anchor: true,
},
],
content: {
position: "callout",
width: "401px",
title: {
string_id: "shopping-opt-in-integrated-headline",
fontSize: "20px",
letterSpacing: "0",
},
subtitle: {
string_id:
"shopping-callout-not-opted-in-integrated-paragraph1",
letterSpacing: "0",
},
above_button_content: [
{
type: "text",
text: {
string_id:
"shopping-callout-not-opted-in-integrated-paragraph2",
letterSpacing: "0",
textAlign: "start",
fontSize: "0.831em",
marginBlock: "0",
marginInline: "0",
},
link_keys: ["privacy_policy", "terms_of_use"],
font_styles: "legal",
},
],
privacy_policy: {
action: {
type: "OPEN_URL",
data: {
args: "https://www.mozilla.org/privacy/firefox?utm_source=review-checker&utm_campaign=privacy-policy&utm_medium=in-product&utm_term=opt-in-screen",
where: "tab",
},
},
},
terms_of_use: {
action: {
type: "OPEN_URL",
data: {
args: "https://www.fakespot.com/terms?utm_source=review-checker&utm_campaign=terms-of-use&utm_medium=in-product",
where: "tab",
},
},
},
logo: {
imageURL:
"chrome://browser/content/shopping/assets/reviewsVisualCallout.svg",
alt: {
string_id: "shopping-callout-not-opted-in-reminder-img-alt",
},
height: "214px",
},
dismiss_button: {
action: { dismiss: true },
size: "small",
marginBlock: "28px 0",
marginInline: "0 28px",
},
secondary_button: {
label: {
string_id:
"shopping-callout-not-opted-in-integrated-reminder-accept-button",
marginBlock: "0 -8px",
},
style: "primary",
action: {
type: "MULTI_ACTION",
collectSelect: true,
data: {
actions: [
{
type: "SET_PREF",
data: {
pref: {
name: "browser.shopping.experience2023.optedIn",
value: 1,
},
},
},
{
type: "OPEN_SIDEBAR",
data: "viewReviewCheckerSidebar",
},
],
},
dismiss: true,
},
},
additional_button: {
label: {
string_id:
"shopping-callout-not-opted-in-integrated-reminder-dismiss-button",
marginBlock: "0 -8px",
},
style: "secondary",
action: { dismiss: true },
},
submenu_button: {
submenu: [
{
type: "action",
label: {
raw: {
string_id:
"shopping-callout-not-opted-in-integrated-reminder-do-not-show",
},
},
action: {
type: "SET_PREF",
data: {
pref: {
name: "messaging-system-action.shopping-block-review-checker-callout-3",
value: true,
},
},
dismiss: true,
},
id: "shopping-callout-not-opted-in-integrated-reminder-do-not-show",
},
{
type: "action",
label: {
raw: {
string_id:
"shopping-callout-not-opted-in-integrated-reminder-show-fewer",
},
},
action: {
type: "MULTI_ACTION",
collectSelect: true,
data: {
actions: [
{
type: "SET_PREF",
data: {
pref: {
name: "messaging-system-action.shopping-block-review-checker-callouts",
value: true,
},
},
},
{
type: "SET_PREF",
data: {
pref: {
name: "messaging-system-action.shopping-block-review-checker-callout-3",
value: true,
},
},
},
],
},
dismiss: true,
},
id: "shopping-callout-not-opted-in-integrated-reminder-show-fewer",
},
{
type: "separator",
},
{
type: "action",
label: {
raw: {
string_id:
"shopping-callout-not-opted-in-integrated-reminder-manage-settings",
},
},
action: {
type: "OPEN_ABOUT_PAGE",
data: {
args: "settings#general-cfrfeatures",
where: "tab",
},
dismiss: true,
},
id: "shopping-callout-not-opted-in-integrated-reminder-manage-settings",
},
],
attached_to: "additional_button",
style: "secondary",
label: {
marginBlock: "0 -8px",
},
},
tiles: {
type: "multiselect",
style: {
flexDirection: "column",
alignItems: "flex-start",
},
data: [],
},
},
},
],
},
priority: 2,
// Review checker is added to the sidebar; Sidebar is closed; Review checker callouts have not been disabled; Integrated Sidebar is enabled; User is not opted in; Has not opted out of CFRs; Onboarding impression was at least 24 hr ago; Frequency of 5 days;
targeting:
"'sidebar.main.tools' | preferenceValue | regExpMatch('reviewchecker') && !'messaging-system-action.shopping-block-review-checker-callout-3' | preferenceValue && !'messaging-system-action.shopping-block-review-checker-callouts' | preferenceValue && isReviewCheckerInSidebarClosed && 'browser.shopping.experience2023.integratedSidebar' | preferenceValue && 'sidebar.revamp' | preferenceValue && 'browser.shopping.experience2023.optedIn' | preferenceValue == 0 && 'browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features' | preferenceValue != false && 'browser.shopping.experience2023.firstImpressionTime' | preferenceValue && ((currentDate | date - ('browser.shopping.experience2023.firstImpressionTime' | preferenceValue * 1000)) / 3600000) > 24 && 'sidebar.verticalTabs' | preferenceValue",
trigger: {
id: "shoppingProductPageWithIntegratedRCSidebarClosed",
},
frequency: {
custom: [
{
cap: 1,
period: 432000000,
},
],
lifetime: 3,
},
skip_in_tests:
"not tested in automation and might pop up unexpectedly during review checker tests",
},
{
// "Callout 4" in the Fakespot Figma spec, for rediscoverability experiment 2.
id: "FAKESPOT_CALLOUT_DISABLED_AUTO_OPEN",

View File

@@ -440,7 +440,7 @@
data-l10n-id="translations-manage-settings-button"/>
</hbox>
<vbox>
<html:div id="translations-manage-install-list" hidden="true" tabindex="-1">
<html:div id="translations-manage-install-list" tabindex="-1">
<hbox class="translations-manage-language">
<label data-l10n-id="translations-manage-download-description"></label>
<button id="translations-manage-install-all"

View File

@@ -1282,7 +1282,6 @@ var gMainPane = {
}
this.updateAllButtons();
this.elements.installList.appendChild(listFragment);
this.elements.installList.hidden = false;
}
/**

View File

@@ -96,7 +96,7 @@ export class ReviewCheckerManager {
return;
}
this.window.gBrowser.addProgressListener(this);
this.window.addEventListener("ShowReviewCheckerSidebar", this);
this.window.addEventListener("OpenReviewCheckerSidebar", this);
this.window.addEventListener("CloseReviewCheckerSidebar", this);
this.#hasListeners = true;
}
@@ -106,7 +106,7 @@ export class ReviewCheckerManager {
return;
}
this.window.gBrowser.removeProgressListener(this);
this.window.removeEventListener("ShowReviewCheckerSidebar", this);
this.window.removeEventListener("OpenReviewCheckerSidebar", this);
this.window.removeEventListener("CloseReviewCheckerSidebar", this);
this.#hasListeners = null;
}
@@ -209,6 +209,14 @@ export class ReviewCheckerManager {
} else {
// Check if we should auto-open to allow opting in.
shouldAutoOpen = lazy.ShoppingUtils.handleAutoActivateOnProduct();
lazy.ShoppingUtils.sendTrigger({
browser: selectedBrowser,
id: "shoppingProductPageWithIntegratedRCSidebarClosed",
context: {
isReviewCheckerInSidebarClosed: !this.SidebarController?.isOpen,
},
});
}
// Only show sidebar if no sidebar panel is currently showing,

View File

@@ -8,8 +8,20 @@
## Opt-in message strings for Review Checker when it is integrated into the global sidebar.
shopping-opt-in-integrated-headline = Shop with confidence
shopping-opt-in-integrated-headline = Can you trust these reviews?
shopping-opt-in-integrated-subtitle = Turn on Review Checker in { -brand-product-name } to see how reliable product reviews are, before you buy. It uses AI technology to analyze reviews. <a data-l10n-name="learn_more">Learn more</a>
## Messages for callout for users not opted into the sidebar integrated version of Review Checker.
# Appears underneath shopping-opt-in-integrated-headline to answer the question 'Can you trust these reviews?'
shopping-callout-not-opted-in-integrated-paragraph1 = Turn on Review Checker from { -brand-product-name } to find out. Its powered by { -fakespot-brand-full-name } and uses AI technology to analyze reviews.
shopping-callout-not-opted-in-integrated-paragraph2 = By selecting “{ shopping-opt-in-integrated-button }” you agree to { -brand-product-name }s <a data-l10n-name="privacy_policy">privacy notice</a> and { -fakespot-brand-full-name }s <a data-l10n-name="terms_of_use">terms of use</a>.
shopping-callout-not-opted-in-integrated-reminder-dismiss-button = Dismiss
shopping-callout-not-opted-in-integrated-reminder-accept-button = Turn on Review Checker
shopping-callout-not-opted-in-integrated-reminder-do-not-show = Dont show this recommendation again
shopping-callout-not-opted-in-integrated-reminder-show-fewer = Show fewer recommendations
shopping-callout-not-opted-in-integrated-reminder-manage-settings = Manage settings
shopping-opt-in-integrated-subtitle-unsupported-site = Review Checker from { -brand-product-name } helps you know how reliable a products reviews are, before you buy. It uses AI technology to analyze reviews. <a data-l10n-name="learn_more">Learn more</a>
shopping-opt-in-integrated-privacy-policy-and-terms-of-use = Review Checker is powered by { -fakespot-brand-full-name }. By selecting “{ shopping-opt-in-integrated-button }“ you agree to { -brand-product-name }s <a data-l10n-name="privacy_policy">privacy notice</a> and { -fakespot-brand-name }s <a data-l10n-name="terms_of_use">terms of use.</a>

View File

@@ -210,6 +210,7 @@ export const SpecialMessageActions = {
"browser.shopping.experience2023.survey.optedInTime",
"browser.shopping.experience2023.survey.hasSeen",
"browser.shopping.experience2023.survey.pdpVisits",
"browser.shopping.experience2023.firstImpressionTime",
"browser.startup.homepage",
"browser.startup.windowsLaunchOnLogin.disableLaunchOnLoginPrompt",
"browser.privateWindowSeparation.enabled",

View File

@@ -269,6 +269,50 @@
"additionalProperties": false
}
},
{
"type": "object",
"properties": {
"id": {
"type": "string",
"enum": ["shoppingProductPageWithIntegratedRCSidebarClosed"]
}
},
"additionalProperties": false,
"required": ["id"],
"description": "Happens when the user navigates to a product page with the sidebar closed",
"context": {
"type": "object",
"properties": {
"isReviewCheckerInSidebarClosed": {
"type": "string",
"description": "Whether the Review Checker panel in the sidebar is closed"
}
},
"additionalProperties": false
}
},
{
"type": "object",
"properties": {
"id": {
"type": "string",
"enum": ["reviewCheckerSidebarClosedCallout"]
}
},
"additionalProperties": false,
"required": ["id"],
"description": "Happens when the user closes the sidebar",
"context": {
"type": "object",
"properties": {
"isReviewCheckerInSidebarClosed": {
"type": "string",
"description": "Whether the Review Checker panel in the sidebar is closed"
}
},
"additionalProperties": false
}
},
{
"type": "object",
"properties": {

View File

@@ -61,6 +61,8 @@ let patterns: string[];
- [`pageActionInUrlbar`](#pageactioninurlbar)
- [`onSearch`](#onsearch)
- [`sidebarToolOpened`](#sidebartoolopened)
- [`shoppingProductPageWithIntegratedRCSidebarClosed`](#shoppingproductpage)
- [`reviewCheckerSidebarClosedCallout`](#sidebarclosed)
- [`elementClicked`](#elementclicked)
### `openArticleURL`
@@ -383,6 +385,32 @@ The `clickCounts` object context variable is also available in targeting, and in
}
```
### `shoppingProductPageWithIntegratedRCSidebarClosed`
Happens when the user navigates to a product page
The `isReviewCheckerInSidebarClosed` string context variable is available in targeting, and will correspond with which whether the Review Checker panel in the sidebar is closed
```js
{
trigger: { id: "shoppingProductPageWithIntegratedRCSidebarClosed" },
targeting: `'sidebar.main.tools' | preferenceValue | regExpMatch('reviewchecker') && !'messaging-system-action.shopping-block-review-checker-callout-3' | preferenceValue && !'messaging-system-action.shopping-block-review-checker-callouts' | preferenceValue && isReviewCheckerInSidebarClosed && 'browser.shopping.experience2023.integratedSidebar' | preferenceValue && 'sidebar.revamp' | preferenceValue && 'browser.shopping.experience2023.optedIn' | preferenceValue == 0 && 'browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features' | preferenceValue != false && 'browser.shopping.experience2023.onboardingImpressionTime' | preferenceValue && ((currentDate | date - ('browser.shopping.experience2023.onboardingImpressionTime' | preferenceValue * 1000)) / 3600000) > 24 && !'sidebar.verticalTabs' | preferenceValue`
}
```
### `reviewCheckerSidebarClosedCallout`
Happens when the user navigates to a product page
The `isReviewCheckerInSidebarClosed` string context variable is available in targeting, and will correspond with which whether the Review Checker panel in the sidebar is closed
```js
{
trigger: { id: "shoppingProductPageWithIntegratedRCSidebarClosed" },
targeting: `'browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features' | preferenceValue && 'browser.shopping.experience2023.integratedSidebar' | preferenceValue && 'sidebar.revamp' | preferenceValue && 'browser.shopping.experience2023.optedIn' | preferenceValue == 1 && isReviewCheckerInSidebarClosed && !'sidebar.verticalTabs' | preferenceValue`
}
```
### `elementClicked`
Happens when an element in the browser chrome is clicked. The trigger will only fire if the element that is clicked has an ID that is within the trigger's params array.