/* 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/. */ import { actionCreators as ac, actionTypes as at, } from "common/Actions.sys.mjs"; import { MIN_RICH_FAVICON_SIZE, TOP_SITES_SOURCE } from "./TopSitesConstants"; import { CollapsibleSection } from "content-src/components/CollapsibleSection/CollapsibleSection"; import { ComponentPerfTimer } from "content-src/components/ComponentPerfTimer/ComponentPerfTimer"; import { connect } from "react-redux"; import { ModalOverlayWrapper } from "../../asrouter/components/ModalOverlay/ModalOverlay"; import React from "react"; import { SearchShortcutsForm } from "./SearchShortcutsForm"; import { TOP_SITES_MAX_SITES_PER_ROW } from "common/Reducers.sys.mjs"; import { TopSiteForm } from "./TopSiteForm"; import { TopSiteList } from "./TopSite"; function topSiteIconType(link) { if (link.customScreenshotURL) { return "custom_screenshot"; } if (link.tippyTopIcon || link.faviconRef === "tippytop") { return "tippytop"; } if (link.faviconSize >= MIN_RICH_FAVICON_SIZE) { return "rich_icon"; } if (link.screenshot) { return "screenshot"; } return "no_image"; } /** * Iterates through TopSites and counts types of images. * @param acc Accumulator for reducer. * @param topsite Entry in TopSites. */ function countTopSitesIconsTypes(topSites) { const countTopSitesTypes = (acc, link) => { acc[topSiteIconType(link)]++; return acc; }; return topSites.reduce(countTopSitesTypes, { custom_screenshot: 0, screenshot: 0, tippytop: 0, rich_icon: 0, no_image: 0, }); } export class _TopSites extends React.PureComponent { constructor(props) { super(props); this.onEditFormClose = this.onEditFormClose.bind(this); this.onSearchShortcutsFormClose = this.onSearchShortcutsFormClose.bind(this); } /** * Dispatch session statistics about the quality of TopSites icons and pinned count. */ _dispatchTopSitesStats() { const topSites = this._getVisibleTopSites().filter( topSite => topSite !== null && topSite !== undefined ); const topSitesIconsStats = countTopSitesIconsTypes(topSites); const topSitesPinned = topSites.filter(site => !!site.isPinned).length; const searchShortcuts = topSites.filter( site => !!site.searchTopSite ).length; // Dispatch telemetry event with the count of TopSites images types. this.props.dispatch( ac.AlsoToMain({ type: at.SAVE_SESSION_PERF_DATA, data: { topsites_icon_stats: topSitesIconsStats, topsites_pinned: topSitesPinned, topsites_search_shortcuts: searchShortcuts, }, }) ); } /** * Return the TopSites that are visible based on prefs and window width. */ _getVisibleTopSites() { // We hide 2 sites per row when not in the wide layout. let sitesPerRow = TOP_SITES_MAX_SITES_PER_ROW; // $break-point-widest = 1072px (from _variables.scss) if (!global.matchMedia(`(min-width: 1072px)`).matches) { sitesPerRow -= 2; } return this.props.TopSites.rows.slice( 0, this.props.TopSitesRows * sitesPerRow ); } componentDidUpdate() { this._dispatchTopSitesStats(); } componentDidMount() { this._dispatchTopSitesStats(); } onEditFormClose() { this.props.dispatch( ac.UserEvent({ source: TOP_SITES_SOURCE, event: "TOP_SITES_EDIT_CLOSE", }) ); this.props.dispatch({ type: at.TOP_SITES_CANCEL_EDIT }); } onSearchShortcutsFormClose() { this.props.dispatch( ac.UserEvent({ source: TOP_SITES_SOURCE, event: "SEARCH_EDIT_CLOSE", }) ); this.props.dispatch({ type: at.TOP_SITES_CLOSE_SEARCH_SHORTCUTS_MODAL }); } render() { const { props } = this; const { editForm, showSearchShortcutsForm } = props.TopSites; const extraMenuOptions = ["AddTopSite"]; const colors = props.Prefs.values["newNewtabExperience.colors"]; if (props.Prefs.values["improvesearch.topSiteSearchShortcuts"]) { extraMenuOptions.push("AddSearchShortcut"); } return (
{editForm && (
)} {showSearchShortcutsForm && (
)}
); } } export const TopSites = connect((state, props) => ({ TopSites: state.TopSites, Prefs: state.Prefs, TopSitesRows: state.Prefs.values.topSitesRows, }))(_TopSites);