11import {
2+ FlowChangeEvent ,
3+ FlowChangeEventHandler ,
24 FlowStateDTO ,
35 FlowStates ,
6+ FlowStep ,
47 FrigadeConfig ,
58 PropertyPayload ,
69 SessionDTO ,
@@ -42,6 +45,10 @@ export class Frigade extends Fetchable {
4245 * @ignore
4346 */
4447 private lastSessionDTO ?: SessionDTO
48+ /**
49+ * @ignore
50+ */
51+ private eventHandlers : Map < FlowChangeEvent , FlowChangeEventHandler [ ] > = new Map ( )
4552
4653 /**
4754 * @ignore
@@ -366,10 +373,14 @@ export class Frigade extends Fetchable {
366373 }
367374 this . initPromise = null
368375 await this . init ( this . config )
376+ this . triggerAllLegacyEventHandlers ( )
369377 this . triggerAllEventHandlers ( )
370378 }
371379
372- private triggerAllEventHandlers ( ) {
380+ /**
381+ * @ignore
382+ */
383+ private triggerAllLegacyEventHandlers ( ) {
373384 this . flows . forEach ( ( flow ) => {
374385 this . getGlobalState ( ) . onFlowStateChangeHandlers . forEach ( ( handler ) => {
375386 const lastFlow = this . getGlobalState ( ) . previousFlows . get ( flow . id )
@@ -379,19 +390,48 @@ export class Frigade extends Fetchable {
379390 } )
380391 }
381392
393+ private triggerAllEventHandlers ( ) {
394+ this . flows . forEach ( ( flow ) => {
395+ const lastFlow = this . getGlobalState ( ) . previousFlows . get ( flow . id )
396+ this . triggerEventHandlers ( flow . rawData , lastFlow ?. rawData )
397+ } )
398+ }
399+
382400 private async resync ( ) {
383401 await this . refreshStateFromAPI ( )
384402 }
385403
386404 /**
387405 * Event handler that captures all changes that happen to the state of the Flows.
406+ * @deprecated Use `frigade.on` instead.
388407 * @param handler
389408 */
390409 public async onStateChange ( handler : ( flow : Flow , previousFlow ?: Flow ) => void ) {
391410 await this . initIfNeeded ( )
392411 this . getGlobalState ( ) . onFlowStateChangeHandlers . push ( handler )
393412 }
394413
414+ /**
415+ * Event handler that captures all changes that happen to the state of the Flows. Use `flow.any` to capture all events.
416+ * @param event `flow.any` | `flow.complete` | `flow.restart` | `flow.skip` | `flow.start` | `step.complete` | `step.skip` | `step.reset` | `step.start`
417+ * @param handler
418+ */
419+ public async on ( event : FlowChangeEvent , handler : FlowChangeEventHandler ) {
420+ this . eventHandlers . set ( event , [ ...( this . eventHandlers . get ( event ) ?? [ ] ) , handler ] )
421+ }
422+
423+ /**
424+ * Removes the given handler.
425+ * @param event `flow.any` | `flow.complete` | `flow.restart` | `flow.skip` | `flow.start` | `step.complete` | `step.skip` | `step.reset` | `step.start`
426+ * @param handler
427+ */
428+ public async off ( event : FlowChangeEvent , handler : FlowChangeEventHandler ) {
429+ this . eventHandlers . set (
430+ event ,
431+ ( this . eventHandlers . get ( event ) ?? [ ] ) . filter ( ( h ) => h !== handler )
432+ )
433+ }
434+
395435 /**
396436 * Returns true if the JS SDK failed to connect to the Frigade API.
397437 */
@@ -435,6 +475,7 @@ export class Frigade extends Fetchable {
435475 const previousState = target [ key ] as StatefulFlow
436476 const newState = value as StatefulFlow
437477 if ( JSON . stringify ( previousState ) !== JSON . stringify ( newState ) ) {
478+ that . triggerDeprecatedEventHandlers ( newState , previousState )
438479 that . triggerEventHandlers ( newState , previousState )
439480 }
440481 }
@@ -515,6 +556,7 @@ export class Frigade extends Fetchable {
515556 // Necessary check to prevent race condition between flow state and collection state
516557 ! overrideFlowStateRaw
517558 ) {
559+ this . triggerAllLegacyEventHandlers ( )
518560 this . triggerAllEventHandlers ( )
519561 }
520562 }
@@ -588,7 +630,10 @@ export class Frigade extends Fetchable {
588630 /**
589631 * @ignore
590632 */
591- private async triggerEventHandlers ( newState : StatefulFlow , previousState ?: StatefulFlow ) {
633+ private async triggerDeprecatedEventHandlers (
634+ newState : StatefulFlow ,
635+ previousState ?: StatefulFlow
636+ ) {
592637 if ( newState ) {
593638 this . flows . forEach ( ( flow ) => {
594639 if ( flow . id == previousState . flowSlug ) {
@@ -603,6 +648,113 @@ export class Frigade extends Fetchable {
603648 }
604649 }
605650
651+ /**
652+ * @ignore
653+ */
654+ private triggerEventHandlers ( newState : StatefulFlow , previousState ?: StatefulFlow ) {
655+ if ( newState ) {
656+ for ( const flow of this . flows ) {
657+ if ( flow . id == newState . flowSlug ) {
658+ const lastFlow = this . getGlobalState ( ) . previousFlows . get ( flow . id )
659+ flow . resyncState ( newState )
660+ for ( const [ event , handlers ] of this . eventHandlers . entries ( ) ) {
661+ switch ( event ) {
662+ case 'flow.complete' :
663+ if ( newState . $state . completed && previousState ?. $state . completed === false ) {
664+ handlers . forEach ( ( handler ) => handler ( event , flow , lastFlow ) )
665+ }
666+ break
667+ case 'flow.restart' :
668+ if ( ! newState . $state . started && previousState ?. $state . started === true ) {
669+ handlers . forEach ( ( handler ) => handler ( event , flow , lastFlow ) )
670+ }
671+ break
672+ case 'flow.skip' :
673+ if ( newState . $state . skipped && previousState ?. $state . skipped === false ) {
674+ handlers . forEach ( ( handler ) => handler ( event , flow , lastFlow ) )
675+ }
676+ break
677+ case 'flow.start' :
678+ if ( newState . $state . started && previousState ?. $state . started === false ) {
679+ handlers . forEach ( ( handler ) => handler ( event , flow , lastFlow ) )
680+ }
681+ break
682+ case 'step.complete' :
683+ for ( const step of newState . data . steps ?? [ ] ) {
684+ if (
685+ step . $state . completed &&
686+ ! previousState ?. data . steps . find (
687+ ( previousStepState ) =>
688+ previousStepState . id === step . id && previousStepState . $state . completed
689+ )
690+ ) {
691+ handlers . forEach ( ( handler ) =>
692+ handler ( event , flow , lastFlow , flow . steps . get ( step . id ) )
693+ )
694+ }
695+ }
696+ break
697+ case 'step.reset' :
698+ for ( const step of newState . data . steps ?? [ ] ) {
699+ const previousStep = previousState ?. data . steps . find (
700+ ( previousStepState ) => previousStepState . id === step . id
701+ )
702+ if (
703+ step . $state . started == false &&
704+ ! step . $state . lastActionAt &&
705+ previousStep ?. $state . started &&
706+ previousStep ?. $state . lastActionAt
707+ ) {
708+ handlers . forEach ( ( handler ) =>
709+ handler ( event , flow , lastFlow , flow . steps . get ( step . id ) )
710+ )
711+ }
712+ }
713+ break
714+ case 'step.skip' :
715+ for ( const step of newState . data . steps ?? [ ] ) {
716+ if (
717+ step . $state . skipped &&
718+ ! previousState ?. data . steps . find (
719+ ( previousStepState ) =>
720+ previousStepState . id === step . id && previousStepState . $state . skipped
721+ )
722+ ) {
723+ handlers . forEach ( ( handler ) =>
724+ handler ( event , flow , lastFlow , flow . steps . get ( step . id ) )
725+ )
726+ }
727+ }
728+ break
729+ case 'step.start' :
730+ for ( const step of newState . data . steps ?? [ ] ) {
731+ if (
732+ step . $state . started &&
733+ previousState ?. data . steps . find (
734+ ( previousStepState ) =>
735+ previousStepState . id === step . id &&
736+ previousStepState . $state . started === false
737+ )
738+ ) {
739+ handlers . forEach ( ( handler ) =>
740+ handler ( event , flow , lastFlow , flow . steps . get ( step . id ) )
741+ )
742+ }
743+ }
744+ break
745+ case 'flow.any' :
746+ if ( JSON . stringify ( newState ) !== JSON . stringify ( previousState ?? { } ) ) {
747+ handlers . forEach ( ( handler ) => handler ( event , flow , lastFlow ) )
748+ }
749+ break
750+ }
751+ }
752+ this . getGlobalState ( ) . previousFlows . set ( flow . id , cloneFlow ( flow ) )
753+ }
754+ }
755+ }
756+ }
757+
606758 /**
607759 * @ignore
608760 */
0 commit comments