diff --git a/app/images/panel/allow.svg b/app/images/panel/allow.svg new file mode 100644 index 000000000..e4998171f --- /dev/null +++ b/app/images/panel/allow.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/images/panel/protected.svg b/app/images/panel/protected.svg new file mode 100644 index 000000000..73d901f4d --- /dev/null +++ b/app/images/panel/protected.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/images/panel/restrict.svg b/app/images/panel/restrict.svg new file mode 100644 index 000000000..4769286af --- /dev/null +++ b/app/images/panel/restrict.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/app/images/panel/tracker-detail-cliqz-ads-icon.svg b/app/images/panel/tracker-detail-cliqz-ads-icon.svg deleted file mode 100644 index 2674f8621..000000000 --- a/app/images/panel/tracker-detail-cliqz-ads-icon.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/app/images/panel/tracker-detail-cliqz-cookies-and-fingerprints-icon.svg b/app/images/panel/tracker-detail-cliqz-cookies-and-fingerprints-icon.svg deleted file mode 100644 index 8a5c467ad..000000000 --- a/app/images/panel/tracker-detail-cliqz-cookies-and-fingerprints-icon.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/panel/components/Blocking.jsx b/app/panel/components/Blocking.jsx index 0b6106099..cd430a761 100644 --- a/app/panel/components/Blocking.jsx +++ b/app/panel/components/Blocking.jsx @@ -294,7 +294,7 @@ class Blocking extends React.Component { show_tracker_urls, sitePolicy, smartBlock, - smartBlockActive + smartBlockActive, } = this.props; const { diff --git a/app/panel/components/Blocking/Category.jsx b/app/panel/components/Blocking/Category.jsx index 10766cc8f..fe9be3483 100644 --- a/app/panel/components/Blocking/Category.jsx +++ b/app/panel/components/Blocking/Category.jsx @@ -12,6 +12,8 @@ */ import React from 'react'; +import ClassNames from 'classnames'; +import { ThemeContext } from '../../contexts/ThemeContext'; import Trackers from './Trackers'; /** @@ -21,6 +23,8 @@ import Trackers from './Trackers'; * @memberOf BlockingComponents */ class Category extends React.Component { + static contextType = ThemeContext; + constructor(props) { super(props); this.state = { @@ -153,6 +157,21 @@ class Category extends React.Component { } } + _renderCaret() { + const { isExpanded } = this.state; + const { isUnknown } = this.props; + const caretClasses = ClassNames(this.context, { + 'caret-down': isExpanded, + 'caret-up': !isExpanded, + Category__antiTrackingCaret: isUnknown + }); + return ( + + + + ); + } + /** * Render a list of categories. Pass globalBlocking flag to all trackers * in the category so that they would know which view they are part of. @@ -169,7 +188,6 @@ class Category extends React.Component { const globalBlocking = !!this.props.globalBlocking; const checkBoxStyle = `${(this.state.totalShownBlocked && this.state.allShownBlocked) ? 'all-blocked ' : (this.state.totalShownBlocked ? 'some-blocked ' : '')} checkbox-container`; - const caretClasses = (this.state.isExpanded ? 'caret-up' : 'caret-down') + (isUnknown ? ' Category__antiTrackingCaret' : ''); const filteredText = { color: 'red' }; let trackersBlockedCount; @@ -224,7 +242,7 @@ class Category extends React.Component {
-
+ { this._renderCaret() } {!isUnknown && (
diff --git a/app/panel/components/Blocking/Tracker.jsx b/app/panel/components/Blocking/Tracker.jsx index 76adf9f3d..917a13457 100644 --- a/app/panel/components/Blocking/Tracker.jsx +++ b/app/panel/components/Blocking/Tracker.jsx @@ -14,9 +14,9 @@ /* eslint react/no-array-index-key: 0 */ import React from 'react'; -import { ReactSVG } from 'react-svg'; import ClassNames from 'classnames'; +import { ThemeContext } from '../../contexts/ThemeContext'; import globals from '../../../../src/classes/Globals'; import { log } from '../../../../src/utils/common'; import { sendMessageInPromise } from '../../utils/msg'; @@ -27,6 +27,8 @@ import { renderKnownTrackerButtons, renderUnknownTrackerButtons } from './tracke * @memberOf BlockingComponents */ class Tracker extends React.Component { + static contextType = ThemeContext; + constructor(props) { super(props); this.state = { @@ -254,15 +256,22 @@ class Tracker extends React.Component { ); } - _renderCliqzCookiesAndFingerprintsIcon() { return this._renderCliqzStatsIcon('cookies-and-fingerprints'); } - - _renderCliqzAdsIcon() { return this._renderCliqzStatsIcon('ads'); } - - _renderCliqzStatsIcon(type) { - const path = `/app/images/panel/tracker-detail-cliqz-${type}-icon.svg`; + _renderCliqzCookiesAndFingerprintsIcon() { + return ( + + + + ); + } + _renderCliqzAdsIcon() { return ( - + + + + + + ); } @@ -347,6 +356,7 @@ class Tracker extends React.Component { tracker.whitelisted, tracker.siteRestricted, tracker.type, + this.context )}
diff --git a/app/panel/components/Blocking/trackerButtonRenderHelpers.jsx b/app/panel/components/Blocking/trackerButtonRenderHelpers.jsx index 932d62004..3bfbb1ae5 100644 --- a/app/panel/components/Blocking/trackerButtonRenderHelpers.jsx +++ b/app/panel/components/Blocking/trackerButtonRenderHelpers.jsx @@ -54,21 +54,33 @@ export const renderKnownTrackerButtons = ( ); export const renderUnknownTrackerButtons = ( - handleCliqzTrackerWhitelist, whitelisted, siteRestricted, type + handleCliqzTrackerWhitelist, whitelisted, siteRestricted, type, contextType ) => { const svgContainerClasses = ClassNames('unknown-svg-container', { whitelisted: whitelisted && !siteRestricted, siteRestricted, }); + const borderClassNames = ClassNames('border', { + protected: type === 'antiTracking', + restricted: type !== 'antiTracking', + contextType + }); + + const backgroundClassNames = ClassNames('background', { + protected: type === 'antiTracking', + restricted: type !== 'antiTracking', + contextType + }); + return (
{/* USE INLINE SVG FOR TRUST CIRCLE TO CHANGE COLORS WITH CSS */} - - + + @@ -80,8 +92,8 @@ export const renderUnknownTrackerButtons = ( - - + + {type === 'antiTracking' ? ( diff --git a/app/panel/components/BuildingBlocks/RadioButton.jsx b/app/panel/components/BuildingBlocks/RadioButton.jsx index d8205a983..12924cfb3 100644 --- a/app/panel/components/BuildingBlocks/RadioButton.jsx +++ b/app/panel/components/BuildingBlocks/RadioButton.jsx @@ -11,8 +11,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0 */ -/* eslint jsx-a11y/label-has-associated-control: 0 */ - import React from 'react'; import PropTypes from 'prop-types'; import ClassNames from 'classnames'; @@ -23,15 +21,16 @@ import ClassNames from 'classnames'; */ const RadioButton = (props) => { + const { checked, handleClick } = props; const OuterCircleClassNames = ClassNames('RadioButton__outerCircle', { - checked: props.checked, + checked, }); const InnerCircleClassNames = ClassNames('RadioButton__innerCircle', { - checked: props.checked, + checked, }); return ( - + @@ -40,6 +39,7 @@ const RadioButton = (props) => { // PropTypes ensure we pass required props of the correct type RadioButton.propTypes = { + checked: PropTypes.bool.isRequired, handleClick: PropTypes.func.isRequired, }; diff --git a/app/panel/components/BuildingBlocks/RadioButtonGroup.jsx b/app/panel/components/BuildingBlocks/RadioButtonGroup.jsx index 883fa60a1..e79cfe6d2 100644 --- a/app/panel/components/BuildingBlocks/RadioButtonGroup.jsx +++ b/app/panel/components/BuildingBlocks/RadioButtonGroup.jsx @@ -24,10 +24,10 @@ import RadioButton from './RadioButton'; const RadioButtonGroup = (props) => { const { indexClicked, handleItemClick } = props; return ( - props.items.map((item, index) => ( -
+ props.labels.map((label, index) => ( +
- {t(item.text)} + {t(label)}
{ // PropTypes ensure we pass required props of the correct type RadioButtonGroup.propTypes = { - items: PropTypes.arrayOf(PropTypes.object).isRequired, // Number of objects in array is the number of radio buttons + labels: PropTypes.arrayOf(PropTypes.string).isRequired, handleItemClick: PropTypes.func.isRequired, indexClicked: PropTypes.number.isRequired }; diff --git a/app/panel/components/BuildingBlocks/StatsGraph.jsx b/app/panel/components/BuildingBlocks/StatsGraph.jsx index 0ae8ab68c..80e89ec4d 100644 --- a/app/panel/components/BuildingBlocks/StatsGraph.jsx +++ b/app/panel/components/BuildingBlocks/StatsGraph.jsx @@ -14,12 +14,14 @@ import { isEqual } from 'underscore'; import React from 'react'; import * as D3 from 'd3'; - +import { ThemeContext } from '../../contexts/ThemeContext'; /** * Generates an animated graph displaying locally stored stats * @memberof PanelBuildingBlocks */ class StatsGraph extends React.Component { + static contextType = ThemeContext; + /** * Lifecycle event */ @@ -170,7 +172,7 @@ class StatsGraph extends React.Component { pathGroup.append('path') .attr('d', line) .attr('fill', 'none') - .attr('stroke', '#124559') + .attr('class', `line ${this.context}`) .attr('stroke-width', 1.5) .call(animator); // ---------------------------------------------------------------------- // @@ -187,7 +189,6 @@ class StatsGraph extends React.Component { .style('opacity', opacity); }, 1); } - // Add data points with event listeners for opening their respective tooltips canvas.append('g') .attr('class', 'point-group') @@ -196,7 +197,7 @@ class StatsGraph extends React.Component { .enter() .append('circle') .attr('class', (d, i) => `point point-${i}`) - .attr('fill', '#124559') + .attr('class', this.context) .attr('cx', d => x(d.index)) .attr('cy', d => y(d.amount)) .attr('r', 0) diff --git a/app/panel/components/Panel.jsx b/app/panel/components/Panel.jsx index c13fbb5da..6ed5e9517 100644 --- a/app/panel/components/Panel.jsx +++ b/app/panel/components/Panel.jsx @@ -16,6 +16,7 @@ import { NavLink } from 'react-router-dom'; import Header from '../containers/HeaderContainer'; import { PremiumPromoModal } from '../../shared-components'; import InsightsPromoModal from './InsightsPromoModal'; +import { ThemeContext } from '../contexts/ThemeContext'; import { DynamicUIPortContext } from '../contexts/DynamicUIPortContext'; import { sendMessage } from '../utils/msg'; import { setTheme } from '../utils/utils'; @@ -408,7 +409,7 @@ class Panel extends React.Component { } const notificationText = this.props.notificationShown && this.renderNotification(); - + const { current_theme } = this.props; return (
{this._renderPromoModal()} @@ -425,9 +426,11 @@ class Panel extends React.Component {
- - { this.props.children } - + + + { this.props.children } + +
); diff --git a/app/panel/components/Stats.jsx b/app/panel/components/Stats.jsx index 39aa829d2..8ec7ac7c7 100644 --- a/app/panel/components/Stats.jsx +++ b/app/panel/components/Stats.jsx @@ -524,13 +524,15 @@ class Stats extends React.Component { * @return {ReactComponent} StatsView instance */ render() { + const { user, loggedIn } = this.props; + const { showResetModal, selection } = this.state; return ( { - const themes = [ - { - name: 'default', - text: 'subscription_default_theme', - }, - { - name: 'midnight-theme', - text: 'subscription_dark_blue_theme', - }, - { - name: 'palm', - text: 'subscription_palm_theme', - }, - { - name: 'leaf', - text: 'subscription_leaf_theme', - } - ]; + const themes = ['default', 'midnight-theme', 'palm-theme', 'leaf-theme']; + const themeLabels = ['subscription_default_theme', 'subscription_dark_blue_theme', 'subscription_palm_theme', 'subscription_leaf_theme']; - const getIndexClicked = () => { - const index = themes.findIndex(theme => theme.name === props.theme); - return index; - }; + const getIndexClicked = () => themes.indexOf(props.theme); - const handleThemeClick = (index) => { - const theme = themes[index]; - props.changeTheme(theme.name); - }; + const handleThemeClick = index => props.changeTheme(themes[index]); return (
@@ -60,7 +38,7 @@ const SubscriptionThemes = (props) => { @@ -73,7 +51,6 @@ const SubscriptionThemes = (props) => { // PropTypes ensure we pass required props of the correct type SubscriptionThemes.propTypes = { changeTheme: PropTypes.func.isRequired, - theme: PropTypes.string.isRequired, }; export default SubscriptionThemes; diff --git a/app/panel/components/Summary.jsx b/app/panel/components/Summary.jsx index cb5a679a5..4b9463999 100644 --- a/app/panel/components/Summary.jsx +++ b/app/panel/components/Summary.jsx @@ -623,6 +623,7 @@ class Summary extends React.Component { const { enable_anti_tracking, is_expert, + current_theme, } = this.props; const isCondensed = this._isCondensed(); @@ -638,6 +639,7 @@ class Summary extends React.Component { isTooltipHeader={is_expert} isTooltipBody={!isCondensed} tooltipPosition={isCondensed ? 'right' : is_expert ? 'top top-right' : 'top'} + current_theme={current_theme} />
); @@ -647,6 +649,7 @@ class Summary extends React.Component { const { enable_ad_block, is_expert, + current_theme, } = this.props; const isCondensed = this._isCondensed(); @@ -662,6 +665,7 @@ class Summary extends React.Component { isTooltipHeader={is_expert} isTooltipBody={!isCondensed} tooltipPosition={isCondensed ? 'right' : 'top'} + current_theme={current_theme} />
); @@ -671,6 +675,7 @@ class Summary extends React.Component { const { enable_smart_block, is_expert, + current_theme, } = this.props; const isCondensed = this._isCondensed(); @@ -686,6 +691,7 @@ class Summary extends React.Component { isTooltipHeader={is_expert} isTooltipBody={!isCondensed} tooltipPosition={isCondensed ? 'right' : is_expert ? 'top top-left' : 'top'} + current_theme={current_theme} />
); @@ -740,7 +746,7 @@ class Summary extends React.Component { * @return {JSX} JSX for rendering the plus upgrade banner or subscriber icon */ _renderPlusUpgradeBannerOrSubscriberIcon() { - const { is_expert } = this.props; + const { is_expert, current_theme } = this.props; const isPlusSubscriber = this._isPlusSubscriber(); const upgradeBannerClassNames = ClassNames('UpgradeBanner', { @@ -752,7 +758,7 @@ class Summary extends React.Component {
{isPlusSubscriber && (
-
+
@@ -779,6 +785,7 @@ class Summary extends React.Component { enable_offers, is_expert, is_expanded, + current_theme } = this.props; const { disableBlocking } = this.state; const isCondensed = this._isCondensed(); @@ -787,39 +794,46 @@ class Summary extends React.Component { 'Summary--expert': is_expert && !is_expanded, 'Summary--condensed': isCondensed, }); + const foregroundClassNames = ClassNames('Summary__foreground', { + active: current_theme === 'palm-theme' + || current_theme === 'leaf-theme', + }); return (
- {!isCondensed && disableBlocking && ()} - {!isCondensed && !disableBlocking && this._renderDonut()} - {!isCondensed && !disableBlocking && this._renderPageHostReadout()} - - {isCondensed && !disableBlocking && this._renderTotalTrackersFound()} +
+
+ {!isCondensed && disableBlocking && ()} + {!isCondensed && !disableBlocking && this._renderDonut()} + {!isCondensed && !disableBlocking && this._renderPageHostReadout()} + + {isCondensed && !disableBlocking && this._renderTotalTrackersFound()} + +
+ {!disableBlocking && this._renderTotalTrackersBlocked()} + {!disableBlocking && this._renderTotalRequestsModified()} + {!disableBlocking && this._renderPageLoadTime()} +
-
- {!disableBlocking && this._renderTotalTrackersBlocked()} - {!disableBlocking && this._renderTotalRequestsModified()} - {!disableBlocking && this._renderPageLoadTime()} -
+ {isCondensed && disableBlocking && ( +
+ )} - {isCondensed && disableBlocking && ( -
- )} +
+ {this._renderGhosteryFeature('trust')} + {this._renderGhosteryFeature('restrict', 'Summary__ghosteryFeatureContainer--middle')} + {this._renderPauseButton()} +
+
+ {this._renderCliqzAntiTracking()} + {this._renderCliqzAdBlock()} + {this._renderCliqzSmartBlock()} +
+ {this._renderStatsNavicon()} + {enable_offers && this._renderRewardsNavicon()} -
- {this._renderGhosteryFeature('trust')} - {this._renderGhosteryFeature('restrict', 'Summary__ghosteryFeatureContainer--middle')} - {this._renderPauseButton()} + {!isCondensed && this._renderPlusUpgradeBannerOrSubscriberIcon()}
-
- {this._renderCliqzAntiTracking()} - {this._renderCliqzAdBlock()} - {this._renderCliqzSmartBlock()} -
- {this._renderStatsNavicon()} - {enable_offers && this._renderRewardsNavicon()} - - {!isCondensed && this._renderPlusUpgradeBannerOrSubscriberIcon()}
); } diff --git a/app/panel/containers/SummaryContainer.js b/app/panel/containers/SummaryContainer.js index 826400617..fac24f3ba 100644 --- a/app/panel/containers/SummaryContainer.js +++ b/app/panel/containers/SummaryContainer.js @@ -29,7 +29,8 @@ const mapStateToProps = state => Object.assign({}, state.summary, state.panel, { is_expanded: state.panel.is_expanded, is_expert: state.panel.is_expert, tab_id: state.panel.tab_id, - user: state.account.user + user: state.account.user, + current_theme: state.panel.current_theme, }); /** * Bind Summary view component action creators using Redux's bindActionCreators diff --git a/app/panel/contexts/ThemeContext.js b/app/panel/contexts/ThemeContext.js new file mode 100644 index 000000000..f47652914 --- /dev/null +++ b/app/panel/contexts/ThemeContext.js @@ -0,0 +1,16 @@ +/** + * Theme Context + * + * Ghostery Browser Extension + * https://www.ghostery.com/ + * + * Copyright 2019 Ghostery, Inc. All rights reserved. + * + * 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 React from 'react'; + +export const ThemeContext = React.createContext(null); diff --git a/app/panel/utils/utils.js b/app/panel/utils/utils.js index a92dde970..2e4a07148 100644 --- a/app/panel/utils/utils.js +++ b/app/panel/utils/utils.js @@ -213,17 +213,16 @@ export function doXHR(method, url, query) { * @param {string} theme css of the theme */ export function setTheme(doc, name, account) { - // if themeName is 'default' all we have to do is to remove style element const styleTitlePrefix = 'Ghostery Theme'; - // First remove all other style elements which may be there const styleList = doc.head.getElementsByTagName('style'); - // Other kinds of loops are not supported equally across browsers - for (let i = 0; i < styleList.length; i++) { - const style = styleList[i]; + let themeStyle = null; + // Get style tag for the active theme + // forEach loops are supported equally across browsers + Array.from(styleList).forEach((style) => { if (style.title.startsWith(styleTitlePrefix)) { - doc.head.removeChild(style); + themeStyle = style; } - } + }); if (name !== 'default') { if (!account) { return; } @@ -232,12 +231,20 @@ export function setTheme(doc, name, account) { const { css } = themeData[name]; // Create element for the theme being set, if it is not there - const themeStyle = doc.createElement('style'); - themeStyle.id = name; - themeStyle.title = `${styleTitlePrefix} ${name}`; - - // Set content of style element to the theme text. + if (!themeStyle) { + themeStyle = doc.createElement('style'); + themeStyle.id = name; + themeStyle.title = `${styleTitlePrefix}`; + themeStyle.textContent = css; + doc.head.appendChild(themeStyle); + } themeStyle.textContent = css; - document.head.appendChild(themeStyle); + } else { + // if themeName is 'default' all we have to do is to remove style element + Array.from(styleList).forEach((style) => { + if (style.title.startsWith(styleTitlePrefix)) { + doc.head.removeChild(style); + } + }); } } diff --git a/app/scss/partials/_blocking_category.scss b/app/scss/partials/_blocking_category.scss index 75fdf1930..518a8b212 100644 --- a/app/scss/partials/_blocking_category.scss +++ b/app/scss/partials/_blocking_category.scss @@ -62,20 +62,21 @@ border-top: 0; padding-top: 0; } - .caret-down { @extend %pointer; display: inline-block; - width: 40px; - height: 20px; - background: url("../../app/images/panel/caret-down.svg") no-repeat center; + margin: 0 15px 4px 0; + path { + fill: $tundora; + } } .caret-up { @extend %pointer; display: inline-block; - width: 40px; - height: 20px; - background: url("../../app/images/panel/caret-up.svg") no-repeat center; + margin: 0 15px 4px 0; + path { + fill: $tundora; + } } .checkbox-container { display: inline-block; diff --git a/app/scss/partials/_blocking_tracker.scss b/app/scss/partials/_blocking_tracker.scss index a238516aa..9f4e9c58e 100644 --- a/app/scss/partials/_blocking_tracker.scss +++ b/app/scss/partials/_blocking_tracker.scss @@ -45,9 +45,20 @@ // vertical alignment with text label position: relative; top: -2px; - display: inline-block; padding-right: 6px; + &.ads-icon.default > g { + .inner-background { + fill: #FFF; + } + path:nth-child(2) { + stroke: #1DAFED; + } + } + &.cookies-and-fingerprints-icon.default > path { + stroke: #1DAFED; + fill: #FFF; + } } .trk-cliqz-stat { color: #1dafed; @@ -143,6 +154,7 @@ align-items: center; justify-content: space-between; + &:not(.whitelisted) { .cliqz-tracker-trust { visibility: hidden; @@ -152,8 +164,29 @@ .trust-circle { stroke: #9B9B9B; } } + .cliqz-tracker-trust > g > path:nth-child(1) { + stroke: #00AEF0; + } + .cliqz-tracker-trust > g > path:nth-child(2) { + fill: #00AEF0; + } + .cliqz-tracker-scrub > g > .border { + fill: #FFF; + stroke: #00AEF0; + } + .cliqz-tracker-scrub > g > .background { + fill: #00AEF0; + stroke: #FFF; + } + .cliqz-tracker-scrub { pointer-events: none; + .background.protected { + fill: #00AEF0; + } + .background.restricted { + fill: #00AEF0; + } } } diff --git a/app/scss/partials/_stats_graph.scss b/app/scss/partials/_stats_graph.scss index a6d087809..5a19435e2 100644 --- a/app/scss/partials/_stats_graph.scss +++ b/app/scss/partials/_stats_graph.scss @@ -119,4 +119,13 @@ so that you can support switching between midnight theme and regular theme */ background-image: buildRightCaret(#00AEF0); background-repeat: no-repeat; } + .point-group { + .default, .midnight-theme { + fill: #124559; + } + } + .line.default, + .line.midnight-theme { + fill: #124559; + } } diff --git a/app/shared-components/Modal/Modal.scss b/app/shared-components/Modal/Modal.scss index 518bcb9fe..eeb0e48b9 100644 --- a/app/shared-components/Modal/Modal.scss +++ b/app/shared-components/Modal/Modal.scss @@ -19,8 +19,9 @@ right: 0; bottom: 0; left: 0; + z-index: 2147483647; } .Modal__background { background-color: rgba(#000000, 0.25); - z-index: 9; } + diff --git a/manifest.json b/manifest.json index c3e8075bd..7cad3c9d9 100644 --- a/manifest.json +++ b/manifest.json @@ -7,7 +7,6 @@ "version_name": "8.5.0", "default_locale": "en", "description": "__MSG_short_description__", - "debug": true, "log": true, "icons": { "16": "app/images/icon16.png",