@@ -28,6 +28,7 @@ import {
2828 GuideData ,
2929 GuideGroupAddedEvent ,
3030 GuideGroupUpdatedEvent ,
31+ GuideLivePreviewUpdatedEvent ,
3132 GuideRemovedEvent ,
3233 GuideSocketEvent ,
3334 GuideStepData ,
@@ -58,7 +59,10 @@ const DEFAULT_COUNTER_INCREMENT_INTERVAL = 30 * 1000; // in milliseconds
5859const SUBSCRIBE_RETRY_LIMIT = 3 ;
5960
6061// Debug query param keys
61- const DEBUG_GUIDE_KEY_PARAM = "knock_guide_key" ;
62+ export const DEBUG_QUERY_PARAMS = {
63+ GUIDE_KEY : "knock_guide_key" ,
64+ PREVIEW_SESSION_ID : "knock_preview_session_id" ,
65+ } ;
6266
6367// Return the global window object if defined, so to safely guard against SSR.
6468const checkForWindow = ( ) => {
@@ -74,13 +78,14 @@ export const guidesApiRootPath = (userId: string | undefined | null) =>
7478const detectDebugParams = ( ) : DebugState => {
7579 const win = checkForWindow ( ) ;
7680 if ( ! win ) {
77- return { forcedGuideKey : null } ;
81+ return { forcedGuideKey : null , previewSessionId : null } ;
7882 }
7983
8084 const urlParams = new URLSearchParams ( win . location . search ) ;
81- const forcedGuideKey = urlParams . get ( DEBUG_GUIDE_KEY_PARAM ) ;
85+ const forcedGuideKey = urlParams . get ( DEBUG_QUERY_PARAMS . GUIDE_KEY ) ;
86+ const previewSessionId = urlParams . get ( DEBUG_QUERY_PARAMS . PREVIEW_SESSION_ID ) ;
8287
83- return { forcedGuideKey } ;
88+ return { forcedGuideKey, previewSessionId } ;
8489} ;
8590
8691const select = ( state : StoreState , filters : SelectFilterParams = { } ) => {
@@ -103,7 +108,7 @@ const select = (state: StoreState, filters: SelectFilterParams = {}) => {
103108 }
104109
105110 for ( const [ index , guideKey ] of displaySequence . entries ( ) ) {
106- const guide = state . guides [ guideKey ] ;
111+ let guide = state . guides [ guideKey ] ;
107112 if ( ! guide ) continue ;
108113
109114 const affirmed = predicate ( guide , {
@@ -113,6 +118,14 @@ const select = (state: StoreState, filters: SelectFilterParams = {}) => {
113118 } ) ;
114119 if ( ! affirmed ) continue ;
115120
121+ // Use preview guide if it exists and matches the forced guide key
122+ if (
123+ state . debug . forcedGuideKey === guideKey &&
124+ state . previewGuides [ guideKey ]
125+ ) {
126+ guide = state . previewGuides [ guideKey ] ;
127+ }
128+
116129 result . set ( index , guide ) ;
117130 }
118131
@@ -177,6 +190,7 @@ export class KnockGuideClient {
177190 "guide.removed" ,
178191 "guide_group.added" ,
179192 "guide_group.updated" ,
193+ "guide.live_preview_updated" ,
180194 ] ;
181195 private subscribeRetryCount = 0 ;
182196
@@ -208,6 +222,7 @@ export class KnockGuideClient {
208222 guideGroups : [ ] ,
209223 guideGroupDisplayLogs : { } ,
210224 guides : { } ,
225+ previewGuides : { } ,
211226 queries : { } ,
212227 location,
213228 // Increment to update the state store and trigger re-selection.
@@ -331,6 +346,7 @@ export class KnockGuideClient {
331346 ...this . targetParams ,
332347 user_id : this . knock . userId ,
333348 force_all_guides : debugState . forcedGuideKey ? true : undefined ,
349+ preview_session_id : debugState . previewSessionId || undefined ,
334350 } ;
335351
336352 const newChannel = this . socket . channel ( this . socketChannelTopic , params ) ;
@@ -411,6 +427,9 @@ export class KnockGuideClient {
411427 case "guide_group.updated" :
412428 return this . addOrReplaceGuideGroup ( payload ) ;
413429
430+ case "guide.live_preview_updated" :
431+ return this . updatePreviewGuide ( payload ) ;
432+
414433 default :
415434 return ;
416435 }
@@ -420,11 +439,19 @@ export class KnockGuideClient {
420439 // Make sure to clear out the stage.
421440 this . clearGroupStage ( ) ;
422441
423- this . store . setState ( ( state ) => ( {
424- ...state ,
425- ...additionalParams ,
426- location : href ,
427- } ) ) ;
442+ this . store . setState ( ( state ) => {
443+ // Clear preview guides if no longer in preview mode
444+ const previewGuides = additionalParams ?. debug ?. previewSessionId
445+ ? state . previewGuides
446+ : { } ;
447+
448+ return {
449+ ...state ,
450+ ...additionalParams ,
451+ previewGuides,
452+ location : href ,
453+ } ;
454+ } ) ;
428455 }
429456
430457 //
@@ -965,6 +992,15 @@ export class KnockGuideClient {
965992 } ) ;
966993 }
967994
995+ private updatePreviewGuide ( { data } : GuideLivePreviewUpdatedEvent ) {
996+ const guide = this . localCopy ( data . guide ) ;
997+
998+ this . store . setState ( ( state ) => {
999+ const previewGuides = { ...state . previewGuides , [ guide . key ] : guide } ;
1000+ return { ...state , previewGuides } ;
1001+ } ) ;
1002+ }
1003+
9681004 // Define as an arrow func property to always bind this to the class instance.
9691005 private handleLocationChange = ( ) => {
9701006 const win = checkForWindow ( ) ;
@@ -980,20 +1016,30 @@ export class KnockGuideClient {
9801016 const newDebugParams = detectDebugParams ( ) ;
9811017 this . setLocation ( href , { debug : newDebugParams } ) ;
9821018
983- // If entering/exiting debug mode, refetch guides and resubscribe to the
984- // websocket channel.
985- if (
986- Boolean ( currentDebugParams . forcedGuideKey ) !==
987- Boolean ( newDebugParams . forcedGuideKey )
988- ) {
1019+ // If debug state has changed, refetch guides and resubscribe to the websocket channel
1020+ const debugStateChanged = this . checkDebugStateChanged (
1021+ currentDebugParams ,
1022+ newDebugParams ,
1023+ ) ;
1024+
1025+ if ( debugStateChanged ) {
9891026 this . knock . log (
990- `[Guide] ${ newDebugParams . forcedGuideKey ? "Entering" : "Exiting" } debug mode ` ,
1027+ `[Guide] Debug state changed, refetching guides and resubscribing to the websocket channel ` ,
9911028 ) ;
9921029 this . fetch ( ) ;
9931030 this . subscribe ( ) ;
9941031 }
9951032 } ;
9961033
1034+ // Returns whether debug params have changed. For guide key, we only check
1035+ // presence since the exact value has no impact on fetch/subscribe
1036+ private checkDebugStateChanged ( a : DebugState , b : DebugState ) : boolean {
1037+ return (
1038+ Boolean ( a . forcedGuideKey ) !== Boolean ( b . forcedGuideKey ) ||
1039+ a . previewSessionId !== b . previewSessionId
1040+ ) ;
1041+ }
1042+
9971043 private listenForLocationChangesFromWindow ( ) {
9981044 const win = checkForWindow ( ) ;
9991045 if ( win ?. history ) {
0 commit comments