1- import _ from 'lodash' ;
21import React , { Component } from 'react' ;
32import PropTypes from 'prop-types' ;
43import Random from 'random-id' ;
@@ -19,6 +18,7 @@ import {
1918import Recognition from './recognition' ;
2019import { ChatIcon , CloseIcon , SubmitIcon , MicIcon } from './icons' ;
2120import { isMobile } from './utils' ;
21+ import { speakFn } from './speechSynthesis' ;
2222
2323class ChatBot extends Component {
2424 /* istanbul ignore next */
@@ -39,7 +39,7 @@ class ChatBot extends Component {
3939 recognitionEnable : props . recognitionEnable && Recognition . isSupported ( ) ,
4040 defaultUserSettings : { } ,
4141 } ;
42-
42+ this . speak = speakFn ( props . speechSynthesis ) ;
4343 this . renderStep = this . renderStep . bind ( this ) ;
4444 this . getTriggeredStep = this . getTriggeredStep . bind ( this ) ;
4545 this . generateRenderedStepsById = this . generateRenderedStepsById . bind ( this ) ;
@@ -95,24 +95,22 @@ class ChatBot extends Component {
9595 steps [ firstStep . id ] . message = firstStep . message ;
9696 }
9797
98- const {
99- currentStep,
100- previousStep,
101- previousSteps,
102- renderedSteps,
103- } = storage . getData ( {
104- cacheName,
105- cache,
106- firstStep,
107- steps,
108- } , ( ) => {
109- // focus input if last step cached is a user step
110- this . setState ( { disabled : false } , ( ) => {
111- if ( enableMobileAutoFocus || ! isMobile ( ) ) {
112- this . input . focus ( ) ;
113- }
114- } ) ;
115- } ) ;
98+ const { currentStep, previousStep, previousSteps, renderedSteps } = storage . getData (
99+ {
100+ cacheName,
101+ cache,
102+ firstStep,
103+ steps,
104+ } ,
105+ ( ) => {
106+ // focus input if last step cached is a user step
107+ this . setState ( { disabled : false } , ( ) => {
108+ if ( enableMobileAutoFocus || ! isMobile ( ) ) {
109+ this . input . focus ( ) ;
110+ }
111+ } ) ;
112+ } ,
113+ ) ;
116114
117115 this . setState ( {
118116 currentStep,
@@ -210,12 +208,7 @@ class ChatBot extends Component {
210208
211209 triggerNextStep ( data ) {
212210 const { enableMobileAutoFocus } = this . props ;
213- const {
214- defaultUserSettings,
215- previousSteps,
216- renderedSteps,
217- steps,
218- } = this . state ;
211+ const { defaultUserSettings, previousSteps, renderedSteps, steps } = this . state ;
219212 let { currentStep, previousStep } = this . state ;
220213 const isEnd = currentStep . end ;
221214
@@ -331,7 +324,10 @@ class ChatBot extends Component {
331324 this . props . handleEnd ( { renderedSteps, steps, values } ) ;
332325 }
333326 }
334-
327+ isInputValueEmpty ( ) {
328+ const { inputValue } = this . state ;
329+ return Boolean ( inputValue ) && inputValue . length > 0 ;
330+ }
335331 isLastPosition ( step ) {
336332 const { renderedSteps } = this . state ;
337333 const length = renderedSteps . length ;
@@ -378,8 +374,8 @@ class ChatBot extends Component {
378374 }
379375
380376 handleSubmitButton ( ) {
381- const { inputValue , speaking, recognitionEnable } = this . state ;
382- if ( ( _ . isEmpty ( inputValue ) || speaking ) && recognitionEnable ) {
377+ const { speaking, recognitionEnable } = this . state ;
378+ if ( ( this . isInputValueEmpty ( ) || speaking ) && recognitionEnable ) {
383379 this . recognition . speak ( ) ;
384380 if ( ! speaking ) {
385381 this . setState ( { speaking : true } ) ;
@@ -406,15 +402,18 @@ class ChatBot extends Component {
406402 renderedSteps . push ( currentStep ) ;
407403 previousSteps . push ( currentStep ) ;
408404
409- this . setState ( {
410- currentStep,
411- renderedSteps,
412- previousSteps,
413- disabled : true ,
414- inputValue : '' ,
415- } , ( ) => {
416- this . input . blur ( ) ;
417- } ) ;
405+ this . setState (
406+ {
407+ currentStep,
408+ renderedSteps,
409+ previousSteps,
410+ disabled : true ,
411+ inputValue : '' ,
412+ } ,
413+ ( ) => {
414+ this . input . blur ( ) ;
415+ } ,
416+ ) ;
418417 }
419418 }
420419
@@ -425,23 +424,29 @@ class ChatBot extends Component {
425424 const value = inputValue ;
426425
427426 if ( typeof result !== 'boolean' || ! result ) {
428- this . setState ( {
429- inputValue : result . toString ( ) ,
430- inputInvalid : true ,
431- disabled : true ,
432- } , ( ) => {
433- setTimeout ( ( ) => {
434- this . setState ( {
435- inputValue : value ,
436- inputInvalid : false ,
437- disabled : false ,
438- } , ( ) => {
439- if ( enableMobileAutoFocus || ! isMobile ( ) ) {
440- this . input . focus ( ) ;
441- }
442- } ) ;
443- } , 2000 ) ;
444- } ) ;
427+ this . setState (
428+ {
429+ inputValue : result . toString ( ) ,
430+ inputInvalid : true ,
431+ disabled : true ,
432+ } ,
433+ ( ) => {
434+ setTimeout ( ( ) => {
435+ this . setState (
436+ {
437+ inputValue : value ,
438+ inputInvalid : false ,
439+ disabled : false ,
440+ } ,
441+ ( ) => {
442+ if ( enableMobileAutoFocus || ! isMobile ( ) ) {
443+ this . input . focus ( ) ;
444+ }
445+ } ,
446+ ) ;
447+ } , 2000 ) ;
448+ } ,
449+ ) ;
445450
446451 return true ;
447452 }
@@ -466,6 +471,7 @@ class ChatBot extends Component {
466471 customStyle,
467472 hideBotAvatar,
468473 hideUserAvatar,
474+ speechSynthesis,
469475 } = this . props ;
470476 const { options, component, asMessage } = step ;
471477 const steps = this . generateRenderedStepsById ( ) ;
@@ -475,10 +481,12 @@ class ChatBot extends Component {
475481 return (
476482 < CustomStep
477483 key = { index }
484+ speak = { this . speak }
478485 step = { step }
479486 steps = { steps }
480487 style = { customStyle }
481488 previousStep = { previousStep }
489+ previousValue = { previousStep . value }
482490 triggerNextStep = { this . triggerNextStep }
483491 />
484492 ) ;
@@ -489,6 +497,8 @@ class ChatBot extends Component {
489497 < OptionsStep
490498 key = { index }
491499 step = { step }
500+ speak = { this . speak }
501+ previousValue = { previousStep . value }
492502 triggerNextStep = { this . triggerNextStep }
493503 bubbleOptionStyle = { bubbleOptionStyle }
494504 />
@@ -500,13 +510,15 @@ class ChatBot extends Component {
500510 key = { index }
501511 step = { step }
502512 steps = { steps }
513+ speak = { this . speak }
503514 previousStep = { previousStep }
504515 previousValue = { previousStep . value }
505516 triggerNextStep = { this . triggerNextStep }
506517 avatarStyle = { avatarStyle }
507518 bubbleStyle = { bubbleStyle }
508519 hideBotAvatar = { hideBotAvatar }
509520 hideUserAvatar = { hideUserAvatar }
521+ speechSynthesis = { speechSynthesis }
510522 isFirst = { this . isFirstPosition ( step ) }
511523 isLast = { this . isLastPosition ( step ) }
512524 />
@@ -556,10 +568,11 @@ class ChatBot extends Component {
556568 ) ;
557569
558570 const icon =
559- ( _ . isEmpty ( inputValue ) || speaking ) && recognitionEnable ? < MicIcon /> : < SubmitIcon /> ;
571+ ( this . isInputValueEmpty ( ) || speaking ) && recognitionEnable ? < MicIcon /> : < SubmitIcon /> ;
560572
561- const inputPlaceholder = speaking ? recognitionPlaceholder :
562- currentStep . placeholder || placeholder ;
573+ const inputPlaceholder = speaking
574+ ? recognitionPlaceholder
575+ : currentStep . placeholder || placeholder ;
563576
564577 const inputAttributesOverride = currentStep . inputAttributes || inputAttributes ;
565578
@@ -593,7 +606,7 @@ class ChatBot extends Component {
593606 height = { height }
594607 hideInput = { currentStep . hideInput }
595608 >
596- { _ . map ( renderedSteps , this . renderStep ) }
609+ { Object . values ( renderedSteps ) . map ( this . renderStep ) }
597610 </ Content >
598611 < Footer className = "rsc-footer" style = { footerStyle } >
599612 { ! currentStep . hideInput && (
@@ -613,18 +626,19 @@ class ChatBot extends Component {
613626 { ...inputAttributesOverride }
614627 />
615628 ) }
616- { ! currentStep . hideInput && ! hideSubmitButton && (
617- < SubmitButton
618- className = "rsc-submit-button"
619- style = { submitButtonStyle }
620- onClick = { this . handleSubmitButton }
621- invalid = { inputInvalid }
622- disabled = { disabled }
623- speaking = { speaking }
624- >
625- { icon }
626- </ SubmitButton >
627- ) }
629+ { ! currentStep . hideInput &&
630+ ! hideSubmitButton && (
631+ < SubmitButton
632+ className = "rsc-submit-button"
633+ style = { submitButtonStyle }
634+ onClick = { this . handleSubmitButton }
635+ invalid = { inputInvalid }
636+ disabled = { disabled }
637+ speaking = { speaking }
638+ >
639+ { icon }
640+ </ SubmitButton >
641+ ) }
628642 </ Footer >
629643 </ ChatBotContainer >
630644 </ div >
@@ -670,6 +684,11 @@ ChatBot.propTypes = {
670684 userDelay : PropTypes . number ,
671685 width : PropTypes . string ,
672686 height : PropTypes . string ,
687+ speechSynthesis : PropTypes . shape ( {
688+ enable : PropTypes . bool ,
689+ lang : PropTypes . string ,
690+ voice : PropTypes . instanceOf ( window . SpeechSynthesisVoice ) ,
691+ } ) ,
673692} ;
674693
675694ChatBot . defaultProps = {
@@ -701,6 +720,11 @@ ChatBot.defaultProps = {
701720 recognitionEnable : false ,
702721 recognitionLang : 'en' ,
703722 recognitionPlaceholder : 'Listening ...' ,
723+ speechSynthesis : {
724+ enable : false ,
725+ lang : 'en' ,
726+ voice : null ,
727+ } ,
704728 style : { } ,
705729 submitButtonStyle : { } ,
706730 toggleFloating : undefined ,
0 commit comments