зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1482205 - Add top search, fixed search and bug fixes to Activity Stream r=k88hudson
Differential Revision: https://phabricator.services.mozilla.com/D3047 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
0273f8788d
Коммит
bd78b91e1f
|
@ -39,6 +39,7 @@ for (const type of [
|
|||
"DIALOG_OPEN",
|
||||
"DISABLE_ONBOARDING",
|
||||
"DOWNLOAD_CHANGED",
|
||||
"FILL_SEARCH_TERM",
|
||||
"INIT",
|
||||
"MIGRATION_CANCEL",
|
||||
"MIGRATION_COMPLETED",
|
||||
|
|
|
@ -24,6 +24,20 @@ function addLocaleDataForReactIntl(locale) {
|
|||
addLocaleData([{locale, parentLocale: "en"}]);
|
||||
}
|
||||
|
||||
// Returns a function will not be continuously triggered when called. The
|
||||
// function will be triggered if called again after `wait` milliseconds.
|
||||
function debounce(func, wait) {
|
||||
let timer;
|
||||
return (...args) => {
|
||||
if (timer) { return; }
|
||||
|
||||
let wakeUp = () => { timer = null; };
|
||||
|
||||
timer = setTimeout(wakeUp, wait);
|
||||
func.apply(this, args);
|
||||
};
|
||||
}
|
||||
|
||||
export class _Base extends React.PureComponent {
|
||||
componentWillMount() {
|
||||
const {App, locale} = this.props;
|
||||
|
@ -48,9 +62,29 @@ export class _Base extends React.PureComponent {
|
|||
this.updateTheme();
|
||||
}
|
||||
|
||||
componentWillUpdate({App}) {
|
||||
hasTopStoriesSectionChanged(nextProps) {
|
||||
const nPropsSections = nextProps.Sections.find(section => section.id === "topstories");
|
||||
const tPropsSections = this.props.Sections.find(section => section.id === "topstories");
|
||||
if (nPropsSections && nPropsSections.options) {
|
||||
if (!tPropsSections || !tPropsSections.options) {
|
||||
return true;
|
||||
}
|
||||
if (nPropsSections.options.show_spocs !== tPropsSections.options.show_spocs) {
|
||||
return true;
|
||||
}
|
||||
if (nPropsSections.options.stories_endpoint !== tPropsSections.options.stories_endpoint) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
componentWillUpdate(nextProps) {
|
||||
this.updateTheme();
|
||||
this.sendNewTabRehydrated(App);
|
||||
if (this.hasTopStoriesSectionChanged(nextProps)) {
|
||||
this.renderNotified = false;
|
||||
}
|
||||
this.sendNewTabRehydrated(nextProps.App);
|
||||
}
|
||||
|
||||
updateTheme() {
|
||||
|
@ -106,6 +140,25 @@ export class BaseContent extends React.PureComponent {
|
|||
constructor(props) {
|
||||
super(props);
|
||||
this.openPreferences = this.openPreferences.bind(this);
|
||||
this.onWindowScroll = debounce(this.onWindowScroll.bind(this), 5);
|
||||
this.state = {fixedSearch: false};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
global.addEventListener("scroll", this.onWindowScroll);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
global.removeEventListener("scroll", this.onWindowScroll);
|
||||
}
|
||||
|
||||
onWindowScroll() {
|
||||
const SCROLL_THRESHOLD = 34;
|
||||
if (global.scrollY > SCROLL_THRESHOLD && !this.state.fixedSearch) {
|
||||
this.setState({fixedSearch: true});
|
||||
} else if (global.scrollY <= SCROLL_THRESHOLD && this.state.fixedSearch) {
|
||||
this.setState({fixedSearch: false});
|
||||
}
|
||||
}
|
||||
|
||||
openPreferences() {
|
||||
|
@ -123,7 +176,8 @@ export class BaseContent extends React.PureComponent {
|
|||
|
||||
const outerClassName = [
|
||||
"outer-wrapper",
|
||||
shouldBeFixedToTop && "fixed-to-top"
|
||||
shouldBeFixedToTop && "fixed-to-top",
|
||||
prefs.showSearch && this.state.fixedSearch && "fixed-search"
|
||||
].filter(v => v).join(" ");
|
||||
|
||||
return (
|
||||
|
@ -154,4 +208,4 @@ export class BaseContent extends React.PureComponent {
|
|||
}
|
||||
}
|
||||
|
||||
export const Base = connect(state => ({App: state.App, Prefs: state.Prefs}))(_Base);
|
||||
export const Base = connect(state => ({App: state.App, Prefs: state.Prefs, Sections: state.Sections}))(_Base);
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
inset-inline-start: 100%;
|
||||
position: absolute;
|
||||
top: ($context-menu-button-size / 4);
|
||||
z-index: 10000;
|
||||
z-index: 8;
|
||||
|
||||
> ul {
|
||||
list-style: none;
|
||||
|
|
|
@ -13,7 +13,7 @@ export class _LinkMenu extends React.PureComponent {
|
|||
const {site, index, source, isPrivateBrowsingEnabled, siteInfo, platform} = props;
|
||||
|
||||
// Handle special case of default site
|
||||
const propOptions = !site.isDefault ? props.options : DEFAULT_SITE_MENU_OPTIONS;
|
||||
const propOptions = (!site.isDefault || site.searchTopSite) ? props.options : DEFAULT_SITE_MENU_OPTIONS;
|
||||
|
||||
const options = propOptions.map(o => LinkMenuOptions[o](site, index, source, isPrivateBrowsingEnabled, siteInfo, platform)).map(option => {
|
||||
const {action, impression, id, string_id, type, userEvent} = option;
|
||||
|
|
|
@ -64,23 +64,25 @@ export class _Search extends React.PureComponent {
|
|||
*/
|
||||
render() {
|
||||
return (<div className="search-wrapper">
|
||||
<label htmlFor="newtab-search-text" className="search-label">
|
||||
<span className="sr-only"><FormattedMessage id="search_web_placeholder" /></span>
|
||||
</label>
|
||||
<input
|
||||
id="newtab-search-text"
|
||||
maxLength="256"
|
||||
placeholder={this.props.intl.formatMessage({id: "search_web_placeholder"})}
|
||||
ref={this.onInputMount}
|
||||
title={this.props.intl.formatMessage({id: "search_web_placeholder"})}
|
||||
type="search" />
|
||||
<button
|
||||
id="searchSubmit"
|
||||
className="search-button"
|
||||
onClick={this.onClick}
|
||||
title={this.props.intl.formatMessage({id: "search_button"})}>
|
||||
<span className="sr-only"><FormattedMessage id="search_button" /></span>
|
||||
</button>
|
||||
<div className="search-inner-wrapper">
|
||||
<label htmlFor="newtab-search-text" className="search-label">
|
||||
<span className="sr-only"><FormattedMessage id="search_web_placeholder" /></span>
|
||||
</label>
|
||||
<input
|
||||
id="newtab-search-text"
|
||||
maxLength="256"
|
||||
placeholder={this.props.intl.formatMessage({id: "search_web_placeholder"})}
|
||||
ref={this.onInputMount}
|
||||
title={this.props.intl.formatMessage({id: "search_web_placeholder"})}
|
||||
type="search" />
|
||||
<button
|
||||
id="searchSubmit"
|
||||
className="search-button"
|
||||
onClick={this.onClick}
|
||||
title={this.props.intl.formatMessage({id: "search_button"})}>
|
||||
<span className="sr-only"><FormattedMessage id="search_button" /></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,21 +1,38 @@
|
|||
.search-wrapper {
|
||||
$search-height: 35px;
|
||||
$search-icon-size: 18px;
|
||||
$search-icon-padding: 8px;
|
||||
$search-icon-width: 2 * $search-icon-padding + $search-icon-size;
|
||||
$search-input-left-label-width: 35px;
|
||||
$search-button-width: 36px;
|
||||
$search-height: 48px;
|
||||
$search-icon-size: 24px;
|
||||
$search-icon-padding: 12px;
|
||||
$search-icon-width: 2 * $search-icon-padding + $search-icon-size -2;
|
||||
$search-button-width: 48px;
|
||||
$glyph-forward: url('chrome://browser/skin/forward.svg');
|
||||
|
||||
cursor: default;
|
||||
display: flex;
|
||||
height: $search-height;
|
||||
margin-bottom: $section-spacing;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
background-color: var(--newtab-background-color);
|
||||
padding: 34px 0 64px;
|
||||
|
||||
@media (max-height: 700px) {
|
||||
& {
|
||||
padding: 0 0 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.search-inner-wrapper {
|
||||
cursor: default;
|
||||
display: flex;
|
||||
height: $search-height;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media (min-width: $break-point-large) {
|
||||
.search-inner-wrapper {
|
||||
margin: 0 auto;
|
||||
width: $max-searchbar-width;
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
background: var(--newtab-textbox-background-color) var(--newtab-search-icon) $search-icon-padding center / $search-icon-size no-repeat;
|
||||
background: var(--newtab-textbox-background-color) var(--newtab-search-icon) $search-icon-padding center no-repeat;
|
||||
background-size: $search-icon-size;
|
||||
border: solid 1px var(--newtab-search-border-color);
|
||||
box-shadow: $shadow-secondary, 0 0 0 1px $black-15;
|
||||
font-size: 15px;
|
||||
|
@ -69,6 +86,44 @@
|
|||
}
|
||||
}
|
||||
|
||||
@media (min-height: 701px) {
|
||||
.fixed-search {
|
||||
main {
|
||||
padding-top: 146px;
|
||||
}
|
||||
|
||||
.search-wrapper {
|
||||
$search-header-bar-height: 95px;
|
||||
$search-height: 35px;
|
||||
$search-icon-size: 16px;
|
||||
$search-icon-padding: 16px;
|
||||
|
||||
background-color: var(--newtab-search-header-background-color);
|
||||
border-bottom: solid 1px var(--newtab-border-secondary-color);
|
||||
height: $search-header-bar-height;
|
||||
left: 0;
|
||||
padding: 30px 0;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
z-index: 9;
|
||||
|
||||
.search-inner-wrapper {
|
||||
height: $search-height;
|
||||
}
|
||||
|
||||
input {
|
||||
background-position-x: $search-icon-padding;
|
||||
background-size: $search-icon-size;
|
||||
|
||||
&:dir(rtl) {
|
||||
background-position-x: right $search-icon-padding;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@at-root {
|
||||
// Adjust the style of the contentSearchUI-generated table
|
||||
.contentSearchSuggestionTable {
|
||||
|
@ -150,4 +205,10 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.contentSearchHeaderRow > td > img,
|
||||
.contentSearchSuggestionRow > td > .historyIcon {
|
||||
margin-inline-start: 7px;
|
||||
margin-inline-end: 15px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,8 @@ export class _StartupOverlay extends React.PureComponent {
|
|||
if (this.props.fxa_endpoint && !this.didFetch) {
|
||||
try {
|
||||
this.didFetch = true;
|
||||
const response = await fetch(`${this.props.fxa_endpoint}/metrics-flow`);
|
||||
const response = await fetch(`${this.props.fxa_endpoint}/metrics-flow?entrypoint=
|
||||
activity-stream-firstrun&utm_source=activity-stream&utm_campaign=firstrun&form_type=email`);
|
||||
if (response.status === 200) {
|
||||
const {flowId, flowBeginTime} = await response.json();
|
||||
this.setState({flowId, flowBeginTime});
|
||||
|
|
|
@ -4,6 +4,7 @@ import {
|
|||
MIN_CORNER_FAVICON_SIZE,
|
||||
MIN_RICH_FAVICON_SIZE,
|
||||
TOP_SITES_CONTEXT_MENU_OPTIONS,
|
||||
TOP_SITES_SEARCH_SHORTCUTS_CONTEXT_MENU_OPTIONS,
|
||||
TOP_SITES_SOURCE
|
||||
} from "./TopSitesConstants";
|
||||
import {LinkMenu} from "content-src/components/LinkMenu/LinkMenu";
|
||||
|
@ -120,6 +121,13 @@ export class TopSiteLink extends React.PureComponent {
|
|||
let hasScreenshotImage = this.state.screenshotImage && this.state.screenshotImage.url;
|
||||
if (defaultStyle) { // force no styles (letter fallback) even if the link has imagery
|
||||
smallFaviconFallback = false;
|
||||
} else if (link.searchTopSite) {
|
||||
imageClassName = "top-site-icon rich-icon";
|
||||
imageStyle = {
|
||||
backgroundColor: link.backgroundColor,
|
||||
backgroundImage: `url(${tippyTopIcon})`
|
||||
};
|
||||
smallFaviconStyle = {backgroundImage: `url(${tippyTopIcon})`};
|
||||
} else if (link.customScreenshotURL) {
|
||||
// assume high quality custom screenshot and use rich icon styles and class names
|
||||
imageClassName = "top-site-icon rich-icon";
|
||||
|
@ -164,6 +172,7 @@ export class TopSiteLink extends React.PureComponent {
|
|||
<a href={link.url} onClick={onClick}>
|
||||
<div className="tile" aria-hidden={true} data-fallback={letterFallback}>
|
||||
<div className={imageClassName} style={imageStyle} />
|
||||
{link.searchTopSite && <div className="top-site-icon search-topsite" />}
|
||||
{showSmallFavicon && <div
|
||||
className="top-site-icon default-icon"
|
||||
data-fallback={smallFaviconFallback && letterFallback}
|
||||
|
@ -203,6 +212,11 @@ export class TopSite extends React.PureComponent {
|
|||
if (this.props.link.isPinned) {
|
||||
value.card_type = "pinned";
|
||||
}
|
||||
if (this.props.link.searchTopSite) {
|
||||
// Set the card_type as "search" regardless of its pinning status
|
||||
value.card_type = "search";
|
||||
value.search_vendor = this.props.link.hostname;
|
||||
}
|
||||
return {value};
|
||||
}
|
||||
|
||||
|
@ -221,10 +235,17 @@ export class TopSite extends React.PureComponent {
|
|||
// specified as a property on the link.
|
||||
event.preventDefault();
|
||||
const {altKey, button, ctrlKey, metaKey, shiftKey} = event;
|
||||
this.props.dispatch(ac.OnlyToMain({
|
||||
type: at.OPEN_LINK,
|
||||
data: Object.assign(this.props.link, {event: {altKey, button, ctrlKey, metaKey, shiftKey}})
|
||||
}));
|
||||
if (!this.props.link.searchTopSite) {
|
||||
this.props.dispatch(ac.OnlyToMain({
|
||||
type: at.OPEN_LINK,
|
||||
data: Object.assign(this.props.link, {event: {altKey, button, ctrlKey, metaKey, shiftKey}})
|
||||
}));
|
||||
} else {
|
||||
this.props.dispatch(ac.OnlyToMain({
|
||||
type: at.FILL_SEARCH_TERM,
|
||||
data: {label: this.props.link.label}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
onMenuButtonClick(event) {
|
||||
|
@ -254,7 +275,7 @@ export class TopSite extends React.PureComponent {
|
|||
dispatch={props.dispatch}
|
||||
index={props.index}
|
||||
onUpdate={this.onMenuUpdate}
|
||||
options={TOP_SITES_CONTEXT_MENU_OPTIONS}
|
||||
options={link.searchTopSite ? TOP_SITES_SEARCH_SHORTCUTS_CONTEXT_MENU_OPTIONS : TOP_SITES_CONTEXT_MENU_OPTIONS}
|
||||
site={link}
|
||||
siteInfo={this._getTelemetryInfo()}
|
||||
source={TOP_SITES_SOURCE} />
|
||||
|
@ -361,7 +382,9 @@ export class _TopSiteList extends React.PureComponent {
|
|||
site: {
|
||||
url: this.state.draggedSite.url,
|
||||
label: this.state.draggedTitle,
|
||||
customScreenshotURL: this.state.draggedSite.customScreenshotURL
|
||||
customScreenshotURL: this.state.draggedSite.customScreenshotURL,
|
||||
// Only if the search topsites experiment is enabled
|
||||
...(this.state.draggedSite.searchTopSite && {searchTopSite: true})
|
||||
},
|
||||
index,
|
||||
draggedFromIndex: this.state.draggedIndex
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
export const TOP_SITES_SOURCE = "TOP_SITES";
|
||||
export const TOP_SITES_CONTEXT_MENU_OPTIONS = ["CheckPinTopSite", "EditTopSite", "Separator",
|
||||
"OpenInNewWindow", "OpenInPrivateWindow", "Separator", "BlockUrl", "DeleteUrl"];
|
||||
// the special top site for search shortcut experiment can only have the option to unpin (which removes) the topsite
|
||||
export const TOP_SITES_SEARCH_SHORTCUTS_CONTEXT_MENU_OPTIONS = ["CheckPinTopSite", "Separator", "BlockUrl"];
|
||||
// minimum size necessary to show a rich icon instead of a screenshot
|
||||
export const MIN_RICH_FAVICON_SIZE = 96;
|
||||
// minimum size necessary to show any icon in the top left corner with a screenshot
|
||||
|
|
|
@ -178,7 +178,8 @@ $half-base-gutter: $base-gutter / 2;
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
.default-icon { // sass-lint:disable block property-sort-order
|
||||
.default-icon,
|
||||
.search-topsite {
|
||||
background-size: $default-icon-size;
|
||||
bottom: -$default-icon-offset;
|
||||
height: $default-icon-wrapper-size;
|
||||
|
@ -196,6 +197,16 @@ $half-base-gutter: $base-gutter / 2;
|
|||
}
|
||||
}
|
||||
|
||||
.search-topsite {
|
||||
background-image: url('#{$image-path}glyph-search-16.svg');
|
||||
background-size: 26px;
|
||||
background-color: $blue-60;
|
||||
border-radius: 42px;
|
||||
-moz-context-properties: fill;
|
||||
fill: $white;
|
||||
box-shadow: inset 0 0 0 1px var(--newtab-inner-box-shadow-color), var(--newtab-card-shadow);
|
||||
}
|
||||
|
||||
.title {
|
||||
color: var(--newtab-topsites-label-color);
|
||||
font: message-box;
|
||||
|
|
|
@ -149,12 +149,18 @@ export const LinkMenuOptions = {
|
|||
data: {url: site.url}
|
||||
})
|
||||
}),
|
||||
PinTopSite: (site, index) => ({
|
||||
PinTopSite: ({url, searchTopSite, label}, index) => ({
|
||||
id: "menu_action_pin",
|
||||
icon: "pin",
|
||||
action: ac.AlsoToMain({
|
||||
type: at.TOP_SITES_PIN,
|
||||
data: {site: {url: site.url}, index}
|
||||
data: {
|
||||
site: {
|
||||
url,
|
||||
...(searchTopSite && {searchTopSite, label})
|
||||
},
|
||||
index
|
||||
}
|
||||
}),
|
||||
userEvent: "PIN"
|
||||
}),
|
||||
|
|
|
@ -58,6 +58,7 @@ body {
|
|||
--newtab-search-border-color: transparent;
|
||||
--newtab-search-dropdown-color: $white;
|
||||
--newtab-search-dropdown-header-color: $grey-10;
|
||||
--newtab-search-header-background-color: $grey-10-95;
|
||||
--newtab-search-icon-color: $grey-90-40;
|
||||
|
||||
// Top Sites
|
||||
|
@ -114,6 +115,7 @@ body {
|
|||
--newtab-search-border-color: $grey-10-20;
|
||||
--newtab-search-dropdown-color: $grey-70;
|
||||
--newtab-search-dropdown-header-color: $grey-60;
|
||||
--newtab-search-header-background-color: $grey-80-95;
|
||||
--newtab-search-icon-color: $grey-10-60;
|
||||
|
||||
// Top Sites
|
||||
|
|
|
@ -23,9 +23,11 @@ $grey-10-20: rgba($grey-10, 0.2);
|
|||
$grey-10-40: rgba($grey-10, 0.4);
|
||||
$grey-10-60: rgba($grey-10, 0.6);
|
||||
$grey-10-80: rgba($grey-10, 0.8);
|
||||
$grey-10-95: rgba($grey-10, 0.95);
|
||||
$grey-20-60: rgba($grey-20, 0.6);
|
||||
$grey-20-80: rgba($grey-20, 0.8);
|
||||
$grey-30-60: rgba($grey-30, 0.6);
|
||||
$grey-80-95: rgba($grey-80, 0.95);
|
||||
$grey-90-10: rgba($grey-90, 0.1);
|
||||
$grey-90-20: rgba($grey-90, 0.2);
|
||||
$grey-90-30: rgba($grey-90, 0.3);
|
||||
|
@ -89,6 +91,8 @@ $break-point-medium: $wrapper-max-width-medium + $base-gutter * 2 + $scrollbar-w
|
|||
$break-point-large: $wrapper-max-width-large + $base-gutter * 2 + $scrollbar-width;
|
||||
$break-point-widest: $wrapper-max-width-widest + $base-gutter * 2 + $scrollbar-width;
|
||||
|
||||
$max-searchbar-width: $grid-unit * 6 + $base-gutter * 5;
|
||||
|
||||
$section-title-font-size: 13px;
|
||||
|
||||
$card-width: $grid-unit * 2 + $base-gutter;
|
||||
|
|
|
@ -55,6 +55,7 @@ body {
|
|||
--newtab-search-border-color: transparent;
|
||||
--newtab-search-dropdown-color: #FFF;
|
||||
--newtab-search-dropdown-header-color: #F9F9FA;
|
||||
--newtab-search-header-background-color: rgba(249, 249, 250, 0.95);
|
||||
--newtab-search-icon-color: rgba(12, 12, 13, 0.4);
|
||||
--newtab-topsites-background-color: #FFF;
|
||||
--newtab-topsites-icon-shadow: inset 0 0 0 1px var(--newtab-inner-box-shadow-color);
|
||||
|
@ -96,6 +97,7 @@ body {
|
|||
--newtab-search-border-color: rgba(249, 249, 250, 0.2);
|
||||
--newtab-search-dropdown-color: #38383D;
|
||||
--newtab-search-dropdown-header-color: #4A4A4F;
|
||||
--newtab-search-header-background-color: rgba(42, 42, 46, 0.95);
|
||||
--newtab-search-icon-color: rgba(249, 249, 250, 0.6);
|
||||
--newtab-topsites-background-color: #38383D;
|
||||
--newtab-topsites-icon-shadow: none;
|
||||
|
@ -552,7 +554,8 @@ main {
|
|||
inset-inline-start: 0;
|
||||
top: 0;
|
||||
width: 100%; }
|
||||
.top-site-outer .default-icon {
|
||||
.top-site-outer .default-icon,
|
||||
.top-site-outer .search-topsite {
|
||||
background-size: 32px;
|
||||
bottom: -6px;
|
||||
height: 42px;
|
||||
|
@ -562,8 +565,17 @@ main {
|
|||
display: flex;
|
||||
font-size: 20px;
|
||||
justify-content: center; }
|
||||
.top-site-outer .default-icon[data-fallback]::before {
|
||||
.top-site-outer .default-icon[data-fallback]::before,
|
||||
.top-site-outer .search-topsite[data-fallback]::before {
|
||||
content: attr(data-fallback); }
|
||||
.top-site-outer .search-topsite {
|
||||
background-image: url("../data/content/assets/glyph-search-16.svg");
|
||||
background-size: 26px;
|
||||
background-color: #0060DF;
|
||||
border-radius: 42px;
|
||||
-moz-context-properties: fill;
|
||||
fill: #FFF;
|
||||
box-shadow: inset 0 0 0 1px var(--newtab-inner-box-shadow-color), var(--newtab-card-shadow); }
|
||||
.top-site-outer .title {
|
||||
color: var(--newtab-topsites-label-color);
|
||||
font: message-box;
|
||||
|
@ -1119,25 +1131,35 @@ a.firstrun-link {
|
|||
display: table; }
|
||||
|
||||
.search-wrapper {
|
||||
cursor: default;
|
||||
display: flex;
|
||||
height: 35px;
|
||||
margin-bottom: 20px;
|
||||
position: relative;
|
||||
width: 100%; }
|
||||
background-color: var(--newtab-background-color);
|
||||
padding: 34px 0 64px; }
|
||||
@media (max-height: 700px) {
|
||||
.search-wrapper {
|
||||
padding: 0 0 30px; } }
|
||||
.search-wrapper .search-inner-wrapper {
|
||||
cursor: default;
|
||||
display: flex;
|
||||
height: 48px;
|
||||
position: relative;
|
||||
width: 100%; }
|
||||
@media (min-width: 866px) {
|
||||
.search-wrapper .search-inner-wrapper {
|
||||
margin: 0 auto;
|
||||
width: 736px; } }
|
||||
.search-wrapper input {
|
||||
background: var(--newtab-textbox-background-color) var(--newtab-search-icon) 8px center/18px no-repeat;
|
||||
background: var(--newtab-textbox-background-color) var(--newtab-search-icon) 12px center no-repeat;
|
||||
background-size: 24px;
|
||||
border: solid 1px var(--newtab-search-border-color);
|
||||
box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.2), 0 0 0 1px rgba(0, 0, 0, 0.15);
|
||||
font-size: 15px;
|
||||
-moz-context-properties: fill;
|
||||
fill: var(--newtab-search-icon-color);
|
||||
padding: 0;
|
||||
padding-inline-end: 36px;
|
||||
padding-inline-start: 34px;
|
||||
padding-inline-end: 48px;
|
||||
padding-inline-start: 46px;
|
||||
width: 100%; }
|
||||
.search-wrapper input:dir(rtl) {
|
||||
background-position-x: right 8px; }
|
||||
background-position-x: right 12px; }
|
||||
.search-wrapper:hover input {
|
||||
box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.2), 0 0 0 1px rgba(0, 0, 0, 0.25); }
|
||||
.search-wrapper:active input,
|
||||
|
@ -1154,7 +1176,7 @@ a.firstrun-link {
|
|||
height: 100%;
|
||||
inset-inline-end: 0;
|
||||
position: absolute;
|
||||
width: 36px; }
|
||||
width: 48px; }
|
||||
.search-wrapper .search-button:focus, .search-wrapper .search-button:hover {
|
||||
background-color: rgba(12, 12, 13, 0.1);
|
||||
cursor: pointer; }
|
||||
|
@ -1163,6 +1185,27 @@ a.firstrun-link {
|
|||
.search-wrapper .search-button:dir(rtl) {
|
||||
transform: scaleX(-1); }
|
||||
|
||||
@media (min-height: 701px) {
|
||||
.fixed-search main {
|
||||
padding-top: 146px; }
|
||||
.fixed-search .search-wrapper {
|
||||
background-color: var(--newtab-search-header-background-color);
|
||||
border-bottom: solid 1px var(--newtab-border-secondary-color);
|
||||
height: 95px;
|
||||
left: 0;
|
||||
padding: 30px 0;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
z-index: 9; }
|
||||
.fixed-search .search-wrapper .search-inner-wrapper {
|
||||
height: 35px; }
|
||||
.fixed-search .search-wrapper input {
|
||||
background-position-x: 16px;
|
||||
background-size: 16px; }
|
||||
.fixed-search .search-wrapper input:dir(rtl) {
|
||||
background-position-x: right 16px; } }
|
||||
|
||||
.contentSearchSuggestionTable {
|
||||
background-color: var(--newtab-search-dropdown-color);
|
||||
border: 0;
|
||||
|
@ -1204,6 +1247,10 @@ a.firstrun-link {
|
|||
background: var(--newtab-element-hover-color);
|
||||
color: var(--newtab-text-primary-color); }
|
||||
|
||||
.contentSearchHeaderRow > td > img, .contentSearchSuggestionRow > td > .historyIcon {
|
||||
margin-inline-start: 7px;
|
||||
margin-inline-end: 15px; }
|
||||
|
||||
.context-menu {
|
||||
background: var(--newtab-contextmenu-background-color);
|
||||
border-radius: 5px;
|
||||
|
@ -1214,7 +1261,7 @@ a.firstrun-link {
|
|||
inset-inline-start: 100%;
|
||||
position: absolute;
|
||||
top: 6.75px;
|
||||
z-index: 10000; }
|
||||
z-index: 8; }
|
||||
.context-menu > ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -58,6 +58,7 @@ body {
|
|||
--newtab-search-border-color: transparent;
|
||||
--newtab-search-dropdown-color: #FFF;
|
||||
--newtab-search-dropdown-header-color: #F9F9FA;
|
||||
--newtab-search-header-background-color: rgba(249, 249, 250, 0.95);
|
||||
--newtab-search-icon-color: rgba(12, 12, 13, 0.4);
|
||||
--newtab-topsites-background-color: #FFF;
|
||||
--newtab-topsites-icon-shadow: inset 0 0 0 1px var(--newtab-inner-box-shadow-color);
|
||||
|
@ -99,6 +100,7 @@ body {
|
|||
--newtab-search-border-color: rgba(249, 249, 250, 0.2);
|
||||
--newtab-search-dropdown-color: #38383D;
|
||||
--newtab-search-dropdown-header-color: #4A4A4F;
|
||||
--newtab-search-header-background-color: rgba(42, 42, 46, 0.95);
|
||||
--newtab-search-icon-color: rgba(249, 249, 250, 0.6);
|
||||
--newtab-topsites-background-color: #38383D;
|
||||
--newtab-topsites-icon-shadow: none;
|
||||
|
@ -555,7 +557,8 @@ main {
|
|||
inset-inline-start: 0;
|
||||
top: 0;
|
||||
width: 100%; }
|
||||
.top-site-outer .default-icon {
|
||||
.top-site-outer .default-icon,
|
||||
.top-site-outer .search-topsite {
|
||||
background-size: 32px;
|
||||
bottom: -6px;
|
||||
height: 42px;
|
||||
|
@ -565,8 +568,17 @@ main {
|
|||
display: flex;
|
||||
font-size: 20px;
|
||||
justify-content: center; }
|
||||
.top-site-outer .default-icon[data-fallback]::before {
|
||||
.top-site-outer .default-icon[data-fallback]::before,
|
||||
.top-site-outer .search-topsite[data-fallback]::before {
|
||||
content: attr(data-fallback); }
|
||||
.top-site-outer .search-topsite {
|
||||
background-image: url("../data/content/assets/glyph-search-16.svg");
|
||||
background-size: 26px;
|
||||
background-color: #0060DF;
|
||||
border-radius: 42px;
|
||||
-moz-context-properties: fill;
|
||||
fill: #FFF;
|
||||
box-shadow: inset 0 0 0 1px var(--newtab-inner-box-shadow-color), var(--newtab-card-shadow); }
|
||||
.top-site-outer .title {
|
||||
color: var(--newtab-topsites-label-color);
|
||||
font: message-box;
|
||||
|
@ -1122,25 +1134,35 @@ a.firstrun-link {
|
|||
display: table; }
|
||||
|
||||
.search-wrapper {
|
||||
cursor: default;
|
||||
display: flex;
|
||||
height: 35px;
|
||||
margin-bottom: 20px;
|
||||
position: relative;
|
||||
width: 100%; }
|
||||
background-color: var(--newtab-background-color);
|
||||
padding: 34px 0 64px; }
|
||||
@media (max-height: 700px) {
|
||||
.search-wrapper {
|
||||
padding: 0 0 30px; } }
|
||||
.search-wrapper .search-inner-wrapper {
|
||||
cursor: default;
|
||||
display: flex;
|
||||
height: 48px;
|
||||
position: relative;
|
||||
width: 100%; }
|
||||
@media (min-width: 866px) {
|
||||
.search-wrapper .search-inner-wrapper {
|
||||
margin: 0 auto;
|
||||
width: 736px; } }
|
||||
.search-wrapper input {
|
||||
background: var(--newtab-textbox-background-color) var(--newtab-search-icon) 8px center/18px no-repeat;
|
||||
background: var(--newtab-textbox-background-color) var(--newtab-search-icon) 12px center no-repeat;
|
||||
background-size: 24px;
|
||||
border: solid 1px var(--newtab-search-border-color);
|
||||
box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.2), 0 0 0 1px rgba(0, 0, 0, 0.15);
|
||||
font-size: 15px;
|
||||
-moz-context-properties: fill;
|
||||
fill: var(--newtab-search-icon-color);
|
||||
padding: 0;
|
||||
padding-inline-end: 36px;
|
||||
padding-inline-start: 34px;
|
||||
padding-inline-end: 48px;
|
||||
padding-inline-start: 46px;
|
||||
width: 100%; }
|
||||
.search-wrapper input:dir(rtl) {
|
||||
background-position-x: right 8px; }
|
||||
background-position-x: right 12px; }
|
||||
.search-wrapper:hover input {
|
||||
box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.2), 0 0 0 1px rgba(0, 0, 0, 0.25); }
|
||||
.search-wrapper:active input,
|
||||
|
@ -1157,7 +1179,7 @@ a.firstrun-link {
|
|||
height: 100%;
|
||||
inset-inline-end: 0;
|
||||
position: absolute;
|
||||
width: 36px; }
|
||||
width: 48px; }
|
||||
.search-wrapper .search-button:focus, .search-wrapper .search-button:hover {
|
||||
background-color: rgba(12, 12, 13, 0.1);
|
||||
cursor: pointer; }
|
||||
|
@ -1166,6 +1188,27 @@ a.firstrun-link {
|
|||
.search-wrapper .search-button:dir(rtl) {
|
||||
transform: scaleX(-1); }
|
||||
|
||||
@media (min-height: 701px) {
|
||||
.fixed-search main {
|
||||
padding-top: 146px; }
|
||||
.fixed-search .search-wrapper {
|
||||
background-color: var(--newtab-search-header-background-color);
|
||||
border-bottom: solid 1px var(--newtab-border-secondary-color);
|
||||
height: 95px;
|
||||
left: 0;
|
||||
padding: 30px 0;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
z-index: 9; }
|
||||
.fixed-search .search-wrapper .search-inner-wrapper {
|
||||
height: 35px; }
|
||||
.fixed-search .search-wrapper input {
|
||||
background-position-x: 16px;
|
||||
background-size: 16px; }
|
||||
.fixed-search .search-wrapper input:dir(rtl) {
|
||||
background-position-x: right 16px; } }
|
||||
|
||||
.contentSearchSuggestionTable {
|
||||
background-color: var(--newtab-search-dropdown-color);
|
||||
border: 0;
|
||||
|
@ -1207,6 +1250,10 @@ a.firstrun-link {
|
|||
background: var(--newtab-element-hover-color);
|
||||
color: var(--newtab-text-primary-color); }
|
||||
|
||||
.contentSearchHeaderRow > td > img, .contentSearchSuggestionRow > td > .historyIcon {
|
||||
margin-inline-start: 7px;
|
||||
margin-inline-end: 15px; }
|
||||
|
||||
.context-menu {
|
||||
background: var(--newtab-contextmenu-background-color);
|
||||
border-radius: 5px;
|
||||
|
@ -1217,7 +1264,7 @@ a.firstrun-link {
|
|||
inset-inline-start: 100%;
|
||||
position: absolute;
|
||||
top: 6.75px;
|
||||
z-index: 10000; }
|
||||
z-index: 8; }
|
||||
.context-menu > ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -55,6 +55,7 @@ body {
|
|||
--newtab-search-border-color: transparent;
|
||||
--newtab-search-dropdown-color: #FFF;
|
||||
--newtab-search-dropdown-header-color: #F9F9FA;
|
||||
--newtab-search-header-background-color: rgba(249, 249, 250, 0.95);
|
||||
--newtab-search-icon-color: rgba(12, 12, 13, 0.4);
|
||||
--newtab-topsites-background-color: #FFF;
|
||||
--newtab-topsites-icon-shadow: inset 0 0 0 1px var(--newtab-inner-box-shadow-color);
|
||||
|
@ -96,6 +97,7 @@ body {
|
|||
--newtab-search-border-color: rgba(249, 249, 250, 0.2);
|
||||
--newtab-search-dropdown-color: #38383D;
|
||||
--newtab-search-dropdown-header-color: #4A4A4F;
|
||||
--newtab-search-header-background-color: rgba(42, 42, 46, 0.95);
|
||||
--newtab-search-icon-color: rgba(249, 249, 250, 0.6);
|
||||
--newtab-topsites-background-color: #38383D;
|
||||
--newtab-topsites-icon-shadow: none;
|
||||
|
@ -552,7 +554,8 @@ main {
|
|||
inset-inline-start: 0;
|
||||
top: 0;
|
||||
width: 100%; }
|
||||
.top-site-outer .default-icon {
|
||||
.top-site-outer .default-icon,
|
||||
.top-site-outer .search-topsite {
|
||||
background-size: 32px;
|
||||
bottom: -6px;
|
||||
height: 42px;
|
||||
|
@ -562,8 +565,17 @@ main {
|
|||
display: flex;
|
||||
font-size: 20px;
|
||||
justify-content: center; }
|
||||
.top-site-outer .default-icon[data-fallback]::before {
|
||||
.top-site-outer .default-icon[data-fallback]::before,
|
||||
.top-site-outer .search-topsite[data-fallback]::before {
|
||||
content: attr(data-fallback); }
|
||||
.top-site-outer .search-topsite {
|
||||
background-image: url("../data/content/assets/glyph-search-16.svg");
|
||||
background-size: 26px;
|
||||
background-color: #0060DF;
|
||||
border-radius: 42px;
|
||||
-moz-context-properties: fill;
|
||||
fill: #FFF;
|
||||
box-shadow: inset 0 0 0 1px var(--newtab-inner-box-shadow-color), var(--newtab-card-shadow); }
|
||||
.top-site-outer .title {
|
||||
color: var(--newtab-topsites-label-color);
|
||||
font: message-box;
|
||||
|
@ -1119,25 +1131,35 @@ a.firstrun-link {
|
|||
display: table; }
|
||||
|
||||
.search-wrapper {
|
||||
cursor: default;
|
||||
display: flex;
|
||||
height: 35px;
|
||||
margin-bottom: 20px;
|
||||
position: relative;
|
||||
width: 100%; }
|
||||
background-color: var(--newtab-background-color);
|
||||
padding: 34px 0 64px; }
|
||||
@media (max-height: 700px) {
|
||||
.search-wrapper {
|
||||
padding: 0 0 30px; } }
|
||||
.search-wrapper .search-inner-wrapper {
|
||||
cursor: default;
|
||||
display: flex;
|
||||
height: 48px;
|
||||
position: relative;
|
||||
width: 100%; }
|
||||
@media (min-width: 866px) {
|
||||
.search-wrapper .search-inner-wrapper {
|
||||
margin: 0 auto;
|
||||
width: 736px; } }
|
||||
.search-wrapper input {
|
||||
background: var(--newtab-textbox-background-color) var(--newtab-search-icon) 8px center/18px no-repeat;
|
||||
background: var(--newtab-textbox-background-color) var(--newtab-search-icon) 12px center no-repeat;
|
||||
background-size: 24px;
|
||||
border: solid 1px var(--newtab-search-border-color);
|
||||
box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.2), 0 0 0 1px rgba(0, 0, 0, 0.15);
|
||||
font-size: 15px;
|
||||
-moz-context-properties: fill;
|
||||
fill: var(--newtab-search-icon-color);
|
||||
padding: 0;
|
||||
padding-inline-end: 36px;
|
||||
padding-inline-start: 34px;
|
||||
padding-inline-end: 48px;
|
||||
padding-inline-start: 46px;
|
||||
width: 100%; }
|
||||
.search-wrapper input:dir(rtl) {
|
||||
background-position-x: right 8px; }
|
||||
background-position-x: right 12px; }
|
||||
.search-wrapper:hover input {
|
||||
box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.2), 0 0 0 1px rgba(0, 0, 0, 0.25); }
|
||||
.search-wrapper:active input,
|
||||
|
@ -1154,7 +1176,7 @@ a.firstrun-link {
|
|||
height: 100%;
|
||||
inset-inline-end: 0;
|
||||
position: absolute;
|
||||
width: 36px; }
|
||||
width: 48px; }
|
||||
.search-wrapper .search-button:focus, .search-wrapper .search-button:hover {
|
||||
background-color: rgba(12, 12, 13, 0.1);
|
||||
cursor: pointer; }
|
||||
|
@ -1163,6 +1185,27 @@ a.firstrun-link {
|
|||
.search-wrapper .search-button:dir(rtl) {
|
||||
transform: scaleX(-1); }
|
||||
|
||||
@media (min-height: 701px) {
|
||||
.fixed-search main {
|
||||
padding-top: 146px; }
|
||||
.fixed-search .search-wrapper {
|
||||
background-color: var(--newtab-search-header-background-color);
|
||||
border-bottom: solid 1px var(--newtab-border-secondary-color);
|
||||
height: 95px;
|
||||
left: 0;
|
||||
padding: 30px 0;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
z-index: 9; }
|
||||
.fixed-search .search-wrapper .search-inner-wrapper {
|
||||
height: 35px; }
|
||||
.fixed-search .search-wrapper input {
|
||||
background-position-x: 16px;
|
||||
background-size: 16px; }
|
||||
.fixed-search .search-wrapper input:dir(rtl) {
|
||||
background-position-x: right 16px; } }
|
||||
|
||||
.contentSearchSuggestionTable {
|
||||
background-color: var(--newtab-search-dropdown-color);
|
||||
border: 0;
|
||||
|
@ -1204,6 +1247,10 @@ a.firstrun-link {
|
|||
background: var(--newtab-element-hover-color);
|
||||
color: var(--newtab-text-primary-color); }
|
||||
|
||||
.contentSearchHeaderRow > td > img, .contentSearchSuggestionRow > td > .historyIcon {
|
||||
margin-inline-start: 7px;
|
||||
margin-inline-end: 15px; }
|
||||
|
||||
.context-menu {
|
||||
background: var(--newtab-contextmenu-background-color);
|
||||
border-radius: 5px;
|
||||
|
@ -1214,7 +1261,7 @@ a.firstrun-link {
|
|||
inset-inline-start: 100%;
|
||||
position: absolute;
|
||||
top: 6.75px;
|
||||
z-index: 10000; }
|
||||
z-index: 8; }
|
||||
.context-menu > ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -205,7 +205,7 @@ const globalImportContext = typeof Window === "undefined" ? BACKGROUND_PROCESS :
|
|||
// }
|
||||
const actionTypes = {};
|
||||
|
||||
for (const type of ["ADDONS_INFO_REQUEST", "ADDONS_INFO_RESPONSE", "ARCHIVE_FROM_POCKET", "AS_ROUTER_TELEMETRY_USER_EVENT", "BLOCK_URL", "BOOKMARK_URL", "COPY_DOWNLOAD_LINK", "DELETE_BOOKMARK_BY_ID", "DELETE_FROM_POCKET", "DELETE_HISTORY_URL", "DIALOG_CANCEL", "DIALOG_OPEN", "DISABLE_ONBOARDING", "DOWNLOAD_CHANGED", "INIT", "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_DOWNLOAD_FILE", "OPEN_LINK", "OPEN_NEW_WINDOW", "OPEN_PRIVATE_WINDOW", "OPEN_WEBEXT_SETTINGS", "PAGE_PRERENDERED", "PLACES_BOOKMARK_ADDED", "PLACES_BOOKMARK_REMOVED", "PLACES_HISTORY_CLEARED", "PLACES_LINKS_CHANGED", "PLACES_LINK_BLOCKED", "PLACES_LINK_DELETED", "PLACES_SAVED_TO_POCKET", "PREFS_INITIAL_VALUES", "PREF_CHANGED", "PREVIEW_REQUEST", "PREVIEW_REQUEST_CANCEL", "PREVIEW_RESPONSE", "REMOVE_DOWNLOAD_FILE", "RICH_ICON_MISSING", "SAVE_SESSION_PERF_DATA", "SAVE_TO_POCKET", "SCREENSHOT_UPDATED", "SECTION_DEREGISTER", "SECTION_DISABLE", "SECTION_ENABLE", "SECTION_MOVE", "SECTION_OPTIONS_CHANGED", "SECTION_REGISTER", "SECTION_UPDATE", "SECTION_UPDATE_CARD", "SETTINGS_CLOSE", "SETTINGS_OPEN", "SET_PREF", "SHOW_DOWNLOAD_FILE", "SHOW_FIREFOX_ACCOUNTS", "SKIPPED_SIGNIN", "SNIPPETS_BLOCKLIST_CLEARED", "SNIPPETS_BLOCKLIST_UPDATED", "SNIPPETS_DATA", "SNIPPETS_RESET", "SNIPPET_BLOCKED", "SUBMIT_EMAIL", "SYSTEM_TICK", "TELEMETRY_IMPRESSION_STATS", "TELEMETRY_PERFORMANCE_EVENT", "TELEMETRY_UNDESIRED_EVENT", "TELEMETRY_USER_EVENT", "TOP_SITES_CANCEL_EDIT", "TOP_SITES_EDIT", "TOP_SITES_INSERT", "TOP_SITES_PIN", "TOP_SITES_PREFS_UPDATED", "TOP_SITES_UNPIN", "TOP_SITES_UPDATED", "TOTAL_BOOKMARKS_REQUEST", "TOTAL_BOOKMARKS_RESPONSE", "UNINIT", "UPDATE_SECTION_PREFS", "WEBEXT_CLICK", "WEBEXT_DISMISS"]) {
|
||||
for (const type of ["ADDONS_INFO_REQUEST", "ADDONS_INFO_RESPONSE", "ARCHIVE_FROM_POCKET", "AS_ROUTER_TELEMETRY_USER_EVENT", "BLOCK_URL", "BOOKMARK_URL", "COPY_DOWNLOAD_LINK", "DELETE_BOOKMARK_BY_ID", "DELETE_FROM_POCKET", "DELETE_HISTORY_URL", "DIALOG_CANCEL", "DIALOG_OPEN", "DISABLE_ONBOARDING", "DOWNLOAD_CHANGED", "FILL_SEARCH_TERM", "INIT", "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_DOWNLOAD_FILE", "OPEN_LINK", "OPEN_NEW_WINDOW", "OPEN_PRIVATE_WINDOW", "OPEN_WEBEXT_SETTINGS", "PAGE_PRERENDERED", "PLACES_BOOKMARK_ADDED", "PLACES_BOOKMARK_REMOVED", "PLACES_HISTORY_CLEARED", "PLACES_LINKS_CHANGED", "PLACES_LINK_BLOCKED", "PLACES_LINK_DELETED", "PLACES_SAVED_TO_POCKET", "PREFS_INITIAL_VALUES", "PREF_CHANGED", "PREVIEW_REQUEST", "PREVIEW_REQUEST_CANCEL", "PREVIEW_RESPONSE", "REMOVE_DOWNLOAD_FILE", "RICH_ICON_MISSING", "SAVE_SESSION_PERF_DATA", "SAVE_TO_POCKET", "SCREENSHOT_UPDATED", "SECTION_DEREGISTER", "SECTION_DISABLE", "SECTION_ENABLE", "SECTION_MOVE", "SECTION_OPTIONS_CHANGED", "SECTION_REGISTER", "SECTION_UPDATE", "SECTION_UPDATE_CARD", "SETTINGS_CLOSE", "SETTINGS_OPEN", "SET_PREF", "SHOW_DOWNLOAD_FILE", "SHOW_FIREFOX_ACCOUNTS", "SKIPPED_SIGNIN", "SNIPPETS_BLOCKLIST_CLEARED", "SNIPPETS_BLOCKLIST_UPDATED", "SNIPPETS_DATA", "SNIPPETS_RESET", "SNIPPET_BLOCKED", "SUBMIT_EMAIL", "SYSTEM_TICK", "TELEMETRY_IMPRESSION_STATS", "TELEMETRY_PERFORMANCE_EVENT", "TELEMETRY_UNDESIRED_EVENT", "TELEMETRY_USER_EVENT", "TOP_SITES_CANCEL_EDIT", "TOP_SITES_EDIT", "TOP_SITES_INSERT", "TOP_SITES_PIN", "TOP_SITES_PREFS_UPDATED", "TOP_SITES_UNPIN", "TOP_SITES_UPDATED", "TOTAL_BOOKMARKS_REQUEST", "TOTAL_BOOKMARKS_RESPONSE", "UNINIT", "UPDATE_SECTION_PREFS", "WEBEXT_CLICK", "WEBEXT_DISMISS"]) {
|
||||
actionTypes[type] = type;
|
||||
}
|
||||
|
||||
|
@ -1538,6 +1538,24 @@ function addLocaleDataForReactIntl(locale) {
|
|||
Object(react_intl__WEBPACK_IMPORTED_MODULE_1__["addLocaleData"])([{ locale, parentLocale: "en" }]);
|
||||
}
|
||||
|
||||
// Returns a function will not be continuously triggered when called. The
|
||||
// function will be triggered if called again after `wait` milliseconds.
|
||||
function debounce(func, wait) {
|
||||
let timer;
|
||||
return (...args) => {
|
||||
if (timer) {
|
||||
return;
|
||||
}
|
||||
|
||||
let wakeUp = () => {
|
||||
timer = null;
|
||||
};
|
||||
|
||||
timer = setTimeout(wakeUp, wait);
|
||||
func.apply(this, args);
|
||||
};
|
||||
}
|
||||
|
||||
class _Base extends react__WEBPACK_IMPORTED_MODULE_8___default.a.PureComponent {
|
||||
componentWillMount() {
|
||||
const { App, locale } = this.props;
|
||||
|
@ -1562,9 +1580,29 @@ class _Base extends react__WEBPACK_IMPORTED_MODULE_8___default.a.PureComponent {
|
|||
this.updateTheme();
|
||||
}
|
||||
|
||||
componentWillUpdate({ App }) {
|
||||
hasTopStoriesSectionChanged(nextProps) {
|
||||
const nPropsSections = nextProps.Sections.find(section => section.id === "topstories");
|
||||
const tPropsSections = this.props.Sections.find(section => section.id === "topstories");
|
||||
if (nPropsSections && nPropsSections.options) {
|
||||
if (!tPropsSections || !tPropsSections.options) {
|
||||
return true;
|
||||
}
|
||||
if (nPropsSections.options.show_spocs !== tPropsSections.options.show_spocs) {
|
||||
return true;
|
||||
}
|
||||
if (nPropsSections.options.stories_endpoint !== tPropsSections.options.stories_endpoint) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
componentWillUpdate(nextProps) {
|
||||
this.updateTheme();
|
||||
this.sendNewTabRehydrated(App);
|
||||
if (this.hasTopStoriesSectionChanged(nextProps)) {
|
||||
this.renderNotified = false;
|
||||
}
|
||||
this.sendNewTabRehydrated(nextProps.App);
|
||||
}
|
||||
|
||||
updateTheme() {
|
||||
|
@ -1621,6 +1659,25 @@ class BaseContent extends react__WEBPACK_IMPORTED_MODULE_8___default.a.PureCompo
|
|||
constructor(props) {
|
||||
super(props);
|
||||
this.openPreferences = this.openPreferences.bind(this);
|
||||
this.onWindowScroll = debounce(this.onWindowScroll.bind(this), 5);
|
||||
this.state = { fixedSearch: false };
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
global.addEventListener("scroll", this.onWindowScroll);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
global.removeEventListener("scroll", this.onWindowScroll);
|
||||
}
|
||||
|
||||
onWindowScroll() {
|
||||
const SCROLL_THRESHOLD = 34;
|
||||
if (global.scrollY > SCROLL_THRESHOLD && !this.state.fixedSearch) {
|
||||
this.setState({ fixedSearch: true });
|
||||
} else if (global.scrollY <= SCROLL_THRESHOLD && this.state.fixedSearch) {
|
||||
this.setState({ fixedSearch: false });
|
||||
}
|
||||
}
|
||||
|
||||
openPreferences() {
|
||||
|
@ -1636,7 +1693,7 @@ class BaseContent extends react__WEBPACK_IMPORTED_MODULE_8___default.a.PureCompo
|
|||
|
||||
const shouldBeFixedToTop = common_PrerenderData_jsm__WEBPACK_IMPORTED_MODULE_7__["PrerenderData"].arePrefsValid(name => prefs[name]);
|
||||
|
||||
const outerClassName = ["outer-wrapper", shouldBeFixedToTop && "fixed-to-top"].filter(v => v).join(" ");
|
||||
const outerClassName = ["outer-wrapper", shouldBeFixedToTop && "fixed-to-top", prefs.showSearch && this.state.fixedSearch && "fixed-search"].filter(v => v).join(" ");
|
||||
|
||||
return react__WEBPACK_IMPORTED_MODULE_8___default.a.createElement(
|
||||
"div",
|
||||
|
@ -1675,7 +1732,7 @@ class BaseContent extends react__WEBPACK_IMPORTED_MODULE_8___default.a.PureCompo
|
|||
}
|
||||
}
|
||||
|
||||
const Base = Object(react_redux__WEBPACK_IMPORTED_MODULE_4__["connect"])(state => ({ App: state.App, Prefs: state.Prefs }))(_Base);
|
||||
const Base = Object(react_redux__WEBPACK_IMPORTED_MODULE_4__["connect"])(state => ({ App: state.App, Prefs: state.Prefs, Sections: state.Sections }))(_Base);
|
||||
/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(1)))
|
||||
|
||||
/***/ }),
|
||||
|
@ -2344,32 +2401,36 @@ class _Search extends react__WEBPACK_IMPORTED_MODULE_4___default.a.PureComponent
|
|||
"div",
|
||||
{ className: "search-wrapper" },
|
||||
react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement(
|
||||
"label",
|
||||
{ htmlFor: "newtab-search-text", className: "search-label" },
|
||||
"div",
|
||||
{ className: "search-inner-wrapper" },
|
||||
react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement(
|
||||
"span",
|
||||
{ className: "sr-only" },
|
||||
react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement(react_intl__WEBPACK_IMPORTED_MODULE_0__["FormattedMessage"], { id: "search_web_placeholder" })
|
||||
)
|
||||
),
|
||||
react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement("input", {
|
||||
id: "newtab-search-text",
|
||||
maxLength: "256",
|
||||
placeholder: this.props.intl.formatMessage({ id: "search_web_placeholder" }),
|
||||
ref: this.onInputMount,
|
||||
title: this.props.intl.formatMessage({ id: "search_web_placeholder" }),
|
||||
type: "search" }),
|
||||
react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement(
|
||||
"button",
|
||||
{
|
||||
id: "searchSubmit",
|
||||
className: "search-button",
|
||||
onClick: this.onClick,
|
||||
title: this.props.intl.formatMessage({ id: "search_button" }) },
|
||||
"label",
|
||||
{ htmlFor: "newtab-search-text", className: "search-label" },
|
||||
react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement(
|
||||
"span",
|
||||
{ className: "sr-only" },
|
||||
react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement(react_intl__WEBPACK_IMPORTED_MODULE_0__["FormattedMessage"], { id: "search_web_placeholder" })
|
||||
)
|
||||
),
|
||||
react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement("input", {
|
||||
id: "newtab-search-text",
|
||||
maxLength: "256",
|
||||
placeholder: this.props.intl.formatMessage({ id: "search_web_placeholder" }),
|
||||
ref: this.onInputMount,
|
||||
title: this.props.intl.formatMessage({ id: "search_web_placeholder" }),
|
||||
type: "search" }),
|
||||
react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement(
|
||||
"span",
|
||||
{ className: "sr-only" },
|
||||
react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement(react_intl__WEBPACK_IMPORTED_MODULE_0__["FormattedMessage"], { id: "search_button" })
|
||||
"button",
|
||||
{
|
||||
id: "searchSubmit",
|
||||
className: "search-button",
|
||||
onClick: this.onClick,
|
||||
title: this.props.intl.formatMessage({ id: "search_button" }) },
|
||||
react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement(
|
||||
"span",
|
||||
{ className: "sr-only" },
|
||||
react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement(react_intl__WEBPACK_IMPORTED_MODULE_0__["FormattedMessage"], { id: "search_button" })
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
@ -2830,12 +2891,17 @@ const LinkMenuOptions = {
|
|||
data: { url: site.url }
|
||||
})
|
||||
}),
|
||||
PinTopSite: (site, index) => ({
|
||||
PinTopSite: ({ url, searchTopSite, label }, index) => ({
|
||||
id: "menu_action_pin",
|
||||
icon: "pin",
|
||||
action: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].AlsoToMain({
|
||||
type: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].TOP_SITES_PIN,
|
||||
data: { site: { url: site.url }, index }
|
||||
data: {
|
||||
site: Object.assign({
|
||||
url
|
||||
}, searchTopSite && { searchTopSite, label }),
|
||||
index
|
||||
}
|
||||
}),
|
||||
userEvent: "PIN"
|
||||
}),
|
||||
|
@ -2927,7 +2993,7 @@ class _LinkMenu extends react__WEBPACK_IMPORTED_MODULE_5___default.a.PureCompone
|
|||
const { site, index, source, isPrivateBrowsingEnabled, siteInfo, platform } = props;
|
||||
|
||||
// Handle special case of default site
|
||||
const propOptions = !site.isDefault ? props.options : DEFAULT_SITE_MENU_OPTIONS;
|
||||
const propOptions = !site.isDefault || site.searchTopSite ? props.options : DEFAULT_SITE_MENU_OPTIONS;
|
||||
|
||||
const options = propOptions.map(o => content_src_lib_link_menu_options__WEBPACK_IMPORTED_MODULE_4__["LinkMenuOptions"][o](site, index, source, isPrivateBrowsingEnabled, siteInfo, platform)).map(option => {
|
||||
const { action, impression, id, string_id, type, userEvent } = option;
|
||||
|
@ -4087,10 +4153,13 @@ const TopSites = Object(react_redux__WEBPACK_IMPORTED_MODULE_4__["connect"])(sta
|
|||
__webpack_require__.r(__webpack_exports__);
|
||||
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TOP_SITES_SOURCE", function() { return TOP_SITES_SOURCE; });
|
||||
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TOP_SITES_CONTEXT_MENU_OPTIONS", function() { return TOP_SITES_CONTEXT_MENU_OPTIONS; });
|
||||
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TOP_SITES_SEARCH_SHORTCUTS_CONTEXT_MENU_OPTIONS", function() { return TOP_SITES_SEARCH_SHORTCUTS_CONTEXT_MENU_OPTIONS; });
|
||||
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MIN_RICH_FAVICON_SIZE", function() { return MIN_RICH_FAVICON_SIZE; });
|
||||
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MIN_CORNER_FAVICON_SIZE", function() { return MIN_CORNER_FAVICON_SIZE; });
|
||||
const TOP_SITES_SOURCE = "TOP_SITES";
|
||||
const TOP_SITES_CONTEXT_MENU_OPTIONS = ["CheckPinTopSite", "EditTopSite", "Separator", "OpenInNewWindow", "OpenInPrivateWindow", "Separator", "BlockUrl", "DeleteUrl"];
|
||||
// the special top site for search shortcut experiment can only have the option to unpin (which removes) the topsite
|
||||
const TOP_SITES_SEARCH_SHORTCUTS_CONTEXT_MENU_OPTIONS = ["CheckPinTopSite", "Separator", "BlockUrl"];
|
||||
// minimum size necessary to show a rich icon instead of a screenshot
|
||||
const MIN_RICH_FAVICON_SIZE = 96;
|
||||
// minimum size necessary to show any icon in the top left corner with a screenshot
|
||||
|
@ -4236,6 +4305,13 @@ class TopSiteLink extends react__WEBPACK_IMPORTED_MODULE_4___default.a.PureCompo
|
|||
if (defaultStyle) {
|
||||
// force no styles (letter fallback) even if the link has imagery
|
||||
smallFaviconFallback = false;
|
||||
} else if (link.searchTopSite) {
|
||||
imageClassName = "top-site-icon rich-icon";
|
||||
imageStyle = {
|
||||
backgroundColor: link.backgroundColor,
|
||||
backgroundImage: `url(${tippyTopIcon})`
|
||||
};
|
||||
smallFaviconStyle = { backgroundImage: `url(${tippyTopIcon})` };
|
||||
} else if (link.customScreenshotURL) {
|
||||
// assume high quality custom screenshot and use rich icon styles and class names
|
||||
imageClassName = "top-site-icon rich-icon";
|
||||
|
@ -4288,6 +4364,7 @@ class TopSiteLink extends react__WEBPACK_IMPORTED_MODULE_4___default.a.PureCompo
|
|||
"div",
|
||||
{ className: "tile", "aria-hidden": true, "data-fallback": letterFallback },
|
||||
react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement("div", { className: imageClassName, style: imageStyle }),
|
||||
link.searchTopSite && react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement("div", { className: "top-site-icon search-topsite" }),
|
||||
showSmallFavicon && react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement("div", {
|
||||
className: "top-site-icon default-icon",
|
||||
"data-fallback": smallFaviconFallback && letterFallback,
|
||||
|
@ -4333,6 +4410,11 @@ class TopSite extends react__WEBPACK_IMPORTED_MODULE_4___default.a.PureComponent
|
|||
if (this.props.link.isPinned) {
|
||||
value.card_type = "pinned";
|
||||
}
|
||||
if (this.props.link.searchTopSite) {
|
||||
// Set the card_type as "search" regardless of its pinning status
|
||||
value.card_type = "search";
|
||||
value.search_vendor = this.props.link.hostname;
|
||||
}
|
||||
return { value };
|
||||
}
|
||||
|
||||
|
@ -4351,10 +4433,17 @@ class TopSite extends react__WEBPACK_IMPORTED_MODULE_4___default.a.PureComponent
|
|||
// specified as a property on the link.
|
||||
event.preventDefault();
|
||||
const { altKey, button, ctrlKey, metaKey, shiftKey } = event;
|
||||
this.props.dispatch(common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].OnlyToMain({
|
||||
type: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].OPEN_LINK,
|
||||
data: Object.assign(this.props.link, { event: { altKey, button, ctrlKey, metaKey, shiftKey } })
|
||||
}));
|
||||
if (!this.props.link.searchTopSite) {
|
||||
this.props.dispatch(common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].OnlyToMain({
|
||||
type: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].OPEN_LINK,
|
||||
data: Object.assign(this.props.link, { event: { altKey, button, ctrlKey, metaKey, shiftKey } })
|
||||
}));
|
||||
} else {
|
||||
this.props.dispatch(common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].OnlyToMain({
|
||||
type: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].FILL_SEARCH_TERM,
|
||||
data: { label: this.props.link.label }
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
onMenuButtonClick(event) {
|
||||
|
@ -4391,7 +4480,7 @@ class TopSite extends react__WEBPACK_IMPORTED_MODULE_4___default.a.PureComponent
|
|||
dispatch: props.dispatch,
|
||||
index: props.index,
|
||||
onUpdate: this.onMenuUpdate,
|
||||
options: _TopSitesConstants__WEBPACK_IMPORTED_MODULE_2__["TOP_SITES_CONTEXT_MENU_OPTIONS"],
|
||||
options: link.searchTopSite ? _TopSitesConstants__WEBPACK_IMPORTED_MODULE_2__["TOP_SITES_SEARCH_SHORTCUTS_CONTEXT_MENU_OPTIONS"] : _TopSitesConstants__WEBPACK_IMPORTED_MODULE_2__["TOP_SITES_CONTEXT_MENU_OPTIONS"],
|
||||
site: link,
|
||||
siteInfo: this._getTelemetryInfo(),
|
||||
source: _TopSitesConstants__WEBPACK_IMPORTED_MODULE_2__["TOP_SITES_SOURCE"] })
|
||||
|
@ -4493,11 +4582,11 @@ class _TopSiteList extends react__WEBPACK_IMPORTED_MODULE_4___default.a.PureComp
|
|||
this.props.dispatch(common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].AlsoToMain({
|
||||
type: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].TOP_SITES_INSERT,
|
||||
data: {
|
||||
site: {
|
||||
site: Object.assign({
|
||||
url: this.state.draggedSite.url,
|
||||
label: this.state.draggedTitle,
|
||||
customScreenshotURL: this.state.draggedSite.customScreenshotURL
|
||||
},
|
||||
}, this.state.draggedSite.searchTopSite && { searchTopSite: true }),
|
||||
index,
|
||||
draggedFromIndex: this.state.draggedIndex
|
||||
}
|
||||
|
@ -4653,7 +4742,8 @@ class _StartupOverlay extends react__WEBPACK_IMPORTED_MODULE_3___default.a.PureC
|
|||
if (_this.props.fxa_endpoint && !_this.didFetch) {
|
||||
try {
|
||||
_this.didFetch = true;
|
||||
const response = yield fetch(`${_this.props.fxa_endpoint}/metrics-flow`);
|
||||
const response = yield fetch(`${_this.props.fxa_endpoint}/metrics-flow?entrypoint=
|
||||
activity-stream-firstrun&utm_source=activity-stream&utm_campaign=firstrun&form_type=email`);
|
||||
if (response.status === 200) {
|
||||
const { flowId, flowBeginTime } = yield response.json();
|
||||
_this.setState({ flowId, flowBeginTime });
|
||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="context-fill" d="M10 0a5.991 5.991 0 0 0-4.885 9.471L.293 14.293a1 1 0 1 0 1.414 1.414l4.822-4.822A6 6 0 1 0 10 0zm0 10a4 4 0 1 1 4-4 4 4 0 0 1-4 4z"/></svg>
|
После Ширина: | Высота: | Размер: 232 B |
Двоичные данные
browser/components/newtab/data/content/tippytop/images/google-com@2x.png
Normal file
Двоичные данные
browser/components/newtab/data/content/tippytop/images/google-com@2x.png
Normal file
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 3.0 KiB |
|
@ -34,6 +34,11 @@
|
|||
"url": "https://www.facebook.com/",
|
||||
"image_url": "facebook-com@2x.png"
|
||||
},
|
||||
{
|
||||
"title": "google",
|
||||
"url": "https://www.google.com/",
|
||||
"image_url": "google-com@2x.png"
|
||||
},
|
||||
{
|
||||
"title": "leboncoin",
|
||||
"url": "http://www.leboncoin.fr/",
|
||||
|
|
|
@ -183,7 +183,8 @@ Schema definitions/validations that can be used for tests can be found in `syste
|
|||
| `action` | [Required] Either `activity_stream_event`, `activity_stream_session`, or `activity_stream_performance`. | :one:
|
||||
| `addon_version` | [Required] Firefox build ID, i.e. `Services.appinfo.appBuildID`. | :one:
|
||||
| `client_id` | [Required] An identifier for this client. | :one:
|
||||
| `card_type` | [Optional] ("bookmark", "pocket", "trending", "pinned") | :one:
|
||||
| `card_type` | [Optional] ("bookmark", "pocket", "trending", "pinned", "search") | :one:
|
||||
| `search_vendor` | [Optional] the vendor of the search shortcut, one of ("google", "amazon", "wikipedia", "duckduckgo", "bing", etc.). This field only exists when `card_type = "search"` | :one:
|
||||
| `date` | [Auto populated by Onyx] The date in YYYY-MM-DD format. | :three:
|
||||
| `experiment_id` | [Optional] The unique identifier for a specific experiment. | :one:
|
||||
| `event_id` | [Required] An identifier shared by multiple performance pings that describe ane entire request flow. | :one:
|
||||
|
|
|
@ -90,8 +90,54 @@ A user event ping includes some basic metadata (tab id, addon version, etc.) as
|
|||
"source": "TOP_SITES",
|
||||
"action_position": 2,
|
||||
"value": {
|
||||
"card_type": "pinned",
|
||||
"icon_type": ["screenshot_with_icon" | "screenshot" | "tippytop" | "rich_icon" | "no_image"]
|
||||
"card_type": ["pinned" | "search"],
|
||||
"icon_type": ["screenshot_with_icon" | "screenshot" | "tippytop" | "rich_icon" | "no_image"],
|
||||
// only exists if its card_type = "search"
|
||||
"search_vendor": "google"
|
||||
}
|
||||
|
||||
// Basic metadata
|
||||
"action": "activity_stream_event",
|
||||
"page": ["about:newtab" | "about:home" | "about:welcome" | "unknown"],
|
||||
"client_id": "26288a14-5cc4-d14f-ae0a-bb01ef45be9c",
|
||||
"session_id": "005deed0-e3e4-4c02-a041-17405fd703f6",
|
||||
"addon_version": "20180710100040",
|
||||
"locale": "en-US",
|
||||
"user_prefs": 7
|
||||
}
|
||||
```
|
||||
|
||||
#### Adding a search shortcut
|
||||
```js
|
||||
{
|
||||
"event": "ADD_SEARCH_SHORTCUT",
|
||||
"source": "TOP_SITES",
|
||||
"action_position": 2,
|
||||
"value": {
|
||||
"card_type": "search",
|
||||
"search_vendor": "google"
|
||||
}
|
||||
|
||||
// Basic metadata
|
||||
"action": "activity_stream_event",
|
||||
"page": ["about:newtab" | "about:home" | "about:welcome" | "unknown"],
|
||||
"client_id": "26288a14-5cc4-d14f-ae0a-bb01ef45be9c",
|
||||
"session_id": "005deed0-e3e4-4c02-a041-17405fd703f6",
|
||||
"addon_version": "20180710100040",
|
||||
"locale": "en-US",
|
||||
"user_prefs": 7
|
||||
}
|
||||
```
|
||||
|
||||
#### Editing a search shortcut
|
||||
```js
|
||||
{
|
||||
"event": "EDIT_SEARCH_SHORTCUT",
|
||||
"source": "TOP_SITES",
|
||||
"action_position": 2,
|
||||
"value": {
|
||||
"card_type": "search",
|
||||
"search_vendor": "google"
|
||||
}
|
||||
|
||||
// Basic metadata
|
||||
|
@ -136,8 +182,10 @@ A user event ping includes some basic metadata (tab id, addon version, etc.) as
|
|||
"source": "TOP_SITES",
|
||||
"action_position": 2,
|
||||
"value": {
|
||||
"card_type": "pinned",
|
||||
"icon_type": ["screenshot_with_icon" | "screenshot" | "tippytop" | "rich_icon" | "no_image"]
|
||||
"card_type": ["pinned" | "search"],
|
||||
"icon_type": ["screenshot_with_icon" | "screenshot" | "tippytop" | "rich_icon" | "no_image"],
|
||||
// only exists if its card_type = "search"
|
||||
"search_vendor": "google"
|
||||
}
|
||||
|
||||
// Basic metadata
|
||||
|
|
|
@ -27,6 +27,7 @@ const DEFAULT_WHITELIST_HOSTS = {
|
|||
const SNIPPETS_ENDPOINT_WHITELIST = "browser.newtab.activity-stream.asrouter.whitelistHosts";
|
||||
|
||||
const LOCAL_MESSAGE_PROVIDERS = {OnboardingMessageProvider};
|
||||
const STARTPAGE_VERSION = "0.1.0";
|
||||
|
||||
const MessageLoaderUtils = {
|
||||
/**
|
||||
|
@ -180,6 +181,10 @@ class _ASRouter {
|
|||
const localProvider = this._localProviders[provider.localProvider];
|
||||
provider.messages = localProvider ? localProvider.getMessages() : [];
|
||||
}
|
||||
if (provider.type === "remote" && provider.url) {
|
||||
provider.url = provider.url.replace(/%STARTPAGE_VERSION%/g, STARTPAGE_VERSION);
|
||||
provider.url = Services.urlFormatter.formatURL(provider.url);
|
||||
}
|
||||
// Reset provider update timestamp to force message refresh
|
||||
provider.lastUpdated = undefined;
|
||||
});
|
||||
|
|
|
@ -163,6 +163,31 @@ const PREFS_CONFIG = new Map([
|
|||
title: "Experiment to show special top sites that perform keyword searches",
|
||||
value: true
|
||||
}],
|
||||
["improvesearch.topSiteSearchShortcuts.searchEngines", {
|
||||
title: "An ordered, comma-delimited list of search shortcuts that we should try and pin",
|
||||
// This pref is dynamic as the shortcuts vary depending on the region
|
||||
getValue: ({geo}) => {
|
||||
if (!geo) {
|
||||
return "";
|
||||
}
|
||||
const searchShortcuts = [];
|
||||
if (geo === "CN") {
|
||||
searchShortcuts.push("baidu");
|
||||
} else if (["BY", "KZ", "RU", "TR"].includes(geo)) {
|
||||
searchShortcuts.push("yandex");
|
||||
} else {
|
||||
searchShortcuts.push("google");
|
||||
}
|
||||
// Always include Amazon - we will only be able to actually pin it if it
|
||||
// is available as a default search engine in that region
|
||||
searchShortcuts.push("amazon");
|
||||
return searchShortcuts.join(",");
|
||||
}
|
||||
}],
|
||||
["improvesearch.topSiteSearchShortcuts.havePinned", {
|
||||
title: "A comma-delimited list of search shortcuts that have previously been pinned",
|
||||
value: ""
|
||||
}],
|
||||
["asrouterExperimentEnabled", {
|
||||
title: "Is the message center experiment on?",
|
||||
value: false
|
||||
|
|
|
@ -278,6 +278,10 @@ class PlacesFeed {
|
|||
}
|
||||
}
|
||||
|
||||
fillSearchTopSiteTerm({_target, data}) {
|
||||
_target.browser.ownerGlobal.gURLBar.search(`${data.label} `, {disableOneOffButtons: true, disableSearchSuggestionsNotification: true});
|
||||
}
|
||||
|
||||
onAction(action) {
|
||||
switch (action.type) {
|
||||
case at.INIT:
|
||||
|
@ -315,6 +319,9 @@ class PlacesFeed {
|
|||
case at.SAVE_TO_POCKET:
|
||||
this.saveToPocket(action.data.site, action._target.browser);
|
||||
break;
|
||||
case at.FILL_SEARCH_TERM:
|
||||
this.fillSearchTopSiteTerm(action);
|
||||
break;
|
||||
case at.OPEN_LINK: {
|
||||
this.openLink(action);
|
||||
break;
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/* 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";
|
||||
|
||||
// List of sites we match against Topsites in order to identify sites
|
||||
// that should be converted to search Topsites
|
||||
const SEARCH_SHORTCUTS = [
|
||||
{keyword: "@google", shortURL: "google", url: "https://google.com", searchIdentifier: /^google/},
|
||||
{keyword: "@baidu", shortURL: "baidu", url: "https://baidu.com", searchIdentifier: /^baidu/},
|
||||
{keyword: "@yandex", shortURL: "yandex", url: "https://yandex.com", searchIdentifier: /^yandex/},
|
||||
{keyword: "@amazon", shortURL: "amazon", url: "https://amazon.com", searchIdentifier: /^amazon/}
|
||||
];
|
||||
this.SEARCH_SHORTCUTS = SEARCH_SHORTCUTS;
|
||||
|
||||
// Note: you must add the activity stream branch to the beginning of this if using outside activity stream
|
||||
this.SEARCH_SHORTCUTS_EXPERIMENT = "improvesearch.topSiteSearchShortcuts";
|
||||
this.SEARCH_SHORTCUTS_SEARCH_ENGINES_PREF = "improvesearch.topSiteSearchShortcuts.searchEngines";
|
||||
this.SEARCH_SHORTCUTS_HAVE_PINNED_PREF = "improvesearch.topSiteSearchShortcuts.havePinned";
|
||||
|
||||
function getSearchProvider(candidateShortURL) {
|
||||
return SEARCH_SHORTCUTS.filter(match => candidateShortURL === match.shortURL)[0] || null;
|
||||
}
|
||||
this.getSearchProvider = getSearchProvider;
|
||||
|
||||
const EXPORTED_SYMBOLS = ["getSearchProvider", "SEARCH_SHORTCUTS", "SEARCH_SHORTCUTS_EXPERIMENT",
|
||||
"SEARCH_SHORTCUTS_SEARCH_ENGINES_PREF", "SEARCH_SHORTCUTS_HAVE_PINNED_PREF"];
|
|
@ -422,7 +422,7 @@ class SectionsFeed {
|
|||
this.store.dispatch(ac.SetPref("sectionOrder", orderedSections.join(",")));
|
||||
}
|
||||
|
||||
onAction(action) {
|
||||
async onAction(action) {
|
||||
switch (action.type) {
|
||||
case at.INIT:
|
||||
SectionsManager.onceInitialized(this.init);
|
||||
|
@ -435,7 +435,7 @@ class SectionsFeed {
|
|||
if (action.data) {
|
||||
const matched = action.data.name.match(/^(feeds.section.(\S+)).options$/i);
|
||||
if (matched) {
|
||||
SectionsManager.addBuiltInSection(matched[1], action.data.value);
|
||||
await SectionsManager.addBuiltInSection(matched[1], action.data.value);
|
||||
this.store.dispatch({type: at.SECTION_OPTIONS_CHANGED, data: matched[2]});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,12 @@ const {insertPinned, TOP_SITES_MAX_SITES_PER_ROW} = ChromeUtils.import("resource
|
|||
const {Dedupe} = ChromeUtils.import("resource://activity-stream/common/Dedupe.jsm", {});
|
||||
const {shortURL} = ChromeUtils.import("resource://activity-stream/lib/ShortURL.jsm", {});
|
||||
const {getDefaultOptions} = ChromeUtils.import("resource://activity-stream/lib/ActivityStreamStorage.jsm", {});
|
||||
const {
|
||||
SEARCH_SHORTCUTS_EXPERIMENT,
|
||||
SEARCH_SHORTCUTS_SEARCH_ENGINES_PREF,
|
||||
SEARCH_SHORTCUTS_HAVE_PINNED_PREF,
|
||||
getSearchProvider
|
||||
} = ChromeUtils.import("resource://activity-stream/lib/SearchShortcuts.jsm", {});
|
||||
|
||||
ChromeUtils.defineModuleGetter(this, "filterAdult",
|
||||
"resource://activity-stream/lib/FilterAdult.jsm");
|
||||
|
@ -128,14 +134,83 @@ this.TopSitesFeed = class TopSitesFeed {
|
|||
if (!this.store.getState().Prefs.values[NO_DEFAULT_SEARCH_TILE_EXP_PREF]) {
|
||||
return false;
|
||||
}
|
||||
// If TopSite Search Shortcuts is enabled we don't want to filter those sites out
|
||||
if (this.store.getState().Prefs.values[SEARCH_SHORTCUTS_EXPERIMENT] && getSearchProvider(hostname)) {
|
||||
return false;
|
||||
}
|
||||
if (SEARCH_FILTERS.includes(hostname) || hostname === this._currentSearchHostname) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* _maybeInsertSearchShortcuts - if the search shortcuts experiment is running,
|
||||
* insert search shortcuts if needed
|
||||
* @param {Array} plainPinnedSites (from the pinnedSitesCache)
|
||||
* @returns {Boolean} Did we insert any search shortcuts?
|
||||
*/
|
||||
async _maybeInsertSearchShortcuts(plainPinnedSites) {
|
||||
// Only insert shortcuts if the experiment is running
|
||||
if (this.store.getState().Prefs.values[SEARCH_SHORTCUTS_EXPERIMENT]) {
|
||||
// We don't want to insert shortcuts we've previously inserted
|
||||
const prevInsertedShortcuts = this.store.getState().Prefs.values[SEARCH_SHORTCUTS_HAVE_PINNED_PREF]
|
||||
.split(",").filter(s => s); // Filter out empty strings
|
||||
const newInsertedShortcuts = [];
|
||||
|
||||
const shouldPin = this.store.getState().Prefs.values[SEARCH_SHORTCUTS_SEARCH_ENGINES_PREF]
|
||||
.split(",")
|
||||
.map(getSearchProvider)
|
||||
.filter(s => s);
|
||||
|
||||
// If we've previously inserted all search shortcuts return early
|
||||
if (shouldPin.every(shortcut => prevInsertedShortcuts.includes(shortcut.shortURL))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const numberOfSlots = this.store.getState().Prefs.values[ROWS_PREF] * TOP_SITES_MAX_SITES_PER_ROW;
|
||||
|
||||
// The plainPinnedSites array is populated with pinned sites at their
|
||||
// respective indices, and null everywhere else, but is not always the
|
||||
// right length
|
||||
const pinnedSites = [...plainPinnedSites].concat(
|
||||
Array(numberOfSlots - plainPinnedSites.length).fill(null)
|
||||
);
|
||||
|
||||
await new Promise(resolve => Services.search.init(resolve));
|
||||
|
||||
const tryToInsertSearchShortcut = shortcut => {
|
||||
const nextAvailable = pinnedSites.indexOf(null);
|
||||
// Only add a search shortcut if the site isn't already pinned, we
|
||||
// haven't previously inserted it, there's space to pin it, and the
|
||||
// search engine is available in Firefox
|
||||
if (
|
||||
!pinnedSites.find(s => s && s.hostname === shortcut.shortURL) &&
|
||||
!prevInsertedShortcuts.includes(shortcut.shortURL) &&
|
||||
nextAvailable > -1 &&
|
||||
Services.search.getEngines().find(e => e.identifier.match(shortcut.searchIdentifier))
|
||||
) {
|
||||
const site = this.topSiteToSearchTopSite({url: shortcut.url});
|
||||
this._pinSiteAt(site, nextAvailable);
|
||||
pinnedSites[nextAvailable] = site;
|
||||
newInsertedShortcuts.push(shortcut.shortURL);
|
||||
}
|
||||
};
|
||||
|
||||
shouldPin.forEach(shortcut => tryToInsertSearchShortcut(shortcut));
|
||||
|
||||
if (newInsertedShortcuts.length) {
|
||||
this.store.dispatch(ac.SetPref(SEARCH_SHORTCUTS_HAVE_PINNED_PREF, prevInsertedShortcuts.concat(newInsertedShortcuts).join(",")));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
async getLinksWithDefaults() {
|
||||
const numItems = this.store.getState().Prefs.values[ROWS_PREF] * TOP_SITES_MAX_SITES_PER_ROW;
|
||||
const searchShortcutsExperiment = this.store.getState().Prefs.values[SEARCH_SHORTCUTS_EXPERIMENT];
|
||||
|
||||
// Get all frecent sites from history
|
||||
const frecent = (await this.frecentCache.request({
|
||||
|
@ -145,24 +220,44 @@ this.TopSitesFeed = class TopSitesFeed {
|
|||
.reduce((validLinks, link) => {
|
||||
const hostname = shortURL(link);
|
||||
if (!this.isExperimentOnAndLinkFilteredSearch(hostname)) {
|
||||
validLinks.push({...link, hostname});
|
||||
validLinks.push({
|
||||
...(searchShortcutsExperiment ? this.topSiteToSearchTopSite(link) : link),
|
||||
hostname
|
||||
});
|
||||
}
|
||||
return validLinks;
|
||||
}, []);
|
||||
|
||||
// Remove any defaults that have been blocked
|
||||
const notBlockedDefaultSites = DEFAULT_TOP_SITES
|
||||
.filter(link => {
|
||||
.reduce((topsites, link) => {
|
||||
const searchProvider = getSearchProvider(shortURL(link));
|
||||
if (NewTabUtils.blockedLinks.isBlocked({url: link.url})) {
|
||||
return false;
|
||||
return topsites;
|
||||
} else if (this.isExperimentOnAndLinkFilteredSearch(link.hostname)) {
|
||||
return false;
|
||||
return topsites;
|
||||
// If we've previously blocked a search shortcut, remove the default top site
|
||||
// that matches the hostname
|
||||
} else if (searchProvider && NewTabUtils.blockedLinks.isBlocked({url: searchProvider.url})) {
|
||||
return topsites;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return [
|
||||
...topsites,
|
||||
searchShortcutsExperiment ? this.topSiteToSearchTopSite(link) : link
|
||||
];
|
||||
}, []);
|
||||
|
||||
// Get pinned links augmented with desired properties
|
||||
const plainPinned = await this.pinnedCache.request();
|
||||
let plainPinned = await this.pinnedCache.request();
|
||||
|
||||
// Insert search shortcuts if we need to.
|
||||
// _maybeInsertSearchShortcuts returns true if any search shortcuts are
|
||||
// inserted, meaning we need to expire and refresh the pinnedCache
|
||||
if (await this._maybeInsertSearchShortcuts(plainPinned)) {
|
||||
this.pinnedCache.expire();
|
||||
plainPinned = await this.pinnedCache.request();
|
||||
}
|
||||
|
||||
const pinned = await Promise.all(plainPinned.map(async link => {
|
||||
if (!link) {
|
||||
return link;
|
||||
|
@ -178,8 +273,12 @@ this.TopSitesFeed = class TopSitesFeed {
|
|||
}
|
||||
// If the link is a frecent site, do not copy over 'isDefault', else check
|
||||
// if the site is a default site
|
||||
const copy = Object.assign({}, frecentSite ||
|
||||
{isDefault: !!notBlockedDefaultSites.find(finder)}, link, {hostname: shortURL(link)});
|
||||
const copy = Object.assign(
|
||||
{},
|
||||
frecentSite || {isDefault: !!notBlockedDefaultSites.find(finder)},
|
||||
link,
|
||||
{hostname: shortURL(link)}
|
||||
);
|
||||
|
||||
// Add in favicons if we don't already have it
|
||||
if (!copy.favicon) {
|
||||
|
@ -216,6 +315,8 @@ this.TopSitesFeed = class TopSitesFeed {
|
|||
// If there is a custom screenshot this is the only image we display
|
||||
if (link.customScreenshotURL) {
|
||||
this._fetchScreenshot(link, link.customScreenshotURL);
|
||||
} else if (link.searchTopSite && !link.isDefault) {
|
||||
this._tippyTopProvider.processSite(link);
|
||||
} else {
|
||||
this._fetchIcon(link);
|
||||
}
|
||||
|
@ -259,6 +360,18 @@ this.TopSitesFeed = class TopSitesFeed {
|
|||
}
|
||||
}
|
||||
|
||||
topSiteToSearchTopSite(site) {
|
||||
const searchProvider = getSearchProvider(shortURL(site));
|
||||
if (!searchProvider) {
|
||||
return site;
|
||||
}
|
||||
return {
|
||||
...site,
|
||||
searchTopSite: true,
|
||||
label: searchProvider.keyword
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an image for the link preferring tippy top, rich favicon, screenshots.
|
||||
*/
|
||||
|
@ -337,7 +450,7 @@ this.TopSitesFeed = class TopSitesFeed {
|
|||
* @param customScreenshotURL {string} User set URL of preview image for site
|
||||
* @param label {string} User set string of custom site name
|
||||
*/
|
||||
async _pinSiteAt({customScreenshotURL, label, url}, index) {
|
||||
async _pinSiteAt({customScreenshotURL, label, url, searchTopSite}, index) {
|
||||
const toPin = {url};
|
||||
if (label) {
|
||||
toPin.label = label;
|
||||
|
@ -345,6 +458,9 @@ this.TopSitesFeed = class TopSitesFeed {
|
|||
if (customScreenshotURL) {
|
||||
toPin.customScreenshotURL = customScreenshotURL;
|
||||
}
|
||||
if (searchTopSite) {
|
||||
toPin.searchTopSite = searchTopSite;
|
||||
}
|
||||
NewTabUtils.pinnedLinks.pin(toPin, index);
|
||||
|
||||
await this._clearLinkCustomScreenshot({customScreenshotURL, url});
|
||||
|
@ -391,6 +507,20 @@ this.TopSitesFeed = class TopSitesFeed {
|
|||
this._broadcastPinnedSitesUpdated();
|
||||
}
|
||||
|
||||
disableSearchImprovements() {
|
||||
Services.prefs.clearUserPref(`browser.newtabpage.activity-stream.${SEARCH_SHORTCUTS_HAVE_PINNED_PREF}`);
|
||||
this.unpinAllSearchShortcuts();
|
||||
}
|
||||
|
||||
unpinAllSearchShortcuts() {
|
||||
for (let pinnedLink of NewTabUtils.pinnedLinks.links) {
|
||||
if (pinnedLink && pinnedLink.searchTopSite) {
|
||||
NewTabUtils.pinnedLinks.unpin(pinnedLink);
|
||||
}
|
||||
}
|
||||
this.pinnedCache.expire();
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a site to pin at a position shifting over any other pinned sites.
|
||||
*/
|
||||
|
@ -478,10 +608,20 @@ this.TopSitesFeed = class TopSitesFeed {
|
|||
this.refresh({broadcast: true});
|
||||
break;
|
||||
case at.PREF_CHANGED:
|
||||
if (action.data.name === DEFAULT_SITES_PREF) {
|
||||
this.refreshDefaults(action.data.value);
|
||||
} else if ([ROWS_PREF, NO_DEFAULT_SEARCH_TILE_EXP_PREF].includes(action.data.name)) {
|
||||
this.refresh({broadcast: true});
|
||||
switch (action.data.name) {
|
||||
case DEFAULT_SITES_PREF:
|
||||
this.refreshDefaults(action.data.value);
|
||||
break;
|
||||
case ROWS_PREF:
|
||||
case NO_DEFAULT_SEARCH_TILE_EXP_PREF:
|
||||
case SEARCH_SHORTCUTS_SEARCH_ENGINES_PREF:
|
||||
this.refresh({broadcast: true});
|
||||
break;
|
||||
case SEARCH_SHORTCUTS_EXPERIMENT:
|
||||
if (!action.data.value) {
|
||||
this.disableSearchImprovements();
|
||||
}
|
||||
this.refresh({broadcast: true});
|
||||
}
|
||||
break;
|
||||
case at.UPDATE_SECTION_PREFS:
|
||||
|
|
|
@ -25,7 +25,7 @@ const DEFAULT_RECS_EXPIRE_TIME = 60 * 60 * 1000; // 1 hour
|
|||
const SECTION_ID = "topstories";
|
||||
const SPOC_IMPRESSION_TRACKING_PREF = "feeds.section.topstories.spoc.impressions";
|
||||
const REC_IMPRESSION_TRACKING_PREF = "feeds.section.topstories.rec.impressions";
|
||||
const MAX_LIFETIME_CAP = 100; // Guard against misconfiguration on the server
|
||||
const MAX_LIFETIME_CAP = 500; // Guard against misconfiguration on the server
|
||||
|
||||
this.TopStoriesFeed = class TopStoriesFeed {
|
||||
constructor() {
|
||||
|
@ -35,33 +35,52 @@ this.TopStoriesFeed = class TopStoriesFeed {
|
|||
this._prefs = new Prefs();
|
||||
}
|
||||
|
||||
init() {
|
||||
const initFeed = () => {
|
||||
SectionsManager.enableSection(SECTION_ID);
|
||||
try {
|
||||
const {options} = SectionsManager.sections.get(SECTION_ID);
|
||||
const apiKey = this.getApiKeyFromPref(options.api_key_pref);
|
||||
this.stories_endpoint = this.produceFinalEndpointUrl(options.stories_endpoint, apiKey);
|
||||
this.topics_endpoint = this.produceFinalEndpointUrl(options.topics_endpoint, apiKey);
|
||||
this.read_more_endpoint = options.read_more_endpoint;
|
||||
this.stories_referrer = options.stories_referrer;
|
||||
this.personalized = options.personalized;
|
||||
this.show_spocs = options.show_spocs;
|
||||
this.maxHistoryQueryResults = options.maxHistoryQueryResults;
|
||||
this.storiesLastUpdated = 0;
|
||||
this.topicsLastUpdated = 0;
|
||||
this.domainAffinitiesLastUpdated = 0;
|
||||
async onInit() {
|
||||
SectionsManager.enableSection(SECTION_ID);
|
||||
try {
|
||||
const {options} = SectionsManager.sections.get(SECTION_ID);
|
||||
const apiKey = this.getApiKeyFromPref(options.api_key_pref);
|
||||
// Set this to true if we're not loading from cache.
|
||||
let shouldBroadcast = false;
|
||||
this.stories_endpoint = this.produceFinalEndpointUrl(options.stories_endpoint, apiKey);
|
||||
this.topics_endpoint = this.produceFinalEndpointUrl(options.topics_endpoint, apiKey);
|
||||
this.read_more_endpoint = options.read_more_endpoint;
|
||||
this.stories_referrer = options.stories_referrer;
|
||||
this.personalized = options.personalized;
|
||||
this.show_spocs = options.show_spocs;
|
||||
this.maxHistoryQueryResults = options.maxHistoryQueryResults;
|
||||
this.storiesLastUpdated = 0;
|
||||
this.topicsLastUpdated = 0;
|
||||
this.storiesLoaded = false;
|
||||
this.domainAffinitiesLastUpdated = 0;
|
||||
|
||||
this.loadCachedData();
|
||||
this.fetchStories();
|
||||
this.fetchTopics();
|
||||
|
||||
Services.obs.addObserver(this, "idle-daily");
|
||||
} catch (e) {
|
||||
Cu.reportError(`Problem initializing top stories feed: ${e.message}`);
|
||||
// Cache is used for new page loads, which shouldn't have changed data.
|
||||
// If we have changed data, cache should be cleared,
|
||||
// and last updated should be 0, and we can fetch.
|
||||
// Think there is a bug here.
|
||||
await this.loadCachedData();
|
||||
if (this.storiesLastUpdated === 0) {
|
||||
shouldBroadcast = true;
|
||||
await this.fetchStories();
|
||||
}
|
||||
};
|
||||
SectionsManager.onceInitialized(initFeed);
|
||||
if (this.topicsLastUpdated === 0) {
|
||||
shouldBroadcast = true;
|
||||
await this.fetchTopics();
|
||||
}
|
||||
this.doContentUpdate(shouldBroadcast);
|
||||
this.storiesLoaded = true;
|
||||
|
||||
// This is filtered so an update function can return true to retry on the next run
|
||||
this.contentUpdateQueue = this.contentUpdateQueue.filter(update => update());
|
||||
|
||||
Services.obs.addObserver(this, "idle-daily");
|
||||
} catch (e) {
|
||||
Cu.reportError(`Problem initializing top stories feed: ${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
init() {
|
||||
SectionsManager.onceInitialized(this.onInit.bind(this));
|
||||
}
|
||||
|
||||
observe(subject, topic, data) {
|
||||
|
@ -73,10 +92,26 @@ this.TopStoriesFeed = class TopStoriesFeed {
|
|||
}
|
||||
|
||||
uninit() {
|
||||
this.storiesLoaded = false;
|
||||
this.cache.set("stories", {});
|
||||
this.cache.set("topics", {});
|
||||
Services.obs.removeObserver(this, "idle-daily");
|
||||
SectionsManager.disableSection(SECTION_ID);
|
||||
}
|
||||
|
||||
doContentUpdate(shouldBroadcast) {
|
||||
let updateProps = {};
|
||||
if (this.stories) {
|
||||
updateProps.rows = this.stories;
|
||||
}
|
||||
if (this.topics) {
|
||||
Object.assign(updateProps, {topics: this.topics, read_more_endpoint: this.read_more_endpoint});
|
||||
}
|
||||
|
||||
// We should only be calling this once per init.
|
||||
this.dispatchUpdateEvent(shouldBroadcast, updateProps);
|
||||
}
|
||||
|
||||
async fetchStories() {
|
||||
if (!this.stories_endpoint) {
|
||||
return;
|
||||
|
@ -97,13 +132,8 @@ this.TopStoriesFeed = class TopStoriesFeed {
|
|||
this.spocs = this.transform(body.spocs).filter(s => s.score >= s.min_score);
|
||||
this.cleanUpCampaignImpressionPref();
|
||||
}
|
||||
|
||||
this.dispatchUpdateEvent(this.storiesLastUpdated, {rows: this.stories});
|
||||
this.storiesLastUpdated = Date.now();
|
||||
body._timestamp = this.storiesLastUpdated;
|
||||
// This is filtered so an update function can return true to retry on the next run
|
||||
this.contentUpdateQueue = this.contentUpdateQueue.filter(update => update());
|
||||
|
||||
this.cache.set("stories", body);
|
||||
} catch (error) {
|
||||
Cu.reportError(`Failed to fetch content: ${error.message}`);
|
||||
|
@ -123,11 +153,11 @@ this.TopStoriesFeed = class TopStoriesFeed {
|
|||
if (stories && stories.length > 0 && this.storiesLastUpdated === 0) {
|
||||
this.updateSettings(data.stories.settings);
|
||||
const rows = this.transform(stories);
|
||||
this.dispatchUpdateEvent(this.storiesLastUpdated, {rows});
|
||||
this.stories = rows;
|
||||
this.storiesLastUpdated = data.stories._timestamp;
|
||||
}
|
||||
if (topics && topics.length > 0 && this.topicsLastUpdated === 0) {
|
||||
this.dispatchUpdateEvent(this.topicsLastUpdated, {topics, read_more_endpoint: this.read_more_endpoint});
|
||||
this.topics = topics;
|
||||
this.topicsLastUpdated = data.topics._timestamp;
|
||||
}
|
||||
}
|
||||
|
@ -169,7 +199,7 @@ this.TopStoriesFeed = class TopStoriesFeed {
|
|||
const body = await response.json();
|
||||
const {topics} = body;
|
||||
if (topics) {
|
||||
this.dispatchUpdateEvent(this.topicsLastUpdated, {topics, read_more_endpoint: this.read_more_endpoint});
|
||||
this.topics = topics;
|
||||
this.topicsLastUpdated = Date.now();
|
||||
body._timestamp = this.topicsLastUpdated;
|
||||
this.cache.set("topics", body);
|
||||
|
@ -179,8 +209,8 @@ this.TopStoriesFeed = class TopStoriesFeed {
|
|||
}
|
||||
}
|
||||
|
||||
dispatchUpdateEvent(lastUpdated, data) {
|
||||
SectionsManager.updateSection(SECTION_ID, data, lastUpdated === 0);
|
||||
dispatchUpdateEvent(shouldBroadcast, data) {
|
||||
SectionsManager.updateSection(SECTION_ID, data, shouldBroadcast);
|
||||
}
|
||||
|
||||
compareScore(a, b) {
|
||||
|
@ -320,7 +350,7 @@ this.TopStoriesFeed = class TopStoriesFeed {
|
|||
return false;
|
||||
};
|
||||
|
||||
if (this.stories) {
|
||||
if (this.storiesLoaded) {
|
||||
updateContent();
|
||||
} else {
|
||||
// Delay updating tab content until initial data has been fetched
|
||||
|
@ -448,18 +478,20 @@ this.TopStoriesFeed = class TopStoriesFeed {
|
|||
this.init();
|
||||
}
|
||||
|
||||
onAction(action) {
|
||||
async onAction(action) {
|
||||
switch (action.type) {
|
||||
case at.INIT:
|
||||
this.init();
|
||||
break;
|
||||
case at.SYSTEM_TICK:
|
||||
if (Date.now() - this.storiesLastUpdated >= STORIES_UPDATE_TIME) {
|
||||
this.fetchStories();
|
||||
await this.fetchStories();
|
||||
}
|
||||
if (Date.now() - this.topicsLastUpdated >= TOPICS_UPDATE_TIME) {
|
||||
this.fetchTopics();
|
||||
await this.fetchTopics();
|
||||
}
|
||||
|
||||
this.doContentUpdate(false);
|
||||
break;
|
||||
case at.UNINIT:
|
||||
this.uninit();
|
||||
|
|
|
@ -128,7 +128,7 @@ topsites_form_title_placeholder=Digite um título
|
|||
topsites_form_url_label=URL
|
||||
topsites_form_image_url_label=URL de imagem personalizada
|
||||
topsites_form_url_placeholder=Digite ou cole um URL
|
||||
topsites_form_use_image_link=Utilizar uma imagem personalizada…
|
||||
topsites_form_use_image_link=Usar uma imagem personalizada…
|
||||
# LOCALIZATION NOTE (topsites_form_*_button): These are verbs/actions.
|
||||
topsites_form_preview_button=Visualizar
|
||||
topsites_form_add_button=Adicionar
|
||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -9,7 +9,7 @@
|
|||
<link rel="stylesheet" href="resource://activity-stream/css/activity-stream.css" />
|
||||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="root"><div data-reactroot=""><div class="outer-wrapper fixed-to-top"><main><div class="non-collapsible-section"><div class="search-wrapper"><label for="newtab-search-text" class="search-label"><span class="sr-only"><span>Otsi veebist</span></span></label><input type="search" id="newtab-search-text" maxLength="256" placeholder="Otsi veebist" title="Otsi veebist"/><button id="searchSubmit" class="search-button" title="Otsi"><span class="sr-only"><span>Otsi</span></span></button></div></div><div class="body-wrapper"><div class="sections-list"><section class="collapsible-section top-sites animation-enabled" data-section-id="topsites"><div class="section-top-bar"><h3 class="section-title"><span class="click-target"><span class="icon icon-small-spacer icon-topsites"></span><span>Top saidid</span></span></h3><div><button class="context-menu-button icon"><span class="sr-only"><span>Ava osa kontekstimenüü</span></span></button></div></div><div class="section-body"><ul class="top-sites-list"><li class="top-site-outer placeholder "><div class="top-site-inner"><a><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Muuda seda saiti"></button></div></li><li class="top-site-outer placeholder "><div class="top-site-inner"><a><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Muuda seda saiti"></button></div></li><li class="top-site-outer placeholder "><div class="top-site-inner"><a><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Muuda seda saiti"></button></div></li><li class="top-site-outer placeholder "><div class="top-site-inner"><a><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Muuda seda saiti"></button></div></li><li class="top-site-outer placeholder "><div class="top-site-inner"><a><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Muuda seda saiti"></button></div></li><li class="top-site-outer placeholder "><div class="top-site-inner"><a><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Muuda seda saiti"></button></div></li><li class="top-site-outer placeholder hide-for-narrow"><div class="top-site-inner"><a><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Muuda seda saiti"></button></div></li><li class="top-site-outer placeholder hide-for-narrow"><div class="top-site-inner"><a><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Muuda seda saiti"></button></div></li></ul><div class="edit-topsites-wrapper"></div></div></section><section class="collapsible-section section normal-cards animation-enabled" data-section-id="topstories"><div class="section-top-bar"><h3 class="section-title"><span class="click-target"><span class="icon icon-small-spacer icon-pocket"></span><span>Pocket soovitab</span></span></h3><div><button class="context-menu-button icon"><span class="sr-only"><span>Ava osa kontekstimenüü</span></span></button></div></div><div class="section-body"><ul class="section-list" style="padding:0"></ul><div class="topic"><span><span>Populaarsed teemad:</span></span><ul></ul></div></div></section><section class="collapsible-section section normal-cards animation-enabled" data-section-id="highlights"><div class="section-top-bar"><h3 class="section-title"><span class="click-target"><span class="icon icon-small-spacer icon-highlights"></span><span>Esiletõstetud</span></span></h3><div><button class="context-menu-button icon"><span class="sr-only"><span>Ava osa kontekstimenüü</span></span></button></div></div><div class="section-body"><ul class="section-list" style="padding:0"></ul></div></section></div><div class="prefs-button"><button class="icon icon-settings" title="Kohanda uue kaardi lehte"></button></div></div></main></div></div></div>
|
||||
<div id="root"><div data-reactroot=""><div class="outer-wrapper fixed-to-top"><main><div class="non-collapsible-section"><div class="search-wrapper"><div class="search-inner-wrapper"><label for="newtab-search-text" class="search-label"><span class="sr-only"><span>Otsi veebist</span></span></label><input type="search" id="newtab-search-text" maxLength="256" placeholder="Otsi veebist" title="Otsi veebist"/><button id="searchSubmit" class="search-button" title="Otsi"><span class="sr-only"><span>Otsi</span></span></button></div></div></div><div class="body-wrapper"><div class="sections-list"><section class="collapsible-section top-sites animation-enabled" data-section-id="topsites"><div class="section-top-bar"><h3 class="section-title"><span class="click-target"><span class="icon icon-small-spacer icon-topsites"></span><span>Top saidid</span></span></h3><div><button class="context-menu-button icon"><span class="sr-only"><span>Ava osa kontekstimenüü</span></span></button></div></div><div class="section-body"><ul class="top-sites-list"><li class="top-site-outer placeholder "><div class="top-site-inner"><a><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Muuda seda saiti"></button></div></li><li class="top-site-outer placeholder "><div class="top-site-inner"><a><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Muuda seda saiti"></button></div></li><li class="top-site-outer placeholder "><div class="top-site-inner"><a><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Muuda seda saiti"></button></div></li><li class="top-site-outer placeholder "><div class="top-site-inner"><a><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Muuda seda saiti"></button></div></li><li class="top-site-outer placeholder "><div class="top-site-inner"><a><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Muuda seda saiti"></button></div></li><li class="top-site-outer placeholder "><div class="top-site-inner"><a><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Muuda seda saiti"></button></div></li><li class="top-site-outer placeholder hide-for-narrow"><div class="top-site-inner"><a><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Muuda seda saiti"></button></div></li><li class="top-site-outer placeholder hide-for-narrow"><div class="top-site-inner"><a><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Muuda seda saiti"></button></div></li></ul><div class="edit-topsites-wrapper"></div></div></section><section class="collapsible-section section normal-cards animation-enabled" data-section-id="topstories"><div class="section-top-bar"><h3 class="section-title"><span class="click-target"><span class="icon icon-small-spacer icon-pocket"></span><span>Pocket soovitab</span></span></h3><div><button class="context-menu-button icon"><span class="sr-only"><span>Ava osa kontekstimenüü</span></span></button></div></div><div class="section-body"><ul class="section-list" style="padding:0"></ul><div class="topic"><span><span>Populaarsed teemad:</span></span><ul></ul></div></div></section><section class="collapsible-section section normal-cards animation-enabled" data-section-id="highlights"><div class="section-top-bar"><h3 class="section-title"><span class="click-target"><span class="icon icon-small-spacer icon-highlights"></span><span>Esiletõstetud</span></span></h3><div><button class="context-menu-button icon"><span class="sr-only"><span>Ava osa kontekstimenüü</span></span></button></div></div><div class="section-body"><ul class="section-list" style="padding:0"></ul></div></section></div><div class="prefs-button"><button class="icon icon-settings" title="Kohanda uue kaardi lehte"></button></div></div></main></div></div></div>
|
||||
<div id="snippets-container">
|
||||
<div id="snippets"></div>
|
||||
</div>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<link rel="stylesheet" href="resource://activity-stream/css/activity-stream.css" />
|
||||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="root"><div data-reactroot=""><div class="outer-wrapper fixed-to-top"><main><div class="non-collapsible-section"><div class="search-wrapper"><label for="newtab-search-text" class="search-label"><span class="sr-only"><span>Otsi veebist</span></span></label><input type="search" id="newtab-search-text" maxLength="256" placeholder="Otsi veebist" title="Otsi veebist"/><button id="searchSubmit" class="search-button" title="Otsi"><span class="sr-only"><span>Otsi</span></span></button></div></div><div class="body-wrapper"><div class="sections-list"><section class="collapsible-section top-sites animation-enabled" data-section-id="topsites"><div class="section-top-bar"><h3 class="section-title"><span class="click-target"><span class="icon icon-small-spacer icon-topsites"></span><span>Top saidid</span></span></h3><div><button class="context-menu-button icon"><span class="sr-only"><span>Ava osa kontekstimenüü</span></span></button></div></div><div class="section-body"><ul class="top-sites-list"><li class="top-site-outer placeholder "><div class="top-site-inner"><a><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Muuda seda saiti"></button></div></li><li class="top-site-outer placeholder "><div class="top-site-inner"><a><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Muuda seda saiti"></button></div></li><li class="top-site-outer placeholder "><div class="top-site-inner"><a><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Muuda seda saiti"></button></div></li><li class="top-site-outer placeholder "><div class="top-site-inner"><a><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Muuda seda saiti"></button></div></li><li class="top-site-outer placeholder "><div class="top-site-inner"><a><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Muuda seda saiti"></button></div></li><li class="top-site-outer placeholder "><div class="top-site-inner"><a><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Muuda seda saiti"></button></div></li><li class="top-site-outer placeholder hide-for-narrow"><div class="top-site-inner"><a><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Muuda seda saiti"></button></div></li><li class="top-site-outer placeholder hide-for-narrow"><div class="top-site-inner"><a><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Muuda seda saiti"></button></div></li></ul><div class="edit-topsites-wrapper"></div></div></section><section class="collapsible-section section normal-cards animation-enabled" data-section-id="topstories"><div class="section-top-bar"><h3 class="section-title"><span class="click-target"><span class="icon icon-small-spacer icon-pocket"></span><span>Pocket soovitab</span></span></h3><div><button class="context-menu-button icon"><span class="sr-only"><span>Ava osa kontekstimenüü</span></span></button></div></div><div class="section-body"><ul class="section-list" style="padding:0"></ul><div class="topic"><span><span>Populaarsed teemad:</span></span><ul></ul></div></div></section><section class="collapsible-section section normal-cards animation-enabled" data-section-id="highlights"><div class="section-top-bar"><h3 class="section-title"><span class="click-target"><span class="icon icon-small-spacer icon-highlights"></span><span>Esiletõstetud</span></span></h3><div><button class="context-menu-button icon"><span class="sr-only"><span>Ava osa kontekstimenüü</span></span></button></div></div><div class="section-body"><ul class="section-list" style="padding:0"></ul></div></section></div><div class="prefs-button"><button class="icon icon-settings" title="Kohanda uue kaardi lehte"></button></div></div></main></div></div></div>
|
||||
<div id="root"><div data-reactroot=""><div class="outer-wrapper fixed-to-top"><main><div class="non-collapsible-section"><div class="search-wrapper"><div class="search-inner-wrapper"><label for="newtab-search-text" class="search-label"><span class="sr-only"><span>Otsi veebist</span></span></label><input type="search" id="newtab-search-text" maxLength="256" placeholder="Otsi veebist" title="Otsi veebist"/><button id="searchSubmit" class="search-button" title="Otsi"><span class="sr-only"><span>Otsi</span></span></button></div></div></div><div class="body-wrapper"><div class="sections-list"><section class="collapsible-section top-sites animation-enabled" data-section-id="topsites"><div class="section-top-bar"><h3 class="section-title"><span class="click-target"><span class="icon icon-small-spacer icon-topsites"></span><span>Top saidid</span></span></h3><div><button class="context-menu-button icon"><span class="sr-only"><span>Ava osa kontekstimenüü</span></span></button></div></div><div class="section-body"><ul class="top-sites-list"><li class="top-site-outer placeholder "><div class="top-site-inner"><a><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Muuda seda saiti"></button></div></li><li class="top-site-outer placeholder "><div class="top-site-inner"><a><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Muuda seda saiti"></button></div></li><li class="top-site-outer placeholder "><div class="top-site-inner"><a><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Muuda seda saiti"></button></div></li><li class="top-site-outer placeholder "><div class="top-site-inner"><a><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Muuda seda saiti"></button></div></li><li class="top-site-outer placeholder "><div class="top-site-inner"><a><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Muuda seda saiti"></button></div></li><li class="top-site-outer placeholder "><div class="top-site-inner"><a><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Muuda seda saiti"></button></div></li><li class="top-site-outer placeholder hide-for-narrow"><div class="top-site-inner"><a><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Muuda seda saiti"></button></div></li><li class="top-site-outer placeholder hide-for-narrow"><div class="top-site-inner"><a><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Muuda seda saiti"></button></div></li></ul><div class="edit-topsites-wrapper"></div></div></section><section class="collapsible-section section normal-cards animation-enabled" data-section-id="topstories"><div class="section-top-bar"><h3 class="section-title"><span class="click-target"><span class="icon icon-small-spacer icon-pocket"></span><span>Pocket soovitab</span></span></h3><div><button class="context-menu-button icon"><span class="sr-only"><span>Ava osa kontekstimenüü</span></span></button></div></div><div class="section-body"><ul class="section-list" style="padding:0"></ul><div class="topic"><span><span>Populaarsed teemad:</span></span><ul></ul></div></div></section><section class="collapsible-section section normal-cards animation-enabled" data-section-id="highlights"><div class="section-top-bar"><h3 class="section-title"><span class="click-target"><span class="icon icon-small-spacer icon-highlights"></span><span>Esiletõstetud</span></span></h3><div><button class="context-menu-button icon"><span class="sr-only"><span>Ava osa kontekstimenüü</span></span></button></div></div><div class="section-body"><ul class="section-list" style="padding:0"></ul></div></section></div><div class="prefs-button"><button class="icon icon-settings" title="Kohanda uue kaardi lehte"></button></div></div></main></div></div></div>
|
||||
<div id="snippets-container">
|
||||
<div id="snippets"></div>
|
||||
</div>
|
||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче