Bug 1498314 - Add simplified onboarding, CFR installing and bug fixes to Activity Stream r=ursula

Differential Revision: https://phabricator.services.mozilla.com/D8443

--HG--
rename : browser/components/newtab/content-src/asrouter/templates/NewsletterSnippet/NewsletterSnippet.jsx => browser/components/newtab/content-src/asrouter/templates/SubmitFormSnippet/SubmitFormSnippet.jsx
rename : browser/components/newtab/content-src/asrouter/templates/NewsletterSnippet/NewsletterSnippet.schema.json => browser/components/newtab/content-src/asrouter/templates/SubmitFormSnippet/SubmitFormSnippet.schema.json
rename : browser/components/newtab/content-src/asrouter/templates/NewsletterSnippet/_NewsletterSnippet.scss => browser/components/newtab/content-src/asrouter/templates/SubmitFormSnippet/_SubmitFormSnippet.scss
rename : browser/components/newtab/test/unit/asrouter/templates/NewsletterSnippet.test.jsx => browser/components/newtab/test/unit/asrouter/templates/SubmitFormSnippet.test.jsx
extra : moz-landing-system : lando
This commit is contained in:
Ed Lee 2018-10-11 20:30:28 +00:00
Родитель 47b312fbc2
Коммит 704e5da4d0
76 изменённых файлов: 790 добавлений и 567 удалений

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

