@@ -364,18 +364,19 @@ const escapeHtml = (unsafe) => {
364364}
365365
366366// Bugster
367+ let bugsterSessionId = null ;
368+
367369function Bugster ( ) {
368370 if ( ! document . querySelector ( '#bugsterModal' ) ) return ;
369371
372+ let theQuestion = null ;
373+ let chatMessages = [ ] ;
370374 const question = document . querySelector ( '#bugsterModal input#question' ) ;
371375 const bugsterChat = document . querySelector ( '#bugsterModal .bugster-chat' ) ;
372- const bugsterChatFooter = document . querySelector ( '#bugsterModal .modal-footer' ) ;
373- const bugsterChatFooterCollapse = new bootstrap . Collapse ( '#bugsterModal .modal-footer' , { toggle : false } ) ;
374376 const userDialog = document . querySelector ( '.bugster-chat .user-dialog' ) ;
375377 const userText = document . querySelector ( '.bugster-chat .user-dialog-text' ) ;
376378 const bugsterDialog = document . querySelector ( '.bugster-chat .bugster-dialog' ) ;
377379 const bugsterText = document . querySelector ( '.bugster-chat .bugster-dialog-text .content' ) ;
378- const askAnotherQuestion = document . querySelector ( '#ask-another-question' ) ;
379380 const md = new remarkable . Remarkable ( ) ;
380381
381382 // Shortcut questions
@@ -391,118 +392,142 @@ function Bugster() {
391392 event . preventDefault ( ) ;
392393
393394 if ( question . value !== "" ) {
394- userDialog . classList . add ( 'd-none' ) ;
395- bugsterDialog . classList . add ( 'd-none' ) ;
395+ bugsterChat . classList . remove ( 'd-none' ) ;
396+ theQuestion = question . value ;
397+ question . value = "" ;
396398
397399 if ( document . querySelector ( '.bugster-hero' ) . checkVisibility ( ) === false ) {
398- bugsterChatFooterCollapse . hide ( ) ;
399- bugsterChatFooter . addEventListener ( 'hidden.bs.collapse' , event => {
400- setTimeout ( ( ) => {
401- bugsterChat . classList . remove ( 'd-none' ) ;
402- userText . innerHTML = `<p>${ escapeHtml ( question . value ) } </p>` ;
403- userDialog . classList . remove ( 'd-none' ) ;
404-
405- setTimeout ( function ( ) {
406- bugsterText . innerHTML = `<div class="spinner-grow spinner-grow-sm" role="status"></div>` ;
407- bugsterDialog . classList . remove ( 'd-none' ) ;
408- } , 500 ) ;
409-
410- setTimeout ( ( ) => bugsterXHR ( ) , 1500 ) ;
411- } , 500 ) ;
412- } ) ;
400+ prepareXHR ( ) ;
413401 }
414402
415403 fadeOut ( document . querySelector ( '.bugster-hero' ) , function ( ) {
416- bugsterChatFooterCollapse . hide ( ) ;
417- bugsterChatFooter . addEventListener ( 'hidden.bs.collapse' , event => {
418- setTimeout ( ( ) => {
419- bugsterChat . classList . remove ( 'd-none' ) ;
420- userText . innerHTML = `<p>${ escapeHtml ( question . value ) } </p>` ;
421- userDialog . classList . remove ( 'd-none' ) ;
422-
423- setTimeout ( function ( ) {
424- bugsterText . innerHTML = `<div class="spinner-grow spinner-grow-sm" role="status"></div>` ;
425- bugsterDialog . classList . remove ( 'd-none' ) ;
426- } , 500 ) ;
427-
428- setTimeout ( ( ) => bugsterXHR ( ) , 1500 ) ;
429- } , 500 ) ;
430- } ) ;
404+ prepareXHR ( ) ;
431405 } ) ;
432406 }
433407 } ) ;
434408
409+ const prepareXHR = ( ) => {
410+ const userMsg = { sender : 'user' , text : theQuestion } ;
411+ chatMessages . push ( userMsg ) ;
412+ renderMessage ( userMsg ) ;
413+
414+ const bugsterMsg = { sender : 'bugster' , text : '' , loading : true } ;
415+ chatMessages . push ( bugsterMsg ) ;
416+ renderMessage ( bugsterMsg ) ;
417+
418+ scrollBottom ( ) ;
419+
420+ setTimeout ( ( ) => bugsterXHR ( ) , 500 ) ;
421+ }
422+
435423 let isRequestInProgress = false ;
436424
437425 const bugsterXHR = ( ) => {
438426 if ( isRequestInProgress ) return ;
439427
440428 isRequestInProgress = true ;
429+
441430 const xhr = new XMLHttpRequest ( ) ;
442431 xhr . open ( "POST" , "https://bugster3.elmah.io/api/docs/ask" , true ) ;
443432 xhr . setRequestHeader ( "Content-Type" , "application/json" ) ;
444433 xhr . setRequestHeader ( "Accept" , "text/plain" ) ;
445434
446435 xhr . onprogress = function ( progressEvent ) {
447436 const { target } = progressEvent ;
448- if ( bugsterText . innerHTML === '<div class="spinner-grow spinner-grow-sm" role="status"></div>' ) {
449- bugsterText . innerHTML = '' ;
450- }
451437 if ( target . status === 200 ) {
452- bugsterText . innerHTML = md . render ( target . response ) ;
438+ updateBugsterLastMessage ( md . render ( target . response ) ) ;
453439 }
454440 } ;
455441
456442 xhr . onload = function ( ) {
457443 if ( xhr . status === 200 ) {
458- // Same as in your `done` function in jQuery
459- bugsterText . querySelectorAll ( 'pre code' ) . forEach ( codeElement => {
444+ if ( ! bugsterSessionId ) {
445+ bugsterSessionId = xhr . getResponseHeader ( "X-Bugster-SessionId" ) ;
446+ }
447+
448+ const lastBugsterMsg = bugsterChat . querySelectorAll ( '.bugster-dialog' ) ;
449+ const el = lastBugsterMsg [ lastBugsterMsg . length - 1 ] ;
450+
451+ el . querySelectorAll ( 'pre code' ) . forEach ( codeElement => {
460452 codeElement . parentNode . style . padding = "0px" ;
461453 hljs . highlightElement ( codeElement ) ;
462454 } ) ;
463455
464- bugsterText . querySelectorAll ( 'a' ) . forEach ( aElement => {
456+ el . querySelectorAll ( 'a' ) . forEach ( aElement => {
465457 aElement . target = "_blank" ;
466458 aElement . rel = "noopener noreferrer" ;
467459 } ) ;
468460
469- bugsterText . querySelectorAll ( 'table' ) . forEach ( tableElement => {
461+ el . querySelectorAll ( 'table' ) . forEach ( tableElement => {
470462 tableElement . classList . add ( 'table' ) ;
471463 tableElement . querySelector ( 'thead' ) . classList . add ( 'table-dark' ) ;
472464 } ) ;
473465
474- askAnotherQuestion . classList . remove ( 'd-none' ) ;
475-
476466 isRequestInProgress = false ;
477467 }
478468 } ;
479469
480470 const payload = JSON . stringify ( {
481- question : question . value ,
482- sessionId : "local"
471+ question : theQuestion ,
472+ ... ( bugsterSessionId ? { sessionId : bugsterSessionId } : { } )
483473 } ) ;
484474 xhr . send ( payload ) ;
485475 }
486476
487- // Ask another question
488- askAnotherQuestion . addEventListener ( 'click' , function ( ) {
489- askAnotherQuestion . classList . add ( 'd-none' ) ;
490- question . value = "" ;
491- bugsterChatFooterCollapse . show ( ) ;
492- } ) ;
477+ const renderMessage = ( msg ) => {
478+ const messageEl = document . createElement ( 'div' ) ;
479+
480+ if ( msg . sender === 'user' ) {
481+ messageEl . className = 'user-dialog' ;
482+ messageEl . innerHTML = `<div class="user-dialog-text"><p>${ msg . text } </p></div>` ;
483+ } else {
484+ messageEl . className = 'bugster-dialog' ;
485+ messageEl . innerHTML = `<div class="bugster-dialog-avatar">
486+ <img class="img-fluid" src="assets/img/bugster.png" width="383" height="374" alt="Bugster">
487+ </div>
488+ <div class="bugster-dialog-text">
489+ <div class="content">
490+ <div class="spinner-grow spinner-grow-sm" role="status"></div>
491+ </div>
492+ </div>` ;
493+ }
494+
495+ bugsterChat . appendChild ( messageEl ) ;
496+ }
497+
498+ const updateBugsterLastMessage = ( text ) => {
499+ const lastBugsterMsg = bugsterChat . querySelectorAll ( '.bugster-dialog' ) ;
500+ const el = lastBugsterMsg [ lastBugsterMsg . length - 1 ] ;
501+ if ( ! el ) return ;
502+
503+ const msgText = el . querySelector ( '.bugster-dialog-text .content' ) ;
504+ msgText . innerHTML = text ;
505+ }
493506
494507 // Reset modal when closed
495508 document . querySelector ( '#bugsterModal' ) . addEventListener ( 'hidden.bs.modal' , ( ) => {
496- document . querySelector ( '.bugster-hero' ) . removeAttribute ( 'style' ) ;
497- bugsterChatFooterCollapse . show ( ) ;
498- bugsterChat . classList . add ( 'd-none' ) ;
499- askAnotherQuestion . classList . add ( 'd-none' ) ;
509+ theQuestion = null ;
500510 question . value = "" ;
501- userDialog . classList . add ( 'd-none' ) ;
502- userText . innerHTML = '' ;
503- bugsterDialog . classList . add ( 'd-none' ) ;
504- bugsterText . innerHTML = '' ;
511+
512+ if ( ! bugsterSessionId ) {
513+ document . querySelector ( '.bugster-hero' ) . removeAttribute ( 'style' ) ;
514+ bugsterChat . classList . add ( 'd-none' ) ;
515+ userDialog . classList . add ( 'd-none' ) ;
516+ userText . innerHTML = '' ;
517+ bugsterDialog . classList . add ( 'd-none' ) ;
518+ bugsterText . innerHTML = '' ;
519+ }
520+ } ) ;
521+
522+ document . querySelector ( '#bugsterModal' ) . addEventListener ( 'show.bs.modal' , ( ) => {
523+ if ( bugsterSessionId ) {
524+ setTimeout ( ( ) => scrollBottom ( ) , 200 ) ;
525+ }
505526 } ) ;
527+
528+ const scrollBottom = ( ) => {
529+ document . querySelector ( '#bugsterModal .modal-body' ) . scrollTop = bugsterChat . scrollHeight ;
530+ }
506531}
507532
508533// FadeOut animation
0 commit comments