diff --git a/browser/extensions/activity-stream/common/Actions.jsm b/browser/extensions/activity-stream/common/Actions.jsm
index dc823172eb9d..fa37e85b3216 100644
--- a/browser/extensions/activity-stream/common/Actions.jsm
+++ b/browser/extensions/activity-stream/common/Actions.jsm
@@ -31,6 +31,7 @@ for (const type of [
"DELETE_HISTORY_URL_CONFIRM",
"DIALOG_CANCEL",
"DIALOG_OPEN",
+ "DISABLE_ONBOARDING",
"INIT",
"LOCALE_UPDATED",
"MIGRATION_CANCEL",
@@ -49,8 +50,8 @@ for (const type of [
"PLACES_BOOKMARK_CHANGED",
"PLACES_BOOKMARK_REMOVED",
"PLACES_HISTORY_CLEARED",
+ "PLACES_LINKS_DELETED",
"PLACES_LINK_BLOCKED",
- "PLACES_LINK_DELETED",
"PREFS_INITIAL_VALUES",
"PREF_CHANGED",
"SAVE_SESSION_PERF_DATA",
diff --git a/browser/extensions/activity-stream/common/PrerenderData.jsm b/browser/extensions/activity-stream/common/PrerenderData.jsm
index b9360662c646..9b040fe0c950 100644
--- a/browser/extensions/activity-stream/common/PrerenderData.jsm
+++ b/browser/extensions/activity-stream/common/PrerenderData.jsm
@@ -74,8 +74,7 @@ this.PrerenderData = new _PrerenderData({
icon: "pocket",
id: "topstories",
order: 1,
- title: {id: "header_recommended_by", values: {provider: "Pocket"}},
- topics: [{}]
+ title: {id: "header_recommended_by", values: {provider: "Pocket"}}
},
{
enabled: true,
diff --git a/browser/extensions/activity-stream/common/Reducers.jsm b/browser/extensions/activity-stream/common/Reducers.jsm
index 5dbd926f7a3f..a6f83d5f4823 100644
--- a/browser/extensions/activity-stream/common/Reducers.jsm
+++ b/browser/extensions/activity-stream/common/Reducers.jsm
@@ -288,7 +288,9 @@ function Sections(prevState = INITIAL_STATE.Sections, action) {
return item;
})
}));
- case at.PLACES_LINK_DELETED:
+ case at.PLACES_LINKS_DELETED:
+ return prevState.map(section => Object.assign({}, section,
+ {rows: section.rows.filter(site => !action.data.includes(site.url))}));
case at.PLACES_LINK_BLOCKED:
return prevState.map(section =>
Object.assign({}, section, {rows: section.rows.filter(site => site.url !== action.data.url)}));
diff --git a/browser/extensions/activity-stream/data/content/activity-stream-initial-state.js b/browser/extensions/activity-stream/data/content/activity-stream-initial-state.js
index 572b9ff068e3..24e71dce86c3 100644
--- a/browser/extensions/activity-stream/data/content/activity-stream-initial-state.js
+++ b/browser/extensions/activity-stream/data/content/activity-stream-initial-state.js
@@ -138,9 +138,6 @@
"enabled": true,
"icon": "pocket",
"id": "topstories",
- "topics": [
- {}
- ],
"initialized": false
},
{
diff --git a/browser/extensions/activity-stream/data/content/activity-stream-prerendered.html b/browser/extensions/activity-stream/data/content/activity-stream-prerendered.html
index 484493d185a1..798f92331f47 100644
--- a/browser/extensions/activity-stream/data/content/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/data/content/activity-stream-prerendered.html
@@ -9,7 +9,7 @@
-
+
diff --git a/browser/extensions/activity-stream/data/content/activity-stream.bundle.js b/browser/extensions/activity-stream/data/content/activity-stream.bundle.js
index 1dbbaa5ff4dd..27b66f8c20b2 100644
--- a/browser/extensions/activity-stream/data/content/activity-stream.bundle.js
+++ b/browser/extensions/activity-stream/data/content/activity-stream.bundle.js
@@ -94,7 +94,7 @@ const globalImportContext = typeof Window === "undefined" ? BACKGROUND_PROCESS :
// UNINIT: "UNINIT"
// }
const actionTypes = {};
-for (const type of ["BLOCK_URL", "BOOKMARK_URL", "DELETE_BOOKMARK_BY_ID", "DELETE_HISTORY_URL", "DELETE_HISTORY_URL_CONFIRM", "DIALOG_CANCEL", "DIALOG_OPEN", "INIT", "LOCALE_UPDATED", "MIGRATION_CANCEL", "MIGRATION_COMPLETED", "MIGRATION_START", "NEW_TAB_INIT", "NEW_TAB_INITIAL_STATE", "NEW_TAB_LOAD", "NEW_TAB_REHYDRATED", "NEW_TAB_STATE_REQUEST", "NEW_TAB_UNLOAD", "OPEN_LINK", "OPEN_NEW_WINDOW", "OPEN_PRIVATE_WINDOW", "PLACES_BOOKMARK_ADDED", "PLACES_BOOKMARK_CHANGED", "PLACES_BOOKMARK_REMOVED", "PLACES_HISTORY_CLEARED", "PLACES_LINK_BLOCKED", "PLACES_LINK_DELETED", "PREFS_INITIAL_VALUES", "PREF_CHANGED", "SAVE_SESSION_PERF_DATA", "SAVE_TO_POCKET", "SCREENSHOT_UPDATED", "SECTION_DEREGISTER", "SECTION_DISABLE", "SECTION_ENABLE", "SECTION_OPTIONS_CHANGED", "SECTION_REGISTER", "SECTION_UPDATE", "SECTION_UPDATE_CARD", "SET_PREF", "SHOW_FIREFOX_ACCOUNTS", "SNIPPETS_DATA", "SNIPPETS_RESET", "SYSTEM_TICK", "TELEMETRY_IMPRESSION_STATS", "TELEMETRY_PERFORMANCE_EVENT", "TELEMETRY_UNDESIRED_EVENT", "TELEMETRY_USER_EVENT", "TOP_SITES_ADD", "TOP_SITES_CANCEL_EDIT", "TOP_SITES_EDIT", "TOP_SITES_PIN", "TOP_SITES_UNPIN", "TOP_SITES_UPDATED", "UNINIT"]) {
+for (const type of ["BLOCK_URL", "BOOKMARK_URL", "DELETE_BOOKMARK_BY_ID", "DELETE_HISTORY_URL", "DELETE_HISTORY_URL_CONFIRM", "DIALOG_CANCEL", "DIALOG_OPEN", "DISABLE_ONBOARDING", "INIT", "LOCALE_UPDATED", "MIGRATION_CANCEL", "MIGRATION_COMPLETED", "MIGRATION_START", "NEW_TAB_INIT", "NEW_TAB_INITIAL_STATE", "NEW_TAB_LOAD", "NEW_TAB_REHYDRATED", "NEW_TAB_STATE_REQUEST", "NEW_TAB_UNLOAD", "OPEN_LINK", "OPEN_NEW_WINDOW", "OPEN_PRIVATE_WINDOW", "PLACES_BOOKMARK_ADDED", "PLACES_BOOKMARK_CHANGED", "PLACES_BOOKMARK_REMOVED", "PLACES_HISTORY_CLEARED", "PLACES_LINKS_DELETED", "PLACES_LINK_BLOCKED", "PREFS_INITIAL_VALUES", "PREF_CHANGED", "SAVE_SESSION_PERF_DATA", "SAVE_TO_POCKET", "SCREENSHOT_UPDATED", "SECTION_DEREGISTER", "SECTION_DISABLE", "SECTION_ENABLE", "SECTION_OPTIONS_CHANGED", "SECTION_REGISTER", "SECTION_UPDATE", "SECTION_UPDATE_CARD", "SET_PREF", "SHOW_FIREFOX_ACCOUNTS", "SNIPPETS_DATA", "SNIPPETS_RESET", "SYSTEM_TICK", "TELEMETRY_IMPRESSION_STATS", "TELEMETRY_PERFORMANCE_EVENT", "TELEMETRY_UNDESIRED_EVENT", "TELEMETRY_USER_EVENT", "TOP_SITES_ADD", "TOP_SITES_CANCEL_EDIT", "TOP_SITES_EDIT", "TOP_SITES_PIN", "TOP_SITES_UNPIN", "TOP_SITES_UPDATED", "UNINIT"]) {
actionTypes[type] = type;
}
@@ -638,7 +638,8 @@ function Sections(prevState = INITIAL_STATE.Sections, action) {
return item;
})
}));
- case at.PLACES_LINK_DELETED:
+ case at.PLACES_LINKS_DELETED:
+ return prevState.map(section => Object.assign({}, section, { rows: section.rows.filter(site => !action.data.includes(site.url)) }));
case at.PLACES_LINK_BLOCKED:
return prevState.map(section => Object.assign({}, section, { rows: section.rows.filter(site => site.url !== action.data.url) }));
default:
@@ -1511,10 +1512,14 @@ class TopSitesEdit extends React.PureComponent {
"section",
{ className: "edit-topsites-inner-wrapper" },
React.createElement(
- "h3",
- { className: "section-title" },
- React.createElement("span", { className: `icon icon-small-spacer icon-topsites` }),
- React.createElement(FormattedMessage, { id: "header_top_sites" })
+ "div",
+ { className: "section-top-bar" },
+ React.createElement(
+ "h3",
+ { className: "section-title" },
+ React.createElement("span", { className: `icon icon-small-spacer icon-topsites` }),
+ React.createElement(FormattedMessage, { id: "header_top_sites" })
+ )
),
React.createElement(
"ul",
@@ -2569,7 +2574,10 @@ class Section extends React.PureComponent {
contextMenuOptions, intl, initialized
} = this.props;
const maxCards = CARDS_PER_ROW * maxRows;
- const shouldShowTopics = id === "topstories" && this.props.topics && this.props.topics.length > 0;
+
+ // Show topics only for top stories and if it's not initialized yet (so
+ // content doesn't shift when it is loaded) or has loaded with topics
+ const shouldShowTopics = id === "topstories" && (!this.props.topics || this.props.topics.length > 0);
const infoOptionIconA11yAttrs = {
"aria-haspopup": "true",
@@ -2694,6 +2702,9 @@ const { FormattedMessage } = __webpack_require__(2);
const cardContextTypes = __webpack_require__(26);
const { actionCreators: ac, actionTypes: at } = __webpack_require__(0);
+// Keep track of pending image loads to only request once
+const gImageLoading = new Map();
+
/**
* Card component.
* Cards are found within a Section component and contain information about a link such
@@ -2706,11 +2717,47 @@ const { actionCreators: ac, actionTypes: at } = __webpack_require__(0);
class Card extends React.PureComponent {
constructor(props) {
super(props);
- this.state = { showContextMenu: false, activeCard: null };
+ this.state = {
+ activeCard: null,
+ imageLoaded: false,
+ showContextMenu: false
+ };
this.onMenuButtonClick = this.onMenuButtonClick.bind(this);
this.onMenuUpdate = this.onMenuUpdate.bind(this);
this.onLinkClick = this.onLinkClick.bind(this);
}
+
+ /**
+ * Helper to conditionally load an image and update state when it loads.
+ */
+ async maybeLoadImage() {
+ // No need to load if it's already loaded or no image
+ const { image } = this.props.link;
+ if (!this.state.imageLoaded && image) {
+ // Initialize a promise to share a load across multiple card updates
+ if (!gImageLoading.has(image)) {
+ const loaderPromise = new Promise((resolve, reject) => {
+ const loader = new Image();
+ loader.addEventListener("load", resolve);
+ loader.addEventListener("error", reject);
+ loader.src = image;
+ });
+
+ // Save and remove the promise only while it's pending
+ gImageLoading.set(image, loaderPromise);
+ loaderPromise.catch(ex => ex).then(() => gImageLoading.delete(image)).catch();
+ }
+
+ // Wait for the image whether just started loading or reused promise
+ await gImageLoading.get(image);
+
+ // Only update state if we're still waiting to load the original image
+ if (this.props.link.image === image && !this.state.imageLoaded) {
+ this.setState({ imageLoaded: true });
+ }
+ }
+ }
+
onMenuButtonClick(event) {
event.preventDefault();
this.setState({
@@ -2742,6 +2789,18 @@ class Card extends React.PureComponent {
onMenuUpdate(showContextMenu) {
this.setState({ showContextMenu });
}
+ componentDidMount() {
+ this.maybeLoadImage();
+ }
+ componentDidUpdate() {
+ this.maybeLoadImage();
+ }
+ componentWillReceiveProps(nextProps) {
+ // Clear the image state if changing images
+ if (nextProps.link.image !== this.props.link.image) {
+ this.setState({ imageLoaded: false });
+ }
+ }
render() {
const { index, link, dispatch, contextMenuOptions, eventSource, shouldSendImpressionStats } = this.props;
const { props } = this;
@@ -2763,7 +2822,7 @@ class Card extends React.PureComponent {
hasImage && React.createElement(
"div",
{ className: "card-preview-image-outer" },
- React.createElement("div", { className: `card-preview-image${link.image ? " loaded" : ""}`, style: imageStyle })
+ React.createElement("div", { className: `card-preview-image${this.state.imageLoaded ? " loaded" : ""}`, style: imageStyle })
),
React.createElement(
"div",
@@ -2894,7 +2953,7 @@ class Topics extends React.PureComponent {
React.createElement(
"ul",
null,
- topics.map(t => React.createElement(Topic, { key: t.name, url: t.url, name: t.name }))
+ topics && topics.map(t => React.createElement(Topic, { key: t.name, url: t.url, name: t.name }))
),
read_more_endpoint && React.createElement(
"a",
@@ -2985,8 +3044,7 @@ var PrerenderData = new _PrerenderData({
icon: "pocket",
id: "topstories",
order: 1,
- title: { id: "header_recommended_by", values: { provider: "Pocket" } },
- topics: [{}]
+ title: { id: "header_recommended_by", values: { provider: "Pocket" } }
}, {
enabled: true,
id: "highlights",
@@ -3257,6 +3315,10 @@ class SnippetsMap extends Map {
await this.set("blockList", blockList);
}
+ disableOnboarding() {
+ this._dispatch(ac.SendToMain({ type: at.DISABLE_ONBOARDING }));
+ }
+
showFirefoxAccounts() {
this._dispatch(ac.SendToMain({ type: at.SHOW_FIREFOX_ACCOUNTS }));
}
@@ -3395,7 +3457,6 @@ class SnippetsProvider {
if (needsUpdate && this.appData.snippetsURL) {
this.snippetsMap.set("snippets-last-update", Date.now());
try {
- // TODO: timeout?
const response = await fetch(this.appData.snippetsURL);
if (response.status === 200) {
const payload = await response.text();
@@ -3409,10 +3470,18 @@ class SnippetsProvider {
}
}
- _showDefaultSnippets() {
+ _noSnippetFallback() {
// TODO
}
+ _forceOnboardingVisibility(shouldBeVisible) {
+ const onboardingEl = document.getElementById("onboarding-notification-bar");
+
+ if (onboardingEl) {
+ onboardingEl.style.display = shouldBeVisible ? "" : "none";
+ }
+ }
+
_showRemoteSnippets() {
const snippetsEl = document.getElementById(this.elementId);
const payload = this.snippetsMap.get("snippets");
@@ -3480,15 +3549,18 @@ class SnippetsProvider {
try {
this._showRemoteSnippets();
} catch (e) {
- this._showDefaultSnippets(e);
+ this._noSnippetFallback(e);
}
window.dispatchEvent(new Event(SNIPPETS_ENABLED_EVENT));
+
+ this._forceOnboardingVisibility(true);
this.initialized = true;
}
uninit() {
window.dispatchEvent(new Event(SNIPPETS_DISABLED_EVENT));
+ this._forceOnboardingVisibility(false);
this.initialized = false;
}
}
@@ -3508,16 +3580,16 @@ function addSnippetsSubscriber(store) {
store.subscribe(async () => {
const state = store.getState();
- // state.Snippets.initialized: Should snippets be initialised?
- // snippets.initialized: Is SnippetsProvider currently initialised?
- if (state.Snippets.initialized && !snippets.initialized && state.Snippets.onboardingFinished) {
- // Don't call init multiple times
- if (!initializing) {
- initializing = true;
- await snippets.init({ appData: state.Snippets });
- initializing = false;
- }
- } else if (state.Snippets.initialized === false && snippets.initialized) {
+ // state.Prefs.values["feeds.snippets"]: Should snippets be shown?
+ // state.Snippets.initialized Is the snippets data initialized?
+ // snippets.initialized: Is SnippetsProvider currently initialised?
+ if (state.Prefs.values["feeds.snippets"] && state.Snippets.initialized && !snippets.initialized &&
+ // Don't call init multiple times
+ !initializing) {
+ initializing = true;
+ await snippets.init({ appData: state.Snippets });
+ initializing = false;
+ } else if (state.Prefs.values["feeds.snippets"] === false && snippets.initialized) {
snippets.uninit();
}
});
diff --git a/browser/extensions/activity-stream/data/content/activity-stream.css b/browser/extensions/activity-stream/data/content/activity-stream.css
index 81e33d2d0e80..6c5d239a2d5a 100644
--- a/browser/extensions/activity-stream/data/content/activity-stream.css
+++ b/browser/extensions/activity-stream/data/content/activity-stream.css
@@ -457,26 +457,24 @@ main {
padding-inline-start: 3px; }
.topsite-form .form-wrapper {
+ margin: auto;
+ max-width: 350px;
padding: 15px 0; }
.topsite-form .form-wrapper .field {
- margin-inline-start: 205px;
position: relative; }
.topsite-form .form-wrapper .url input:not(:placeholder-shown):dir(rtl) {
direction: ltr;
text-align: right; }
.topsite-form .form-wrapper .section-title {
- margin-bottom: 5px;
- margin-inline-start: 205px; }
+ margin-bottom: 5px; }
.topsite-form .form-wrapper input[type='text'] {
border: solid 1px rgba(12, 12, 13, 0.2);
border-radius: 2px;
margin: 5px 0;
padding: 7px;
- width: 350px; }
+ width: 100%; }
.topsite-form .form-wrapper input[type='text']:focus {
border: solid 1px rgba(12, 12, 13, 0.4); }
- .topsite-form .form-wrapper input[type='text']::placeholder {
- font-style: italic; }
.topsite-form .form-wrapper .invalid input[type='text'] {
border: solid 1px #D70022;
box-shadow: 0 0 0 2px rgba(215, 0, 34, 0.35); }
@@ -676,28 +674,19 @@ main {
width: 100%;
height: 36px; }
.search-wrapper input {
- border: 0;
- box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1);
border: 1px solid rgba(0, 0, 0, 0.15);
- box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1);
border-radius: 3px;
- border-radius: 4px;
+ box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1);
color: inherit;
padding: 0;
padding-inline-end: 36px;
padding-inline-start: 35px;
width: 100%;
font-size: 15px; }
- .search-wrapper input:focus {
- border-color: #0060DF;
- box-shadow: 0 0 0 2px #0060DF;
- z-index: 1; }
- .search-wrapper input:focus + .search-button {
- z-index: 1;
- background-color: #0060DF;
- background-image: url("chrome://browser/skin/forward.svg");
- fill: #FFF;
- -moz-context-properties: fill; }
+ .search-wrapper:active input,
+ .search-wrapper input:focus {
+ border-color: #0A84FF;
+ box-shadow: 0 0 0 2px #0A84FF; }
.search-wrapper .search-label {
background: url("chrome://browser/skin/search-glass.svg") no-repeat 12px center/16px;
fill: rgba(12, 12, 13, 0.4);
@@ -705,8 +694,7 @@ main {
position: absolute;
offset-inline-start: 0;
height: 100%;
- width: 35px;
- z-index: 2; }
+ width: 35px; }
.search-wrapper .search-button {
background: url("chrome://browser/skin/forward.svg") no-repeat center center;
border-radius: 0 3px 3px 0;
@@ -718,11 +706,11 @@ main {
height: 100%;
offset-inline-end: 0;
position: absolute; }
- .search-wrapper .search-button:hover {
- z-index: 1;
- background-color: #0060DF;
- fill: #FFF;
+ .search-wrapper .search-button:focus, .search-wrapper .search-button:hover {
+ background-color: rgba(12, 12, 13, 0.1);
cursor: pointer; }
+ .search-wrapper .search-button:active {
+ background-color: rgba(12, 12, 13, 0.15); }
.search-wrapper .search-button:dir(rtl) {
transform: scaleX(-1); }
.search-wrapper .contentSearchSuggestionTable {
@@ -1009,13 +997,11 @@ main {
.card-outer:-moz-any(:hover, :focus, .active):not(.placeholder) .card-title {
color: #0060DF; }
.card-outer .card-preview-image-outer {
+ background-color: #F9F9FA;
position: relative;
- background: linear-gradient(135deg, #B1B1B3, #D7D7DB);
height: 122px;
border-radius: 3px 3px 0 0;
overflow: hidden; }
- .card-outer .card-preview-image-outer:dir(rtl) {
- background: linear-gradient(225deg, #B1B1B3, #D7D7DB); }
.card-outer .card-preview-image-outer::after {
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
bottom: 0;
@@ -1028,8 +1014,8 @@ main {
background-size: cover;
background-position: center;
background-repeat: no-repeat;
- transition: opacity 1s;
- opacity: 0; }
+ opacity: 0;
+ transition: opacity 1s cubic-bezier(0.07, 0.95, 0, 1); }
.card-outer .card-preview-image-outer .card-preview-image.loaded {
opacity: 1; }
.card-outer .card-details {
diff --git a/browser/extensions/activity-stream/data/locales.json b/browser/extensions/activity-stream/data/locales.json
index f24bf2d1c53f..fb0979a9b847 100644
--- a/browser/extensions/activity-stream/data/locales.json
+++ b/browser/extensions/activity-stream/data/locales.json
@@ -580,7 +580,51 @@
"time_label_hour": "{number}e",
"time_label_day": "{number}d",
"settings_pane_button_label": "Personelait ho pajenn Ivinell Nevez",
- "settings_pane_header": "Gwellvezioù an ivinell nevez"
+ "settings_pane_header": "Gwellvezioù an ivinell nevez",
+ "settings_pane_body2": "Dibabit petra a welit war ar bajenn-mañ.",
+ "settings_pane_search_header": "Klask",
+ "settings_pane_search_body": "Klask er web adalek an ivinell nevez.",
+ "settings_pane_topsites_header": "Lec'hiennoù gwellañ",
+ "settings_pane_topsites_body": "Kit war al lec'hiennoù gweladennet ar muiañ ganeoc'h.",
+ "settings_pane_topsites_options_showmore": "Diskouez daou vann",
+ "settings_pane_bookmarks_header": "Sinedoù nevez",
+ "settings_pane_bookmarks_body": "Ho sinedoù nevez strollet en ul lec'h aes da dizhout.",
+ "settings_pane_visit_again_header": "Gweladenniñ en-dro",
+ "settings_pane_visit_again_body": "Firefox a ziskouezo deoc'h ul lodenn eus ho roll istor a c'hallfec'h kaout c'hoant da zerc'hel soñj pe da zistreiñ eno.",
+ "settings_pane_highlights_header": "Mareoù pouezus",
+ "settings_pane_highlights_body2": "Adkavit an traoù dedennus gweladennet pe lakaet er sinedoù nevez ’zo.",
+ "settings_pane_highlights_options_bookmarks": "Sinedoù",
+ "settings_pane_highlights_options_visited": "Lec'hiennoù gweladennet",
+ "settings_pane_snippets_header": "Notennigoù",
+ "settings_pane_snippets_body": "Lennit an hizivadurioù berr ha dous graet gant Mozilla evit Firefox, sevenadur ar genrouedad, hag ur mem dre-zegouezh ur wech an amzer.",
+ "settings_pane_done_button": "Graet",
+ "edit_topsites_button_text": "Embann",
+ "edit_topsites_button_label": "Personelaat ar gevrenn “lec'hiennoù gweladennet ar muiañ”",
+ "edit_topsites_showmore_button": "Diskouez muioc'h",
+ "edit_topsites_showless_button": "Diskouez nebeutoc'h",
+ "edit_topsites_done_button": "Graet",
+ "edit_topsites_pin_button": "Spilhennañ al lec'hienn-mañ",
+ "edit_topsites_unpin_button": "Dispilhennañ al lec'hienn-mañ",
+ "edit_topsites_edit_button": "Embann al lec'hienn-mañ",
+ "edit_topsites_dismiss_button": "Dilemel al lec'hienn-mañ",
+ "edit_topsites_add_button": "Ouzhpennañ",
+ "topsites_form_add_header": "Lec'hiennoù gwellañ nevez",
+ "topsites_form_edit_header": "Embann al Lec'hiennoù Gwellañ",
+ "topsites_form_title_placeholder": "Enankañ un titl",
+ "topsites_form_url_placeholder": "Skrivit pe pegit un URL",
+ "topsites_form_add_button": "Ouzhpennañ",
+ "topsites_form_save_button": "Enrollañ",
+ "topsites_form_cancel_button": "Nullañ",
+ "topsites_form_url_validation": "URL talvoudek azgoulennet",
+ "pocket_read_more": "Danvezioù brudet:",
+ "pocket_read_even_more": "Gwelet muioc'h a istorioù",
+ "pocket_feedback_header": "Ar gwellañ eus ar web, dibabet gant ouzhpenn 25 milion a dud.",
+ "pocket_description": "Dizoloit pennadoù eus an dibab ho pije gellout c'hwitout a-hent all warno, a-drugarez da bPocket, hag a zo bremañ ul lodenn deus Mozilla.",
+ "highlights_empty_state": "Krogit da verdeiñ hag e tiskouezimp deoc’h pennadoù, videoioù ha pajennoù all gweladennet pe lakaet er sinedoù nevez ’zo.",
+ "topstories_empty_state": "Aet oc'h betek penn. Distroit diwezhatoc'h evit muioc’h a istorioù digant {provider}. N’oc'h ket evit gortoz? Dibabit un danvez brudet evit klask muioc’h a bennadoù dedennus eus pep lec’h er web.",
+ "manual_migration_explanation2": "Amprouit Firefox gant sinedoù, roll istor ha gerioù-tremen ur merdeer all.",
+ "manual_migration_cancel_button": "N'am bo ket",
+ "manual_migration_import_button": "Emporzhiañ bremañ"
},
"ca": {
"newtab_page_title": "Pestanya nova",
@@ -955,6 +999,7 @@
"default_label_loading": "Indlæser…",
"header_top_sites": "Mest besøgte websider",
"header_stories": "Tophistorier",
+ "header_highlights": "Højdepunkter",
"header_visit_again": "Besøg igen",
"header_bookmarks": "Seneste bogmærker",
"header_recommended_by": "Anbefalet af {provider}",
@@ -986,6 +1031,7 @@
"search_web_placeholder": "Søg på internettet",
"search_settings": "Skift søgeindstillinger",
"section_info_option": "Info",
+ "section_info_send_feedback": "Send feedback",
"welcome_title": "Velkommen til nyt faneblad",
"welcome_body": "Firefox vil bruge denne plads til at vise dine mest relevante bogmærker, artikler, videoer og sider, du har besøgt for nylig - så kan du nemmere finde dem.",
"welcome_label": "Finder dine højdepunkter",
@@ -995,7 +1041,6 @@
"time_label_day": "{number} d.",
"settings_pane_button_label": "Tilpas siden Nyt faneblad",
"settings_pane_header": "Indstillinger for Nyt faneblad",
- "settings_pane_body": "Vælg, hvad der vises, når du åbner et nyt faneblad.",
"settings_pane_search_header": "Søgning",
"settings_pane_search_body": "Søg på nettet fra Nyt faneblad.",
"settings_pane_topsites_header": "Mest besøgte websider",
@@ -1005,8 +1050,6 @@
"settings_pane_bookmarks_body": "Dine seneste bogmærker samlet ét sted.",
"settings_pane_visit_again_header": "Besøg igen",
"settings_pane_visit_again_body": "Firefox viser dig dele af din browserhistorik, som du måske vil huske på eller vende tilbage til.",
- "settings_pane_pocketstories_header": "Tophistorier",
- "settings_pane_pocketstories_body": "Pocket, en del af Mozilla-familien, hjælper dig med at opdage indhold af høj kvalitet, som du måske ellers ikke ville have fundet.",
"settings_pane_done_button": "Færdig",
"edit_topsites_button_text": "Rediger",
"edit_topsites_button_label": "Tilpas afsnittet Mest besøgte websider",
@@ -1029,10 +1072,7 @@
"pocket_read_more": "Populære emner:",
"pocket_read_even_more": "Se flere historier",
"pocket_feedback_header": "Det bedste fra nettet, udvalgt af mere end 25 millioner mennesker.",
- "pocket_feedback_body": "Pocket, en del af Mozilla-familien, hjælper dig med at opdage indhold af høj kvalitet, som du måske ellers ikke ville have fundet.",
- "pocket_send_feedback": "Send feedback",
"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_explanation": "Prøv Firefox med dine favorit-websteder og bogmærker fra en anden browser.",
"manual_migration_cancel_button": "Nej tak",
"manual_migration_import_button": "Importer nu"
},
@@ -3788,8 +3828,8 @@
"kk": {
"newtab_page_title": "Жаңа бет",
"default_label_loading": "Жүктелуде…",
- "header_top_sites": "Топ сайттар",
- "header_stories": "Топ хикаялар",
+ "header_top_sites": "Үздік сайттар",
+ "header_stories": "Үздік хикаялар",
"header_highlights": "Ерекше жаңалықтар",
"header_visit_again": "Қайтадан шолу",
"header_bookmarks": "Соңғы бетбелгілер",
@@ -3815,7 +3855,7 @@
"menu_action_unpin": "Бекітуді алып тастау",
"confirm_history_delete_p1": "Бұл парақтың барлық кездесулерін шолу тарихыңыздан өшіруді қалайсыз ба?",
"confirm_history_delete_notice_p2": "Бұл әрекетті болдырмау мүмкін болмайды.",
- "menu_action_save_to_pocket": "Pocket-ке сақтау",
+ "menu_action_save_to_pocket": "Pocket ішіне сақтау",
"search_for_something_with": "{search_term} ұғымын көмегімен іздеу:",
"search_button": "Іздеу",
"search_header": "{search_engine_name} іздеуі",
@@ -3833,10 +3873,10 @@
"time_label_day": "{number} күн",
"settings_pane_button_label": "Жаңа бетті баптаңыз",
"settings_pane_header": "Жаңа бет баптаулары",
- "settings_pane_body2": "Бұл парақта не көргіңіз келетінді таңдаңыз.",
+ "settings_pane_body2": "Бұл бетте не көргіңіз келетінді таңдаңыз.",
"settings_pane_search_header": "Іздеу",
"settings_pane_search_body": "Жаңа беттен интернеттен іздеңіз.",
- "settings_pane_topsites_header": "Топ сайттар",
+ "settings_pane_topsites_header": "Үздік сайттар",
"settings_pane_topsites_body": "Көбірек қаралатын сайттарға қатынау.",
"settings_pane_topsites_options_showmore": "Екі жолды көрсету",
"settings_pane_bookmarks_header": "Соңғы бетбелгілер",
@@ -3848,7 +3888,7 @@
"settings_pane_highlights_options_bookmarks": "Бетбелгілер",
"settings_pane_highlights_options_visited": "Ашылған сайттар",
"settings_pane_snippets_header": "Үзінділер",
- "settings_pane_snippets_body": "Mozilla-дан Firefox және интернет мәдениеті туралы қысқа жаңалықтарды, және кездейсоқ мемдерді оқыңыз.",
+ "settings_pane_snippets_body": "Mozilla ұсынған Firefox және интернет мәдениеті туралы қысқа жаңалықтарды, және кездейсоқ мемдерді оқыңыз.",
"settings_pane_done_button": "Дайын",
"edit_topsites_button_text": "Түзету",
"edit_topsites_button_label": "Топ сайттар санатын баптау",
@@ -3874,7 +3914,7 @@
"pocket_description": "Ол болмаса, сіз жіберіп алатын мүмкіндігі бар жоғары сапалы құраманы Pocket көмегімен табыңыз, ол енді Mozilla-ның бөлігі болып табылады.",
"highlights_empty_state": "Шолуды бастаңыз, сіз жақында шолған немесе бетбелгілерге қосқан тамаша мақалалар, видеолар немесе басқа парақтардың кейбіреулері осында көрсетіледі.",
"topstories_empty_state": "Дайын. {provider} ұсынған көбірек мақалаларды алу үшін кейінірек тексеріңіз. Күте алмайсыз ба? Интернеттен көбірек тамаша мақалаларды алу үшін әйгілі теманы таңдаңыз.",
- "manual_migration_explanation2": "Firefox-ты басқа браузер бетбелгілері, тарихы және парольдерімен қолданып көріңіз.",
+ "manual_migration_explanation2": "Firefox қолданбасын басқа браузер бетбелгілері, тарихы және парольдерімен қолданып көріңіз.",
"manual_migration_cancel_button": "Жоқ, рахмет",
"manual_migration_import_button": "Қазір импорттау"
},
@@ -4203,13 +4243,15 @@
"manual_migration_import_button": "Importuoti dabar"
},
"lv": {
- "newtab_page_title": "Jauna cilne"
+ "newtab_page_title": "Jauna cilne",
+ "default_label_loading": "Notiek ielāde…"
},
"mk": {
"newtab_page_title": "Ново јазиче",
"default_label_loading": "Се вчитува…",
- "header_top_sites": "Врвни мрежни места",
- "header_stories": "Врвни написи",
+ "header_top_sites": "Популарни мрежни места",
+ "header_stories": "Популарни написи",
+ "header_highlights": "Интереси",
"header_visit_again": "Посети повторно",
"header_bookmarks": "Скорешни обележувачи",
"header_recommended_by": "Препорачано од {provider}",
@@ -4232,7 +4274,7 @@
"menu_action_delete": "Избриши од историја",
"menu_action_pin": "Прикачи",
"menu_action_unpin": "Откачи",
- "confirm_history_delete_p1": "Дали сте сигурни дека сакате да ја избришете оваа страница отсекаде во Вашата историја на прелистување?",
+ "confirm_history_delete_p1": "Дали сте сигурни дека сакате да ја избришете оваа страница отсекаде во вашата историја на прелистување?",
"confirm_history_delete_notice_p2": "Ова дејство не може да се одврати.",
"menu_action_save_to_pocket": "Зачувај во Pocket",
"search_for_something_with": "Пребарај за {search_term} со:",
@@ -4241,30 +4283,36 @@
"search_web_placeholder": "Пребарајте на Интернет",
"search_settings": "Промени поставувања за пребарување",
"section_info_option": "Инфо",
+ "section_info_send_feedback": "Испрати мислење",
+ "section_info_privacy_notice": "Белешка за приватност",
"welcome_title": "Добредојдовте во новото јазиче",
"welcome_body": "Firefox ќе го искористи овој простор за да Ви ги прикаже најрелевантните обележувачи, написи, видеа и страници што сте ги посетиле, за да можете лесно да им се навратите.",
- "welcome_label": "Ги откривам Вашите интереси",
+ "welcome_label": "Ги откривам вашите Интереси",
"time_label_less_than_minute": "< 1 м",
"time_label_minute": "{number} м",
"time_label_hour": "{number} ч",
"time_label_day": "{number} д",
- "settings_pane_button_label": "Прилагодете ја страницата на Вашето Ново јазиче",
+ "settings_pane_button_label": "Прилагодете ја страницата на вашето Ново јазиче",
"settings_pane_header": "Преференци за Ново јазиче",
- "settings_pane_body": "Изберете што ќе гледате кога ќе отворите ново јазиче.",
+ "settings_pane_body2": "Изберете што ќе гледате на оваа страница.",
"settings_pane_search_header": "Пребарување",
- "settings_pane_search_body": "Пребарајте низ Интернет од Вашето ново јазиче.",
+ "settings_pane_search_body": "Пребарајте низ Интернет од вашето ново јазиче.",
"settings_pane_topsites_header": "Врвни мрежни места",
"settings_pane_topsites_body": "Пристапете до мрежните места што ги посетувате најмногу.",
"settings_pane_topsites_options_showmore": "Прикажи два реда",
"settings_pane_bookmarks_header": "Скорешни обележувачи",
"settings_pane_bookmarks_body": "Вашите нови обележувачи во едно згодно место.",
"settings_pane_visit_again_header": "Посети повторно",
- "settings_pane_visit_again_body": "Firefox ќе прикаже делови од Вашата историја на прелистување кои можеби би сакале да ги запомните или пак да им се навратите.",
- "settings_pane_pocketstories_header": "Врвни написи",
- "settings_pane_pocketstories_body": "Pocket, дел од семејството на Mozilla, ќе Ви помогне да стигнете до високо-квалитетни содржини кои можеби не би ги откриле на друг начин.",
+ "settings_pane_visit_again_body": "Firefox ќе прикаже делови од вашата историја на прелистување кои можеби би сакале да ги запомните или пак да им се навратите.",
+ "settings_pane_highlights_header": "Интереси",
+ "settings_pane_highlights_body2": "Навратете се на интересни места што неодамна сте ги посетиле или обележале.",
+ "settings_pane_highlights_options_bookmarks": "Обележувачи",
+ "settings_pane_highlights_options_visited": "Посетени мрежни места",
+ "settings_pane_snippets_header": "Исечоци",
+ "settings_pane_snippets_body": "Прочитајте кратки и слатки новости од Mozilla во врска со Firefox, Интернет-културата и повремените случајни меми.",
"settings_pane_done_button": "Готово",
"edit_topsites_button_text": "Уреди",
- "edit_topsites_button_label": "Прилагодете ги Вашите Врвни мрежни места",
+ "edit_topsites_button_label": "Прилагодете ги вашите Популарни мрежни места",
"edit_topsites_showmore_button": "Прикажи повеќе",
"edit_topsites_showless_button": "Прикажи помалку",
"edit_topsites_done_button": "Готово",
@@ -4284,10 +4332,10 @@
"pocket_read_more": "Популарни теми:",
"pocket_read_even_more": "Види повеќе написи",
"pocket_feedback_header": "Најдоброто од Интернет, одбрано од повеќе од 25 милиони луѓе.",
- "pocket_feedback_body": "Pocket, дел од семејството на Mozilla, ќе Ви помогне да стигнете до високо-квалитетни содржини кои можеби не би ги откриле на друг начин.",
- "pocket_send_feedback": "Остави коментар",
+ "pocket_description": "Откријте високо-квалитетни содржини, коишто инаку би можеле да ги пропуштите, со помош на Pocket, кој сега е дел од Mozilla.",
+ "highlights_empty_state": "Започнете со прелистување и ние овде ќе ви прикажеме некои од одличните написи, видеа и други страници што неодамна сте ги поселите или обележале.",
"topstories_empty_state": "Имате видено сѐ! Навратете се подоцна за нови содржини од {provider}. Не можете да чекате? Изберете популарна тема и откријте уште одлични содржини ширум Интернет.",
- "manual_migration_explanation": "Пробајте го Firefox со Вашите омилени мрежни места и обележувачи од друг прелистувач.",
+ "manual_migration_explanation2": "Пробајте го Firefox со обележувачите, историјата и лозинките на друг прелистувач.",
"manual_migration_cancel_button": "Не, благодарам",
"manual_migration_import_button": "Увези сега"
},
@@ -6092,7 +6140,7 @@
"header_stories": "เรื่องราวเด่น",
"header_highlights": "รายการเด่น",
"header_visit_again": "เยี่ยมชมอีกครั้ง",
- "header_bookmarks": "ที่คั่นหน้าเมื่อเร็ว ๆ นี้",
+ "header_bookmarks": "ที่คั่นหน้าล่าสุด",
"header_recommended_by": "แนะนำโดย {provider}",
"header_bookmarks_placeholder": "คุณยังไม่มีที่คั่นหน้าใด ๆ",
"header_stories_from": "จาก",
@@ -6139,7 +6187,7 @@
"settings_pane_topsites_header": "ไซต์เด่น",
"settings_pane_topsites_body": "เข้าถึงเว็บไซต์ที่คุณเยี่ยมชมมากที่สุด",
"settings_pane_topsites_options_showmore": "แสดงสองแถว",
- "settings_pane_bookmarks_header": "ที่คั่นหน้าเมื่อเร็ว ๆ นี้",
+ "settings_pane_bookmarks_header": "ที่คั่นหน้าล่าสุด",
"settings_pane_bookmarks_body": "ที่คั่นหน้าที่สร้างใหม่ของคุณในตำแหน่งที่ตั้งเดียวที่สะดวก",
"settings_pane_visit_again_header": "เยี่ยมชมอีกครั้ง",
"settings_pane_highlights_header": "รายการเด่น",
@@ -6617,8 +6665,8 @@
"settings_pane_highlights_body2": "根据您最近访问的页面和添加的书签推荐您感兴趣的东西。",
"settings_pane_highlights_options_bookmarks": "书签",
"settings_pane_highlights_options_visited": "访问过的网站",
- "settings_pane_snippets_header": "板报",
- "settings_pane_snippets_body": "阅读和了解 Mozilla 就 Firefox、互联网文化等提供的一些简短而有趣的更新。",
+ "settings_pane_snippets_header": "只言片语",
+ "settings_pane_snippets_body": "阅读 Mozilla 就 Firefox、互联网文化、偶尔还有模因提供的一些简短而有趣的小文章。",
"settings_pane_done_button": "完成",
"edit_topsites_button_text": "编辑",
"edit_topsites_button_label": "定制您的“常用网站”区域",
@@ -6641,7 +6689,7 @@
"pocket_read_more": "热门主题:",
"pocket_read_even_more": "查看更多文章",
"pocket_feedback_header": "由超过 2500 万人挑选出来的网上精华内容。",
- "pocket_description": "借助 Pocket(目前所属 Mozilla)发现有趣的高品质内容。",
+ "pocket_description": "借助 Pocket(目前属 Mozilla 旗下)发现您不容错过的高品质内容。",
"highlights_empty_state": "开始浏览旅程吧,之后这里会显示您最近看过或加了书签的精彩文章、视频以及其他页面。",
"topstories_empty_state": "所有文章都读完啦!晚点再来,{provider} 将推荐更多热门文章。等不及了?选择一个热门话题,找到更多网上的好文章。",
"manual_migration_explanation2": "把在其他浏览器中保存的书签、历史记录和密码带到 Firefox 吧。",
diff --git a/browser/extensions/activity-stream/install.rdf.in b/browser/extensions/activity-stream/install.rdf.in
index 6d13034a391f..1d46f75e7951 100644
--- a/browser/extensions/activity-stream/install.rdf.in
+++ b/browser/extensions/activity-stream/install.rdf.in
@@ -8,7 +8,7 @@
2
true
false
- 2017.09.22.1389-2ee94db4
+ 2017.09.27.1211-43262ffa
Activity Stream
A rich visual history feed and a reimagined home page make it easier than ever to find exactly what you're looking for in Firefox.
true
diff --git a/browser/extensions/activity-stream/lib/HighlightsFeed.jsm b/browser/extensions/activity-stream/lib/HighlightsFeed.jsm
index d82f6f4d13d5..5732e25c885f 100644
--- a/browser/extensions/activity-stream/lib/HighlightsFeed.jsm
+++ b/browser/extensions/activity-stream/lib/HighlightsFeed.jsm
@@ -160,7 +160,7 @@ this.HighlightsFeed = class HighlightsFeed {
break;
case at.MIGRATION_COMPLETED:
case at.PLACES_HISTORY_CLEARED:
- case at.PLACES_LINK_DELETED:
+ case at.PLACES_LINKS_DELETED:
case at.PLACES_LINK_BLOCKED:
this.fetchHighlights(true);
break;
diff --git a/browser/extensions/activity-stream/lib/PlacesFeed.jsm b/browser/extensions/activity-stream/lib/PlacesFeed.jsm
index e4359ae96912..ecdf6c1f3d9c 100644
--- a/browser/extensions/activity-stream/lib/PlacesFeed.jsm
+++ b/browser/extensions/activity-stream/lib/PlacesFeed.jsm
@@ -42,11 +42,24 @@ class HistoryObserver extends Observer {
* @param {obj} uri A URI object representing the link's url
* {str} uri.spec The URI as a string
*/
- onDeleteURI(uri) {
- this.dispatch({
- type: at.PLACES_LINK_DELETED,
- data: {url: uri.spec}
- });
+ async onDeleteURI(uri) {
+ // Add to an existing array of links if we haven't dispatched yet
+ const {spec} = uri;
+ if (this._deletedLinks) {
+ this._deletedLinks.push(spec);
+ } else {
+ // Store an array of synchronously deleted links
+ this._deletedLinks = [spec];
+
+ // Only dispatch a single action when we've gotten all deleted urls
+ await Promise.resolve().then(() => {
+ this.dispatch({
+ type: at.PLACES_LINKS_DELETED,
+ data: this._deletedLinks
+ });
+ delete this._deletedLinks;
+ });
+ }
}
/**
diff --git a/browser/extensions/activity-stream/lib/PrefsFeed.jsm b/browser/extensions/activity-stream/lib/PrefsFeed.jsm
index a5391acbde97..5d23ce9e8ced 100644
--- a/browser/extensions/activity-stream/lib/PrefsFeed.jsm
+++ b/browser/extensions/activity-stream/lib/PrefsFeed.jsm
@@ -8,6 +8,9 @@ const {utils: Cu} = Components;
const {actionCreators: ac, actionTypes: at} = Cu.import("resource://activity-stream/common/Actions.jsm", {});
const {Prefs} = Cu.import("resource://activity-stream/lib/ActivityStreamPrefs.jsm", {});
const {PrerenderData} = Cu.import("resource://activity-stream/common/PrerenderData.jsm", {});
+Cu.import("resource://gre/modules/Services.jsm");
+
+const ONBOARDING_FINISHED_PREF = "browser.onboarding.notification.finished";
this.PrefsFeed = class PrefsFeed {
constructor(prefMap) {
@@ -27,11 +30,30 @@ this.PrefsFeed = class PrefsFeed {
}
}
+ _initOnboardingPref() {
+ const snippetsEnabled = this._prefs.get("feeds.snippets");
+ if (!snippetsEnabled) {
+ this.setOnboardingDisabledDefault(true);
+ }
+ }
+
+ setOnboardingDisabledDefault(value) {
+ const branch = Services.prefs.getDefaultBranch("");
+ branch.setBoolPref(ONBOARDING_FINISHED_PREF, value);
+ }
+
onPrefChanged(name, value) {
if (this._prefMap.has(name)) {
this.store.dispatch(ac.BroadcastToContent({type: at.PREF_CHANGED, data: {name, value}}));
}
+
this._checkPrerender(name);
+
+ if (name === "feeds.snippets") {
+ // If snippets are disabled, onboarding notifications should also be
+ // disabled because they look like snippets.
+ this.setOnboardingDisabledDefault(!value);
+ }
}
init() {
@@ -47,6 +69,7 @@ this.PrefsFeed = class PrefsFeed {
this.store.dispatch(ac.BroadcastToContent({type: at.PREFS_INITIAL_VALUES, data: values}));
this._setPrerenderPref();
+ this._initOnboardingPref();
}
removeListeners() {
this._prefs.ignoreBranch(this);
@@ -62,6 +85,9 @@ this.PrefsFeed = class PrefsFeed {
case at.SET_PREF:
this._prefs.set(action.data.name, action.data.value);
break;
+ case at.DISABLE_ONBOARDING:
+ this.setOnboardingDisabledDefault(true);
+ break;
}
}
};
diff --git a/browser/extensions/activity-stream/lib/SnippetsFeed.jsm b/browser/extensions/activity-stream/lib/SnippetsFeed.jsm
index aba53e9156a2..c95f338f1668 100644
--- a/browser/extensions/activity-stream/lib/SnippetsFeed.jsm
+++ b/browser/extensions/activity-stream/lib/SnippetsFeed.jsm
@@ -17,8 +17,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "ProfileAge",
// Url to fetch snippets, in the urlFormatter service format.
const SNIPPETS_URL_PREF = "browser.aboutHomeSnippets.updateUrl";
const TELEMETRY_PREF = "datareporting.healthreport.uploadEnabled";
-const ONBOARDING_FINISHED_PREF = "browser.onboarding.notification.finished";
const FXA_USERNAME_PREF = "services.sync.username";
+const ONBOARDING_FINISHED_PREF = "browser.onboarding.notification.finished";
// Prefix for any target matching a search engine.
const TARGET_SEARCHENGINE_PREFIX = "searchEngine-";
diff --git a/browser/extensions/activity-stream/lib/TelemetryFeed.jsm b/browser/extensions/activity-stream/lib/TelemetryFeed.jsm
index c7f96352400b..f83c82f03f8f 100644
--- a/browser/extensions/activity-stream/lib/TelemetryFeed.jsm
+++ b/browser/extensions/activity-stream/lib/TelemetryFeed.jsm
@@ -21,6 +21,7 @@ XPCOMUtils.defineLazyServiceGetter(this, "gUUIDGenerator",
"@mozilla.org/uuid-generator;1",
"nsIUUIDGenerator");
+const ACTIVITY_STREAM_ID = "activity-stream";
const ACTIVITY_STREAM_ENDPOINT_PREF = "browser.newtabpage.activity-stream.telemetry.ping.endpoint";
// This is a mapping table between the user preferences and its encoding code
@@ -160,12 +161,10 @@ this.TelemetryFeed = class TelemetryFeed {
* Lazily initialize PingCentre to send pings
*/
get pingCentre() {
- const ACTIVITY_STREAM_ID = "activity-stream";
Object.defineProperty(this, "pingCentre",
{
value: new PingCentre({
topic: ACTIVITY_STREAM_ID,
- filter: ACTIVITY_STREAM_ID,
overrideEndpointPref: ACTIVITY_STREAM_ENDPOINT_PREF
})
});
@@ -363,7 +362,8 @@ this.TelemetryFeed = class TelemetryFeed {
async sendEvent(event_object) {
if (this.telemetryEnabled) {
- this.pingCentre.sendPing(event_object);
+ this.pingCentre.sendPing(event_object,
+ {filter: ACTIVITY_STREAM_ID});
}
}
diff --git a/browser/extensions/activity-stream/lib/TopSitesFeed.jsm b/browser/extensions/activity-stream/lib/TopSitesFeed.jsm
index d6f670e44c37..04dafcd4cd85 100644
--- a/browser/extensions/activity-stream/lib/TopSitesFeed.jsm
+++ b/browser/extensions/activity-stream/lib/TopSitesFeed.jsm
@@ -264,8 +264,8 @@ this.TopSitesFeed = class TopSitesFeed {
// All these actions mean we need new top sites
case at.MIGRATION_COMPLETED:
case at.PLACES_HISTORY_CLEARED:
- case at.PLACES_LINK_DELETED:
case at.PLACES_LINK_BLOCKED:
+ case at.PLACES_LINKS_DELETED:
this.frecentCache.expire();
this.refresh();
break;
diff --git a/browser/extensions/activity-stream/test/unit/common/Reducers.test.js b/browser/extensions/activity-stream/test/unit/common/Reducers.test.js
index 3916b870975e..6d97e29c437d 100644
--- a/browser/extensions/activity-stream/test/unit/common/Reducers.test.js
+++ b/browser/extensions/activity-stream/test/unit/common/Reducers.test.js
@@ -358,13 +358,22 @@ describe("Reducers", () => {
});
it("should remove blocked and deleted urls from all rows in all sections", () => {
const blockAction = {type: at.PLACES_LINK_BLOCKED, data: {url: "www.foo.bar"}};
- const deleteAction = {type: at.PLACES_LINK_DELETED, data: {url: "www.foo.bar"}};
+ const deleteAction = {type: at.PLACES_LINKS_DELETED, data: ["www.foo.bar"]};
const newBlockState = Sections(oldState, blockAction);
const newDeleteState = Sections(oldState, deleteAction);
newBlockState.concat(newDeleteState).forEach(section => {
assert.deepEqual(section.rows, [{url: "www.other.url"}]);
});
});
+ it("should remove all deleted urls", () => {
+ const deleteAction = {type: at.PLACES_LINKS_DELETED, data: ["www.foo.bar", "www.other.url"]};
+
+ const newState = Sections(oldState, deleteAction);
+
+ newState.forEach(section => {
+ assert.lengthOf(section.rows, 0);
+ });
+ });
it("should not update state for empty action.data on PLACES_BOOKMARK_ADDED", () => {
const nextState = Sections(undefined, {type: at.PLACES_BOOKMARK_ADDED});
assert.equal(nextState, INITIAL_STATE.Sections);
diff --git a/browser/extensions/activity-stream/test/unit/lib/HighlightsFeed.test.js b/browser/extensions/activity-stream/test/unit/lib/HighlightsFeed.test.js
index 01fccfb7f636..61566df30a32 100644
--- a/browser/extensions/activity-stream/test/unit/lib/HighlightsFeed.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/HighlightsFeed.test.js
@@ -296,10 +296,10 @@ describe("Highlights Feed", () => {
assert.calledOnce(feed.fetchHighlights);
assert.calledWith(feed.fetchHighlights, true);
});
- it("should fetch highlights on PLACES_LINK_DELETED", async () => {
+ it("should fetch highlights on PLACES_LINKS_DELETED", async () => {
await feed.fetchHighlights();
feed.fetchHighlights = sinon.spy();
- feed.onAction({type: at.PLACES_LINK_DELETED});
+ feed.onAction({type: at.PLACES_LINKS_DELETED});
assert.calledOnce(feed.fetchHighlights);
assert.calledWith(feed.fetchHighlights, true);
});
diff --git a/browser/extensions/activity-stream/test/unit/lib/PlacesFeed.test.js b/browser/extensions/activity-stream/test/unit/lib/PlacesFeed.test.js
index ec469881bf7e..4f93c84b9183 100644
--- a/browser/extensions/activity-stream/test/unit/lib/PlacesFeed.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/PlacesFeed.test.js
@@ -179,9 +179,20 @@ describe("PlacesFeed", () => {
assert.property(observer, "QueryInterface");
});
describe("#onDeleteURI", () => {
- it("should dispatch a PLACES_LINK_DELETED action with the right url", () => {
+ it("should dispatch a PLACES_LINKS_DELETED action with the right url", async() => {
+ await observer.onDeleteURI({spec: "foo.com"});
+
+ assert.calledWith(dispatch, {type: at.PLACES_LINKS_DELETED, data: ["foo.com"]});
+ });
+ it("should dispatch a PLACES_LINKS_DELETED action with multiple urls", async() => {
+ const promise = observer.onDeleteURI({spec: "bar.com"});
observer.onDeleteURI({spec: "foo.com"});
- assert.calledWith(dispatch, {type: at.PLACES_LINK_DELETED, data: {url: "foo.com"}});
+ await promise;
+
+ const result = dispatch.firstCall.args[0].data;
+ assert.lengthOf(result, 2);
+ assert.equal(result[0], "bar.com");
+ assert.equal(result[1], "foo.com");
});
});
describe("#onClearHistory", () => {
diff --git a/browser/extensions/activity-stream/test/unit/lib/PrefsFeed.test.js b/browser/extensions/activity-stream/test/unit/lib/PrefsFeed.test.js
index cd2a1cbf657b..28d194c876bf 100644
--- a/browser/extensions/activity-stream/test/unit/lib/PrefsFeed.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/PrefsFeed.test.js
@@ -4,6 +4,7 @@ const {PrerenderData} = require("common/PrerenderData.jsm");
const {initialPrefs} = PrerenderData;
const PRERENDER_PREF_NAME = "prerender";
+const ONBOARDING_FINISHED_PREF = "browser.onboarding.notification.finished";
describe("PrefsFeed", () => {
let feed;
@@ -62,6 +63,44 @@ describe("PrefsFeed", () => {
assert.calledWith(feed._prefs.set, PRERENDER_PREF_NAME, false);
});
});
+ describe("Onboarding", () => {
+ let sandbox;
+ let defaultBranch;
+ beforeEach(() => {
+ sandbox = sinon.sandbox.create();
+ defaultBranch = {setBoolPref: sandbox.stub()};
+ sandbox.stub(global.Services.prefs, "getDefaultBranch").returns(defaultBranch);
+ });
+ afterEach(() => {
+ sandbox.restore();
+ });
+ it("should set ONBOARDING_FINISHED_PREF to true if prefs.feeds.snippets if false", () => {
+ FAKE_PREFS.set("feeds.snippets", false);
+ feed.onAction({type: at.INIT});
+ assert.calledWith(defaultBranch.setBoolPref, ONBOARDING_FINISHED_PREF, true);
+ });
+ it("should not set ONBOARDING_FINISHED_PREF if prefs.feeds.snippets is true", () => {
+ FAKE_PREFS.set("feeds.snippets", true);
+ feed.onAction({type: at.INIT});
+ assert.notCalled(defaultBranch.setBoolPref);
+ });
+ it("should set ONBOARDING_FINISHED_PREF to true if the feeds.snippets pref changes to false", () => {
+ feed.onPrefChanged("feeds.snippets", false);
+ assert.calledWith(defaultBranch.setBoolPref, ONBOARDING_FINISHED_PREF, true);
+ });
+ it("should set ONBOARDING_FINISHED_PREF to false if the feeds.snippets pref changes to true", () => {
+ feed.onPrefChanged("feeds.snippets", true);
+ assert.calledWith(defaultBranch.setBoolPref, ONBOARDING_FINISHED_PREF, false);
+ });
+ it("should not set ONBOARDING_FINISHED_PREF if an unrelated pref changes", () => {
+ feed.onPrefChanged("foo", true);
+ assert.notCalled(defaultBranch.setBoolPref);
+ });
+ it("should set ONBOARDING_FINISHED_PREF to true if a DISABLE_ONBOARDING action was received", () => {
+ feed.onAction({type: at.DISABLE_ONBOARDING});
+ assert.calledWith(defaultBranch.setBoolPref, ONBOARDING_FINISHED_PREF, true);
+ });
+ });
describe("onPrefChanged prerendering", () => {
it("should not change the prerender pref if the pref is not included in invalidatingPrefs", () => {
feed.onPrefChanged("foo123", true);
diff --git a/browser/extensions/activity-stream/test/unit/lib/TopSitesFeed.test.js b/browser/extensions/activity-stream/test/unit/lib/TopSitesFeed.test.js
index b13dbfc329d9..4cb86e6ca46c 100644
--- a/browser/extensions/activity-stream/test/unit/lib/TopSitesFeed.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/TopSitesFeed.test.js
@@ -507,9 +507,9 @@ describe("Top Sites Feed", () => {
assert.calledOnce(feed.refresh);
assert.equal(feed.refresh.firstCall.args[0], null);
});
- it("should call refresh without a target on PLACES_LINK_DELETED action", async () => {
+ it("should call refresh without a target on PLACES_LINKS_DELETED action", async () => {
sinon.stub(feed, "refresh");
- await feed.onAction({type: at.PLACES_LINK_DELETED});
+ await feed.onAction({type: at.PLACES_LINKS_DELETED});
assert.calledOnce(feed.refresh);
assert.equal(feed.refresh.firstCall.args[0], null);
});
diff --git a/browser/extensions/activity-stream/test/unit/unit-entry.js b/browser/extensions/activity-stream/test/unit/unit-entry.js
index e0cdd4fd0cd8..0538666a60f8 100644
--- a/browser/extensions/activity-stream/test/unit/unit-entry.js
+++ b/browser/extensions/activity-stream/test/unit/unit-entry.js
@@ -27,6 +27,8 @@ overrider.set({
ContentSearchUIController: function() {}, // NB: This is a function/constructor
dump() {},
fetch() {},
+ // eslint-disable-next-line object-shorthand
+ Image: function() {}, // NB: This is a function/constructor
Preferences: FakePrefs,
Services: {
locale: {
diff --git a/browser/modules/PingCentre.jsm b/browser/modules/PingCentre.jsm
index 8f3b79fa6245..dace651f99e2 100644
--- a/browser/modules/PingCentre.jsm
+++ b/browser/modules/PingCentre.jsm
@@ -38,7 +38,6 @@ class PingCentre {
}
this._topic = options.topic;
- this._filter = options.filter;
this._prefs = Services.prefs.getBranch("");
this._setPingEndpoint(options.topic, options.overrideEndpointPref);
@@ -92,12 +91,12 @@ class PingCentre {
this._fhrEnabled = this._prefs.getBoolPref(prefKey);
}
- _createExperimentsString(activeExperiments) {
+ _createExperimentsString(activeExperiments, filter) {
let experimentsString = "";
for (let experimentID in activeExperiments) {
if (!activeExperiments[experimentID] ||
!activeExperiments[experimentID].branch ||
- (this._filter && !experimentID.includes(this._filter))) {
+ (filter && !experimentID.includes(filter))) {
continue;
}
let expString = `${experimentID}:${activeExperiments[experimentID].branch}`;
@@ -106,9 +105,10 @@ class PingCentre {
return experimentsString;
}
- async sendPing(data) {
+ async sendPing(data, options) {
+ let filter = options && options.filter;
let experiments = TelemetryEnvironment.getActiveExperiments();
- let experimentsString = this._createExperimentsString(experiments);
+ let experimentsString = this._createExperimentsString(experiments, filter);
if (!this.enabled) {
return Promise.resolve();
}