@ -3,17 +3,18 @@ import {actionCreators as ac} from "common/Actions.jsm";
import {OUTGOING_MESSAGE_NAME as AS_GENERAL_OUTGOING_MESSAGE_NAME} from "content-src/lib/init-store";
import {ImpressionsWrapper} from "./components/ImpressionsWrapper/ImpressionsWrapper";
import {MessageContext} from "fluent";
import {NewsletterSnippet} from "./templates/NewsletterSnippet/NewsletterSnippet";
import {OnboardingMessage} from "./templates/OnboardingMessage/OnboardingMessage";
import React from "react";
import ReactDOM from "react-dom";
import {safeURI} from "./template-utils";
import {SimpleSnippet} from "./templates/SimpleSnippet/SimpleSnippet";
import {SubmitFormSnippet} from "./templates/SubmitFormSnippet/SubmitFormSnippet";
// Key names matching schema name of templates
const SnippetComponents = {
simple_snippet: SimpleSnippet,
newsletter_snippet: NewsletterSnippet,
newsletter_snippet: props => <SubmitFormSnippet {...props} form_method="POST" />,
fxa_signup_snippet: props => <SubmitFormSnippet {...props} form_method="GET" />,
};
const INCOMING_MESSAGE_NAME = "ASRouter:parent-to-child";
@ -266,12 +267,12 @@ export class ASRouterUISurface extends React.PureComponent {
// This helps with testing
document={this.props.document}>
<LocalizationProvider messages={generateMessages({
privacy_notice: content.privacy_notice_text,
snippet_text: content.text,
privacy_notice: content.scene2_privacy_html,
snippet_text: content.text || content.scene1_text,
})}>
<SnippetComponent
{...this.state.message}
richText={<RichText text={this.state.message.content.text}
richText={<RichText text={content.text || content.scene1_text}
localization_id="snippet_text"
links={this.state.message.content.links}
sendClick={this.sendClick} />}

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

@ -26,6 +26,7 @@ Please note that some targeting attributes require stricter controls on the tele
* [sync](#sync)
* [topFrecentSites](#topfrecentsites)
* [totalBookmarksCount](#totalbookmarkscount)
* [xpinstallEnabled](#xpinstallEnabled)
## Detailed usage
@ -374,4 +375,12 @@ Total number of bookmarks.
declare const totalBookmarksCount: number;
```
### `xpinstallEnabled`
Pref used by system administrators to disallow add-ons from installed altogether.
#### Definition
```ts
declare const xpinstallEnabled: boolean;
```

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

@ -2,7 +2,7 @@ import React from "react";
import {SimpleSnippet} from "../SimpleSnippet/SimpleSnippet";
import {SnippetBase} from "../../components/SnippetBase/SnippetBase";
export class NewsletterSnippet extends React.PureComponent {
export class SubmitFormSnippet extends React.PureComponent {
constructor(props) {
super(props);
this.expandSnippet = this.expandSnippet.bind(this);
@ -11,21 +11,33 @@ export class NewsletterSnippet extends React.PureComponent {
expanded: false,
signupSubmitted: false,
signupSuccess: false,
disableForm: false,
};
}
async handleSubmit(event) {
let json;
if (this.state.disableForm) {
return;
}
event.preventDefault();
this.setState({disableForm: true});
this.props.sendUserActionTelemetry({event: "CLICK_BUTTON", value: "conversion-subscribe-activation", id: "NEWTAB_FOOTER_BAR_CONTENT"});
if (this.props.form_method.toUpperCase() === "GET") {
this.refs.form.submit();
return;
}
const fetchConfig = {
body: new FormData(this.refs.newsletterForm),
body: new FormData(this.refs.form),
method: "POST",
};
event.preventDefault();
this.props.sendUserActionTelemetry({event: "CLICK_BUTTON", value: "conversion-subscribe-activation", id: "NEWTAB_FOOTER_BAR_CONTENT"});
try {
const fetchRequest = new Request(this.refs.newsletterForm.action, fetchConfig);
const fetchRequest = new Request(this.refs.form.action, fetchConfig);
const response = await fetch(fetchRequest);
json = await response.json();
} catch (err) {
@ -39,6 +51,8 @@ export class NewsletterSnippet extends React.PureComponent {
this.setState({signupSuccess: false, signupSubmitted: true});
this.props.sendUserActionTelemetry({event: "CLICK_BUTTON", value: "subscribe-error", id: "NEWTAB_FOOTER_BAR_CONTENT"});
}
this.setState({disableForm: false});
}
expandSnippet() {
@ -60,7 +74,7 @@ export class NewsletterSnippet extends React.PureComponent {
}
renderFormPrivacyNotice() {
return (<label className="privacy-notice" htmlFor="id_privacy">
return this.props.privacyNoticeRichText && (<label className="privacy-notice" htmlFor="id_privacy">
<p>
<input type="checkbox" id="id_privacy" name="privacy" required="required" />
<span>{this.props.privacyNoticeRichText}</span>
@ -75,17 +89,17 @@ export class NewsletterSnippet extends React.PureComponent {
return (<SimpleSnippet className={this.props.className}
onButtonClick={onButtonClick}
provider={this.props.provider}
content={{button_label: this.props.content.button_label, text: message}} />);
content={{button_label: this.props.content.scene1_button_label, text: message}} />);
}
renderSignupView() {
const {content} = this.props;
return (<SnippetBase {...this.props} className="NewsletterSnippet" footerDismiss={true}>
return (<SnippetBase {...this.props} className="SubmitFormSnippet" footerDismiss={true}>
<div className="message">
<p>{content.scene2_text}</p>
</div>
<form action={content.form_action} method="POST" onSubmit={this.handleSubmit} ref="newsletterForm">
<form action={content.form_action} method={this.props.form_method} onSubmit={this.handleSubmit} ref="form">
{this.renderHiddenFormInputs()}
<div>
<input type="email" name="email" required="required" placeholder={content.scene2_email_placeholder_text} autoFocus={true} />
@ -96,13 +110,22 @@ export class NewsletterSnippet extends React.PureComponent {
</SnippetBase>);
}
getFirstSceneContent() {
return Object.keys(this.props.content).filter(key => key.includes("scene1")).reduce((acc, key) => {
acc[key.substr(7)] = this.props.content[key];
return acc;
}, {});
}
render() {
const content = {...this.props.content, ...this.getFirstSceneContent()};
if (this.state.signupSubmitted) {
return this.renderSignupSubmitted();
}
if (this.state.expanded) {
return this.renderSignupView();
}
return <SimpleSnippet {...this.props} onButtonClick={this.expandSnippet} />;
return <SimpleSnippet {...this.props} content={content} onButtonClick={this.expandSnippet} />;
}
}

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

@ -1,5 +1,5 @@
{
"title": "NewsletterSchema",
"title": "SubmitFormSnippet",
"description": "A template with two states: a SimpleSnippet and another that contains a form",
"version": "1.0.0",
"type": "object",
@ -19,23 +19,29 @@
}
},
"properties": {
"title": {
"allOf": [
{"$ref": "#/definitions/plainText"},
{"description": "Snippet title displayed before snippet text"}
"scene1_title": {
"allof": [
{"$ref": "#/definitions/plaintext"},
{"description": "snippet title displayed before snippet text"}
]
},
"text": {
"scene1_text": {
"allOf": [
{"$ref": "#/definitions/richText"},
{"description": "Main body text of snippet. HTML subset allowed: i, b, u, strong, em, br"}
]
},
"icon": {
"scene2_text": {
"allOf": [
{"$ref": "#/definitions/richText"},
{"description": "Main body text of snippet. HTML subset allowed: i, b, u, strong, em, br"}
]
},
"scene1_icon": {
"type": "string",
"description": "Snippet icon. 64x64px. SVG or PNG preferred."
},
"title_icon": {
"scene1_title_icon": {
"type": "string",
"description": "Small icon that shows up before the title / text. 16x16px. SVG or PNG preferred. Grayscale."
},
@ -51,10 +57,6 @@
"type": "string",
"description": "Message shown if registration failed."
},
"scene2_text": {
"type": "string",
"description": "Main body of the snippet in the second scene."
},
"scene2_email_placeholder_text": {
"type": "string",
"description": "Value to show while input is empty."
@ -71,7 +73,7 @@
"type": "object",
"description": "Each entry represents a hidden input, key is used as value for the name property."
},
"button_label": {
"scene1_button_label": {
"allOf": [
{"$ref": "#/definitions/plainText"},
{"description": "Text for a button next to main snippet text that links to button_url. Requires button_url."}
@ -105,9 +107,9 @@
}
},
"additionalProperties": false,
"required": ["text", "form_action", "scene2_text", "hidden_inputs"],
"required": ["scene1_text", "form_action", "scene2_text", "hidden_inputs", "error_text", "success_text", "scene1_button_label"],
"dependencies": {
"button_color": ["button_label"],
"button_background_color": ["button_label"]
"scene1_button_color": ["scene1_button_label"],
"scene1_button_background_color": ["scene1_button_label"]
}
}

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

@ -1,4 +1,4 @@
.NewsletterSnippet {
.SubmitFormSnippet {
flex-direction: column;
flex: 1 1 100%;
width: 100%;

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

@ -22,7 +22,9 @@
}
@media (min-width: $break-point-widest) and (max-width: $break-point-widest + 2 * $card-width) {
:nth-child(3n) {
// 3n for normal cards, 4n for compact cards
:nth-child(3n),
:nth-child(4n) {
@include context-menu-open-left;
}
}

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

@ -150,5 +150,5 @@ input {
@import '../asrouter/components/SnippetBase/SnippetBase';
@import '../asrouter/components/ModalOverlay/ModalOverlay';
@import '../asrouter/templates/SimpleSnippet/SimpleSnippet';
@import '../asrouter/templates/NewsletterSnippet/NewsletterSnippet';
@import '../asrouter/templates/SubmitFormSnippet/SubmitFormSnippet';
@import '../asrouter/templates/OnboardingMessage/OnboardingMessage';

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

@ -111,7 +111,7 @@ You can also write a detailed description of the commit:
Just as in the **subject**, use the imperative, present tense: "change" not "changed" nor "changes"
It should include the motivation for the change and contrast this with previous behavior.
###Footer
### Footer
The footer should contain any information about **Breaking Changes** and is also the place to
reference GitHub issues that this commit **Closes**.

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

@ -874,7 +874,8 @@ main {
inset-inline-end: 0;
inset-inline-start: auto; } }
@media (min-width: 1122px) and (max-width: 1570px) {
.sections-list .section-list :nth-child(3n) .context-menu {
.sections-list .section-list :nth-child(3n) .context-menu,
.sections-list .section-list :nth-child(4n) .context-menu {
margin-inline-end: 5px;
margin-inline-start: auto;
inset-inline-end: 0;
@ -2088,47 +2089,47 @@ a.firstrun-link {
.SimpleSnippet .ASRouterButton {
cursor: pointer; }
.NewsletterSnippet {
.SubmitFormSnippet {
flex-direction: column;
flex: 1 1 100%;
width: 100%; }
.NewsletterSnippet .ASRouterButton.primary {
.SubmitFormSnippet .ASRouterButton.primary {
font-size: 15px;
flex: 1 1 0; }
.NewsletterSnippet form {
.SubmitFormSnippet form {
display: flex;
flex-direction: column;
width: 100%; }
.NewsletterSnippet .message {
.SubmitFormSnippet .message {
font-size: 14px;
align-self: stretch;
flex: 0 0 100%; }
.NewsletterSnippet .privacy-notice {
.SubmitFormSnippet .privacy-notice {
color: var(--newtab-text-secondary-color);
flex: 0 0 100%; }
.NewsletterSnippet .innerWrapper {
.SubmitFormSnippet .innerWrapper {
max-width: 670px;
flex-wrap: wrap;
justify-items: center; }
.NewsletterSnippet .footer {
.SubmitFormSnippet .footer {
width: 100%;
margin: 0 auto;
text-align: right;
background: #EDEDF0;
padding: 10px 0; }
.NewsletterSnippet .footer .footer-content {
.SubmitFormSnippet .footer .footer-content {
margin: 0 auto;
max-width: 768px;
width: 100%;
text-align: right; }
.NewsletterSnippet input[type='email'] {
.SubmitFormSnippet input[type='email'] {
background-color: var(--newtab-textbox-background-color);
border: 1px solid var(--newtab-textbox-border);
padding: 0 8px;
height: 32px;
font-size: 15px;
width: 50%; }
.NewsletterSnippet input[type='email']:focus {
.SubmitFormSnippet input[type='email']:focus {
border: 1px solid var(--newtab-textbox-focus-color);
box-shadow: var(--newtab-textbox-focus-boxshadow); }

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

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

@ -877,7 +877,8 @@ main {
inset-inline-end: 0;
inset-inline-start: auto; } }
@media (min-width: 1122px) and (max-width: 1570px) {
.sections-list .section-list :nth-child(3n) .context-menu {
.sections-list .section-list :nth-child(3n) .context-menu,
.sections-list .section-list :nth-child(4n) .context-menu {
margin-inline-end: 5px;
margin-inline-start: auto;
inset-inline-end: 0;
@ -2091,47 +2092,47 @@ a.firstrun-link {
.SimpleSnippet .ASRouterButton {
cursor: pointer; }
.NewsletterSnippet {
.SubmitFormSnippet {
flex-direction: column;
flex: 1 1 100%;
width: 100%; }
.NewsletterSnippet .ASRouterButton.primary {
.SubmitFormSnippet .ASRouterButton.primary {
font-size: 15px;
flex: 1 1 0; }
.NewsletterSnippet form {
.SubmitFormSnippet form {
display: flex;
flex-direction: column;
width: 100%; }
.NewsletterSnippet .message {
.SubmitFormSnippet .message {
font-size: 14px;
align-self: stretch;
flex: 0 0 100%; }
.NewsletterSnippet .privacy-notice {
.SubmitFormSnippet .privacy-notice {
color: var(--newtab-text-secondary-color);
flex: 0 0 100%; }
.NewsletterSnippet .innerWrapper {
.SubmitFormSnippet .innerWrapper {
max-width: 670px;
flex-wrap: wrap;
justify-items: center; }
.NewsletterSnippet .footer {
.SubmitFormSnippet .footer {
width: 100%;
margin: 0 auto;
text-align: right;
background: #EDEDF0;
padding: 10px 0; }
.NewsletterSnippet .footer .footer-content {
.SubmitFormSnippet .footer .footer-content {
margin: 0 auto;
max-width: 768px;
width: 100%;
text-align: right; }
.NewsletterSnippet input[type='email'] {
.SubmitFormSnippet input[type='email'] {
background-color: var(--newtab-textbox-background-color);
border: 1px solid var(--newtab-textbox-border);
padding: 0 8px;
height: 32px;
font-size: 15px;
width: 50%; }
.NewsletterSnippet input[type='email']:focus {
.SubmitFormSnippet input[type='email']:focus {
border: 1px solid var(--newtab-textbox-focus-color);
box-shadow: var(--newtab-textbox-focus-boxshadow); }

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

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

@ -874,7 +874,8 @@ main {
inset-inline-end: 0;
inset-inline-start: auto; } }
@media (min-width: 1122px) and (max-width: 1570px) {
.sections-list .section-list :nth-child(3n) .context-menu {
.sections-list .section-list :nth-child(3n) .context-menu,
.sections-list .section-list :nth-child(4n) .context-menu {
margin-inline-end: 5px;
margin-inline-start: auto;
inset-inline-end: 0;
@ -2088,47 +2089,47 @@ a.firstrun-link {
.SimpleSnippet .ASRouterButton {
cursor: pointer; }
.NewsletterSnippet {
.SubmitFormSnippet {
flex-direction: column;
flex: 1 1 100%;
width: 100%; }
.NewsletterSnippet .ASRouterButton.primary {
.SubmitFormSnippet .ASRouterButton.primary {
font-size: 15px;
flex: 1 1 0; }
.NewsletterSnippet form {
.SubmitFormSnippet form {
display: flex;
flex-direction: column;
width: 100%; }
.NewsletterSnippet .message {
.SubmitFormSnippet .message {
font-size: 14px;
align-self: stretch;
flex: 0 0 100%; }
.NewsletterSnippet .privacy-notice {
.SubmitFormSnippet .privacy-notice {
color: var(--newtab-text-secondary-color);
flex: 0 0 100%; }
.NewsletterSnippet .innerWrapper {
.SubmitFormSnippet .innerWrapper {
max-width: 670px;
flex-wrap: wrap;
justify-items: center; }
.NewsletterSnippet .footer {
.SubmitFormSnippet .footer {
width: 100%;
margin: 0 auto;
text-align: right;
background: #EDEDF0;
padding: 10px 0; }
.NewsletterSnippet .footer .footer-content {
.SubmitFormSnippet .footer .footer-content {
margin: 0 auto;
max-width: 768px;
width: 100%;
text-align: right; }
.NewsletterSnippet input[type='email'] {
.SubmitFormSnippet input[type='email'] {
background-color: var(--newtab-textbox-background-color);
border: 1px solid var(--newtab-textbox-border);
padding: 0 8px;
height: 32px;
font-size: 15px;
width: 50%; }
.NewsletterSnippet input[type='email']:focus {
.SubmitFormSnippet input[type='email']:focus {
border: 1px solid var(--newtab-textbox-focus-color);
box-shadow: var(--newtab-textbox-focus-boxshadow); }

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

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

@ -101,7 +101,7 @@ __webpack_require__.r(__webpack_exports__);
/* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_7___default = /*#__PURE__*/__webpack_require__.n(react_redux__WEBPACK_IMPORTED_MODULE_7__);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(5);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_8___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_8__);
/* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(13);
/* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(10);
/* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_9___default = /*#__PURE__*/__webpack_require__.n(react_dom__WEBPACK_IMPORTED_MODULE_9__);
/* harmony import */ var common_Reducers_jsm__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(46);
@ -930,14 +930,14 @@ __webpack_require__.r(__webpack_exports__);
/* harmony import */ var content_src_lib_init_store__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(7);
/* harmony import */ var _components_ImpressionsWrapper_ImpressionsWrapper__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(9);
/* harmony import */ var fluent__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(44);
/* harmony import */ var _templates_NewsletterSnippet_NewsletterSnippet__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(10);
/* harmony import */ var _templates_OnboardingMessage_OnboardingMessage__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(47);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(5);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_7___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_7__);
/* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(13);
/* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_8___default = /*#__PURE__*/__webpack_require__.n(react_dom__WEBPACK_IMPORTED_MODULE_8__);
/* harmony import */ var _template_utils__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(11);
/* harmony import */ var _templates_SimpleSnippet_SimpleSnippet__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(48);
/* harmony import */ var _templates_OnboardingMessage_OnboardingMessage__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(48);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(5);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_6___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_6__);
/* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(10);
/* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_7___default = /*#__PURE__*/__webpack_require__.n(react_dom__WEBPACK_IMPORTED_MODULE_7__);
/* harmony import */ var _template_utils__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(11);
/* harmony import */ var _templates_SimpleSnippet_SimpleSnippet__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(47);
/* harmony import */ var _templates_SubmitFormSnippet_SubmitFormSnippet__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(13);
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
@ -954,8 +954,9 @@ var _extends = Object.assign || function (target) { for (var i = 1; i < argument
// Key names matching schema name of templates
const SnippetComponents = {
simple_snippet: _templates_SimpleSnippet_SimpleSnippet__WEBPACK_IMPORTED_MODULE_10__["SimpleSnippet"],
newsletter_snippet: _templates_NewsletterSnippet_NewsletterSnippet__WEBPACK_IMPORTED_MODULE_5__["NewsletterSnippet"]
simple_snippet: _templates_SimpleSnippet_SimpleSnippet__WEBPACK_IMPORTED_MODULE_9__["SimpleSnippet"],
newsletter_snippet: props => react__WEBPACK_IMPORTED_MODULE_6___default.a.createElement(_templates_SubmitFormSnippet_SubmitFormSnippet__WEBPACK_IMPORTED_MODULE_10__["SubmitFormSnippet"], _extends({}, props, { form_method: "POST" })),
fxa_signup_snippet: props => react__WEBPACK_IMPORTED_MODULE_6___default.a.createElement(_templates_SubmitFormSnippet_SubmitFormSnippet__WEBPACK_IMPORTED_MODULE_10__["SubmitFormSnippet"], _extends({}, props, { form_method: "GET" }))
};
const INCOMING_MESSAGE_NAME = "ASRouter:parent-to-child";
@ -1031,12 +1032,12 @@ function generateMessages(content) {
// Elements allowed in snippet content
const ALLOWED_TAGS = {
b: react__WEBPACK_IMPORTED_MODULE_7___default.a.createElement("b", null),
i: react__WEBPACK_IMPORTED_MODULE_7___default.a.createElement("i", null),
u: react__WEBPACK_IMPORTED_MODULE_7___default.a.createElement("u", null),
strong: react__WEBPACK_IMPORTED_MODULE_7___default.a.createElement("strong", null),
em: react__WEBPACK_IMPORTED_MODULE_7___default.a.createElement("em", null),
br: react__WEBPACK_IMPORTED_MODULE_7___default.a.createElement("br", null)
b: react__WEBPACK_IMPORTED_MODULE_6___default.a.createElement("b", null),
i: react__WEBPACK_IMPORTED_MODULE_6___default.a.createElement("i", null),
u: react__WEBPACK_IMPORTED_MODULE_6___default.a.createElement("u", null),
strong: react__WEBPACK_IMPORTED_MODULE_6___default.a.createElement("strong", null),
em: react__WEBPACK_IMPORTED_MODULE_6___default.a.createElement("em", null),
br: react__WEBPACK_IMPORTED_MODULE_6___default.a.createElement("br", null)
};
/**
@ -1048,9 +1049,9 @@ function convertLinks(links, sendClick) {
return Object.keys(links).reduce((acc, linkTag) => {
const { action } = links[linkTag];
// Setting the value to false will not include the attribute in the anchor
const url = action ? false : Object(_template_utils__WEBPACK_IMPORTED_MODULE_9__["safeURI"])(links[linkTag].url);
const url = action ? false : Object(_template_utils__WEBPACK_IMPORTED_MODULE_8__["safeURI"])(links[linkTag].url);
acc[linkTag] = react__WEBPACK_IMPORTED_MODULE_7___default.a.createElement("a", { href: url,
acc[linkTag] = react__WEBPACK_IMPORTED_MODULE_6___default.a.createElement("a", { href: url,
"data-metric": links[linkTag].metric,
"data-action": action,
"data-args": links[linkTag].args,
@ -1066,10 +1067,10 @@ function convertLinks(links, sendClick) {
* Message wrapper used to sanitize markup and render HTML.
*/
function RichText(props) {
return react__WEBPACK_IMPORTED_MODULE_7___default.a.createElement(
return react__WEBPACK_IMPORTED_MODULE_6___default.a.createElement(
fluent_react__WEBPACK_IMPORTED_MODULE_0__["Localized"],
_extends({ id: props.localization_id }, ALLOWED_TAGS, convertLinks(props.links, props.sendClick)),
react__WEBPACK_IMPORTED_MODULE_7___default.a.createElement(
react__WEBPACK_IMPORTED_MODULE_6___default.a.createElement(
"span",
null,
props.text
@ -1077,7 +1078,7 @@ function RichText(props) {
);
}
class ASRouterUISurface extends react__WEBPACK_IMPORTED_MODULE_7___default.a.PureComponent {
class ASRouterUISurface extends react__WEBPACK_IMPORTED_MODULE_6___default.a.PureComponent {
constructor(props) {
super(props);
this.onMessageFromParent = this.onMessageFromParent.bind(this);
@ -1196,13 +1197,13 @@ class ASRouterUISurface extends react__WEBPACK_IMPORTED_MODULE_7___default.a.Pur
const { content } = this.state.message;
if (this.state.message.template === "newsletter_snippet") {
privacyNoticeRichText = react__WEBPACK_IMPORTED_MODULE_7___default.a.createElement(RichText, { text: content.scene2_privacy_html,
privacyNoticeRichText = react__WEBPACK_IMPORTED_MODULE_6___default.a.createElement(RichText, { text: content.scene2_privacy_html,
localization_id: "privacy_notice",
links: content.links,
sendClick: this.sendClick });
}
return react__WEBPACK_IMPORTED_MODULE_7___default.a.createElement(
return react__WEBPACK_IMPORTED_MODULE_6___default.a.createElement(
_components_ImpressionsWrapper_ImpressionsWrapper__WEBPACK_IMPORTED_MODULE_3__["ImpressionsWrapper"],
{
id: "NEWTAB_FOOTER_BAR",
@ -1211,14 +1212,14 @@ class ASRouterUISurface extends react__WEBPACK_IMPORTED_MODULE_7___default.a.Pur
shouldSendImpressionOnUpdate: shouldSendImpressionOnUpdate
// This helps with testing
, document: this.props.document },
react__WEBPACK_IMPORTED_MODULE_7___default.a.createElement(
react__WEBPACK_IMPORTED_MODULE_6___default.a.createElement(
fluent_react__WEBPACK_IMPORTED_MODULE_0__["LocalizationProvider"],
{ messages: generateMessages({
privacy_notice: content.privacy_notice_text,
snippet_text: content.text
privacy_notice: content.scene2_privacy_html,
snippet_text: content.text || content.scene1_text
}) },
react__WEBPACK_IMPORTED_MODULE_7___default.a.createElement(SnippetComponent, _extends({}, this.state.message, {
richText: react__WEBPACK_IMPORTED_MODULE_7___default.a.createElement(RichText, { text: this.state.message.content.text,
react__WEBPACK_IMPORTED_MODULE_6___default.a.createElement(SnippetComponent, _extends({}, this.state.message, {
richText: react__WEBPACK_IMPORTED_MODULE_6___default.a.createElement(RichText, { text: content.text || content.scene1_text,
localization_id: "snippet_text",
links: this.state.message.content.links,
sendClick: this.sendClick }),
@ -1233,7 +1234,7 @@ class ASRouterUISurface extends react__WEBPACK_IMPORTED_MODULE_7___default.a.Pur
}
renderOnboarding() {
return react__WEBPACK_IMPORTED_MODULE_7___default.a.createElement(_templates_OnboardingMessage_OnboardingMessage__WEBPACK_IMPORTED_MODULE_6__["OnboardingMessage"], _extends({}, this.state.bundle, {
return react__WEBPACK_IMPORTED_MODULE_6___default.a.createElement(_templates_OnboardingMessage_OnboardingMessage__WEBPACK_IMPORTED_MODULE_5__["OnboardingMessage"], _extends({}, this.state.bundle, {
UISurface: "NEWTAB_OVERLAY",
onAction: ASRouterUtils.executeAction,
onDoneButton: this.clearBundle(this.state.bundle.bundle),
@ -1245,11 +1246,11 @@ class ASRouterUISurface extends react__WEBPACK_IMPORTED_MODULE_7___default.a.Pur
return null;
}
return react__WEBPACK_IMPORTED_MODULE_7___default.a.createElement(
return react__WEBPACK_IMPORTED_MODULE_6___default.a.createElement(
"div",
{ className: "snippets-preview-banner" },
react__WEBPACK_IMPORTED_MODULE_7___default.a.createElement("span", { className: "icon icon-small-spacer icon-info" }),
react__WEBPACK_IMPORTED_MODULE_7___default.a.createElement(
react__WEBPACK_IMPORTED_MODULE_6___default.a.createElement("span", { className: "icon icon-small-spacer icon-info" }),
react__WEBPACK_IMPORTED_MODULE_6___default.a.createElement(
"span",
null,
"Preview Purposes Only"
@ -1262,8 +1263,8 @@ class ASRouterUISurface extends react__WEBPACK_IMPORTED_MODULE_7___default.a.Pur
if (!message.id && !bundle.template) {
return null;
}
return react__WEBPACK_IMPORTED_MODULE_7___default.a.createElement(
react__WEBPACK_IMPORTED_MODULE_7___default.a.Fragment,
return react__WEBPACK_IMPORTED_MODULE_6___default.a.createElement(
react__WEBPACK_IMPORTED_MODULE_6___default.a.Fragment,
null,
this.renderPreviewBanner(),
bundle.template === "onboarding" ? this.renderOnboarding() : this.renderSnippets()
@ -1288,11 +1289,11 @@ class ASRouterContent {
global.document.body.appendChild(this.containerElement);
}
react_dom__WEBPACK_IMPORTED_MODULE_8___default.a.render(react__WEBPACK_IMPORTED_MODULE_7___default.a.createElement(ASRouterUISurface, null), this.containerElement);
react_dom__WEBPACK_IMPORTED_MODULE_7___default.a.render(react__WEBPACK_IMPORTED_MODULE_6___default.a.createElement(ASRouterUISurface, null), this.containerElement);
}
_unmount() {
react_dom__WEBPACK_IMPORTED_MODULE_8___default.a.unmountComponentAtNode(this.containerElement);
react_dom__WEBPACK_IMPORTED_MODULE_7___default.a.unmountComponentAtNode(this.containerElement);
}
init() {
@ -1555,155 +1556,9 @@ ImpressionsWrapper.defaultProps = {
/***/ }),
/* 10 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
/***/ (function(module, exports) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "NewsletterSnippet", function() { return NewsletterSnippet; });
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(5);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _SimpleSnippet_SimpleSnippet__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(48);
/* harmony import */ var _components_SnippetBase_SnippetBase__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(12);
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
class NewsletterSnippet extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureComponent {
constructor(props) {
super(props);
this.expandSnippet = this.expandSnippet.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.state = {
expanded: false,
signupSubmitted: false,
signupSuccess: false
};
}
handleSubmit(event) {
var _this = this;
return _asyncToGenerator(function* () {
let json;
const fetchConfig = {
body: new FormData(_this.refs.newsletterForm),
method: "POST"
};
event.preventDefault();
_this.props.sendUserActionTelemetry({ event: "CLICK_BUTTON", value: "conversion-subscribe-activation", id: "NEWTAB_FOOTER_BAR_CONTENT" });
try {
const fetchRequest = new Request(_this.refs.newsletterForm.action, fetchConfig);
const response = yield fetch(fetchRequest);
json = yield response.json();
} catch (err) {
console.log(err); // eslint-disable-line no-console
}
if (json && json.status === "ok") {
_this.setState({ signupSuccess: true, signupSubmitted: true });
_this.props.onBlock({ preventDismiss: true });
_this.props.sendUserActionTelemetry({ event: "CLICK_BUTTON", value: "subscribe-success", id: "NEWTAB_FOOTER_BAR_CONTENT" });
} else {
_this.setState({ signupSuccess: false, signupSubmitted: true });
_this.props.sendUserActionTelemetry({ event: "CLICK_BUTTON", value: "subscribe-error", id: "NEWTAB_FOOTER_BAR_CONTENT" });
}
})();
}
expandSnippet() {
this.setState({
expanded: true,
signupSuccess: false,
signupSubmitted: false
});
}
renderHiddenFormInputs() {
const { hidden_inputs } = this.props.content;
if (!hidden_inputs) {
return null;
}
return Object.keys(hidden_inputs).map((key, idx) => react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("input", { key: idx, type: "hidden", name: key, value: hidden_inputs[key] }));
}
renderFormPrivacyNotice() {
return react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(
"label",
{ className: "privacy-notice", htmlFor: "id_privacy" },
react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(
"p",
null,
react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("input", { type: "checkbox", id: "id_privacy", name: "privacy", required: "required" }),
react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(
"span",
null,
this.props.privacyNoticeRichText
)
)
);
}
renderSignupSubmitted() {
const message = this.state.signupSuccess ? this.props.content.success_text : this.props.content.error_text;
const onButtonClick = !this.state.signupSuccess ? this.expandSnippet : null;
return react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_SimpleSnippet_SimpleSnippet__WEBPACK_IMPORTED_MODULE_1__["SimpleSnippet"], { className: this.props.className,
onButtonClick: onButtonClick,
provider: this.props.provider,
content: { button_label: this.props.content.button_label, text: message } });
}
renderSignupView() {
const { content } = this.props;
return react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(
_components_SnippetBase_SnippetBase__WEBPACK_IMPORTED_MODULE_2__["SnippetBase"],
_extends({}, this.props, { className: "NewsletterSnippet", footerDismiss: true }),
react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(
"div",
{ className: "message" },
react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(
"p",
null,
content.scene2_text
)
),
react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(
"form",
{ action: content.form_action, method: "POST", onSubmit: this.handleSubmit, ref: "newsletterForm" },
this.renderHiddenFormInputs(),
react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(
"div",
null,
react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("input", { type: "email", name: "email", required: "required", placeholder: content.scene2_email_placeholder_text, autoFocus: true }),
react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(
"button",
{ type: "submit", className: "ASRouterButton primary", ref: "formSubmitBtn" },
content.scene2_button_label
)
),
this.renderFormPrivacyNotice()
)
);
}
render() {
if (this.state.signupSubmitted) {
return this.renderSignupSubmitted();
}
if (this.state.expanded) {
return this.renderSignupView();
}
return react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_SimpleSnippet_SimpleSnippet__WEBPACK_IMPORTED_MODULE_1__["SimpleSnippet"], _extends({}, this.props, { onButtonClick: this.expandSnippet }));
}
}
module.exports = ReactDOM;
/***/ }),
/* 11 */
@ -1789,9 +1644,178 @@ class SnippetBase extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureCompo
/***/ }),
/* 13 */
/***/ (function(module, exports) {
/***/ (function(module, __webpack_exports__, __webpack_require__) {
module.exports = ReactDOM;
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SubmitFormSnippet", function() { return SubmitFormSnippet; });
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(5);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _SimpleSnippet_SimpleSnippet__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(47);
/* harmony import */ var _components_SnippetBase_SnippetBase__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(12);
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
class SubmitFormSnippet extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureComponent {
constructor(props) {
super(props);
this.expandSnippet = this.expandSnippet.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.state = {
expanded: false,
signupSubmitted: false,
signupSuccess: false,
disableForm: false
};
}
handleSubmit(event) {
var _this = this;
return _asyncToGenerator(function* () {
let json;
if (_this.state.disableForm) {
return;
}
event.preventDefault();
_this.setState({ disableForm: true });
_this.props.sendUserActionTelemetry({ event: "CLICK_BUTTON", value: "conversion-subscribe-activation", id: "NEWTAB_FOOTER_BAR_CONTENT" });
if (_this.props.form_method.toUpperCase() === "GET") {
_this.refs.form.submit();
return;
}
const fetchConfig = {
body: new FormData(_this.refs.form),
method: "POST"
};
try {
const fetchRequest = new Request(_this.refs.form.action, fetchConfig);
const response = yield fetch(fetchRequest);
json = yield response.json();
} catch (err) {
console.log(err); // eslint-disable-line no-console
}
if (json && json.status === "ok") {
_this.setState({ signupSuccess: true, signupSubmitted: true });
_this.props.onBlock({ preventDismiss: true });
_this.props.sendUserActionTelemetry({ event: "CLICK_BUTTON", value: "subscribe-success", id: "NEWTAB_FOOTER_BAR_CONTENT" });
} else {
_this.setState({ signupSuccess: false, signupSubmitted: true });
_this.props.sendUserActionTelemetry({ event: "CLICK_BUTTON", value: "subscribe-error", id: "NEWTAB_FOOTER_BAR_CONTENT" });
}
_this.setState({ disableForm: false });
})();
}
expandSnippet() {
this.setState({
expanded: true,
signupSuccess: false,
signupSubmitted: false
});
}
renderHiddenFormInputs() {
const { hidden_inputs } = this.props.content;
if (!hidden_inputs) {
return null;
}
return Object.keys(hidden_inputs).map((key, idx) => react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("input", { key: idx, type: "hidden", name: key, value: hidden_inputs[key] }));
}
renderFormPrivacyNotice() {
return this.props.privacyNoticeRichText && react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(
"label",
{ className: "privacy-notice", htmlFor: "id_privacy" },
react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(
"p",
null,
react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("input", { type: "checkbox", id: "id_privacy", name: "privacy", required: "required" }),
react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(
"span",
null,
this.props.privacyNoticeRichText
)
)
);
}
renderSignupSubmitted() {
const message = this.state.signupSuccess ? this.props.content.success_text : this.props.content.error_text;
const onButtonClick = !this.state.signupSuccess ? this.expandSnippet : null;
return react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_SimpleSnippet_SimpleSnippet__WEBPACK_IMPORTED_MODULE_1__["SimpleSnippet"], { className: this.props.className,
onButtonClick: onButtonClick,
provider: this.props.provider,
content: { button_label: this.props.content.scene1_button_label, text: message } });
}
renderSignupView() {
const { content } = this.props;
return react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(
_components_SnippetBase_SnippetBase__WEBPACK_IMPORTED_MODULE_2__["SnippetBase"],
_extends({}, this.props, { className: "SubmitFormSnippet", footerDismiss: true }),
react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(
"div",
{ className: "message" },
react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(
"p",
null,
content.scene2_text
)
),
react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(
"form",
{ action: content.form_action, method: this.props.form_method, onSubmit: this.handleSubmit, ref: "form" },
this.renderHiddenFormInputs(),
react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(
"div",
null,
react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("input", { type: "email", name: "email", required: "required", placeholder: content.scene2_email_placeholder_text, autoFocus: true }),
react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(
"button",
{ type: "submit", className: "ASRouterButton primary", ref: "formSubmitBtn" },
content.scene2_button_label
)
),
this.renderFormPrivacyNotice()
)
);
}
getFirstSceneContent() {
return Object.keys(this.props.content).filter(key => key.includes("scene1")).reduce((acc, key) => {
acc[key.substr(7)] = this.props.content[key];
return acc;
}, {});
}
render() {
const content = Object.assign({}, this.props.content, this.getFirstSceneContent());
if (this.state.signupSubmitted) {
return this.renderSignupSubmitted();
}
if (this.state.expanded) {
return this.renderSignupView();
}
return react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_SimpleSnippet_SimpleSnippet__WEBPACK_IMPORTED_MODULE_1__["SimpleSnippet"], _extends({}, this.props, { content: content, onButtonClick: this.expandSnippet }));
}
}
/***/ }),
/* 14 */
@ -8789,6 +8813,138 @@ var reducers = { TopSites, App, ASRouter, Snippets, Prefs, Dialog, Sections, Poc
var external_React_ = __webpack_require__(5);
var external_React_default = /*#__PURE__*/__webpack_require__.n(external_React_);
// CONCATENATED MODULE: ./content-src/asrouter/components/Button/Button.jsx
const ALLOWED_STYLE_TAGS = ["color", "backgroundColor"];
const Button = props => {
const style = {};
// Add allowed style tags from props, e.g. props.color becomes style={color: props.color}
for (const tag of ALLOWED_STYLE_TAGS) {
if (typeof props[tag] !== "undefined") {
style[tag] = props[tag];
}
}
// remove border if bg is set to something custom
if (style.backgroundColor) {
style.border = "0";
}
return external_React_default.a.createElement(
"button",
{ onClick: props.onClick,
className: props.className || "ASRouterButton",
style: style },
props.children
);
};
// EXTERNAL MODULE: ./content-src/asrouter/template-utils.js
var template_utils = __webpack_require__(11);
// EXTERNAL MODULE: ./content-src/asrouter/components/SnippetBase/SnippetBase.jsx
var SnippetBase = __webpack_require__(12);
// CONCATENATED MODULE: ./content-src/asrouter/templates/SimpleSnippet/SimpleSnippet.jsx
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SimpleSnippet", function() { return SimpleSnippet_SimpleSnippet; });
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
const DEFAULT_ICON_PATH = "chrome://branding/content/icon64.png";
class SimpleSnippet_SimpleSnippet extends external_React_default.a.PureComponent {
constructor(props) {
super(props);
this.onButtonClick = this.onButtonClick.bind(this);
}
onButtonClick() {
if (this.props.provider !== "preview") {
this.props.sendUserActionTelemetry({ event: "CLICK_BUTTON", id: this.props.UISurface });
}
this.props.onAction({
type: this.props.content.button_action,
data: { args: this.props.content.button_action_args }
});
if (!this.props.content.do_not_autoblock) {
this.props.onBlock();
}
}
renderTitle() {
const { title } = this.props.content;
return title ? external_React_default.a.createElement(
"h3",
{ className: "title" },
title
) : null;
}
renderTitleIcon() {
const titleIcon = Object(template_utils["safeURI"])(this.props.content.title_icon);
return titleIcon ? external_React_default.a.createElement("span", { className: "titleIcon", style: { backgroundImage: `url("${titleIcon}")` } }) : null;
}
renderButton() {
const { props } = this;
if (!props.content.button_action && !props.onButtonClick) {
return null;
}
return external_React_default.a.createElement(
Button,
{
onClick: props.onButtonClick || this.onButtonClick,
color: props.content.button_color,
backgroundColor: props.content.button_background_color },
props.content.button_label
);
}
render() {
const { props } = this;
const className = `SimpleSnippet${props.content.tall ? " tall" : ""}`;
return external_React_default.a.createElement(
SnippetBase["SnippetBase"],
_extends({}, props, { className: className }),
external_React_default.a.createElement("img", { src: Object(template_utils["safeURI"])(props.content.icon) || DEFAULT_ICON_PATH, className: "icon" }),
external_React_default.a.createElement(
"div",
null,
this.renderTitleIcon(),
" ",
this.renderTitle(),
" ",
external_React_default.a.createElement(
"p",
{ className: "body" },
props.richText || props.content.text
)
),
external_React_default.a.createElement(
"div",
null,
this.renderButton()
)
);
}
}
/***/ }),
/* 48 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
// EXTERNAL MODULE: external "React"
var external_React_ = __webpack_require__(5);
var external_React_default = /*#__PURE__*/__webpack_require__.n(external_React_);
// CONCATENATED MODULE: ./content-src/asrouter/components/ModalOverlay/ModalOverlay.jsx
@ -8923,138 +9079,6 @@ class OnboardingMessage_OnboardingMessage extends external_React_default.a.PureC
}
}
/***/ }),
/* 48 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
// EXTERNAL MODULE: external "React"
var external_React_ = __webpack_require__(5);
var external_React_default = /*#__PURE__*/__webpack_require__.n(external_React_);
// CONCATENATED MODULE: ./content-src/asrouter/components/Button/Button.jsx
const ALLOWED_STYLE_TAGS = ["color", "backgroundColor"];
const Button = props => {
const style = {};
// Add allowed style tags from props, e.g. props.color becomes style={color: props.color}
for (const tag of ALLOWED_STYLE_TAGS) {
if (typeof props[tag] !== "undefined") {
style[tag] = props[tag];
}
}
// remove border if bg is set to something custom
if (style.backgroundColor) {
style.border = "0";
}
return external_React_default.a.createElement(
"button",
{ onClick: props.onClick,
className: props.className || "ASRouterButton",
style: style },
props.children
);
};
// EXTERNAL MODULE: ./content-src/asrouter/template-utils.js
var template_utils = __webpack_require__(11);
// EXTERNAL MODULE: ./content-src/asrouter/components/SnippetBase/SnippetBase.jsx
var SnippetBase = __webpack_require__(12);
// CONCATENATED MODULE: ./content-src/asrouter/templates/SimpleSnippet/SimpleSnippet.jsx
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SimpleSnippet", function() { return SimpleSnippet_SimpleSnippet; });
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
const DEFAULT_ICON_PATH = "chrome://branding/content/icon64.png";
class SimpleSnippet_SimpleSnippet extends external_React_default.a.PureComponent {
constructor(props) {
super(props);
this.onButtonClick = this.onButtonClick.bind(this);
}
onButtonClick() {
if (this.props.provider !== "preview") {
this.props.sendUserActionTelemetry({ event: "CLICK_BUTTON", id: this.props.UISurface });
}
this.props.onAction({
type: this.props.content.button_action,
data: { args: this.props.content.button_action_args }
});
if (!this.props.content.do_not_autoblock) {
this.props.onBlock();
}
}
renderTitle() {
const { title } = this.props.content;
return title ? external_React_default.a.createElement(
"h3",
{ className: "title" },
title
) : null;
}
renderTitleIcon() {
const titleIcon = Object(template_utils["safeURI"])(this.props.content.title_icon);
return titleIcon ? external_React_default.a.createElement("span", { className: "titleIcon", style: { backgroundImage: `url("${titleIcon}")` } }) : null;
}
renderButton() {
const { props } = this;
if (!props.content.button_action && !props.onButtonClick) {
return null;
}
return external_React_default.a.createElement(
Button,
{
onClick: props.onButtonClick || this.onButtonClick,
color: props.content.button_color,
backgroundColor: props.content.button_background_color },
props.content.button_label
);
}
render() {
const { props } = this;
const className = `SimpleSnippet${props.content.tall ? " tall" : ""}`;
return external_React_default.a.createElement(
SnippetBase["SnippetBase"],
_extends({}, props, { className: className }),
external_React_default.a.createElement("img", { src: Object(template_utils["safeURI"])(props.content.icon) || DEFAULT_ICON_PATH, className: "icon" }),
external_React_default.a.createElement(
"div",
null,
this.renderTitleIcon(),
" ",
this.renderTitle(),
" ",
external_React_default.a.createElement(
"p",
{ className: "body" },
props.richText || props.content.text
)
),
external_React_default.a.createElement(
"div",
null,
this.renderButton()
)
);
}
}
/***/ }),
/* 49 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

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

@ -892,7 +892,7 @@ class _ASRouter {
UITour.showMenu(target.browser.ownerGlobal, action.data.args);
break;
case ra.INSTALL_ADDON_FROM_URL:
await MessageLoaderUtils.installAddonFromURL(target.browser, action.data.args);
await MessageLoaderUtils.installAddonFromURL(target.browser, action.data.url);
break;
case ra.SHOW_FIREFOX_ACCOUNTS:
const url = await FxAccounts.config.promiseSignUpURI("snippets");

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

@ -140,6 +140,10 @@ const TargetingGetters = {
totalDevices: Services.prefs.getIntPref("services.sync.numClients", 0),
};
},
get xpinstallEnabled() {
// This is needed for all add-on recommendations, to know if we allow xpi installs in the first place
return Services.prefs.getBoolPref("xpinstall.enabled", true);
},
get addonsInfo() {
return AddonManager.getActiveAddons(["extension", "service"])
.then(({addons, fullData}) => {

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

@ -182,7 +182,7 @@ const PREFS_CONFIG = new Map([
} else {
searchShortcuts.push("google");
}
if (["DE", "FR", "GB", "IT", "JP", "US"].includes(geo)) {
if (["AT", "DE", "FR", "GB", "IT", "JP", "US"].includes(geo)) {
searchShortcuts.push("amazon");
}
return searchShortcuts.join(",");
@ -209,7 +209,7 @@ const PREFS_CONFIG = new Map([
id: "onboarding",
type: "local",
localProvider: "OnboardingMessageProvider",
enabled: false,
enabled: true,
}, {
id: "snippets",
type: "remote",

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

@ -84,6 +84,7 @@ const CFR_MESSAGES = [
targeting: `
localeLanguageCode == "en" &&
(providerCohorts.cfr == "one_per_day_amazon") &&
(xpinstallEnabled == true) &&
(${JSON.stringify(AMAZON_ASSISTANT_PARAMS.existing_addons)} intersect addonsInfo.addons|keys)|length == 0 &&
(${JSON.stringify(AMAZON_ASSISTANT_PARAMS.open_urls)} intersect topFrecentSites[.frecency >= ${AMAZON_ASSISTANT_PARAMS.min_frecency}]|mapToProperty('host'))|length > 0`,
trigger: {id: "openURL", params: AMAZON_ASSISTANT_PARAMS.open_urls},
@ -127,6 +128,7 @@ const CFR_MESSAGES = [
targeting: `
localeLanguageCode == "en" &&
(providerCohorts.cfr == "three_per_day_amazon") &&
(xpinstallEnabled == true) &&
(${JSON.stringify(AMAZON_ASSISTANT_PARAMS.existing_addons)} intersect addonsInfo.addons|keys)|length == 0 &&
(${JSON.stringify(AMAZON_ASSISTANT_PARAMS.open_urls)} intersect topFrecentSites[.frecency >= ${AMAZON_ASSISTANT_PARAMS.min_frecency}]|mapToProperty('host'))|length > 0`,
trigger: {id: "openURL", params: AMAZON_ASSISTANT_PARAMS.open_urls},
@ -170,6 +172,7 @@ const CFR_MESSAGES = [
targeting: `
localeLanguageCode == "en" &&
(providerCohorts.cfr in ["one_per_day", "nightly"]) &&
(xpinstallEnabled == true) &&
(${JSON.stringify(FACEBOOK_CONTAINER_PARAMS.existing_addons)} intersect addonsInfo.addons|keys)|length == 0 &&
(${JSON.stringify(FACEBOOK_CONTAINER_PARAMS.open_urls)} intersect topFrecentSites[.frecency >= ${FACEBOOK_CONTAINER_PARAMS.min_frecency}]|mapToProperty('host'))|length > 0`,
trigger: {id: "openURL", params: FACEBOOK_CONTAINER_PARAMS.open_urls},
@ -213,6 +216,7 @@ const CFR_MESSAGES = [
targeting: `
localeLanguageCode == "en" &&
(providerCohorts.cfr == "three_per_day") &&
(xpinstallEnabled == true) &&
(${JSON.stringify(FACEBOOK_CONTAINER_PARAMS.existing_addons)} intersect addonsInfo.addons|keys)|length == 0 &&
(${JSON.stringify(FACEBOOK_CONTAINER_PARAMS.open_urls)} intersect topFrecentSites[.frecency >= ${FACEBOOK_CONTAINER_PARAMS.min_frecency}]|mapToProperty('host'))|length > 0`,
trigger: {id: "openURL", params: FACEBOOK_CONTAINER_PARAMS.open_urls},
@ -256,6 +260,7 @@ const CFR_MESSAGES = [
targeting: `
localeLanguageCode == "en" &&
(providerCohorts.cfr in ["one_per_day", "nightly"]) &&
(xpinstallEnabled == true) &&
(${JSON.stringify(GOOGLE_TRANSLATE_PARAMS.existing_addons)} intersect addonsInfo.addons|keys)|length == 0 &&
(${JSON.stringify(GOOGLE_TRANSLATE_PARAMS.open_urls)} intersect topFrecentSites[.frecency >= ${GOOGLE_TRANSLATE_PARAMS.min_frecency}]|mapToProperty('host'))|length > 0`,
trigger: {id: "openURL", params: GOOGLE_TRANSLATE_PARAMS.open_urls},
@ -299,6 +304,7 @@ const CFR_MESSAGES = [
targeting: `
localeLanguageCode == "en" &&
(providerCohorts.cfr == "three_per_day") &&
(xpinstallEnabled == true) &&
(${JSON.stringify(GOOGLE_TRANSLATE_PARAMS.existing_addons)} intersect addonsInfo.addons|keys)|length == 0 &&
(${JSON.stringify(GOOGLE_TRANSLATE_PARAMS.open_urls)} intersect topFrecentSites[.frecency >= ${GOOGLE_TRANSLATE_PARAMS.min_frecency}]|mapToProperty('host'))|length > 0`,
trigger: {id: "openURL", params: GOOGLE_TRANSLATE_PARAMS.open_urls},
@ -342,6 +348,7 @@ const CFR_MESSAGES = [
targeting: `
localeLanguageCode == "en" &&
(providerCohorts.cfr in ["one_per_day", "nightly"]) &&
(xpinstallEnabled == true) &&
(${JSON.stringify(YOUTUBE_ENHANCE_PARAMS.existing_addons)} intersect addonsInfo.addons|keys)|length == 0 &&
(${JSON.stringify(YOUTUBE_ENHANCE_PARAMS.open_urls)} intersect topFrecentSites[.frecency >= ${YOUTUBE_ENHANCE_PARAMS.min_frecency}]|mapToProperty('host'))|length > 0`,
trigger: {id: "openURL", params: YOUTUBE_ENHANCE_PARAMS.open_urls},
@ -385,6 +392,7 @@ const CFR_MESSAGES = [
targeting: `
localeLanguageCode == "en" &&
(providerCohorts.cfr == "three_per_day") &&
(xpinstallEnabled == true) &&
(${JSON.stringify(YOUTUBE_ENHANCE_PARAMS.existing_addons)} intersect addonsInfo.addons|keys)|length == 0 &&
(${JSON.stringify(YOUTUBE_ENHANCE_PARAMS.open_urls)} intersect topFrecentSites[.frecency >= ${YOUTUBE_ENHANCE_PARAMS.min_frecency}]|mapToProperty('host'))|length > 0`,
trigger: {id: "openURL", params: YOUTUBE_ENHANCE_PARAMS.open_urls},
@ -428,6 +436,7 @@ const CFR_MESSAGES = [
targeting: `
localeLanguageCode == "en" &&
(providerCohorts.cfr in ["one_per_day", "nightly"]) &&
(xpinstallEnabled == true) &&
(${JSON.stringify(WIKIPEDIA_CONTEXT_MENU_SEARCH_PARAMS.existing_addons)} intersect addonsInfo.addons|keys)|length == 0 &&
(${JSON.stringify(WIKIPEDIA_CONTEXT_MENU_SEARCH_PARAMS.open_urls)} intersect topFrecentSites[.frecency >= ${WIKIPEDIA_CONTEXT_MENU_SEARCH_PARAMS.min_frecency}]|mapToProperty('host'))|length > 0`,
trigger: {id: "openURL", params: WIKIPEDIA_CONTEXT_MENU_SEARCH_PARAMS.open_urls},
@ -471,6 +480,7 @@ const CFR_MESSAGES = [
targeting: `
localeLanguageCode == "en" &&
(providerCohorts.cfr == "three_per_day") &&
(xpinstallEnabled == true) &&
(${JSON.stringify(WIKIPEDIA_CONTEXT_MENU_SEARCH_PARAMS.existing_addons)} intersect addonsInfo.addons|keys)|length == 0 &&
(${JSON.stringify(WIKIPEDIA_CONTEXT_MENU_SEARCH_PARAMS.open_urls)} intersect topFrecentSites[.frecency >= ${WIKIPEDIA_CONTEXT_MENU_SEARCH_PARAMS.min_frecency}]|mapToProperty('host'))|length > 0`,
trigger: {id: "openURL", params: WIKIPEDIA_CONTEXT_MENU_SEARCH_PARAMS.open_urls},
@ -514,6 +524,7 @@ const CFR_MESSAGES = [
targeting: `
localeLanguageCode == "en" &&
(providerCohorts.cfr in ["one_per_day", "nightly"]) &&
(xpinstallEnabled == true) &&
(${JSON.stringify(REDDIT_ENHANCEMENT_PARAMS.existing_addons)} intersect addonsInfo.addons|keys)|length == 0 &&
(${JSON.stringify(REDDIT_ENHANCEMENT_PARAMS.open_urls)} intersect topFrecentSites[.frecency >= ${REDDIT_ENHANCEMENT_PARAMS.min_frecency}]|mapToProperty('host'))|length > 0`,
trigger: {id: "openURL", params: REDDIT_ENHANCEMENT_PARAMS.open_urls},
@ -557,6 +568,7 @@ const CFR_MESSAGES = [
targeting: `
localeLanguageCode == "en" &&
(providerCohorts.cfr == "three_per_day") &&
(xpinstallEnabled == true) &&
(${JSON.stringify(REDDIT_ENHANCEMENT_PARAMS.existing_addons)} intersect addonsInfo.addons|keys)|length == 0 &&
(${JSON.stringify(REDDIT_ENHANCEMENT_PARAMS.open_urls)} intersect topFrecentSites[.frecency >= ${REDDIT_ENHANCEMENT_PARAMS.min_frecency}]|mapToProperty('host'))|length > 0`,
trigger: {id: "openURL", params: REDDIT_ENHANCEMENT_PARAMS.open_urls},

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

@ -124,10 +124,10 @@ class BookmarksObserver extends Observer {
class PlacesObserver extends Observer {
constructor(dispatch) {
super(dispatch, Ci.nsINavBookmarkObserver);
this.handlePlacesEvents = this.handlePlacesEvents.bind(this);
this.handlePlacesEvent = this.handlePlacesEvent.bind(this);
}
handlePlacesEvents(events) {
handlePlacesEvent(events) {
for (let {itemType, source, dateAdded, guid, title, url, isTagging} of events) {
// Skips items that are not bookmarks (like folders), about:* pages or
// default bookmarks, added when the profile is created.
@ -148,8 +148,8 @@ class PlacesObserver extends Observer {
bookmarkGuid: guid,
bookmarkTitle: title,
dateAdded: dateAdded * 1000,
url
}
url,
},
});
}
}
@ -173,7 +173,7 @@ class PlacesFeed {
.getService(Ci.nsINavBookmarksService)
.addObserver(this.bookmarksObserver, true);
PlacesUtils.observers.addListener(["bookmark-added"],
this.placesObserver.handlePlacesEvents);
this.placesObserver.handlePlacesEvent);
Services.obs.addObserver(this, LINK_BLOCKED_EVENT);
}
@ -215,7 +215,7 @@ class PlacesFeed {
PlacesUtils.history.removeObserver(this.historyObserver);
PlacesUtils.bookmarks.removeObserver(this.bookmarksObserver);
PlacesUtils.observers.removeListener(["bookmark-added"],
this.placesObserver.handlePlacesEvents);
this.placesObserver.handlePlacesEvent);
Services.obs.removeObserver(this, LINK_BLOCKED_EVENT);
}
@ -336,5 +336,6 @@ this.PlacesFeed = PlacesFeed;
// Exported for testing only
PlacesFeed.HistoryObserver = HistoryObserver;
PlacesFeed.BookmarksObserver = BookmarksObserver;
PlacesFeed.PlacesObserver = PlacesObserver;
const EXPORTED_SYMBOLS = ["PlacesFeed"];

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

@ -146,6 +146,8 @@ pocket_read_even_more=اعرض المزيد من الأخبار
pocket_more_reccommendations=مقترحات أخرى
pocket_learn_more=اطّلع على المزيد
pocket_cta_button=نزِّل بوكِت
pocket_cta_text=احفظ القصص التي تحبّها في بوكِت، وزوّد عقلك بمقالات رائعة.
highlights_empty_state=ابدأ التصفح وسنعرض أمامك بعض المقالات والفيديوهات والمواقع الأخرى التي زرتها حديثا أو أضفتها إلى العلامات هنا.
# LOCALIZATION NOTE (topstories_empty_state): When there are no recommendations,

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

@ -144,6 +144,11 @@ pocket_read_more=Temes populars:
# end of the list of popular topic links.
pocket_read_even_more=Mostra més articles
pocket_more_reccommendations=Més recomanacions
pocket_learn_more=Més informació
pocket_cta_button=Obtén el Pocket
pocket_cta_text=Deseu els vostres articles preferits al Pocket i gaudiu d'altres recomanacions fascinants.
highlights_empty_state=Comenceu a navegar i aquí us mostrarem els millors articles, vídeos i altres pàgines que hàgiu visitat o afegit a les adreces d'interès recentment.
# LOCALIZATION NOTE (topstories_empty_state): When there are no recommendations,
# in the space that would have shown a few stories, this is shown instead.

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

@ -146,6 +146,7 @@ pocket_read_even_more=Zobrazit více článků
pocket_more_reccommendations=Další doporučení
pocket_learn_more=Zjistit více
pocket_how_it_works=Jak to funguje
pocket_cta_button=Získejte Pocket
pocket_cta_text=Ukládejte si články do služby Pocket a užívejte si skvělé čtení.

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

@ -155,6 +155,12 @@ pocket_read_more=Populære emner:
# LOCALIZATION NOTE (pocket_read_even_more): This is shown as a link at the
# end of the list of popular topic links.
pocket_read_even_more=Se flere historier
pocket_more_reccommendations=Flere anbefalinger
pocket_learn_more=Læs mere
pocket_cta_button=Hent Pocket
pocket_cta_text=Gem dine yndlingshistorier i Pocket og hav dem altid ved hånden.
# LOCALIZATION NOTE (pocket_description): This is shown in the settings pane
# to provide more information about Pocket.
pocket_description=Opdag indhold af høj kvalitet, som du måske ellers ikke ville have opdaget. Indholdet kommer fra Pocket, der nu er en del af Mozilla.

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

@ -146,6 +146,7 @@ pocket_read_even_more=View More Stories
pocket_more_reccommendations=More Recommendations
pocket_learn_more=Learn More
pocket_how_it_works=How it works
pocket_cta_button=Get Pocket
pocket_cta_text=Save the stories you love in Pocket, and fuel your mind with fascinating reads.

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

@ -144,6 +144,11 @@ pocket_read_more=Ĉefaj temoj:
# end of the list of popular topic links.
pocket_read_even_more=Montri pli da artikoloj
pocket_more_reccommendations=Pli da rekomendoj
pocket_learn_more=Pli da informo
pocket_cta_button=Instali Pocket
pocket_cta_text=Konservu viajn ŝatatajn artikolojn en Pocket, kaj stimulu vian menson per ravaj legaĵoj.
highlights_empty_state=Komencu retumi kaj ĉi tie ni montros al vi kelkajn el la plej bonaj artikoloj, filmetoj kaj aliaj paĝoj, kiujn vi antaŭ nelonge vizits aŭ por kiuj vi aldonis legosignon.
# LOCALIZATION NOTE (topstories_empty_state): When there are no recommendations,
# in the space that would have shown a few stories, this is shown instead.

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

@ -146,7 +146,9 @@ pocket_read_even_more=Ahechaseve Mombe'upy
pocket_more_reccommendations=Hetave jeeporã
pocket_learn_more=Kuaave
pocket_how_it_works=Mbaéichapa ombaapo
pocket_cta_button=Eguereko Pocket
pocket_cta_text=Eñongatu umi eipotáva tembiasakue Pocket-pe ha emombarete ne akã ñemoñeẽ haevévape.
highlights_empty_state=Eñepyrũ eikundaha ha rohechaukáta ndéve mba'ehai, mba'erecharã oĩva ha ambue ñandutirenda reikeva'ekue ýrõ rembotechaukava'ekue.
# LOCALIZATION NOTE (topstories_empty_state): When there are no recommendations,

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

@ -64,7 +64,7 @@ menu_action_remove_download=इतिहास से हटाएँ
# LOCALIZATION NOTE (search_button): This is screenreader only text for the
# search button.
search_button=खोज
search_button=खोजें
# LOCALIZATION NOTE (search_header): Displayed at the top of the panel
# showing search suggestions. {search_engine_name} is replaced with the name of
@ -78,7 +78,7 @@ search_web_placeholder=वेब पर खोजें
# LOCALIZATION NOTE (section_disclaimer_topstories): This is shown below
# the topstories section title to provide additional information about
# how the stories are selected.
section_disclaimer_topstories=वेब पर सबसे दिलचस्प कहानियाँ, आपके पठन के आधार पर चयनित. Pocket के द्वारा, जो अब है Mozilla का हिस्सा.
section_disclaimer_topstories=वेब पर सबसे दिलचस्प कहानियाँ, आपके पढने के आधार पर चयनित। Pocket के द्वारा, जो अब Mozilla का हिस्सा है।
section_disclaimer_topstories_linktext=जाने यह कैसे काम करता है.
# LOCALIZATION NOTE (section_disclaimer_topstories_buttontext): The text of
# the button used to acknowledge, and hide this disclaimer in the future.
@ -144,6 +144,8 @@ pocket_read_more=लोकप्रिय विषय:
# end of the list of popular topic links.
pocket_read_even_more=और कहानियाँ देखें
pocket_learn_more=अधिक जानें
highlights_empty_state=ब्राउज़िंग प्रारंभ करें, और हम कुछ प्रमुख आलेख, विडियो, तथा अन्य पृष्ठों को प्रदर्शित करेंगे जिन्हें आपने हाल ही में देखा या पुस्तचिन्हित किया है.
# LOCALIZATION NOTE (topstories_empty_state): When there are no recommendations,
# in the space that would have shown a few stories, this is shown instead.

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

@ -145,6 +145,7 @@ pocket_read_more=Argomenti popolari:
pocket_read_even_more=Visualizza altre storie
pocket_more_reccommendations = Altri suggerimenti
pocket_learn_more = Ulteriori informazioni
pocket_how_it_works = Come funziona
pocket_cta_button = Ottieni Pocket
pocket_cta_text = Salva le storie che ami in Pocket e nutri la tua mente con letture appassionanti.

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

@ -147,6 +147,7 @@ pocket_read_even_more=Көбірек хикаяларды қарау
pocket_more_reccommendations=Көбірек ұсыныстар
pocket_learn_more=Көбірек білу
pocket_cta_button=Pocket-ті алу
pocket_cta_text=Өзіңіз ұнатқан хикаяларды Pocket ішіне сақтап, миіңізді тамаша оқумен толықтырыңыз.
highlights_empty_state=Шолуды бастаңыз, сіз жақында шолған немесе бетбелгілерге қосқан тамаша мақалалар, видеолар немесе басқа парақтардың кейбіреулері осында көрсетіледі.
# LOCALIZATION NOTE (topstories_empty_state): When there are no recommendations,

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

@ -144,6 +144,11 @@ pocket_read_more=Populārās tēmas:
# end of the list of popular topic links.
pocket_read_even_more=Parādīt vairāk lapas
pocket_more_reccommendations=Vairāk ieteikumu
pocket_learn_more=Uzzināt vairāk
pocket_cta_button=Izmēģiniet Pocket
pocket_cta_text=Saglabājiet interesantus stāstus Pocket un barojiet savu prātu ar interesantu lasāmvielu.
highlights_empty_state=Sāciet pārlūkošanu un mēs šeit parādīsim lieliskus rakstus, video un citas apmeklētās lapas.
# LOCALIZATION NOTE (topstories_empty_state): When there are no recommendations,
# in the space that would have shown a few stories, this is shown instead.
@ -173,6 +178,7 @@ section_menu_action_expand_section=Izvērst sadaļu
section_menu_action_manage_section=Pārvaldīt sadaļu
section_menu_action_manage_webext=Pārvaldīt paplašinājumu
section_menu_action_add_topsite=Pievienot populāru lapu
section_menu_action_add_search_engine=Pievienot meklētāju
section_menu_action_move_up=Pārvietot augšup
section_menu_action_move_down=Pārvietot lejup
section_menu_action_privacy_notice=Privātuma politika
@ -201,4 +207,3 @@ firstrun_privacy_notice=Privātuma politikai
firstrun_continue_to_login=Turpināt
firstrun_skip_login=Izlaist šo soli
section_menu_action_add_search_engine=Pievienot meklētāju

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

@ -146,6 +146,7 @@ pocket_read_even_more=Papar Kisah Selanjutnya
pocket_more_reccommendations=Saranan Lain
pocket_learn_more=Ketahui Selanjutnya
pocket_how_it_works=Cara pelaksanaan
pocket_cta_button=Dapatkan Pocket
pocket_cta_text=Simpan cerita yang anda suka dalam Pocket dan jana minda dengan bahan bacaan yang menarik.

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

@ -147,6 +147,7 @@ pocket_read_even_more=Vis flere saker
pocket_more_reccommendations=Flere anbefalinger
pocket_learn_more=Les mer
pocket_cta_button=Hent Pocket
pocket_cta_text=Lagre artiklene du synes er interessante i Pocket, og stimuler dine tanker med fasinerende lesermateriell.
highlights_empty_state=Begynn å surfe, og vi viser noen av de beste artiklene, videoer og andre sider du nylig har besøkt eller bokmerket her.
# LOCALIZATION NOTE (topstories_empty_state): When there are no recommendations,

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

@ -146,6 +146,7 @@ pocket_read_even_more=Meer verhalen bekijken
pocket_more_reccommendations=Meer aanbevelingen
pocket_learn_more=Meer info
pocket_how_it_works=Hoe het werkt
pocket_cta_button=Pocket gebruiken
pocket_cta_text=Bewaar de verhalen die u interessant vindt in Pocket, en stimuleer uw gedachten met boeiende leesstof.

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

@ -147,6 +147,7 @@ pocket_read_even_more=Vis fleire saker
pocket_more_reccommendations=Fleire tilrådingar
pocket_learn_more=Les meir
pocket_cta_button=Last ned Pocket
pocket_cta_text=Lagre artiklane du synest er interessante i Pocket, og stimuler tankane dine med fasinerande lesemateriell.
highlights_empty_state=Begynn å surfe, og vi vil vise deg nokre av dei beste artiklane, videoane og andre sider du nyleg har besøkt eller bokmerka her.
# LOCALIZATION NOTE (topstories_empty_state): When there are no recommendations,

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

@ -146,6 +146,7 @@ pocket_read_even_more=Ver mais histórias
pocket_more_reccommendations=Mais recomendações
pocket_learn_more=Saber mais
pocket_how_it_works=Como funciona
pocket_cta_button=Obter o Pocket
pocket_cta_text=Guarde as histórias que adora no Pocket, e abasteça a sua mente com leituras fascinantes.

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

@ -36,11 +36,11 @@ menu_action_dismiss=Înlătură
menu_action_delete=Șterge din istoric
menu_action_pin=Fixează
menu_action_unpin=Anulează fixarea
confirm_history_delete_p1=Sigur vrei să ştergi fiecare instanţă a acestei pagini din istoric?
confirm_history_delete_p1=Sigur vrei să ștergi fiecare instanță a paginii din istoric?
# LOCALIZATION NOTE (confirm_history_delete_notice_p2): this string is displayed in
# the same dialog as confirm_history_delete_p1. "This action" refers to deleting a
# page from history.
confirm_history_delete_notice_p2=Această acțiune este ireversibilă.
confirm_history_delete_notice_p2=Acțiunea este ireversibilă.
menu_action_save_to_pocket=Salvează în Pocket
menu_action_delete_pocket=Şterge din Pocket
menu_action_archive_pocket=Arhivează în Pocket

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

@ -146,6 +146,8 @@ pocket_read_even_more=Prikaži več vesti
pocket_more_reccommendations=Več priporočil
pocket_learn_more=Več o tem
pocket_cta_button=Prenesi Pocket
pocket_cta_text=Shranite zgodbe, ki jih imate radi, v Pocket, in napolnite svoje misli z navdušujočim branjem.
highlights_empty_state=Začnite z brskanjem, mi pa vam bomo tu prikazovali odlične članke, videoposnetke ter druge strani, ki ste jih nedavno obiskali ali shranili med zaznamke.
# LOCALIZATION NOTE (topstories_empty_state): When there are no recommendations,

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

@ -146,6 +146,7 @@ pocket_read_even_more=Visa fler nyheter
pocket_more_reccommendations=Fler rekommendationer
pocket_learn_more=Läs mer
pocket_how_it_works=Hur fungerar det
pocket_cta_button=Hämta Pocket
pocket_cta_text=Spara de historier som du tycker är intressant i Pocket, och stimulera dina tankar med fascinerande läsmaterial.

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

@ -146,6 +146,7 @@ pocket_read_even_more=Daha fazla yazı göster
pocket_more_reccommendations=Daha fazla öneri
pocket_learn_more=Daha fazla bilgi al
pocket_how_it_works=Nasıl çalışıyor?
pocket_cta_button=Pocketı edinin
pocket_cta_text=Sevdiğiniz yazıları Pocketa kaydedin, aklınız okumaya değer şeylerle doldurun.

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

@ -146,6 +146,7 @@ pocket_read_even_more=檢視更多文章
pocket_more_reccommendations=更多推薦項目
pocket_learn_more=了解更多
pocket_how_it_works=原理是什麼
pocket_cta_button=取得 Pocket
pocket_cta_text=將您喜愛的故事儲存到 Pocket閱讀一篇篇好文章。

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

@ -76,8 +76,8 @@ window.gActivityStreamStrings = {
"pocket_read_even_more": "اعرض المزيد من الأخبار",
"pocket_more_reccommendations": "مقترحات أخرى",
"pocket_how_it_works": "How it works",
"pocket_cta_button": "Get Pocket",
"pocket_cta_text": "Save the stories you love in Pocket, and fuel your mind with fascinating reads.",
"pocket_cta_button": "نزِّل بوكِت",
"pocket_cta_text": "احفظ القصص التي تحبّها في بوكِت، وزوّد عقلك بمقالات رائعة.",
"highlights_empty_state": "ابدأ التصفح وسنعرض أمامك بعض المقالات والفيديوهات والمواقع الأخرى التي زرتها حديثا أو أضفتها إلى العلامات هنا.",
"topstories_empty_state": "لا جديد. تحقق لاحقًا للحصول على مزيد من أهم الأخبار من {provider}. لا يمكنك الانتظار؟ اختر موضوعًا شائعًا للعثور على المزيد من القصص الرائعة من جميع أنحاء الوِب.",
"manual_migration_explanation2": "جرب فَيَرفُكس مع العلامات، و التأريخ، و كلمات السر من متصفح آخر.",

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

@ -74,10 +74,10 @@ window.gActivityStreamStrings = {
"topsites_form_image_validation": "S'ha produït un error en carregar la imatge. Proveu un altre URL.",
"pocket_read_more": "Temes populars:",
"pocket_read_even_more": "Mostra més articles",
"pocket_more_reccommendations": "More Recommendations",
"pocket_more_reccommendations": "Més recomanacions",
"pocket_how_it_works": "How it works",
"pocket_cta_button": "Get Pocket",
"pocket_cta_text": "Save the stories you love in Pocket, and fuel your mind with fascinating reads.",
"pocket_cta_button": "Obtén el Pocket",
"pocket_cta_text": "Deseu els vostres articles preferits al Pocket i gaudiu d'altres recomanacions fascinants.",
"highlights_empty_state": "Comenceu a navegar i aquí us mostrarem els millors articles, vídeos i altres pàgines que hàgiu visitat o afegit a les adreces d'interès recentment.",
"topstories_empty_state": "Ja esteu al dia. Torneu més tard per veure més articles populars de {provider}. No podeu esperar? Trieu un tema popular per descobrir els articles més interessants de tot el web.",
"manual_migration_explanation2": "Proveu el Firefox amb les adreces d'interès, l'historial i les contrasenyes d'un altre navegador.",
@ -106,5 +106,6 @@ window.gActivityStreamStrings = {
"firstrun_terms_of_service": "Condicions del servei",
"firstrun_privacy_notice": "Avís de privadesa",
"firstrun_continue_to_login": "Continua",
"firstrun_skip_login": "Omet aquest pas"
"firstrun_skip_login": "Omet aquest pas",
"pocket_learn_more": "Més informació"
};

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

@ -75,7 +75,7 @@ window.gActivityStreamStrings = {
"pocket_read_more": "Populární témata:",
"pocket_read_even_more": "Zobrazit více článků",
"pocket_more_reccommendations": "Další doporučení",
"pocket_how_it_works": "How it works",
"pocket_how_it_works": "Jak to funguje",
"pocket_cta_button": "Získejte Pocket",
"pocket_cta_text": "Ukládejte si články do služby Pocket a užívejte si skvělé čtení.",
"highlights_empty_state": "Začněte prohlížet a my vám zde ukážeme některé skvělé články, videa a další stránky, které jste nedávno viděli nebo uložili do záložek.",

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

@ -74,10 +74,10 @@ window.gActivityStreamStrings = {
"topsites_form_image_validation": "Kunne ikke indlæse billede. Prøv en anden URL.",
"pocket_read_more": "Populære emner:",
"pocket_read_even_more": "Se flere historier",
"pocket_more_reccommendations": "More Recommendations",
"pocket_more_reccommendations": "Flere anbefalinger",
"pocket_how_it_works": "How it works",
"pocket_cta_button": "Get Pocket",
"pocket_cta_text": "Save the stories you love in Pocket, and fuel your mind with fascinating reads.",
"pocket_cta_button": "Hent Pocket",
"pocket_cta_text": "Gem dine yndlingshistorier i Pocket og hav dem altid ved hånden.",
"highlights_empty_state": "Gå i gang med at browse, så vil vi vise dig nogle af de artikler, videoer og andre sider, du har besøgt eller gemt et bogmærke til for nylig.",
"topstories_empty_state": "Der er ikke flere nye historier. Kom tilbage senere for at se flere tophistorier fra {provider}. Kan du ikke vente? Vælg et populært emne og find flere spændende historier fra hele verden.",
"manual_migration_explanation2": "Prøv Firefox med bogmærkerne, historikken og adgangskoderne fra en anden browser.",
@ -119,5 +119,6 @@ window.gActivityStreamStrings = {
"settings_pane_snippets_body": "Læs korte opdateringer fra Mozilla om Firefox, internet-kultur og lidt underholdning fra tid til anden.",
"settings_pane_done_button": "Færdig",
"settings_pane_topstories_options_sponsored": "Vis sponsorerede historier",
"pocket_learn_more": "Læs mere",
"pocket_description": "Opdag indhold af høj kvalitet, som du måske ellers ikke ville have opdaget. Indholdet kommer fra Pocket, der nu er en del af Mozilla."
};

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

@ -74,10 +74,10 @@ window.gActivityStreamStrings = {
"topsites_form_image_validation": "Ne eblis ŝargi la bildon. Klopodu alian retadreson.",
"pocket_read_more": "Ĉefaj temoj:",
"pocket_read_even_more": "Montri pli da artikoloj",
"pocket_more_reccommendations": "More Recommendations",
"pocket_more_reccommendations": "Pli da rekomendoj",
"pocket_how_it_works": "How it works",
"pocket_cta_button": "Get Pocket",
"pocket_cta_text": "Save the stories you love in Pocket, and fuel your mind with fascinating reads.",
"pocket_cta_button": "Instali Pocket",
"pocket_cta_text": "Konservu viajn ŝatatajn artikolojn en Pocket, kaj stimulu vian menson per ravaj legaĵoj.",
"highlights_empty_state": "Komencu retumi kaj ĉi tie ni montros al vi kelkajn el la plej bonaj artikoloj, filmetoj kaj aliaj paĝoj, kiujn vi antaŭ nelonge vizits aŭ por kiuj vi aldonis legosignon.",
"topstories_empty_state": "Vi legis ĉion. Kontrolu denove poste ĉu estas pli da novaĵon de {provider}. Ĉu vi ne povas atendi? Elektu popularan temon por trovi pli da interesaj artikoloj en la tuta teksaĵo.",
"manual_migration_explanation2": "Provu Firefox kun la legosignoj, historio kaj pasvortoj de alia retumilo.",
@ -106,5 +106,6 @@ window.gActivityStreamStrings = {
"firstrun_terms_of_service": "kondiĉojn de uzo",
"firstrun_privacy_notice": "rimarkon pri privateco",
"firstrun_continue_to_login": "Daŭrigi",
"firstrun_skip_login": "Pretersalti tiun ĉi paŝon"
"firstrun_skip_login": "Pretersalti tiun ĉi paŝon",
"pocket_learn_more": "Pli da informo"
};

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

@ -75,9 +75,9 @@ window.gActivityStreamStrings = {
"pocket_read_more": "Ñe'ẽmbyrã Ojehayhuvéva:",
"pocket_read_even_more": "Ahechaseve Mombe'upy",
"pocket_more_reccommendations": "Hetave jeeporã",
"pocket_how_it_works": "How it works",
"pocket_how_it_works": "Mbaéichapa ombaapo",
"pocket_cta_button": "Eguereko Pocket",
"pocket_cta_text": "Save the stories you love in Pocket, and fuel your mind with fascinating reads.",
"pocket_cta_text": "Eñongatu umi eipotáva tembiasakue Pocket-pe ha emombarete ne akã ñemoñeẽ haevévape.",
"highlights_empty_state": "Eñepyrũ eikundaha ha rohechaukáta ndéve mba'ehai, mba'erecharã oĩva ha ambue ñandutirenda reikeva'ekue ýrõ rembotechaukava'ekue.",
"topstories_empty_state": "Ko'ág̃a reikuaapáma ipyahúva. Eikejey ag̃ave ápe eikuaávo mombe'upy pyahu {provider} oikuave'ẽva ndéve. Ndaikatuvéima reha'ãrõ? Eiporavo peteĩ ñe'ẽmbyrã ha emoñe'ẽve oĩvéva ñande yvy ape ári.",
"manual_migration_explanation2": "Eipuru Firefox reheve techaukaha, tembiasakue ha ñe'ẽñemi ambue kundaharapegua.",

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

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

@ -32,10 +32,10 @@ window.gActivityStreamStrings = {
"menu_action_copy_download_link": "डाउनलोड लिंक कॉपी करें",
"menu_action_go_to_download_page": "डाउनलोड पृष्ठ पर जाएं",
"menu_action_remove_download": "इतिहास से हटाएँ",
"search_button": "खोज",
"search_button": "खोजें",
"search_header": "{search_engine_name} खोज",
"search_web_placeholder": "वेब पर खोजें",
"section_disclaimer_topstories": "वेब पर सबसे दिलचस्प कहानियाँ, आपके पठन के आधार पर चयनित. Pocket के द्वारा, जो अब है Mozilla का हिस्सा.",
"section_disclaimer_topstories": "वेब पर सबसे दिलचस्प कहानियाँ, आपके पढने के आधार पर चयनित। Pocket के द्वारा, जो अब Mozilla का हिस्सा है।",
"section_disclaimer_topstories_linktext": "जाने यह कैसे काम करता है.",
"section_disclaimer_topstories_buttontext": "ठीक है, समझ गए",
"prefs_home_header": "Firefox होम सामग्री",
@ -106,5 +106,6 @@ window.gActivityStreamStrings = {
"firstrun_terms_of_service": "सेवा की शर्तें",
"firstrun_privacy_notice": "गोपनीयता नीति",
"firstrun_continue_to_login": "जारी रखें",
"firstrun_skip_login": "इस चरण को छोड़ दें"
"firstrun_skip_login": "इस चरण को छोड़ दें",
"pocket_learn_more": "अधिक जानें"
};

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

@ -75,7 +75,7 @@ window.gActivityStreamStrings = {
"pocket_read_more": "Argomenti popolari:",
"pocket_read_even_more": "Visualizza altre storie",
"pocket_more_reccommendations": "Altri suggerimenti",
"pocket_how_it_works": "How it works",
"pocket_how_it_works": "Come funziona",
"pocket_cta_button": "Ottieni Pocket",
"pocket_cta_text": "Salva le storie che ami in Pocket e nutri la tua mente con letture appassionanti.",
"highlights_empty_state": "Inizia a navigare e, in questa sezione, verranno visualizzati articoli, video e altre pagine visitate di recente o aggiunte ai segnalibri.",

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

@ -77,7 +77,7 @@ window.gActivityStreamStrings = {
"pocket_more_reccommendations": "Көбірек ұсыныстар",
"pocket_how_it_works": "How it works",
"pocket_cta_button": "Pocket-ті алу",
"pocket_cta_text": "Save the stories you love in Pocket, and fuel your mind with fascinating reads.",
"pocket_cta_text": "Өзіңіз ұнатқан хикаяларды Pocket ішіне сақтап, миіңізді тамаша оқумен толықтырыңыз.",
"highlights_empty_state": "Шолуды бастаңыз, сіз жақында шолған немесе бетбелгілерге қосқан тамаша мақалалар, видеолар немесе басқа парақтардың кейбіреулері осында көрсетіледі.",
"topstories_empty_state": "Дайын. {provider} ұсынған көбірек мақалаларды алу үшін кейінірек тексеріңіз. Күте алмайсыз ба? Интернеттен көбірек тамаша мақалаларды алу үшін әйгілі теманы таңдаңыз.",
"manual_migration_explanation2": "Firefox қолданбасын басқа браузер бетбелгілері, тарихы және парольдерімен қолданып көріңіз.",

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

@ -74,10 +74,10 @@ window.gActivityStreamStrings = {
"topsites_form_image_validation": "NEizdevās ielādēt attēlu. Izmēģiniet citu adresi.",
"pocket_read_more": "Populārās tēmas:",
"pocket_read_even_more": "Parādīt vairāk lapas",
"pocket_more_reccommendations": "More Recommendations",
"pocket_more_reccommendations": "Vairāk ieteikumu",
"pocket_how_it_works": "How it works",
"pocket_cta_button": "Get Pocket",
"pocket_cta_text": "Save the stories you love in Pocket, and fuel your mind with fascinating reads.",
"pocket_cta_button": "Izmēģiniet Pocket",
"pocket_cta_text": "Saglabājiet interesantus stāstus Pocket un barojiet savu prātu ar interesantu lasāmvielu.",
"highlights_empty_state": "Sāciet pārlūkošanu un mēs šeit parādīsim lieliskus rakstus, video un citas apmeklētās lapas.",
"topstories_empty_state": "Viss ir apskatīts! Atnāciet atpakaļ nedaudz vēlāk, lai redzētu populāros stāstus no {provider}. Nevarat sagaidīt? Izvēlieties kādu no tēmām jau tagad.",
"manual_migration_explanation2": "Izmēģiniet Firefox ar grāmatzīmēm, vēsturi un parolēm no cita pārlūka.",
@ -106,5 +106,6 @@ window.gActivityStreamStrings = {
"firstrun_terms_of_service": "Lietošanas noteikumiem",
"firstrun_privacy_notice": "Privātuma politikai",
"firstrun_continue_to_login": "Turpināt",
"firstrun_skip_login": "Izlaist šo soli"
"firstrun_skip_login": "Izlaist šo soli",
"pocket_learn_more": "Uzzināt vairāk"
};

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

@ -75,7 +75,7 @@ window.gActivityStreamStrings = {
"pocket_read_more": "Topik Popular:",
"pocket_read_even_more": "Papar Kisah Selanjutnya",
"pocket_more_reccommendations": "Saranan Lain",
"pocket_how_it_works": "How it works",
"pocket_how_it_works": "Cara pelaksanaan",
"pocket_cta_button": "Dapatkan Pocket",
"pocket_cta_text": "Simpan cerita yang anda suka dalam Pocket dan jana minda dengan bahan bacaan yang menarik.",
"highlights_empty_state": "Mulakan melayar dan kami akan paparkan beberapa artikel, video dan halaman menarik lain yang sudah anda layari dan tandabuku di sini.",

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

@ -77,7 +77,7 @@ window.gActivityStreamStrings = {
"pocket_more_reccommendations": "Flere anbefalinger",
"pocket_how_it_works": "How it works",
"pocket_cta_button": "Hent Pocket",
"pocket_cta_text": "Save the stories you love in Pocket, and fuel your mind with fascinating reads.",
"pocket_cta_text": "Lagre artiklene du synes er interessante i Pocket, og stimuler dine tanker med fasinerende lesermateriell.",
"highlights_empty_state": "Begynn å surfe, og vi viser noen av de beste artiklene, videoer og andre sider du nylig har besøkt eller bokmerket her.",
"topstories_empty_state": "Du har tatt igjen. Kom tilbake senere for flere topphistorier fra {provider}. Kan du ikke vente? Velg et populært emne for å finne flere gode artikler fra hele Internett.",
"manual_migration_explanation2": "Prøv Firefox med bokmerkene, historikk og passord fra en annen nettleser.",

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

@ -75,7 +75,7 @@ window.gActivityStreamStrings = {
"pocket_read_more": "Populaire onderwerpen:",
"pocket_read_even_more": "Meer verhalen bekijken",
"pocket_more_reccommendations": "Meer aanbevelingen",
"pocket_how_it_works": "How it works",
"pocket_how_it_works": "Hoe het werkt",
"pocket_cta_button": "Pocket gebruiken",
"pocket_cta_text": "Bewaar de verhalen die u interessant vindt in Pocket, en stimuleer uw gedachten met boeiende leesstof.",
"highlights_empty_state": "Begin met surfen, en we tonen hier een aantal geweldige artikelen, videos en andere paginas die u onlangs hebt bezocht of waarvoor u een bladwijzer hebt gemaakt.",

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

@ -77,7 +77,7 @@ window.gActivityStreamStrings = {
"pocket_more_reccommendations": "Fleire tilrådingar",
"pocket_how_it_works": "How it works",
"pocket_cta_button": "Last ned Pocket",
"pocket_cta_text": "Save the stories you love in Pocket, and fuel your mind with fascinating reads.",
"pocket_cta_text": "Lagre artiklane du synest er interessante i Pocket, og stimuler tankane dine med fasinerande lesemateriell.",
"highlights_empty_state": "Begynn å surfe, og vi vil vise deg nokre av dei beste artiklane, videoane og andre sider du nyleg har besøkt eller bokmerka her.",
"topstories_empty_state": "Det finst ikkje fleire. Kom tilbake seinare for fleire topphistoriar frå {provider}. Kan du ikkje vente? Vel eit populært emne for å finne fleire gode artiklar frå heile nettet.",
"manual_migration_explanation2": "Prøv Firefox med bokmerka, historikk og passord frå ein annan nettlesar.",

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

@ -75,7 +75,7 @@ window.gActivityStreamStrings = {
"pocket_read_more": "Tópicos populares:",
"pocket_read_even_more": "Ver mais histórias",
"pocket_more_reccommendations": "Mais recomendações",
"pocket_how_it_works": "How it works",
"pocket_how_it_works": "Como funciona",
"pocket_cta_button": "Obter o Pocket",
"pocket_cta_text": "Salve as histórias que você gosta no Pocket e abasteça sua mente com leituras fascinantes.",
"highlights_empty_state": "Comece a navegar e nós mostraremos aqui alguns ótimos artigos, vídeos e outras páginas que você favoritou ou visitou recentemente.",

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

@ -75,7 +75,7 @@ window.gActivityStreamStrings = {
"pocket_read_more": "Tópicos populares:",
"pocket_read_even_more": "Ver mais histórias",
"pocket_more_reccommendations": "Mais recomendações",
"pocket_how_it_works": "How it works",
"pocket_how_it_works": "Como funciona",
"pocket_cta_button": "Obter o Pocket",
"pocket_cta_text": "Guarde as histórias que adora no Pocket, e abasteça a sua mente com leituras fascinantes.",
"highlights_empty_state": "Comece a navegar, e iremos mostrar-lhe alguns dos ótimos artigos, vídeos, e outras páginas que visitou recentemente ou adicionou aos marcadores aqui.",

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

@ -19,8 +19,8 @@ window.gActivityStreamStrings = {
"menu_action_delete": "Șterge din istoric",
"menu_action_pin": "Fixează",
"menu_action_unpin": "Anulează fixarea",
"confirm_history_delete_p1": "Sigur vrei să ştergi fiecare instanţă a acestei pagini din istoric?",
"confirm_history_delete_notice_p2": "Această acțiune este ireversibilă.",
"confirm_history_delete_p1": "Sigur vrei să ștergi fiecare instanță a paginii din istoric?",
"confirm_history_delete_notice_p2": "Acțiunea este ireversibilă.",
"menu_action_save_to_pocket": "Salvează în Pocket",
"menu_action_delete_pocket": "Şterge din Pocket",
"menu_action_archive_pocket": "Arhivează în Pocket",

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

@ -76,8 +76,8 @@ window.gActivityStreamStrings = {
"pocket_read_even_more": "Prikaži več vesti",
"pocket_more_reccommendations": "Več priporočil",
"pocket_how_it_works": "How it works",
"pocket_cta_button": "Get Pocket",
"pocket_cta_text": "Save the stories you love in Pocket, and fuel your mind with fascinating reads.",
"pocket_cta_button": "Prenesi Pocket",
"pocket_cta_text": "Shranite zgodbe, ki jih imate radi, v Pocket, in napolnite svoje misli z navdušujočim branjem.",
"highlights_empty_state": "Začnite z brskanjem, mi pa vam bomo tu prikazovali odlične članke, videoposnetke ter druge strani, ki ste jih nedavno obiskali ali shranili med zaznamke.",
"topstories_empty_state": "Zdaj ste seznanjeni z novicami. Vrnite se pozneje in si oglejte nove prispevke iz {provider}. Komaj čakate? Izberite priljubljeno temo in odkrijte več velikih zgodb na spletu.",
"manual_migration_explanation2": "Preskusite Firefox z zaznamki, zgodovino in gesli iz drugega brskalnika.",

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

@ -75,7 +75,7 @@ window.gActivityStreamStrings = {
"pocket_read_more": "Populära ämnen:",
"pocket_read_even_more": "Visa fler nyheter",
"pocket_more_reccommendations": "Fler rekommendationer",
"pocket_how_it_works": "How it works",
"pocket_how_it_works": "Hur fungerar det",
"pocket_cta_button": "Hämta Pocket",
"pocket_cta_text": "Spara de historier som du tycker är intressant i Pocket, och stimulera dina tankar med fascinerande läsmaterial.",
"highlights_empty_state": "Börja surfa, och vi visar några av de bästa artiklarna, videoklippen och andra sidor du nyligen har besökt eller bokmärkt här.",

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

@ -75,7 +75,7 @@ window.gActivityStreamStrings = {
"pocket_read_more": "Popüler konular:",
"pocket_read_even_more": "Daha fazla yazı göster",
"pocket_more_reccommendations": "Daha fazla öneri",
"pocket_how_it_works": "How it works",
"pocket_how_it_works": "Nasıl çalışıyor?",
"pocket_cta_button": "Pocketı edinin",
"pocket_cta_text": "Sevdiğiniz yazıları Pocketa kaydedin, aklınız okumaya değer şeylerle doldurun.",
"highlights_empty_state": "Gezinmeye başlayın. Son zamanlarda baktığınız veya yer imlerinize eklediğiniz bazı güzel makaleleri, videoları ve diğer sayfaları burada göstereceğiz.",

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

@ -75,7 +75,7 @@ window.gActivityStreamStrings = {
"pocket_read_more": "热门主题:",
"pocket_read_even_more": "查看更多文章",
"pocket_more_reccommendations": "更多推荐",
"pocket_how_it_works": "How it works",
"pocket_how_it_works": "原理是什麼",
"pocket_cta_button": "获取 Pocket",
"pocket_cta_text": "将您喜爱的故事保存到 Pocket用精彩的读物为思想注入活力。",
"highlights_empty_state": "开始网上冲浪之旅吧,之后这里会显示您最近看过或加了书签的精彩文章、视频与其他页面。",

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

@ -75,7 +75,7 @@ window.gActivityStreamStrings = {
"pocket_read_more": "熱門主題:",
"pocket_read_even_more": "檢視更多文章",
"pocket_more_reccommendations": "更多推薦項目",
"pocket_how_it_works": "How it works",
"pocket_how_it_works": "原理是什麼",
"pocket_cta_button": "取得 Pocket",
"pocket_cta_text": "將您喜愛的故事儲存到 Pocket閱讀一篇篇好文章。",
"highlights_empty_state": "開始上網,我們就會把您在網路上發現的好文章、影片、剛加入書籤的頁面顯示於此。",

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

@ -362,3 +362,14 @@ add_task(async function check_provider_cohorts() {
is(await ASRouterTargeting.Environment.providerCohorts.onboarding, "foo");
is(await ASRouterTargeting.Environment.providerCohorts.cfr, "bar");
});
add_task(async function check_xpinstall_enabled() {
// should default to true if pref doesn't exist
is(await ASRouterTargeting.Environment.xpinstallEnabled, true);
// flip to false, check targeting reflects that
await pushPrefs(["xpinstall.enabled", false]);
is(await ASRouterTargeting.Environment.xpinstallEnabled, false);
// flip to true, check targeting reflects that
await pushPrefs(["xpinstall.enabled", true]);
is(await ASRouterTargeting.Environment.xpinstallEnabled, true);
});

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

@ -8,11 +8,11 @@ test_newtab({
before: setDefaultTopSites,
// Test verifies the menu options for a default top site.
test: async function defaultTopSites_menuOptions() {
await ContentTaskUtils.waitForCondition(() => content.document.querySelector(".top-site-icon"),
const siteSelector = ".top-site-outer:not(.search-shortcut):not(.placeholder)";
await ContentTaskUtils.waitForCondition(() => content.document.querySelector(siteSelector),
"Topsite tippytop icon not found");
let contextMenuItems = content.openContextMenuAndGetOptions(".top-sites-list li:not(.search-shortcut)").map(v => v.textContent);
const contextMenuItems = content.openContextMenuAndGetOptions(siteSelector).map(v => v.textContent);
Assert.equal(contextMenuItems.length, 5, "Number of options is correct");
const expectedItemsText = ["Pin", "Edit", "Open in a New Window", "Open in a New Private Window", "Dismiss"];
@ -27,26 +27,27 @@ test_newtab({
before: setDefaultTopSites,
// Test verifies that the next top site in queue replaces a dismissed top site.
test: async function defaultTopSites_dismiss() {
await ContentTaskUtils.waitForCondition(() => content.document.querySelector(".top-site-icon"),
const siteSelector = ".top-site-outer:not(.search-shortcut):not(.placeholder)";
await ContentTaskUtils.waitForCondition(() => content.document.querySelector(siteSelector),
"Topsite tippytop icon not found");
// Don't count search topsites
let defaultTopSitesNumber = content.document.querySelectorAll(".top-site-outer:not(.placeholder):not(.search-shortcut)").length;
const defaultTopSitesNumber = content.document.querySelectorAll(siteSelector).length;
Assert.equal(defaultTopSitesNumber, 5, "5 top sites are loaded by default");
// Skip the search topsites select the second default topsite
let secondTopSite = content.document.querySelectorAll(".top-sites-list li:not(.search-shortcut):not(.placeholder)")[1].getAttribute("href");
const secondTopSite = content.document.querySelectorAll(siteSelector)[1].getAttribute("href");
let contextMenuItems = content.openContextMenuAndGetOptions("li:not(.search-shortcut)");
const contextMenuItems = content.openContextMenuAndGetOptions(siteSelector);
Assert.equal(contextMenuItems[4].textContent, "Dismiss", "'Dismiss' is the 5th item in the context menu list");
contextMenuItems[4].querySelector("a").click();
// Wait for the topsite to be dismissed and the second one to replace it
await ContentTaskUtils.waitForCondition(() => content.document.querySelector(".top-sites-list li:not(.search-shortcut):not(.placeholder)").getAttribute("href") === secondTopSite,
await ContentTaskUtils.waitForCondition(() => content.document.querySelector(siteSelector).getAttribute("href") === secondTopSite,
"First default topsite was dismissed");
await ContentTaskUtils.waitForCondition(() => content.document.querySelectorAll(".top-site-outer:not(.placeholder):not(.search-shortcut)").length === 4, "4 top sites are displayed after one of them is dismissed");
await ContentTaskUtils.waitForCondition(() => content.document.querySelectorAll(siteSelector).length === 4, "4 top sites are displayed after one of them is dismissed");
},
async after() {
await new Promise(resolve => NewTabUtils.undoAll(resolve));
@ -56,16 +57,17 @@ test_newtab({
test_newtab({
before: setDefaultTopSites,
test: async function searchTopSites_dismiss() {
await ContentTaskUtils.waitForCondition(() => content.document.querySelectorAll(".search-shortcut").length === 2,
const siteSelector = ".search-shortcut";
await ContentTaskUtils.waitForCondition(() => content.document.querySelectorAll(siteSelector).length === 2,
"2 search topsites are loaded by default");
let contextMenuItems = content.openContextMenuAndGetOptions(".search-shortcut");
const contextMenuItems = content.openContextMenuAndGetOptions(siteSelector);
is(contextMenuItems.length, 2, "Search TopSites should only have Unpin and Dismiss");
// Unpin
contextMenuItems[0].querySelector("a").click();
await ContentTaskUtils.waitForCondition(() => content.document.querySelectorAll(".search-shortcut").length === 1,
await ContentTaskUtils.waitForCondition(() => content.document.querySelectorAll(siteSelector).length === 1,
"1 search topsite displayed after we unpin the other one");
},
after: () => {

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

@ -25,10 +25,11 @@ test_newtab({
before: setDefaultTopSites,
// it should pin the website when we click the first option of the topsite context menu.
test: async function topsites_pin_unpin() {
await ContentTaskUtils.waitForCondition(() => content.document.querySelector(".top-site-icon"),
const siteSelector = ".top-site-outer:not(.search-shortcut):not(.placeholder)";
await ContentTaskUtils.waitForCondition(() => content.document.querySelector(siteSelector),
"Topsite tippytop icon not found");
// There are only topsites on the page, the selector with find the first topsite menu button.
let topsiteEl = content.document.querySelector(".top-site-outer:not(.search-shortcut)");
let topsiteEl = content.document.querySelector(siteSelector);
let topsiteContextBtn = topsiteEl.querySelector(".context-menu-button");
topsiteContextBtn.click();

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

@ -793,7 +793,7 @@ describe("ASRouter", () => {
describe("#onMessage: INSTALL_ADDON_FROM_URL", () => {
it("should call installAddonFromURL with correct arguments", async () => {
sandbox.stub(MessageLoaderUtils, "installAddonFromURL").resolves(null);
const msg = fakeExecuteUserAction({type: "INSTALL_ADDON_FROM_URL", data: {args: "foo.com"}});
const msg = fakeExecuteUserAction({type: "INSTALL_ADDON_FROM_URL", data: {url: "foo.com"}});
await Router.onMessage(msg);

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

@ -41,4 +41,12 @@ describe("CFRMessageProvider", () => {
assert.deepEqual(cohort3.frequency, {lifetime: 3}, "three day cohort has the right frequency cap");
assert.include(cohort3.targeting, `(providerCohorts.cfr == "three_per_day_amazon")`);
});
it("should always have xpinstallEnabled as targeting if it is an addon", () => {
for (const message of messages) {
// Ensure that the CFR messages that are recommending an addon have this targeting.
// In the future when we can do targeting based on category, this test will change.
// See bug 1494778 and 1497653
assert.include(message.targeting, `(xpinstallEnabled == true)`);
}
});
});

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

@ -5,6 +5,8 @@ import {GlobalOverrider} from "test/unit/utils";
import {mount} from "enzyme";
import React from "react";
let [FAKE_MESSAGE] = FAKE_LOCAL_MESSAGES;
const FAKE_NEWSLETTER_SNIPPET = FAKE_LOCAL_MESSAGES.find(msg => msg.id === "newsletter");
const FAKE_FXA_SNIPPET = FAKE_LOCAL_MESSAGES.find(msg => msg.id === "fxa");
FAKE_MESSAGE = Object.assign({}, FAKE_MESSAGE, {provider: "fakeprovider"});
const FAKE_BUNDLED_MESSAGE = {bundle: [{id: "foo", template: "onboarding", content: {title: "Foo", body: "Foo123"}}], extraTemplateStrings: {}, template: "onboarding"};
@ -85,6 +87,20 @@ describe("ASRouterUISurface", () => {
assert.isTrue(wrapper.exists());
});
it("should pass in the correct form_method for newsletter snippets", () => {
wrapper.setState({message: FAKE_NEWSLETTER_SNIPPET});
assert.isTrue(wrapper.find("SubmitFormSnippet").exists());
assert.propertyVal(wrapper.find("SubmitFormSnippet").props(), "form_method", "POST");
});
it("should pass in the correct form_method for fxa snippets", () => {
wrapper.setState({message: FAKE_FXA_SNIPPET});
assert.isTrue(wrapper.find("SubmitFormSnippet").exists());
assert.propertyVal(wrapper.find("SubmitFormSnippet").props(), "form_method", "GET");
});
it("should render the component if a bundle of messages is defined", () => {
wrapper.setState({bundle: FAKE_BUNDLED_MESSAGE});
assert.isTrue(wrapper.exists());

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

@ -7,6 +7,8 @@ export const FAKE_LOCAL_MESSAGES = [
{id: "foo2", template: "simple_snippet", bundled: 2, order: 2, content: {title: "Foo2", body: "Foo123-2"}},
{id: "bar", template: "fancy_template", content: {title: "Foo", body: "Foo123"}},
{id: "baz", content: {title: "Foo", body: "Foo123"}},
{id: "newsletter", template: "newsletter_snippet", content: {title: "Foo", body: "Foo123"}},
{id: "fxa", template: "fxa_signup_snippet", content: {title: "Foo", body: "Foo123"}},
];
export const FAKE_LOCAL_PROVIDER = {id: "onboarding", type: "local", localProvider: "FAKE_LOCAL_PROVIDER", enabled: true, cohort: 0};
export const FAKE_LOCAL_PROVIDERS = {FAKE_LOCAL_PROVIDER: {getMessages: () => FAKE_LOCAL_MESSAGES}};

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

@ -1,25 +1,27 @@
import {mount} from "enzyme";
import {NewsletterSnippet} from "content-src/asrouter/templates/NewsletterSnippet/NewsletterSnippet.jsx";
import React from "react";
import schema from "content-src/asrouter/templates/NewsletterSnippet/NewsletterSnippet.schema.json";
import schema from "content-src/asrouter/templates/SubmitFormSnippet/SubmitFormSnippet.schema.json";
import {SubmitFormSnippet} from "content-src/asrouter/templates/SubmitFormSnippet/SubmitFormSnippet.jsx";
const DEFAULT_CONTENT = {
text: "foo",
scene1_text: "foo",
scene2_text: "bar",
button_label: "Sign Up",
scene1_button_label: "Sign Up",
form_action: "foo.com",
hidden_inputs: {"foo": "foo"},
error_text: "error",
success_text: "success",
};
describe("NewsletterSnippet", () => {
describe("SubmitFormSnippet", () => {
let sandbox;
let onBlockStub;
/**
* mountAndCheckProps - Mounts a NewsletterSnippet with DEFAULT_CONTENT extended with any props
* mountAndCheckProps - Mounts a SubmitFormSnippet with DEFAULT_CONTENT extended with any props
* passed in the content param and validates props against the schema.
* @param {obj} content Object containing custom message content (e.g. {text, icon, title})
* @returns enzyme wrapper for SimpleSnippet
* @returns enzyme wrapper for SubmitFormSnippet
*/
function mountAndCheckProps(content = {}) {
const props = {
@ -28,9 +30,10 @@ describe("NewsletterSnippet", () => {
onDismiss: sandbox.stub(),
sendUserActionTelemetry: sandbox.stub(),
onAction: sandbox.stub(),
form_method: "POST",
};
assert.jsonSchema(props.content, schema);
return mount(<NewsletterSnippet {...props} />);
return mount(<SubmitFormSnippet {...props} />);
}
beforeEach(() => {
@ -43,7 +46,7 @@ describe("NewsletterSnippet", () => {
});
it("should render .text", () => {
const wrapper = mountAndCheckProps({text: "bar"});
const wrapper = mountAndCheckProps({scene1_text: "bar"});
assert.equal(wrapper.find(".body").text(), "bar");
});
it("should not render title element if no .title prop is supplied", () => {
@ -51,15 +54,15 @@ describe("NewsletterSnippet", () => {
assert.lengthOf(wrapper.find(".title"), 0);
});
it("should render .title", () => {
const wrapper = mountAndCheckProps({title: "Foo"});
const wrapper = mountAndCheckProps({scene1_title: "Foo"});
assert.equal(wrapper.find(".title").text(), "Foo");
});
it("should render .icon", () => {
const wrapper = mountAndCheckProps({icon: "data:image/gif;base64,R0lGODl"});
const wrapper = mountAndCheckProps({scene1_icon: "data:image/gif;base64,R0lGODl"});
assert.equal(wrapper.find(".icon").prop("src"), "data:image/gif;base64,R0lGODl");
});
it("should render .button_label and default className", () => {
const wrapper = mountAndCheckProps({button_label: "Click here"});
const wrapper = mountAndCheckProps({scene1_button_label: "Click here"});
const button = wrapper.find("button.ASRouterButton");
assert.equal(button.text(), "Click here");
@ -73,7 +76,7 @@ describe("NewsletterSnippet", () => {
beforeEach(() => {
wrapper = mountAndCheckProps({
text: "bar",
scene1_text: "bar",
scene2_email_placeholder_text: "Email",
scene2_text: "signup",
});
@ -167,5 +170,25 @@ describe("NewsletterSnippet", () => {
assert.equal(wrapper.state().signupSubmitted, false);
});
it("should not render the privacy notice checkbox if prop is missing", () => {
wrapper.setState({expanded: true});
assert.isFalse(wrapper.find(".privacy-notice").exists());
});
it("should render the privacy notice checkbox if prop is provided", () => {
wrapper.setProps({privacyNoticeRichText: "privacy notice"});
wrapper.setState({expanded: true});
assert.isTrue(wrapper.find(".privacy-notice").exists());
});
it("should not call fetch if form_method is GET", async () => {
sandbox.stub(window, "fetch").resolves(fetchOk);
wrapper.setProps({form_method: "GET"});
wrapper.setState({expanded: true});
await wrapper.instance().handleSubmit({preventDefault: sandbox.stub()});
assert.notCalled(window.fetch);
});
});
});

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

@ -1,7 +1,7 @@
import {actionCreators as ac, actionTypes as at} from "common/Actions.jsm";
import {GlobalOverrider} from "test/unit/utils";
import {PlacesFeed} from "lib/PlacesFeed.jsm";
const {HistoryObserver, BookmarksObserver} = PlacesFeed;
const {HistoryObserver, BookmarksObserver, PlacesObserver} = PlacesFeed;
const FAKE_BOOKMARK = {bookmarkGuid: "xi31", bookmarkTitle: "Foo", dateAdded: 123214232, url: "foo.com"};
const TYPE_BOOKMARK = 0; // This is fake, for testing
@ -38,6 +38,8 @@ describe("PlacesFeed", () => {
sandbox.spy(global.PlacesUtils.bookmarks, "removeObserver");
sandbox.spy(global.PlacesUtils.history, "addObserver");
sandbox.spy(global.PlacesUtils.history, "removeObserver");
sandbox.spy(global.PlacesUtils.observers, "addListener");
sandbox.spy(global.PlacesUtils.observers, "removeListener");
sandbox.spy(global.Services.obs, "addObserver");
sandbox.spy(global.Services.obs, "removeObserver");
sandbox.spy(global.Cu, "reportError");
@ -74,21 +76,33 @@ describe("PlacesFeed", () => {
assert.calledOnce(feed.store.dispatch);
assert.equal(feed.store.dispatch.firstCall.args[0].type, action.type);
});
it("should have a PlacesObserver that dispatches to the store", () => {
assert.instanceOf(feed.placesObserver, PlacesObserver);
const action = {type: "FOO"};
feed.placesObserver.dispatch(action);
assert.calledOnce(feed.store.dispatch);
assert.equal(feed.store.dispatch.firstCall.args[0].type, action.type);
});
describe("#onAction", () => {
it("should add bookmark, history, blocked observers on INIT", () => {
it("should add bookmark, history, places, blocked observers on INIT", () => {
feed.onAction({type: at.INIT});
assert.calledWith(global.PlacesUtils.history.addObserver, feed.historyObserver, true);
assert.calledWith(global.PlacesUtils.bookmarks.addObserver, feed.bookmarksObserver, true);
assert.calledWith(global.PlacesUtils.observers.addListener, ["bookmark-added"], feed.placesObserver.handlePlacesEvent);
assert.calledWith(global.Services.obs.addObserver, feed, BLOCKED_EVENT);
});
it("should remove bookmark, history, blocked observers, and timers on UNINIT", () => {
it("should remove bookmark, history, places, blocked observers, and timers on UNINIT", () => {
feed.placesChangedTimer = global.Cc["@mozilla.org/timer;1"].createInstance();
let spy = feed.placesChangedTimer.cancel;
feed.onAction({type: at.UNINIT});
assert.calledWith(global.PlacesUtils.history.removeObserver, feed.historyObserver);
assert.calledWith(global.PlacesUtils.bookmarks.removeObserver, feed.bookmarksObserver);
assert.calledWith(global.PlacesUtils.observers.removeListener, ["bookmark-added"], feed.placesObserver.handlePlacesEvent);
assert.calledWith(global.Services.obs.removeObserver, feed, BLOCKED_EVENT);
assert.equal(feed.placesChangedTimer, null);
assert.calledOnce(spy);
@ -343,19 +357,21 @@ describe("PlacesFeed", () => {
});
describe("Custom dispatch", () => {
it("should only dispatch 1 PLACES_LINKS_CHANGED action if many onItemAdded notifications happened at once", async () => {
const args = {
type: "bookmark-added",
it("should only dispatch 1 PLACES_LINKS_CHANGED action if many bookmark-added notifications happened at once", async () => {
// Yes, onItemAdded has at least 8 arguments. See function definition for docs.
const args = [{
itemType: TYPE_BOOKMARK,
url: "https://" + FAKE_BOOKMARK.url,
title: FAKE_BOOKMARK.bookmarkTitle,
source: SOURCES.DEFAULT,
dateAdded: FAKE_BOOKMARK.dateAdded,
guid: FAKE_BOOKMARK.bookmarkGuid,
source: SOURCES.DEFAULT,
};
await feed.placesObserver.handlePlacesEvents([args]);
await feed.placesObserver.handlePlacesEvents([args]);
await feed.placesObserver.handlePlacesEvents([args]);
title: FAKE_BOOKMARK.bookmarkTitle,
url: "https://www.foo.com",
isTagging: false,
}];
await feed.placesObserver.handlePlacesEvent(args);
await feed.placesObserver.handlePlacesEvent(args);
await feed.placesObserver.handlePlacesEvent(args);
await feed.placesObserver.handlePlacesEvent(args);
assert.calledOnce(feed.store.dispatch.withArgs(ac.OnlyToMain({type: at.PLACES_LINKS_CHANGED})));
});
it("should only dispatch 1 PLACES_LINKS_CHANGED action if many onItemRemoved notifications happened at once", async () => {
@ -377,125 +393,138 @@ describe("PlacesFeed", () => {
});
describe("PlacesObserver", () => {
let dispatch;
let observer;
beforeEach(() => {
dispatch = sandbox.spy();
observer = new PlacesObserver(dispatch);
});
describe("#handlePlacesEvents", () => {
describe("#bookmark-added", () => {
let dispatch;
let observer;
beforeEach(() => {
dispatch = sandbox.spy();
observer = new PlacesObserver(dispatch);
});
it("should dispatch a PLACES_BOOKMARK_ADDED action with the bookmark data - http", async () => {
const args = {
type: "bookmark-added",
const args = [{
itemType: TYPE_BOOKMARK,
url: "http://" + FAKE_BOOKMARK.url,
title: FAKE_BOOKMARK.bookmarkTitle,
source: SOURCES.DEFAULT,
dateAdded: FAKE_BOOKMARK.dateAdded,
guid: FAKE_BOOKMARK.bookmarkGuid,
source: SOURCES.DEFAULT,
};
await observer.handlePlacesEvents([args]);
title: FAKE_BOOKMARK.bookmarkTitle,
url: "http://www.foo.com",
isTagging: false,
}];
await observer.handlePlacesEvent(args);
assert.calledWith(dispatch, {type: at.PLACES_BOOKMARK_ADDED, data: FAKE_BOOKMARK});
assert.calledWith(dispatch.secondCall, {
type: at.PLACES_BOOKMARK_ADDED,
data: {
bookmarkGuid: FAKE_BOOKMARK.bookmarkGuid,
bookmarkTitle: FAKE_BOOKMARK.bookmarkTitle,
dateAdded: FAKE_BOOKMARK.dateAdded * 1000,
url: "http://www.foo.com",
},
});
});
it("should dispatch a PLACES_BOOKMARK_ADDED action with the bookmark data - https", async () => {
const args = {
type: "bookmark-added",
const args = [{
itemType: TYPE_BOOKMARK,
url: "https://" + FAKE_BOOKMARK.url,
title: FAKE_BOOKMARK.bookmarkTitle,
source: SOURCES.DEFAULT,
dateAdded: FAKE_BOOKMARK.dateAdded,
guid: FAKE_BOOKMARK.bookmarkGuid,
source: SOURCES.DEFAULT,
};
await observer.handlePlacesEvents([args]);
title: FAKE_BOOKMARK.bookmarkTitle,
url: "https://www.foo.com",
isTagging: false,
}];
await observer.handlePlacesEvent(args);
assert.calledWith(dispatch, {type: at.PLACES_BOOKMARK_ADDED, data: FAKE_BOOKMARK});
assert.calledWith(dispatch.secondCall, {
type: at.PLACES_BOOKMARK_ADDED,
data: {
bookmarkGuid: FAKE_BOOKMARK.bookmarkGuid,
bookmarkTitle: FAKE_BOOKMARK.bookmarkTitle,
dateAdded: FAKE_BOOKMARK.dateAdded * 1000,
url: "https://www.foo.com",
},
});
});
it("should not dispatch a PLACES_BOOKMARK_ADDED action - not http/https", async () => {
const args = {
type: "bookmark-added",
const args = [{
itemType: TYPE_BOOKMARK,
url: "places://" + FAKE_BOOKMARK.url,
title: FAKE_BOOKMARK.bookmarkTitle,
source: SOURCES.DEFAULT,
dateAdded: FAKE_BOOKMARK.dateAdded,
guid: FAKE_BOOKMARK.bookmarkGuid,
source: SOURCES.DEFAULT,
};
await observer.handlePlacesEvents([args]);
title: FAKE_BOOKMARK.bookmarkTitle,
url: "foo.com",
isTagging: false,
}];
await observer.handlePlacesEvent(args);
assert.notCalled(dispatch);
});
it("should not dispatch a PLACES_BOOKMARK_ADDED action - has IMPORT source", async () => {
const args = {
type: "bookmark-added",
const args = [{
itemType: TYPE_BOOKMARK,
url: "http://" + FAKE_BOOKMARK.url,
title: FAKE_BOOKMARK.bookmarkTitle,
source: SOURCES.IMPORT,
dateAdded: FAKE_BOOKMARK.dateAdded,
guid: FAKE_BOOKMARK.bookmarkGuid,
source: SOURCES.IMPORT,
};
await observer.handlePlacesEvents([args]);
title: FAKE_BOOKMARK.bookmarkTitle,
url: "foo.com",
isTagging: false,
}];
await observer.handlePlacesEvent(args);
assert.notCalled(dispatch);
});
it("should not dispatch a PLACES_BOOKMARK_ADDED action - has RESTORE source", async () => {
const args = {
type: "bookmark-added",
const args = [{
itemType: TYPE_BOOKMARK,
url: "http://" + FAKE_BOOKMARK.url,
title: FAKE_BOOKMARK.bookmarkTitle,
source: SOURCES.RESTORE,
dateAdded: FAKE_BOOKMARK.dateAdded,
guid: FAKE_BOOKMARK.bookmarkGuid,
source: SOURCES.RESTORE,
};
await observer.handlePlacesEvents([args]);
title: FAKE_BOOKMARK.bookmarkTitle,
url: "foo.com",
isTagging: false,
}];
await observer.handlePlacesEvent(args);
assert.notCalled(dispatch);
});
it("should not dispatch a PLACES_BOOKMARK_ADDED action - has RESTORE_ON_STARTUP source", async () => {
const args = {
type: "bookmark-added",
const args = [{
itemType: TYPE_BOOKMARK,
url: "http://" + FAKE_BOOKMARK.url,
title: FAKE_BOOKMARK.bookmarkTitle,
source: SOURCES.RESTORE_ON_STARTUP,
dateAdded: FAKE_BOOKMARK.dateAdded,
guid: FAKE_BOOKMARK.bookmarkGuid,
source: SOURCES.RESTORE_ON_STARTUP,
};
await observer.handlePlacesEvents([args]);
title: FAKE_BOOKMARK.bookmarkTitle,
url: "foo.com",
isTagging: false,
}];
await observer.handlePlacesEvent(args);
assert.notCalled(dispatch);
});
it("should not dispatch a PLACES_BOOKMARK_ADDED action - has SYNC source", async () => {
const args = {
type: "bookmark-added",
const args = [{
itemType: TYPE_BOOKMARK,
url: "http://" + FAKE_BOOKMARK.url,
title: FAKE_BOOKMARK.bookmarkTitle,
source: SOURCES.SYNC,
dateAdded: FAKE_BOOKMARK.dateAdded,
guid: FAKE_BOOKMARK.bookmarkGuid,
source: SOURCES.SYNC,
};
await observer.handlePlacesEvents([args]);
title: FAKE_BOOKMARK.bookmarkTitle,
url: "foo.com",
isTagging: false,
}];
await observer.handlePlacesEvent(args);
assert.notCalled(dispatch);
});
it("should ignore events that are not of TYPE_BOOKMARK", async () => {
const args = {
type: "bookmark-added",
const args = [{
itemType: "nottypebookmark",
url: "http://" + FAKE_BOOKMARK.url,
title: FAKE_BOOKMARK.bookmarkTitle,
source: SOURCES.DEFAULT,
dateAdded: FAKE_BOOKMARK.dateAdded,
guid: FAKE_BOOKMARK.bookmarkGuid,
source: SOURCES.SYNC,
};
await observer.handlePlacesEvents([args]);
title: FAKE_BOOKMARK.bookmarkTitle,
url: "https://www.foo.com",
isTagging: false,
}];
await observer.handlePlacesEvent(args);
assert.notCalled(dispatch);
});

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

@ -91,6 +91,10 @@ const TEST_GLOBAL = {
get history() {
return TEST_GLOBAL.Cc["@mozilla.org/browser/nav-history-service;1"];
},
observers: {
addListener() {},
removeListener() {},
},
},
PluralForm: {get() {}},
Preferences: FakePrefs,