--- a/browser/extensions/activity-stream/common/Actions.jsm
+++ b/browser/extensions/activity-stream/common/Actions.jsm
@@ -26,16 +26,17 @@ 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",
+ "DISABLE_ONBOARDING",
"INIT",
"LOCALE_UPDATED",
"MIGRATION_CANCEL",
"MIGRATION_COMPLETED",
"MIGRATION_START",
"NEW_TAB_INIT",
"NEW_TAB_INITIAL_STATE",
"NEW_TAB_LOAD",
@@ -44,18 +45,18 @@ for (const type of [
"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",
- "PLACES_LINK_DELETED",
"PREFS_INITIAL_VALUES",
"PREF_CHANGED",
"SAVE_SESSION_PERF_DATA",
"SAVE_TO_POCKET",
"SCREENSHOT_UPDATED",
"SECTION_DEREGISTER",
"SECTION_DISABLE",
"SECTION_ENABLE",
--- a/browser/extensions/activity-stream/common/PrerenderData.jsm
+++ b/browser/extensions/activity-stream/common/PrerenderData.jsm
@@ -69,18 +69,17 @@ this.PrerenderData = new _PrerenderData(
{oneOf: ["feeds.section.topstories", "feeds.section.highlights"]}
],
initialSections: [
{
enabled: true,
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",
icon: "highlights",
order: 2,
title: {id: "header_highlights"}
}
--- a/browser/extensions/activity-stream/common/Reducers.jsm
+++ b/browser/extensions/activity-stream/common/Reducers.jsm
@@ -283,17 +283,19 @@ function Sections(prevState = INITIAL_ST
if (!newSite.type || newSite.type === "bookmark") {
newSite.type = "history";
}
return newSite;
}
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:
return prevState;
}
}
--- a/browser/extensions/activity-stream/data/content/activity-stream-initial-state.js
+++ b/browser/extensions/activity-stream/data/content/activity-stream-initial-state.js
@@ -133,19 +133,16 @@
"provider": "Pocket"
}
},
"rows": [],
"order": 1,
"enabled": true,
"icon": "pocket",
"id": "topstories",
- "topics": [
- {}
- ],
"initialized": false
},
{
"title": {
"id": "header_highlights"
},
"rows": [],
"order": 2,
--- a/browser/extensions/activity-stream/data/content/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/data/content/activity-stream-prerendered.html
@@ -4,17 +4,17 @@
<meta charset="utf-8">
<meta http-equiv="Content-Security-Policy-Report-Only" content="script-src 'unsafe-inline'; img-src http: https: data: blob:; style-src 'unsafe-inline'; child-src 'none'; object-src 'none'; report-uri https://tiles.services.mozilla.com/v4/links/activity-stream/csp">
<title></title>
<link rel="icon" type="image/png" id="favicon" href="chrome://branding/content/icon32.png"/>
<link rel="stylesheet" href="chrome://browser/content/contentSearchUI.css" />
<link rel="stylesheet" href="resource://activity-stream/data/content/activity-stream.css" />
</head>
<body class="activity-stream">
- <div id="root"><div class="outer-wrapper fixed-to-top" data-reactroot="" data-reactid="1" data-react-checksum="-1229441695"><main data-reactid="2"><div class="search-wrapper" data-reactid="3"><label for="newtab-search-text" class="search-label" data-reactid="4"><span class="sr-only" data-reactid="5"><span data-reactid="6">Search the Web</span></span></label><input type="search" id="newtab-search-text" maxlength="256" placeholder="Search the Web" title="Search the Web" data-reactid="7"/><button id="searchSubmit" class="search-button" title=" " data-reactid="8"><span class="sr-only" data-reactid="9"><span data-reactid="10"> </span></span></button></div><div class="body-wrapper" data-reactid="11"><section class="top-sites" data-reactid="12"><div class="section-top-bar" data-reactid="13"><h3 class="section-title" data-reactid="14"><span class="icon icon-small-spacer icon-topsites" data-reactid="15"></span><span data-reactid="16"> </span></h3></div><ul class="top-sites-list" data-reactid="17"><li class="top-site-outer placeholder" data-reactid="18"><a data-reactid="19"><div class="tile" aria-hidden="true" data-reactid="20"><span class="letter-fallback" data-reactid="21"></span><div class="screenshot" style="background-image:none;" data-reactid="22"></div></div><div class="title " data-reactid="23"><span dir="auto" data-reactid="24"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="25"><a data-reactid="26"><div class="tile" aria-hidden="true" data-reactid="27"><span class="letter-fallback" data-reactid="28"></span><div class="screenshot" style="background-image:none;" data-reactid="29"></div></div><div class="title " data-reactid="30"><span dir="auto" data-reactid="31"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="32"><a data-reactid="33"><div class="tile" aria-hidden="true" data-reactid="34"><span class="letter-fallback" data-reactid="35"></span><div class="screenshot" style="background-image:none;" data-reactid="36"></div></div><div class="title " data-reactid="37"><span dir="auto" data-reactid="38"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="39"><a data-reactid="40"><div class="tile" aria-hidden="true" data-reactid="41"><span class="letter-fallback" data-reactid="42"></span><div class="screenshot" style="background-image:none;" data-reactid="43"></div></div><div class="title " data-reactid="44"><span dir="auto" data-reactid="45"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="46"><a data-reactid="47"><div class="tile" aria-hidden="true" data-reactid="48"><span class="letter-fallback" data-reactid="49"></span><div class="screenshot" style="background-image:none;" data-reactid="50"></div></div><div class="title " data-reactid="51"><span dir="auto" data-reactid="52"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="53"><a data-reactid="54"><div class="tile" aria-hidden="true" data-reactid="55"><span class="letter-fallback" data-reactid="56"></span><div class="screenshot" style="background-image:none;" data-reactid="57"></div></div><div class="title " data-reactid="58"><span dir="auto" data-reactid="59"></span></div></a></li></ul><div class="edit-topsites-wrapper" data-reactid="60"><div class="edit-topsites-button" data-reactid="61"><button class="edit" title=" " data-reactid="62"><span data-reactid="63"> </span></button></div></div></section><div class="sections-list" data-reactid="64"><section data-reactid="65"><div class="section-top-bar" data-reactid="66"><h3 class="section-title" data-reactid="67"><span class="icon icon-small-spacer icon-pocket" data-reactid="68"></span><span data-reactid="69"> </span></h3></div><ul class="section-list" style="padding:0;" data-reactid="70"><li class="card-outer placeholder" data-reactid="71"><a data-reactid="72"><div class="card" data-reactid="73"><div class="card-details no-image" data-reactid="74"><div class="card-text no-context no-description no-host-name no-image" data-reactid="75"><h4 class="card-title" dir="auto" data-reactid="76"></h4><p class="card-description" dir="auto" data-reactid="77"></p></div><div class="card-context" data-reactid="78"></div></div></div></a></li><li class="card-outer placeholder" data-reactid="79"><a data-reactid="80"><div class="card" data-reactid="81"><div class="card-details no-image" data-reactid="82"><div class="card-text no-context no-description no-host-name no-image" data-reactid="83"><h4 class="card-title" dir="auto" data-reactid="84"></h4><p class="card-description" dir="auto" data-reactid="85"></p></div><div class="card-context" data-reactid="86"></div></div></div></a></li><li class="card-outer placeholder" data-reactid="87"><a data-reactid="88"><div class="card" data-reactid="89"><div class="card-details no-image" data-reactid="90"><div class="card-text no-context no-description no-host-name no-image" data-reactid="91"><h4 class="card-title" dir="auto" data-reactid="92"></h4><p class="card-description" dir="auto" data-reactid="93"></p></div><div class="card-context" data-reactid="94"></div></div></div></a></li></ul><div class="topic" data-reactid="95"><span data-reactid="96"><span data-reactid="97"> </span></span><ul data-reactid="98"><li data-reactid="99"><a class="topic-link" data-reactid="100"></a></li></ul></div></section><section data-reactid="101"><div class="section-top-bar" data-reactid="102"><h3 class="section-title" data-reactid="103"><span class="icon icon-small-spacer icon-highlights" data-reactid="104"></span><span data-reactid="105"> </span></h3></div><ul class="section-list" style="padding:0;" data-reactid="106"><li class="card-outer placeholder" data-reactid="107"><a data-reactid="108"><div class="card" data-reactid="109"><div class="card-details no-image" data-reactid="110"><div class="card-text no-context no-description no-host-name no-image" data-reactid="111"><h4 class="card-title" dir="auto" data-reactid="112"></h4><p class="card-description" dir="auto" data-reactid="113"></p></div><div class="card-context" data-reactid="114"></div></div></div></a></li><li class="card-outer placeholder" data-reactid="115"><a data-reactid="116"><div class="card" data-reactid="117"><div class="card-details no-image" data-reactid="118"><div class="card-text no-context no-description no-host-name no-image" data-reactid="119"><h4 class="card-title" dir="auto" data-reactid="120"></h4><p class="card-description" dir="auto" data-reactid="121"></p></div><div class="card-context" data-reactid="122"></div></div></div></a></li><li class="card-outer placeholder" data-reactid="123"><a data-reactid="124"><div class="card" data-reactid="125"><div class="card-details no-image" data-reactid="126"><div class="card-text no-context no-description no-host-name no-image" data-reactid="127"><h4 class="card-title" dir="auto" data-reactid="128"></h4><p class="card-description" dir="auto" data-reactid="129"></p></div><div class="card-context" data-reactid="130"></div></div></div></a></li></ul></section></div></div><!-- react-empty: 131 --></main></div></div>
+ <div id="root"><div class="outer-wrapper fixed-to-top" data-reactroot="" data-reactid="1" data-react-checksum="-1946542515"><main data-reactid="2"><div class="search-wrapper" data-reactid="3"><label for="newtab-search-text" class="search-label" data-reactid="4"><span class="sr-only" data-reactid="5"><span data-reactid="6">Search the Web</span></span></label><input type="search" id="newtab-search-text" maxlength="256" placeholder="Search the Web" title="Search the Web" data-reactid="7"/><button id="searchSubmit" class="search-button" title=" " data-reactid="8"><span class="sr-only" data-reactid="9"><span data-reactid="10"> </span></span></button></div><div class="body-wrapper" data-reactid="11"><section class="top-sites" data-reactid="12"><div class="section-top-bar" data-reactid="13"><h3 class="section-title" data-reactid="14"><span class="icon icon-small-spacer icon-topsites" data-reactid="15"></span><span data-reactid="16"> </span></h3></div><ul class="top-sites-list" data-reactid="17"><li class="top-site-outer placeholder" data-reactid="18"><a data-reactid="19"><div class="tile" aria-hidden="true" data-reactid="20"><span class="letter-fallback" data-reactid="21"></span><div class="screenshot" style="background-image:none;" data-reactid="22"></div></div><div class="title " data-reactid="23"><span dir="auto" data-reactid="24"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="25"><a data-reactid="26"><div class="tile" aria-hidden="true" data-reactid="27"><span class="letter-fallback" data-reactid="28"></span><div class="screenshot" style="background-image:none;" data-reactid="29"></div></div><div class="title " data-reactid="30"><span dir="auto" data-reactid="31"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="32"><a data-reactid="33"><div class="tile" aria-hidden="true" data-reactid="34"><span class="letter-fallback" data-reactid="35"></span><div class="screenshot" style="background-image:none;" data-reactid="36"></div></div><div class="title " data-reactid="37"><span dir="auto" data-reactid="38"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="39"><a data-reactid="40"><div class="tile" aria-hidden="true" data-reactid="41"><span class="letter-fallback" data-reactid="42"></span><div class="screenshot" style="background-image:none;" data-reactid="43"></div></div><div class="title " data-reactid="44"><span dir="auto" data-reactid="45"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="46"><a data-reactid="47"><div class="tile" aria-hidden="true" data-reactid="48"><span class="letter-fallback" data-reactid="49"></span><div class="screenshot" style="background-image:none;" data-reactid="50"></div></div><div class="title " data-reactid="51"><span dir="auto" data-reactid="52"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="53"><a data-reactid="54"><div class="tile" aria-hidden="true" data-reactid="55"><span class="letter-fallback" data-reactid="56"></span><div class="screenshot" style="background-image:none;" data-reactid="57"></div></div><div class="title " data-reactid="58"><span dir="auto" data-reactid="59"></span></div></a></li></ul><div class="edit-topsites-wrapper" data-reactid="60"><div class="edit-topsites-button" data-reactid="61"><button class="edit" title=" " data-reactid="62"><span data-reactid="63"> </span></button></div></div></section><div class="sections-list" data-reactid="64"><section data-reactid="65"><div class="section-top-bar" data-reactid="66"><h3 class="section-title" data-reactid="67"><span class="icon icon-small-spacer icon-pocket" data-reactid="68"></span><span data-reactid="69"> </span></h3></div><ul class="section-list" style="padding:0;" data-reactid="70"><li class="card-outer placeholder" data-reactid="71"><a data-reactid="72"><div class="card" data-reactid="73"><div class="card-details no-image" data-reactid="74"><div class="card-text no-context no-description no-host-name no-image" data-reactid="75"><h4 class="card-title" dir="auto" data-reactid="76"></h4><p class="card-description" dir="auto" data-reactid="77"></p></div><div class="card-context" data-reactid="78"></div></div></div></a></li><li class="card-outer placeholder" data-reactid="79"><a data-reactid="80"><div class="card" data-reactid="81"><div class="card-details no-image" data-reactid="82"><div class="card-text no-context no-description no-host-name no-image" data-reactid="83"><h4 class="card-title" dir="auto" data-reactid="84"></h4><p class="card-description" dir="auto" data-reactid="85"></p></div><div class="card-context" data-reactid="86"></div></div></div></a></li><li class="card-outer placeholder" data-reactid="87"><a data-reactid="88"><div class="card" data-reactid="89"><div class="card-details no-image" data-reactid="90"><div class="card-text no-context no-description no-host-name no-image" data-reactid="91"><h4 class="card-title" dir="auto" data-reactid="92"></h4><p class="card-description" dir="auto" data-reactid="93"></p></div><div class="card-context" data-reactid="94"></div></div></div></a></li></ul><div class="topic" data-reactid="95"><span data-reactid="96"><span data-reactid="97"> </span></span><ul data-reactid="98"></ul></div></section><section data-reactid="99"><div class="section-top-bar" data-reactid="100"><h3 class="section-title" data-reactid="101"><span class="icon icon-small-spacer icon-highlights" data-reactid="102"></span><span data-reactid="103"> </span></h3></div><ul class="section-list" style="padding:0;" data-reactid="104"><li class="card-outer placeholder" data-reactid="105"><a data-reactid="106"><div class="card" data-reactid="107"><div class="card-details no-image" data-reactid="108"><div class="card-text no-context no-description no-host-name no-image" data-reactid="109"><h4 class="card-title" dir="auto" data-reactid="110"></h4><p class="card-description" dir="auto" data-reactid="111"></p></div><div class="card-context" data-reactid="112"></div></div></div></a></li><li class="card-outer placeholder" data-reactid="113"><a data-reactid="114"><div class="card" data-reactid="115"><div class="card-details no-image" data-reactid="116"><div class="card-text no-context no-description no-host-name no-image" data-reactid="117"><h4 class="card-title" dir="auto" data-reactid="118"></h4><p class="card-description" dir="auto" data-reactid="119"></p></div><div class="card-context" data-reactid="120"></div></div></div></a></li><li class="card-outer placeholder" data-reactid="121"><a data-reactid="122"><div class="card" data-reactid="123"><div class="card-details no-image" data-reactid="124"><div class="card-text no-context no-description no-host-name no-image" data-reactid="125"><h4 class="card-title" dir="auto" data-reactid="126"></h4><p class="card-description" dir="auto" data-reactid="127"></p></div><div class="card-context" data-reactid="128"></div></div></div></a></li></ul></section></div></div><!-- react-empty: 129 --></main></div></div>
<div id="snippets-container">
<div id="snippets"></div>
</div>
<script>
// Don't directly load the following scripts as part of html to let the page
// finish loading to render the content sooner.
for (const src of [
"resource://activity-stream/data/content/activity-stream-initial-state.js",
--- a/browser/extensions/activity-stream/data/content/activity-stream.bundle.js
+++ b/browser/extensions/activity-stream/data/content/activity-stream.bundle.js
@@ -89,17 +89,17 @@ const globalImportContext = typeof Windo
// Create an object that avoids accidental differing key/value pairs:
// {
// INIT: "INIT",
// 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;
}
// Helper function for creating routed actions between content and main
// Not intended to be used by consumers
function _RouteMessage(action, options) {
const meta = action.meta ? Object.assign({}, action.meta) : {};
if (!options || !options.from || !options.to) {
@@ -633,17 +633,18 @@ function Sections(prevState = INITIAL_ST
if (!newSite.type || newSite.type === "bookmark") {
newSite.type = "history";
}
return newSite;
}
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:
return prevState;
}
}
function Snippets(prevState = INITIAL_STATE.Snippets, action) {
@@ -1506,20 +1507,24 @@ class TopSitesEdit extends React.PureCom
React.createElement("div", { className: "modal-overlay", onClick: this.onModalOverlayClick }),
React.createElement(
"div",
{ className: "modal" },
React.createElement(
"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",
{ className: "top-sites-list" },
realTopSites.map((link, index) => link && React.createElement(TopSite, {
key: link.guid || link.url,
dispatch: this.props.dispatch,
link: link,
@@ -2564,17 +2569,20 @@ class Section extends React.PureComponen
render() {
const {
id, eventSource, title, icon, rows,
infoOption, emptyState, dispatch, maxRows,
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",
"aria-controls": "info-option",
"aria-expanded": this.state.infoActive ? "true" : "false",
"role": "note",
"tabIndex": 0
};
@@ -2689,33 +2697,72 @@ module.exports._unconnectedSection = Sec
/***/ (function(module, exports, __webpack_require__) {
const React = __webpack_require__(1);
const LinkMenu = __webpack_require__(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
* as preview image, page title, page description, and some context about if the page
* was visited, bookmarked, trending etc...
* Each Section can make an unordered list of Cards which will create one instane of
* this class. Each card will then get a context menu which reflects the actions that
* can be done on this Card.
*/
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({
activeCard: this.props.index,
showContextMenu: true
});
}
onLinkClick(event) {
@@ -2737,16 +2784,28 @@ class Card extends React.PureComponent {
incognito: true,
tiles: [{ id: this.props.link.guid, pos: this.props.index }]
}));
}
}
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;
const isContextMenuOpen = this.state.showContextMenu && this.state.activeCard === index;
// Display "now" as "trending" until we have new strings #3402
const { icon, intlID } = cardContextTypes[link.type === "now" ? "trending" : link.type] || {};
const hasImage = link.image || link.hasImage;
const imageStyle = { backgroundImage: link.image ? `url(${link.image})` : "none" };
@@ -2758,17 +2817,17 @@ class Card extends React.PureComponent {
"a",
{ href: link.url, onClick: !props.placeholder && this.onLinkClick },
React.createElement(
"div",
{ className: "card" },
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",
{ className: `card-details${hasImage ? "" : " no-image"}` },
link.hostname && React.createElement(
"div",
{ className: "card-host-name" },
link.hostname
@@ -2889,17 +2948,17 @@ class Topics extends React.PureComponent
React.createElement(
"span",
null,
React.createElement(FormattedMessage, { id: "pocket_read_more" })
),
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",
{ className: "topic-read-more", href: read_more_endpoint },
React.createElement(FormattedMessage, { id: "pocket_read_even_more" })
)
);
}
@@ -2980,18 +3039,17 @@ var PrerenderData = new _PrerenderData({
// This means if either of these are set to their default values,
// prerendering can be used.
{ oneOf: ["feeds.section.topstories", "feeds.section.highlights"] }],
initialSections: [{
enabled: true,
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",
icon: "highlights",
order: 2,
title: { id: "header_highlights" }
}]
});
@@ -3252,16 +3310,20 @@ class SnippetsMap extends Map {
}
let blockList = this.blockList;
if (!blockList.includes(id)) {
blockList.push(id);
}
await this.set("blockList", blockList);
}
+ disableOnboarding() {
+ this._dispatch(ac.SendToMain({ type: at.DISABLE_ONBOARDING }));
+ }
+
showFirefoxAccounts() {
this._dispatch(ac.SendToMain({ type: at.SHOW_FIREFOX_ACCOUNTS }));
}
/**
* connect - Attaches an indexedDB back-end to the Map so that any set values
* are also cached in a store. It also restores any existing values
* that are already stored in the indexedDB store.
@@ -3390,34 +3452,41 @@ class SnippetsProvider {
// Has enough time passed for us to require an update?
const lastUpdate = this.snippetsMap.get("snippets-last-update");
const needsUpdate = !(lastUpdate >= 0) || Date.now() - lastUpdate > SNIPPETS_UPDATE_INTERVAL_MS;
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();
this.snippetsMap.set("snippets", payload);
this.snippetsMap.set("snippets-cached-version", this.appData.version);
}
} catch (e) {
console.error(e); // eslint-disable-line no-console
}
}
}
- _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");
if (!snippetsEl) {
throw new Error(`No element was found with id '${this.elementId}'.`);
}
@@ -3475,25 +3544,28 @@ class SnippetsProvider {
// Refresh snippets, if enough time has passed.
await this._refreshSnippets();
// Try showing remote snippets, falling back to defaults if necessary.
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;
}
}
/**
* addSnippetsSubscriber - Creates a SnippetsProvider that Initializes
* when the store has received the appropriate
* Snippet data.
@@ -3503,26 +3575,26 @@ class SnippetsProvider {
*/
function addSnippetsSubscriber(store) {
const snippets = new SnippetsProvider(store.dispatch);
let initializing = false;
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();
}
});
// These values are returned for testing purposes
return snippets;
}
--- a/browser/extensions/activity-stream/data/content/activity-stream.css
+++ b/browser/extensions/activity-stream/data/content/activity-stream.css
@@ -452,36 +452,34 @@ main {
.edit-topsites-wrapper .show-more:dir(rtl),
.edit-topsites-wrapper .show-less:dir(rtl) {
background-position: right 10px center; }
.edit-topsites-wrapper .show-more span,
.edit-topsites-wrapper .show-less span {
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); }
.topsite-form .form-wrapper .error-tooltip {
animation: fade-up-tt 450ms;
background: #D70022;
border-radius: 2px;
color: #FFF;
@@ -671,63 +669,53 @@ main {
.search-wrapper {
cursor: default;
display: flex;
position: relative;
margin: 0 0 40px;
width: 100%;
height: 36px; }
.search-wrapper input {
- border: 0;
+ border: 1px solid rgba(0, 0, 0, 0.15);
+ border-radius: 3px;
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;
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);
-moz-context-properties: fill;
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;
border: 0;
width: 36px;
fill: rgba(12, 12, 13, 0.4);
-moz-context-properties: fill;
background-size: 16px 16px;
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 {
border: 0;
transform: translateY(2px); }
.context-menu {
display: block;
@@ -1004,37 +992,35 @@ main {
box-shadow: 0 0 0 5px #D7D7DB;
transition: box-shadow 150ms; }
.card-outer:-moz-any(:hover, :focus, .active):not(.placeholder) .context-menu-button {
transform: scale(1);
opacity: 1; }
.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;
content: " ";
position: absolute;
width: 100%; }
.card-outer .card-preview-image-outer .card-preview-image {
width: 100%;
height: 100%;
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 {
padding: 15px 16px 12px; }
.card-outer .card-details.no-image {
padding-top: 16px; }
.card-outer .card-text {
overflow: hidden;
--- a/browser/extensions/activity-stream/data/locales.json
+++ b/browser/extensions/activity-stream/data/locales.json
@@ -575,17 +575,61 @@
"welcome_title": "Donemat war un ivinell nevez",
"welcome_body": "Firefox a implijo al lec'h-mañ evit diskouez deoc'h sinedoù, pennadoù, videoioù ha pajennoù bet gweladennet ganeoc'h, evit adkavout anezho en un doare aes.",
"welcome_label": "Naoudiañ ho mareoù pouezus",
"time_label_less_than_minute": "< 1 m",
"time_label_minute": "{number}m",
"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",
"default_label_loading": "S'està carregant…",
"header_top_sites": "Llocs principals",
"header_stories": "Articles populars",
"header_highlights": "Destacats",
"header_visit_again": "Torneu a visitar",
@@ -950,16 +994,17 @@
"manual_migration_cancel_button": "Dim Diolch",
"manual_migration_import_button": "Mewnforio Nawr"
},
"da": {
"newtab_page_title": "Nyt faneblad",
"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}",
"header_bookmarks_placeholder": "Du har ingen bogmærker endnu.",
"header_stories_from": "fra",
"type_label_visited": "Besøgt",
"type_label_bookmarked": "Bogmærket",
"type_label_synced": "Synkroniseret fra en anden enhed",
@@ -981,37 +1026,35 @@
"confirm_history_delete_notice_p2": "Denne handling kan ikke fortrydes.",
"menu_action_save_to_pocket": "Gem til Pocket",
"search_for_something_with": "Søg efter {search_term} med:",
"search_button": "Søg",
"search_header": "{search_engine_name}-søgning",
"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",
"time_label_less_than_minute": "<1 m.",
"time_label_minute": "{number} m.",
"time_label_hour": "{number} t.",
"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",
"settings_pane_topsites_body": "Adgang til de websider, du besøger oftest.",
"settings_pane_topsites_options_showmore": "Vis to rækker",
"settings_pane_bookmarks_header": "Seneste bogmærker",
"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",
"edit_topsites_showmore_button": "Vis flere",
"edit_topsites_showless_button": "Vis færre",
"edit_topsites_done_button": "Færdig",
"edit_topsites_pin_button": "Fastgør denne webside",
"edit_topsites_unpin_button": "Frigør denne webside",
@@ -1024,20 +1067,17 @@
"topsites_form_url_placeholder": "Indtast eller indsæt en URL",
"topsites_form_add_button": "Tilføj",
"topsites_form_save_button": "Gem",
"topsites_form_cancel_button": "Annuller",
"topsites_form_url_validation": "Gyldig URL påkrævet",
"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"
},
"de": {
"newtab_page_title": "Neuer Tab",
"default_label_loading": "Wird geladen…",
"header_top_sites": "Meistbesuchte Seiten",
"header_stories": "Meistgelesene Meldungen",
@@ -3783,18 +3823,18 @@
"topstories_empty_state": "Ulac wiyaḍ. Uɣal-d ticki s wugar n imagraden seg {provider}. Ur tebɣiḍ ara ad terǧuḍ? Fren asentel seg wid yettwasnen akken ad twaliḍ imagraden yelhan di Web.",
"manual_migration_explanation2": "Σreḍ Firefox s ticṛaḍ n isebtar, amazray akked awalen uffiren sγur ilinigen nniḍen.",
"manual_migration_cancel_button": "Ala, tanemmirt",
"manual_migration_import_button": "Kter tura"
},
"kk": {
"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}",
"header_bookmarks_placeholder": "Сізде әлі бетбелгілер жоқ.",
"header_stories_from": "ұсынған",
"type_label_visited": "Қаралған",
"type_label_bookmarked": "Бетбелгілерде",
@@ -3810,17 +3850,17 @@
"menu_action_open_new_window": "Жаңа терезеде ашу",
"menu_action_open_private_window": "Жаңа жекелік терезесінде ашу",
"menu_action_dismiss": "Тайдыру",
"menu_action_delete": "Тарихтан өшіру",
"menu_action_pin": "Бекіту",
"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} іздеуі",
"search_web_placeholder": "Интернетте іздеу",
"search_settings": "Іздеу баптауларын өзгерту",
"section_info_option": "Ақпарат",
"section_info_send_feedback": "Кері байланыс хабарламасын жіберу",
"section_info_privacy_notice": "Жекелік ескертуі",
@@ -3828,32 +3868,32 @@
"welcome_body": "Firefox бұл орында ең маңызды бетбелгілер, мақалалар, видеолар және жуырда қаралған беттерді көрсетеді, оның көмегімен сіз оларға оңай түрде орала аласыз.",
"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_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": "Соңғы бетбелгілер",
"settings_pane_bookmarks_body": "Сіздің жаңадан жасалған бетбелгілер бір ыңғайлы жерде.",
"settings_pane_visit_again_header": "Қайтадан шолу",
"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_snippets_body": "Mozilla ұсынған Firefox және интернет мәдениеті туралы қысқа жаңалықтарды, және кездейсоқ мемдерді оқыңыз.",
"settings_pane_done_button": "Дайын",
"edit_topsites_button_text": "Түзету",
"edit_topsites_button_label": "Топ сайттар санатын баптау",
"edit_topsites_showmore_button": "Көбірек көрсету",
"edit_topsites_showless_button": "Азырақ көрсету",
"edit_topsites_done_button": "Дайын",
"edit_topsites_pin_button": "Бұл сайтты жапсыру",
"edit_topsites_unpin_button": "Бұл сайтты бекітуден алып тастау",
@@ -3869,17 +3909,17 @@
"topsites_form_cancel_button": "Бас тарту",
"topsites_form_url_validation": "Жарамды сілтеме керек",
"pocket_read_more": "Әйгілі тақырыптар:",
"pocket_read_even_more": "Көбірек хикаяларды қарау",
"pocket_feedback_header": "Интернеттің ең жақсысы, 25 миллион адаммен танылған.",
"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": "Қазір импорттау"
},
"km": {
"newtab_page_title": "ផ្ទាំងថ្មី",
"default_label_loading": "កំពុងផ្ទុក...",
"header_top_sites": "វិបសាយលើគេ",
"header_highlights": "ការរំលេច",
@@ -4198,23 +4238,25 @@
"pocket_description": "Atraskite kokybišką turinį, kurio kitaip galbūt nerastumėte, su „Pocket“, kuri yra tapusi „Mozillos“ dalimi, pagalba.",
"highlights_empty_state": "Pradėkite naršyti, o mes čia pateiksime puikių straipsnių, vaizdo įrašų bei kitų tinklalapių, kuriuose neseniai lankėtės ar įtraukėte į adresyną.",
"topstories_empty_state": "Viską perskaitėte. Užsukite vėliau, norėdami rasti daugiau gerų straipsnių iš „{provider}“. Nekantraujate? Pasirinkite populiarią temą, norėdami rasti daugiau puikių straipsnių saityne.",
"manual_migration_explanation2": "Išbandykite „Firefox“ su adresynu, žurnalu bei slaptažodžiais iš kitos naršyklės.",
"manual_migration_cancel_button": "Ačiū, ne",
"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}",
"header_bookmarks_placeholder": "Сѐ уште немате обележувачи.",
"header_stories_from": "од",
"type_label_visited": "Посетени",
"type_label_bookmarked": "Обележани",
"type_label_synced": "Синхронизирани од други уреди",
@@ -4227,49 +4269,55 @@
"menu_action_copy_address": "Копирај адреса",
"menu_action_email_link": "Испрати врска…",
"menu_action_open_new_window": "Отвори во нов прозорец",
"menu_action_open_private_window": "Отвори во нов приватен прозорец",
"menu_action_dismiss": "Откажи",
"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} со:",
"search_button": "Барај",
"search_header": "Пребарување со {search_engine_name}",
"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": "Готово",
"edit_topsites_pin_button": "Прикачи го ова мрежно место",
"edit_topsites_unpin_button": "Откачи го ова мрежно место",
"edit_topsites_edit_button": "Уреди го ова место",
"edit_topsites_dismiss_button": "Отфрли го ова место",
"edit_topsites_add_button": "Додај",
@@ -4279,20 +4327,20 @@
"topsites_form_url_placeholder": "Внесете или вметнете URL",
"topsites_form_add_button": "Додај",
"topsites_form_save_button": "Сними",
"topsites_form_cancel_button": "Откажи",
"topsites_form_url_validation": "Потребен е валиден URL",
"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": "Увези сега"
},
"ml": {
"newtab_page_title": "പുതിയ ടാബ്",
"default_label_loading": "ലോഡ്ചെയ്യുന്നു…",
"header_top_sites": "മികച്ച സൈറ്റുകൾ",
"header_highlights": "ഹൈലൈറ്റുകൾ",
@@ -6087,17 +6135,17 @@
},
"th": {
"newtab_page_title": "แท็บใหม่",
"default_label_loading": "กำลังโหลด…",
"header_top_sites": "ไซต์เด่น",
"header_stories": "เรื่องราวเด่น",
"header_highlights": "รายการเด่น",
"header_visit_again": "เยี่ยมชมอีกครั้ง",
- "header_bookmarks": "ที่คั่นหน้าเมื่อเร็ว ๆ นี้",
+ "header_bookmarks": "ที่คั่นหน้าล่าสุด",
"header_recommended_by": "แนะนำโดย {provider}",
"header_bookmarks_placeholder": "คุณยังไม่มีที่คั่นหน้าใด ๆ",
"header_stories_from": "จาก",
"type_label_visited": "เยี่ยมชมแล้ว",
"type_label_bookmarked": "มีที่คั่นหน้าแล้ว",
"type_label_synced": "ซิงค์จากอุปกรณ์อื่น",
"type_label_recommended": "กำลังนิยม",
"type_label_open": "เปิด",
@@ -6134,17 +6182,17 @@
"settings_pane_button_label": "ปรับแต่งหน้าแท็บใหม่ของคุณ",
"settings_pane_header": "ค่ากำหนดแท็บใหม่",
"settings_pane_body2": "เลือกสิ่งที่คุณเห็นในหน้านี้",
"settings_pane_search_header": "ค้นหา",
"settings_pane_search_body": "ค้นหาเว็บจากแท็บใหม่ของคุณ",
"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": "รายการเด่น",
"settings_pane_highlights_options_bookmarks": "ที่คั่นหน้า",
"settings_pane_highlights_options_visited": "ไซต์ที่เยี่ยมชมแล้ว",
"settings_pane_snippets_header": "ส่วนย่อย",
"settings_pane_done_button": "เสร็จสิ้น",
"edit_topsites_button_text": "แก้ไข",
@@ -6612,18 +6660,18 @@
"settings_pane_bookmarks_header": "最近的书签",
"settings_pane_bookmarks_body": "您最近创建的书签将在此显示。",
"settings_pane_visit_again_header": "再次造访",
"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_snippets_header": "只言片语",
+ "settings_pane_snippets_body": "阅读 Mozilla 就 Firefox、互联网文化、偶尔还有模因提供的一些简短而有趣的小文章。",
"settings_pane_done_button": "完成",
"edit_topsites_button_text": "编辑",
"edit_topsites_button_label": "定制您的“常用网站”区域",
"edit_topsites_showmore_button": "显示更多",
"edit_topsites_showless_button": "显示更少",
"edit_topsites_done_button": "完成",
"edit_topsites_pin_button": "固定此网站",
"edit_topsites_unpin_button": "取消固定此网站",
@@ -6636,17 +6684,17 @@
"topsites_form_url_placeholder": "输入或粘贴一个网址",
"topsites_form_add_button": "添加",
"topsites_form_save_button": "保存",
"topsites_form_cancel_button": "取消",
"topsites_form_url_validation": "需要有效的网址",
"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 吧。",
"manual_migration_cancel_button": "不用了",
"manual_migration_import_button": "立即导入"
},
"zh-TW": {
"newtab_page_title": "新分頁",
@@ -6728,17 +6776,17 @@
"topsites_form_title_placeholder": "輸入標題",
"topsites_form_url_placeholder": "輸入或貼上網址",
"topsites_form_add_button": "新增",
"topsites_form_save_button": "儲存",
"topsites_form_cancel_button": "取消",
"topsites_form_url_validation": "請輸入有效的網址",
"pocket_read_more": "熱門主題:",
"pocket_read_even_more": "檢視更多文章",
- "pocket_feedback_header": "由超過兩千五百萬人找出來的 Web 最佳內容。",
- "pocket_description": "透過現在也是 Mozilla 旗下一員的 Pocket 的幫助,發現您先前可能錯過的高品質內容。",
+ "pocket_feedback_header": "超過兩千五百萬人共同探索出的 Web 最佳內容。",
+ "pocket_description": "透過 Mozilla 旗下的 Pocket 服務,發現您可能錯過的優質內容。",
"highlights_empty_state": "開始上網,我們就會把您在網路上發現的好文章、影片、剛加入書籤的頁面顯示於此。",
"topstories_empty_state": "所有文章都讀完啦!晚點再來,{provider} 將提供更多推薦故事。等不及了?選擇熱門主題,看看 Web 上各式精采資訊。",
"manual_migration_explanation2": "試試將其他瀏覽器的書籤、瀏覽記錄與密碼匯入 Firefox。",
"manual_migration_cancel_button": "不必了",
"manual_migration_import_button": "立即匯入"
}
}
\ No newline at end of file
--- a/browser/extensions/activity-stream/install.rdf.in
+++ b/browser/extensions/activity-stream/install.rdf.in
@@ -3,17 +3,17 @@
#filter substitution
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest">
<em:id>activity-stream@mozilla.org</em:id>
<em:type>2</em:type>
<em:bootstrap>true</em:bootstrap>
<em:unpack>false</em:unpack>
- <em:version>2017.09.22.1389-2ee94db4</em:version>
+ <em:version>2017.09.28.0389-b24927a8</em:version>
<em:name>Activity Stream</em:name>
<em:description>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.</em:description>
<em:multiprocessCompatible>true</em:multiprocessCompatible>
<em:targetApplication>
<Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
<em:minVersion>@MOZ_APP_VERSION@</em:minVersion>
--- a/browser/extensions/activity-stream/lib/HighlightsFeed.jsm
+++ b/browser/extensions/activity-stream/lib/HighlightsFeed.jsm
@@ -155,17 +155,17 @@ this.HighlightsFeed = class HighlightsFe
this.fetchHighlights(true);
} else if (Date.now() - this.highlightsLastUpdated >= HIGHLIGHTS_UPDATE_TIME) {
// If the last time we refreshed the data is greater than 15 minutes, fetch again.
this.fetchHighlights(false);
}
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;
case at.PLACES_BOOKMARK_ADDED:
case at.PLACES_BOOKMARK_REMOVED:
case at.TOP_SITES_UPDATED:
this.fetchHighlights(false);
break;
--- a/browser/extensions/activity-stream/lib/PlacesFeed.jsm
+++ b/browser/extensions/activity-stream/lib/PlacesFeed.jsm
@@ -37,21 +37,34 @@ class HistoryObserver extends Observer {
}
/**
* onDeleteURI - Called when an link is deleted from history.
*
* @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;
+ });
+ }
}
/**
* onClearHistory - Called when the user clears their entire history.
*/
onClearHistory() {
this.dispatch({type: at.PLACES_HISTORY_CLEARED});
}
--- a/browser/extensions/activity-stream/lib/PrefsFeed.jsm
+++ b/browser/extensions/activity-stream/lib/PrefsFeed.jsm
@@ -3,16 +3,19 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
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) {
this._prefMap = prefMap;
this._prefs = new Prefs();
}
// If the any prefs are set to something other than what the prerendered version
@@ -22,48 +25,72 @@ this.PrefsFeed = class PrefsFeed {
}
_checkPrerender(name) {
if (PrerenderData.invalidatingPrefs.includes(name)) {
this._setPrerenderPref();
}
}
+ _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() {
this._prefs.observeBranch(this);
// Get the initial value of each activity stream pref
const values = {};
for (const name of this._prefMap.keys()) {
values[name] = this._prefs.get(name);
}
// Set the initial state of all prefs in redux
this.store.dispatch(ac.BroadcastToContent({type: at.PREFS_INITIAL_VALUES, data: values}));
this._setPrerenderPref();
+ this._initOnboardingPref();
}
removeListeners() {
this._prefs.ignoreBranch(this);
}
onAction(action) {
switch (action.type) {
case at.INIT:
this.init();
break;
case at.UNINIT:
this.removeListeners();
+ this.setOnboardingDisabledDefault(false);
break;
case at.SET_PREF:
this._prefs.set(action.data.name, action.data.value);
break;
+ case at.DISABLE_ONBOARDING:
+ this.setOnboardingDisabledDefault(true);
+ break;
}
}
};
this.EXPORTED_SYMBOLS = ["PrefsFeed"];
--- a/browser/extensions/activity-stream/lib/SnippetsFeed.jsm
+++ b/browser/extensions/activity-stream/lib/SnippetsFeed.jsm
@@ -12,18 +12,18 @@ const {actionTypes: at, actionCreators:
XPCOMUtils.defineLazyModuleGetter(this, "ShellService",
"resource:///modules/ShellService.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ProfileAge",
"resource://gre/modules/ProfileAge.jsm");
// Url to fetch snippets, in the urlFormatter service format.
const SNIPPETS_URL_PREF = "browser.aboutHomeSnippets.updateUrl";
const TELEMETRY_PREF = "datareporting.healthreport.uploadEnabled";
+const FXA_USERNAME_PREF = "services.sync.username";
const ONBOARDING_FINISHED_PREF = "browser.onboarding.notification.finished";
-const FXA_USERNAME_PREF = "services.sync.username";
// Prefix for any target matching a search engine.
const TARGET_SEARCHENGINE_PREFIX = "searchEngine-";
const SEARCH_ENGINE_OBSERVER_TOPIC = "browser-search-engine-modified";
// Should be bumped up if the snippets content format changes.
const STARTPAGE_VERSION = 5;
--- a/browser/extensions/activity-stream/lib/TelemetryFeed.jsm
+++ b/browser/extensions/activity-stream/lib/TelemetryFeed.jsm
@@ -16,16 +16,17 @@ XPCOMUtils.defineLazyModuleGetter(this,
"resource://activity-stream/common/PerfService.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PingCentre",
"resource:///modules/PingCentre.jsm");
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
const USER_PREFS_ENCODING = {
"showSearch": 1 << 0,
"showTopSites": 1 << 1,
"feeds.section.topstories": 1 << 2
};
@@ -155,22 +156,20 @@ this.TelemetryFeed = class TelemetryFeed
_onTelemetryPrefChange(prefVal) {
this.telemetryEnabled = prefVal;
}
/**
* 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
})
});
return this.pingCentre;
}
/**
* Get encoded user preferences, multiple prefs will be combined via bitwise OR operator
@@ -358,17 +357,18 @@ this.TelemetryFeed = class TelemetryFeed
action: "activity_stream_session",
perf: session.perf
}
);
}
async sendEvent(event_object) {
if (this.telemetryEnabled) {
- this.pingCentre.sendPing(event_object);
+ this.pingCentre.sendPing(event_object,
+ {filter: ACTIVITY_STREAM_ID});
}
}
handleImpressionStats(action) {
const payload = action.data;
let guidSet;
let index;
--- a/browser/extensions/activity-stream/lib/TopSitesFeed.jsm
+++ b/browser/extensions/activity-stream/lib/TopSitesFeed.jsm
@@ -259,18 +259,18 @@ this.TopSitesFeed = class TopSitesFeed {
(Date.now() - this.lastUpdated >= UPDATE_TIME)
) {
this.refresh(action.meta.fromTarget);
}
break;
// 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;
case at.PREF_CHANGED:
if (action.data.name === DEFAULT_SITES_PREF) {
this.refreshDefaults(action.data.value);
}
break;
--- a/browser/extensions/activity-stream/test/unit/common/Reducers.test.js
+++ b/browser/extensions/activity-stream/test/unit/common/Reducers.test.js
@@ -353,23 +353,32 @@ describe("Reducers", () => {
const data = {rows: [], initialized: false, id: "foo_bar_2"};
const action = {type: at.SECTION_UPDATE, data};
const newState = Sections(oldState, action);
const updatedSection = newState.find(section => section.id === "foo_bar_2");
assert.propertyVal(updatedSection, "initialized", false);
});
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);
});
it("should bookmark an item when PLACES_BOOKMARK_ADDED is received", () => {
const action = {
type: at.PLACES_BOOKMARK_ADDED,
data: {
--- a/browser/extensions/activity-stream/test/unit/lib/HighlightsFeed.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/HighlightsFeed.test.js
@@ -291,20 +291,20 @@ describe("Highlights Feed", () => {
});
it("should fetch highlights on PLACES_HISTORY_CLEARED", async () => {
await feed.fetchHighlights();
feed.fetchHighlights = sinon.spy();
feed.onAction({type: at.PLACES_HISTORY_CLEARED});
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);
});
it("should fetch highlights on PLACES_LINK_BLOCKED", async () => {
await feed.fetchHighlights();
feed.fetchHighlights = sinon.spy();
feed.onAction({type: at.PLACES_LINK_BLOCKED});
assert.calledOnce(feed.fetchHighlights);
--- a/browser/extensions/activity-stream/test/unit/lib/PlacesFeed.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/PlacesFeed.test.js
@@ -174,19 +174,30 @@ describe("PlacesFeed", () => {
beforeEach(() => {
dispatch = sandbox.spy();
observer = new HistoryObserver(dispatch);
});
it("should have a QueryInterface property", () => {
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", () => {
it("should dispatch a PLACES_HISTORY_CLEARED action", () => {
observer.onClearHistory();
assert.calledWith(dispatch, {type: at.PLACES_HISTORY_CLEARED});
});
});
--- a/browser/extensions/activity-stream/test/unit/lib/PrefsFeed.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/PrefsFeed.test.js
@@ -1,14 +1,15 @@
const {PrefsFeed} = require("lib/PrefsFeed.jsm");
const {actionTypes: at, actionCreators: ac} = require("common/Actions.jsm");
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;
let FAKE_PREFS;
beforeEach(() => {
FAKE_PREFS = new Map([["foo", 1], ["bar", 2]]);
feed = new PrefsFeed(FAKE_PREFS);
feed.store = {dispatch: sinon.spy()};
@@ -57,16 +58,54 @@ describe("PrefsFeed", () => {
});
it("should set prerender pref to false if a pref does not match its initial value", () => {
Object.keys(initialPrefs).forEach(name => FAKE_PREFS.set(name, initialPrefs[name]));
FAKE_PREFS.set("showSearch", false);
feed.onAction({type: at.INIT});
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);
assert.notCalled(feed._prefs.set);
});
it("should set the prerender pref to false if a pref in invalidatingPrefs is changed from its original value", () => {
Object.keys(initialPrefs).forEach(name => FAKE_PREFS.set(name, initialPrefs[name]));
--- a/browser/extensions/activity-stream/test/unit/lib/TopSitesFeed.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/TopSitesFeed.test.js
@@ -502,19 +502,19 @@ describe("Top Sites Feed", () => {
assert.equal(feed.refresh.firstCall.args[0], null);
});
it("should call refresh without a target on PLACES_LINK_BLOCKED action", async () => {
sinon.stub(feed, "refresh");
await feed.onAction({type: at.PLACES_LINK_BLOCKED});
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);
});
it("should call pin with correct args on TOP_SITES_ADD", () => {
const addAction = {
type: at.TOP_SITES_ADD,
data: {site: {url: "foo.bar", label: "foo"}}
};
--- a/browser/extensions/activity-stream/test/unit/unit-entry.js
+++ b/browser/extensions/activity-stream/test/unit/unit-entry.js
@@ -22,16 +22,18 @@ overrider.set({
now: () => window.performance.now()
},
isSuccessCode: () => true
},
// eslint-disable-next-line object-shorthand
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: {
getAppLocalesAsLangTags() {},
getRequestedLocale() {},
negotiateLanguages() {}
},
urlFormatter: {formatURL: str => str},
--- a/browser/modules/PingCentre.jsm
+++ b/browser/modules/PingCentre.jsm
@@ -33,17 +33,16 @@ const FHR_UPLOAD_ENABLED_PREF = "datarep
*/
class PingCentre {
constructor(options) {
if (!options.topic) {
throw new Error("Must specify topic.");
}
this._topic = options.topic;
- this._filter = options.filter;
this._prefs = Services.prefs.getBranch("");
this._setPingEndpoint(options.topic, options.overrideEndpointPref);
this._enabled = this._prefs.getBoolPref(TELEMETRY_PREF);
this._onTelemetryPrefChange = this._onTelemetryPrefChange.bind(this);
this._prefs.addObserver(TELEMETRY_PREF, this._onTelemetryPrefChange);
@@ -87,33 +86,34 @@ class PingCentre {
_onTelemetryPrefChange(aSubject, aTopic, prefKey) {
this._enabled = this._prefs.getBoolPref(prefKey);
}
_onFhrPrefChange(aSubject, aTopic, prefKey) {
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}`;
experimentsString = experimentsString.concat(`${expString};`);
}
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();
}
let clientID = data.client_id || await this.telemetryClientId;
let locale = data.locale || Services.locale.getAppLocalesAsLangTags().pop();
const payload = Object.assign({
locale,