diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 05d4804bc..c65f1e1c6 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -1593,6 +1593,60 @@ "setup_upgrade_button_go": { "message": "Set Me Up" }, + "hub_home_page_title": { + "message": "Ghostery Hub - Home" + }, + "hub_home_header_text": { + "message": "Ghostery is ready!" + }, + "hub_home_header_tagline": { + "message": "You are now protected with our default settings." + }, + "hub_home_header_tagline_2": { + "message": "Start Browsing!" + }, + "hub_home_header_checkbox_label": { + "message": "Share analytics and Human Web data to improve Ghostery’s performance. " + }, + "hub_home_header_checkbox_link": { + "message": "Learn More." + }, + "hub_home_subheader_optimize": { + "message": "Optimize your Ghostery experience" + }, + "hub_home_subheader_create_account": { + "message": "Create Account" + }, + "hub_home_feature_tutorial_title": { + "message": "Take a Tutorial" + }, + "hub_home_feature_tutorial_text": { + "message": "Walk through Ghostery's main features." + }, + "hub_home_feature_tutorial_button": { + "message": "Start" + }, + "hub_home_feature_tutorial_button_alt": { + "message": "Tutorial Complete" + }, + "hub_home_feature_setup_title": { + "message": "Customize Setup" + }, + "hub_home_feature_setup_text": { + "message": "Edit your settings and blocking preferences." + }, + "hub_home_feature_setup_button": { + "message": "Edit Setup" + }, + "hub_home_feature_setup_button_alt": { + "message": "Custom Setup Complete" + }, + "hub_home_feature_supporter_text": { + "message": "Become a Ghostery Supporter and unlock special features." + }, + "hub_home_feature_supporter_button": { + "message": "Become a Supporter" + }, "hub_setup_page_title": { "message": "Ghostery Hub - Setup" }, diff --git a/app/hub/components/App.jsx b/app/hub/App.jsx similarity index 89% rename from app/hub/components/App.jsx rename to app/hub/App.jsx index 1dd755e9a..cee8caa14 100644 --- a/app/hub/components/App.jsx +++ b/app/hub/App.jsx @@ -14,7 +14,7 @@ */ import React, { Component } from 'react'; -import SideNavigation from './SideNavigation'; +import SideNavigation from './Views/SideNavigationView'; /** * @class Implements the container App for the Ghostery Hub @@ -33,10 +33,10 @@ class App extends Component { location: 'list', type: 'link', href: '/', icon: 'home', text: 'Home' }, { - location: 'list', type: 'link', href: '/setup/1', icon: 'home', text: 'Customize Setup' + location: 'list', type: 'link', href: '/setup', icon: 'home', text: 'Customize Setup' }, { - location: 'list', type: 'link', href: '/tutorial/1', icon: 'home', text: 'Visit Tutorial' + location: 'list', type: 'link', href: '/tutorial', icon: 'home', text: 'Visit Tutorial' }, { location: 'list', type: 'link', href: '/supporter', icon: 'home', text: 'Become a Ghostery Supporter' diff --git a/app/hub/Views/HomeView/HomeView.jsx b/app/hub/Views/HomeView/HomeView.jsx index 1dc179ba9..584ca0d06 100644 --- a/app/hub/Views/HomeView/HomeView.jsx +++ b/app/hub/Views/HomeView/HomeView.jsx @@ -9,42 +9,137 @@ * 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 - * - * ToDo: Update this file. */ -import React, { Component } from 'react'; +import React from 'react'; +import ClassNames from 'classnames'; +import PropTypes from 'prop-types'; +import { NavLink } from 'react-router-dom'; +import { ToggleCheckbox } from '../../../shared-components'; /** - * @class Implement the Home View for the Ghostery Hub - * @extends Component + * A Functional React component for rendering the Home View + * @return {JSX} JSX for rendering the Home View of the Hub app * @memberof HubComponents */ -class HomeView extends Component { - constructor(props) { - super(props); - - this.state = { - title: '' - }; - } +const HomeView = (props) => { + const { + justInstalled, + setup_complete, + tutorial_complete, + enable_human_web, + changeHumanWeb, + account_text, + account_link, + } = props; + const tutorialFeatureClassNames = ClassNames('HomeView__onboardingFeature columns flex-container align-middle flex-dir-column', { + 'feature-tutorial-complete': tutorial_complete, + 'feature-tutorial': !tutorial_complete, + }); + const tutorialButtonClassNames = ClassNames('HomeView__featureButton button primary', { + hollow: tutorial_complete, + }); + const setupFeatureClassNames = ClassNames('HomeView__onboardingFeature columns flex-container align-middle flex-dir-column', { + 'feature-setup-complete': setup_complete, + 'feature-setup': !setup_complete, + }); + const setupButtonClassNames = ClassNames('HomeView__featureButton button primary', { + hollow: setup_complete, + }); - /** - * Lifecycle Event - */ - componentWillMount() { - const { title } = this.state; - window.document.title = title; - } + return ( +
+
+
+ +
+

+ {t('hub_home_header_text')} +

+ {justInstalled && ( +
+ {t('hub_home_header_tagline')} +
+ )} +
+ {t('hub_home_header_tagline_2')} +
+
+ +
+ {t('hub_home_header_checkbox_label')} + + {t('hub_home_header_checkbox_link')} + +
+
+
+
+
+
+ {t('hub_home_subheader_optimize')} +
+ + {account_text} + +
+
+
+
+
+ {t('hub_home_feature_tutorial_title')} +
+
+ {t('hub_home_feature_tutorial_text')} +
+ + {tutorial_complete ? t('hub_home_feature_tutorial_button_alt') : t('hub_home_feature_tutorial_button')} + +
+
+
+
+
+ {t('hub_home_feature_setup_title')} +
+
+ {t('hub_home_feature_setup_text')} +
+ + {setup_complete ? t('hub_home_feature_setup_button_alt') : t('hub_home_feature_setup_button')} + +
+
+
+
+
+ {t('hub_home_feature_supporter_text')} +
+
+
+ + {t('hub_home_feature_supporter_button')} + +
+
+
+
+
+ ); +}; - /** - * React's required render function. Returns JSX - * @return {JSX} JSX for rendering the Home View of the Hub app - */ - render() { - const { title } = this.state; - return
{title}
; - } -} +// PropTypes ensure we pass required props of the correct type +HomeView.propTypes = { + justInstalled: PropTypes.bool.isRequired, + setup_complete: PropTypes.bool.isRequired, + tutorial_complete: PropTypes.bool.isRequired, + enable_human_web: PropTypes.bool.isRequired, + changeHumanWeb: PropTypes.func.isRequired, + account_text: PropTypes.string.isRequired, + account_link: PropTypes.string.isRequired, +}; export default HomeView; diff --git a/app/hub/Views/HomeView/HomeView.scss b/app/hub/Views/HomeView/HomeView.scss new file mode 100644 index 000000000..b94472f63 --- /dev/null +++ b/app/hub/Views/HomeView/HomeView.scss @@ -0,0 +1,121 @@ +/** + * Home View Sass + * + * Ghostery Browser Extension + * https://www.ghostery.com/ + * + * Copyright 2018 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 + */ + +// Home View +.HomeView { + padding-top: 45px; + padding-bottom: 25px; + color: #4a4a4a; +} +.HomeView--bolded { + font-weight: 700; +} +.HomeView__header img { + max-width: 156px; + margin-right: 23px; +} +.HomeView__header h1 { + color: #4a4a4a; + margin-bottom: 19px; +} +.HomeView__headerTagline { + font-size: 20px; + line-height: 38px; + color: #4a4a4a; +} +.HomeView__supportContainer { + margin-top: 12px; + margin-left: -12px; +} +.HomeView__subHeader { + margin-top: 12px; + font-size: 16px; + font-weight: 500; + line-height: 32px; +} +.HomeView__subHeader a { + text-decoration: underline; +} +.HomeView__onboarding { + margin-top: 7px; + padding: 27px 12px 30px; + border-radius: 4px; + background-color: #f0f3f4; +} +.HomeView__onboardingFeature { + margin: 0 8px; + padding: 0 36px; +} +.HomeView__onboardingFeatureDivider { + margin: 7px 0 4px; + width: 1px; + padding: 0; + background-color: #979797; +} +.HomeView__supporter { + margin-top: 22px; + padding: 22px 12px; + background-color: #f0f3f4; + border-radius: 4px; +} +.HomeView__supporterFeature { + padding: 0 36px; +} +.HomeView__featureIcon { + height: 50px; + width: 50px; + margin: 0 24px; + background-repeat: no-repeat; + background-position: center center; +} +.HomeView__onboardingFeature.feature-tutorial .HomeView__featureIcon { + background-image: buildIconWand(#4a4a4a); +} +.HomeView__onboardingFeature.feature-tutorial-complete .HomeView__featureIcon { + width: 100px; + margin: 0 0 0 50px; + background-image: buildIconWandCheck(#4a4a4a); +} +.HomeView__onboardingFeature.feature-setup .HomeView__featureIcon { + background-image: buildIconClipboard(#4a4a4a); +} +.HomeView__onboardingFeature.feature-setup-complete .HomeView__featureIcon { + width: 100px; + margin: 0 0 0 50px; + background-image: buildIconClipboardCheck(#4a4a4a); +} +.HomeView__featureIcon.feature-supporter { + background-image: buildIconFlag(#4a4a4a); +} +.HomeView__featureTitle { + text-align: center; + font-size: 18px; + padding: 22px 0 10px; +} +.HomeView__featureText { + text-align: center; + font-size: 14px; + line-height: 27px; +} +.HomeView__featureButton { + margin: 0; + font-size: 14px; + font-weight: 700; + line-height: 1.3; + text-transform: uppercase; + box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.24), 0 0 2px 0 rgba(0, 0, 0, 0.12); +} +.HomeView__onboarding .HomeView__featureButton { + margin-top: 26px; + min-width: 130px; +} diff --git a/app/hub/Views/HomeView/HomeViewActions.js b/app/hub/Views/HomeView/HomeViewActions.js new file mode 100644 index 000000000..150748103 --- /dev/null +++ b/app/hub/Views/HomeView/HomeViewActions.js @@ -0,0 +1,41 @@ +/** + * Home View Action creators + * + * Ghostery Browser Extension + * https://www.ghostery.com/ + * + * Copyright 2018 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 { log, sendMessageInPromise } from '../../utils'; +import { GET_HOME_PROPS, SET_HUMAN_WEB } from './HomeViewConstants'; + +export function getHomeProps() { + return function (dispatch) { + return sendMessageInPromise(GET_HOME_PROPS).then((data) => { + dispatch({ + type: GET_HOME_PROPS, + data, + }); + }).catch((err) => { + log('homeView Action getHomeProps Error', err); + }); + }; +} + +export function setHumanWeb(actionData) { + return function (dispatch) { + return sendMessageInPromise(SET_HUMAN_WEB, actionData).then((data) => { + dispatch({ + type: SET_HUMAN_WEB, + data, + }); + }).catch((err) => { + log('homeView Action setHumanWeb Error', err); + }); + }; +} diff --git a/app/hub/Views/TutorialViews/TutorialSimpleDetailedView/index.js b/app/hub/Views/HomeView/HomeViewConstants.js similarity index 64% rename from app/hub/Views/TutorialViews/TutorialSimpleDetailedView/index.js rename to app/hub/Views/HomeView/HomeViewConstants.js index 41d76d8af..458c93d53 100644 --- a/app/hub/Views/TutorialViews/TutorialSimpleDetailedView/index.js +++ b/app/hub/Views/HomeView/HomeViewConstants.js @@ -1,5 +1,5 @@ /** - * Point of entry index.js file for Tutorial Simple or Detailed View + * Custom Home Constants * * Ghostery Browser Extension * https://www.ghostery.com/ @@ -11,6 +11,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0 */ -import TutorialSimpleDetailedView from './TutorialSimpleDetailedView'; - -export default TutorialSimpleDetailedView; +// Home View +export const GET_HOME_PROPS = 'GET_HOME_PROPS'; +export const SET_HUMAN_WEB = 'SET_HUMAN_WEB'; diff --git a/app/hub/Views/HomeView/HomeViewContainer.jsx b/app/hub/Views/HomeView/HomeViewContainer.jsx new file mode 100644 index 000000000..d131ed431 --- /dev/null +++ b/app/hub/Views/HomeView/HomeViewContainer.jsx @@ -0,0 +1,100 @@ +/** + * Tutorial View Container + * + * Ghostery Browser Extension + * https://www.ghostery.com/ + * + * Copyright 2018 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'; +import PropTypes from 'prop-types'; +import QueryString from 'query-string'; +import HomeView from './HomeView'; + +/** + * @class Implement the Home View Container for the Ghostery Hub + * @extends Component + * @memberof HubContainers + */ +class HomeViewContainer extends React.Component { + constructor(props) { + super(props); + + const { justInstalled } = QueryString.parse(window.location.search); + this.state = { + justInstalled, + }; + + const title = t('hub_home_page_title'); + window.document.title = title; + + this.props.actions.getHomeProps(); + } + + /** + * Function to handle toggling Human Web Opt-In + */ + _handleToggleHumanWeb = () => { + const enable_human_web = !this.props.home.enable_human_web; + this.props.actions.setHumanWeb({ enable_human_web }); + } + + /** + * React's required render function. Returns JSX + * @return {JSX} JSX for rendering the Home View of the Hub app + */ + render() { + // ToDo: Get these from action, reducer and props. Will be on this.props.home + // These are passed as props so we can the user's email and link to their account when they are signed in + const account_text = t('hub_home_subheader_create_account'); + const account_link = '/create-account'; + + const { justInstalled } = this.state; + const { + setup_complete, + tutorial_complete, + enable_human_web, + } = this.props.home; + const childProps = { + justInstalled, + setup_complete, + tutorial_complete, + enable_human_web, + changeHumanWeb: this._handleToggleHumanWeb, + account_text, + account_link, + }; + + return ; + } +} + +// PropTypes ensure we pass required props of the correct type +// Note: isRequired is not needed when a prop has a default value +HomeViewContainer.propTypes = { + home: PropTypes.shape({ + setup_complete: PropTypes.bool, + tutorial_complete: PropTypes.bool, + enable_human_web: PropTypes.bool, + account_text: PropTypes.string, + account_link: PropTypes.string, + }), +}; + +// Default props used throughout the Setup flow +HomeViewContainer.defaultProps = { + home: { + setup_complete: false, + tutorial_complete: false, + enable_human_web: false, + account_text: '', + account_link: '', + }, +}; + +export default HomeViewContainer; diff --git a/app/hub/Views/HomeView/HomeViewReducer.js b/app/hub/Views/HomeView/HomeViewReducer.js new file mode 100644 index 000000000..05a34d962 --- /dev/null +++ b/app/hub/Views/HomeView/HomeViewReducer.js @@ -0,0 +1,49 @@ +/** + * Reducer used in the Home View + * + * Ghostery Browser Extension + * https://www.ghostery.com/ + * + * Copyright 2018 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 { GET_HOME_PROPS, SET_HUMAN_WEB } from './HomeViewConstants'; + +const initialState = {}; + +function HomeViewReducer(state = initialState, action) { + switch (action.type) { + case GET_HOME_PROPS: { + const { + setup_complete, + tutorial_complete, + enable_human_web, + account_text, + account_link, + } = action.data; + return Object.assign({}, state, { + home: Object.assign({}, state.home, { + setup_complete, + tutorial_complete, + enable_human_web, + account_text, + account_link, + }), + }); + } + case SET_HUMAN_WEB: { + const { enable_human_web } = action.data; + return Object.assign({}, state, { + home: Object.assign({}, state.home, { enable_human_web }), + }); + } + + default: return state; + } +} + +export default HomeViewReducer; diff --git a/app/hub/Views/HomeView/__tests__/HomeView.test.jsx b/app/hub/Views/HomeView/__tests__/HomeView.test.jsx new file mode 100644 index 000000000..af4286485 --- /dev/null +++ b/app/hub/Views/HomeView/__tests__/HomeView.test.jsx @@ -0,0 +1,93 @@ +/** + * Setup Anti-Suite View Test Component + * + * Ghostery Browser Extension + * https://www.ghostery.com/ + * + * Copyright 2018 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'; +import renderer from 'react-test-renderer'; +import { shallow } from 'enzyme'; +import HomeView from '../HomeView'; +import HomeViewActions from '../HomeViewActions'; +import { MemoryRouter } from 'react-router'; +import { ToggleCheckbox } from '../../../../shared-components'; + + +describe('app/hub/Views/HomeView component', () => { + describe('Snapshot tests with react-test-renderer', () => { + test('home view is rendered correctly: all true', () => { + const initialState = { + justInstalled: true, + setup_complete: true, + tutorial_complete: true, + enable_human_web: true, + changeHumanWeb: () => {}, + account_text: 'test username', + account_link: '/test/url', + }; + + const component = renderer.create( + + + + ).toJSON(); + expect(component).toMatchSnapshot(); + }); + + test('home view is rendered correctly: all false', () => { + const initialState = { + justInstalled: false, + setup_complete: false, + tutorial_complete: false, + enable_human_web: false, + changeHumanWeb: () => {}, + account_text: 'test username', + account_link: '/test/url', + }; + + const component = renderer.create( + + + ).toJSON(); + expect(component).toMatchSnapshot(); + }); + }); + + describe('Shallow snapshot tests rendered with Enzyme', () => { + test('the happy path of the component', () => { + const initialState = { + justInstalled: true, + setup_complete: false, + tutorial_complete: false, + enable_human_web: false, + changeHumanWeb: jest.fn(), + account_text: 'test username', + account_link: '/test/url', + }; + + const component = shallow(); + expect(component.find('.HomeView').length).toBe(1); + expect(component.find('.button').length).toBe(3); + expect(component.find('.HomeView__header').length).toBe(1); + expect(component.find('.HomeView__subHeader').length).toBe(1); + + expect(component.find('.HomeView__headerTagline').length).toBe(2); + component.setProps({ justInstalled: false }); + expect(component.find('.HomeView__headerTagline').length).toBe(1); + + expect(component.find('.HomeView__featureButton.hollow').length).toBe(0); + component.setProps({ tutorial_complete: true }); + expect(component.find('.HomeView__featureButton.hollow').length).toBe(1); + + component.setProps({ setup_complete: true }); + expect(component.find('.HomeView__featureButton.hollow').length).toBe(2); + }); + }); +}); diff --git a/app/hub/Views/HomeView/__tests__/__snapshots__/HomeView.test.jsx.snap b/app/hub/Views/HomeView/__tests__/__snapshots__/HomeView.test.jsx.snap new file mode 100644 index 000000000..123d25711 --- /dev/null +++ b/app/hub/Views/HomeView/__tests__/__snapshots__/HomeView.test.jsx.snap @@ -0,0 +1,336 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`app/hub/Views/HomeView component Snapshot tests with react-test-renderer home view is rendered correctly: all false 1`] = ` +
+
+
+ +
+

