Bug 1729946 - Adding Pocket newtab load more button. r=fluent-reviewers,gvn,flod

Differential Revision: https://phabricator.services.mozilla.com/D125631
This commit is contained in:
Scott 2021-09-23 02:48:38 +00:00
Родитель 800e6ffa5d
Коммит 21db2db736
21 изменённых файлов: 420 добавлений и 25 удалений

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

@ -1474,6 +1474,8 @@ pref("browser.newtabpage.activity-stream.asrouter.useRemoteL10n", true);
pref("browser.newtabpage.activity-stream.discoverystream.enabled", true);
pref("browser.newtabpage.activity-stream.discoverystream.hardcoded-basic-layout", false);
pref("browser.newtabpage.activity-stream.discoverystream.compactLayout.enabled", false);
pref("browser.newtabpage.activity-stream.discoverystream.loadMore.enabled", false);
pref("browser.newtabpage.activity-stream.discoverystream.lastCardMessage.enabled", false);
pref("browser.newtabpage.activity-stream.discoverystream.spoc-positions", "2,4,11,20");
pref("browser.newtabpage.activity-stream.discoverystream.spocs-endpoint", "");
pref("browser.newtabpage.activity-stream.discoverystream.spocs-endpoint-query", "");

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

@ -55,7 +55,7 @@ function templateHTML(options) {
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; object-src 'none'; script-src resource: chrome:; connect-src https:; img-src https: data: blob:; style-src 'unsafe-inline';">
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; object-src 'none'; script-src resource: chrome:; connect-src https:; img-src https: data: blob: chrome:; style-src 'unsafe-inline';">
<title data-l10n-id="newtab-page-title"></title>
<link rel="icon" type="image/png" href="chrome://branding/content/icon32.png"/>
<link rel="localization" href="branding/brand.ftl" />

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

@ -209,6 +209,8 @@ export class _DiscoveryStreamBase extends React.PureComponent {
items={component.properties.items}
compact={component.properties.compact}
include_descriptions={!component.properties.compact}
loadMoreEnabled={component.loadMoreEnabled}
lastCardMessageEnabled={component.lastCardMessageEnabled}
cta_variant={component.cta_variant}
display_engagement_labels={ENGAGEMENT_LABEL_ENABLED}
/>

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

@ -2,17 +2,48 @@
* 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 { DSCard, PlaceholderDSCard } from "../DSCard/DSCard.jsx";
import {
DSCard,
PlaceholderDSCard,
LastCardMessage,
} from "../DSCard/DSCard.jsx";
import { DSEmptyState } from "../DSEmptyState/DSEmptyState.jsx";
import { FluentOrText } from "../../FluentOrText/FluentOrText.jsx";
import { actionCreators as ac } from "common/Actions.jsm";
import React from "react";
export class CardGrid extends React.PureComponent {
constructor(props) {
super(props);
this.state = { moreLoaded: false };
this.loadMoreClicked = this.loadMoreClicked.bind(this);
}
loadMoreClicked() {
this.props.dispatch(
ac.UserEvent({
event: "CLICK",
source: "DS_LOAD_MORE_BUTTON",
})
);
this.setState({ moreLoaded: true });
}
renderCards() {
const recs = this.props.data.recommendations.slice(0, this.props.items);
let { items } = this.props;
const { loadMoreEnabled } = this.props;
const { lastCardMessageEnabled } = this.props;
let showLastCardMessage = lastCardMessageEnabled;
if (loadMoreEnabled && !this.state.moreLoaded) {
items = 12;
// We don't want to show this until after load more has been clicked.
showLastCardMessage = false;
}
const recs = this.props.data.recommendations.slice(0, items);
const cards = [];
for (let index = 0; index < this.props.items; index++) {
for (let index = 0; index < items; index++) {
const rec = recs[index];
cards.push(
!rec || rec.placeholder ? (
@ -53,6 +84,15 @@ export class CardGrid extends React.PureComponent {
);
}
// Replace last card with "you are all caught up card"
if (showLastCardMessage) {
cards.splice(
cards.length - 1,
1,
<LastCardMessage key={`dscard-last-${cards.length - 1}`} />
);
}
// Used for CSS overrides to default styling (eg: "hero")
const variantClass = this.props.display_variant
? `ds-card-grid-${this.props.display_variant}`
@ -85,6 +125,7 @@ export class CardGrid extends React.PureComponent {
// Handle the case where a user has dismissed all recommendations
const isEmpty = data.recommendations.length === 0;
const { loadMoreEnabled } = this.props;
return (
<div>
@ -109,6 +150,13 @@ export class CardGrid extends React.PureComponent {
) : (
this.renderCards()
)}
{loadMoreEnabled && !this.state.moreLoaded && (
<button
className="ASRouterButton primary ds-card-grid-load-more-button"
onClick={this.loadMoreClicked}
data-l10n-id="newtab-pocket-load-more-stories-button"
/>
)}
</div>
);
}
@ -118,4 +166,5 @@ CardGrid.defaultProps = {
border: `border`,
items: 4, // Number of stories to display
enable_video_playheads: false,
lastCardMessageEnabled: false,
};

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

@ -174,6 +174,7 @@ $col4-header-font-size: 14;
padding: 12px 0 0;
.source,
.ds-last-card-desc,
.story-sponsored-label {
font-size: 13px;
color: var(--newtab-text-secondary-color);
@ -211,6 +212,22 @@ $col4-header-font-size: 14;
.img-wrapper .img img {
border-radius: 8px;
box-shadow: $shadow-card;
&.last-card-message-image {
background: transparent;
box-shadow: none;
object-fit: contain;
}
}
}
}
.ds-layout {
.ds-card-grid-load-more-button {
display: block;
margin: 33px auto;
font-size: 13px;
line-height: 16px;
border-radius: 4px;
}
}

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

@ -296,6 +296,35 @@ export class _DSCard extends React.PureComponent {
<div className="ds-card placeholder" ref={this.setPlaceholderRef} />
);
}
if (this.props.lastCard) {
return (
<div className="ds-card last-card-message">
<div className="img-wrapper">
<picture className="ds-image img loaded">
<img
data-l10n-id="newtab-pocket-last-card-image"
className="last-card-message-image"
src="chrome://activity-stream/content/data/content/assets/caught-up-illustration.svg"
alt="Youre all caught up"
/>
</picture>
</div>
<div className="meta">
<div className="info-wrap">
<header
className="title clamp"
data-l10n-id="newtab-pocket-last-card-title"
/>
<p
className="ds-last-card-desc"
data-l10n-id="newtab-pocket-last-card-desc"
/>
</div>
</div>
</div>
);
}
const isButtonCTA = this.props.cta_variant === "button";
const includeDescriptions = this.props.include_descriptions;
const baseClass = `ds-card ${this.props.is_video ? `video-card` : ``}`;
@ -400,3 +429,4 @@ export const DSCard = connect(state => ({
}))(_DSCard);
export const PlaceholderDSCard = props => <DSCard placeholder={true} />;
export const LastCardMessage = props => <DSCard lastCard={true} />;

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

@ -95,6 +95,24 @@ $excerpt-line-height: 20;
margin-top: 4px;
}
&.last-card-message {
.ds-last-card-desc {
font-size: 13px;
color: var(--newtab-text-secondary-color);
}
.ds-last-card-desc,
.title {
text-align: center;
}
.last-card-message-image {
object-fit: contain;
background: transparent;
box-shadow: none;
}
}
.meta {
display: flex;
flex-direction: column;

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

@ -2714,6 +2714,7 @@ main.has-snippet {
padding: 12px 0 0;
}
.ds-card-grid.ds-card-grid-compact-variant .ds-card .meta .source,
.ds-card-grid.ds-card-grid-compact-variant .ds-card .meta .ds-last-card-desc,
.ds-card-grid.ds-card-grid-compact-variant .ds-card .meta .story-sponsored-label {
font-size: 13px;
color: var(--newtab-text-secondary-color);
@ -2748,6 +2749,20 @@ main.has-snippet {
border-radius: 8px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
}
.outer-wrapper .ds-card-grid.ds-card-grid-compact-variant.ds-card-grid-border .ds-card:not(.placeholder) .img-wrapper .img img.last-card-message-image,
.outer-wrapper.newtab-experience .ds-card-grid.ds-card-grid-compact-variant.ds-card-grid-border .ds-card:not(.placeholder) .img-wrapper .img img.last-card-message-image {
background: transparent;
box-shadow: none;
object-fit: contain;
}
.ds-layout .ds-card-grid-load-more-button {
display: block;
margin: 33px auto;
font-size: 13px;
line-height: 16px;
border-radius: 4px;
}
.ds-dismiss.ds-dismiss-ds-collection .ds-dismiss-button {
margin: 15px 0 0;
@ -3525,6 +3540,19 @@ main.has-snippet {
.ds-card.video-card .meta {
margin-top: 4px;
}
.ds-card.last-card-message .ds-last-card-desc {
font-size: 13px;
color: var(--newtab-text-secondary-color);
}
.ds-card.last-card-message .ds-last-card-desc,
.ds-card.last-card-message .title {
text-align: center;
}
.ds-card.last-card-message .last-card-message-image {
object-fit: contain;
background: transparent;
box-shadow: none;
}
.ds-card .meta {
display: flex;
flex-direction: column;

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

@ -2718,6 +2718,7 @@ main.has-snippet {
padding: 12px 0 0;
}
.ds-card-grid.ds-card-grid-compact-variant .ds-card .meta .source,
.ds-card-grid.ds-card-grid-compact-variant .ds-card .meta .ds-last-card-desc,
.ds-card-grid.ds-card-grid-compact-variant .ds-card .meta .story-sponsored-label {
font-size: 13px;
color: var(--newtab-text-secondary-color);
@ -2752,6 +2753,20 @@ main.has-snippet {
border-radius: 8px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
}
.outer-wrapper .ds-card-grid.ds-card-grid-compact-variant.ds-card-grid-border .ds-card:not(.placeholder) .img-wrapper .img img.last-card-message-image,
.outer-wrapper.newtab-experience .ds-card-grid.ds-card-grid-compact-variant.ds-card-grid-border .ds-card:not(.placeholder) .img-wrapper .img img.last-card-message-image {
background: transparent;
box-shadow: none;
object-fit: contain;
}
.ds-layout .ds-card-grid-load-more-button {
display: block;
margin: 33px auto;
font-size: 13px;
line-height: 16px;
border-radius: 4px;
}
.ds-dismiss.ds-dismiss-ds-collection .ds-dismiss-button {
margin: 15px 0 0;
@ -3529,6 +3544,19 @@ main.has-snippet {
.ds-card.video-card .meta {
margin-top: 4px;
}
.ds-card.last-card-message .ds-last-card-desc {
font-size: 13px;
color: var(--newtab-text-secondary-color);
}
.ds-card.last-card-message .ds-last-card-desc,
.ds-card.last-card-message .title {
text-align: center;
}
.ds-card.last-card-message .last-card-message-image {
object-fit: contain;
background: transparent;
box-shadow: none;
}
.ds-card .meta {
display: flex;
flex-direction: column;

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

@ -2714,6 +2714,7 @@ main.has-snippet {
padding: 12px 0 0;
}
.ds-card-grid.ds-card-grid-compact-variant .ds-card .meta .source,
.ds-card-grid.ds-card-grid-compact-variant .ds-card .meta .ds-last-card-desc,
.ds-card-grid.ds-card-grid-compact-variant .ds-card .meta .story-sponsored-label {
font-size: 13px;
color: var(--newtab-text-secondary-color);
@ -2748,6 +2749,20 @@ main.has-snippet {
border-radius: 8px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
}
.outer-wrapper .ds-card-grid.ds-card-grid-compact-variant.ds-card-grid-border .ds-card:not(.placeholder) .img-wrapper .img img.last-card-message-image,
.outer-wrapper.newtab-experience .ds-card-grid.ds-card-grid-compact-variant.ds-card-grid-border .ds-card:not(.placeholder) .img-wrapper .img img.last-card-message-image {
background: transparent;
box-shadow: none;
object-fit: contain;
}
.ds-layout .ds-card-grid-load-more-button {
display: block;
margin: 33px auto;
font-size: 13px;
line-height: 16px;
border-radius: 4px;
}
.ds-dismiss.ds-dismiss-ds-collection .ds-dismiss-button {
margin: 15px 0 0;
@ -3525,6 +3540,19 @@ main.has-snippet {
.ds-card.video-card .meta {
margin-top: 4px;
}
.ds-card.last-card-message .ds-last-card-desc {
font-size: 13px;
color: var(--newtab-text-secondary-color);
}
.ds-card.last-card-message .ds-last-card-desc,
.ds-card.last-card-message .title {
text-align: center;
}
.ds-card.last-card-message .last-card-message-image {
object-fit: contain;
background: transparent;
box-shadow: none;
}
.ds-card .meta {
display: flex;
flex-direction: column;

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

@ -22,7 +22,7 @@
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; object-src 'none'; script-src resource: chrome:; connect-src https:; img-src https: data: blob:; style-src 'unsafe-inline';">
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; object-src 'none'; script-src resource: chrome:; connect-src https:; img-src https: data: blob: chrome:; style-src 'unsafe-inline';">
<title data-l10n-id="newtab-page-title"></title>
<link rel="icon" type="image/png" href="chrome://branding/content/icon32.png"/>
<link rel="localization" href="branding/brand.ftl" />

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

@ -3289,6 +3289,8 @@ class _DiscoveryStreamBase extends react__WEBPACK_IMPORTED_MODULE_13___default.a
items: component.properties.items,
compact: component.properties.compact,
include_descriptions: !component.properties.compact,
loadMoreEnabled: component.loadMoreEnabled,
lastCardMessageEnabled: component.lastCardMessageEnabled,
cta_variant: component.cta_variant,
display_engagement_labels: ENGAGEMENT_LABEL_ENABLED
});
@ -3465,8 +3467,9 @@ __webpack_require__.r(__webpack_exports__);
/* harmony import */ var _DSCard_DSCard_jsx__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(18);
/* harmony import */ var _DSEmptyState_DSEmptyState_jsx__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(31);
/* harmony import */ var _FluentOrText_FluentOrText_jsx__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(30);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(8);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_3__);
/* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(1);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(8);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_4__);
/* 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/. */
@ -3474,16 +3477,52 @@ __webpack_require__.r(__webpack_exports__);
class CardGrid extends react__WEBPACK_IMPORTED_MODULE_3___default.a.PureComponent {
class CardGrid extends react__WEBPACK_IMPORTED_MODULE_4___default.a.PureComponent {
constructor(props) {
super(props);
this.state = {
moreLoaded: false
};
this.loadMoreClicked = this.loadMoreClicked.bind(this);
}
loadMoreClicked() {
this.props.dispatch(common_Actions_jsm__WEBPACK_IMPORTED_MODULE_3__["actionCreators"].UserEvent({
event: "CLICK",
source: "DS_LOAD_MORE_BUTTON"
}));
this.setState({
moreLoaded: true
});
}
renderCards() {
const recs = this.props.data.recommendations.slice(0, this.props.items);
let {
items
} = this.props;
const {
loadMoreEnabled
} = this.props;
const {
lastCardMessageEnabled
} = this.props;
let showLastCardMessage = lastCardMessageEnabled;
if (loadMoreEnabled && !this.state.moreLoaded) {
items = 12; // We don't want to show this until after load more has been clicked.
showLastCardMessage = false;
}
const recs = this.props.data.recommendations.slice(0, items);
const cards = [];
for (let index = 0; index < this.props.items; index++) {
for (let index = 0; index < items; index++) {
const rec = recs[index];
cards.push(!rec || rec.placeholder ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_3___default.a.createElement(_DSCard_DSCard_jsx__WEBPACK_IMPORTED_MODULE_0__["PlaceholderDSCard"], {
cards.push(!rec || rec.placeholder ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement(_DSCard_DSCard_jsx__WEBPACK_IMPORTED_MODULE_0__["PlaceholderDSCard"], {
key: `dscard-${index}`
}) : /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_3___default.a.createElement(_DSCard_DSCard_jsx__WEBPACK_IMPORTED_MODULE_0__["DSCard"], {
}) : /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement(_DSCard_DSCard_jsx__WEBPACK_IMPORTED_MODULE_0__["DSCard"], {
key: `dscard-${rec.id}`,
pos: rec.pos,
flightId: rec.flight_id,
@ -3514,13 +3553,20 @@ class CardGrid extends react__WEBPACK_IMPORTED_MODULE_3___default.a.PureComponen
is_video: this.props.enable_video_playheads && rec.is_video,
is_collection: this.props.is_collection
}));
} // Replace last card with "you are all caught up card"
if (showLastCardMessage) {
cards.splice(cards.length - 1, 1, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement(_DSCard_DSCard_jsx__WEBPACK_IMPORTED_MODULE_0__["LastCardMessage"], {
key: `dscard-last-${cards.length - 1}`
}));
} // Used for CSS overrides to default styling (eg: "hero")
const variantClass = this.props.display_variant ? `ds-card-grid-${this.props.display_variant}` : ``;
const compactClass = this.props.compact ? `ds-card-grid-compact-variant` : ``;
const includeDescriptions = this.props.include_descriptions ? `ds-card-grid-include-descriptions` : ``;
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_3___default.a.createElement("div", {
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement("div", {
className: `ds-card-grid ds-card-grid-${this.props.border} ${variantClass} ${compactClass} ${includeDescriptions}`
}, cards);
}
@ -3536,21 +3582,28 @@ class CardGrid extends react__WEBPACK_IMPORTED_MODULE_3___default.a.PureComponen
const isEmpty = data.recommendations.length === 0;
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_3___default.a.createElement("div", null, this.props.title && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_3___default.a.createElement("div", {
const {
loadMoreEnabled
} = this.props;
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement("div", null, this.props.title && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement("div", {
className: "ds-header"
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_3___default.a.createElement("div", {
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement("div", {
className: "title"
}, this.props.title), this.props.context && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_3___default.a.createElement(_FluentOrText_FluentOrText_jsx__WEBPACK_IMPORTED_MODULE_2__["FluentOrText"], {
}, this.props.title), this.props.context && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement(_FluentOrText_FluentOrText_jsx__WEBPACK_IMPORTED_MODULE_2__["FluentOrText"], {
message: this.props.context
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_3___default.a.createElement("div", {
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement("div", {
className: "ds-context"
}))), isEmpty ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_3___default.a.createElement("div", {
}))), isEmpty ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement("div", {
className: "ds-card-grid empty"
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_3___default.a.createElement(_DSEmptyState_DSEmptyState_jsx__WEBPACK_IMPORTED_MODULE_1__["DSEmptyState"], {
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement(_DSEmptyState_DSEmptyState_jsx__WEBPACK_IMPORTED_MODULE_1__["DSEmptyState"], {
status: data.status,
dispatch: this.props.dispatch,
feed: this.props.feed
})) : this.renderCards());
})) : this.renderCards(), loadMoreEnabled && !this.state.moreLoaded && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement("button", {
className: "ASRouterButton primary ds-card-grid-load-more-button",
onClick: this.loadMoreClicked,
"data-l10n-id": "newtab-pocket-load-more-stories-button"
}));
}
}
@ -3558,7 +3611,8 @@ CardGrid.defaultProps = {
border: `border`,
items: 4,
// Number of stories to display
enable_video_playheads: false
enable_video_playheads: false,
lastCardMessageEnabled: false
};
/***/ }),
@ -3574,6 +3628,7 @@ __webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "_DSCard", function() { return _DSCard; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "DSCard", function() { return DSCard; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "PlaceholderDSCard", function() { return PlaceholderDSCard; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "LastCardMessage", function() { return LastCardMessage; });
/* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
/* harmony import */ var _DSImage_DSImage_jsx__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(19);
/* harmony import */ var _DSLinkMenu_DSLinkMenu__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(20);
@ -3852,6 +3907,31 @@ class _DSCard extends react__WEBPACK_IMPORTED_MODULE_4___default.a.PureComponent
});
}
if (this.props.lastCard) {
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement("div", {
className: "ds-card last-card-message"
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement("div", {
className: "img-wrapper"
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement("picture", {
className: "ds-image img loaded"
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement("img", {
"data-l10n-id": "newtab-pocket-last-card-image",
className: "last-card-message-image",
src: "chrome://activity-stream/content/data/content/assets/caught-up-illustration.svg",
alt: "You\u2019re all caught up"
}))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement("div", {
className: "meta"
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement("div", {
className: "info-wrap"
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement("header", {
className: "title clamp",
"data-l10n-id": "newtab-pocket-last-card-title"
}), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement("p", {
className: "ds-last-card-desc",
"data-l10n-id": "newtab-pocket-last-card-desc"
}))));
}
const isButtonCTA = this.props.cta_variant === "button";
const includeDescriptions = this.props.include_descriptions;
const baseClass = `ds-card ${this.props.is_video ? `video-card` : ``}`;
@ -3938,6 +4018,9 @@ const DSCard = Object(react_redux__WEBPACK_IMPORTED_MODULE_8__["connect"])(state
const PlaceholderDSCard = props => /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement(DSCard, {
placeholder: true
});
const LastCardMessage = props => /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement(DSCard, {
lastCard: true
});
/***/ }),
/* 19 */

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

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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/. -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 124.3 100.9" xml:space="preserve">
<path class="st0" d="M119 90.7C119 94 82.8 99 55.6 99S13.1 89 13.1 85.6s18.8-5.6 46-5.6 60 7.2 60 10.7z" fill="#FCB643"></path>
<path class="st1" d="m73 14.5-51 11c-.2 0-.3.2-.2.4l13.8 67c0 .2.2.3.3.3l50.3-9.9c.1 0 .3-.2.2-.4l-13-68.1c0-.2-.2-.3-.4-.3z" fill="#fff"></path>
<path d="M74.3 14.6a1.3 1.3 0 0 0-1.5-1l-51 11c-.7 0-1.2.8-1 1.5l13.8 67c.1.7.7 1.1 1.3 1.1h.3l50.2-10c.7 0 1.1-.7 1-1.5l-13-68.1zm11.9 68.7L36 93.2h-.1c-.1 0-.3-.1-.3-.3L21.8 26c0-.2 0-.4.2-.4l51-11c.2 0 .3 0 .4.3l13 68.1c0 .2 0 .4-.2.4z"></path>
<path class="st1" d="m93.3 19.2-52-2.6c-.2 0-.4.1-.4.3l-4 68.3c0 .2 0 .4.3.4l51 3.5c.2 0 .3-.2.4-.3l5-69.3v-.2l-.3-.1z" fill="#fff"></path>
<path d="m93.4 18.2-52.1-2.6c-.8 0-1.3.5-1.4 1.2l-4 68.4c0 .7.5 1.3 1.2 1.4L88 90h.1c.7 0 1.3-.5 1.4-1.2l5-69.3c0-.7-.5-1.4-1.2-1.4zm-5.2 70.9-51-3.5a.3.3 0 0 1-.3-.4l4-68.3c0-.2.2-.3.3-.3l52.1 2.6.3.1v.3l-5 69.2c0 .2-.2.3-.4.3z"></path>
<path class="st1" d="M109 26.6 58 15.1c-.1 0-.3.1-.3.3L42 82c0 .2.1.4.3.4L92 94.6c.2 0 .3 0 .4-.3L109.2 27v-.3h-.3z" fill="#fff"></path>
<path d="M91.8 95.6h.3c.6 0 1.1-.4 1.3-1l16.8-67.3c.1-.8-.3-1.5-1-1.7L58.3 14.2H58c-.6 0-1.1.4-1.3 1L41.1 81.8c-.2.7.2 1.4 1 1.6l49.7 12.2zm.3-1H92L42.3 82.4a.3.3 0 0 1-.3-.4l15.7-66.6c0-.2.2-.3.3-.3l51 11.5.1.2s.1.1 0 .2L92.5 94.4l-.3.2z"></path>
<path class="st2" d="M101.7 31.5 62 22.7c-1-.2-2 .4-2.2 1.4l-3 12c-.2 1 .4 1.9 1.4 2.1L98 47.8c1 .2 2-.4 2.1-1.4l3-12.7c.2-1-.4-2-1.4-2.2zm-9.5 40.9-11.9-2.6a1 1 0 0 0-1.1.7L76 82c-.1.5.2 1 .7 1.1l12.2 3.3c.5.1 1-.2 1.2-.7l2.9-12a1 1 0 0 0-.8-1.2z" fill="#EF4056"></path>
<path d="M97.3 52.3c-1.8-.4-3 .6-4 1.4-.8.7-1.3 1.1-2 1-.9-.3-1.2-.8-1.6-1.9-.5-1.1-1-2.5-2.9-3-1.8-.3-3 .6-3.9 1.4-.8.7-1.4 1.1-2.1 1-.8-.2-1.1-.8-1.5-1.8-.5-1.2-1.1-2.6-3-3s-2.9.6-3.8 1.4c-.9.7-1.4 1-2.2 1-.7-.3-1-.9-1.5-1.9-.5-1.1-1-2.5-2.9-3-1.7-.4-3 .6-3.9 1.4-.8.7-1.3 1.1-2.1 1-.8-.2-1-.8-1.5-1.9-.5-1-1-2.5-2.9-3a1 1 0 0 0-1.2.8c-.1.6.2 1.1.8 1.2.7.2 1 .8 1.5 1.8.4 1.2 1 2.6 2.8 3 1.8.4 3-.6 4-1.4.8-.7 1.3-1 2.1-.9s1 .8 1.5 1.8c.5 1.1 1 2.5 2.9 3 1.8.4 3-.6 3.9-1.4.8-.7 1.4-1.1 2.1-1s1 .8 1.5 1.9c.5 1 1.1 2.5 3 3 1.7.3 2.9-.7 3.8-1.4.9-.8 1.4-1.2 2.2-1 .7.2 1 .8 1.5 1.8.5 1.1 1 2.5 2.9 3h.8c1.3 0 2.3-.7 3-1.4 1-.7 1.4-1.1 2.2-1a1 1 0 1 0 .5-1.9zM95 61.7c-1.7-.4-3 .6-3.9 1.4-.8.7-1.3 1.1-2.1 1s-1-.8-1.5-1.9c-.5-1-1.1-2.5-2.9-3-1.8-.3-3 .7-3.9 1.4-.9.8-1.4 1.2-2.2 1-.7-.2-1-.8-1.5-1.8-.4-1.1-1-2.5-2.8-3-1.8-.4-3 .6-4 1.4-.8.7-1.3 1.1-2 1-.9-.3-1.1-.8-1.6-1.9-.5-1.1-1-2.5-2.9-3-1.8-.4-3 .6-3.9 1.4-.8.7-1.3 1.1-2.1 1s-1-.8-1.5-1.8c-.5-1.2-1.1-2.6-2.9-3a1 1 0 1 0-.4 2c.7.1 1 .7 1.5 1.8.4 1 1 2.5 2.8 3 1.8.3 3-.7 4-1.4.8-.8 1.3-1.2 2-1 .9.2 1.1.8 1.6 1.8.5 1.1 1 2.5 2.9 3 1.8.4 3-.6 3.9-1.4.8-.7 1.3-1.1 2.1-1 .8.3 1 .8 1.5 1.9.5 1.1 1.1 2.5 2.9 3 1.8.4 3-.6 3.9-1.4.8-.7 1.4-1.1 2.1-1s1.1.8 1.6 1.8c.4 1.2 1 2.6 2.8 3h.9c1.3 0 2.3-.7 3-1.4.9-.7 1.4-1.1 2.2-1 .5.2 1-.1 1.2-.7s-.2-1-.8-1.2zM76.3 71c-.7-.2-1-.8-1.5-1.8-.4-1.1-1-2.5-2.8-3-1.8-.4-3 .6-4 1.4-.8.7-1.3 1.1-2 1-.9-.2-1.1-.8-1.6-1.9-.5-1.1-1-2.5-2.9-3-1.8-.3-3 .6-3.9 1.4-.8.8-1.3 1.2-2.1 1-.8-.2-1-.8-1.5-1.8-.5-1.2-1.1-2.6-2.9-3a1 1 0 1 0-.4 2c.7.1 1 .7 1.5 1.8.4 1 1 2.5 2.8 3 1.8.3 3-.6 4-1.4.8-.8 1.3-1.2 2-1s1.1.8 1.6 1.8c.5 1.2 1 2.6 2.9 3 1.8.4 3-.6 3.9-1.4.8-.7 1.3-1.1 2.1-1s1 .9 1.5 1.9c.5 1.1 1.1 2.5 2.9 3h.2a1 1 0 0 0 .3-2zm-2.2 9.4c-.7-.2-1-.8-1.5-1.8-.4-1.1-1-2.5-2.8-3-1.8-.4-3 .6-4 1.4-.8.7-1.3 1.1-2 1-.9-.2-1.1-.8-1.6-1.9-.5-1.1-1-2.5-2.9-3-1.8-.3-3 .6-3.9 1.4-.8.8-1.3 1.2-2.1 1-.8-.2-1-.8-1.5-1.8-.5-1.2-1.1-2.6-2.9-3a1 1 0 0 0-1.2.8c-.1.5.2 1 .7 1.2.8.1 1.1.7 1.5 1.8.5 1 1.1 2.5 3 3 1.7.3 2.9-.6 3.8-1.4.9-.8 1.4-1.2 2.2-1s1 .8 1.5 1.8c.5 1.2 1 2.6 2.9 3 1.8.4 3-.6 3.9-1.4.8-.7 1.3-1.1 2.1-1 .8.3 1 .9 1.5 1.9.5 1.1 1.1 2.5 2.9 3h.2a1 1 0 0 0 .2-2z"></path>
<path class="st3" fill="#1CB0A8" d="M176.8 13.9h5.8v5.8h-5.8zM1 28.4 3 23l5.4 2-2 5.4zM34.3 4.2l6.7-3 3 6.5-6.6 3.3z"></path>
<path class="st2" d="M19.6 8a3 3 0 1 0 0 6.2 3 3 0 0 0 0-6.2zm47.5-4.6a3.3 3.3 0 1 0 0 6.6 3.3 3.3 0 0 0 0-6.6z" fill="#EF4056"></path>
<circle class="st2" cx="120.1" cy="31.3" r="2.2" fill="#EF4056"></circle>
<path class="st0" fill="#FCB643" d="m14.3 51.3 1.6 3.8 1.7 3.8 2.5-3.4 2.4-3.3-4.1-.5zm98.1-41.8-3.5 1.5 3.1 2.3 3.1 2.3.4-3.9.4-3.8zm-7.5 64.2 4.3 3.2 4.4 3.2.6-5.4.6-5.4-5 2.2z"></path>
<path class="st3" d="M46 22.7a2 2 0 0 0-2 1.7l-1 11.7c-.1.4 0 1 .3 1.3.4.4.8.6 1.4.7l6.5.6L54.9 23l-8.9-.3zm-1.3 21.8a2 2 0 0 0-2 1.7l-1 11.7c-.1.5 0 1 .3 1.3.4.4.8.6 1.4.7l2.8.3 3.6-15.5-5-.2zM44.5 67h-1.1a2 2 0 0 0-2 1.7l-1 11.6a1.8 1.8 0 0 0 .7 1.6v-.1L44.5 67z" fill="#1CB0A8"></path>
<path d="M37.8 30.6c-1.8.4-2.4 1.8-3 2.9-.4 1-.7 1.6-1.5 1.8s-1.3-.3-2-1c-1-.9-2.1-1.9-4-1.5a1 1 0 0 0 .5 2c.7-.2 1.2.2 2 1 .9.7 1.8 1.5 3.2 1.5h.7c1.8-.4 2.5-1.8 3-3 .5-1 .8-1.6 1.5-1.7h.8l.1-2h-1.3zm-1 12.3c-.4 1-.7 1.6-1.5 1.8-.8.1-1.3-.3-2.1-1-1-.9-2.1-1.9-4-1.5a1 1 0 0 0 .5 2c.8-.2 1.3.2 2.1 1 .8.6 1.7 1.5 3.1 1.5h.8a4 4 0 0 0 2.6-2.3l.2-3.8c-.9.6-1.3 1.5-1.7 2.3z"></path>
<path class="st0" d="M32.8 54c-.8.2-1.3 1-1.1 1.7l4.6 23 1.5-26-5 1.3zm6.6 33 1.6-.2-2.8-.2c.3.3.7.5 1.2.5z" fill="#FCB643"></path>
</svg>

После

Ширина:  |  Высота:  |  Размер: 5.3 KiB

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

@ -674,6 +674,25 @@ This reports the user's interaction with those Pocket tiles.
}
```
### Load more button ping
```js
{
"event": "CLICK",
"source": "DS_LOAD_MORE_BUTTON",
// Basic metadata
"action": "activity_stream_event",
"page": ["about:newtab" | "about:home" | "about:welcome" | "unknown"],
"client_id": "26288a14-5cc4-d14f-ae0a-bb01ef45be9c",
"session_id": "005deed0-e3e4-4c02-a041-17405fd703f6",
"browser_session_id": "e7e52665-7db3-f348-9918-e93160eb2ef3",
"addon_version": "20180710100040",
"locale": "en-US",
"user_prefs": 7
}
```
## Save to Pocket button pings
Right now the save to Pocket button, while technically outside of newtab, has some similarities with the newtab telemetry.

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

@ -459,6 +459,12 @@ this.DiscoveryStreamFeed = class DiscoveryStreamFeed {
.getState()
.Prefs.values?.pocketConfig?.spocPositions?.split(`,`);
const loadMoreEnabled = this.store.getState().Prefs.values?.pocketConfig
?.loadMore;
const lastCardMessageEnabled = this.store.getState().Prefs.values
?.pocketConfig?.lastCardMessageEnabled;
const sponsoredCollectionsEnabled = this.store.getState().Prefs.values[
PREF_COLLECTIONS_ENABLED
];
@ -477,6 +483,8 @@ this.DiscoveryStreamFeed = class DiscoveryStreamFeed {
spocPositions: this.parseSpocPositions(spocPositions),
sponsoredCollectionsEnabled,
compactLayout,
loadMoreEnabled,
lastCardMessageEnabled,
});
}
@ -1852,11 +1860,14 @@ this.DiscoveryStreamFeed = class DiscoveryStreamFeed {
// `spocPositions` Changes the position of spoc cards.
// `sponsoredCollectionsEnabled` Tuns on and off the sponsored collection section.
// `compactLayout` Changes cards to smaller more compact cards.
// `lastCardMessageEnabled` Shows a message card at the end of the feed.
getHardcodedLayout = ({
items = 21,
spocPositions = [2, 4, 11, 20],
sponsoredCollectionsEnabled = false,
compactLayout = false,
loadMoreEnabled = false,
lastCardMessageEnabled = false,
}) => ({
lastUpdate: Date.now(),
spocs: {
@ -1932,6 +1943,8 @@ getHardcodedLayout = ({
items,
compact: compactLayout,
},
loadMoreEnabled,
lastCardMessageEnabled,
cta_variant: "link",
header: {
title: "",

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

@ -6,7 +6,7 @@
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; object-src 'none'; script-src resource: chrome:; connect-src https:; img-src https: data: blob:; style-src 'unsafe-inline';">
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; object-src 'none'; script-src resource: chrome:; connect-src https:; img-src https: data: blob: chrome:; style-src 'unsafe-inline';">
<title data-l10n-id="newtab-page-title"></title>
<link rel="icon" type="image/png" href="chrome://branding/content/icon32.png"/>
<link rel="localization" href="branding/brand.ftl" />

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

@ -6,7 +6,7 @@
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; object-src 'none'; script-src resource: chrome:; connect-src https:; img-src https: data: blob:; style-src 'unsafe-inline';">
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; object-src 'none'; script-src resource: chrome:; connect-src https:; img-src https: data: blob: chrome:; style-src 'unsafe-inline';">
<title data-l10n-id="newtab-page-title"></title>
<link rel="icon" type="image/png" href="chrome://branding/content/icon32.png"/>
<link rel="localization" href="branding/brand.ftl" />

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

@ -6,7 +6,7 @@
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; object-src 'none'; script-src resource: chrome:; connect-src https:; img-src https: data: blob:; style-src 'unsafe-inline';">
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; object-src 'none'; script-src resource: chrome:; connect-src https:; img-src https: data: blob: chrome:; style-src 'unsafe-inline';">
<title data-l10n-id="newtab-page-title"></title>
<link rel="icon" type="image/png" href="chrome://branding/content/icon32.png"/>
<link rel="localization" href="branding/brand.ftl" />

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

@ -1,5 +1,9 @@
import { CardGrid } from "content-src/components/DiscoveryStreamComponents/CardGrid/CardGrid";
import { DSCard } from "content-src/components/DiscoveryStreamComponents/DSCard/DSCard";
import {
DSCard,
LastCardMessage,
} from "content-src/components/DiscoveryStreamComponents/DSCard/DSCard";
import { actionCreators as ac } from "common/Actions.jsm";
import React from "react";
import { shallow } from "enzyme";
@ -55,4 +59,31 @@ describe("<CardGrid>", () => {
assert.ok(wrapper.find(".ds-card-grid-include-descriptions").exists());
});
it("should show last card and more loaded state", () => {
const dispatch = sinon.stub();
wrapper.setProps({
dispatch,
compact: true,
loadMoreEnabled: true,
lastCardMessageEnabled: true,
data: { recommendations: [{}, {}] },
});
const loadMoreButton = wrapper.find(".ds-card-grid-load-more-button");
assert.ok(loadMoreButton.exists());
loadMoreButton.simulate("click", { preventDefault: () => {} });
assert.calledOnce(dispatch);
assert.calledWith(
dispatch,
ac.UserEvent({
event: "CLICK",
source: "DS_LOAD_MORE_BUTTON",
})
);
const lastCard = wrapper.find(LastCardMessage);
assert.ok(lastCard.exists());
});
});

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

@ -234,6 +234,17 @@ newtab-pocket-learn-more = Learn more
newtab-pocket-cta-button = Get { -pocket-brand-name }
newtab-pocket-cta-text = Save the stories you love in { -pocket-brand-name }, and fuel your mind with fascinating reads.
# This is a button shown at the bottom of the Pocket section that loads more stories when clicked.
newtab-pocket-load-more-stories-button = Load more stories
## Pocket Final Card Section.
## This is for the final card in the Pocket grid.
newtab-pocket-last-card-title = Youre all caught up!
newtab-pocket-last-card-desc = Check back later for more.
newtab-pocket-last-card-image =
.alt = Youre all caught up
## Error Fallback Content.
## This message and suggested action link are shown in each section of UI that fails to render.

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

@ -158,6 +158,20 @@ const FeatureManifest = {
"browser.newtabpage.activity-stream.discoverystream.compactLayout.enabled",
description: "Enable compact cards on newtab grid",
},
loadMore: {
type: "boolean",
fallbackPref:
"browser.newtabpage.activity-stream.discoverystream.loadMore.enabled",
description:
"A button to load more stories at the bottom of the Pocket section.",
},
lastCardMessageEnabled: {
type: "boolean",
fallbackPref:
"browser.newtabpage.activity-stream.discoverystream.lastCardMessage.enabled",
description:
"The last card in the Pocket section is a message that they are currently at the end of the list of stories.",
},
},
},
"password-autocomplete": {