зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
47b312fbc2
Коммит
704e5da4d0
|
@ -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 je’eporã
|
||||
pocket_learn_more=Kuaave
|
||||
pocket_how_it_works=Mba’éichapa omba’apo
|
||||
pocket_cta_button=Eguereko Pocket
|
||||
pocket_cta_text=Eñongatu umi eipotáva tembiasakue Pocket-pe ha emombarete ne akã ñemoñe’ẽ ha’evé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ı Pocket’a 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 je’eporã",
|
||||
"pocket_how_it_works": "How it works",
|
||||
"pocket_how_it_works": "Mba’éichapa omba’apo",
|
||||
"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’ẽ ha’evé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, video’s en andere pagina’s 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ı Pocket’a 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,
|
||||
|
|
Загрузка…
Ссылка в новой задаче