Skip to content

Commit

Permalink
Guided Tours: find & show tours based on state selectors
Browse files Browse the repository at this point in the history
Previously, GuidedTours was able to trigger only a single tour -- `
main`. This was based solely on the presence of `tour=main` in the
query arguments. This change introduces a selectors-based approach to
triggering tours that is based on the current context and tour-specific
requirements for that context.
  • Loading branch information
mcsf authored and lsinger committed Jul 14, 2016
1 parent ca39735 commit 35cdd84
Show file tree
Hide file tree
Showing 9 changed files with 395 additions and 52 deletions.
5 changes: 5 additions & 0 deletions client/boot/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,11 @@ function reduxStoreReady( reduxStore ) {
}

// If `?tour` is present, show the guided tour
//
// TODO(mcsf): Though the rest of the Guided Tours selector-based code
// works without this, analytics are triggered during the dispatch of
// this action. Thus, keep dispatching this until a solution is
// implemented.
if ( config.isEnabled( 'guided-tours' ) && context.query.tour ) {
context.store.dispatch( showGuidedTour( {
shouldShow: true,
Expand Down
12 changes: 2 additions & 10 deletions client/layout/guided-tours/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import i18n from 'i18n-calypso';
*/
import { getSelectedSite } from 'state/ui/selectors';

function get( tour = 'main' ) {
function get( tour ) {
const tours = {
main: {
version: '20160601',
Expand Down Expand Up @@ -108,17 +108,9 @@ function get( tour = 'main' ) {
linkUrl: 'https://learn.wordpress.com',
},
},
test: {
version: '20160516',
init: {
description: 'Testing multi tour support',
text: i18n.translate( 'Single step tour!' ),
type: 'FinishStep',
},
}
};

return tours[ tour ] || tours.main;
return tours[ tour ] || null;
}

export default {
Expand Down
6 changes: 5 additions & 1 deletion client/layout/guided-tours/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,10 @@ class GuidedTours extends Component {
return true;
};
const proceedToNextStep = () => {
this.props.nextGuidedTourStep( { stepName: nextStepName } );
this.props.nextGuidedTourStep( {
stepName: nextStepName,
tour: this.props.tourState.tour,
} );
};
const abortTour = () => {
const ERROR_WAITED_TOO_LONG = 'waited too long for next target';
Expand All @@ -99,6 +102,7 @@ class GuidedTours extends Component {
this.currentTarget && this.currentTarget.classList.remove( 'guided-tours__overlay' );
this.props.quitGuidedTour( Object.assign( {
stepName: this.props.tourState.stepName,
tour: this.props.tourState.tour,
}, options ) );
}

Expand Down
21 changes: 15 additions & 6 deletions client/state/ui/action-log/reducer.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
/**
* External dependencies
*/
import includes from 'lodash/includes';
import takeRight from 'lodash/takeRight';
import { takeRight } from 'lodash';

/**
* Internal dependencies
Expand All @@ -15,19 +14,29 @@ import {
ROUTE_SET,
} from 'state/action-types';

const isRelevantActionType = includes.bind( null, [
const relevantTypes = {
GUIDED_TOUR_SHOW,
GUIDED_TOUR_UPDATE,
THEMES_RECEIVE,
PREVIEW_IS_SHOWING,
ROUTE_SET,
] );
};

const isRelevantAction = ( action ) =>
relevantTypes.hasOwnProperty( action.type ) &&
( typeof relevantTypes[ action.type ] !== 'function' ||
relevantTypes[ action.type ]( action ) );

const newAction = ( action ) => ( {
...action, timestamp: Date.now()
} );

const maybeAdd = ( state, action ) =>
action
? takeRight( [ ...state, action ], 50 )
: state;

export default ( state = [], action ) =>
isRelevantActionType( action.type )
? takeRight( [ ...state, newAction( action ) ], 50 )
isRelevantAction( action )
? maybeAdd( state, newAction( action ) )
: state;
21 changes: 19 additions & 2 deletions client/state/ui/action-log/test/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { expect } from 'chai';
import { useFakeTimers } from 'test/helpers/use-sinon';
import {
ROUTE_SET,
COMMENTS_LIKE,
} from 'state/action-types';
import reducer from '../reducer';

Expand All @@ -25,11 +26,11 @@ describe( 'reducer', () => {
const actions = [
{
type: ROUTE_SET,
path: '/menus/77203074',
path: '/design/77203074',
},
{
type: ROUTE_SET,
path: '/menus/foobar',
path: '/design/foobar',
},
];
const state = actions.reduce( reducer, undefined );
Expand All @@ -39,4 +40,20 @@ describe( 'reducer', () => {
{ ...actions[ 1 ], timestamp: 1337 },
] );
} );

it( 'should discard them if payload is irrelevant', () => {
const actions = [
{
type: COMMENTS_LIKE,
path: '/menus/77203074',
},
{
type: COMMENTS_LIKE,
path: '/menus/foobar',
},
];
const state = actions.reduce( reducer, undefined );

expect( state ).to.eql( [] );
} );
} );
12 changes: 8 additions & 4 deletions client/state/ui/guided-tours/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,16 @@ import { getPreference } from 'state/preferences/selectors';
* @param {Object} options Options object, see fn signature.
* @return {Object} Action object
*/
export function showGuidedTour( { shouldShow, shouldDelay = false, tour = 'main' } ) {
export function showGuidedTour( { shouldShow, shouldDelay = false, tour } ) {
const showAction = {
type: GUIDED_TOUR_SHOW,
shouldShow,
shouldDelay,
tour,
};

// TODO(mcsf): track this *somewhere*, now that we'll stop relying on this
// action for launching tours
const trackEvent = recordTracksEvent( 'calypso_guided_tours_show', {
tour_version: guidedToursConfig.get( tour ).version,
tour,
Expand All @@ -37,14 +39,15 @@ export function showGuidedTour( { shouldShow, shouldDelay = false, tour = 'main'
return shouldDelay ? showAction : withAnalytics( trackEvent, showAction );
}

export function quitGuidedTour( { tour = 'main', stepName, finished, error } ) {
export function quitGuidedTour( { tour, stepName, finished, error } ) {
const quitAction = {
type: GUIDED_TOUR_UPDATE,
shouldShow: false,
shouldReallyShow: false,
shouldDelay: false,
tour,
stepName,
finished,
};

const trackEvent = recordTracksEvent( `calypso_guided_tours_${ finished ? 'finished' : 'quit' }`, {
Expand All @@ -55,14 +58,15 @@ export function quitGuidedTour( { tour = 'main', stepName, finished, error } ) {
} );

return ( dispatch, getState ) => {
dispatch( withAnalytics( trackEvent, quitAction ) );
dispatch( addSeenGuidedTour( getState, tour, finished ) );
dispatch( withAnalytics( trackEvent, quitAction ) );
};
}

export function nextGuidedTourStep( { tour = 'main', stepName } ) {
export function nextGuidedTourStep( { tour, stepName } ) {
const nextAction = {
type: GUIDED_TOUR_UPDATE,
tour,
stepName,
};

Expand Down
Loading

0 comments on commit 35cdd84

Please sign in to comment.