+ hub_home_header_text +

+
+ hub_home_header_tagline_2 +
+
+
+ + + +
+
+ + hub_home_header_checkbox_label + + + hub_home_header_checkbox_link + +
+
+
+
+
+
+ hub_home_subheader_optimize +
+ + test username + +
+
+
+
+
+ hub_home_feature_tutorial_title +
+
+ hub_home_feature_tutorial_text +
+ + hub_home_feature_tutorial_button + +
+
+
+
+
+ hub_home_feature_setup_title +
+
+ hub_home_feature_setup_text +
+ + hub_home_feature_setup_button + +
+
+
+
+
+ hub_home_feature_supporter_text +
+ +
+
+
+`; + +exports[`app/hub/Views/HomeView component Snapshot tests with react-test-renderer home view is rendered correctly: all true 1`] = ` +
+
+
+ +
+

+ hub_home_header_text +

+
+ hub_home_header_tagline +
+
+ hub_home_header_tagline_2 +
+
+
+ + + +
+
+ + hub_home_header_checkbox_label + + + hub_home_header_checkbox_link + +
+
+
+
+
+
+ hub_home_subheader_optimize +
+ + test username + +
+
+
+
+
+ hub_home_feature_tutorial_title +
+
+ hub_home_feature_tutorial_text +
+ + hub_home_feature_tutorial_button_alt + +
+
+
+
+
+ hub_home_feature_setup_title +
+
+ hub_home_feature_setup_text +
+ + hub_home_feature_setup_button_alt + +
+
+
+
+
+ hub_home_feature_supporter_text +
+ +
+
+
+`; diff --git a/app/hub/Views/HomeView/index.js b/app/hub/Views/HomeView/index.js index 57c9415fc..3997c15b2 100644 --- a/app/hub/Views/HomeView/index.js +++ b/app/hub/Views/HomeView/index.js @@ -11,6 +11,31 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0 */ -import HomeView from './HomeView'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; -export default HomeView; +import HomeViewContainer from './HomeViewContainer'; +import HomeViewReducer from './HomeViewReducer'; +import * as HomeViewActions from './HomeViewActions'; + +/** + * Map redux store state properties to the component's own properties. + * @param {Object} state entire Redux store's state + * @return {function} this function returns a plain object, which will be merged into the component's props + * @memberof HubContainers + */ +const mapStateToProps = state => Object.assign({}, state.home); + +/** + * Bind the component's action creators using Redux's bindActionCreators. + * @param {function} dispatch redux store method which dispatches actions + * @return {function} to be used as an argument in redux connect call + * @memberof SetupContainers + */ +const mapDispatchToProps = dispatch => ({ + actions: bindActionCreators(Object.assign(HomeViewActions), dispatch), +}); + +export const reducer = HomeViewReducer; + +export default connect(mapStateToProps, mapDispatchToProps)(HomeViewContainer); diff --git a/app/hub/Views/SetupView/SetupView.jsx b/app/hub/Views/SetupView/SetupView.jsx index 79ed1eefa..c9bfdba04 100644 --- a/app/hub/Views/SetupView/SetupView.jsx +++ b/app/hub/Views/SetupView/SetupView.jsx @@ -24,26 +24,33 @@ import SetupHeader from '../SetupViews/SetupHeader'; * @return {JSX} JSX for rendering the Setup View of the Hub app * @memberof HubComponents */ -const SetupView = props => ( -
-
- {props.steps.map(step => ( - ( -
- - -
- )} - /> - ))} -
+const SetupView = (props) => { + const { + steps, + sendMountActions, + } = props; + + return ( +
+
+ {steps.map(step => ( + ( +
+ + +
+ )} + /> + ))} +
- -
-); + +
+ ); +}; // PropTypes ensure we pass required props of the correct type SetupView.propTypes = { @@ -56,6 +63,7 @@ SetupView.propTypes = { titleImage: PropTypes.string.isRequired, }).isRequired, })).isRequired, + sendMountActions: PropTypes.bool.isRequired, }; export default SetupView; diff --git a/app/hub/Views/SetupView/SetupViewConstants.js b/app/hub/Views/SetupView/SetupViewConstants.js index b81dbb44c..65ee03a3f 100644 --- a/app/hub/Views/SetupView/SetupViewConstants.js +++ b/app/hub/Views/SetupView/SetupViewConstants.js @@ -32,3 +32,6 @@ export const SET_GHOSTERY_REWARDS = 'SET_GHOSTERY_REWARDS'; // Setup Human Web export const SET_HUMAN_WEB = 'SET_HUMAN_WEB'; + +// Setup Done +export const SET_SETUP_COMPLETE = 'SET_SETUP_COMPLETE'; diff --git a/app/hub/Views/SetupView/SetupViewContainer.jsx b/app/hub/Views/SetupView/SetupViewContainer.jsx index e4b3bf955..e05610600 100644 --- a/app/hub/Views/SetupView/SetupViewContainer.jsx +++ b/app/hub/Views/SetupView/SetupViewContainer.jsx @@ -12,7 +12,7 @@ */ import React from 'react'; -import { NavLink } from 'react-router-dom'; +import { NavLink, withRouter } from 'react-router-dom'; import QueryString from 'query-string'; import PropTypes from 'prop-types'; import SetupView from './SetupView'; @@ -26,7 +26,7 @@ import SetupHumanWebView from '../SetupViews/SetupHumanWebView'; import SetupDoneView from '../SetupViews/SetupDoneView'; /** - * @class Implement the Setup Container View for the Ghostery Hub + * @class Implement the Setup View for the Ghostery Hub * @extends Component * @memberof HubContainers */ @@ -37,14 +37,10 @@ class SetupViewContainer extends React.Component { sendMountActions: false, showModal: false, }; - } - /** - * Lifecycle Event - */ - componentWillMount() { - const title = t('hub_setup_page_title'); + this.props.history.push('/setup/1'); + const title = t('hub_setup_page_title'); window.document.title = title; this.props.actions.initSetupProps(this.props.setup); @@ -267,4 +263,4 @@ SetupViewContainer.defaultProps = { }, }; -export default SetupViewContainer; +export default withRouter(SetupViewContainer); diff --git a/app/hub/Views/SetupViews/SetupDoneView/SetupDoneViewActions.js b/app/hub/Views/SetupViews/SetupDoneView/SetupDoneViewActions.js new file mode 100644 index 000000000..4d7383305 --- /dev/null +++ b/app/hub/Views/SetupViews/SetupDoneView/SetupDoneViewActions.js @@ -0,0 +1,28 @@ +/** + * Setup Done View Action creators + * + * Ghostery Browser Extension + * https://www.ghostery.com/ + * + * Copyright 2018 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 { log, sendMessageInPromise } from '../../../utils'; +import { SET_SETUP_COMPLETE } from '../../SetupView/SetupViewConstants'; + +export function setSetupComplete() { + return function (dispatch) { + return sendMessageInPromise(SET_SETUP_COMPLETE).then((data) => { + dispatch({ + type: SET_SETUP_COMPLETE, + data, + }); + }).catch((err) => { + log('setupDone Action setSetupComplete Error', err); + }); + }; +} diff --git a/app/hub/Views/SetupViews/SetupDoneView/SetupDoneViewContainer.jsx b/app/hub/Views/SetupViews/SetupDoneView/SetupDoneViewContainer.jsx index b1f1779a6..8abe362bb 100644 --- a/app/hub/Views/SetupViews/SetupDoneView/SetupDoneViewContainer.jsx +++ b/app/hub/Views/SetupViews/SetupDoneView/SetupDoneViewContainer.jsx @@ -28,7 +28,7 @@ class SetupDoneViewContainer extends Component { const title = t('hub_setup_page_title_done'); window.document.title = title; - const { index } = this.props; + const { index, sendMountActions } = this.props; this.props.actions.setSetupNavigation({ activeIndex: index, hrefPrev: `/setup/${index - 1}`, @@ -38,6 +38,10 @@ class SetupDoneViewContainer extends Component { textNext: t('hub_setup_nav_done'), textDone: false, }); + + if (sendMountActions) { + this.props.actions.setSetupComplete(); + } } /** diff --git a/app/hub/Views/SetupViews/SetupDoneView/__tests__/SetupDoneViewActions.test.js b/app/hub/Views/SetupViews/SetupDoneView/__tests__/SetupDoneViewActions.test.js new file mode 100644 index 000000000..aec94ca5c --- /dev/null +++ b/app/hub/Views/SetupViews/SetupDoneView/__tests__/SetupDoneViewActions.test.js @@ -0,0 +1,47 @@ +/** + * Test file for Setup Done View Action creators + * + * Ghostery Browser Extension + * https://www.ghostery.com/ + * + * Copyright 2018 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 configureStore from 'redux-mock-store'; +import thunk from 'redux-thunk'; +import * as utils from '../../../../utils'; +import * as SetupDoneViewActions from '../SetupDoneViewActions'; +import { SET_SETUP_COMPLETE } from '../../../SetupView/SetupViewConstants'; + +const middlewares = [thunk]; +const mockStore = configureStore(middlewares); + +const testData = { test: true }; +utils.sendMessageInPromise = jest.fn((name) => new Promise((resolve, reject) => { + switch (name) { + case SET_SETUP_COMPLETE: { + resolve(testData); + break; + } + default: resolve(message); + } +})); + +describe('app/hub/Views/SetupViews/SetupDoneView actions', () => { + test('setSetupComplete action should return correctly', () => { + const initialState = {}; + const store = mockStore(initialState); + + const data = testData; + const expectedPayload = { data, type: SET_SETUP_COMPLETE }; + + return store.dispatch(SetupDoneViewActions.setSetupComplete()).then(() => { + const actions = store.getActions(); + expect(actions).toEqual([expectedPayload]); + }); + }); +}); diff --git a/app/hub/Views/SetupViews/SetupDoneView/index.js b/app/hub/Views/SetupViews/SetupDoneView/index.js index fa38134c1..70eab435b 100644 --- a/app/hub/Views/SetupViews/SetupDoneView/index.js +++ b/app/hub/Views/SetupViews/SetupDoneView/index.js @@ -15,6 +15,7 @@ import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import SetupDoneViewContainer from './SetupDoneViewContainer'; +import * as SetupDoneViewActions from './SetupDoneViewActions'; import { setSetupNavigation } from '../../SetupView/SetupViewActions'; /** @@ -32,7 +33,9 @@ const mapStateToProps = state => Object.assign({}, state.setup); * @memberof SetupContainers */ const mapDispatchToProps = dispatch => ({ - actions: bindActionCreators(Object.assign({}, { setSetupNavigation }), dispatch), + actions: bindActionCreators(Object.assign({}, SetupDoneViewActions, { + setSetupNavigation, + }), dispatch), }); export default connect(mapStateToProps, mapDispatchToProps)(SetupDoneViewContainer); diff --git a/app/hub/components/SideNavigation.jsx b/app/hub/Views/SideNavigationView/SideNavigationView.jsx similarity index 62% rename from app/hub/components/SideNavigation.jsx rename to app/hub/Views/SideNavigationView/SideNavigationView.jsx index 22e170529..9317d85b2 100644 --- a/app/hub/components/SideNavigation.jsx +++ b/app/hub/Views/SideNavigationView/SideNavigationView.jsx @@ -10,7 +10,7 @@ * 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 * - * ToDo: Update this file. + * ToDo: Add Proptypes (items) */ import React, { Component } from 'react'; @@ -21,7 +21,7 @@ import { NavLink } from 'react-router-dom'; * @extends Component * @memberof HubComponents */ -class SideNavigation extends Component { +class SideNavigationView extends Component { constructor(props) { super(props); @@ -51,17 +51,31 @@ class SideNavigation extends Component { } /** - * A helper function for rendering a Side Navigation List Item - * @return {JSX} JSX for the Navigation Item + * Function to tell you if a route has nested routes + * @param {Object} item The route item in the Side Navigation + * @return {Boolean} Whether the route item has a nested route */ + _hasNestedRoutes(item) { + const parentRoutes = ['setup', 'tutorial']; + return parentRoutes.some(parentRoute => item.href.includes(parentRoute)); + } + + /** + * Helper render function for rendering a Side Navigation List Item + * @return {JSX} JSX for the Navigation Item + */ _renderItem(item = {}, index) { switch (item.type) { case 'separator': return
; case 'link': return ( - -
{item.text}
+ +
{item.text}
+
+
+
+
); default: @@ -77,12 +91,18 @@ class SideNavigation extends Component { const { topItems, listItems, bottomItems } = this.state; return ( -
+
{topItems.map((item, i) => this._renderItem(item, i))}
- {listItems.map((item, i) => this._renderItem(item, i))} +
    + {listItems.map((item, i) => ( +
  • + {this._renderItem(item, i)} +
  • + ))} +
{bottomItems.map((item, i) => this._renderItem(item, i))} @@ -92,4 +112,4 @@ class SideNavigation extends Component { } } -export default SideNavigation; +export default SideNavigationView; diff --git a/app/hub/Views/SideNavigationView/SideNavigationView.scss b/app/hub/Views/SideNavigationView/SideNavigationView.scss new file mode 100644 index 000000000..185b41fca --- /dev/null +++ b/app/hub/Views/SideNavigationView/SideNavigationView.scss @@ -0,0 +1,71 @@ +/** + * Ghostery Hub - Side Navigation Sass + * + * Ghostery Browser Extension + * https://www.ghostery.com/ + * + * Copyright 2018 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 + * + * ToDo: Update to BEM [Block Element Modifier](https://csswizardry.com/2013/01/mindbemding-getting-your-head-round-bem-syntax/) + */ + +.App__leftNavigation { + background-color: $hoki; + height: 100%; + min-width: 200px; + max-width: 200px; + color: $white; + ul { + list-style: none; + margin-left: 20px; + li { + height: 80px; + } + } + a { + height: 80px; + .flex-child-auto { + max-width: 160px; + } + color: $white; + &.active { + text-decoration: underline; + } + } +} + +// Side Navigation +.SideNavigation__container { + background-color: $hoki; + .SideNavigation__top { + min-height: 80px; + background-color: $mystic; + } + .SideNavigation__bottom { + margin: 0px 0px 20px 20px; + } + .active .arrow-left { + position: relative; + height: 80px; + width: 20px; + background-color: transparent; + .arrow-left-bottom { + width: 0; + height: 0; + border-style: solid; + border-width: 0 20px 40px 0; + border-color: transparent $white transparent transparent; + } + .arrow-left-top { + width: 0; + height: 0; + border-style: solid; + border-width: 0 0 40px 20px; + border-color: transparent transparent $white transparent; + } + } +} diff --git a/app/hub/Views/TutorialViews/TutorialTrustRestrictView/index.js b/app/hub/Views/SideNavigationView/index.js similarity index 65% rename from app/hub/Views/TutorialViews/TutorialTrustRestrictView/index.js rename to app/hub/Views/SideNavigationView/index.js index d3cd1f6c2..4d9f6d120 100644 --- a/app/hub/Views/TutorialViews/TutorialTrustRestrictView/index.js +++ b/app/hub/Views/SideNavigationView/index.js @@ -1,5 +1,5 @@ /** - * Point of entry index.js file for Tutorial Trust/Restrict View + * Point of entry index.js file for Side Navigation View * * Ghostery Browser Extension * https://www.ghostery.com/ @@ -11,6 +11,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0 */ -import TutorialTrustRestrictView from './TutorialTrustRestrictView'; +import SideNavigationView from './SideNavigationView'; -export default TutorialTrustRestrictView; +export default SideNavigationView; diff --git a/app/hub/Views/TutorialView/TutorialView.jsx b/app/hub/Views/TutorialView/TutorialView.jsx index f322f247a..17f57a718 100644 --- a/app/hub/Views/TutorialView/TutorialView.jsx +++ b/app/hub/Views/TutorialView/TutorialView.jsx @@ -23,25 +23,32 @@ import TutorialNavigation from '../TutorialViews/TutorialNavigation'; * @return {JSX} JSX for rendering the Setup View of the Hub app * @memberof HubComponents */ -const TutorialView = props => ( -
-
- {props.steps.map(step => ( - ( -
- -
- )} - /> - ))} -
+const TutorialView = (props) => { + const { + steps, + sendMountActions, + } = this.props; + + return ( +
+
+ {steps.map(step => ( + ( +
+ +
+ )} + /> + ))} +
- -
-); + +
+ ); +}; // PropTypes ensure we pass required props of the correct type TutorialView.propTypes = { @@ -50,6 +57,7 @@ TutorialView.propTypes = { path: PropTypes.string.isRequired, bodyComponent: PropTypes.func.isRequired, })).isRequired, + sendMountActions: PropTypes.bool.isRequired, }; export default TutorialView; diff --git a/app/hub/Views/TutorialView/TutorialView.scss b/app/hub/Views/TutorialView/TutorialView.scss new file mode 100644 index 000000000..496763ecf --- /dev/null +++ b/app/hub/Views/TutorialView/TutorialView.scss @@ -0,0 +1,43 @@ +/** + * Tutorial View Sass + * + * Ghostery Browser Extension + * https://www.ghostery.com/ + * + * Copyright 2018 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 + * + * ToDo: Update to BEM [Block Element Modifier](https://csswizardry.com/2013/01/mindbemding-getting-your-head-round-bem-syntax/) + */ + +// purple rectangle outline +.rectangle-1 { + width: 120px; + height: 43px; + border-radius: 7.2px; + box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.5); + border: solid 2.7px #930194; + transform: scale(0); + -webkit-animation: zoom .5s; + animation: zoom .5s; + -webkit-animation-fill-mode: forwards; + animation-fill-mode: forwards; + -webkit-animation-delay: 1s; + animation-delay: 1s; +} + + +@-webkit-keyframes zoom { + 100% { + -webkit-transform: scale(1,1); + } +} + +@keyframes zoom { + 100% { + transform: scale(1,1); + } +} diff --git a/app/hub/Views/TutorialView/TutorialViewActions.js b/app/hub/Views/TutorialView/TutorialViewActions.js index 5e16af138..3cd06cde5 100644 --- a/app/hub/Views/TutorialView/TutorialViewActions.js +++ b/app/hub/Views/TutorialView/TutorialViewActions.js @@ -11,7 +11,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0 */ -import { INIT_TUTORIAL_PROPS } from './TutorialViewConstants'; +import { INIT_TUTORIAL_PROPS, SET_TUTORIAL_NAVIGATION } from './TutorialViewConstants'; export function initTutorialProps(data) { return { @@ -19,3 +19,10 @@ export function initTutorialProps(data) { data, }; } + +export function setTutorialNavigation(data) { + return { + type: SET_TUTORIAL_NAVIGATION, + data, + }; +} diff --git a/app/hub/Views/TutorialView/TutorialViewConstants.js b/app/hub/Views/TutorialView/TutorialViewConstants.js index 21aac1be4..5bfcc3799 100644 --- a/app/hub/Views/TutorialView/TutorialViewConstants.js +++ b/app/hub/Views/TutorialView/TutorialViewConstants.js @@ -13,3 +13,7 @@ // Tutorial View export const INIT_TUTORIAL_PROPS = 'INIT_TUTORIAL_PROPS'; +export const SET_TUTORIAL_NAVIGATION = 'SET_TUTORIAL_NAVIGATION'; + +// Tutorial Done +export const SET_TUTORIAL_COMPLETE = 'SET_TUTORIAL_COMPLETE'; diff --git a/app/hub/Views/TutorialView/TutorialViewContainer.jsx b/app/hub/Views/TutorialView/TutorialViewContainer.jsx index 2b0c2543c..c0d9d590d 100644 --- a/app/hub/Views/TutorialView/TutorialViewContainer.jsx +++ b/app/hub/Views/TutorialView/TutorialViewContainer.jsx @@ -9,35 +9,42 @@ * 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 + * + * ToDo: Add Proptypes */ import React from 'react'; -import { connect } from 'react-redux'; -import { bindActionCreators } from 'redux'; - +import { withRouter } from 'react-router-dom'; import TutorialView from './TutorialView'; -import * as actions from './TutorialViewActions'; // Component Views import TutorialVideoView from '../TutorialViews/TutorialVideoView'; import TutorialTrackerListView from '../TutorialViews/TutorialTrackerListView'; -import TutorialSimpleDetailedView from '../TutorialViews/TutorialSimpleDetailedView'; +import TutorialLayoutView from '../TutorialViews/TutorialLayoutView'; import TutorialBlockingView from '../TutorialViews/TutorialBlockingView'; -import TutorialTrustRestrictView from '../TutorialViews/TutorialTrustRestrictView'; +import TutorialTrustView from '../TutorialViews/TutorialTrustView'; import TutorialAntiSuiteView from '../TutorialViews/TutorialAntiSuiteView'; /** - * @class Implement the Tutorial View Container for the Ghostery Hub + * @class Implement the Tutorial View for the Ghostery Hub * @extends Component * @memberof HubContainers */ class TutorialViewContainer extends React.Component { - /** - * Lifecycle Event - */ - componentWillMount() { + constructor(props) { + super(props); + this.state = { + sendMountActions: false, + }; + + this.props.history.push('/tutorial/1'); + const title = ''; window.document.title = title; + + this.props.actions.initTutorialProps(this.props.tutorial).then(() => { + this.setState({ sendMountActions: true }); + }); } /** @@ -45,7 +52,7 @@ class TutorialViewContainer extends React.Component { * @return {JSX} JSX for rendering the Tutorial View of the Hub app */ render() { - const activeIndex = +this.props.location.pathname.split('/').pop(); + const { sendMountActions } = this.state; const steps = [ { index: 1, @@ -60,7 +67,7 @@ class TutorialViewContainer extends React.Component { { index: 3, path: '/tutorial/3', - bodyComponent: TutorialSimpleDetailedView, + bodyComponent: TutorialLayoutView, }, { index: 4, @@ -70,7 +77,7 @@ class TutorialViewContainer extends React.Component { { index: 5, path: '/tutorial/5', - bodyComponent: TutorialTrustRestrictView, + bodyComponent: TutorialTrustView, }, { index: 6, @@ -79,29 +86,24 @@ class TutorialViewContainer extends React.Component { }, ]; - return ; + return ; } } // Default props used throughout the Tutorial flow -TutorialViewContainer.defaultProps = {}; +TutorialViewContainer.defaultProps = { + tutorial: { + navigation: { + activeIndex: 0, + hrefPrev: false, + hrefNext: false, + hrefDone: false, + textPrev: false, + textNext: false, + textDone: false, + }, + }, +}; -/** - * Map redux store state properties to the component's own properties. - * @param {Object} state entire Redux store's state - * @return {function} this function returns a plain object, which will be merged into the component's props - * @memberof HubContainers - */ -const mapStateToProps = () => Object.assign({}); - -/** - * Bind the component's action creators using Redux's bindActionCreators. - * @param {function} dispatch redux store method which dispatches actions - * @return {function} to be used as an argument in redux connect call - * @memberof SetupContainers - */ -const mapDispatchToProps = dispatch => ({ - actions: bindActionCreators(Object.assign(actions), dispatch), -}); -export default connect(mapStateToProps, mapDispatchToProps)(TutorialViewContainer); +export default withRouter(TutorialViewContainer); diff --git a/app/hub/Views/TutorialView/TutorialViewReducer.js b/app/hub/Views/TutorialView/TutorialViewReducer.js new file mode 100644 index 000000000..6e8fb4d97 --- /dev/null +++ b/app/hub/Views/TutorialView/TutorialViewReducer.js @@ -0,0 +1,73 @@ +/** + * Reducer used throughout the Tutorial View's flow + * + * Ghostery Browser Extension + * https://www.ghostery.com/ + * + * Copyright 2018 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 { INIT_TUTORIAL_PROPS, SET_TUTORIAL_NAVIGATION } from './TutorialViewConstants'; + +const initialState = {}; + +function TutorialViewReducer(state = initialState, action) { + switch (action.type) { + // Tutorial View + case INIT_TUTORIAL_PROPS: { + const { + activeIndex, + hrefPrev, + hrefNext, + hrefDone, + textPrev, + textNext, + textDone, + } = action.data.navigation; + return Object.assign({}, state, { + tutorial: { + navigation: { + activeIndex, + hrefPrev, + hrefNext, + hrefDone, + textPrev, + textNext, + textDone, + } + }, + }); + } + case SET_TUTORIAL_NAVIGATION: { + const { + activeIndex, + hrefPrev, + hrefNext, + hrefDone, + textPrev, + textNext, + textDone, + } = action.data; + return Object.assign({}, state, { + tutorial: Object.assign({}, state.tutorial, { + navigation: { + activeIndex, + hrefPrev, + hrefNext, + hrefDone, + textPrev, + textNext, + textDone, + }, + }), + }); + } + + default: return state; + } +} + +export default TutorialViewReducer; diff --git a/app/hub/Views/TutorialView/index.js b/app/hub/Views/TutorialView/index.js index 9ac71d2ac..6707b4f08 100644 --- a/app/hub/Views/TutorialView/index.js +++ b/app/hub/Views/TutorialView/index.js @@ -11,6 +11,31 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0 */ +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; + import TutorialViewContainer from './TutorialViewContainer'; +import TutorialViewReducer from './TutorialViewReducer'; +import * as TutorialViewActions from './TutorialViewActions'; + +/** + * Map redux store state properties to the component's own properties. + * @param {Object} state entire Redux store's state + * @return {function} this function returns a plain object, which will be merged into the component's props + * @memberof HubContainers + */ +const mapStateToProps = state => Object.assign({}, state.tutorial); + +/** + * Bind the component's action creators using Redux's bindActionCreators. + * @param {function} dispatch redux store method which dispatches actions + * @return {function} to be used as an argument in redux connect call + * @memberof SetupContainers + */ +const mapDispatchToProps = dispatch => ({ + actions: bindActionCreators(Object.assign(TutorialViewActions), dispatch), +}); + +export const reducer = TutorialViewReducer; -export default TutorialViewContainer; +export default connect(mapStateToProps, mapDispatchToProps)(TutorialViewContainer); diff --git a/app/hub/Views/TutorialViews/TutorialAntiSuiteView/TutorialAntiSuiteView.jsx b/app/hub/Views/TutorialViews/TutorialAntiSuiteView/TutorialAntiSuiteView.jsx index af62aae7d..55533308f 100644 --- a/app/hub/Views/TutorialViews/TutorialAntiSuiteView/TutorialAntiSuiteView.jsx +++ b/app/hub/Views/TutorialViews/TutorialAntiSuiteView/TutorialAntiSuiteView.jsx @@ -1,5 +1,5 @@ /** - * Tutorial Anti-Suite View Component + * Tutorial Anti Suite View Component * * Ghostery Browser Extension * https://www.ghostery.com/ @@ -13,38 +13,13 @@ * ToDo: Update this file. */ -import React, { Component } from 'react'; +import React from 'react'; /** - * @class Implement the Tutorial Anti-Suite View for the Ghostery Hub + * @class Implement the Tutorial Anti Suite View for the Ghostery Hub * @extends Component * @memberof HubComponents */ -class TutorialAntiSuiteView extends Component { - constructor(props) { - super(props); - - this.state = { - title: '' - }; - } - - /** - * Lifecycle Event - */ - componentWillMount() { - const { title } = this.state; - window.document.title = title; - } - - /** - * React's required render function. Returns JSX - * @return {JSX} JSX for rendering the Tutorial Anti-Suite View of the Hub app - */ - render() { - const { title } = this.state; - return
{title}
; - } -} +const TutorialAntiSuiteView = () =>
Ghostery Hub - Tutorial Anti Suite View
; export default TutorialAntiSuiteView; diff --git a/app/hub/Views/TutorialViews/TutorialAntiSuiteView/TutorialAntiSuiteViewActions.js b/app/hub/Views/TutorialViews/TutorialAntiSuiteView/TutorialAntiSuiteViewActions.js new file mode 100644 index 000000000..1b4ee12c3 --- /dev/null +++ b/app/hub/Views/TutorialViews/TutorialAntiSuiteView/TutorialAntiSuiteViewActions.js @@ -0,0 +1,28 @@ +/** + * Tutorial Done View Action creators + * + * Ghostery Browser Extension + * https://www.ghostery.com/ + * + * Copyright 2018 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 { log, sendMessageInPromise } from '../../../utils'; +import { SET_TUTORIAL_COMPLETE } from '../../TutorialView/TutorialViewConstants'; + +export function setTutorialComplete() { + return function (dispatch) { + return sendMessageInPromise(SET_TUTORIAL_COMPLETE).then((data) => { + dispatch({ + type: SET_TUTORIAL_COMPLETE, + data, + }); + }).catch((err) => { + log('tutorialAntiSuite Action setTutorialComplete Error', err); + }); + }; +} diff --git a/app/hub/Views/TutorialViews/TutorialAntiSuiteView/TutorialAntiSuiteViewContainer.jsx b/app/hub/Views/TutorialViews/TutorialAntiSuiteView/TutorialAntiSuiteViewContainer.jsx new file mode 100644 index 000000000..7954899ee --- /dev/null +++ b/app/hub/Views/TutorialViews/TutorialAntiSuiteView/TutorialAntiSuiteViewContainer.jsx @@ -0,0 +1,58 @@ +/** + * Tutorial Anti Suite View Container + * + * Ghostery Browser Extension + * https://www.ghostery.com/ + * + * Copyright 2018 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, { Component } from 'react'; +import PropTypes from 'prop-types'; + +import TutorialAntiSuiteView from './TutorialAntiSuiteView'; + +/** + * @class Implement the Tutorial Anti Suite View for the Ghostery Hub + * @extends Component + * @memberof HubComponents + */ +class TutorialAntiSuiteViewContainer extends Component { + constructor(props) { + super(props); + + // TODO call setTutorialNavigation action + const { index, sendMountActions } = this.props; + this.props.actions.setTutorialNavigation({ + activeIndex: index, + hrefPrev: `/tutorial/${index - 1}`, + hrefNext: '/', + hrefDone: '/', + textPrev: t('hub_setup_nav_previous'), + textNext: t('hub_setup_nav_done'), + textDone: t('hub_setup_exit_flow'), + }); + + if (sendMountActions) { + this.props.actions.setTutorialComplete(); + } + } + + render() { + return ; + } +} + +// PropTypes ensure we pass required props of the correct type +TutorialAntiSuiteViewContainer.propTypes = { + index: PropTypes.number.isRequired, + actions: PropTypes.shape({ + setTutorialNavigation: PropTypes.func.isRequired, + }).isRequired, +}; + +export default TutorialAntiSuiteViewContainer; diff --git a/app/hub/Views/TutorialViews/TutorialAntiSuiteView/index.js b/app/hub/Views/TutorialViews/TutorialAntiSuiteView/index.js index 4b7ea65d6..4570d30da 100644 --- a/app/hub/Views/TutorialViews/TutorialAntiSuiteView/index.js +++ b/app/hub/Views/TutorialViews/TutorialAntiSuiteView/index.js @@ -1,5 +1,5 @@ /** - * Point of entry index.js file for Tutorial Anti-Suite View + * Point of entry index.js file for Tutorial Anti Suite View * * Ghostery Browser Extension * https://www.ghostery.com/ @@ -11,6 +11,31 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0 */ -import TutorialAntiSuiteView from './TutorialAntiSuiteView'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; -export default TutorialAntiSuiteView; +import TutorialAntiSuiteViewContainer from './TutorialAntiSuiteViewContainer'; +import * as TutorialAntiSuiteViewActions from './TutorialAntiSuiteViewActions'; +import { setTutorialNavigation } from '../../TutorialView/TutorialViewActions'; + +/** + * Map redux store state properties to the component's own properties. + * @param {Object} state entire Redux store's state + * @return {function} this function returns a plain object, which will be merged into the component's props + * @memberof HubContainers + */ +const mapStateToProps = state => Object.assign({}, state.tutorial); + +/** + * Bind the component's action creators using Redux's bindActionCreators. + * @param {function} dispatch redux store method which dispatches actions + * @return {function} to be used as an argument in redux connect call + * @memberof TutorialContainers + */ +const mapDispatchToProps = dispatch => ({ + actions: bindActionCreators(Object.assign({}, TutorialAntiSuiteViewActions, { + setTutorialNavigation, + }), dispatch), +}); + +export default connect(mapStateToProps, mapDispatchToProps)(TutorialAntiSuiteViewContainer); diff --git a/app/hub/Views/TutorialViews/TutorialBlockingView/TutorialBlockingView.jsx b/app/hub/Views/TutorialViews/TutorialBlockingView/TutorialBlockingView.jsx index e6c623c3e..11e5c57f7 100644 --- a/app/hub/Views/TutorialViews/TutorialBlockingView/TutorialBlockingView.jsx +++ b/app/hub/Views/TutorialViews/TutorialBlockingView/TutorialBlockingView.jsx @@ -13,38 +13,13 @@ * ToDo: Update this file. */ -import React, { Component } from 'react'; +import React from 'react'; /** * @class Implement the Tutorial Blocking View for the Ghostery Hub * @extends Component * @memberof HubComponents */ -class TutorialBlockingView extends Component { - constructor(props) { - super(props); - - this.state = { - title: '' - }; - } - - /** - * Lifecycle Event - */ - componentWillMount() { - const { title } = this.state; - window.document.title = title; - } - - /** - * React's required render function. Returns JSX - * @return {JSX} JSX for rendering the Tutorial Blocking View of the Hub app - */ - render() { - const { title } = this.state; - return
{title}
; - } -} +const TutorialBlockingView = () =>
Ghostery Hub - Tutorial Blocking View
; export default TutorialBlockingView; diff --git a/app/hub/Views/TutorialViews/TutorialBlockingView/TutorialBlockingViewContainer.jsx b/app/hub/Views/TutorialViews/TutorialBlockingView/TutorialBlockingViewContainer.jsx new file mode 100644 index 000000000..d05ae1fcf --- /dev/null +++ b/app/hub/Views/TutorialViews/TutorialBlockingView/TutorialBlockingViewContainer.jsx @@ -0,0 +1,54 @@ +/** + * Tutorial Blocking View Container + * + * Ghostery Browser Extension + * https://www.ghostery.com/ + * + * Copyright 2018 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, { Component } from 'react'; +import PropTypes from 'prop-types'; + +import TutorialBlockingView from './TutorialBlockingView'; + +/** + * @class Implement the Tutorial Blocking View for the Ghostery Hub + * @extends Component + * @memberof HubComponents + */ +class TutorialBlockingViewContainer extends Component { + constructor(props) { + super(props); + + // TODO call setTutorialNavigation action + const { index } = this.props; + this.props.actions.setTutorialNavigation({ + activeIndex: index, + hrefPrev: `/tutorial/${index - 1}`, + hrefNext: `/tutorial/${index + 1}`, + hrefDone: '/', + textPrev: t('hub_setup_nav_previous'), + textNext: t('hub_setup_nav_next'), + textDone: t('hub_setup_exit_flow'), + }); + } + + render() { + return ; + } +} + +// PropTypes ensure we pass required props of the correct type +TutorialBlockingViewContainer.propTypes = { + index: PropTypes.number.isRequired, + actions: PropTypes.shape({ + setTutorialNavigation: PropTypes.func.isRequired, + }).isRequired, +}; + +export default TutorialBlockingViewContainer; diff --git a/app/hub/Views/TutorialViews/TutorialBlockingView/index.js b/app/hub/Views/TutorialViews/TutorialBlockingView/index.js index 22d97f1e0..0c58bef1f 100644 --- a/app/hub/Views/TutorialViews/TutorialBlockingView/index.js +++ b/app/hub/Views/TutorialViews/TutorialBlockingView/index.js @@ -11,6 +11,28 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0 */ -import TutorialBlockingView from './TutorialBlockingView'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; -export default TutorialBlockingView; +import TutorialBlockingViewContainer from './TutorialBlockingViewContainer'; +import { setTutorialNavigation } from '../../TutorialView/TutorialViewActions'; + +/** + * Map redux store state properties to the component's own properties. + * @param {Object} state entire Redux store's state + * @return {function} this function returns a plain object, which will be merged into the component's props + * @memberof HubContainers + */ +const mapStateToProps = state => Object.assign({}, state.tutorial); + +/** + * Bind the component's action creators using Redux's bindActionCreators. + * @param {function} dispatch redux store method which dispatches actions + * @return {function} to be used as an argument in redux connect call + * @memberof TutorialContainers + */ +const mapDispatchToProps = dispatch => ({ + actions: bindActionCreators(Object.assign({}, { setTutorialNavigation }), dispatch), +}); + +export default connect(mapStateToProps, mapDispatchToProps)(TutorialBlockingViewContainer); diff --git a/app/hub/Views/TutorialViews/TutorialLayoutView/TutorialLayoutView.jsx b/app/hub/Views/TutorialViews/TutorialLayoutView/TutorialLayoutView.jsx new file mode 100644 index 000000000..a34ddfb50 --- /dev/null +++ b/app/hub/Views/TutorialViews/TutorialLayoutView/TutorialLayoutView.jsx @@ -0,0 +1,25 @@ +/** + * Tutorial Layout View Component + * + * Ghostery Browser Extension + * https://www.ghostery.com/ + * + * Copyright 2018 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 + * + * ToDo: Update this file. + */ + +import React from 'react'; + +/** + * @class Implement the Tutorial Layout View for the Ghostery Hub + * @extends Component + * @memberof HubComponents + */ +const TutorialLayoutView = () =>
Ghostery Hub - Tutorial Simple / Detailed Layout View
; + +export default TutorialLayoutView; diff --git a/app/hub/Views/TutorialViews/TutorialLayoutView/TutorialLayoutViewContainer.jsx b/app/hub/Views/TutorialViews/TutorialLayoutView/TutorialLayoutViewContainer.jsx new file mode 100644 index 000000000..3cdb56fba --- /dev/null +++ b/app/hub/Views/TutorialViews/TutorialLayoutView/TutorialLayoutViewContainer.jsx @@ -0,0 +1,54 @@ +/** + * Tutorial Layout View Container + * + * Ghostery Browser Extension + * https://www.ghostery.com/ + * + * Copyright 2018 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, { Component } from 'react'; +import PropTypes from 'prop-types'; + +import TutorialLayoutView from './TutorialLayoutView'; + +/** + * @class Implement the Tutorial Layout View for the Ghostery Hub + * @extends Component + * @memberof HubComponents + */ +class TutorialLayoutViewContainer extends Component { + constructor(props) { + super(props); + + // TODO call setTutorialNavigation action + const { index } = this.props; + this.props.actions.setTutorialNavigation({ + activeIndex: index, + hrefPrev: `/tutorial/${index - 1}`, + hrefNext: `/tutorial/${index + 1}`, + hrefDone: '/', + textPrev: t('hub_setup_nav_previous'), + textNext: t('hub_setup_nav_next'), + textDone: t('hub_setup_exit_flow'), + }); + } + + render() { + return ; + } +} + +// PropTypes ensure we pass required props of the correct type +TutorialLayoutViewContainer.propTypes = { + index: PropTypes.number.isRequired, + actions: PropTypes.shape({ + setTutorialNavigation: PropTypes.func.isRequired, + }).isRequired, +}; + +export default TutorialLayoutViewContainer; diff --git a/app/hub/Views/TutorialViews/TutorialLayoutView/index.js b/app/hub/Views/TutorialViews/TutorialLayoutView/index.js new file mode 100644 index 000000000..2dc8e75f7 --- /dev/null +++ b/app/hub/Views/TutorialViews/TutorialLayoutView/index.js @@ -0,0 +1,38 @@ +/** + * Point of entry index.js file for Tutorial Layout View + * + * Ghostery Browser Extension + * https://www.ghostery.com/ + * + * Copyright 2018 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 { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; + +import TutorialLayoutViewContainer from './TutorialLayoutViewContainer'; +import { setTutorialNavigation } from '../../TutorialView/TutorialViewActions'; + +/** + * Map redux store state properties to the component's own properties. + * @param {Object} state entire Redux store's state + * @return {function} this function returns a plain object, which will be merged into the component's props + * @memberof HubContainers + */ +const mapStateToProps = state => Object.assign({}, state.tutorial); + +/** + * Bind the component's action creators using Redux's bindActionCreators. + * @param {function} dispatch redux store method which dispatches actions + * @return {function} to be used as an argument in redux connect call + * @memberof TutorialContainers + */ +const mapDispatchToProps = dispatch => ({ + actions: bindActionCreators(Object.assign({}, { setTutorialNavigation }), dispatch), +}); + +export default connect(mapStateToProps, mapDispatchToProps)(TutorialLayoutViewContainer); diff --git a/app/hub/Views/TutorialViews/TutorialNavigation/TutorialNavigationContainer.jsx b/app/hub/Views/TutorialViews/TutorialNavigation/TutorialNavigationContainer.jsx index 7daf171a6..5c18084de 100644 --- a/app/hub/Views/TutorialViews/TutorialNavigation/TutorialNavigationContainer.jsx +++ b/app/hub/Views/TutorialViews/TutorialNavigation/TutorialNavigationContainer.jsx @@ -12,6 +12,7 @@ */ import React from 'react'; +import PropTypes from 'prop-types'; import { SteppedNavigation } from '../../../../shared-components'; /** @@ -19,19 +20,49 @@ import { SteppedNavigation } from '../../../../shared-components'; * @extends Component * @memberof HubComponents */ -const TutorialNavigationContainer = () => { - const childProps = { - totalSteps: 6, - activeIndex: 1, - hrefPrev: false, - hrefNext: '/tutorial/2', - hrefDone: '/', - textPrev: false, - textNext: '', - textDone: '', + +const TutorialNavigationContainer = (props) => { + const { totalSteps, tutorial } = props; + const { navigation } = tutorial; + const navigationProps = { + totalSteps, + ...navigation, }; + return ; +}; - return ; +// PropTypes ensure we pass required props of the correct type +TutorialNavigationContainer.propTypes = { + totalSteps: PropTypes.number.isRequired, + tutorial: PropTypes.shape({ + navigation: PropTypes.shape({ + activeIndex: PropTypes.number.isRequired, + hrefPrev: PropTypes.oneOfType([ + PropTypes.bool, + PropTypes.string, + ]).isRequired, + hrefNext: PropTypes.oneOfType([ + PropTypes.bool, + PropTypes.string, + ]).isRequired, + hrefDone: PropTypes.oneOfType([ + PropTypes.bool, + PropTypes.string, + ]).isRequired, + textPrev: PropTypes.oneOfType([ + PropTypes.bool, + PropTypes.string, + ]).isRequired, + textNext: PropTypes.oneOfType([ + PropTypes.bool, + PropTypes.string, + ]).isRequired, + textDone: PropTypes.oneOfType([ + PropTypes.bool, + PropTypes.string, + ]).isRequired, + }).isRequired, + }).isRequired }; export default TutorialNavigationContainer; diff --git a/app/hub/Views/TutorialViews/TutorialNavigation/index.js b/app/hub/Views/TutorialViews/TutorialNavigation/index.js index 00610531a..71760758a 100644 --- a/app/hub/Views/TutorialViews/TutorialNavigation/index.js +++ b/app/hub/Views/TutorialViews/TutorialNavigation/index.js @@ -11,6 +11,28 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0 */ +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; + import TutorialNavigationContainer from './TutorialNavigationContainer'; -export default TutorialNavigationContainer; +/** + * Map redux store state properties to the component's own properties. + * @param {Object} state entire Redux store's state + * @return {function} this function returns a plain object, which will be merged into the component's props + * @memberof HubContainers + */ +const mapStateToProps = state => Object.assign({}, state.tutorial); + +/** + * Bind the component's action creators using Redux's bindActionCreators. + * @param {function} dispatch redux store method which dispatches actions + * @return {function} to be used as an argument in redux connect call + * @memberof SetupContainers + */ +const mapDispatchToProps = dispatch => ({ + actions: bindActionCreators(Object.assign({}, { + }), dispatch), +}); + +export default connect(mapStateToProps, mapDispatchToProps)(TutorialNavigationContainer); diff --git a/app/hub/Views/TutorialViews/TutorialSimpleDetailedView/TutorialSimpleDetailedView.jsx b/app/hub/Views/TutorialViews/TutorialSimpleDetailedView/TutorialSimpleDetailedView.jsx deleted file mode 100644 index a5f530f15..000000000 --- a/app/hub/Views/TutorialViews/TutorialSimpleDetailedView/TutorialSimpleDetailedView.jsx +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Tutorial Simple and Detailed View Component - * - * Ghostery Browser Extension - * https://www.ghostery.com/ - * - * Copyright 2018 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 - * - * ToDo: Update this file. - */ - -import React, { Component } from 'react'; - -/** - * @class Implement the Tutorial Simple and Detailed View for the Ghostery Hub - * @extends Component - * @memberof HubComponents - */ -class TutorailSimpleDetailedView extends Component { - constructor(props) { - super(props); - - this.state = { - title: '' - }; - } - - /** - * Lifecycle Event - */ - componentWillMount() { - const { title } = this.state; - window.document.title = title; - } - - /** - * React's required render function. Returns JSX - * @return {JSX} JSX for rendering the Tutorial Simple and Detailed View of the Hub app - */ - render() { - const { title } = this.state; - return
{title}
; - } -} - -export default TutorailSimpleDetailedView; diff --git a/app/hub/Views/TutorialViews/TutorialTrackerListView/TutorialTrackerListView.jsx b/app/hub/Views/TutorialViews/TutorialTrackerListView/TutorialTrackerListView.jsx index d9eb392b6..95eb3fa89 100644 --- a/app/hub/Views/TutorialViews/TutorialTrackerListView/TutorialTrackerListView.jsx +++ b/app/hub/Views/TutorialViews/TutorialTrackerListView/TutorialTrackerListView.jsx @@ -13,38 +13,13 @@ * ToDo: Update this file. */ -import React, { Component } from 'react'; +import React from 'react'; /** * @class Implement the Tutorial Tracker List View for the Ghostery Hub * @extends Component * @memberof HubComponents */ -class TutorailTrackerListView extends Component { - constructor(props) { - super(props); +const TutorialTrackerListView = () =>
Ghostery Hub - Tutorial Tracker List View
; - this.state = { - title: '' - }; - } - - /** - * Lifecycle Event - */ - componentWillMount() { - const { title } = this.state; - window.document.title = title; - } - - /** - * React's required render function. Returns JSX - * @return {JSX} JSX for rendering the Tutorial Tracker List View of the Hub app - */ - render() { - const { title } = this.state; - return
{title}
; - } -} - -export default TutorailTrackerListView; +export default TutorialTrackerListView; diff --git a/app/hub/Views/TutorialViews/TutorialTrackerListView/TutorialTrackerListViewContainer.jsx b/app/hub/Views/TutorialViews/TutorialTrackerListView/TutorialTrackerListViewContainer.jsx new file mode 100644 index 000000000..ee3aba16b --- /dev/null +++ b/app/hub/Views/TutorialViews/TutorialTrackerListView/TutorialTrackerListViewContainer.jsx @@ -0,0 +1,54 @@ +/** + * Tutorial Tracker List View Container + * + * Ghostery Browser Extension + * https://www.ghostery.com/ + * + * Copyright 2018 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, { Component } from 'react'; +import PropTypes from 'prop-types'; + +import TutorialTrackerListView from './TutorialTrackerListView'; + +/** + * @class Implement the Tutorial Tracker List View for the Ghostery Hub + * @extends Component + * @memberof HubComponents + */ +class TutorialTrackerListViewContainer extends Component { + constructor(props) { + super(props); + + // TODO call setTutorialNavigation action + const { index } = this.props; + this.props.actions.setTutorialNavigation({ + activeIndex: index, + hrefPrev: `/tutorial/${index - 1}`, + hrefNext: `/tutorial/${index + 1}`, + hrefDone: '/', + textPrev: t('hub_setup_nav_previous'), + textNext: t('hub_setup_nav_next'), + textDone: t('hub_setup_exit_flow'), + }); + } + + render() { + return ; + } +} + +// PropTypes ensure we pass required props of the correct type +TutorialTrackerListViewContainer.propTypes = { + index: PropTypes.number.isRequired, + actions: PropTypes.shape({ + setTutorialNavigation: PropTypes.func.isRequired, + }).isRequired, +}; + +export default TutorialTrackerListViewContainer; diff --git a/app/hub/Views/TutorialViews/TutorialTrackerListView/index.js b/app/hub/Views/TutorialViews/TutorialTrackerListView/index.js index b1e418c3e..95efc9645 100644 --- a/app/hub/Views/TutorialViews/TutorialTrackerListView/index.js +++ b/app/hub/Views/TutorialViews/TutorialTrackerListView/index.js @@ -11,6 +11,28 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0 */ -import TutorialTrackerListView from './TutorialTrackerListView'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; -export default TutorialTrackerListView; +import TutorialTrackerListViewContainer from './TutorialTrackerListViewContainer'; +import { setTutorialNavigation } from '../../TutorialView/TutorialViewActions'; + +/** + * Map redux store state properties to the component's own properties. + * @param {Object} state entire Redux store's state + * @return {function} this function returns a plain object, which will be merged into the component's props + * @memberof HubContainers + */ +const mapStateToProps = state => Object.assign({}, state.tutorial); + +/** + * Bind the component's action creators using Redux's bindActionCreators. + * @param {function} dispatch redux store method which dispatches actions + * @return {function} to be used as an argument in redux connect call + * @memberof TutorialContainers + */ +const mapDispatchToProps = dispatch => ({ + actions: bindActionCreators(Object.assign({}, { setTutorialNavigation }), dispatch), +}); + +export default connect(mapStateToProps, mapDispatchToProps)(TutorialTrackerListViewContainer); diff --git a/app/hub/Views/TutorialViews/TutorialTrustRestrictView/TutorialTrustRestrictView.jsx b/app/hub/Views/TutorialViews/TutorialTrustRestrictView/TutorialTrustRestrictView.jsx deleted file mode 100644 index ae3127a9f..000000000 --- a/app/hub/Views/TutorialViews/TutorialTrustRestrictView/TutorialTrustRestrictView.jsx +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Tutorial Trust and Restrict View Component - * - * Ghostery Browser Extension - * https://www.ghostery.com/ - * - * Copyright 2018 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 - * - * ToDo: Update this file. - */ - -import React, { Component } from 'react'; - -/** - * @class Implement the Tutorial Trust and Restrict View for the Ghostery Hub - * @extends Component - * @memberof HubComponents - */ -class TutorailTrustRestrictView extends Component { - constructor(props) { - super(props); - - this.state = { - title: '' - }; - } - - /** - * Lifecycle Event - */ - componentWillMount() { - const { title } = this.state; - window.document.title = title; - } - - /** - * React's required render function. Returns JSX - * @return {JSX} JSX for rendering the Tutorial Trust and Restrict View of the Hub app - */ - render() { - const { title } = this.state; - return
{title}
; - } -} - -export default TutorailTrustRestrictView; diff --git a/app/scss/partials/_hub_other_partials.scss b/app/hub/Views/TutorialViews/TutorialTrustView/TutorialTrustView.jsx similarity index 51% rename from app/scss/partials/_hub_other_partials.scss rename to app/hub/Views/TutorialViews/TutorialTrustView/TutorialTrustView.jsx index f89e813d7..d1f20107c 100644 --- a/app/scss/partials/_hub_other_partials.scss +++ b/app/hub/Views/TutorialViews/TutorialTrustView/TutorialTrustView.jsx @@ -1,5 +1,5 @@ /** - * Ghostery Hub - Other Sass + * Tutorial Trust View Component * * Ghostery Browser Extension * https://www.ghostery.com/ @@ -10,22 +10,16 @@ * 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 * - * ToDo: Remove this file. + * ToDo: Update this file. */ -// App Scss -.App { - height: 100%; - width: 100%; - display: flex; -} -.App__leftNavigation { - height: 100%; - background-color: pink; - min-width: 200px; - max-width: 200px; -} -.App__mainContent { - height: 100%; - flex-grow: 1; -} +import React from 'react'; + +/** + * @class Implement the Tutorial Trust View for the Ghostery Hub + * @extends Component + * @memberof HubComponents + */ +const TutorialTrustView = () =>
Ghostery Hub - Tutorial Trust
; + +export default TutorialTrustView; diff --git a/app/hub/Views/TutorialViews/TutorialTrustView/TutorialTrustViewContainer.jsx b/app/hub/Views/TutorialViews/TutorialTrustView/TutorialTrustViewContainer.jsx new file mode 100644 index 000000000..f8911318c --- /dev/null +++ b/app/hub/Views/TutorialViews/TutorialTrustView/TutorialTrustViewContainer.jsx @@ -0,0 +1,54 @@ +/** + * Tutorial Trust View Container + * + * Ghostery Browser Extension + * https://www.ghostery.com/ + * + * Copyright 2018 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, { Component } from 'react'; +import PropTypes from 'prop-types'; + +import TutorialTrustView from './TutorialTrustView'; + +/** + * @class Implement the Tutorial Trust View for the Ghostery Hub + * @extends Component + * @memberof HubComponents + */ +class TutorialTrustViewContainer extends Component { + constructor(props) { + super(props); + + // TODO call setTutorialNavigation action + const { index } = this.props; + this.props.actions.setTutorialNavigation({ + activeIndex: index, + hrefPrev: `/tutorial/${index - 1}`, + hrefNext: `/tutorial/${index + 1}`, + hrefDone: '/', + textPrev: t('hub_setup_nav_previous'), + textNext: t('hub_setup_nav_next'), + textDone: t('hub_setup_exit_flow'), + }); + } + + render() { + return ; + } +} + +// PropTypes ensure we pass required props of the correct type +TutorialTrustViewContainer.propTypes = { + index: PropTypes.number.isRequired, + actions: PropTypes.shape({ + setTutorialNavigation: PropTypes.func.isRequired, + }).isRequired, +}; + +export default TutorialTrustViewContainer; diff --git a/app/hub/Views/TutorialViews/TutorialTrustView/index.js b/app/hub/Views/TutorialViews/TutorialTrustView/index.js new file mode 100644 index 000000000..95b4907d2 --- /dev/null +++ b/app/hub/Views/TutorialViews/TutorialTrustView/index.js @@ -0,0 +1,38 @@ +/** + * Point of entry index.js file for Tutorial Trust View + * + * Ghostery Browser Extension + * https://www.ghostery.com/ + * + * Copyright 2018 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 { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; + +import TutorialTrustViewContainer from './TutorialTrustViewContainer'; +import { setTutorialNavigation } from '../../TutorialView/TutorialViewActions'; + +/** + * Map redux store state properties to the component's own properties. + * @param {Object} state entire Redux store's state + * @return {function} this function returns a plain object, which will be merged into the component's props + * @memberof HubContainers + */ +const mapStateToProps = state => Object.assign({}, state.tutorial); + +/** + * Bind the component's action creators using Redux's bindActionCreators. + * @param {function} dispatch redux store method which dispatches actions + * @return {function} to be used as an argument in redux connect call + * @memberof TutorialContainers + */ +const mapDispatchToProps = dispatch => ({ + actions: bindActionCreators(Object.assign({}, { setTutorialNavigation }), dispatch), +}); + +export default connect(mapStateToProps, mapDispatchToProps)(TutorialTrustViewContainer); diff --git a/app/hub/Views/TutorialViews/TutorialVideoView/TutorialVideoView.jsx b/app/hub/Views/TutorialViews/TutorialVideoView/TutorialVideoView.jsx index 9f929cba6..416c861f7 100644 --- a/app/hub/Views/TutorialViews/TutorialVideoView/TutorialVideoView.jsx +++ b/app/hub/Views/TutorialViews/TutorialVideoView/TutorialVideoView.jsx @@ -13,38 +13,13 @@ * ToDo: Update this file. */ -import React, { Component } from 'react'; +import React from 'react'; /** * @class Implement the Tutorial Video View for the Ghostery Hub * @extends Component * @memberof HubComponents */ -class TutorialVideoView extends Component { - constructor(props) { - super(props); - - this.state = { - title: '' - }; - } - - /** - * Lifecycle Event - */ - componentWillMount() { - const { title } = this.state; - window.document.title = title; - } - - /** - * React's required render function. Returns JSX - * @return {JSX} JSX for rendering the Tutorial Video View of the Hub app - */ - render() { - const { title } = this.state; - return
{title}
; - } -} +const TutorialVideoView = () =>
Ghostery Hub - Tutorial Video View
; export default TutorialVideoView; diff --git a/app/hub/Views/TutorialViews/TutorialVideoView/TutorialVideoViewContainer.jsx b/app/hub/Views/TutorialViews/TutorialVideoView/TutorialVideoViewContainer.jsx new file mode 100644 index 000000000..756e654a0 --- /dev/null +++ b/app/hub/Views/TutorialViews/TutorialVideoView/TutorialVideoViewContainer.jsx @@ -0,0 +1,54 @@ +/** + * Tutorial Video View Container + * + * Ghostery Browser Extension + * https://www.ghostery.com/ + * + * Copyright 2018 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, { Component } from 'react'; +import PropTypes from 'prop-types'; + +import TutorialVideoView from './TutorialVideoView'; + +/** + * @class Implement the Tutorial Video View for the Ghostery Hub + * @extends Component + * @memberof HubComponents + */ +class TutorialVideoViewContainer extends Component { + constructor(props) { + super(props); + + // TODO call setTutorialNavigation action + const { index } = this.props; + this.props.actions.setTutorialNavigation({ + activeIndex: index, + hrefPrev: false, + hrefNext: `/tutorial/${index + 1}`, + hrefDone: '/', + textPrev: false, + textNext: t('hub_setup_nav_next'), + textDone: t('hub_setup_exit_flow'), + }); + } + + render() { + return ; + } +} + +// PropTypes ensure we pass required props of the correct type +TutorialVideoViewContainer.propTypes = { + index: PropTypes.number.isRequired, + actions: PropTypes.shape({ + setTutorialNavigation: PropTypes.func.isRequired, + }).isRequired, +}; + +export default TutorialVideoViewContainer; diff --git a/app/hub/Views/TutorialViews/TutorialVideoView/index.js b/app/hub/Views/TutorialViews/TutorialVideoView/index.js index a0a33e016..f59987b7f 100644 --- a/app/hub/Views/TutorialViews/TutorialVideoView/index.js +++ b/app/hub/Views/TutorialViews/TutorialVideoView/index.js @@ -11,6 +11,28 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0 */ -import TutorialVideoView from './TutorialVideoView'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; -export default TutorialVideoView; +import TutorialVideoViewContainer from './TutorialVideoViewContainer'; +import { setTutorialNavigation } from '../../TutorialView/TutorialViewActions'; + +/** + * Map redux store state properties to the component's own properties. + * @param {Object} state entire Redux store's state + * @return {function} this function returns a plain object, which will be merged into the component's props + * @memberof HubContainers + */ +const mapStateToProps = state => Object.assign({}, state.tutorial); + +/** + * Bind the component's action creators using Redux's bindActionCreators. + * @param {function} dispatch redux store method which dispatches actions + * @return {function} to be used as an argument in redux connect call + * @memberof TutorialContainers + */ +const mapDispatchToProps = dispatch => ({ + actions: bindActionCreators(Object.assign({}, { setTutorialNavigation }), dispatch), +}); + +export default connect(mapStateToProps, mapDispatchToProps)(TutorialVideoViewContainer); diff --git a/app/hub/createStore.js b/app/hub/createStore.js index 1bf66a39f..92f9f0857 100644 --- a/app/hub/createStore.js +++ b/app/hub/createStore.js @@ -21,10 +21,14 @@ import { } from 'redux'; import thunk from 'redux-thunk'; +import { reducer as home } from './Views/HomeView'; import { reducer as setup } from './Views/SetupView'; +import { reducer as tutorial } from './Views/TutorialView'; const reducer = combineReducers({ + home, setup, + tutorial, }); /** diff --git a/app/hub/index.jsx b/app/hub/index.jsx index fb2a94fac..1f22f5a86 100644 --- a/app/hub/index.jsx +++ b/app/hub/index.jsx @@ -20,7 +20,7 @@ import { Provider } from 'react-redux'; import createStore from './createStore'; // Components -import App from './components/App'; +import App from './App'; // Containers import HomeView from './Views/HomeView'; diff --git a/app/images/hub/home/ghosty-bubble-heart.svg b/app/images/hub/home/ghosty-bubble-heart.svg new file mode 100644 index 000000000..6d3709603 --- /dev/null +++ b/app/images/hub/home/ghosty-bubble-heart.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/scss/_settings_hub.scss b/app/scss/_settings_hub.scss index 979967b02..d16357ee3 100644 --- a/app/scss/_settings_hub.scss +++ b/app/scss/_settings_hub.scss @@ -78,7 +78,7 @@ $foundation-palette: ( ); $body-background: $white; $body-font-color: $tundora; -$body-font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Roboto, Arial, sans-serif; +$body-font-family: Roboto, "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; $body-antialiased: true; $global-margin: 1rem; $global-padding: 1rem; diff --git a/app/scss/hub.scss b/app/scss/hub.scss index cf556e7e2..716a42194 100644 --- a/app/scss/hub.scss +++ b/app/scss/hub.scss @@ -12,15 +12,21 @@ */ html, body, #root { - font-family: Roboto, "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; height: 100%; width: 100%; margin: 0; } -// Foundation Overrides -h1, .h1, h2, .h2, h3, .h3, h4, .h4, h5, .h5, h6, .h6 { - font-family: Roboto, "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; +.App { + height: 100%; + width: 100%; + display: flex; + overflow-y: scroll; +} + +.App__mainContent { + height: 100%; + flex-grow: 1; } // Helper Classes @@ -29,12 +35,13 @@ h1, .h1, h2, .h2, h3, .h3, h4, .h4, h5, .h5, h6, .h6 { .full-width { width: 100%; } .display-inline { display: inline-block; } +@import 'settings_hub'; @import './partials/_hub_mixins'; @import './partials/_hub_svgs'; -@import './partials/_hub_side_navigation'; -@import './partials/_hub_other_partials'; // ToDo: Remove this file -// Imports from ../hub directory for the Custom Setup Workflow + +@import '../hub/Views/HomeView/HomeView.scss'; +@import '../hub/Views/SideNavigationView/SideNavigationView.scss'; @import '../hub/Views/SetupView/SetupView.scss'; @import '../hub/Views/SetupViews/SetupHeader/SetupHeader.scss'; @import '../hub/Views/SetupViews/SetupBlockingView/SetupBlockingView.scss'; @@ -42,6 +49,8 @@ h1, .h1, h2, .h2, h3, .h3, h4, .h4, h5, .h5, h6, .h6 { @import '../hub/Views/SetupViews/SetupHumanWebView/SetupHumanWebView.scss'; @import '../hub/Views/SetupViews/SetupDoneView/SetupDoneView.scss'; +@import '../hub/Views/TutorialView/TutorialView.scss'; + // Imports from ../shared-components directory @import '../shared-components/Modal/Modal.scss'; @import '../shared-components/SteppedNavigation/SteppedNavigation.scss'; diff --git a/app/scss/partials/_hub_side_navigation.scss b/app/scss/partials/_hub_side_navigation.scss deleted file mode 100644 index 456496014..000000000 --- a/app/scss/partials/_hub_side_navigation.scss +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Ghostery Hub - Side Navigation Sass - * - * Ghostery Browser Extension - * https://www.ghostery.com/ - * - * Copyright 2018 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 - */ - - // Side Navigation - .SideNavigation__top { - min-height: 100px; - margin: 16px; - } - .SideNavigation__list { - margin: 16px; - } - .SideNavigation__bottom { - margin: 16px; - } diff --git a/app/scss/partials/_hub_svgs.scss b/app/scss/partials/_hub_svgs.scss index 775c5af19..00bf171ee 100644 --- a/app/scss/partials/_hub_svgs.scss +++ b/app/scss/partials/_hub_svgs.scss @@ -40,6 +40,23 @@ @return '%23' + str-slice('#{$color}', 2, -1); } +// Used in Home View +@function buildIconClipboard($stroke-color) { + @return url('data:image/svg+xml;charset%3dUS-ASCII,%3Csvg%20width%3D%2234%22%20height%3D%2242%22%20xmlns%3D%22http://www.w3.org/2000/svg%22%3E%3Cg%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Cpath%20d%3D%22M7.953%2026.497h18.263V24.69H7.953v1.808zm-.001-3.72h18.262v-1.805H7.952v1.806zm0-3.712h18.262v-1.808H7.952v1.808zm.014-3.703h18.248V13.55H7.966v1.812zm3.623-11.118v1.83h10.972v-1.83h.373c2.269%200%204.537-.002%206.806%200%201.22%200%201.986.778%201.986%202.018v29.357c0%201.266-.757%202.035-2.004%202.035-8.429.002-16.856.002-25.286%200-1.223%200-1.992-.774-1.992-2.008-.002-9.787-.002-19.574%200-29.36%200-1.274.747-2.04%201.992-2.042%202.368-.002%204.734%200%207.153%200z%22%20fill%3D%22#DAF4FF%22/%3E%3Cpath%20d%3D%22M16.775%205.742c.976.008%201.811-.831%201.83-1.838.02-1.014-.816-1.89-1.81-1.897-.977-.007-1.813.832-1.83%201.839-.02%201.014.818%201.89%201.81%201.896zm-5.456%200H4.206c-1.24%200-1.982.773-1.982%202.056-.002%209.845-.002%2019.691%200%2029.537%200%201.241.763%202.022%201.982%202.022h25.15c1.24%200%201.992-.774%201.992-2.048V7.772c0-1.246-.76-2.03-1.973-2.03h-7.141v1.84H11.319v-1.84zm1.813-1.865c.067-2.229%201.74-3.626%203.426-3.74%201.734-.115%203.75%201.156%203.898%203.74h.387c2.886%200%205.774-.002%208.662%200%202.065.001%203.66%201.638%203.66%203.76%200%209.946.002%2019.89%200%2029.835%200%202.12-1.595%203.75-3.67%203.75H4.054c-2.048%200-3.65-1.643-3.65-3.736-.002-9.932-.002-19.865%200-29.797%200-2.205%201.567-3.81%203.72-3.812%202.863-.002%205.725%200%208.588%200h.419z%22%20fill%3D%22#{_url-friendly-color($stroke-color)}%22/%3E%3Cpath%20fill%3D%22#{_url-friendly-color($stroke-color)}%22%20d%3D%22M7.655%2016.532h18.228V14.99H7.655zM7.655%2020.133h18.228V18.59H7.655zM7.655%2024.248h18.228v-1.543H7.655zM7.655%2027.849h18.228v-1.543H7.655z%22/%3E%3C/g%3E%3C/svg%3E'); +} +@function buildIconClipboardCheck($stroke-color) { + @return url('data:image/svg+xml;charset%3dUS-ASCII,%3Csvg%20width%3D%2281%22%20height%3D%2242%22%20xmlns%3D%22http://www.w3.org/2000/svg%22%3E%3Cg%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Cpath%20d%3D%22M7.953%2026.497h18.263V24.69H7.953v1.808zm-.001-3.72h18.262v-1.805H7.952v1.806zm0-3.712h18.262v-1.808H7.952v1.808zm.014-3.703h18.248V13.55H7.966v1.812zm3.623-11.118v1.83h10.972v-1.83h.373c2.269%200%204.537-.002%206.806%200%201.22%200%201.986.778%201.986%202.018v29.357c0%201.266-.757%202.035-2.004%202.035-8.429.002-16.856.002-25.286%200-1.223%200-1.992-.774-1.992-2.008-.002-9.787-.002-19.574%200-29.36%200-1.274.747-2.04%201.992-2.042%202.368-.002%204.734%200%207.153%200z%22%20fill%3D%22#DAF4FF%22/%3E%3Cpath%20d%3D%22M16.775%205.742c.976.008%201.811-.831%201.83-1.838.02-1.014-.816-1.89-1.81-1.897-.977-.007-1.813.832-1.83%201.839-.02%201.014.818%201.89%201.81%201.896zm-5.456%200H4.206c-1.24%200-1.982.773-1.982%202.056-.002%209.845-.002%2019.691%200%2029.537%200%201.241.763%202.022%201.982%202.022h25.15c1.24%200%201.992-.774%201.992-2.048V7.772c0-1.246-.76-2.03-1.973-2.03h-7.141v1.84H11.319v-1.84zm1.813-1.865c.067-2.229%201.74-3.626%203.426-3.74%201.734-.115%203.75%201.156%203.898%203.74h.387c2.886%200%205.774-.002%208.662%200%202.065.001%203.66%201.638%203.66%203.76%200%209.946.002%2019.89%200%2029.835%200%202.12-1.595%203.75-3.67%203.75H4.054c-2.048%200-3.65-1.643-3.65-3.736-.002-9.932-.002-19.865%200-29.797%200-2.205%201.567-3.81%203.72-3.812%202.863-.002%205.725%200%208.588%200h.419z%22%20fill%3D%22#{_url-friendly-color($stroke-color)}%22/%3E%3Cpath%20fill%3D%22#{_url-friendly-color($stroke-color)}%22%20d%3D%22M7.655%2016.532h18.228V14.99H7.655zM7.655%2020.133h18.228V18.59H7.655zM7.655%2024.248h18.228v-1.543H7.655zM7.655%2027.849h18.228v-1.543H7.655z%22/%3E%3Cpath%20d%3D%22M59.68%2035C54.443%2030.894%2049.256%2026.823%2044%2022.704c1.29-1.671%202.544-3.303%203.846-4.987l11.089%208.691C64.728%2019.951%2070.521%2013.501%2076.353%207L81%2011.233C73.883%2019.17%2066.8%2027.063%2059.68%2035%22%20fill%3D%22#1DAFED%22/%3E%3C/g%3E%3C/svg%3E'); +} +@function buildIconWand($stroke-color) { + @return url('data:image/svg+xml;charset%3dUS-ASCII,%3Csvg%20width%3D%2251%22%20height%3D%2245%22%20xmlns%3D%22http://www.w3.org/2000/svg%22%3E%3Cg%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Cpath%20fill%3D%22#DAF4FF%22%20d%3D%22M20%2021l6-6%2025%2023-6%207z%22/%3E%3Cpath%20d%3D%22M46.92%2038.41L17.702%2010.42c-.747-.715-1.653-1.022-2.613-1.022s-1.92.358-2.612%201.022l-.587.562a3.5%203.5%200%200%200%200%205.057l29.165%2027.94c.747.715%201.653%201.021%202.613%201.021s1.92-.358%202.612-1.022l.64-.51c1.44-1.43%201.44-3.678%200-5.057zM13.33%2012.413l.586-.562c.32-.306.693-.46%201.12-.46.426%200%20.853.154%201.12.46l4.052%203.882-2.826%202.707-4.052-3.882c-.587-.562-.587-1.532%200-2.145zm32.044%2029.574l-.586.562c-.32.307-.694.46-1.12.46-.427%200-.853-.153-1.12-.46l-23.62-22.627%202.826-2.708%2023.673%2022.68a1.423%201.423%200%200%201-.053%202.093zM26.5%206.896c2.08%200%203.733%202.196%203.733%204.086%200%20.562.48%201.021%201.066%201.021.586%200%201.066-.46%201.066-1.021%200-1.89%201.6-4.086%203.733-4.086.586%200%201.066-.46%201.066-1.022s-.48-1.022-1.066-1.022c-2.026%200-3.733-1.583-3.733-3.575%200-.562-.48-1.022-1.066-1.022-.587%200-1.066.46-1.066%201.022%200%201.94-1.653%203.575-3.733%203.575-.586%200-1.066.46-1.066%201.022s.48%201.022%201.066%201.022zm4.746-2.401c.426.562.96%201.073%201.546%201.43-.587.409-1.12.97-1.546%201.584-.427-.613-.96-1.175-1.547-1.584.64-.357%201.12-.868%201.547-1.43zm-15.57%2027.378c-1.332%200-2.452-1.073-2.452-2.35%200-.562-.48-1.021-1.066-1.021-.587%200-1.067.46-1.067%201.021%200%201.277-1.12%202.35-2.452%202.35-.587%200-1.067.46-1.067%201.021%200%20.562.48%201.022%201.067%201.022%201.28%200%202.452%201.328%202.452%202.707%200%20.562.48%201.022%201.067%201.022.586%200%201.066-.46%201.066-1.022%200-1.379%201.173-2.707%202.453-2.707.586%200%201.066-.46%201.066-1.022%200-.561-.48-1.021-1.066-1.021zm-3.518%201.788c-.214-.256-.427-.511-.694-.715.267-.205.48-.41.694-.613.213.255.426.46.693.613-.267.255-.533.46-.693.715zM9.277%205.414c.587%200%201.067-.46%201.067-1.021%200-.562-.48-1.022-1.067-1.022-1.333%200-2.452-1.072-2.452-2.35C6.825.46%206.345%200%205.758%200c-.586%200-1.066.46-1.066%201.022%200%201.277-1.12%202.35-2.453%202.35-.586%200-1.066.459-1.066%201.02%200%20.563.48%201.022%201.066%201.022%201.28%200%202.453%201.328%202.453%202.707%200%20.562.48%201.022%201.066%201.022.587%200%201.067-.46%201.067-1.022%200-1.379%201.173-2.707%202.452-2.707zM5.758%205.16c-.213-.255-.426-.51-.693-.715.267-.204.48-.409.693-.613.214.255.427.46.694.613-.267.255-.48.46-.694.715zm-.64%2017.162c0%20.562-.48%201.022-1.066%201.022-.213%200-.426.306-.426.613%200%20.562-.48%201.021-1.067%201.021-.586%200-1.066-.46-1.066-1.021%200-.307-.267-.613-.427-.613C.48%2023.343%200%2022.883%200%2022.32S.48%2021.3%201.066%2021.3a.412.412%200%200%200%20.427-.409c0-.562.48-1.022%201.066-1.022.587%200%201.067.46%201.067%201.022%200%20.255.213.409.426.409.64%200%201.067.46%201.067%201.021z%22%20fill%3D%22#{_url-friendly-color($stroke-color)}%22%20fill-rule%3D%22nonzero%22/%3E%3C/g%3E%3C/svg%3E'); +} +@function buildIconWandCheck($stroke-color) { + @return url('data:image/svg+xml;charset%3dUS-ASCII,%3Csvg%20width%3D%2290%22%20height%3D%2245%22%20xmlns%3D%22http://www.w3.org/2000/svg%22%3E%3Cg%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Cpath%20d%3D%22M68.68%2037C63.443%2032.894%2058.256%2028.823%2053%2024.704c1.29-1.671%202.544-3.303%203.846-4.987l11.089%208.691C73.728%2021.951%2079.521%2015.501%2085.353%209L90%2013.233C82.883%2021.17%2075.8%2029.063%2068.68%2037%22%20fill%3D%22#1DAFED%22/%3E%3Cpath%20fill%3D%22#DAF4FF%22%20d%3D%22M20%2021l6-6%2025%2023-6%207z%22/%3E%3Cpath%20d%3D%22M46.92%2038.41L17.702%2010.42c-.747-.715-1.653-1.022-2.613-1.022s-1.92.358-2.612%201.022l-.587.562a3.5%203.5%200%200%200%200%205.057l29.165%2027.94c.747.715%201.653%201.021%202.613%201.021s1.92-.358%202.612-1.022l.64-.51c1.44-1.43%201.44-3.678%200-5.057zM13.33%2012.413l.586-.562c.32-.306.693-.46%201.12-.46.426%200%20.853.154%201.12.46l4.052%203.882-2.826%202.707-4.052-3.882c-.587-.562-.587-1.532%200-2.145zm32.044%2029.574l-.586.562c-.32.307-.694.46-1.12.46-.427%200-.853-.153-1.12-.46l-23.62-22.627%202.826-2.708%2023.673%2022.68a1.423%201.423%200%200%201-.053%202.093zM26.5%206.896c2.08%200%203.733%202.196%203.733%204.086%200%20.562.48%201.021%201.066%201.021.586%200%201.066-.46%201.066-1.021%200-1.89%201.6-4.086%203.733-4.086.586%200%201.066-.46%201.066-1.022s-.48-1.022-1.066-1.022c-2.026%200-3.733-1.583-3.733-3.575%200-.562-.48-1.022-1.066-1.022-.587%200-1.066.46-1.066%201.022%200%201.94-1.653%203.575-3.733%203.575-.586%200-1.066.46-1.066%201.022s.48%201.022%201.066%201.022zm4.746-2.401c.426.562.96%201.073%201.546%201.43-.587.409-1.12.97-1.546%201.584-.427-.613-.96-1.175-1.547-1.584.64-.357%201.12-.868%201.547-1.43zm-15.57%2027.378c-1.332%200-2.452-1.073-2.452-2.35%200-.562-.48-1.021-1.066-1.021-.587%200-1.067.46-1.067%201.021%200%201.277-1.12%202.35-2.452%202.35-.587%200-1.067.46-1.067%201.021%200%20.562.48%201.022%201.067%201.022%201.28%200%202.452%201.328%202.452%202.707%200%20.562.48%201.022%201.067%201.022.586%200%201.066-.46%201.066-1.022%200-1.379%201.173-2.707%202.453-2.707.586%200%201.066-.46%201.066-1.022%200-.561-.48-1.021-1.066-1.021zm-3.518%201.788c-.214-.256-.427-.511-.694-.715.267-.205.48-.41.694-.613.213.255.426.46.693.613-.267.255-.533.46-.693.715zM9.277%205.414c.587%200%201.067-.46%201.067-1.021%200-.562-.48-1.022-1.067-1.022-1.333%200-2.452-1.072-2.452-2.35C6.825.46%206.345%200%205.758%200c-.586%200-1.066.46-1.066%201.022%200%201.277-1.12%202.35-2.453%202.35-.586%200-1.066.459-1.066%201.02%200%20.563.48%201.022%201.066%201.022%201.28%200%202.453%201.328%202.453%202.707%200%20.562.48%201.022%201.066%201.022.587%200%201.067-.46%201.067-1.022%200-1.379%201.173-2.707%202.452-2.707zM5.758%205.16c-.213-.255-.426-.51-.693-.715.267-.204.48-.409.693-.613.214.255.427.46.694.613-.267.255-.48.46-.694.715zm-.64%2017.162c0%20.562-.48%201.022-1.066%201.022-.213%200-.426.306-.426.613%200%20.562-.48%201.021-1.067%201.021-.586%200-1.066-.46-1.066-1.021%200-.307-.267-.613-.427-.613C.48%2023.343%200%2022.883%200%2022.32S.48%2021.3%201.066%2021.3a.412.412%200%200%200%20.427-.409c0-.562.48-1.022%201.066-1.022.587%200%201.067.46%201.067%201.022%200%20.255.213.409.426.409.64%200%201.067.46%201.067%201.021z%22%20fill%3D%22#{_url-friendly-color($stroke-color)}%22%20fill-rule%3D%22nonzero%22/%3E%3C/g%3E%3C/svg%3E'); +} +@function buildIconFlag($stroke-color) { + @return url('data:image/svg+xml;charset%3dUS-ASCII,%3Csvg%20width%3D%2252%22%20height%3D%2254%22%20xmlns%3D%22http://www.w3.org/2000/svg%22%3E%3Cg%20fill-rule%3D%22nonzero%22%20fill%3D%22none%22%3E%3Cpath%20d%3D%22M21.71%2023.779c.975.796%209.19-.614%2016.256-6.778%205.392-4.702%209.754-4.641%2012.122-4.039-1.506-2.093-4.454-5.429-7.66-4.946a.623.623%200%200%201-.584-.238.63.63%200%200%201-.08-.63c.45-1.055%201.847-3.52%205.529-5.237A15.944%2015.944%200%200%200%2042.595.569c-4.576-.552-8.687%201.043-12.22%204.746-2.784%202.917-5.238%204.238-7.289%203.933a3.55%203.55%200%200%201-1.024-.323l2.693%2011.34a.632.632%200%200%201-.155.58.618.618%200%200%201-.568.182%2017.4%2017.4%200%200%200-3.03-.32c.432.72.868%201.797.707%203.072zm1.497-4.135L20.093%206.536c-1.335-.607-7.775-3.142-12.483%201.87l3.407%2014.343c1.415-1.202%205.576-4.066%2012.19-3.105z%22%20fill%3D%22#DAF4FF%22/%3E%3Cpath%20d%3D%22M3.187%202.805a2.833%202.833%200%200%201%203.41%202.108%202.855%202.855%200%200%201-1.275%203.08c.013.03.034.057.042.091l.596%202.506c5.836-5.443%2013.566-1.478%2013.646-1.436.16.084.275.233.317.41l.227.955c.13.213.656.962%201.71%201.11%201.122.16%203.103-.332%206.187-3.564%209.11-9.548%2019.517-3.064%2019.621-2.997a.628.628%200%200%201-.12%201.12c-3.21%201.173-4.823%202.985-5.575%204.156%204.895.302%208.653%206.93%208.823%207.233a.631.631%200%200%201-.116.765.618.618%200%200%201-.765.07c-.051-.034-5.21-3.257-12.564%203.155-5.675%204.95-12.59%207.236-16.002%207.236-.718%200-1.281-.1-1.646-.3-.492-.27-.734-.719-.663-1.232.195-1.42-.677-2.564-.966-2.9-5.107.397-7.861%203.199-8.005%203.349-.01.01-.024.014-.034.024l5.823%2024.515a.627.627%200%200%201-.606.772.625.625%200%200%201-.605-.481L4.16%208.407A2.84%202.84%200%200%201%201.093%206.24c-.362-1.527.577-3.068%202.094-3.434zm17.092%2024.592c.976.797%209.191-.614%2016.257-6.777%205.391-4.702%209.753-4.642%2012.122-4.039-1.507-2.093-4.454-5.43-7.66-4.946a.623.623%200%200%201-.585-.238.63.63%200%200%201-.079-.63c.45-1.055%201.847-3.521%205.528-5.238a15.944%2015.944%200%200%200-4.698-1.341c-4.575-.552-8.687%201.043-12.22%204.746-2.784%202.916-5.238%204.238-7.288%203.933a3.55%203.55%200%200%201-1.025-.323l2.694%2011.34a.632.632%200%200%201-.156.58.618.618%200%200%201-.568.182%2017.4%2017.4%200%200%200-3.03-.321c.433.72.868%201.797.708%203.072zm1.616-4.142L18.78%2010.147c-1.335-.608-7.775-3.143-12.483%201.87l3.407%2014.342c1.415-1.201%205.576-4.065%2012.19-3.104zM2.305%205.948A1.583%201.583%200%200%200%204.213%207.13a1.598%201.598%200%200%200%201.173-1.923%201.587%201.587%200%200%200-1.909-1.18%201.598%201.598%200%200%200-1.172%201.922z%22%20fill%3D%22#{_url-friendly-color($stroke-color)}%22%20stroke%3D%22#{_url-friendly-color($stroke-color)}%22%20stroke-width%3D%221.166%22/%3E%3C/g%3E%3C/svg%3E'); +} + // Used in Stepped Navigation @function buildIconX($stroke-color) { @return url('data:image/svg+xml;charset%3dUS-ASCII,%3Csvg%20width%3D%2215%22%20height%3D%2215%22%20xmlns%3D%22http://www.w3.org/2000/svg%22%3E%3Cpath%20d%3D%22M10.078%207.5l4.386-4.386a1.824%201.824%200%200%200-2.578-2.58L7.5%204.922%203.114.534a1.824%201.824%200%201%200-2.58%202.58L4.92%207.5.534%2011.886a1.824%201.824%200%200%200%202.578%202.58L7.5%2010.078l4.386%204.386a1.824%201.824%200%201%200%202.58-2.58L10.078%207.5z%22%20fill%3D%22#{_url-friendly-color($stroke-color)}%22%20fill-rule%3D%22evenodd%22/%3E%3C/svg%3E'); diff --git a/src/background.js b/src/background.js index cf267b179..b9f7aaa87 100644 --- a/src/background.js +++ b/src/background.js @@ -489,6 +489,19 @@ function handleRewards(name, message, callback) { */ function handleGhosteryHub(name, message, callback) { switch (name) { + case 'GET_HOME_PROPS': { + const { + setup_complete, + tutorial_complete, + enable_human_web, + } = conf; + callback({ + setup_complete, + tutorial_complete, + enable_human_web, + }); + break; + } case 'GET_SETUP_SHOW_WARNING_OVERRIDE': { const { setup_show_warning_override } = conf; callback({ setup_show_warning_override }); @@ -580,6 +593,18 @@ function handleGhosteryHub(name, message, callback) { callback({ enable_human_web }); break; } + case 'SET_SETUP_COMPLETE': { + const setup_complete = true; + conf.setup_complete = setup_complete; + callback({ setup_complete }); + break; + } + case 'SET_TUTORIAL_COMPLETE': { + const tutorial_complete = true; + conf.tutorial_complete = tutorial_complete; + callback({ tutorial_complete }); + break; + } default: break; } } diff --git a/src/classes/ConfData.js b/src/classes/ConfData.js index 7a38562cb..4942eb78e 100644 --- a/src/classes/ConfData.js +++ b/src/classes/ConfData.js @@ -132,6 +132,8 @@ class ConfData { _initProperty('setup_show_warning_override', true); _initProperty('setup_path', 0); _initProperty('setup_block', 1); + _initProperty('setup_complete', false); + _initProperty('tutorial_complete', false); _initProperty('cliqz_import_state', 0); // Complex props