240                  this.props.onSubmit( {
   241                          howCanWeHelp,
   242                          howYouFeel,
   243                          message,
   244                          subject,
   245                          site: this.props.selectedSite,
   246                  } );
   247          };
   248
   249          /**
   250           * Render the contact form
   251           * @return {object} ReactJS JSX object
   252           */
   253          render() {
   254                  const {
   255                          formDescription,
   256                          buttonLabel,
   257                          showHowCanWeHelpField,
   258                          showHowYouFeelField,
   259                          showSubjectField,
   260                          showSiteField,
   261                          showHelpLanguagePrompt,
   262                          translate,
   263                  } = this.props;
   264                  const howCanWeHelpOptions = [
   265                          {
   266                                  value: 'gettingStarted',
   267                                  label: translate( 'Help getting started' ),
   268                                  subtext: translate( 'Can you show me how to…' ),
   269                          },
   270                          {
   271                                  value: 'somethingBroken',
   272                                  label: translate( 'Something is broken' ),
   273                                  subtext: translate( 'Can you check this out…' ),
   274                          },
   275                          {
   276                                  value: 'suggestion',
   277                                  label: translate( 'I have a suggestion' ),
   278                                  subtext: translate( 'I think it would be cool if…' ),
   279                          },
   280                  ];
   281                  const howYouFeelOptions = [
   282                          { value: 'unspecified', label: translate( "I'd rather not" ) },
   283                          { value: 'happy', label: translate( 'Happy' ) },
   284                          { value: 'confused', label: translate( 'Confused' ) },
   285                          { value: 'discouraged', label: translate( 'Discouraged' ) },
   286                          { value: 'upset', label: translate( 'Upset' ) },
   287                          { value: 'panicked', label: translate( 'Panicked' ) },
   288                  ];
   289
   290                  return (
   291                          <div className="help-contact-form">
   292                                  { formDescription && <p>{ formDescription }</p> }
   293
   294                                  <ChatBusinessConciergeNotice from="2017-07-19T00:00:00Z" to="2017-07-21T00:00:00Z" />
   295
   296                                  { showHowCanWeHelpField && (
   297                                          <div>
   298                                                  <FormLabel>{ translate( 'How can we help?' ) }</FormLabel>
   299                                                  { this.renderFormSelection( 'howCanWeHelp', howCanWeHelpOptions ) }
   300                                          </div>
   301                                  ) }
   302
   303                                  { showHowYouFeelField && (
   304                                          <div>
   305                                                  <FormLabel>{ translate( 'Mind sharing how you feel?' ) }</FormLabel>
   306                                                  { this.renderFormSelection( 'howYouFeel', howYouFeelOptions ) }
   307                                          </div>
   308                                  ) }
   309
   310                                  { showSiteField &&
   311                                          this.props.selectedSite && (
   312                                                  <div className="help-contact-form__site-selection">
   313                                                          <FormLabel>{ translate( 'Which site do you need help with?' ) }</FormLabel>
   314                                                          <SitesDropdown
   315                                                                  selectedSiteId={ this.props.selectedSite.ID }
   316                                                                  onSiteSelect={ this.props.onChangeSite }
   317                                                          />
   318                                                  </div>
   319                                          ) }
   320
   321                                  { showSubjectField && (
   322                                          <div className="help-contact-form__subject">
   323                                                  <FormLabel>{ translate( 'Subject' ) }</FormLabel>
   324                                                  <FormTextInput
   325                                                          name="subject"
   326                                                          value={ this.state.subject }
   327                                                          onChange={ this.handleChange }
   328                                                  />
   329                                          </div>
   330                                  ) }
   331
   332                                  <FormLabel>{ translate( 'What are you trying to do?' ) }</FormLabel>
   333                                  <FormTextarea
   334                                          placeholder={ translate( 'Please be descriptive' ) }
   335                                          name="message"
   336                                          value={ this.state.message }
   337                                          onChange={ this.handleChange }
   338                                  />
   339
   340                                  { showHelpLanguagePrompt && (
   341                                          <strong className="help-contact-form__help-language-prompt">
   342                                                  { translate( 'Note: Support is only available in English at the moment.' ) }
   343                                          </strong>
   344                                  ) }
   345
   346                                  <HelpResults
   347                                          header={ translate( 'Do you want the answer to any of these questions?' ) }
   348                                          helpLinks={ this.state.qanda }
   349                                          iconTypeDescription="book"
   350                                          onClick={ this.trackSibylClick }
   351                                  />
   352
   353                                  <FormButton disabled={ ! this.canSubmitForm() } type="button" onClick={ this.submitForm }>
   354                                          { buttonLabel }
   355                                  </FormButton>
   356                          </div>
   357                  );
   358          }
   359
   360          handleChange = e => {
   361                  const { name, value } = e.currentTarget;
   362                  this.setState( { [ name ]: value } );
   363          };
   364  }
   365
   366  const mapStateToProps = state => ( {
   367          selectedSite: getHelpSelectedSite( state ),
   368  } );
   369
   370  const mapDispatchToProps = {
   371          onChangeSite: selectSiteId,
   372          trackSibylClick,
   373          trackSupportAfterSibylClick,
   374  };
   375
   376  export default connect( mapStateToProps, mapDispatchToProps )( localize( HelpContactForm ) );
