diff --git a/app/panel/components/Subscription/SubscriptionThemes.jsx b/app/panel/components/Subscription/SubscriptionThemes.jsx
index 3a00f0282..046d77445 100644
--- a/app/panel/components/Subscription/SubscriptionThemes.jsx
+++ b/app/panel/components/Subscription/SubscriptionThemes.jsx
@@ -12,36 +12,45 @@
*/
import React from 'react';
-import { ToggleSlider } from '../BuildingBlocks';
+import PropTypes from 'prop-types';
+import { RadioButtonGroup } from '../BuildingBlocks';
/**
* @class Implement Themes subview as a React component.
* The view opens from the left-side menu of the main Subscription view.
- * It allows to switch between available Ghostery themes. Right now it handles just one theme. Hence - slider.
+ * It allows to switch between available Ghostery themes.
* @memberOf SettingsComponents
- */
-const SubscriptionThemes = props => (
-
-
-
-
{ t('subscription_themes_title') }
-
-
-
- {t('subscription_midnight_theme')}
-
-
-
-

-
+*/
+const SubscriptionThemes = (props) => {
+ 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 = () => themes.indexOf(props.theme);
+
+ const handleThemeClick = index => props.changeTheme(themes[index]);
+
+ return (
+
+
+
+
{t('subscription_themes_title')}
+
+
+
-
-);
+ );
+};
+
+// PropTypes ensure we pass required props of the correct type
+SubscriptionThemes.propTypes = {
+ changeTheme: PropTypes.func.isRequired,
+};
+
export default SubscriptionThemes;
diff --git a/app/panel/components/Summary.jsx b/app/panel/components/Summary.jsx
index cb5a679a5..35ba61ba9 100644
--- a/app/panel/components/Summary.jsx
+++ b/app/panel/components/Summary.jsx
@@ -623,6 +623,8 @@ class Summary extends React.Component {
const {
enable_anti_tracking,
is_expert,
+ current_theme,
+ antiTracking,
} = this.props;
const isCondensed = this._isCondensed();
@@ -638,6 +640,8 @@ class Summary extends React.Component {
isTooltipHeader={is_expert}
isTooltipBody={!isCondensed}
tooltipPosition={isCondensed ? 'right' : is_expert ? 'top top-right' : 'top'}
+ current_theme={current_theme}
+ trackerCount={antiTracking.trackerCount}
/>
);
@@ -647,6 +651,8 @@ class Summary extends React.Component {
const {
enable_ad_block,
is_expert,
+ current_theme,
+ adBlock,
} = this.props;
const isCondensed = this._isCondensed();
@@ -662,6 +668,8 @@ class Summary extends React.Component {
isTooltipHeader={is_expert}
isTooltipBody={!isCondensed}
tooltipPosition={isCondensed ? 'right' : 'top'}
+ current_theme={current_theme}
+ trackerCount={adBlock.trackerCount}
/>
);
@@ -671,6 +679,8 @@ class Summary extends React.Component {
const {
enable_smart_block,
is_expert,
+ current_theme,
+ smartBlock
} = this.props;
const isCondensed = this._isCondensed();
@@ -686,6 +696,8 @@ class Summary extends React.Component {
isTooltipHeader={is_expert}
isTooltipBody={!isCondensed}
tooltipPosition={isCondensed ? 'right' : is_expert ? 'top top-left' : 'top'}
+ current_theme={current_theme}
+ trackerCount={Object.keys(smartBlock.blocked).length}
/>
);
@@ -779,6 +791,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 +800,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/StatsContainer.js b/app/panel/containers/StatsContainer.js
index 5819bd7f7..2a9033a07 100644
--- a/app/panel/containers/StatsContainer.js
+++ b/app/panel/containers/StatsContainer.js
@@ -22,7 +22,7 @@ import Stats from '../components/Stats';
* @todo We are not using ownProps, so we better not specify it explicitly,
* in this case it won't be passed by React (see https://github.com/reactjs/react-redux/blob/master/docs/api.md).
*/
-const mapStateToProps = state => Object.assign({}, state.account);
+const mapStateToProps = state => Object.assign({}, state.account, {});
/**
* Connects Subscription view component to the Redux store. Pass updated match, location, and history props to the wrapped component.
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..b925d71d3 100644
--- a/app/panel/utils/utils.js
+++ b/app/panel/utils/utils.js
@@ -218,10 +218,11 @@ export function setTheme(doc, name, account) {
// 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
+ let themeStyle = null;
for (let i = 0; i < styleList.length; i++) {
const style = styleList[i];
if (style.title.startsWith(styleTitlePrefix)) {
- doc.head.removeChild(style);
+ themeStyle = style;
}
}
@@ -232,12 +233,21 @@ 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.
- themeStyle.textContent = css;
- document.head.appendChild(themeStyle);
+ if (!themeStyle) {
+ themeStyle = doc.createElement('style');
+ themeStyle.id = name;
+ themeStyle.title = `${styleTitlePrefix}`;
+ themeStyle.textContent = css;
+ doc.head.appendChild(themeStyle);
+ } else {
+ themeStyle.textContent = css;
+ }
+ } else {
+ for (let i = 0; i < styleList.length; i++) {
+ const style = styleList[i];
+ if (style.title.startsWith(styleTitlePrefix)) {
+ doc.head.removeChild(style);
+ }
+ }
}
}
diff --git a/app/scss/panel.scss b/app/scss/panel.scss
index eaef66ef7..21ab2f760 100644
--- a/app/scss/panel.scss
+++ b/app/scss/panel.scss
@@ -65,6 +65,7 @@ html body {
@import './partials/_account';
@import './partials/_drawer';
@import './partials/_toggle_slider';
+@import './partials/_radio_button';
@import './partials/_pause_button';
@import './partials/_donut_graph';
@import './partials/_ghostery_feature';
@@ -76,6 +77,7 @@ html body {
@import './partials/_stats_graph';
@import './partials/_modal_exit_button';
@import './partials/insights_promo_modal.scss';
+// @import './themes/theme.scss';
// Imports from ../shared-components directory
@import '../shared-components/Modal/Modal.scss';
diff --git a/app/scss/partials/_radio_button.scss b/app/scss/partials/_radio_button.scss
new file mode 100644
index 000000000..51ddeb4bb
--- /dev/null
+++ b/app/scss/partials/_radio_button.scss
@@ -0,0 +1,42 @@
+/**
+ * Radio Button Sass
+ *
+ * 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
+ */
+
+.RadioButtonGroup__label {
+ margin-right: 10px;
+ font-weight: bolder;
+}
+.RadioButtonGroup__container {
+ margin: 16px 0;
+ width: 138px;
+}
+.RadioButton__outerCircle {
+ border: 1px solid #4a4a4a;
+ height: 16px;
+ width: 16px;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ box-sizing: border-box;
+ &.checked {
+ border: 2px solid #2092bf;
+ }
+}
+.RadioButton__innerCircle {
+ &.checked {
+ height: 8px;
+ width: 8px;
+ background-color: #2092bf;
+ border-radius: 50%;
+ }
+}
diff --git a/app/scss/partials/_settings.scss b/app/scss/partials/_settings.scss
index dccf04282..96981e5aa 100644
--- a/app/scss/partials/_settings.scss
+++ b/app/scss/partials/_settings.scss
@@ -5,7 +5,7 @@
* 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
@@ -58,7 +58,6 @@
text-align: left;
}
}
-
.s-tab-title {
font-weight: 400;
font-size: 18px;
@@ -195,12 +194,11 @@
line-height: 1.4;
}
img.s-question {
- top: -5px;
- width: 14px;
+ width: 16px;
height: auto;
cursor: default;
- vertical-align: top;
opacity: 1.0;
+ margin: -6px 0 0 10px;
}
.s-option-group {
margin-bottom: 12px;
@@ -279,7 +277,7 @@
border-color: #CCCCCC;
background-size: 8px 5px;
-moz-appearance: none;
- -webkit-appearance:none;
+ -webkit-appearance: none;
}
}
#select-file {
@@ -458,4 +456,4 @@
.blocking-trackers {
margin-left: 25px;
}
-}
\ No newline at end of file
+}
diff --git a/app/scss/partials/_subscribe.scss b/app/scss/partials/_subscribe.scss
index 11e7375e4..c771b102d 100644
--- a/app/scss/partials/_subscribe.scss
+++ b/app/scss/partials/_subscribe.scss
@@ -83,17 +83,16 @@
-webkit-font-smoothing: antialiased;
text-align: left;
}
- .themes-slider-label {
- @extend .field-label;
- padding-right: 20px !important;
- }
- .themes-slider-container {
- padding-top: 10px !important;
- }
.column-subscription {
padding-left: 30px !important;
padding-right: 30px !important;
}
+ .subscription-title {
+ display: inline;
+ }
+ .tooltip-icon {
+ margin-left: 5px !important;
+ }
.status-label {
@extend .field-label;
padding-right: 7px !important;
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 fdc3ff517..af877af3d 100644
--- a/manifest.json
+++ b/manifest.json
@@ -103,4 +103,4 @@
"cliqz/offers-reminder/index.html",
"cliqz/popup-notification/images/*"
]
-}
\ No newline at end of file
+}