Bug 1812690 - Pocket newtab enabling onboarding experience for new users seeing the Pocket section for the first time. r=gvn,fluent-reviewers,flod

Differential Revision: https://phabricator.services.mozilla.com/D174710
This commit is contained in:
scottdowne 2023-04-25 16:49:25 +00:00
Родитель bbf1e58474
Коммит 42b6e74c81
23 изменённых файлов: 940 добавлений и 206 удалений

Просмотреть файл

@ -1634,6 +1634,10 @@ pref("browser.newtabpage.activity-stream.discoverystream.recs.personalized", fal
// System pref to allow Pocket sponsored content personalization to be turned on/off.
pref("browser.newtabpage.activity-stream.discoverystream.spocs.personalized", true);
// Flip this once the user has dismissed the Pocket onboarding experience,
pref("browser.newtabpage.activity-stream.discoverystream.onboardingExperience.dismissed", false);
pref("browser.newtabpage.activity-stream.discoverystream.onboardingExperience.enabled", false);
// User pref to show stories on newtab (feeds.system.topstories has to be set to true as well)
pref("browser.newtabpage.activity-stream.feeds.section.topstories", true);

Просмотреть файл

@ -79,6 +79,7 @@ for (const type of [
"FAKE_FOCUS_SEARCH",
"FILL_SEARCH_TERM",
"HANDOFF_SEARCH_TO_AWESOMEBAR",
"HIDE_PERSONALIZE",
"HIDE_PRIVACY_INFO",
"INIT",
"NEW_TAB_INIT",
@ -127,6 +128,7 @@ for (const type of [
"SET_PREF",
"SHOW_DOWNLOAD_FILE",
"SHOW_FIREFOX_ACCOUNTS",
"SHOW_PERSONALIZE",
"SHOW_PRIVACY_INFO",
"SHOW_SEARCH",
"SKIPPED_SIGNIN",

Просмотреть файл

@ -17,6 +17,7 @@ export const INITIAL_STATE = {
initialized: false,
locale: "",
isForStartupCache: false,
customizeMenuVisible: false,
},
ASRouter: { initialized: false },
Snippets: { initialized: false },
@ -108,6 +109,14 @@ function App(prevState = INITIAL_STATE.App, action) {
return Object.assign({}, prevState, action.data || {}, {
isForStartupCache: false,
});
case at.SHOW_PERSONALIZE:
return Object.assign({}, prevState, {
customizeMenuVisible: true,
});
case at.HIDE_PERSONALIZE:
return Object.assign({}, prevState, {
customizeMenuVisible: false,
});
default:
return prevState;
}

Просмотреть файл

@ -111,7 +111,7 @@ export class BaseContent extends React.PureComponent {
this.handleOnKeyDown = this.handleOnKeyDown.bind(this);
this.onWindowScroll = debounce(this.onWindowScroll.bind(this), 5);
this.setPref = this.setPref.bind(this);
this.state = { fixedSearch: false, customizeMenuVisible: false };
this.state = { fixedSearch: false };
}
componentDidMount() {
@ -140,13 +140,13 @@ export class BaseContent extends React.PureComponent {
}
openCustomizationMenu() {
this.setState({ customizeMenuVisible: true });
this.props.dispatch({ type: at.SHOW_PERSONALIZE });
this.props.dispatch(ac.UserEvent({ event: "SHOW_PERSONALIZE" }));
}
closeCustomizationMenu() {
if (this.state.customizeMenuVisible) {
this.setState({ customizeMenuVisible: false });
if (this.props.App.customizeMenuVisible) {
this.props.dispatch({ type: at.HIDE_PERSONALIZE });
this.props.dispatch(ac.UserEvent({ event: "HIDE_PERSONALIZE" }));
}
}
@ -164,7 +164,7 @@ export class BaseContent extends React.PureComponent {
render() {
const { props } = this;
const { App } = props;
const { initialized } = App;
const { initialized, customizeMenuVisible } = App;
const prefs = props.Prefs.values;
const isDiscoveryStream =
@ -180,7 +180,6 @@ export class BaseContent extends React.PureComponent {
!pocketEnabled &&
filteredSections.filter(section => section.enabled).length === 0;
const searchHandoffEnabled = prefs["improvesearch.handoffToAwesomebar"];
const showCustomizationMenu = this.state.customizeMenuVisible;
const enabledSections = {
topSitesEnabled: prefs["feeds.topsites"],
pocketEnabled: prefs["feeds.section.topstories"],
@ -224,7 +223,7 @@ export class BaseContent extends React.PureComponent {
enabledSections={enabledSections}
pocketRegion={pocketRegion}
mayHaveSponsoredTopSites={mayHaveSponsoredTopSites}
showing={showCustomizationMenu}
showing={customizeMenuVisible}
/>
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions*/}
<div className={outerClassName} onClick={this.closeCustomizationMenu}>

Просмотреть файл

@ -192,6 +192,7 @@ export class _DiscoveryStreamBase extends React.PureComponent {
fourCardLayout={component.properties.fourCardLayout}
compactGrid={component.properties.compactGrid}
essentialReadsHeader={component.properties.essentialReadsHeader}
onboardingExperience={component.properties.onboardingExperience}
editorsPicksHeader={component.properties.editorsPicksHeader}
recentSavesEnabled={this.props.DiscoveryStream.recentSavesEnabled}
hideDescriptions={this.props.DiscoveryStream.hideDescriptions}

Просмотреть файл

@ -4,6 +4,7 @@
import { DSCard, PlaceholderDSCard } from "../DSCard/DSCard.jsx";
import { DSEmptyState } from "../DSEmptyState/DSEmptyState.jsx";
import { DSDismiss } from "content-src/components/DiscoveryStreamComponents/DSDismiss/DSDismiss";
import { TopicsWidget } from "../TopicsWidget/TopicsWidget.jsx";
import { SafeAnchor } from "../SafeAnchor/SafeAnchor";
import { FluentOrText } from "../../FluentOrText/FluentOrText.jsx";
@ -13,6 +14,9 @@ import {
} from "common/Actions.sys.mjs";
import React, { useEffect, useState, useRef, useCallback } from "react";
import { connect, useSelector } from "react-redux";
const PREF_ONBOARDING_EXPERIENCE_DISMISSED =
"discoverystream.onboardingExperience.dismissed";
const INTERSECTION_RATIO = 0.5;
const WIDGET_IDS = {
TOPICS: 1,
};
@ -25,6 +29,99 @@ export function DSSubHeader({ children }) {
);
}
export function OnboardingExperience({
children,
dispatch,
windowObj = global,
}) {
const [dismissed, setDismissed] = useState(false);
const [maxHeight, setMaxHeight] = useState(null);
const heightElement = useRef(null);
const onDismissClick = useCallback(() => {
// We update this as state and redux.
// The state update is for this newtab,
// and the redux update is for other tabs, offscreen tabs, and future tabs.
// We need the state update for this tab to support the transition.
setDismissed(true);
dispatch(ac.SetPref(PREF_ONBOARDING_EXPERIENCE_DISMISSED, true));
dispatch(
ac.DiscoveryStreamUserEvent({
event: "BLOCK",
source: "POCKET_ONBOARDING",
})
);
}, [dispatch]);
useEffect(() => {
const resizeObserver = new windowObj.ResizeObserver(() => {
if (heightElement.current) {
setMaxHeight(heightElement.current.offsetHeight);
}
});
const options = { threshold: INTERSECTION_RATIO };
const intersectionObserver = new windowObj.IntersectionObserver(entries => {
if (
entries.some(
entry =>
entry.isIntersecting &&
entry.intersectionRatio >= INTERSECTION_RATIO
)
) {
dispatch(
ac.DiscoveryStreamUserEvent({
event: "IMPRESSION",
source: "POCKET_ONBOARDING",
})
);
// Once we have observed an impression, we can stop for this instance of newtab.
intersectionObserver.unobserve(heightElement.current);
}
}, options);
if (heightElement.current) {
resizeObserver.observe(heightElement.current);
intersectionObserver.observe(heightElement.current);
setMaxHeight(heightElement.current.offsetHeight);
}
// Return unmount callback to clean up observers.
return () => {
resizeObserver?.disconnect();
intersectionObserver?.disconnect();
};
}, [dispatch, windowObj]);
const style = {};
if (dismissed) {
style.maxHeight = "0";
style.opacity = "0";
style.transition = "max-height 0.26s ease, opacity 0.26s ease";
} else if (maxHeight) {
style.maxHeight = `${maxHeight}px`;
}
return (
<div style={style}>
<div className="ds-onboarding-ref" ref={heightElement}>
<div className="ds-onboarding">
<DSDismiss
onDismissClick={onDismissClick}
extraClasses={`ds-onboarding-dismiss`}
>
<div className="ds-onboarding-graphic" />
<header>
<span className="icon icon-pocket" />
<span data-l10n-id="newtab-pocket-onboarding-discover" />
</header>
<p data-l10n-id="newtab-pocket-onboarding-cta" />
</DSDismiss>
</div>
</div>
</div>
);
}
export function IntersectionObserver({
children,
windowObj = window,
@ -204,6 +301,7 @@ export class _CardGrid extends React.PureComponent {
compactGrid,
essentialReadsHeader,
editorsPicksHeader,
onboardingExperience,
widgets,
recentSavesEnabled,
hideDescriptions,
@ -211,6 +309,8 @@ export class _CardGrid extends React.PureComponent {
} = this.props;
const { saveToPocketCard } = DiscoveryStream;
const showRecentSaves = prefs.showRecentSaves && recentSavesEnabled;
const isOnboardingExperienceDismissed =
prefs[PREF_ONBOARDING_EXPERIENCE_DISMISSED];
const recs = this.props.data.recommendations.slice(0, items);
const cards = [];
@ -319,10 +419,13 @@ export class _CardGrid extends React.PureComponent {
? `ds-card-grid-hybrid-layout`
: ``;
const gridClassName = `ds-card-grid ds-card-grid-border ${hybridLayoutClassName} ${hideCardBackgroundClass} ${fourCardLayoutClass} ${hideDescriptionsClassName} ${compactGridClassName}`;
const gridClassName = `ds-card-grid ${hybridLayoutClassName} ${hideCardBackgroundClass} ${fourCardLayoutClass} ${hideDescriptionsClassName} ${compactGridClassName}`;
return (
<>
{!isOnboardingExperienceDismissed && onboardingExperience && (
<OnboardingExperience dispatch={this.props.dispatch} />
)}
{essentialReadsCards?.length > 0 && (
<div className={gridClassName}>{essentialReadsCards}</div>
)}

Просмотреть файл

@ -1,6 +1,123 @@
$col4-header-line-height: 20;
$col4-header-font-size: 14;
.ds-onboarding,
.ds-card-grid .ds-card {
@include dark-theme-only {
background: none;
}
background: $white;
border-radius: 4px;
&:not(.placeholder) {
@include dark-theme-only {
background: var(--newtab-background-color-secondary);
}
border-radius: $border-radius-new;
box-shadow: $shadow-card;
.img-wrapper .img {
img,
.placeholder-image {
border-radius: $border-radius-new $border-radius-new 0 0;
}
}
}
}
.ds-onboarding-ref {
display: inline-block;
}
.ds-onboarding {
padding-inline-start: 16px;
padding-inline-end: 16px;
@media (min-width: $break-point-medium) {
padding-inline-end: 48px;
}
@media (min-width: $break-point-large) {
padding-inline-end: 56px;
}
margin-bottom: 24px;
display: inline-block;
// This is to position the dismiss button to the right most of this element.
position: relative;
.ds-dismiss {
position: static;
.ds-dismiss-button {
inset-inline-end: 8px;
top: 8px;
}
}
header {
@include dark-theme-only {
color: var(--newtab-background-color-primary);
}
display: flex;
margin: 32px 0 8px;
@media (min-width: $break-point-medium) {
margin: 16px 0 8px;
display: block;
height: 24px;
}
font-size: 17px;
line-height: 23.8px;
font-weight: 600;
color: $pocket-icon-fill;
}
p {
margin: 8px 0 16px;
font-size: 13px;
line-height: 19.5px;
}
.icon-pocket {
@include dark-theme-only {
fill: var(--newtab-text-primary-color);
}
fill: $pocket-icon-fill;
margin-top: 3px;
margin-inline-end: 8px;
height: 22px;
width: 22px;
background-image: url('chrome://global/skin/icons/pocket.svg');
@media (min-width: $break-point-medium) {
margin-top: -5px;
margin-inline-start: -2px;
margin-inline-end: 15px;
height: 30px;
width: 30px;
}
background-size: contain;
}
.ds-onboarding-graphic {
background-image: url('chrome://activity-stream/content/data/content/assets/pocket-onboarding.avif');
@media (min-resolution: 2x) {
background-image: url('chrome://activity-stream/content/data/content/assets/pocket-onboarding@2x.avif');
}
border-radius: 8px;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
height: 120px;
width: 200px;
margin-inline-start: 54px;
margin-bottom: 16px;
float: inline-end;
display: none;
@media (min-width: $break-point-large) {
display: block;
}
}
}
.ds-card-grid {
display: grid;
grid-gap: 24px;
@ -20,32 +137,6 @@ $col4-header-font-size: 14;
}
}
.ds-card {
@include dark-theme-only {
background: none;
}
background: $white;
border-radius: 4px;
}
&.ds-card-grid-border {
.ds-card:not(.placeholder) {
@include dark-theme-only {
background: var(--newtab-background-color-secondary);
}
border-radius: $border-radius-new;
box-shadow: $shadow-card;
.img-wrapper .img {
img,
.placeholder-image {
border-radius: $border-radius-new $border-radius-new 0 0;
}
}
}
}
.ds-card-link:focus {
@include ds-focus;
transition: none;
@ -193,8 +284,8 @@ $col4-header-font-size: 14;
}
}
.outer-wrapper .ds-card-grid.ds-card-grid-hide-background.ds-card-grid-border .ds-card,
.outer-wrapper.newtab-experience .ds-card-grid.ds-card-grid-hide-background.ds-card-grid-border .ds-card {
.outer-wrapper .ds-card-grid.ds-card-grid-hide-background .ds-card,
.outer-wrapper.newtab-experience .ds-card-grid.ds-card-grid-hide-background .ds-card {
&:not(.placeholder) {
box-shadow: none;
background: none;

Просмотреть файл

@ -23,9 +23,5 @@
&:is(:hover, :focus, .active) {
@include context-menu-button-hover;
outline: none;
&.ds-card-grid-border {
@include fade-in-card;
}
}
}

Просмотреть файл

@ -2625,6 +2625,127 @@ main.has-snippet {
text-decoration: underline;
}
.ds-onboarding,
.ds-card-grid .ds-card {
background: #FFF;
border-radius: 4px;
}
[lwt-newtab-brighttext] .ds-onboarding,
[lwt-newtab-brighttext] .ds-card-grid .ds-card {
background: none;
}
.ds-onboarding:not(.placeholder),
.ds-card-grid .ds-card:not(.placeholder) {
border-radius: 8px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
}
[lwt-newtab-brighttext] .ds-onboarding:not(.placeholder),
[lwt-newtab-brighttext] .ds-card-grid .ds-card:not(.placeholder) {
background: var(--newtab-background-color-secondary);
}
.ds-onboarding:not(.placeholder) .img-wrapper .img img,
.ds-onboarding:not(.placeholder) .img-wrapper .img .placeholder-image,
.ds-card-grid .ds-card:not(.placeholder) .img-wrapper .img img,
.ds-card-grid .ds-card:not(.placeholder) .img-wrapper .img .placeholder-image {
border-radius: 8px 8px 0 0;
}
.ds-onboarding-ref {
display: inline-block;
}
.ds-onboarding {
padding-inline-start: 16px;
padding-inline-end: 16px;
margin-bottom: 24px;
display: inline-block;
position: relative;
}
@media (min-width: 610px) {
.ds-onboarding {
padding-inline-end: 48px;
}
}
@media (min-width: 866px) {
.ds-onboarding {
padding-inline-end: 56px;
}
}
.ds-onboarding .ds-dismiss {
position: static;
}
.ds-onboarding .ds-dismiss .ds-dismiss-button {
inset-inline-end: 8px;
top: 8px;
}
.ds-onboarding header {
display: flex;
margin: 32px 0 8px;
font-size: 17px;
line-height: 23.8px;
font-weight: 600;
color: #EF4056;
}
[lwt-newtab-brighttext] .ds-onboarding header {
color: var(--newtab-background-color-primary);
}
@media (min-width: 610px) {
.ds-onboarding header {
margin: 16px 0 8px;
display: block;
height: 24px;
}
}
.ds-onboarding p {
margin: 8px 0 16px;
font-size: 13px;
line-height: 19.5px;
}
.ds-onboarding .icon-pocket {
fill: #EF4056;
margin-top: 3px;
margin-inline-end: 8px;
height: 22px;
width: 22px;
background-image: url("chrome://global/skin/icons/pocket.svg");
background-size: contain;
}
[lwt-newtab-brighttext] .ds-onboarding .icon-pocket {
fill: var(--newtab-text-primary-color);
}
@media (min-width: 610px) {
.ds-onboarding .icon-pocket {
margin-top: -5px;
margin-inline-start: -2px;
margin-inline-end: 15px;
height: 30px;
width: 30px;
}
}
.ds-onboarding .ds-onboarding-graphic {
background-image: url("chrome://activity-stream/content/data/content/assets/pocket-onboarding.avif");
border-radius: 8px;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
height: 120px;
width: 200px;
margin-inline-start: 54px;
margin-bottom: 16px;
float: inline-end;
display: none;
}
@media (min-resolution: 2x) {
.ds-onboarding .ds-onboarding-graphic {
background-image: url("chrome://activity-stream/content/data/content/assets/pocket-onboarding@2x.avif");
}
}
@media (min-width: 866px) {
.ds-onboarding .ds-onboarding-graphic {
display: block;
}
}
.ds-card-grid {
display: grid;
grid-gap: 24px;
@ -2637,24 +2758,6 @@ main.has-snippet {
display: none;
}
}
.ds-card-grid .ds-card {
background: #FFF;
border-radius: 4px;
}
[lwt-newtab-brighttext] .ds-card-grid .ds-card {
background: none;
}
.ds-card-grid.ds-card-grid-border .ds-card:not(.placeholder) {
border-radius: 8px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
}
[lwt-newtab-brighttext] .ds-card-grid.ds-card-grid-border .ds-card:not(.placeholder) {
background: var(--newtab-background-color-secondary);
}
.ds-card-grid.ds-card-grid-border .ds-card:not(.placeholder) .img-wrapper .img img,
.ds-card-grid.ds-card-grid-border .ds-card:not(.placeholder) .img-wrapper .img .placeholder-image {
border-radius: 8px 8px 0 0;
}
.ds-card-grid .ds-card-link:focus {
border: 0;
outline: 0;
@ -2851,28 +2954,28 @@ main.has-snippet {
}
}
.outer-wrapper .ds-card-grid.ds-card-grid-hide-background.ds-card-grid-border .ds-card:not(.placeholder),
.outer-wrapper.newtab-experience .ds-card-grid.ds-card-grid-hide-background.ds-card-grid-border .ds-card:not(.placeholder) {
.outer-wrapper .ds-card-grid.ds-card-grid-hide-background .ds-card:not(.placeholder),
.outer-wrapper.newtab-experience .ds-card-grid.ds-card-grid-hide-background .ds-card:not(.placeholder) {
box-shadow: none;
background: none;
}
.outer-wrapper .ds-card-grid.ds-card-grid-hide-background.ds-card-grid-border .ds-card:not(.placeholder) .ds-card-link:focus,
.outer-wrapper.newtab-experience .ds-card-grid.ds-card-grid-hide-background.ds-card-grid-border .ds-card:not(.placeholder) .ds-card-link:focus {
.outer-wrapper .ds-card-grid.ds-card-grid-hide-background .ds-card:not(.placeholder) .ds-card-link:focus,
.outer-wrapper.newtab-experience .ds-card-grid.ds-card-grid-hide-background .ds-card:not(.placeholder) .ds-card-link:focus {
box-shadow: none;
}
.outer-wrapper .ds-card-grid.ds-card-grid-hide-background.ds-card-grid-border .ds-card:not(.placeholder) .ds-card-link:focus .img-wrapper .img img,
.outer-wrapper.newtab-experience .ds-card-grid.ds-card-grid-hide-background.ds-card-grid-border .ds-card:not(.placeholder) .ds-card-link:focus .img-wrapper .img img {
.outer-wrapper .ds-card-grid.ds-card-grid-hide-background .ds-card:not(.placeholder) .ds-card-link:focus .img-wrapper .img img,
.outer-wrapper.newtab-experience .ds-card-grid.ds-card-grid-hide-background .ds-card:not(.placeholder) .ds-card-link:focus .img-wrapper .img img {
border: 0;
outline: 0;
box-shadow: 0 0 0 3px var(--newtab-primary-action-background-dimmed), 0 0 0 1px var(--newtab-primary-action-background);
}
.outer-wrapper .ds-card-grid.ds-card-grid-hide-background.ds-card-grid-border .ds-card:not(.placeholder) .img-wrapper .img img,
.outer-wrapper.newtab-experience .ds-card-grid.ds-card-grid-hide-background.ds-card-grid-border .ds-card:not(.placeholder) .img-wrapper .img img {
.outer-wrapper .ds-card-grid.ds-card-grid-hide-background .ds-card:not(.placeholder) .img-wrapper .img img,
.outer-wrapper.newtab-experience .ds-card-grid.ds-card-grid-hide-background .ds-card:not(.placeholder) .img-wrapper .img img {
border-radius: 8px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
}
.outer-wrapper .ds-card-grid.ds-card-grid-hide-background.ds-card-grid-border .ds-card:not(.placeholder) .meta,
.outer-wrapper.newtab-experience .ds-card-grid.ds-card-grid-hide-background.ds-card-grid-border .ds-card:not(.placeholder) .meta {
.outer-wrapper .ds-card-grid.ds-card-grid-hide-background .ds-card:not(.placeholder) .meta,
.outer-wrapper.newtab-experience .ds-card-grid.ds-card-grid-hide-background .ds-card:not(.placeholder) .meta {
padding: 12px 0 0;
}
@ -3271,11 +3374,6 @@ main.has-snippet {
transform: scale(1);
transition-delay: 333ms;
}
.ds-card:is(:hover, :focus, .active).ds-card-grid-border,
.ds-signup:is(:hover, :focus, .active).ds-card-grid-border {
box-shadow: 0 0 0 5px var(--newtab-element-secondary-color);
transition: box-shadow 150ms;
}
.ds-card {
display: flex;

Просмотреть файл

@ -2629,6 +2629,127 @@ main.has-snippet {
text-decoration: underline;
}
.ds-onboarding,
.ds-card-grid .ds-card {
background: #FFF;
border-radius: 4px;
}
[lwt-newtab-brighttext] .ds-onboarding,
[lwt-newtab-brighttext] .ds-card-grid .ds-card {
background: none;
}
.ds-onboarding:not(.placeholder),
.ds-card-grid .ds-card:not(.placeholder) {
border-radius: 8px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
}
[lwt-newtab-brighttext] .ds-onboarding:not(.placeholder),
[lwt-newtab-brighttext] .ds-card-grid .ds-card:not(.placeholder) {
background: var(--newtab-background-color-secondary);
}
.ds-onboarding:not(.placeholder) .img-wrapper .img img,
.ds-onboarding:not(.placeholder) .img-wrapper .img .placeholder-image,
.ds-card-grid .ds-card:not(.placeholder) .img-wrapper .img img,
.ds-card-grid .ds-card:not(.placeholder) .img-wrapper .img .placeholder-image {
border-radius: 8px 8px 0 0;
}
.ds-onboarding-ref {
display: inline-block;
}
.ds-onboarding {
padding-inline-start: 16px;
padding-inline-end: 16px;
margin-bottom: 24px;
display: inline-block;
position: relative;
}
@media (min-width: 610px) {
.ds-onboarding {
padding-inline-end: 48px;
}
}
@media (min-width: 866px) {
.ds-onboarding {
padding-inline-end: 56px;
}
}
.ds-onboarding .ds-dismiss {
position: static;
}
.ds-onboarding .ds-dismiss .ds-dismiss-button {
inset-inline-end: 8px;
top: 8px;
}
.ds-onboarding header {
display: flex;
margin: 32px 0 8px;
font-size: 17px;
line-height: 23.8px;
font-weight: 600;
color: #EF4056;
}
[lwt-newtab-brighttext] .ds-onboarding header {
color: var(--newtab-background-color-primary);
}
@media (min-width: 610px) {
.ds-onboarding header {
margin: 16px 0 8px;
display: block;
height: 24px;
}
}
.ds-onboarding p {
margin: 8px 0 16px;
font-size: 13px;
line-height: 19.5px;
}
.ds-onboarding .icon-pocket {
fill: #EF4056;
margin-top: 3px;
margin-inline-end: 8px;
height: 22px;
width: 22px;
background-image: url("chrome://global/skin/icons/pocket.svg");
background-size: contain;
}
[lwt-newtab-brighttext] .ds-onboarding .icon-pocket {
fill: var(--newtab-text-primary-color);
}
@media (min-width: 610px) {
.ds-onboarding .icon-pocket {
margin-top: -5px;
margin-inline-start: -2px;
margin-inline-end: 15px;
height: 30px;
width: 30px;
}
}
.ds-onboarding .ds-onboarding-graphic {
background-image: url("chrome://activity-stream/content/data/content/assets/pocket-onboarding.avif");
border-radius: 8px;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
height: 120px;
width: 200px;
margin-inline-start: 54px;
margin-bottom: 16px;
float: inline-end;
display: none;
}
@media (min-resolution: 2x) {
.ds-onboarding .ds-onboarding-graphic {
background-image: url("chrome://activity-stream/content/data/content/assets/pocket-onboarding@2x.avif");
}
}
@media (min-width: 866px) {
.ds-onboarding .ds-onboarding-graphic {
display: block;
}
}
.ds-card-grid {
display: grid;
grid-gap: 24px;
@ -2641,24 +2762,6 @@ main.has-snippet {
display: none;
}
}
.ds-card-grid .ds-card {
background: #FFF;
border-radius: 4px;
}
[lwt-newtab-brighttext] .ds-card-grid .ds-card {
background: none;
}
.ds-card-grid.ds-card-grid-border .ds-card:not(.placeholder) {
border-radius: 8px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
}
[lwt-newtab-brighttext] .ds-card-grid.ds-card-grid-border .ds-card:not(.placeholder) {
background: var(--newtab-background-color-secondary);
}
.ds-card-grid.ds-card-grid-border .ds-card:not(.placeholder) .img-wrapper .img img,
.ds-card-grid.ds-card-grid-border .ds-card:not(.placeholder) .img-wrapper .img .placeholder-image {
border-radius: 8px 8px 0 0;
}
.ds-card-grid .ds-card-link:focus {
border: 0;
outline: 0;
@ -2855,28 +2958,28 @@ main.has-snippet {
}
}
.outer-wrapper .ds-card-grid.ds-card-grid-hide-background.ds-card-grid-border .ds-card:not(.placeholder),
.outer-wrapper.newtab-experience .ds-card-grid.ds-card-grid-hide-background.ds-card-grid-border .ds-card:not(.placeholder) {
.outer-wrapper .ds-card-grid.ds-card-grid-hide-background .ds-card:not(.placeholder),
.outer-wrapper.newtab-experience .ds-card-grid.ds-card-grid-hide-background .ds-card:not(.placeholder) {
box-shadow: none;
background: none;
}
.outer-wrapper .ds-card-grid.ds-card-grid-hide-background.ds-card-grid-border .ds-card:not(.placeholder) .ds-card-link:focus,
.outer-wrapper.newtab-experience .ds-card-grid.ds-card-grid-hide-background.ds-card-grid-border .ds-card:not(.placeholder) .ds-card-link:focus {
.outer-wrapper .ds-card-grid.ds-card-grid-hide-background .ds-card:not(.placeholder) .ds-card-link:focus,
.outer-wrapper.newtab-experience .ds-card-grid.ds-card-grid-hide-background .ds-card:not(.placeholder) .ds-card-link:focus {
box-shadow: none;
}
.outer-wrapper .ds-card-grid.ds-card-grid-hide-background.ds-card-grid-border .ds-card:not(.placeholder) .ds-card-link:focus .img-wrapper .img img,
.outer-wrapper.newtab-experience .ds-card-grid.ds-card-grid-hide-background.ds-card-grid-border .ds-card:not(.placeholder) .ds-card-link:focus .img-wrapper .img img {
.outer-wrapper .ds-card-grid.ds-card-grid-hide-background .ds-card:not(.placeholder) .ds-card-link:focus .img-wrapper .img img,
.outer-wrapper.newtab-experience .ds-card-grid.ds-card-grid-hide-background .ds-card:not(.placeholder) .ds-card-link:focus .img-wrapper .img img {
border: 0;
outline: 0;
box-shadow: 0 0 0 3px var(--newtab-primary-action-background-dimmed), 0 0 0 1px var(--newtab-primary-action-background);
}
.outer-wrapper .ds-card-grid.ds-card-grid-hide-background.ds-card-grid-border .ds-card:not(.placeholder) .img-wrapper .img img,
.outer-wrapper.newtab-experience .ds-card-grid.ds-card-grid-hide-background.ds-card-grid-border .ds-card:not(.placeholder) .img-wrapper .img img {
.outer-wrapper .ds-card-grid.ds-card-grid-hide-background .ds-card:not(.placeholder) .img-wrapper .img img,
.outer-wrapper.newtab-experience .ds-card-grid.ds-card-grid-hide-background .ds-card:not(.placeholder) .img-wrapper .img img {
border-radius: 8px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
}
.outer-wrapper .ds-card-grid.ds-card-grid-hide-background.ds-card-grid-border .ds-card:not(.placeholder) .meta,
.outer-wrapper.newtab-experience .ds-card-grid.ds-card-grid-hide-background.ds-card-grid-border .ds-card:not(.placeholder) .meta {
.outer-wrapper .ds-card-grid.ds-card-grid-hide-background .ds-card:not(.placeholder) .meta,
.outer-wrapper.newtab-experience .ds-card-grid.ds-card-grid-hide-background .ds-card:not(.placeholder) .meta {
padding: 12px 0 0;
}
@ -3275,11 +3378,6 @@ main.has-snippet {
transform: scale(1);
transition-delay: 333ms;
}
.ds-card:is(:hover, :focus, .active).ds-card-grid-border,
.ds-signup:is(:hover, :focus, .active).ds-card-grid-border {
box-shadow: 0 0 0 5px var(--newtab-element-secondary-color);
transition: box-shadow 150ms;
}
.ds-card {
display: flex;

Просмотреть файл

@ -2625,6 +2625,127 @@ main.has-snippet {
text-decoration: underline;
}
.ds-onboarding,
.ds-card-grid .ds-card {
background: #FFF;
border-radius: 4px;
}
[lwt-newtab-brighttext] .ds-onboarding,
[lwt-newtab-brighttext] .ds-card-grid .ds-card {
background: none;
}
.ds-onboarding:not(.placeholder),
.ds-card-grid .ds-card:not(.placeholder) {
border-radius: 8px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
}
[lwt-newtab-brighttext] .ds-onboarding:not(.placeholder),
[lwt-newtab-brighttext] .ds-card-grid .ds-card:not(.placeholder) {
background: var(--newtab-background-color-secondary);
}
.ds-onboarding:not(.placeholder) .img-wrapper .img img,
.ds-onboarding:not(.placeholder) .img-wrapper .img .placeholder-image,
.ds-card-grid .ds-card:not(.placeholder) .img-wrapper .img img,
.ds-card-grid .ds-card:not(.placeholder) .img-wrapper .img .placeholder-image {
border-radius: 8px 8px 0 0;
}
.ds-onboarding-ref {
display: inline-block;
}
.ds-onboarding {
padding-inline-start: 16px;
padding-inline-end: 16px;
margin-bottom: 24px;
display: inline-block;
position: relative;
}
@media (min-width: 610px) {
.ds-onboarding {
padding-inline-end: 48px;
}
}
@media (min-width: 866px) {
.ds-onboarding {
padding-inline-end: 56px;
}
}
.ds-onboarding .ds-dismiss {
position: static;
}
.ds-onboarding .ds-dismiss .ds-dismiss-button {
inset-inline-end: 8px;
top: 8px;
}
.ds-onboarding header {
display: flex;
margin: 32px 0 8px;
font-size: 17px;
line-height: 23.8px;
font-weight: 600;
color: #EF4056;
}
[lwt-newtab-brighttext] .ds-onboarding header {
color: var(--newtab-background-color-primary);
}
@media (min-width: 610px) {
.ds-onboarding header {
margin: 16px 0 8px;
display: block;
height: 24px;
}
}
.ds-onboarding p {
margin: 8px 0 16px;
font-size: 13px;
line-height: 19.5px;
}
.ds-onboarding .icon-pocket {
fill: #EF4056;
margin-top: 3px;
margin-inline-end: 8px;
height: 22px;
width: 22px;
background-image: url("chrome://global/skin/icons/pocket.svg");
background-size: contain;
}
[lwt-newtab-brighttext] .ds-onboarding .icon-pocket {
fill: var(--newtab-text-primary-color);
}
@media (min-width: 610px) {
.ds-onboarding .icon-pocket {
margin-top: -5px;
margin-inline-start: -2px;
margin-inline-end: 15px;
height: 30px;
width: 30px;
}
}
.ds-onboarding .ds-onboarding-graphic {
background-image: url("chrome://activity-stream/content/data/content/assets/pocket-onboarding.avif");
border-radius: 8px;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
height: 120px;
width: 200px;
margin-inline-start: 54px;
margin-bottom: 16px;
float: inline-end;
display: none;
}
@media (min-resolution: 2x) {
.ds-onboarding .ds-onboarding-graphic {
background-image: url("chrome://activity-stream/content/data/content/assets/pocket-onboarding@2x.avif");
}
}
@media (min-width: 866px) {
.ds-onboarding .ds-onboarding-graphic {
display: block;
}
}
.ds-card-grid {
display: grid;
grid-gap: 24px;
@ -2637,24 +2758,6 @@ main.has-snippet {
display: none;
}
}
.ds-card-grid .ds-card {
background: #FFF;
border-radius: 4px;
}
[lwt-newtab-brighttext] .ds-card-grid .ds-card {
background: none;
}
.ds-card-grid.ds-card-grid-border .ds-card:not(.placeholder) {
border-radius: 8px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
}
[lwt-newtab-brighttext] .ds-card-grid.ds-card-grid-border .ds-card:not(.placeholder) {
background: var(--newtab-background-color-secondary);
}
.ds-card-grid.ds-card-grid-border .ds-card:not(.placeholder) .img-wrapper .img img,
.ds-card-grid.ds-card-grid-border .ds-card:not(.placeholder) .img-wrapper .img .placeholder-image {
border-radius: 8px 8px 0 0;
}
.ds-card-grid .ds-card-link:focus {
border: 0;
outline: 0;
@ -2851,28 +2954,28 @@ main.has-snippet {
}
}
.outer-wrapper .ds-card-grid.ds-card-grid-hide-background.ds-card-grid-border .ds-card:not(.placeholder),
.outer-wrapper.newtab-experience .ds-card-grid.ds-card-grid-hide-background.ds-card-grid-border .ds-card:not(.placeholder) {
.outer-wrapper .ds-card-grid.ds-card-grid-hide-background .ds-card:not(.placeholder),
.outer-wrapper.newtab-experience .ds-card-grid.ds-card-grid-hide-background .ds-card:not(.placeholder) {
box-shadow: none;
background: none;
}
.outer-wrapper .ds-card-grid.ds-card-grid-hide-background.ds-card-grid-border .ds-card:not(.placeholder) .ds-card-link:focus,
.outer-wrapper.newtab-experience .ds-card-grid.ds-card-grid-hide-background.ds-card-grid-border .ds-card:not(.placeholder) .ds-card-link:focus {
.outer-wrapper .ds-card-grid.ds-card-grid-hide-background .ds-card:not(.placeholder) .ds-card-link:focus,
.outer-wrapper.newtab-experience .ds-card-grid.ds-card-grid-hide-background .ds-card:not(.placeholder) .ds-card-link:focus {
box-shadow: none;
}
.outer-wrapper .ds-card-grid.ds-card-grid-hide-background.ds-card-grid-border .ds-card:not(.placeholder) .ds-card-link:focus .img-wrapper .img img,
.outer-wrapper.newtab-experience .ds-card-grid.ds-card-grid-hide-background.ds-card-grid-border .ds-card:not(.placeholder) .ds-card-link:focus .img-wrapper .img img {
.outer-wrapper .ds-card-grid.ds-card-grid-hide-background .ds-card:not(.placeholder) .ds-card-link:focus .img-wrapper .img img,
.outer-wrapper.newtab-experience .ds-card-grid.ds-card-grid-hide-background .ds-card:not(.placeholder) .ds-card-link:focus .img-wrapper .img img {
border: 0;
outline: 0;
box-shadow: 0 0 0 3px var(--newtab-primary-action-background-dimmed), 0 0 0 1px var(--newtab-primary-action-background);
}
.outer-wrapper .ds-card-grid.ds-card-grid-hide-background.ds-card-grid-border .ds-card:not(.placeholder) .img-wrapper .img img,
.outer-wrapper.newtab-experience .ds-card-grid.ds-card-grid-hide-background.ds-card-grid-border .ds-card:not(.placeholder) .img-wrapper .img img {
.outer-wrapper .ds-card-grid.ds-card-grid-hide-background .ds-card:not(.placeholder) .img-wrapper .img img,
.outer-wrapper.newtab-experience .ds-card-grid.ds-card-grid-hide-background .ds-card:not(.placeholder) .img-wrapper .img img {
border-radius: 8px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
}
.outer-wrapper .ds-card-grid.ds-card-grid-hide-background.ds-card-grid-border .ds-card:not(.placeholder) .meta,
.outer-wrapper.newtab-experience .ds-card-grid.ds-card-grid-hide-background.ds-card-grid-border .ds-card:not(.placeholder) .meta {
.outer-wrapper .ds-card-grid.ds-card-grid-hide-background .ds-card:not(.placeholder) .meta,
.outer-wrapper.newtab-experience .ds-card-grid.ds-card-grid-hide-background .ds-card:not(.placeholder) .meta {
padding: 12px 0 0;
}
@ -3271,11 +3374,6 @@ main.has-snippet {
transform: scale(1);
transition-delay: 333ms;
}
.ds-card:is(:hover, :focus, .active).ds-card-grid-border,
.ds-signup:is(:hover, :focus, .active).ds-card-grid-border {
box-shadow: 0 0 0 5px var(--newtab-element-secondary-color);
transition: box-shadow 150ms;
}
.ds-card {
display: flex;

Просмотреть файл

@ -152,6 +152,7 @@ for (const type of [
"FAKE_FOCUS_SEARCH",
"FILL_SEARCH_TERM",
"HANDOFF_SEARCH_TO_AWESOMEBAR",
"HIDE_PERSONALIZE",
"HIDE_PRIVACY_INFO",
"INIT",
"NEW_TAB_INIT",
@ -200,6 +201,7 @@ for (const type of [
"SET_PREF",
"SHOW_DOWNLOAD_FILE",
"SHOW_FIREFOX_ACCOUNTS",
"SHOW_PERSONALIZE",
"SHOW_PRIVACY_INFO",
"SHOW_SEARCH",
"SKIPPED_SIGNIN",
@ -8216,6 +8218,59 @@ class DSEmptyState extends (external_React_default()).PureComponent {
}, this.renderState()));
}
}
;// CONCATENATED MODULE: ./content-src/components/DiscoveryStreamComponents/DSDismiss/DSDismiss.jsx
/* 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/. */
class DSDismiss extends (external_React_default()).PureComponent {
constructor(props) {
super(props);
this.onDismissClick = this.onDismissClick.bind(this);
this.onHover = this.onHover.bind(this);
this.offHover = this.offHover.bind(this);
this.state = {
hovering: false
};
}
onDismissClick() {
if (this.props.onDismissClick) {
this.props.onDismissClick();
}
}
onHover() {
this.setState({
hovering: true
});
}
offHover() {
this.setState({
hovering: false
});
}
render() {
let className = `ds-dismiss
${this.state.hovering ? ` hovering` : ``}
${this.props.extraClasses ? ` ${this.props.extraClasses}` : ``}`;
return /*#__PURE__*/external_React_default().createElement("div", {
className: className
}, this.props.children, /*#__PURE__*/external_React_default().createElement("button", {
className: "ds-dismiss-button",
"data-l10n-id": "newtab-dismiss-button-tooltip",
onHover: this.onHover,
onClick: this.onDismissClick,
onMouseEnter: this.onHover,
onMouseLeave: this.offHover
}, /*#__PURE__*/external_React_default().createElement("span", {
className: "icon icon-dismiss"
})));
}
}
;// CONCATENATED MODULE: ./content-src/components/DiscoveryStreamComponents/TopicsWidget/TopicsWidget.jsx
/* This Source Code Form is subject to the terms of the Mozilla Public
@ -8352,6 +8407,9 @@ const TopicsWidget = (0,external_ReactRedux_namespaceObject.connect)(state => ({
const PREF_ONBOARDING_EXPERIENCE_DISMISSED = "discoverystream.onboardingExperience.dismissed";
const CardGrid_INTERSECTION_RATIO = 0.5;
const WIDGET_IDS = {
TOPICS: 1
};
@ -8364,6 +8422,88 @@ function DSSubHeader({
className: "section-title-container"
}, children));
}
function OnboardingExperience({
children,
dispatch,
windowObj = __webpack_require__.g
}) {
const [dismissed, setDismissed] = (0,external_React_namespaceObject.useState)(false);
const [maxHeight, setMaxHeight] = (0,external_React_namespaceObject.useState)(null);
const heightElement = (0,external_React_namespaceObject.useRef)(null);
const onDismissClick = (0,external_React_namespaceObject.useCallback)(() => {
// We update this as state and redux.
// The state update is for this newtab,
// and the redux update is for other tabs, offscreen tabs, and future tabs.
// We need the state update for this tab to support the transition.
setDismissed(true);
dispatch(actionCreators.SetPref(PREF_ONBOARDING_EXPERIENCE_DISMISSED, true));
dispatch(actionCreators.DiscoveryStreamUserEvent({
event: "BLOCK",
source: "POCKET_ONBOARDING"
}));
}, [dispatch]);
(0,external_React_namespaceObject.useEffect)(() => {
const resizeObserver = new windowObj.ResizeObserver(() => {
if (heightElement.current) {
setMaxHeight(heightElement.current.offsetHeight);
}
});
const options = {
threshold: CardGrid_INTERSECTION_RATIO
};
const intersectionObserver = new windowObj.IntersectionObserver(entries => {
if (entries.some(entry => entry.isIntersecting && entry.intersectionRatio >= CardGrid_INTERSECTION_RATIO)) {
dispatch(actionCreators.DiscoveryStreamUserEvent({
event: "IMPRESSION",
source: "POCKET_ONBOARDING"
})); // Once we have observed an impression, we can stop for this instance of newtab.
intersectionObserver.unobserve(heightElement.current);
}
}, options);
if (heightElement.current) {
resizeObserver.observe(heightElement.current);
intersectionObserver.observe(heightElement.current);
setMaxHeight(heightElement.current.offsetHeight);
} // Return unmount callback to clean up observers.
return () => {
resizeObserver === null || resizeObserver === void 0 ? void 0 : resizeObserver.disconnect();
intersectionObserver === null || intersectionObserver === void 0 ? void 0 : intersectionObserver.disconnect();
};
}, [dispatch, windowObj]);
const style = {};
if (dismissed) {
style.maxHeight = "0";
style.opacity = "0";
style.transition = "max-height 0.26s ease, opacity 0.26s ease";
} else if (maxHeight) {
style.maxHeight = `${maxHeight}px`;
}
return /*#__PURE__*/external_React_default().createElement("div", {
style: style
}, /*#__PURE__*/external_React_default().createElement("div", {
className: "ds-onboarding-ref",
ref: heightElement
}, /*#__PURE__*/external_React_default().createElement("div", {
className: "ds-onboarding"
}, /*#__PURE__*/external_React_default().createElement(DSDismiss, {
onDismissClick: onDismissClick,
extraClasses: `ds-onboarding-dismiss`
}, /*#__PURE__*/external_React_default().createElement("div", {
className: "ds-onboarding-graphic"
}), /*#__PURE__*/external_React_default().createElement("header", null, /*#__PURE__*/external_React_default().createElement("span", {
className: "icon icon-pocket"
}), /*#__PURE__*/external_React_default().createElement("span", {
"data-l10n-id": "newtab-pocket-onboarding-discover"
})), /*#__PURE__*/external_React_default().createElement("p", {
"data-l10n-id": "newtab-pocket-onboarding-cta"
})))));
}
function CardGrid_IntersectionObserver({
children,
windowObj = window,
@ -8537,6 +8677,7 @@ class _CardGrid extends (external_React_default()).PureComponent {
compactGrid,
essentialReadsHeader,
editorsPicksHeader,
onboardingExperience,
widgets,
recentSavesEnabled,
hideDescriptions,
@ -8546,6 +8687,7 @@ class _CardGrid extends (external_React_default()).PureComponent {
saveToPocketCard
} = DiscoveryStream;
const showRecentSaves = prefs.showRecentSaves && recentSavesEnabled;
const isOnboardingExperienceDismissed = prefs[PREF_ONBOARDING_EXPERIENCE_DISMISSED];
const recs = this.props.data.recommendations.slice(0, items);
const cards = [];
let essentialReadsCards = [];
@ -8638,8 +8780,10 @@ class _CardGrid extends (external_React_default()).PureComponent {
const hideDescriptionsClassName = !hideDescriptions ? `ds-card-grid-include-descriptions` : ``;
const compactGridClassName = compactGrid ? `ds-card-grid-compact` : ``;
const hybridLayoutClassName = hybridLayout ? `ds-card-grid-hybrid-layout` : ``;
const gridClassName = `ds-card-grid ds-card-grid-border ${hybridLayoutClassName} ${hideCardBackgroundClass} ${fourCardLayoutClass} ${hideDescriptionsClassName} ${compactGridClassName}`;
return /*#__PURE__*/external_React_default().createElement((external_React_default()).Fragment, null, ((_essentialReadsCards = essentialReadsCards) === null || _essentialReadsCards === void 0 ? void 0 : _essentialReadsCards.length) > 0 && /*#__PURE__*/external_React_default().createElement("div", {
const gridClassName = `ds-card-grid ${hybridLayoutClassName} ${hideCardBackgroundClass} ${fourCardLayoutClass} ${hideDescriptionsClassName} ${compactGridClassName}`;
return /*#__PURE__*/external_React_default().createElement((external_React_default()).Fragment, null, !isOnboardingExperienceDismissed && onboardingExperience && /*#__PURE__*/external_React_default().createElement(OnboardingExperience, {
dispatch: this.props.dispatch
}), ((_essentialReadsCards = essentialReadsCards) === null || _essentialReadsCards === void 0 ? void 0 : _essentialReadsCards.length) > 0 && /*#__PURE__*/external_React_default().createElement("div", {
className: gridClassName
}, essentialReadsCards), showRecentSaves && /*#__PURE__*/external_React_default().createElement(RecentSavesContainer, {
gridClassName: gridClassName,
@ -8696,59 +8840,6 @@ const CardGrid = (0,external_ReactRedux_namespaceObject.connect)(state => ({
Prefs: state.Prefs,
DiscoveryStream: state.DiscoveryStream
}))(_CardGrid);
;// CONCATENATED MODULE: ./content-src/components/DiscoveryStreamComponents/DSDismiss/DSDismiss.jsx
/* 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/. */
class DSDismiss extends (external_React_default()).PureComponent {
constructor(props) {
super(props);
this.onDismissClick = this.onDismissClick.bind(this);
this.onHover = this.onHover.bind(this);
this.offHover = this.offHover.bind(this);
this.state = {
hovering: false
};
}
onDismissClick() {
if (this.props.onDismissClick) {
this.props.onDismissClick();
}
}
onHover() {
this.setState({
hovering: true
});
}
offHover() {
this.setState({
hovering: false
});
}
render() {
let className = `ds-dismiss
${this.state.hovering ? ` hovering` : ``}
${this.props.extraClasses ? ` ${this.props.extraClasses}` : ``}`;
return /*#__PURE__*/external_React_default().createElement("div", {
className: className
}, this.props.children, /*#__PURE__*/external_React_default().createElement("button", {
className: "ds-dismiss-button",
"data-l10n-id": "newtab-dismiss-button-tooltip",
onHover: this.onHover,
onClick: this.onDismissClick,
onMouseEnter: this.onHover,
onMouseLeave: this.offHover
}, /*#__PURE__*/external_React_default().createElement("span", {
className: "icon icon-dismiss"
})));
}
}
;// CONCATENATED MODULE: ./content-src/components/DiscoveryStreamComponents/CollectionCardGrid/CollectionCardGrid.jsx
/* 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,
@ -10561,6 +10652,7 @@ const INITIAL_STATE = {
initialized: false,
locale: "",
isForStartupCache: false,
customizeMenuVisible: false,
},
ASRouter: { initialized: false },
Snippets: { initialized: false },
@ -10652,6 +10744,14 @@ function App(prevState = INITIAL_STATE.App, action) {
return Object.assign({}, prevState, action.data || {}, {
isForStartupCache: false,
});
case actionTypes.SHOW_PERSONALIZE:
return Object.assign({}, prevState, {
customizeMenuVisible: true,
});
case actionTypes.HIDE_PERSONALIZE:
return Object.assign({}, prevState, {
customizeMenuVisible: false,
});
default:
return prevState;
}
@ -13890,6 +13990,7 @@ class _DiscoveryStreamBase extends (external_React_default()).PureComponent {
fourCardLayout: component.properties.fourCardLayout,
compactGrid: component.properties.compactGrid,
essentialReadsHeader: component.properties.essentialReadsHeader,
onboardingExperience: component.properties.onboardingExperience,
editorsPicksHeader: component.properties.editorsPicksHeader,
recentSavesEnabled: this.props.DiscoveryStream.recentSavesEnabled,
hideDescriptions: this.props.DiscoveryStream.hideDescriptions
@ -14733,8 +14834,7 @@ class BaseContent extends (external_React_default()).PureComponent {
this.onWindowScroll = debounce(this.onWindowScroll.bind(this), 5);
this.setPref = this.setPref.bind(this);
this.state = {
fixedSearch: false,
customizeMenuVisible: false
fixedSearch: false
};
}
@ -14773,8 +14873,8 @@ class BaseContent extends (external_React_default()).PureComponent {
}
openCustomizationMenu() {
this.setState({
customizeMenuVisible: true
this.props.dispatch({
type: actionTypes.SHOW_PERSONALIZE
});
this.props.dispatch(actionCreators.UserEvent({
event: "SHOW_PERSONALIZE"
@ -14782,9 +14882,9 @@ class BaseContent extends (external_React_default()).PureComponent {
}
closeCustomizationMenu() {
if (this.state.customizeMenuVisible) {
this.setState({
customizeMenuVisible: false
if (this.props.App.customizeMenuVisible) {
this.props.dispatch({
type: actionTypes.HIDE_PERSONALIZE
});
this.props.dispatch(actionCreators.UserEvent({
event: "HIDE_PERSONALIZE"
@ -14810,7 +14910,8 @@ class BaseContent extends (external_React_default()).PureComponent {
App
} = props;
const {
initialized
initialized,
customizeMenuVisible
} = App;
const prefs = props.Prefs.values;
const isDiscoveryStream = props.DiscoveryStream.config && props.DiscoveryStream.config.enabled;
@ -14818,7 +14919,6 @@ class BaseContent extends (external_React_default()).PureComponent {
const pocketEnabled = prefs["feeds.section.topstories"] && prefs["feeds.system.topstories"];
const noSectionsEnabled = !prefs["feeds.topsites"] && !pocketEnabled && filteredSections.filter(section => section.enabled).length === 0;
const searchHandoffEnabled = prefs["improvesearch.handoffToAwesomebar"];
const showCustomizationMenu = this.state.customizeMenuVisible;
const enabledSections = {
topSitesEnabled: prefs["feeds.topsites"],
pocketEnabled: prefs["feeds.section.topstories"],
@ -14842,7 +14942,7 @@ class BaseContent extends (external_React_default()).PureComponent {
enabledSections: enabledSections,
pocketRegion: pocketRegion,
mayHaveSponsoredTopSites: mayHaveSponsoredTopSites,
showing: showCustomizationMenu
showing: customizeMenuVisible
}), /*#__PURE__*/external_React_default().createElement("div", {
className: outerClassName,
onClick: this.closeCustomizationMenu

Двоичный файл не отображается.

Двоичный файл не отображается.

Просмотреть файл

@ -73,6 +73,7 @@ A user event ping includes some basic metadata (tab id, addon version, etc.) as
"SEARCH",
"BLOCK",
"DELETE",
"IMPRESSION",
"OPEN_NEW_WINDOW",
"OPEN_PRIVATE_WINDOW",
"BOOKMARK_DELETE",

Просмотреть файл

@ -396,6 +396,15 @@ const PREFS_CONFIG = new Map([
value: false,
},
],
[
"discoverystream.onboardingExperience.dismissed",
{
title: "Allows the user to dismiss the new Pocket onboarding experience",
skipBroadcast: true,
alsoToPreloaded: true,
value: false,
},
],
[
"discoverystream.region-basic-layout",
{

Просмотреть файл

@ -661,6 +661,8 @@ class DiscoveryStreamFeed {
const pocketConfig =
this.store.getState().Prefs.values?.pocketConfig || {};
const onboardingExperience =
this.isBff && pocketConfig.onboardingExperience;
let items = isBasicLayout ? 3 : 21;
if (pocketConfig.fourCardLayout || pocketConfig.hybridLayout) {
@ -742,6 +744,7 @@ class DiscoveryStreamFeed {
this.locale.startsWith("en-") && pocketConfig.essentialReadsHeader,
editorsPicksHeader:
this.locale.startsWith("en-") && pocketConfig.editorsPicksHeader,
onboardingExperience,
});
}
@ -2208,6 +2211,7 @@ class DiscoveryStreamFeed {
`compactGrid` Reduce the number of pixels between the Pocket cards.
`essentialReadsHeader` Updates the Pocket section header and title to say "Todays Essential Reads", moves the "Recommended by Pocket" header to the right side.
`editorsPicksHeader` Updates the Pocket section header and title to say "Editors Picks", if used with essentialReadsHeader, creates a second section 2 rows down for editorsPicks.
`onboardingExperience` Show new users some UI explaining Pocket above the Pocket section.
*/
getHardcodedLayout = ({
spocsUrl = SPOCS_URL,
@ -2227,6 +2231,7 @@ getHardcodedLayout = ({
compactGrid = false,
essentialReadsHeader = false,
editorsPicksHeader = false,
onboardingExperience = false,
}) => ({
lastUpdate: Date.now(),
spocs: {
@ -2324,6 +2329,7 @@ getHardcodedLayout = ({
compactGrid,
essentialReadsHeader,
editorsPicksHeader,
onboardingExperience,
},
widgets: {
positions: widgetPositions.map(position => {

Просмотреть файл

@ -32,8 +32,16 @@ class PrefsFeed {
onPrefChanged(name, value) {
const prefItem = this._prefMap.get(name);
if (prefItem) {
let action = "BroadcastToContent";
if (prefItem.skipBroadcast) {
action = "OnlyToMain";
if (prefItem.alsoToPreloaded) {
action = "AlsoToPreloaded";
}
}
this.store.dispatch(
ac[prefItem.skipBroadcast ? "OnlyToMain" : "BroadcastToContent"]({
ac[action]({
type: at.PREF_CHANGED,
data: { name, value },
})

Просмотреть файл

@ -99,11 +99,17 @@ describe("<BaseContent>", () => {
it("should dispatch a user event when the customize menu is opened or closed", () => {
const dispatch = sinon.stub();
const wrapper = shallow(
<BaseContent {...DEFAULT_PROPS} dispatch={dispatch} />
<BaseContent
{...DEFAULT_PROPS}
dispatch={dispatch}
App={{ customizeMenuVisible: true }}
/>
);
wrapper.instance().openCustomizationMenu();
assert.calledWith(dispatch, { type: "SHOW_PERSONALIZE" });
assert.calledWith(dispatch, ac.UserEvent({ event: "SHOW_PERSONALIZE" }));
wrapper.instance().closeCustomizationMenu();
assert.calledWith(dispatch, { type: "HIDE_PERSONALIZE" });
assert.calledWith(dispatch, ac.UserEvent({ event: "HIDE_PERSONALIZE" }));
});

Просмотреть файл

@ -2,6 +2,7 @@ import {
_CardGrid as CardGrid,
IntersectionObserver,
RecentSavesContainer,
OnboardingExperience,
DSSubHeader,
} from "content-src/components/DiscoveryStreamComponents/CardGrid/CardGrid";
import { combineReducers, createStore } from "redux";
@ -290,3 +291,78 @@ describe("<RecentSavesContainer>", () => {
);
});
});
describe("<OnboardingExperience>", () => {
let wrapper;
let fakeWindow;
let intersectEntries;
let dispatch;
let resizeCallback;
let fakeResizeObserver = class {
constructor(callback) {
resizeCallback = callback;
}
observe() {}
unobserve() {}
disconnect() {}
};
beforeEach(() => {
dispatch = sinon.stub();
intersectEntries = [{ isIntersecting: true, intersectionRatio: 1 }];
fakeWindow = {
ResizeObserver: fakeResizeObserver,
IntersectionObserver: buildIntersectionObserver(intersectEntries),
};
wrapper = mount(
<WrapWithProvider state={{}}>
<OnboardingExperience windowObj={fakeWindow} dispatch={dispatch} />
</WrapWithProvider>
).find(OnboardingExperience);
});
it("should render a ds-onboarding", () => {
assert.ok(wrapper.exists());
assert.lengthOf(wrapper.find(".ds-onboarding"), 1);
});
it("should dismiss on dismiss click", () => {
wrapper.find(".ds-dismiss-button").simulate("click");
assert.calledWith(
dispatch,
ac.DiscoveryStreamUserEvent({
event: "BLOCK",
source: "POCKET_ONBOARDING",
})
);
assert.calledWith(
dispatch,
ac.SetPref("discoverystream.onboardingExperience.dismissed", true)
);
assert.equal(wrapper.getDOMNode().style["max-height"], "0px");
assert.equal(wrapper.getDOMNode().style.opacity, "0");
});
it("should update max-height on resize", () => {
sinon
.stub(wrapper.find(".ds-onboarding-ref").getDOMNode(), "offsetHeight")
.get(() => 123);
resizeCallback();
assert.equal(wrapper.getDOMNode().style["max-height"], "123px");
});
it("should fire intersection events", () => {
assert.calledWith(
dispatch,
ac.DiscoveryStreamUserEvent({
event: "IMPRESSION",
source: "POCKET_ONBOARDING",
})
);
});
});

Просмотреть файл

@ -18,6 +18,7 @@ describe("PrefsFeed", () => {
["foo", 1],
["bar", 2],
["baz", { value: 1, skipBroadcast: true }],
["qux", { value: 1, skipBroadcast: true, alsoToPreloaded: true }],
]);
feed = new PrefsFeed(FAKE_PREFS);
const storage = {
@ -262,6 +263,23 @@ describe("PrefsFeed", () => {
})
);
});
it("should send AlsoToPreloaded pref update if config for pref has skipBroadcast: true and alsoToPreloaded: true", async () => {
feed.onPrefChanged("qux", {
value: 2,
skipBroadcast: true,
alsoToPreloaded: true,
});
assert.calledWith(
feed.store.dispatch,
ac.AlsoToPreloaded({
type: at.PREF_CHANGED,
data: {
name: "qux",
value: { value: 2, skipBroadcast: true, alsoToPreloaded: true },
},
})
);
});
describe("#observe", () => {
it("should call dispatch from observe", () => {
feed.observe(undefined, global.Region.REGION_TOPIC);

Просмотреть файл

@ -240,6 +240,11 @@ newtab-pocket-pocket-firefox-family = { -pocket-brand-name } is part of the { -b
newtab-pocket-save = Save
newtab-pocket-saved = Saved
## Pocket content onboarding experience dialog and modal for new users seeing the Pocket section for the first time, shown as the first item in the Pocket section.
newtab-pocket-onboarding-discover = Discover the best of the web
newtab-pocket-onboarding-cta = { -pocket-brand-name } explores a diverse range of publications to bring the most informative, inspirational, and trustworthy content right to your { -brand-product-name } browser.
## Error Fallback Content.
## This message and suggested action link are shown in each section of UI that fails to render.

Просмотреть файл

@ -479,6 +479,12 @@ pocketNewtab:
browser.newtabpage.activity-stream.discoverystream.descLines
description: >-
Changes the maximum number of lines a description can be for Pocket cards on newtab.
onboardingExperience:
type: boolean
fallbackPref: >-
browser.newtabpage.activity-stream.discoverystream.onboardingExperience.enabled
description: >-
Enables an onboarding experience for Pocket section on newtab.
essentialReadsHeader:
type: boolean
fallbackPref: >-