\n--- wrapper top/methods ---
     1  /** @format */
     2
     3  /**
     4   * External dependencies
     5   */
     6
     7  import React from 'react';
     8  import page from 'page';
     9  import { connect } from 'react-redux';
    10  import i18n, { localize } from 'i18n-calypso';
    11
    12  /**
    13   * Internal dependencies
    14   */
    15  import config from 'config';
    16  import Main from 'components/main';
    17  import Card from 'components/card';
    18  import Notice from 'components/notice';
    19  import HelpContactForm from 'me/help/help-contact-form';
    20  import HelpContactClosed from 'me/help/help-contact-closed';
    21  import HelpContactConfirmation from 'me/help/help-contact-confirmation';
    22  import HeaderCake from 'components/header-cake';
    23  import wpcomLib from 'lib/wp';
    24  import notices from 'notices';
    25  import analytics from 'lib/analytics';
    26  import getHappychatUserInfo from 'state/happychat/selectors/get-happychat-userinfo';
    27  import isHappychatAvailable from 'state/happychat/selectors/is-happychat-available';
    28  import isHappychatUserEligible from 'state/happychat/selectors/is-happychat-user-eligible';
    29  import {
    30          isTicketSupportEligible,
    31          isTicketSupportConfigurationReady,
    32          getTicketSupportRequestError,
    33  } from 'state/help/ticket/selectors';
    34  import HappychatConnection from 'components/happychat/connection-connected';
    35  import QueryTicketSupportConfiguration from 'components/data/query-ticket-support-configuration';
    36  import HelpUnverifiedWarning from '../help-unverified-warning';
    37  import {
    38          sendMessage as sendHappychatMessage,
    39          sendUserInfo,
    40  } from 'state/happychat/connection/actions';
    41  import { openChat as openHappychat } from 'state/happychat/ui/actions';
    42  import {
    43          getCurrentUser,
    44          getCurrentUserLocale,
    45          getCurrentUserSiteCount,
    46          isCurrentUserEmailVerified,
    47  } from 'state/current-user/selectors';
    48  import {
    49          askQuestion as askDirectlyQuestion,
    50          initialize as initializeDirectly,
    51  } from 'state/help/directly/actions';
    52  import { getSitePlan, isCurrentPlanPaid, isRequestingSites } from 'state/sites/selectors';
    53  import {
    54          hasUserAskedADirectlyQuestion,
    55          isDirectlyFailed,
    56          isDirectlyReady,
    57          isDirectlyUninitialized,
    58  } from 'state/selectors';
    59  import QueryUserPurchases from 'components/data/query-user-purchases';
    60  import { getHelpSelectedSiteId } from 'state/help/selectors';
    61
    62  /**
    63   * Module variables
    64   */
    65  const wpcom = wpcomLib.undocumented();
    66  let savedContactForm = null;
    67
    68  const SUPPORT_DIRECTLY = 'SUPPORT_DIRECTLY';
    69  const SUPPORT_HAPPYCHAT = 'SUPPORT_HAPPYCHAT';
    70  const SUPPORT_TICKET = 'SUPPORT_TICKET';
    71  const SUPPORT_FORUM = 'SUPPORT_FORUM';
    72
    73  const startShowingChristmas2017ClosureNoticeAt = i18n.moment( 'Sun, 17 Dec 2017 00:00:00 +0000' );
    74  const stopShowingChristmas2017ClosureNoticeAt = i18n.moment( 'Tue, 26 Dec 2017 00:00:00 +0000' );
    75  const startShowingNewYear2018ClosureNoticeAt = i18n.moment( 'Fri, 29 Dec 2017 00:00:00 +0000' );
    76  const stopShowingNewYear2018ClosureNoticeAt = i18n.moment( 'Tue, 2 Jan 2018 00:00:00 +0000' );
    77
    78  class HelpContact extends React.Component {
    79          state = {
    80                  isSubmitting: false,
    81                  confirmation: null,
    82          };
    83
    84          componentDidMount() {
    85                  this.prepareDirectlyWidget();
    86          }
    87
    88          componentDidUpdate() {
    89                  // Directly initialization is a noop if it's already happened. This catches
    90                  // instances where a state/prop change moves a user to Directly support from
    91                  // some other variation.
    92                  this.prepareDirectlyWidget();
    93          }
    94
    95          backToHelp = () => {
    96                  page( '/help' );
    97          };
    98
    99          clearSavedContactForm = () => {
   100                  savedContactForm = null;
   101          };
   102
   103          startHappychat = contactForm => {
   104                  this.props.openHappychat();
   105                  const { howCanWeHelp, howYouFeel, message, site } = contactForm;
   106
   107                  this.props.sendUserInfo( this.props.getUserInfo( { howCanWeHelp, howYouFeel, site } ) );
   108                  this.props.sendHappychatMessage( message, { includeInSummary: true } );
   109
   110                  analytics.tracks.recordEvent( 'calypso_help_live_chat_begin', {
   111                          site_plan_product_id: site ? site.plan.product_id : null,
   112                          is_automated_transfer: site ? site.options.is_automated_transfer : null,
   113                  } );
   114
   115                  page( '/help' );
   116          };
   117
   118          prepareDirectlyWidget = () => {
   119                  if (
   120                          this.hasDataToDetermineVariation() &&
   121                          this.getSupportVariation() === SUPPORT_DIRECTLY &&
   122                          this.props.isDirectlyUninitialized
   123                  ) {
   124                          this.props.initializeDirectly();
   125                  }
   126          };
   127
   128          submitDirectlyQuestion = contactForm => {
   129                  const { display_name, email } = this.props.currentUser;
   130
   131                  this.props.askDirectlyQuestion( contactForm.message, display_name, email );
   132
   133                  this.clearSavedContactForm();
   134
   135                  page( '/help' );
   136          };
   137
   138          submitKayakoTicket = contactForm => {
   139                  const { subject, message, howCanWeHelp, howYouFeel, site } = contactForm;
   140                  const { currentUserLocale } = this.props;
   141
   142                  const ticketMeta = [
   143                          'How can you help: ' + howCanWeHelp,
   144                          'How I feel: ' + howYouFeel,
   145                          'Site I need help with: ' + ( site ? site.URL : 'N/A' ),
   146                  ];
   147
   148                  const kayakoMessage = [ ...ticketMeta, '\n', message ].join( '\n' );
   149
   150                  this.setState( { isSubmitting: true } );
   151
   152                  wpcom.submitKayakoTicket(
   153                          subject,
   154                          kayakoMessage,
   155                          currentUserLocale,
   156                          this.props.clientSlug,
   157                          error => {
   158                            <response clipped><NOTE>Due to the max output limit, only part of the full response has been shown to you.</NOTE> 302                                                          '{{strong}}Please do not{{/strong}} provide financial or contact ' +
   303                                                          'information when submitting this form.',
   304                                                  {
   305                                                          components: {
   306                                                                  // Need to use linebreaks since the entire text is wrapped in a <p>...</p>
   307                                                                  br: <br />,
   308                                                                  strong: <strong />,
   309                                                          },
   310                                                  }
   311                                          ),
   312                                          showSubjectField: false,
   313                                          showHowCanWeHelpField: false,
   314                                          showHowYouFeelField: false,
   315                                          showSiteField: false,
   316                                  };
   317
   318                          default:
   319                                  return {
   320                                          onSubmit: this.submitSupportForumsTopic,
   321                                          buttonLabel: isSubmitting
   322                                                  ? translate( 'Asking in the forums' )
   323                                                  : translate( 'Ask in the forums' ),
   324                                          formDescription: translate(
   325                                                  'Post a new question in our {{strong}}public forums{{/strong}}, ' +
   326                                                          'where it may be answered by helpful community members, ' +
   327                                                          'by submitting the form below. ' +
   328                                                          '{{strong}}Please do not{{/strong}} provide financial or ' +
   329                                                          'contact information when submitting this form.',
   330                                                  {
   331                                                          components: {
   332                                                                  strong: <strong />,
   333                                                          },
   334                                                  }
   335                                          ),
   336                                          showSubjectField: true,
   337                                          showHowCanWeHelpField: false,
   338                                          showHowYouFeelField: false,
   339                                          showSiteField: false,
   340                                  };
   341                  }
   342          };
   343
   344          getContactFormCommonProps = variationSlug => {
   345                  const { isSubmitting } = this.state;
   346                  const { currentUserLocale } = this.props;
   347
   348                  // Let the user know we only offer support in English.
   349                  // We only need to show the message if:
   350                  // 1. The user's locale doesn't match the live chat locale (usually English)
   351                  // 2. The support request isn't sent to the forums. Because forum support
   352                  //    requests are sent to the language specific forums (for popular languages)
   353                  //    we don't tell the user that support is only offered in English.
   354                  const showHelpLanguagePrompt =
   355                          config( 'support_locales' ).indexOf( currentUserLocale ) === -1 &&
   356                          SUPPORT_FORUM !== variationSlug;
   357
   358                  return {
   359                          disabled: isSubmitting,
   360                          showHelpLanguagePrompt: showHelpLanguagePrompt,
   361                          valueLink: {
   362                                  value: savedContactForm,
   363                                  requestChange: contactForm => ( savedContactForm = contactForm ),
   364                          },
   365                  };
   366          };
   367
   368          shouldShowTicketRequestErrorNotice = variationSlug => {
   369                  const { ticketSupportRequestError } = this.props;
   370
   371                  return SUPPORT_HAPPYCHAT !== variationSlug && null != ticketSupportRequestError;
   372          };
   373
   374          /**
   375           * Before determining which variation to assign, certain async data needs to be in place.
   376           * This function helps assess whether we're ready to say which variation the user should see.
   377           *
   378           * @returns {Boolean} Whether all the data is present to determine the variation to show
   379           */
   380          hasDataToDetermineVariation = () => {
   381                  const ticketReadyOrError =
   382                          this.props.ticketSupportConfigurationReady || null != this.props.ticketSupportRequestError;
   383                  const happychatReadyOrDisabled =
   384                          ! config.isEnabled( 'happychat' ) || this.props.isHappychatUserEligible !== null;
   385
   386                  return ticketReadyOrError && happychatReadyOrDisabled;
   387          };
   388
   389          shouldShowPreloadForm = () => {
   390                  const waitingOnDirectly =
\n--- wrapper bottom ---
   390                  const waitingOnDirectly =
   391                          this.getSupportVariation() === SUPPORT_DIRECTLY && ! this.props.isDirectlyReady;
   392
   393                  return (
   394                          this.props.isRequestingSites || ! this.hasDataToDetermineVariation() || waitingOnDirectly
   395                  );
   396          };
   397
   398          /**
   399           * Get the view for the contact page.
   400           * @return {object} A JSX object that should be rendered
   401           */
   402          getView = () => {
   403                  const { confirmation } = this.state;
   404                  const { translate, selectedSitePlanSlug } = this.props;
   405
   406                  if ( confirmation ) {
   407                          return <HelpContactConfirmation { ...confirmation } />;
   408                  }
   409
   410                  if ( this.shouldShowPreloadForm() ) {
   411                          return (
   412                                  <div className="help-contact__placeholder">
   413                                          <h4 className="help-contact__header">Loading contact form</h4>
   414                                          <div className="help-contact__textarea" />
   415
   416                                          <h4 className="help-contact__header">Loading contact form</h4>
   417                                          <div className="help-contact__textarea" />
   418
   419                                          <h4 className="help-contact__header">Loading contact form</h4>
   420                                          <div className="help-contact__textarea" />
   421                                  </div>
   422                          );
   423                  }
   424
   425                  const supportVariation = this.getSupportVariation();
   426
   427                  if ( supportVariation === SUPPORT_DIRECTLY && this.props.hasAskedADirectlyQuestion ) {
   428                          // We're taking the Directly confirmation outside the standard `confirmation` state object
   429                          // that other variations use, because we need this to persist even if the component is
   430                          // removed and re-mounted. Using `confirmation` in component state would mean you could
   431                          // ask a new Directy question every time you left the help section and came back.
   432                          const directlyConfirmation = {
   433                                  title: translate( "We're on it!" ),
   434                                  message: translate(
   435                                          'We sent your question to our {{strong}}Expert Users{{/strong}}. ' +
   436                                                  'You will hear back via email as soon as an Expert has responded ' +
   437                                                  '(usually within an hour). For now you can close this window or ' +
   438                                                  'continue using WordPress.com.',
   439                                          {
   440                                                  components: {
   441                                                          strong: <strong />,
   442                                                  },
   443                                          }
   444                                  ),
   445                          };
   446                          return <HelpContactConfirmation { ...directlyConfirmation } />;
   447                  }
   448
   449                  const contactFormProps = Object.assign(
   450                          this.getContactFormCommonProps( supportVariation ),
   451                          this.getContactFormPropsVariation( supportVariation )
   452                  );
   453
   454                  const currentDate = i18n.moment();
   455
   456                  // Customers sent to Directly and Forum are not affected by the Christmas closures
   457                  const isUserAffectedByChristmas2017Closure =
   458                          supportVariation !== SUPPORT_DIRECTLY && supportVariation !== SUPPORT_FORUM;
   459
   460                  const isClosureNoticeInEffect =
   461                          currentDate.isBetween(
   462                                  startShowingChristmas2017ClosureNoticeAt,
   463                                  stopShowingChristmas2017ClosureNoticeAt
   464                          ) ||
   465                          currentDate.isBetween(
   466                                  startShowingNewYear2018ClosureNoticeAt,
   467                                  stopShowingNewYear2018ClosureNoticeAt
   468                          );
   469
   470                  const shouldShowClosureNotice = isUserAffectedByChristmas2017Closure && isClosureNoticeInEffect;
   471
   472                  return (
   473                          <div>
   474                                  { shouldShowClosureNotice && <HelpContactClosed sitePlanSlug={ selectedSitePlanSlug } /> }
   475                                  { this.shouldShowTicketRequestErrorNotice( supportVariation ) && (
   476                                          <Notice
   477                                                  status="is-warning"
   478                                                  text={ translate(
   479                                                          'We had trouble loading the support information for your account. ' +
   480                                                                  'Please check your internet connection and reload the page, or try again later.'
   481                                                  ) }
   482                                                  showDismiss={ false }
   483                                          />
   484                                  ) }
   485                                  <HelpContactForm { ...contactFormProps } />
   486                          </div>
   487                  );
   488          };
   489
   490          render() {
   491                  return (
   492                          <Main className="help-contact">
   493                                  <HeaderCake onClick={ this.backToHelp } isCompact={ true }>
   494                                          { this.props.translate( 'Contact Us' ) }
   495                                  </HeaderCake>
   496                                  { ! this.props.isEmailVerified && <HelpUnverifiedWarning /> }
   497                                  <Card className="help-contact__form">{ this.getView() }</Card>
   498                                  { this.props.shouldStartHappychatConnection && <HappychatConnection /> }
   499                                  <QueryTicketSupportConfiguration />
   500                                  <QueryUserPurchases userId={ this.props.currentUser.ID } />
   501                          </Main>
   502                  );
   503          }
   504  }
   505
   506  export default connect(
   507          state => {
   508                  const helpSelectedSiteId = getHelpSelectedSiteId( state );
   509                  const selectedSitePlan = getSitePlan( state, helpSelectedSiteId );
   510                  return {
   511                          currentUserLocale: getCurrentUserLocale( state ),
   512                          currentUser: getCurrentUser( state ),
   513                          getUserInfo: getHappychatUserInfo( state ),
   514                          hasAskedADirectlyQuestion: hasUserAskedADirectlyQuestion( state ),
   515                          isDirectlyFailed: isDirectlyFailed( state ),
   516                          isDirectlyReady: isDirectlyReady( state ),
   517                          isDirectlyUninitialized: isDirectlyUninitialized( state ),
   518                          isEmailVerified: isCurrentUserEmailVerified( state ),
   519                          isHappychatAvailable: isHappychatAvailable( state ),
   520                          isHappychatUserEligible: isHappychatUserEligible( state ),
   521                          ticketSupportConfigurationReady: isTicketSupportConfigurationReady( state ),
   522                          ticketSupportEligible: isTicketSupportEligible( state ),
   523                          ticketSupportRequestError: getTicketSupportRequestError( state ),
   524                          hasMoreThanOneSite: getCurrentUserSiteCount( state ) > 1,
   525                          shouldStartHappychatConnection: ! isRequestingSites( state ) && helpSelectedSiteId,
   526                          isRequestingSites: isRequestingSites( state ),
   527                          isSelectedHelpSiteOnPaidPlan: isCurrentPlanPaid( state, helpSelectedSiteId ),
   528                          selectedSitePlanSlug: selectedSitePlan && selectedSitePlan.product_slug,
   529                  };
   530          },
[The command completed with exit code 0.]
[Current working directory: /workspace/wp-calypso]
[Python interpreter: /usr/bin/python]
[Command finished with exit code 0]