зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1383917 - Add manual migration, bookmarking and bug fixes to Activity Stream. r=dmose
MozReview-Commit-ID: 3KfzoBGnv7P --HG-- extra : rebase_source : 16c49d4482649797d3767ad782da769189705adc
This commit is contained in:
Родитель
4619abb60c
Коммит
f17eecfc62
|
@ -34,6 +34,8 @@ for (const type of [
|
|||
"FEED_INIT",
|
||||
"INIT",
|
||||
"LOCALE_UPDATED",
|
||||
"MIGRATION_CANCEL",
|
||||
"MIGRATION_START",
|
||||
"NEW_TAB_INITIAL_STATE",
|
||||
"NEW_TAB_LOAD",
|
||||
"NEW_TAB_UNLOAD",
|
||||
|
|
|
@ -202,6 +202,37 @@ function Sections(prevState = INITIAL_STATE.Sections, action) {
|
|||
}
|
||||
return section;
|
||||
});
|
||||
case at.PLACES_BOOKMARK_ADDED:
|
||||
if (!action.data) {
|
||||
return prevState;
|
||||
}
|
||||
return prevState.map(section => Object.assign({}, section, {
|
||||
rows: section.rows.map(item => {
|
||||
// find the item within the rows that is attempted to be bookmarked
|
||||
if (item.url === action.data.url) {
|
||||
const {bookmarkGuid, bookmarkTitle, lastModified} = action.data;
|
||||
Object.assign(item, {bookmarkGuid, bookmarkTitle, bookmarkDateCreated: lastModified});
|
||||
}
|
||||
return item;
|
||||
})
|
||||
}));
|
||||
case at.PLACES_BOOKMARK_REMOVED:
|
||||
if (!action.data) {
|
||||
return prevState;
|
||||
}
|
||||
return prevState.map(section => Object.assign({}, section, {
|
||||
rows: section.rows.map(item => {
|
||||
// find the bookmark within the rows that is attempted to be removed
|
||||
if (item.url === action.data.url) {
|
||||
const newSite = Object.assign({}, item);
|
||||
delete newSite.bookmarkGuid;
|
||||
delete newSite.bookmarkTitle;
|
||||
delete newSite.bookmarkDateCreated;
|
||||
return newSite;
|
||||
}
|
||||
return item;
|
||||
})
|
||||
}));
|
||||
case at.PLACES_LINK_DELETED:
|
||||
case at.PLACES_LINK_BLOCKED:
|
||||
return prevState.map(section =>
|
||||
|
|
|
@ -1,41 +1,41 @@
|
|||
/******/ (function(modules) { // webpackBootstrap
|
||||
/******/ // The module cache
|
||||
/******/ var installedModules = {};
|
||||
|
||||
/******/
|
||||
/******/ // The require function
|
||||
/******/ function __webpack_require__(moduleId) {
|
||||
|
||||
/******/
|
||||
/******/ // Check if module is in cache
|
||||
/******/ if(installedModules[moduleId])
|
||||
/******/ return installedModules[moduleId].exports;
|
||||
|
||||
/******/
|
||||
/******/ // Create a new module (and put it into the cache)
|
||||
/******/ var module = installedModules[moduleId] = {
|
||||
/******/ i: moduleId,
|
||||
/******/ l: false,
|
||||
/******/ exports: {}
|
||||
/******/ };
|
||||
|
||||
/******/
|
||||
/******/ // Execute the module function
|
||||
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
|
||||
|
||||
/******/
|
||||
/******/ // Flag the module as loaded
|
||||
/******/ module.l = true;
|
||||
|
||||
/******/
|
||||
/******/ // Return the exports of the module
|
||||
/******/ return module.exports;
|
||||
/******/ }
|
||||
|
||||
|
||||
/******/
|
||||
/******/
|
||||
/******/ // expose the modules object (__webpack_modules__)
|
||||
/******/ __webpack_require__.m = modules;
|
||||
|
||||
/******/
|
||||
/******/ // expose the module cache
|
||||
/******/ __webpack_require__.c = installedModules;
|
||||
|
||||
/******/
|
||||
/******/ // identity function for calling harmony imports with the correct context
|
||||
/******/ __webpack_require__.i = function(value) { return value; };
|
||||
|
||||
/******/
|
||||
/******/ // define getter function for harmony exports
|
||||
/******/ __webpack_require__.d = function(exports, name, getter) {
|
||||
/******/ if(!__webpack_require__.o(exports, name)) {
|
||||
|
@ -46,7 +46,7 @@
|
|||
/******/ });
|
||||
/******/ }
|
||||
/******/ };
|
||||
|
||||
/******/
|
||||
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
||||
/******/ __webpack_require__.n = function(module) {
|
||||
/******/ var getter = module && module.__esModule ?
|
||||
|
@ -55,15 +55,15 @@
|
|||
/******/ __webpack_require__.d(getter, 'a', getter);
|
||||
/******/ return getter;
|
||||
/******/ };
|
||||
|
||||
/******/
|
||||
/******/ // Object.prototype.hasOwnProperty.call
|
||||
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
|
||||
|
||||
/******/
|
||||
/******/ // __webpack_public_path__
|
||||
/******/ __webpack_require__.p = "";
|
||||
|
||||
/******/
|
||||
/******/ // Load entry module and return exports
|
||||
/******/ return __webpack_require__(__webpack_require__.s = 25);
|
||||
/******/ return __webpack_require__(__webpack_require__.s = 26);
|
||||
/******/ })
|
||||
/************************************************************************/
|
||||
/******/ ([
|
||||
|
@ -103,7 +103,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", "FEED_INIT", "INIT", "LOCALE_UPDATED", "NEW_TAB_INITIAL_STATE", "NEW_TAB_LOAD", "NEW_TAB_UNLOAD", "NEW_TAB_VISIBLE", "OPEN_NEW_WINDOW", "OPEN_PRIVATE_WINDOW", "PINNED_SITES_UPDATED", "PLACES_BOOKMARK_ADDED", "PLACES_BOOKMARK_CHANGED", "PLACES_BOOKMARK_REMOVED", "PLACES_HISTORY_CLEARED", "PLACES_LINK_BLOCKED", "PLACES_LINK_DELETED", "PREFS_INITIAL_VALUES", "PREF_CHANGED", "SAVE_TO_POCKET", "SCREENSHOT_UPDATED", "SECTION_DEREGISTER", "SECTION_REGISTER", "SECTION_ROWS_UPDATE", "SET_PREF", "SNIPPETS_DATA", "SNIPPETS_RESET", "SYSTEM_TICK", "TELEMETRY_PERFORMANCE_EVENT", "TELEMETRY_UNDESIRED_EVENT", "TELEMETRY_USER_EVENT", "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", "FEED_INIT", "INIT", "LOCALE_UPDATED", "MIGRATION_CANCEL", "MIGRATION_START", "NEW_TAB_INITIAL_STATE", "NEW_TAB_LOAD", "NEW_TAB_UNLOAD", "NEW_TAB_VISIBLE", "OPEN_NEW_WINDOW", "OPEN_PRIVATE_WINDOW", "PINNED_SITES_UPDATED", "PLACES_BOOKMARK_ADDED", "PLACES_BOOKMARK_CHANGED", "PLACES_BOOKMARK_REMOVED", "PLACES_HISTORY_CLEARED", "PLACES_LINK_BLOCKED", "PLACES_LINK_DELETED", "PREFS_INITIAL_VALUES", "PREF_CHANGED", "SAVE_TO_POCKET", "SCREENSHOT_UPDATED", "SECTION_DEREGISTER", "SECTION_REGISTER", "SECTION_ROWS_UPDATE", "SET_PREF", "SNIPPETS_DATA", "SNIPPETS_RESET", "SYSTEM_TICK", "TELEMETRY_PERFORMANCE_EVENT", "TELEMETRY_UNDESIRED_EVENT", "TELEMETRY_USER_EVENT", "TOP_SITES_PIN", "TOP_SITES_UNPIN", "TOP_SITES_UPDATED", "UNINIT"]) {
|
||||
actionTypes[type] = type;
|
||||
}
|
||||
|
||||
|
@ -312,6 +312,7 @@ module.exports = ReactRedux;
|
|||
* via Services.eTLD.getPublicSuffix
|
||||
* {str} link.hostname (optional) - The hostname of the url
|
||||
* e.g. for http://www.hello.com/foo/bar, the hostname would be "www.hello.com"
|
||||
* {str} link.title (optional) - The title of the link
|
||||
* @return {str} A short url
|
||||
*/
|
||||
module.exports = function shortURL(link) {
|
||||
|
@ -326,7 +327,7 @@ module.exports = function shortURL(link) {
|
|||
const eTLDLength = (eTLD || "").length || hostname.match(/\.com$/) && 3;
|
||||
const eTLDExtra = eTLDLength > 0 ? -(eTLDLength + 1) : Infinity;
|
||||
// If URL and hostname are not present fallback to page title.
|
||||
return hostname.slice(0, eTLDExtra).toLowerCase() || hostname || link.title;
|
||||
return hostname.slice(0, eTLDExtra).toLowerCase() || hostname || link.title || link.url;
|
||||
};
|
||||
|
||||
/***/ }),
|
||||
|
@ -348,7 +349,7 @@ var _require2 = __webpack_require__(1);
|
|||
|
||||
const ac = _require2.actionCreators;
|
||||
|
||||
const linkMenuOptions = __webpack_require__(21);
|
||||
const linkMenuOptions = __webpack_require__(22);
|
||||
const DEFAULT_SITE_MENU_OPTIONS = ["CheckPinTopSite", "Separator", "OpenInNewWindow", "OpenInPrivateWindow"];
|
||||
|
||||
class LinkMenu extends React.Component {
|
||||
|
@ -420,11 +421,12 @@ var _require2 = __webpack_require__(2);
|
|||
const addLocaleData = _require2.addLocaleData,
|
||||
IntlProvider = _require2.IntlProvider;
|
||||
|
||||
const TopSites = __webpack_require__(19);
|
||||
const Search = __webpack_require__(17);
|
||||
const TopSites = __webpack_require__(20);
|
||||
const Search = __webpack_require__(18);
|
||||
const ConfirmDialog = __webpack_require__(14);
|
||||
const PreferencesPane = __webpack_require__(16);
|
||||
const Sections = __webpack_require__(18);
|
||||
const ManualMigration = __webpack_require__(16);
|
||||
const PreferencesPane = __webpack_require__(17);
|
||||
const Sections = __webpack_require__(19);
|
||||
|
||||
// Locales that should be displayed RTL
|
||||
const RTL_LIST = ["ar", "he", "fa", "ur"];
|
||||
|
@ -468,7 +470,6 @@ class Base extends React.Component {
|
|||
initialized = _props$App.initialized;
|
||||
|
||||
const prefs = props.Prefs.values;
|
||||
|
||||
if (!initialized) {
|
||||
return null;
|
||||
}
|
||||
|
@ -483,6 +484,7 @@ class Base extends React.Component {
|
|||
"main",
|
||||
null,
|
||||
prefs.showSearch && React.createElement(Search, null),
|
||||
!prefs.migrationExpired && React.createElement(ManualMigration, null),
|
||||
prefs.showTopSites && React.createElement(TopSites, null),
|
||||
React.createElement(Sections, null),
|
||||
React.createElement(ConfirmDialog, null)
|
||||
|
@ -506,7 +508,7 @@ var _require = __webpack_require__(1);
|
|||
|
||||
const at = _require.actionTypes;
|
||||
|
||||
var _require2 = __webpack_require__(22);
|
||||
var _require2 = __webpack_require__(23);
|
||||
|
||||
const perfSvc = _require2.perfService;
|
||||
|
||||
|
@ -579,7 +581,7 @@ module.exports = class DetectUserSessionStart {
|
|||
|
||||
/* eslint-env mozilla/frame-script */
|
||||
|
||||
var _require = __webpack_require__(24);
|
||||
var _require = __webpack_require__(25);
|
||||
|
||||
const createStore = _require.createStore,
|
||||
combineReducers = _require.combineReducers,
|
||||
|
@ -920,7 +922,7 @@ class SnippetsProvider {
|
|||
module.exports.SnippetsMap = SnippetsMap;
|
||||
module.exports.SnippetsProvider = SnippetsProvider;
|
||||
module.exports.SNIPPETS_UPDATE_INTERVAL_MS = SNIPPETS_UPDATE_INTERVAL_MS;
|
||||
/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(23)))
|
||||
/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(24)))
|
||||
|
||||
/***/ }),
|
||||
/* 10 */
|
||||
|
@ -1159,6 +1161,41 @@ function Sections() {
|
|||
}
|
||||
return section;
|
||||
});
|
||||
case at.PLACES_BOOKMARK_ADDED:
|
||||
if (!action.data) {
|
||||
return prevState;
|
||||
}
|
||||
return prevState.map(section => Object.assign({}, section, {
|
||||
rows: section.rows.map(item => {
|
||||
// find the item within the rows that is attempted to be bookmarked
|
||||
if (item.url === action.data.url) {
|
||||
var _action$data3 = action.data;
|
||||
const bookmarkGuid = _action$data3.bookmarkGuid,
|
||||
bookmarkTitle = _action$data3.bookmarkTitle,
|
||||
lastModified = _action$data3.lastModified;
|
||||
|
||||
Object.assign(item, { bookmarkGuid, bookmarkTitle, bookmarkDateCreated: lastModified });
|
||||
}
|
||||
return item;
|
||||
})
|
||||
}));
|
||||
case at.PLACES_BOOKMARK_REMOVED:
|
||||
if (!action.data) {
|
||||
return prevState;
|
||||
}
|
||||
return prevState.map(section => Object.assign({}, section, {
|
||||
rows: section.rows.map(item => {
|
||||
// find the bookmark within the rows that is attempted to be removed
|
||||
if (item.url === action.data.url) {
|
||||
const newSite = Object.assign({}, item);
|
||||
delete newSite.bookmarkGuid;
|
||||
delete newSite.bookmarkTitle;
|
||||
delete newSite.bookmarkDateCreated;
|
||||
return newSite;
|
||||
}
|
||||
return item;
|
||||
})
|
||||
}));
|
||||
case at.PLACES_LINK_DELETED:
|
||||
case at.PLACES_LINK_BLOCKED:
|
||||
return prevState.map(section => Object.assign({}, section, { rows: section.rows.filter(site => site.url !== action.data.url) }));
|
||||
|
@ -1224,9 +1261,18 @@ class Card extends React.Component {
|
|||
constructor(props) {
|
||||
super(props);
|
||||
this.state = { showContextMenu: false, activeCard: null };
|
||||
this.onMenuButtonClick = this.onMenuButtonClick.bind(this);
|
||||
this.onMenuUpdate = this.onMenuUpdate.bind(this);
|
||||
}
|
||||
toggleContextMenu(event, index) {
|
||||
this.setState({ showContextMenu: true, activeCard: index });
|
||||
onMenuButtonClick(event) {
|
||||
event.preventDefault();
|
||||
this.setState({
|
||||
activeCard: this.props.index,
|
||||
showContextMenu: true
|
||||
});
|
||||
}
|
||||
onMenuUpdate(showContextMenu) {
|
||||
this.setState({ showContextMenu });
|
||||
}
|
||||
render() {
|
||||
var _props = this.props;
|
||||
|
@ -1267,14 +1313,14 @@ class Card extends React.Component {
|
|||
{ className: `card-text${link.image ? "" : " full-height"}` },
|
||||
React.createElement(
|
||||
"h4",
|
||||
{ className: "card-title" },
|
||||
{ className: "card-title", dir: "auto" },
|
||||
" ",
|
||||
link.title,
|
||||
" "
|
||||
),
|
||||
React.createElement(
|
||||
"p",
|
||||
{ className: "card-description" },
|
||||
{ className: "card-description", dir: "auto" },
|
||||
" ",
|
||||
link.description,
|
||||
" "
|
||||
|
@ -1296,10 +1342,7 @@ class Card extends React.Component {
|
|||
React.createElement(
|
||||
"button",
|
||||
{ className: "context-menu-button",
|
||||
onClick: e => {
|
||||
e.preventDefault();
|
||||
this.toggleContextMenu(e, index);
|
||||
} },
|
||||
onClick: this.onMenuButtonClick },
|
||||
React.createElement(
|
||||
"span",
|
||||
{ className: "sr-only" },
|
||||
|
@ -1308,11 +1351,11 @@ class Card extends React.Component {
|
|||
),
|
||||
React.createElement(LinkMenu, {
|
||||
dispatch: dispatch,
|
||||
visible: isContextMenuOpen,
|
||||
onUpdate: val => this.setState({ showContextMenu: val }),
|
||||
index: index,
|
||||
onUpdate: this.onMenuUpdate,
|
||||
options: link.context_menu_options || contextMenuOptions,
|
||||
site: link,
|
||||
options: link.context_menu_options || contextMenuOptions })
|
||||
visible: isContextMenuOpen })
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1337,6 +1380,10 @@ module.exports = {
|
|||
trending: {
|
||||
intlID: "type_label_recommended",
|
||||
icon: "trending"
|
||||
},
|
||||
now: {
|
||||
intlID: "type_label_now",
|
||||
icon: "now"
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1489,25 +1536,9 @@ class ContextMenu extends React.Component {
|
|||
window.removeEventListener("click", this.hideContext);
|
||||
}
|
||||
}
|
||||
componentDidUnmount() {
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener("click", this.hideContext);
|
||||
}
|
||||
onKeyDown(event, option) {
|
||||
switch (event.key) {
|
||||
case "Tab":
|
||||
// tab goes down in context menu, shift + tab goes up in context menu
|
||||
// if we're on the last item, one more tab will close the context menu
|
||||
// similarly, if we're on the first item, one more shift + tab will close it
|
||||
if (event.shiftKey && option.first || !event.shiftKey && option.last) {
|
||||
this.hideContext();
|
||||
}
|
||||
break;
|
||||
case "Enter":
|
||||
this.hideContext();
|
||||
option.onClick();
|
||||
break;
|
||||
}
|
||||
}
|
||||
render() {
|
||||
return React.createElement(
|
||||
"span",
|
||||
|
@ -1515,32 +1546,59 @@ class ContextMenu extends React.Component {
|
|||
React.createElement(
|
||||
"ul",
|
||||
{ role: "menu", className: "context-menu-list" },
|
||||
this.props.options.map((option, i) => {
|
||||
if (option.type === "separator") {
|
||||
return React.createElement("li", { key: i, className: "separator" });
|
||||
}
|
||||
return React.createElement(
|
||||
"li",
|
||||
{ role: "menuitem", className: "context-menu-item", key: i },
|
||||
React.createElement(
|
||||
"a",
|
||||
{ tabIndex: "0",
|
||||
onKeyDown: e => this.onKeyDown(e, option),
|
||||
onClick: () => {
|
||||
this.hideContext();
|
||||
option.onClick();
|
||||
} },
|
||||
option.icon && React.createElement("span", { className: `icon icon-spacer icon-${ option.icon }` }),
|
||||
option.label
|
||||
this.props.options.map((option, i) => option.type === "separator" ? React.createElement("li", { key: i, className: "separator" }) : React.createElement(ContextMenuItem, { key: i, option: option, hideContext: this.hideContext }))
|
||||
)
|
||||
);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
class ContextMenuItem extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.onClick = this.onClick.bind(this);
|
||||
this.onKeyDown = this.onKeyDown.bind(this);
|
||||
}
|
||||
onClick() {
|
||||
this.props.hideContext();
|
||||
this.props.option.onClick();
|
||||
}
|
||||
onKeyDown(event) {
|
||||
const option = this.props.option;
|
||||
|
||||
switch (event.key) {
|
||||
case "Tab":
|
||||
// tab goes down in context menu, shift + tab goes up in context menu
|
||||
// if we're on the last item, one more tab will close the context menu
|
||||
// similarly, if we're on the first item, one more shift + tab will close it
|
||||
if (event.shiftKey && option.first || !event.shiftKey && option.last) {
|
||||
this.props.hideContext();
|
||||
}
|
||||
break;
|
||||
case "Enter":
|
||||
this.props.hideContext();
|
||||
option.onClick();
|
||||
break;
|
||||
}
|
||||
}
|
||||
render() {
|
||||
const option = this.props.option;
|
||||
|
||||
return React.createElement(
|
||||
"li",
|
||||
{ role: "menuitem", className: "context-menu-item" },
|
||||
React.createElement(
|
||||
"a",
|
||||
{ onClick: this.onClick, onKeyDown: this.onKeyDown, tabIndex: "0" },
|
||||
option.icon && React.createElement("span", { className: `icon icon-spacer icon-${option.icon}` }),
|
||||
option.label
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ContextMenu;
|
||||
module.exports.ContextMenu = ContextMenu;
|
||||
module.exports.ContextMenuItem = ContextMenuItem;
|
||||
|
||||
/***/ }),
|
||||
/* 16 */
|
||||
|
@ -1549,6 +1607,84 @@ module.exports = ContextMenu;
|
|||
"use strict";
|
||||
|
||||
|
||||
const React = __webpack_require__(0);
|
||||
|
||||
var _require = __webpack_require__(3);
|
||||
|
||||
const connect = _require.connect;
|
||||
|
||||
var _require2 = __webpack_require__(2);
|
||||
|
||||
const FormattedMessage = _require2.FormattedMessage;
|
||||
|
||||
var _require3 = __webpack_require__(1);
|
||||
|
||||
const at = _require3.actionTypes,
|
||||
ac = _require3.actionCreators;
|
||||
|
||||
/**
|
||||
* Manual migration component used to start the profile import wizard.
|
||||
* Message is presented temporarily and will go away if:
|
||||
* 1. User clicks "No Thanks"
|
||||
* 2. User completed the data import
|
||||
* 3. After 3 active days
|
||||
* 4. User clicks "Cancel" on the import wizard (currently not implemented).
|
||||
*/
|
||||
|
||||
class ManualMigration extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.onLaunchTour = this.onLaunchTour.bind(this);
|
||||
this.onCancelTour = this.onCancelTour.bind(this);
|
||||
}
|
||||
onLaunchTour() {
|
||||
this.props.dispatch(ac.SendToMain({ type: at.MIGRATION_START }));
|
||||
this.props.dispatch(ac.UserEvent({ event: at.MIGRATION_START }));
|
||||
}
|
||||
|
||||
onCancelTour() {
|
||||
this.props.dispatch(ac.SendToMain({ type: at.MIGRATION_CANCEL }));
|
||||
this.props.dispatch(ac.UserEvent({ event: at.MIGRATION_CANCEL }));
|
||||
}
|
||||
|
||||
render() {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ className: "manual-migration-container" },
|
||||
React.createElement(
|
||||
"p",
|
||||
null,
|
||||
React.createElement("span", { className: "icon icon-info" }),
|
||||
React.createElement(FormattedMessage, { id: "manual_migration_explanation" })
|
||||
),
|
||||
React.createElement(
|
||||
"div",
|
||||
{ className: "manual-migration-actions actions" },
|
||||
React.createElement(
|
||||
"button",
|
||||
{ onClick: this.onCancelTour },
|
||||
React.createElement(FormattedMessage, { id: "manual_migration_cancel_button" })
|
||||
),
|
||||
React.createElement(
|
||||
"button",
|
||||
{ className: "done", onClick: this.onLaunchTour },
|
||||
React.createElement(FormattedMessage, { id: "manual_migration_import_button" })
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = connect()(ManualMigration);
|
||||
module.exports._unconnected = ManualMigration;
|
||||
|
||||
/***/ }),
|
||||
/* 17 */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
|
||||
|
||||
const React = __webpack_require__(0);
|
||||
|
||||
var _require = __webpack_require__(3);
|
||||
|
@ -1671,7 +1807,7 @@ module.exports.PreferencesPane = PreferencesPane;
|
|||
module.exports.PreferencesInput = PreferencesInput;
|
||||
|
||||
/***/ }),
|
||||
/* 17 */
|
||||
/* 18 */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
|
@ -1770,7 +1906,7 @@ module.exports = connect()(injectIntl(Search));
|
|||
module.exports._unconnected = Search;
|
||||
|
||||
/***/ }),
|
||||
/* 18 */
|
||||
/* 19 */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
|
@ -1789,7 +1925,7 @@ var _require2 = __webpack_require__(2);
|
|||
const FormattedMessage = _require2.FormattedMessage;
|
||||
|
||||
const Card = __webpack_require__(12);
|
||||
const Topics = __webpack_require__(20);
|
||||
const Topics = __webpack_require__(21);
|
||||
|
||||
class Section extends React.Component {
|
||||
render() {
|
||||
|
@ -1890,7 +2026,7 @@ module.exports._unconnected = Sections;
|
|||
module.exports.Section = Section;
|
||||
|
||||
/***/ }),
|
||||
/* 19 */
|
||||
/* 20 */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
|
@ -1920,17 +2056,30 @@ class TopSite extends React.Component {
|
|||
constructor(props) {
|
||||
super(props);
|
||||
this.state = { showContextMenu: false, activeTile: null };
|
||||
this.onLinkClick = this.onLinkClick.bind(this);
|
||||
this.onMenuButtonClick = this.onMenuButtonClick.bind(this);
|
||||
this.onMenuUpdate = this.onMenuUpdate.bind(this);
|
||||
}
|
||||
toggleContextMenu(event, index) {
|
||||
this.setState({ showContextMenu: true, activeTile: index });
|
||||
this.setState({
|
||||
activeTile: index,
|
||||
showContextMenu: true
|
||||
});
|
||||
}
|
||||
trackClick() {
|
||||
onLinkClick() {
|
||||
this.props.dispatch(ac.UserEvent({
|
||||
event: "CLICK",
|
||||
source: TOP_SITES_SOURCE,
|
||||
action_position: this.props.index
|
||||
}));
|
||||
}
|
||||
onMenuButtonClick(event) {
|
||||
event.preventDefault();
|
||||
this.toggleContextMenu(event, this.props.index);
|
||||
}
|
||||
onMenuUpdate(showContextMenu) {
|
||||
this.setState({ showContextMenu });
|
||||
}
|
||||
render() {
|
||||
var _props = this.props;
|
||||
const link = _props.link,
|
||||
|
@ -1947,7 +2096,7 @@ class TopSite extends React.Component {
|
|||
{ className: topSiteOuterClassName, key: link.guid || link.url },
|
||||
React.createElement(
|
||||
"a",
|
||||
{ onClick: () => this.trackClick(), href: link.url },
|
||||
{ href: link.url, onClick: this.onLinkClick },
|
||||
React.createElement(
|
||||
"div",
|
||||
{ className: "tile", "aria-hidden": true },
|
||||
|
@ -1964,18 +2113,14 @@ class TopSite extends React.Component {
|
|||
link.isPinned && React.createElement("div", { className: "icon icon-pin-small" }),
|
||||
React.createElement(
|
||||
"span",
|
||||
null,
|
||||
{ dir: "auto" },
|
||||
title
|
||||
)
|
||||
)
|
||||
),
|
||||
React.createElement(
|
||||
"button",
|
||||
{ className: "context-menu-button",
|
||||
onClick: e => {
|
||||
e.preventDefault();
|
||||
this.toggleContextMenu(e, index);
|
||||
} },
|
||||
{ className: "context-menu-button", onClick: this.onMenuButtonClick },
|
||||
React.createElement(
|
||||
"span",
|
||||
{ className: "sr-only" },
|
||||
|
@ -1984,12 +2129,12 @@ class TopSite extends React.Component {
|
|||
),
|
||||
React.createElement(LinkMenu, {
|
||||
dispatch: dispatch,
|
||||
visible: isContextMenuOpen,
|
||||
onUpdate: val => this.setState({ showContextMenu: val }),
|
||||
site: link,
|
||||
index: index,
|
||||
onUpdate: this.onMenuUpdate,
|
||||
options: TOP_SITES_CONTEXT_MENU_OPTIONS,
|
||||
site: link,
|
||||
source: TOP_SITES_SOURCE,
|
||||
options: TOP_SITES_CONTEXT_MENU_OPTIONS })
|
||||
visible: isContextMenuOpen })
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -2000,6 +2145,7 @@ const TopSites = props => React.createElement(
|
|||
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(
|
||||
|
@ -2018,7 +2164,7 @@ module.exports._unconnected = TopSites;
|
|||
module.exports.TopSite = TopSite;
|
||||
|
||||
/***/ }),
|
||||
/* 20 */
|
||||
/* 21 */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
|
@ -2083,7 +2229,7 @@ module.exports._unconnected = Topics;
|
|||
module.exports.Topic = Topic;
|
||||
|
||||
/***/ }),
|
||||
/* 21 */
|
||||
/* 22 */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
|
@ -2194,7 +2340,7 @@ module.exports.CheckBookmark = site => site.bookmarkGuid ? module.exports.Remove
|
|||
module.exports.CheckPinTopSite = (site, index) => site.isPinned ? module.exports.UnpinTopSite(site) : module.exports.PinTopSite(site, index);
|
||||
|
||||
/***/ }),
|
||||
/* 22 */
|
||||
/* 23 */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
|
@ -2299,7 +2445,7 @@ module.exports = {
|
|||
};
|
||||
|
||||
/***/ }),
|
||||
/* 23 */
|
||||
/* 24 */
|
||||
/***/ (function(module, exports) {
|
||||
|
||||
var g;
|
||||
|
@ -2326,13 +2472,13 @@ module.exports = g;
|
|||
|
||||
|
||||
/***/ }),
|
||||
/* 24 */
|
||||
/* 25 */
|
||||
/***/ (function(module, exports) {
|
||||
|
||||
module.exports = Redux;
|
||||
|
||||
/***/ }),
|
||||
/* 25 */
|
||||
/* 26 */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
|
|
|
@ -41,6 +41,8 @@ input {
|
|||
background-image: url("assets/glyph-delete-16.svg"); }
|
||||
.icon.icon-dismiss {
|
||||
background-image: url("assets/glyph-dismiss-16.svg"); }
|
||||
.icon.icon-info {
|
||||
background-image: url("assets/glyph-info-16.svg"); }
|
||||
.icon.icon-new-window {
|
||||
background-image: url("assets/glyph-newWindow-16.svg"); }
|
||||
.icon.icon-new-window-private {
|
||||
|
@ -59,6 +61,8 @@ input {
|
|||
background-image: url("assets/glyph-trending-16.svg"); }
|
||||
.icon.icon-now {
|
||||
background-image: url("assets/glyph-now-16.svg"); }
|
||||
.icon.icon-topsites {
|
||||
background-image: url("assets/glyph-topsites-16.svg"); }
|
||||
.icon.icon-pin-small {
|
||||
background-image: url("assets/glyph-pin-12.svg");
|
||||
background-size: 12px;
|
||||
|
@ -344,7 +348,8 @@ main {
|
|||
height: 266px;
|
||||
display: flex;
|
||||
border: solid 1px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 3px; }
|
||||
border-radius: 3px;
|
||||
margin-bottom: 16px; }
|
||||
.sections-list .section-empty-state .empty-state {
|
||||
margin: auto;
|
||||
max-width: 350px; }
|
||||
|
@ -397,7 +402,7 @@ main {
|
|||
cursor: default;
|
||||
display: flex;
|
||||
position: relative;
|
||||
margin: 0 0 48px;
|
||||
margin: 0 0 40px;
|
||||
width: 100%;
|
||||
height: 36px; }
|
||||
.search-wrapper input {
|
||||
|
@ -757,3 +762,34 @@ main {
|
|||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap; }
|
||||
|
||||
.manual-migration-container {
|
||||
background: rgba(215, 215, 219, 0.5);
|
||||
font-size: 13px;
|
||||
height: 50px;
|
||||
border-radius: 2px;
|
||||
margin-bottom: 40px;
|
||||
display: flex;
|
||||
justify-content: space-between; }
|
||||
.manual-migration-container p {
|
||||
margin: 0 4px 0 12px;
|
||||
align-self: center;
|
||||
display: flex;
|
||||
justify-content: space-between; }
|
||||
.manual-migration-container .icon {
|
||||
margin: 0 12px 0 0;
|
||||
align-self: center; }
|
||||
|
||||
.manual-migration-actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
border: none;
|
||||
padding: 0; }
|
||||
.manual-migration-actions button {
|
||||
align-self: center;
|
||||
padding: 0 12px;
|
||||
height: 24px;
|
||||
margin-inline-end: 0;
|
||||
margin-right: 12px;
|
||||
font-size: 13px;
|
||||
width: 106px; }
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<path fill="context-fill" d="M8 1a7 7 0 1 0 7 7 7.008 7.008 0 0 0-7-7zm0 13a6 6 0 1 1 6-6 6.007 6.007 0 0 1-6 6zm0-7a1 1 0 0 0-1 1v3a1 1 0 1 0 2 0V8a1 1 0 0 0-1-1zm0-3.188A1.188 1.188 0 1 0 9.188 5 1.188 1.188 0 0 0 8 3.812z"/>
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 533 B |
|
@ -0,0 +1,11 @@
|
|||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<g fill="#4d4d4d">
|
||||
<rect x="1" y="1" width="6" height="6" rx="1" ry="1"/>
|
||||
<rect x="9" y="1" width="6" height="6" rx="1" ry="1"/>
|
||||
<rect x="1" y="9" width="6" height="6" rx="1" ry="1"/>
|
||||
<rect x="9" y="9" width="6" height="6" rx="1" ry="1"/>
|
||||
</g>
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 567 B |
|
@ -3,7 +3,7 @@
|
|||
"newtab_page_title": "Dirica matidi manyen",
|
||||
"default_label_loading": "Tye ka cano…",
|
||||
"header_top_sites": "Kakube maloyo",
|
||||
"header_highlights": "Wiye madito",
|
||||
"header_bookmarks_placeholder": "Pud i pee ki alamabuk.",
|
||||
"type_label_visited": "Kilimo",
|
||||
"type_label_bookmarked": "Kiketo alamabuk",
|
||||
"type_label_synced": "Kiribo ki i nyonyo mukene",
|
||||
|
@ -17,6 +17,8 @@
|
|||
"menu_action_open_private_window": "Yab i dirica manyen me mung",
|
||||
"menu_action_dismiss": "Kwer",
|
||||
"menu_action_delete": "Kwany ki ii gin mukato",
|
||||
"menu_action_pin": "Mwon",
|
||||
"menu_action_save_to_pocket": "Gwoki i jaba",
|
||||
"search_for_something_with": "Yeny pi {search_term} ki:",
|
||||
"search_button": "Yeny",
|
||||
"search_header": "Yeny me {search_engine_name}",
|
||||
|
@ -37,8 +39,8 @@
|
|||
"settings_pane_topsites_header": "Kakube ma gi loyo",
|
||||
"settings_pane_topsites_body": "Nong kakube ma ilimo loyo.",
|
||||
"settings_pane_topsites_options_showmore": "Nyut rek ariyo",
|
||||
"settings_pane_highlights_header": "Wiye madito",
|
||||
"settings_pane_highlights_body": "Nen angec i yeny mamegi mukato ki alamabukke ni ma i cweyo manyen.",
|
||||
"settings_pane_bookmarks_header": "Alamabuk ma cocoki",
|
||||
"settings_pane_visit_again_header": "Lim Kidoco",
|
||||
"settings_pane_done_button": "Otum",
|
||||
"edit_topsites_button_text": "Yubi",
|
||||
"edit_topsites_button_label": "Yub bute pi kakubi ni ma giloyo",
|
||||
|
@ -47,7 +49,12 @@
|
|||
"edit_topsites_done_button": "Otum",
|
||||
"edit_topsites_pin_button": "Mwon kakube man",
|
||||
"edit_topsites_edit_button": "Yub kakube man",
|
||||
"edit_topsites_dismiss_button": "Kwer kakube man"
|
||||
"edit_topsites_dismiss_button": "Kwer kakube man",
|
||||
"edit_topsites_add_button": "Medi",
|
||||
"topsites_form_edit_header": "Yub Kakube maloyo",
|
||||
"topsites_form_add_button": "Medi",
|
||||
"topsites_form_save_button": "Gwoki",
|
||||
"topsites_form_cancel_button": "Kwer"
|
||||
},
|
||||
"af": {},
|
||||
"an": {},
|
||||
|
@ -55,8 +62,10 @@
|
|||
"newtab_page_title": "لسان جديد",
|
||||
"default_label_loading": "يُحمّل…",
|
||||
"header_top_sites": "المواقع الأكثر زيارة",
|
||||
"header_highlights": "أهم الأحداث",
|
||||
"header_stories": "أهم الأخبار",
|
||||
"header_visit_again": "زرها مجددا",
|
||||
"header_bookmarks": "أحدث العلامات",
|
||||
"header_bookmarks_placeholder": "لا علامات لديك بعد.",
|
||||
"header_stories_from": "من",
|
||||
"type_label_visited": "مُزارة",
|
||||
"type_label_bookmarked": "معلّمة",
|
||||
|
@ -72,6 +81,10 @@
|
|||
"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",
|
||||
"search_for_something_with": "ابحث عن {search_term} مستخدما:",
|
||||
"search_button": "ابحث",
|
||||
|
@ -93,8 +106,10 @@
|
|||
"settings_pane_topsites_header": "المواقع الأكثر زيارة",
|
||||
"settings_pane_topsites_body": "وصول للمواقع التي تزورها أكثر.",
|
||||
"settings_pane_topsites_options_showmore": "اعرض صفّين",
|
||||
"settings_pane_highlights_header": "أهم الأحداث",
|
||||
"settings_pane_highlights_body": "اطّلع على تأريخ التصفح الأحدث، و العلامات المنشأة حديثًا.",
|
||||
"settings_pane_bookmarks_header": "أحدث العلامات",
|
||||
"settings_pane_bookmarks_body": "علاماتك المعلّمة حديثًا في مكان واحد.",
|
||||
"settings_pane_visit_again_header": "زرها مجددا",
|
||||
"settings_pane_visit_again_body": "سيعرض لك فَيَرفُكس بعضًا من تأريخ تصفحك الذي قد تود تذكّره لاحقًا.",
|
||||
"settings_pane_pocketstories_header": "أهم المواضيع",
|
||||
"settings_pane_pocketstories_body": "يساعدك Pocket –عضو في أسرة موزيلا– على الوصول إلى محتوى عالِ الجودة ربما لم يُكن ليتاح لك بدونه.",
|
||||
"settings_pane_done_button": "تمّ",
|
||||
|
@ -496,6 +511,7 @@
|
|||
"settings_pane_topsites_options_showmore": "Mostra dues files",
|
||||
"settings_pane_bookmarks_header": "Adreces d'interès recents",
|
||||
"settings_pane_bookmarks_body": "Les adreces d'interès que aneu creant, en un lloc còmode.",
|
||||
"settings_pane_visit_again_header": "Torneu a visitar",
|
||||
"settings_pane_visit_again_body": "El Firefox us mostrarà parts del vostre historial de navegació que potser us agradaria recordar o tornar a visitar.",
|
||||
"settings_pane_pocketstories_header": "Articles populars",
|
||||
"settings_pane_pocketstories_body": "El Pocket, membre de la família Mozilla, us permet accedir a contingut d'alta qualitat que d'altra manera potser no trobaríeu.",
|
||||
|
@ -600,7 +616,7 @@
|
|||
"topsites_form_url_validation": "Je vyžadována platná URL",
|
||||
"pocket_read_more": "Populární témata:",
|
||||
"pocket_read_even_more": "Zobrazit více příběhů",
|
||||
"pocket_feedback_header": "To nejlepší na webu podle hodnocení více než 25 miliony lidí.",
|
||||
"pocket_feedback_header": "To nejlepší na webu podle hodnocení více než 25 milionů lidí.",
|
||||
"pocket_feedback_body": "Pocket, služba od Mozilly, vám pomůže najít vysoce kvalitní obsah, který byste jinak neobjevili.",
|
||||
"pocket_send_feedback": "Odeslat zpětnou vazbu"
|
||||
},
|
||||
|
@ -608,10 +624,15 @@
|
|||
"newtab_page_title": "Tab Newydd",
|
||||
"default_label_loading": "Llwytho…",
|
||||
"header_top_sites": "Hoff Wefannau",
|
||||
"header_highlights": "Goreuon",
|
||||
"header_stories": "Hoff Straeon",
|
||||
"header_visit_again": "Ymweld Eto",
|
||||
"header_bookmarks": "Nodau Tudalen Diweddar",
|
||||
"header_bookmarks_placeholder": "Nid oes gennych unrhyw nodau tudalen eto.",
|
||||
"header_stories_from": "oddi wrth",
|
||||
"type_label_visited": "Ymwelwyd",
|
||||
"type_label_bookmarked": "Nod Tudalen",
|
||||
"type_label_synced": "Cydweddwyd o ddyfais arall",
|
||||
"type_label_recommended": "Trendio",
|
||||
"type_label_open": "Ar Agor",
|
||||
"type_label_topic": "Pwnc",
|
||||
"menu_action_bookmark": "Nod Tudalen",
|
||||
|
@ -622,6 +643,11 @@
|
|||
"menu_action_open_private_window": "Agor mewn Ffenestr Preifat Newydd",
|
||||
"menu_action_dismiss": "Cau",
|
||||
"menu_action_delete": "Dileu o'r Hanes",
|
||||
"menu_action_pin": "Pinio",
|
||||
"menu_action_unpin": "Dad-binio",
|
||||
"confirm_history_delete_p1": "Ydych chi'n siŵr eich bod chi am ddileu pob enghraifft o'r dudalen hon o'ch hanes?",
|
||||
"confirm_history_delete_notice_p2": "Nid oes modd dadwneud hyn.",
|
||||
"menu_action_save_to_pocket": "Cadw i Pocket",
|
||||
"search_for_something_with": "Chwilio am {search_term} gyda:",
|
||||
"search_button": "Chwilio",
|
||||
"search_header": "{search_engine_name} Chwilio",
|
||||
|
@ -642,8 +668,12 @@
|
|||
"settings_pane_topsites_header": "Hoff Wefannau",
|
||||
"settings_pane_topsites_body": "Cael mynediad at y gwefannau rydych yn ymweld â nhw amlaf.",
|
||||
"settings_pane_topsites_options_showmore": "Dangos dwy res",
|
||||
"settings_pane_highlights_header": "Goreuon",
|
||||
"settings_pane_highlights_body": "Edrych nôl ar eich hanes pori a nodau tudalen diweddar.",
|
||||
"settings_pane_bookmarks_header": "Nodau Tudalen Diweddar",
|
||||
"settings_pane_bookmarks_body": "Eich nodau tudalen diweddaraf mewn un lleoliad hwylus.",
|
||||
"settings_pane_visit_again_header": "Ymweld Eto",
|
||||
"settings_pane_visit_again_body": "Gall Firefox ddangos i chi rannau o'ch hanes pori yr hoffech eu cofio neu fynd nôl atyn nhw.",
|
||||
"settings_pane_pocketstories_header": "Hoff Straeon",
|
||||
"settings_pane_pocketstories_body": "Gall Pocket, sy'n rhan o deulu Mozilla, eich helpu i ganfod cynnwys o ansawdd uchel na fyddech wedi eu canfod fel arall.",
|
||||
"settings_pane_done_button": "Gorffen",
|
||||
"edit_topsites_button_text": "Golygu",
|
||||
"edit_topsites_button_label": "Cyfaddasu eich adran Hoff Wefannau",
|
||||
|
@ -651,8 +681,23 @@
|
|||
"edit_topsites_showless_button": "Dangos llai",
|
||||
"edit_topsites_done_button": "Gorffen",
|
||||
"edit_topsites_pin_button": "Pinio'r wefan",
|
||||
"edit_topsites_unpin_button": "Dad-binio'r wefan",
|
||||
"edit_topsites_edit_button": "Golygu'r wefan",
|
||||
"edit_topsites_dismiss_button": "Dileu'r wefan"
|
||||
"edit_topsites_dismiss_button": "Dileu'r wefan",
|
||||
"edit_topsites_add_button": "Ychwanegu",
|
||||
"topsites_form_add_header": "Hoff Wefan Newydd",
|
||||
"topsites_form_edit_header": "Golygu'r Hoff Wefan",
|
||||
"topsites_form_title_placeholder": "Rhoi teitl",
|
||||
"topsites_form_url_placeholder": "Teipio neu ludo URL",
|
||||
"topsites_form_add_button": "Ychwanegu",
|
||||
"topsites_form_save_button": "Cadw",
|
||||
"topsites_form_cancel_button": "Diddymu",
|
||||
"topsites_form_url_validation": "Mae angen URL Ddilys",
|
||||
"pocket_read_more": "Pynciau Poblogaidd:",
|
||||
"pocket_read_even_more": "Gweld Rhagor o Straeon",
|
||||
"pocket_feedback_header": "Y gorau o'r we, wedi ei gasglu gan dros 25 miliwn o bobl.",
|
||||
"pocket_feedback_body": "Gall Pocket, sy'n rhan o deulu Mozilla, eich helpu i ganfod cynnwys o ansawdd uchel na fyddech wedi eu canfod fel arall.",
|
||||
"pocket_send_feedback": "Anfon Adborth"
|
||||
},
|
||||
"da": {
|
||||
"newtab_page_title": "Nyt faneblad",
|
||||
|
@ -703,7 +748,9 @@
|
|||
"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",
|
||||
|
@ -972,10 +1019,15 @@
|
|||
"newtab_page_title": "New Tab",
|
||||
"default_label_loading": "Loading…",
|
||||
"header_top_sites": "Top Sites",
|
||||
"header_highlights": "Highlights",
|
||||
"header_stories": "Top Stories",
|
||||
"header_visit_again": "Visit Again",
|
||||
"header_bookmarks": "Recent Bookmarks",
|
||||
"header_bookmarks_placeholder": "You don’t have any bookmarks yet.",
|
||||
"header_stories_from": "from",
|
||||
"type_label_visited": "Visited",
|
||||
"type_label_bookmarked": "Bookmarked",
|
||||
"type_label_synced": "Synchronised from another device",
|
||||
"type_label_recommended": "Trending",
|
||||
"type_label_open": "Open",
|
||||
"type_label_topic": "Topic",
|
||||
"menu_action_bookmark": "Bookmark",
|
||||
|
@ -986,6 +1038,11 @@
|
|||
"menu_action_open_private_window": "Open in a New Private Window",
|
||||
"menu_action_dismiss": "Dismiss",
|
||||
"menu_action_delete": "Delete from History",
|
||||
"menu_action_pin": "Pin",
|
||||
"menu_action_unpin": "Unpin",
|
||||
"confirm_history_delete_p1": "Are you sure you want to delete every instance of this page from your history?",
|
||||
"confirm_history_delete_notice_p2": "This action cannot be undone.",
|
||||
"menu_action_save_to_pocket": "Save to Pocket",
|
||||
"search_for_something_with": "Search for {search_term} with:",
|
||||
"search_button": "Search",
|
||||
"search_header": "{search_engine_name} Search",
|
||||
|
@ -1006,8 +1063,12 @@
|
|||
"settings_pane_topsites_header": "Top Sites",
|
||||
"settings_pane_topsites_body": "Access the web sites you visit most.",
|
||||
"settings_pane_topsites_options_showmore": "Show two rows",
|
||||
"settings_pane_highlights_header": "Highlights",
|
||||
"settings_pane_highlights_body": "Look back at your recent browsing history and newly created bookmarks.",
|
||||
"settings_pane_bookmarks_header": "Recent Bookmarks",
|
||||
"settings_pane_bookmarks_body": "Your newly created bookmarks in one handy location.",
|
||||
"settings_pane_visit_again_header": "Visit Again",
|
||||
"settings_pane_visit_again_body": "Firefox will show you parts of your browsing history that you might want to remember or get back to.",
|
||||
"settings_pane_pocketstories_header": "Top Stories",
|
||||
"settings_pane_pocketstories_body": "Pocket, a part of the Mozilla family, will help connect you to high-quality content that you may not have found otherwise.",
|
||||
"settings_pane_done_button": "Done",
|
||||
"edit_topsites_button_text": "Edit",
|
||||
"edit_topsites_button_label": "Customise your Top Sites section",
|
||||
|
@ -1015,8 +1076,23 @@
|
|||
"edit_topsites_showless_button": "Show less",
|
||||
"edit_topsites_done_button": "Done",
|
||||
"edit_topsites_pin_button": "Pin this site",
|
||||
"edit_topsites_unpin_button": "Unpin this site",
|
||||
"edit_topsites_edit_button": "Edit this site",
|
||||
"edit_topsites_dismiss_button": "Dismiss this site"
|
||||
"edit_topsites_dismiss_button": "Dismiss this site",
|
||||
"edit_topsites_add_button": "Add",
|
||||
"topsites_form_add_header": "Top Sites",
|
||||
"topsites_form_edit_header": "Edit Top Site",
|
||||
"topsites_form_title_placeholder": "Enter a title",
|
||||
"topsites_form_url_placeholder": "Type or paste a URL",
|
||||
"topsites_form_add_button": "Add",
|
||||
"topsites_form_save_button": "Save",
|
||||
"topsites_form_cancel_button": "Cancel",
|
||||
"topsites_form_url_validation": "Valid URL required",
|
||||
"pocket_read_more": "Popular Topics:",
|
||||
"pocket_read_even_more": "View More Stories",
|
||||
"pocket_feedback_header": "The best of the web, curated by over 25 million people.",
|
||||
"pocket_feedback_body": "Pocket, a part of the Mozilla family, will help connect you to high-quality content that you may not have found otherwise.",
|
||||
"pocket_send_feedback": "Send Feedback"
|
||||
},
|
||||
"en-US": {
|
||||
"newtab_page_title": "New Tab",
|
||||
|
@ -1034,6 +1110,7 @@
|
|||
"type_label_recommended": "Trending",
|
||||
"type_label_open": "Open",
|
||||
"type_label_topic": "Topic",
|
||||
"type_label_now": "Now",
|
||||
"menu_action_bookmark": "Bookmark",
|
||||
"menu_action_remove_bookmark": "Remove Bookmark",
|
||||
"menu_action_copy_address": "Copy Address",
|
||||
|
@ -1098,17 +1175,25 @@
|
|||
"pocket_feedback_header": "The best of the web, curated by over 25 million people.",
|
||||
"pocket_feedback_body": "Pocket, a part of the Mozilla family, will help connect you to high-quality content that you may not have found otherwise.",
|
||||
"pocket_send_feedback": "Send Feedback",
|
||||
"empty_state_topstories": "You’ve caught up. Check back later for more top stories from Pocket. Can’t wait? Select a popular topic to find more great stories from around the web."
|
||||
"topstories_empty_state": "You’ve caught up. Check back later for more top stories from {provider}. Can’t wait? Select a popular topic to find more great stories from around the web.",
|
||||
"manual_migration_explanation": "Try Firefox with your favorite sites and bookmarks from another browser.",
|
||||
"manual_migration_cancel_button": "No Thanks",
|
||||
"manual_migration_import_button": "Import Now"
|
||||
},
|
||||
"en-ZA": {},
|
||||
"eo": {
|
||||
"newtab_page_title": "Nova legosigno",
|
||||
"default_label_loading": "Ŝargado…",
|
||||
"header_top_sites": "Plej vizititaj",
|
||||
"header_highlights": "Elstaraĵoj",
|
||||
"header_stories": "Ĉefaj artikoloj",
|
||||
"header_visit_again": "Viziti denove",
|
||||
"header_bookmarks": "Ĵusaj legosignoj",
|
||||
"header_bookmarks_placeholder": "Vi ankoraŭ ne havas legosignojn.",
|
||||
"header_stories_from": "el",
|
||||
"type_label_visited": "Vizititaj",
|
||||
"type_label_bookmarked": "Kun legosigno",
|
||||
"type_label_synced": "Spegulitaj el alia aparato",
|
||||
"type_label_recommended": "Tendencoj",
|
||||
"type_label_open": "Malfermita",
|
||||
"type_label_topic": "Temo",
|
||||
"menu_action_bookmark": "Aldoni legosignon",
|
||||
|
@ -1119,6 +1204,11 @@
|
|||
"menu_action_open_private_window": "Malfermi en nova privata fenestro",
|
||||
"menu_action_dismiss": "Ignori",
|
||||
"menu_action_delete": "Forigi el historio",
|
||||
"menu_action_pin": "Alpingli",
|
||||
"menu_action_unpin": "Depingli",
|
||||
"confirm_history_delete_p1": "Ĉu vi certe volas forigi ĉiun aperon de tiu ĉi paĝo el via historio?",
|
||||
"confirm_history_delete_notice_p2": "Tiu ĉi ago ne estas malfarebla.",
|
||||
"menu_action_save_to_pocket": "Konservi en Pocket",
|
||||
"search_for_something_with": "Serĉi {search_term} per:",
|
||||
"search_button": "Serĉi",
|
||||
"search_header": "Serĉo de {search_engine_name}",
|
||||
|
@ -1131,7 +1221,44 @@
|
|||
"time_label_minute": "{number}m",
|
||||
"time_label_hour": "{number}h",
|
||||
"time_label_day": "{number}t",
|
||||
"settings_pane_button_label": "Personecigi la paĝon por novaj langetoj"
|
||||
"settings_pane_button_label": "Personecigi la paĝon por novaj langetoj",
|
||||
"settings_pane_header": "Preferoj pri nova langeto",
|
||||
"settings_pane_body": "Elekti tion, kio estos videbla je malfermo de nova langeto.",
|
||||
"settings_pane_search_header": "Serĉi",
|
||||
"settings_pane_search_body": "Serĉi la Teksaĵon el via nova langeto.",
|
||||
"settings_pane_topsites_header": "Plej vizitaj",
|
||||
"settings_pane_topsites_body": "Aliri la plej ofte vizitajn retejojn.",
|
||||
"settings_pane_topsites_options_showmore": "Montri en du vicoj",
|
||||
"settings_pane_bookmarks_header": "Ĵusaj legosignoj",
|
||||
"settings_pane_bookmarks_body": "Viaj ĵus kreitaj legosignoj, ĉemane.",
|
||||
"settings_pane_visit_again_header": "Viziti denove",
|
||||
"settings_pane_visit_again_body": "Firefoĉ montros al vi partojn de via retuma historio, kiujn vi eble volas memori aŭ viziti denove.",
|
||||
"settings_pane_pocketstories_header": "Ĉefaj artikoloj",
|
||||
"settings_pane_pocketstories_body": "Pocket, parto de la familio de Mozilla, helpos vin trovi altkvalitan enhavon, kiun vi eble ne trovos aliloke.",
|
||||
"settings_pane_done_button": "Farita",
|
||||
"edit_topsites_button_text": "Redakti",
|
||||
"edit_topsites_button_label": "Personecigi la sekcion 'plej vizititaj'",
|
||||
"edit_topsites_showmore_button": "Montri pli",
|
||||
"edit_topsites_showless_button": "Montri malpli",
|
||||
"edit_topsites_done_button": "Farita",
|
||||
"edit_topsites_pin_button": "Alpingli ĉi tiun retejon",
|
||||
"edit_topsites_unpin_button": "Depingli tiun ĉi retejon",
|
||||
"edit_topsites_edit_button": "Redakti ĉi tiun retejon",
|
||||
"edit_topsites_dismiss_button": "Ignori ĉi tiun retejon",
|
||||
"edit_topsites_add_button": "Aldoni",
|
||||
"topsites_form_add_header": "Nova ofta retejo",
|
||||
"topsites_form_edit_header": "Redakti ofta retejo",
|
||||
"topsites_form_title_placeholder": "Tajpu titolon",
|
||||
"topsites_form_url_placeholder": "Tajpu aŭ alguu retadreson",
|
||||
"topsites_form_add_button": "Aldoni",
|
||||
"topsites_form_save_button": "Konservi",
|
||||
"topsites_form_cancel_button": "Nuligi",
|
||||
"topsites_form_url_validation": "Valida retadreso estas postulata",
|
||||
"pocket_read_more": "Ĉefaj temoj:",
|
||||
"pocket_read_even_more": "Montri pli da artikoloj",
|
||||
"pocket_feedback_header": "La plejbono el la Teksaĵo, reviziita de pli ol 25 milionoj da personoj.",
|
||||
"pocket_feedback_body": "Pocket, parto de la familio de Mozilla, helpos vin trovi altkvalitan enhavon, kiun vi eble ne trovos aliloke.",
|
||||
"pocket_send_feedback": "Sendi komentojn"
|
||||
},
|
||||
"es-AR": {
|
||||
"newtab_page_title": "Nueva pestaña",
|
||||
|
@ -2027,10 +2154,15 @@
|
|||
"newtab_page_title": "לשונית חדשה",
|
||||
"default_label_loading": "בטעינה…",
|
||||
"header_top_sites": "אתרים מובילים",
|
||||
"header_highlights": "המלצות",
|
||||
"header_stories": "סיפורים מובילים",
|
||||
"header_visit_again": "ביקור חוזר",
|
||||
"header_bookmarks": "סימניות אחרונות",
|
||||
"header_bookmarks_placeholder": "אין לך סימניות עדיין.",
|
||||
"header_stories_from": "מאת",
|
||||
"type_label_visited": "ביקורים קודמים",
|
||||
"type_label_bookmarked": "נוצרה סימניה",
|
||||
"type_label_synced": "סונכרן מהתקן אחר",
|
||||
"type_label_recommended": "פופולרי",
|
||||
"type_label_open": "פתיחה",
|
||||
"type_label_topic": "נושא",
|
||||
"menu_action_bookmark": "הוספת סימניה",
|
||||
|
@ -2039,8 +2171,13 @@
|
|||
"menu_action_email_link": "שליחת קישור בדוא״ל…",
|
||||
"menu_action_open_new_window": "פתיחה בחלון חדש",
|
||||
"menu_action_open_private_window": "פתיחה בלשונית פרטית חדשה",
|
||||
"menu_action_dismiss": "ביטול",
|
||||
"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",
|
||||
"search_for_something_with": "חיפוש אחר {search_term} עם:",
|
||||
"search_button": "חיפוש",
|
||||
"search_header": "חיפוש ב־{search_engine_name}",
|
||||
|
@ -2061,8 +2198,12 @@
|
|||
"settings_pane_topsites_header": "אתרים מובילים",
|
||||
"settings_pane_topsites_body": "גישה לאתרים בהם ביקרת הכי הרבה.",
|
||||
"settings_pane_topsites_options_showmore": "הצגת שתי שורות",
|
||||
"settings_pane_highlights_header": "המלצות",
|
||||
"settings_pane_highlights_body": "ניתן להסתכל על היסטוריית הגלישה העדכנית שלך ועל הסימניות האחרונות שנוצרו.",
|
||||
"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_done_button": "סיום",
|
||||
"edit_topsites_button_text": "עריכה",
|
||||
"edit_topsites_button_label": "התאמת אגף האתרים המובילים שלך",
|
||||
|
@ -2070,8 +2211,23 @@
|
|||
"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_dismiss_button": "התעלמות מאתר זה",
|
||||
"edit_topsites_add_button": "הוספה",
|
||||
"topsites_form_add_header": "אתר מוביל חדש",
|
||||
"topsites_form_edit_header": "עריכת אתר מוביל",
|
||||
"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": "המיטב מרחבי האינטרנט, נאסף על ידי 25 מיליון אנשים.",
|
||||
"pocket_feedback_body": "Pocket, חלק ממשפחת Mozilla, יסייע לך להתחבר לתוכן באיכות גבוהה שיתכן שלא היה מגיע אליך בדרך אחרת.",
|
||||
"pocket_send_feedback": "שליחת משוב"
|
||||
},
|
||||
"hi-IN": {
|
||||
"newtab_page_title": "नया टैब",
|
||||
|
@ -3038,8 +3194,10 @@
|
|||
"newtab_page_title": "Nauja kortelė",
|
||||
"default_label_loading": "Įkeliama…",
|
||||
"header_top_sites": "Lankomiausios svetainės",
|
||||
"header_highlights": "Akcentai",
|
||||
"header_stories": "Populiariausi straipsniai",
|
||||
"header_visit_again": "Aplankykite vėl",
|
||||
"header_bookmarks": "Paskiausi adresyno įrašai",
|
||||
"header_bookmarks_placeholder": "Jūs dar neturite adresyno įrašų.",
|
||||
"header_stories_from": "iš",
|
||||
"type_label_visited": "Aplankyti",
|
||||
"type_label_bookmarked": "Adresyne",
|
||||
|
@ -3055,6 +3213,10 @@
|
|||
"menu_action_open_private_window": "Atverti naujame privačiajame lange",
|
||||
"menu_action_dismiss": "Paslėpti",
|
||||
"menu_action_delete": "Pašalinti iš istorijos",
|
||||
"menu_action_pin": "Įsegti",
|
||||
"menu_action_unpin": "Išsegti",
|
||||
"confirm_history_delete_p1": "Ar tikrai norite pašalinti visus šio tinklalapio įrašus iš savo naršymo žurnalo?",
|
||||
"confirm_history_delete_notice_p2": "Atlikus šį veiksmą, jo atšaukti neįmanoma.",
|
||||
"menu_action_save_to_pocket": "Įrašyti į „Pocket“",
|
||||
"search_for_something_with": "Ieškoti „{search_term}“ per:",
|
||||
"search_button": "Ieškoti",
|
||||
|
@ -3076,8 +3238,10 @@
|
|||
"settings_pane_topsites_header": "Lankomiausios svetainės",
|
||||
"settings_pane_topsites_body": "Pasiekite jūsų dažniausiai lankomas svetaines.",
|
||||
"settings_pane_topsites_options_showmore": "Rodyti dvi eilutes",
|
||||
"settings_pane_highlights_header": "Akcentai",
|
||||
"settings_pane_highlights_body": "Pažvelkite į savo naujausią naršymo istoriją bei paskiausiai pridėtus adresyno įrašus.",
|
||||
"settings_pane_bookmarks_header": "Paskiausi adresyno įrašai",
|
||||
"settings_pane_bookmarks_body": "Jūsų naujai sukurti adresyno įrašai vienoje vietoje.",
|
||||
"settings_pane_visit_again_header": "Aplankykite vėl",
|
||||
"settings_pane_visit_again_body": "„Firefox“ pateiks ištraukas iš jūsų naršymo žurnalo, kurias galbūt norėtumėte prisiminti.",
|
||||
"settings_pane_pocketstories_header": "Populiariausi straipsniai",
|
||||
"settings_pane_pocketstories_body": "„Pocket“, „Mozillos“ šeimos dalis, padės jums atrasti kokybišką turinį, kurio kitaip gal nebūtumėte radę.",
|
||||
"settings_pane_done_button": "Atlikta",
|
||||
|
@ -3846,7 +4010,7 @@
|
|||
"menu_action_open_new_window": "Abrir em nova janela",
|
||||
"menu_action_open_private_window": "Abrir em nova janela privada",
|
||||
"menu_action_dismiss": "Dispensar",
|
||||
"menu_action_delete": "Eliminar do histórico",
|
||||
"menu_action_delete": "Apagar do histórico",
|
||||
"menu_action_pin": "Afixar",
|
||||
"menu_action_unpin": "Desafixar",
|
||||
"confirm_history_delete_p1": "Tem a certeza de que deseja apagar todas as instâncias desta página do seu histórico?",
|
||||
|
@ -3865,9 +4029,9 @@
|
|||
"time_label_hour": "{number}h",
|
||||
"time_label_day": "{number}d",
|
||||
"settings_pane_button_label": "Personalizar a sua página de novo separador",
|
||||
"settings_pane_header": "Novas preferências de separador",
|
||||
"settings_pane_body": "Escolha o que ver quando abre um novo separador.",
|
||||
"settings_pane_search_header": "Pesquisar",
|
||||
"settings_pane_header": "Preferências de novo separador",
|
||||
"settings_pane_body": "Escolha o que vê quando abre um novo separador.",
|
||||
"settings_pane_search_header": "Pesquisa",
|
||||
"settings_pane_search_body": "Pesquise na Web a partir do seu novo separador.",
|
||||
"settings_pane_topsites_header": "Sites mais visitados",
|
||||
"settings_pane_topsites_body": "Aceda aos websites que mais visita.",
|
||||
|
@ -4999,7 +5163,41 @@
|
|||
"pocket_send_feedback": "جواب الجواب ارسال کریں"
|
||||
},
|
||||
"uz": {},
|
||||
"vi": {},
|
||||
"vi": {
|
||||
"newtab_page_title": "Tab mới",
|
||||
"default_label_loading": "Đang tải…",
|
||||
"header_top_sites": "Trang web hàng đầu",
|
||||
"header_stories": "Câu chuyện hàng đầu",
|
||||
"header_visit_again": "Truy cập lại",
|
||||
"header_bookmarks": "Các bookmark gần đây",
|
||||
"header_bookmarks_placeholder": "Bạn chưa có bookmark nào.",
|
||||
"header_stories_from": "từ",
|
||||
"type_label_visited": "Đã truy cập",
|
||||
"type_label_bookmarked": "Đã được đánh dấu",
|
||||
"type_label_synced": "Đồng bộ từ thiết bị khác",
|
||||
"type_label_recommended": "Xu hướng",
|
||||
"type_label_open": "Mở",
|
||||
"type_label_topic": "Chủ đề",
|
||||
"menu_action_bookmark": "Đánh dấu",
|
||||
"menu_action_remove_bookmark": "Xóa đánh dấu",
|
||||
"menu_action_copy_address": "Chép địa chỉ",
|
||||
"menu_action_email_link": "Liên kết Email...",
|
||||
"menu_action_open_new_window": "Mở trong Cửa Sổ Mới",
|
||||
"menu_action_open_private_window": "Mở trong cửa sổ riêng tư mới",
|
||||
"menu_action_dismiss": "Bỏ qua",
|
||||
"menu_action_delete": "Xóa từ lịch xử",
|
||||
"menu_action_pin": "Ghim",
|
||||
"menu_action_unpin": "Bỏ ghim",
|
||||
"confirm_history_delete_notice_p2": "Hành động này không thể hoàn tác.",
|
||||
"menu_action_save_to_pocket": "Lưu vào Pocket",
|
||||
"search_for_something_with": "Tìm {search_term} với:",
|
||||
"search_button": "Tìm kiếm",
|
||||
"search_header": "Công cụ tìm kiếm {search_engine_name}",
|
||||
"time_label_less_than_minute": "<1phút",
|
||||
"time_label_minute": "{number}phút",
|
||||
"time_label_hour": "{number}giờ",
|
||||
"settings_pane_search_header": "Tìm kiếm"
|
||||
},
|
||||
"wo": {},
|
||||
"xh": {},
|
||||
"zh-CN": {
|
||||
|
|
|
@ -10,6 +10,7 @@ const {utils: Cu} = Components;
|
|||
const {actionTypes: at} = Cu.import("resource://activity-stream/common/Actions.jsm", {});
|
||||
const {DefaultPrefs} = Cu.import("resource://activity-stream/lib/ActivityStreamPrefs.jsm", {});
|
||||
const {LocalizationFeed} = Cu.import("resource://activity-stream/lib/LocalizationFeed.jsm", {});
|
||||
const {ManualMigration} = Cu.import("resource://activity-stream/lib/ManualMigration.jsm", {});
|
||||
const {NewTabInit} = Cu.import("resource://activity-stream/lib/NewTabInit.jsm", {});
|
||||
const {PlacesFeed} = Cu.import("resource://activity-stream/lib/PlacesFeed.jsm", {});
|
||||
const {PrefsFeed} = Cu.import("resource://activity-stream/lib/PrefsFeed.jsm", {});
|
||||
|
@ -81,6 +82,18 @@ const PREFS_CONFIG = new Map([
|
|||
"provider_name": "Pocket",
|
||||
"provider_icon": "pocket"
|
||||
}`
|
||||
}],
|
||||
["migrationExpired", {
|
||||
title: "Boolean flag that decides whether to show the migration message or not.",
|
||||
value: false
|
||||
}],
|
||||
["migrationRemainingDays", {
|
||||
title: "Number of days to show the manual migration message",
|
||||
value: 4
|
||||
}],
|
||||
["migrationLastShownDate", {
|
||||
title: "Timestamp when migration message was last shown. In seconds.",
|
||||
value: 0
|
||||
}]
|
||||
]);
|
||||
|
||||
|
@ -133,6 +146,12 @@ for (const {name, factory, title, value} of SECTION_FEEDS_CONFIG.concat([
|
|||
factory: () => new TopSitesFeed(),
|
||||
title: "Queries places and gets metadata for Top Sites section",
|
||||
value: true
|
||||
},
|
||||
{
|
||||
name: "migration",
|
||||
factory: () => new ManualMigration(),
|
||||
title: "Manual migration wizard",
|
||||
value: true
|
||||
}
|
||||
])) {
|
||||
const pref = `feeds.${name}`;
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"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 MIGRATION_ENDED_EVENT = "Migration:Ended";
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "MigrationUtils", "resource:///modules/MigrationUtils.jsm");
|
||||
|
||||
this.ManualMigration = class ManualMigration {
|
||||
constructor() {
|
||||
Services.obs.addObserver(this, MIGRATION_ENDED_EVENT);
|
||||
this._prefs = new Prefs();
|
||||
}
|
||||
|
||||
uninit() {
|
||||
Services.obs.removeObserver(this, MIGRATION_ENDED_EVENT);
|
||||
}
|
||||
|
||||
isMigrationMessageExpired() {
|
||||
let migrationLastShownDate = new Date(this._prefs.get("migrationLastShownDate") * 1000);
|
||||
let today = new Date();
|
||||
// Round down to midnight.
|
||||
today = new Date(today.getFullYear(), today.getMonth(), today.getDate());
|
||||
|
||||
if (migrationLastShownDate < today) {
|
||||
let migrationRemainingDays = this._prefs.get("migrationRemainingDays") - 1;
|
||||
|
||||
this._prefs.set("migrationRemainingDays", migrationRemainingDays);
|
||||
// .valueOf returns a value that is too large to store so we need to divide by 1000.
|
||||
this._prefs.set("migrationLastShownDate", today.valueOf() / 1000);
|
||||
|
||||
if (migrationRemainingDays <= 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* While alreadyExpired is false the migration message is displayed and we also
|
||||
* keep checking if we should expire it. Broadcast expiration to store.
|
||||
*
|
||||
* @param {bool} alreadyExpired Pref flag that is false for the first 3 active days,
|
||||
* time in which we display the migration message to the user.
|
||||
*/
|
||||
expireIfNecessary(alreadyExpired) {
|
||||
if (!alreadyExpired && this.isMigrationMessageExpired()) {
|
||||
this.expireMigration();
|
||||
}
|
||||
}
|
||||
|
||||
expireMigration() {
|
||||
this.store.dispatch(ac.SetPref("migrationExpired", true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Event listener for migration wizard completion event.
|
||||
*/
|
||||
observe() {
|
||||
this.expireMigration();
|
||||
}
|
||||
|
||||
onAction(action) {
|
||||
switch (action.type) {
|
||||
case at.PREFS_INITIAL_VALUES:
|
||||
this.expireIfNecessary(action.data.migrationExpired);
|
||||
break;
|
||||
case at.MIGRATION_START:
|
||||
MigrationUtils.showMigrationWizard(action._target.browser.ownerGlobal, [MigrationUtils.MIGRATION_ENTRYPOINT_NEWTAB]);
|
||||
break;
|
||||
case at.MIGRATION_CANCEL:
|
||||
this.expireMigration();
|
||||
break;
|
||||
case at.UNINIT:
|
||||
this.uninit();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["ManualMigration"];
|
|
@ -14,6 +14,7 @@ const {Prefs} = Cu.import("resource://activity-stream/lib/ActivityStreamPrefs.js
|
|||
|
||||
const STORIES_UPDATE_TIME = 30 * 60 * 1000; // 30 minutes
|
||||
const TOPICS_UPDATE_TIME = 3 * 60 * 60 * 1000; // 3 hours
|
||||
const STORIES_NOW_THRESHOLD = 24 * 60 * 60 * 1000; // 24 hours
|
||||
const SECTION_ID = "TopStories";
|
||||
|
||||
this.TopStoriesFeed = class TopStoriesFeed {
|
||||
|
@ -38,7 +39,7 @@ this.TopStoriesFeed = class TopStoriesFeed {
|
|||
title: {id: "header_recommended_by", values: {provider: options.provider_name}},
|
||||
rows: [],
|
||||
maxCards: 3,
|
||||
contextMenuOptions: ["SaveToPocket", "Separator", "CheckBookmark", "Separator", "OpenInNewWindow", "OpenInPrivateWindow", "Separator", "BlockUrl"],
|
||||
contextMenuOptions: ["CheckBookmark", "SaveToPocket", "Separator", "OpenInNewWindow", "OpenInPrivateWindow", "Separator", "BlockUrl"],
|
||||
infoOption: {
|
||||
header: {id: "pocket_feedback_header"},
|
||||
body: {id: "pocket_feedback_body"},
|
||||
|
@ -48,7 +49,7 @@ this.TopStoriesFeed = class TopStoriesFeed {
|
|||
}
|
||||
},
|
||||
emptyState: {
|
||||
message: {id: "empty_state_topstories"},
|
||||
message: {id: "topstories_empty_state", values: {provider: options.provider_name}},
|
||||
icon: "check"
|
||||
}
|
||||
};
|
||||
|
@ -77,15 +78,14 @@ this.TopStoriesFeed = class TopStoriesFeed {
|
|||
.then(body => {
|
||||
let items = JSON.parse(body).list;
|
||||
items = items
|
||||
.filter(s => !NewTabUtils.blockedLinks.isBlocked(s.dedupe_url))
|
||||
.filter(s => !NewTabUtils.blockedLinks.isBlocked({"url": s.dedupe_url}))
|
||||
.map(s => ({
|
||||
"guid": s.id,
|
||||
"type": "trending",
|
||||
"type": (Date.now() - (s.published_timestamp * 1000)) <= STORIES_NOW_THRESHOLD ? "now" : "trending",
|
||||
"title": s.title,
|
||||
"description": s.excerpt,
|
||||
"image": this._normalizeUrl(s.image_src),
|
||||
"url": s.dedupe_url,
|
||||
"lastVisitDate": s.published_timestamp
|
||||
"url": s.dedupe_url
|
||||
}));
|
||||
return items;
|
||||
})
|
||||
|
|
|
@ -8,5 +8,8 @@ module.exports = {
|
|||
"assert": true,
|
||||
"sinon": true,
|
||||
"chai": true
|
||||
},
|
||||
"rules": {
|
||||
"react/jsx-no-bind": 0
|
||||
}
|
||||
};
|
||||
|
|
|
@ -251,6 +251,66 @@ describe("Reducers", () => {
|
|||
assert.deepEqual(section.rows, [{url: "www.other.url"}]);
|
||||
});
|
||||
});
|
||||
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: {
|
||||
url: "www.foo.bar",
|
||||
bookmarkGuid: "bookmark123",
|
||||
bookmarkTitle: "Title for bar.com",
|
||||
lastModified: 1234567
|
||||
}
|
||||
};
|
||||
const nextState = Sections(oldState, action);
|
||||
// check a section to ensure the correct url was bookmarked
|
||||
const newRow = nextState[0].rows[0];
|
||||
const oldRow = nextState[0].rows[1];
|
||||
|
||||
// new row has bookmark data
|
||||
assert.equal(newRow.url, action.data.url);
|
||||
assert.equal(newRow.bookmarkGuid, action.data.bookmarkGuid);
|
||||
assert.equal(newRow.bookmarkTitle, action.data.bookmarkTitle);
|
||||
assert.equal(newRow.bookmarkDateCreated, action.data.lastModified);
|
||||
|
||||
// old row is unchanged
|
||||
assert.equal(oldRow, oldState[0].rows[1]);
|
||||
});
|
||||
it("should not update state for empty action.data on PLACES_BOOKMARK_REMOVED", () => {
|
||||
const nextState = Sections(undefined, {type: at.PLACES_BOOKMARK_REMOVED});
|
||||
assert.equal(nextState, INITIAL_STATE.Sections);
|
||||
});
|
||||
it("should remove the bookmark when PLACES_BOOKMARK_REMOVED is received", () => {
|
||||
const action = {
|
||||
type: at.PLACES_BOOKMARK_REMOVED,
|
||||
data: {
|
||||
url: "www.foo.bar",
|
||||
bookmarkGuid: "bookmark123"
|
||||
}
|
||||
};
|
||||
// add some bookmark data for the first url in rows
|
||||
oldState.forEach(item => {
|
||||
item.rows[0].bookmarkGuid = "bookmark123";
|
||||
item.rows[0].bookmarkTitle = "Title for bar.com";
|
||||
item.rows[0].bookmarkDateCreated = 1234567;
|
||||
});
|
||||
const nextState = Sections(oldState, action);
|
||||
// check a section to ensure the correct bookmark was removed
|
||||
const newRow = nextState[0].rows[0];
|
||||
const oldRow = nextState[0].rows[1];
|
||||
|
||||
// new row has bookmark data
|
||||
assert.equal(newRow.url, action.data.url);
|
||||
assert.isUndefined(newRow.bookmarkGuid);
|
||||
assert.isUndefined(newRow.bookmarkTitle);
|
||||
assert.isUndefined(newRow.bookmarkDateCreated);
|
||||
|
||||
// old row is unchanged
|
||||
assert.equal(oldRow, oldState[0].rows[1]);
|
||||
});
|
||||
});
|
||||
describe("#insertPinned", () => {
|
||||
let links;
|
||||
|
|
|
@ -13,14 +13,15 @@ describe("ActivityStream", () => {
|
|||
sandbox = sinon.sandbox.create();
|
||||
({ActivityStream, SECTIONS} = injector({
|
||||
"lib/LocalizationFeed.jsm": {LocalizationFeed: Fake},
|
||||
"lib/ManualMigration.jsm": {ManualMigration: Fake},
|
||||
"lib/NewTabInit.jsm": {NewTabInit: Fake},
|
||||
"lib/PlacesFeed.jsm": {PlacesFeed: Fake},
|
||||
"lib/TelemetryFeed.jsm": {TelemetryFeed: Fake},
|
||||
"lib/TopSitesFeed.jsm": {TopSitesFeed: Fake},
|
||||
"lib/PrefsFeed.jsm": {PrefsFeed: Fake},
|
||||
"lib/SnippetsFeed.jsm": {SnippetsFeed: Fake},
|
||||
"lib/TopStoriesFeed.jsm": {TopStoriesFeed: Fake},
|
||||
"lib/SystemTickFeed.jsm": {SystemTickFeed: Fake}
|
||||
"lib/SystemTickFeed.jsm": {SystemTickFeed: Fake},
|
||||
"lib/TelemetryFeed.jsm": {TelemetryFeed: Fake},
|
||||
"lib/TopSitesFeed.jsm": {TopSitesFeed: Fake},
|
||||
"lib/TopStoriesFeed.jsm": {TopStoriesFeed: Fake}
|
||||
}));
|
||||
as = new ActivityStream();
|
||||
sandbox.stub(as.store, "init");
|
||||
|
@ -118,6 +119,10 @@ describe("ActivityStream", () => {
|
|||
assert.instanceOf(feed, Fake);
|
||||
});
|
||||
});
|
||||
it("should create a ManualMigration feed", () => {
|
||||
const feed = as.feeds.get("feeds.migration")();
|
||||
assert.instanceOf(feed, Fake);
|
||||
});
|
||||
it("should create a Snippets feed", () => {
|
||||
const feed = as.feeds.get("feeds.snippets")();
|
||||
assert.instanceOf(feed, Fake);
|
||||
|
|
|
@ -0,0 +1,207 @@
|
|||
const injector = require("inject!lib/ManualMigration.jsm");
|
||||
const {actionCreators: ac, actionTypes: at} = require("common/Actions.jsm");
|
||||
const {GlobalOverrider} = require("test/unit/utils");
|
||||
|
||||
describe("ManualMigration", () => {
|
||||
let dispatch;
|
||||
let store;
|
||||
let instance;
|
||||
let globals;
|
||||
|
||||
let migrationWizardStub;
|
||||
let fakeServices;
|
||||
let fakePrefs;
|
||||
|
||||
beforeEach(() => {
|
||||
migrationWizardStub = sinon.stub();
|
||||
let fakeMigrationUtils = {
|
||||
showMigrationWizard: migrationWizardStub,
|
||||
MIGRATION_ENTRYPOINT_NEWTAB: "MIGRATION_ENTRYPOINT_NEWTAB"
|
||||
};
|
||||
fakeServices = {
|
||||
obs: {
|
||||
addObserver: sinon.stub(),
|
||||
removeObserver: sinon.stub()
|
||||
}
|
||||
};
|
||||
fakePrefs = function() {};
|
||||
fakePrefs.get = sinon.stub();
|
||||
fakePrefs.set = sinon.stub();
|
||||
|
||||
const {ManualMigration} = injector({"lib/ActivityStreamPrefs.jsm": {Prefs: fakePrefs}});
|
||||
|
||||
globals = new GlobalOverrider();
|
||||
globals.set("Services", fakeServices);
|
||||
globals.set("MigrationUtils", fakeMigrationUtils);
|
||||
|
||||
dispatch = sinon.stub();
|
||||
store = {dispatch};
|
||||
instance = new ManualMigration();
|
||||
instance.store = store;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
globals.restore();
|
||||
});
|
||||
|
||||
it("should set an event listener for Migration:Ended", () => {
|
||||
assert.calledOnce(fakeServices.obs.addObserver);
|
||||
assert.calledWith(fakeServices.obs.addObserver, instance, "Migration:Ended");
|
||||
});
|
||||
|
||||
describe("onAction", () => {
|
||||
it("should call expireIfNecessary on PREFS_INITIAL_VALUE", () => {
|
||||
const action = {
|
||||
type: at.PREFS_INITIAL_VALUES,
|
||||
data: {migrationExpired: true}
|
||||
};
|
||||
|
||||
const expireStub = sinon.stub(instance, "expireIfNecessary");
|
||||
instance.onAction(action);
|
||||
|
||||
assert.calledOnce(expireStub);
|
||||
assert.calledWithExactly(expireStub, action.data.migrationExpired);
|
||||
});
|
||||
it("should call launch the migration wizard on MIGRATION_START", () => {
|
||||
const action = {
|
||||
type: at.MIGRATION_START,
|
||||
_target: {browser: {ownerGlobal: "browser.xul"}},
|
||||
data: {migrationExpired: false}
|
||||
};
|
||||
|
||||
instance.onAction(action);
|
||||
|
||||
assert.calledOnce(migrationWizardStub);
|
||||
assert.calledWithExactly(migrationWizardStub, action._target.browser.ownerGlobal, ["MIGRATION_ENTRYPOINT_NEWTAB"]);
|
||||
});
|
||||
it("should set migrationStatus to true on MIGRATION_CANCEL", () => {
|
||||
const action = {type: at.MIGRATION_CANCEL};
|
||||
|
||||
const setStatusStub = sinon.spy(instance, "expireMigration");
|
||||
instance.onAction(action);
|
||||
|
||||
assert.calledOnce(setStatusStub);
|
||||
assert.calledOnce(dispatch);
|
||||
assert.calledWithExactly(dispatch, ac.SetPref("migrationExpired", true));
|
||||
});
|
||||
it("should set migrationStatus when isMigrationMessageExpired is true", () => {
|
||||
const setStatusStub = sinon.stub(instance, "expireMigration");
|
||||
const isExpiredStub = sinon.stub(instance, "isMigrationMessageExpired");
|
||||
isExpiredStub.returns(true);
|
||||
|
||||
instance.expireIfNecessary(false);
|
||||
assert.calledOnce(setStatusStub);
|
||||
});
|
||||
it("should call isMigrationMessageExpired if migrationExpired is false", () => {
|
||||
const action = {
|
||||
type: at.PREFS_INITIAL_VALUES,
|
||||
data: {migrationExpired: false}
|
||||
};
|
||||
|
||||
const stub = sinon.stub(instance, "isMigrationMessageExpired");
|
||||
instance.onAction(action);
|
||||
|
||||
assert.calledOnce(stub);
|
||||
});
|
||||
describe("isMigrationMessageExpired", () => {
|
||||
beforeEach(() => {
|
||||
instance._prefs = fakePrefs;
|
||||
});
|
||||
it("should check migrationLastShownDate (case: today)", () => {
|
||||
const action = {
|
||||
type: at.PREFS_INITIAL_VALUES,
|
||||
data: {migrationExpired: false}
|
||||
};
|
||||
let today = new Date();
|
||||
today = new Date(today.getFullYear(), today.getMonth(), today.getDate());
|
||||
|
||||
const migrationSpy = sinon.spy(instance, "isMigrationMessageExpired");
|
||||
fakePrefs.get.returns(today);
|
||||
instance.onAction(action);
|
||||
|
||||
assert.calledOnce(migrationSpy);
|
||||
assert.calledOnce(fakePrefs.get);
|
||||
assert.calledWithExactly(fakePrefs.get, "migrationLastShownDate");
|
||||
});
|
||||
it("should return false if lastShownDate is today", () => {
|
||||
let today = new Date();
|
||||
today = new Date(today.getFullYear(), today.getMonth(), today.getDate());
|
||||
|
||||
const migrationSpy = sinon.spy(instance, "isMigrationMessageExpired");
|
||||
fakePrefs.get.returns(today);
|
||||
const ret = instance.isMigrationMessageExpired();
|
||||
|
||||
assert.calledOnce(migrationSpy);
|
||||
assert.calledOnce(fakePrefs.get);
|
||||
assert.equal(ret, false);
|
||||
});
|
||||
it("should check migrationLastShownDate (case: yesterday)", () => {
|
||||
const action = {
|
||||
type: at.PREFS_INITIAL_VALUES,
|
||||
data: {migrationExpired: false}
|
||||
};
|
||||
let today = new Date();
|
||||
let yesterday = new Date(today.getFullYear(), today.getMonth(), today.getDate() - 1);
|
||||
|
||||
const migrationSpy = sinon.spy(instance, "isMigrationMessageExpired");
|
||||
fakePrefs.get.withArgs("migrationLastShownDate").returns(yesterday.valueOf() / 1000);
|
||||
fakePrefs.get.withArgs("migrationRemainingDays").returns(4);
|
||||
instance.onAction(action);
|
||||
|
||||
assert.calledOnce(migrationSpy);
|
||||
assert.calledTwice(fakePrefs.get);
|
||||
assert.calledWithExactly(fakePrefs.get, "migrationLastShownDate");
|
||||
assert.calledWithExactly(fakePrefs.get, "migrationRemainingDays");
|
||||
});
|
||||
it("should update the migration prefs", () => {
|
||||
const action = {
|
||||
type: at.PREFS_INITIAL_VALUES,
|
||||
data: {migrationExpired: false}
|
||||
};
|
||||
let today = new Date();
|
||||
let yesterday = new Date(today.getFullYear(), today.getMonth(), today.getDate() - 1);
|
||||
today = new Date(today.getFullYear(), today.getMonth(), today.getDate());
|
||||
|
||||
const migrationSpy = sinon.spy(instance, "isMigrationMessageExpired");
|
||||
fakePrefs.get.withArgs("migrationLastShownDate").returns(yesterday.valueOf() / 1000);
|
||||
fakePrefs.get.withArgs("migrationRemainingDays").returns(4);
|
||||
instance.onAction(action);
|
||||
|
||||
assert.calledOnce(migrationSpy);
|
||||
assert.calledTwice(fakePrefs.set);
|
||||
assert.calledWithExactly(fakePrefs.set, "migrationRemainingDays", 3);
|
||||
assert.calledWithExactly(fakePrefs.set, "migrationLastShownDate", today.valueOf() / 1000);
|
||||
});
|
||||
it("should return true if remainingDays reaches 0", () => {
|
||||
let today = new Date();
|
||||
let yesterday = new Date(today.getFullYear(), today.getMonth(), today.getDate() - 1);
|
||||
|
||||
const migrationSpy = sinon.spy(instance, "isMigrationMessageExpired");
|
||||
fakePrefs.get.withArgs("migrationLastShownDate").returns(yesterday.valueOf() / 1000);
|
||||
fakePrefs.get.withArgs("migrationRemainingDays").returns(1);
|
||||
const ret = instance.isMigrationMessageExpired();
|
||||
|
||||
assert.calledOnce(migrationSpy);
|
||||
assert.calledTwice(fakePrefs.set);
|
||||
assert.calledWithExactly(fakePrefs.set, "migrationRemainingDays", 0);
|
||||
assert.equal(ret, true);
|
||||
});
|
||||
});
|
||||
});
|
||||
it("should have observe as a proxy for setMigrationStatus", () => {
|
||||
const setStatusStub = sinon.stub(instance, "expireMigration");
|
||||
instance.observe();
|
||||
|
||||
assert.calledOnce(setStatusStub);
|
||||
});
|
||||
it("should remove observer at uninit", () => {
|
||||
const uninitSpy = sinon.spy(instance, "uninit");
|
||||
const action = {type: at.UNINIT};
|
||||
|
||||
instance.onAction(action);
|
||||
|
||||
assert.calledOnce(uninitSpy);
|
||||
assert.calledOnce(fakeServices.obs.removeObserver);
|
||||
assert.calledWith(fakeServices.obs.removeObserver, instance, "Migration:Ended");
|
||||
});
|
||||
});
|
|
@ -51,7 +51,7 @@ describe("Top Stories Feed", () => {
|
|||
title: {id: "header_recommended_by", values: {provider: "test-provider"}},
|
||||
rows: [],
|
||||
maxCards: 3,
|
||||
contextMenuOptions: ["SaveToPocket", "Separator", "CheckBookmark", "Separator", "OpenInNewWindow", "OpenInPrivateWindow", "Separator", "BlockUrl"],
|
||||
contextMenuOptions: ["CheckBookmark", "SaveToPocket", "Separator", "OpenInNewWindow", "OpenInPrivateWindow", "Separator", "BlockUrl"],
|
||||
infoOption: {
|
||||
header: {id: "pocket_feedback_header"},
|
||||
body: {id: "pocket_feedback_body"},
|
||||
|
@ -61,7 +61,7 @@ describe("Top Stories Feed", () => {
|
|||
}
|
||||
},
|
||||
emptyState: {
|
||||
message: {id: "empty_state_topstories"},
|
||||
message: {id: "topstories_empty_state", values: {provider: "test-provider"}},
|
||||
icon: "check"
|
||||
}
|
||||
};
|
||||
|
@ -141,12 +141,11 @@ describe("Top Stories Feed", () => {
|
|||
}]}`;
|
||||
const stories = [{
|
||||
"guid": "1",
|
||||
"type": "trending",
|
||||
"type": "now",
|
||||
"title": "title",
|
||||
"description": "description",
|
||||
"image": "image-url",
|
||||
"url": "rec-url",
|
||||
"lastVisitDate": "123"
|
||||
"url": "rec-url"
|
||||
}];
|
||||
|
||||
instance.stories_endpoint = "stories-endpoint";
|
||||
|
@ -181,7 +180,7 @@ describe("Top Stories Feed", () => {
|
|||
it("should exclude blocked (dismissed) URLs", async () => {
|
||||
let fetchStub = globals.sandbox.stub();
|
||||
globals.set("fetch", fetchStub);
|
||||
globals.set("NewTabUtils", {blockedLinks: {isBlocked: url => url === "blocked"}});
|
||||
globals.set("NewTabUtils", {blockedLinks: {isBlocked: site => site.url === "blocked"}});
|
||||
|
||||
const response = `{"list": [{"dedupe_url" : "blocked"}, {"dedupe_url" : "not_blocked"}]}`;
|
||||
instance.stories_endpoint = "stories-endpoint";
|
||||
|
@ -193,6 +192,30 @@ describe("Top Stories Feed", () => {
|
|||
assert.equal(instance.store.dispatch.firstCall.args[0].data.rows.length, 1);
|
||||
assert.equal(instance.store.dispatch.firstCall.args[0].data.rows[0].url, "not_blocked");
|
||||
});
|
||||
it("should mark stories as new", async () => {
|
||||
let fetchStub = globals.sandbox.stub();
|
||||
globals.set("fetch", fetchStub);
|
||||
globals.set("NewTabUtils", {blockedLinks: {isBlocked: globals.sandbox.spy()}});
|
||||
clock.restore();
|
||||
const response = JSON.stringify({
|
||||
"list": [
|
||||
{"published_timestamp": Date.now() / 1000},
|
||||
{"published_timestamp": "0"},
|
||||
{"published_timestamp": (Date.now() - 2 * 24 * 60 * 60 * 1000) / 1000}
|
||||
]
|
||||
});
|
||||
|
||||
instance.stories_endpoint = "stories-endpoint";
|
||||
fetchStub.resolves({ok: true, status: 200, text: () => response});
|
||||
|
||||
await instance.fetchStories();
|
||||
assert.calledOnce(instance.store.dispatch);
|
||||
assert.propertyVal(instance.store.dispatch.firstCall.args[0], "type", at.SECTION_ROWS_UPDATE);
|
||||
assert.equal(instance.store.dispatch.firstCall.args[0].data.rows.length, 3);
|
||||
assert.equal(instance.store.dispatch.firstCall.args[0].data.rows[0].type, "now");
|
||||
assert.equal(instance.store.dispatch.firstCall.args[0].data.rows[1].type, "trending");
|
||||
assert.equal(instance.store.dispatch.firstCall.args[0].data.rows[2].type, "trending");
|
||||
});
|
||||
it("should fetch topics and send event", async () => {
|
||||
let fetchStub = globals.sandbox.stub();
|
||||
globals.set("fetch", fetchStub);
|
||||
|
|
Загрузка…
Ссылка в новой задаче