Skip to content

Commit

Permalink
Move the olark operator availability notice logic to the olark module…
Browse files Browse the repository at this point in the history
… and discontinue showing these notices in the olark widget as visitor notices.

Also keep chat inlined if chat ends while on the contact form with no available operators.
  • Loading branch information
omarjackman committed Dec 17, 2015
1 parent ec7544c commit 0953de4
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 41 deletions.
11 changes: 11 additions & 0 deletions client/lib/olark-store/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ const olarkActions = {
} );
},

setExpanded( isOlarkExpanded ) {
dispatcher.handleServerAction( {
isOlarkExpanded,
type: ActionTypes.OLARK_SET_EXPANDED
} );
},

updateDetails() {
olarkApi( 'api.visitor.getDetails', ( details ) => {
dispatcher.handleServerAction( {
Expand All @@ -54,6 +61,10 @@ const olarkActions = {
} );
},

sendNotificationToVisitor( body ) {
olarkApi( 'api.chat.sendNotificationToVisitor', { body } );
},

sendNotificationToOperator( body ) {
olarkApi( 'api.chat.sendNotificationToOperator', { body } );
},
Expand Down
1 change: 1 addition & 0 deletions client/lib/olark-store/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ export const action = keyMirror( {
OLARK_DETAILS: null,
OLARK_LOCALE: null,
OLARK_USER_ELIGIBILITY: null,
OLARK_SET_EXPANDED: null
} );
4 changes: 4 additions & 0 deletions client/lib/olark-store/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const initialState = {
isOperatorAvailable: false,
isOlarkReady: false,
isUserEligible: false,
isOlarkExpanded: false,
locale: 'en',
details: {}
};
Expand All @@ -35,6 +36,9 @@ const olarkStore = createReducerStore( function( state, payload ) {
case ActionTypes.OLARK_OPERATORS_AVAILABLE:
stateChanges = { isOperatorAvailable: true };
break;
case ActionTypes.OLARK_SET_EXPANDED:
stateChanges = { isOlarkExpanded: action.isOlarkExpanded };
break;
case ActionTypes.OLARK_DETAILS:
stateChanges = { details: action.details };
break;
Expand Down
62 changes: 53 additions & 9 deletions client/lib/olark/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { isBusiness, isEnterprise } from 'lib/products-values';
import olarkApi from 'lib/olark-api';
import notices from 'notices';
import olarkEvents from 'lib/olark-events';
import olarkStore from 'lib/olark-store';
import olarkActions from 'lib/olark-store/actions';
import i18n from 'lib/mixins/i18n';

Expand Down Expand Up @@ -140,6 +141,13 @@ const olark = {
'api.chat.onMessageToVisitor',
'api.chat.onMessageToOperator',
'api.chat.onCommandFromOperator'
],
olarkExpandedEvents = [
'api.box.onShow',
'api.box.onExpand',
'api.box.onHide',
'api.box.onShrink',
'api.chat.onMessageToVisitor'
];

olarkEvents.initialize();
Expand All @@ -148,10 +156,24 @@ const olark = {
olarkEvents.on( 'api.chat.onOperatorsAway', olarkActions.setOperatorsAway );
olarkEvents.on( 'api.chat.onOperatorsAvailable', olarkActions.setOperatorsAvailable );

updateDetailsEvents.forEach( event => olarkEvents.on( event, olarkActions.updateDetails ) );
olarkExpandedEvents.forEach( this.hookExpansionEventToStoreSync.bind( this ) );

updateDetailsEvents.forEach( eventName => olarkEvents.on( eventName, olarkActions.updateDetails ) );

debug( 'Olark code loaded, beginning configuration' );

olarkEvents.on( 'api.chat.onCommandFromOperator', ( event ) => {
if ( event.command.name === 'end' ) {
olarkActions.sendNotificationToVisitor( i18n.translate(
"Your live chat has ended. We'll send a transcript to %(email)s.",
{ args: { email: userData.email } }
) );
}
} );

olarkEvents.on( 'api.chat.onOperatorsAway', this.showAvailabilityNotice );
olarkEvents.on( 'api.chat.onOperatorsAvailable', this.showAvailabilityNotice );

this.setOlarkOptions( userData, wpcomOlarkConfig );
this.updateLivechatActiveCookie();
this.bindBeginConversationEvent( userData, siteUrl );
Expand All @@ -164,6 +186,36 @@ const olark = {
}
},

syncStoreWithExpandedState() {
// We query the dom here because there is no other 100% accurate way to figure this out. Olark does not
// provide initial events for api.box.onExpand when the api.box.show event is fired.
const isOlarkExpanded = !! document.querySelector( '.olrk-state-expanded' );
if ( isOlarkExpanded !== olarkStore.get().isOlarkExpanded ) {
olarkActions.setExpanded( isOlarkExpanded );
}
},

hookExpansionEventToStoreSync( eventName ) {
olarkEvents.on( eventName, this.syncStoreWithExpandedState );
},

showAvailabilityNotice() {
const noticeOptions = { showDismiss: true };
const { isOperatorAvailable, isUserEligible, isOlarkExpanded, isOlarkReady } = olarkStore.get();
const onEligibleContactForm = isUserEligible && window.location.pathname === '/help/contact';
const showNotice = isOlarkReady && ( isOlarkExpanded || onEligibleContactForm );

if ( ! showNotice ) {
return;
}

if ( isOperatorAvailable ) {
notices.success( i18n.translate( 'Our Happiness Engineers have returned, chat with us.' ), noticeOptions );
} else {
notices.warning( i18n.translate( 'Sorry! We just missed you as our Happiness Engineers stepped away.' ), noticeOptions );
}
},

setOlarkOptions( userData, wpcomOlarkConfig = {} ) {
const group = wpcomOlarkConfig.group || config( 'olark' ).business_group_id;
const identity = wpcomOlarkConfig.identity || config( 'olark' ).business_account_id;
Expand Down Expand Up @@ -264,10 +316,6 @@ const olark = {
return;
}

olarkApi( 'api.chat.onOperatorsAway', function() {
olarkApi( 'api.chat.sendNotificationToVisitor', { body: i18n.translate( "Oops, our operators have all stepped away for a moment. If you don't hear back from us shortly, please try again later. Thanks!" ) } );
} );

store.set( this.operatorsAvailableKey, true );
},

Expand All @@ -278,10 +326,6 @@ const olark = {
return;
}

olarkApi( 'api.chat.onOperatorsAvailable', function() {
olarkApi( 'api.chat.sendNotificationToVisitor', { body: i18n.translate( "Hey, we're back. If you don't hear from us shortly, please try your question once more. Thanks!" ) } );
} );

store.set( this.operatorsAvailableKey, false );
},

Expand Down
40 changes: 8 additions & 32 deletions client/me/help/help-contact/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,6 @@ import analytics from 'analytics';
/**
* Module variables
*/
const noticeOptions = {
duration: 10000,
showDismiss: false
};
const wpcom = wpcomLib.undocumented();
const sites = siteList();

Expand All @@ -37,7 +33,7 @@ module.exports = React.createClass( {
componentDidMount: function() {
olarkStore.on( 'change', this.updateOlarkState );
olarkEvents.on( 'api.chat.onOperatorsAway', this.onOperatorsAway );
olarkEvents.on( 'api.chat.onOperatorsAvailable', this.onOperatorsAvailable );
olarkEvents.on( 'api.chat.onCommandFromOperator', this.onCommandFromOperator );

sites.on( 'change', this.onSitesChanged );

Expand All @@ -55,7 +51,7 @@ module.exports = React.createClass( {

olarkStore.removeListener( 'change', this.updateOlarkState );
olarkEvents.off( 'api.chat.onOperatorsAway', this.onOperatorsAway );
olarkEvents.off( 'api.chat.onOperatorsAvailable', this.onOperatorsAvailable );
olarkEvents.off( 'api.chat.onCommandFromOperator', this.onCommandFromOperator );

if ( details.isConversing && ! isOperatorAvailable ) {
olarkActions.shrinkBox();
Expand All @@ -69,6 +65,7 @@ module.exports = React.createClass( {
olark: olarkStore.get(),
isSubmitting: false,
confirmation: null,
isChatEnded: false,
sitesInitialized: sites.initialized
};
},
Expand Down Expand Up @@ -208,10 +205,6 @@ module.exports = React.createClass( {
widgetInput.onkeydown( { keyCode: KEY_ENTER } );
},

onOperatorsAvailable: function() {
this.showOperatorAvailabilityNotice( true );
},

onOperatorsAway: function() {
const { details } = this.state.olark;

Expand All @@ -220,28 +213,11 @@ module.exports = React.createClass( {
form_type: 'kayako'
} );
}

this.showOperatorAvailabilityNotice( false );
},

showOperatorAvailabilityNotice: function( isAvailable ) {
const { isOlarkReady, isUserEligible, details } = this.state.olark;

// We check isOlarkReady because the operator availability events fire before the ready event to indicate if operators are available.
// Here we only care if the availability has changed while we were viewing the contact form
if ( ! isOlarkReady ) {
return;
}

if ( ! ( isUserEligible || details.isConversing ) ) {
// If the user is not currently chatting or the user is not eligible to chat then no need to show the notice
return;
}

if ( isAvailable ) {
notices.success( this.translate( 'Our Happiness Engineers have returned, chat with us.' ), noticeOptions );
} else {
notices.warning( this.translate( 'Sorry! We just missed you as our Happiness Engineers stepped away.' ), noticeOptions );
onCommandFromOperator: function( event ) {
if ( event.command.name === 'end' ) {
this.setState( { isChatEnded: true } );
}
},

Expand All @@ -250,7 +226,7 @@ module.exports = React.createClass( {
* @return {object} A JSX object that should be rendered
*/
getView: function() {
const { olark, confirmation, sitesInitialized, isSubmitting } = this.state;
const { olark, confirmation, sitesInitialized, isSubmitting, isChatEnded } = this.state;
const showChatVariation = olark.isUserEligible && olark.isOperatorAvailable;
const showKayakoVariation = ! showChatVariation && ( olark.details.isConversing || olark.isUserEligible );
const showForumsVariation = ! ( showChatVariation || showKayakoVariation );
Expand All @@ -263,7 +239,7 @@ module.exports = React.createClass( {
return <div className="help-contact__placeholder" />;
}

if ( olark.details.isConversing && olark.isOperatorAvailable ) {
if ( isChatEnded || ( olark.details.isConversing && olark.isOperatorAvailable ) ) {
return <OlarkChatbox />;
}

Expand Down

0 comments on commit 0953de4

Please sign in to comment.