@knocklabs/client 0.14.3 → 0.14.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/cjs/clients/feed/feed.js +1 -1
- package/dist/cjs/clients/feed/feed.js.map +1 -1
- package/dist/cjs/clients/guide/client.js +1 -1
- package/dist/cjs/clients/guide/client.js.map +1 -1
- package/dist/cjs/helpers.js +2 -0
- package/dist/cjs/helpers.js.map +1 -0
- package/dist/cjs/knock.js +1 -1
- package/dist/cjs/knock.js.map +1 -1
- package/dist/esm/clients/feed/feed.mjs +41 -37
- package/dist/esm/clients/feed/feed.mjs.map +1 -1
- package/dist/esm/clients/guide/client.mjs +123 -65
- package/dist/esm/clients/guide/client.mjs.map +1 -1
- package/dist/esm/helpers.mjs +8 -0
- package/dist/esm/helpers.mjs.map +1 -0
- package/dist/esm/knock.mjs +24 -24
- package/dist/esm/knock.mjs.map +1 -1
- package/dist/types/clients/feed/feed.d.ts.map +1 -1
- package/dist/types/clients/guide/client.d.ts +22 -1
- package/dist/types/clients/guide/client.d.ts.map +1 -1
- package/dist/types/helpers.d.ts +2 -0
- package/dist/types/helpers.d.ts.map +1 -0
- package/dist/types/knock.d.ts +1 -1
- package/dist/types/knock.d.ts.map +1 -1
- package/package.json +5 -4
- package/src/clients/feed/feed.ts +8 -0
- package/src/clients/guide/client.ts +146 -2
- package/src/helpers.ts +6 -0
- package/src/knock.ts +2 -2
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { GenericData } from '@knocklabs/types';
|
|
2
2
|
import { Store } from '@tanstack/store';
|
|
3
|
+
import { URLPattern } from 'urlpattern-polyfill';
|
|
3
4
|
import { default as Knock } from '../../knock';
|
|
4
5
|
export declare const guidesApiRootPath: (userId: string | undefined | null) => string;
|
|
5
6
|
interface StepMessageState {
|
|
@@ -18,6 +19,10 @@ interface GuideStepData {
|
|
|
18
19
|
message: StepMessageState;
|
|
19
20
|
content: any;
|
|
20
21
|
}
|
|
22
|
+
interface GuideActivationLocationRuleData {
|
|
23
|
+
directive: "allow" | "block";
|
|
24
|
+
pathname: string;
|
|
25
|
+
}
|
|
21
26
|
interface GuideData {
|
|
22
27
|
__typename: "Guide";
|
|
23
28
|
channel_id: string;
|
|
@@ -27,6 +32,7 @@ interface GuideData {
|
|
|
27
32
|
type: string;
|
|
28
33
|
semver: string;
|
|
29
34
|
steps: GuideStepData[];
|
|
35
|
+
activation_location_rules: GuideActivationLocationRuleData[];
|
|
30
36
|
inserted_at: string;
|
|
31
37
|
updated_at: string;
|
|
32
38
|
}
|
|
@@ -37,8 +43,12 @@ export interface KnockGuideStep extends GuideStepData {
|
|
|
37
43
|
}) => void;
|
|
38
44
|
markAsArchived: () => void;
|
|
39
45
|
}
|
|
46
|
+
interface KnockGuideActivationLocationRule extends GuideActivationLocationRuleData {
|
|
47
|
+
pattern: URLPattern;
|
|
48
|
+
}
|
|
40
49
|
export interface KnockGuide extends GuideData {
|
|
41
50
|
steps: KnockGuideStep[];
|
|
51
|
+
activation_location_rules: KnockGuideActivationLocationRule[];
|
|
42
52
|
}
|
|
43
53
|
type GetGuidesQueryParams = {
|
|
44
54
|
data?: string;
|
|
@@ -60,6 +70,7 @@ type QueryStatus = {
|
|
|
60
70
|
type StoreState = {
|
|
61
71
|
guides: KnockGuide[];
|
|
62
72
|
queries: Record<QueryKey, QueryStatus>;
|
|
73
|
+
location: string | undefined;
|
|
63
74
|
};
|
|
64
75
|
type QueryFilterParams = Pick<GetGuidesQueryParams, "type">;
|
|
65
76
|
export type SelectFilterParams = {
|
|
@@ -70,16 +81,23 @@ export type TargetParams = {
|
|
|
70
81
|
data?: GenericData | undefined;
|
|
71
82
|
tenant?: string | undefined;
|
|
72
83
|
};
|
|
84
|
+
type ConstructorOpts = {
|
|
85
|
+
trackLocationFromWindow?: boolean;
|
|
86
|
+
};
|
|
73
87
|
export declare class KnockGuideClient {
|
|
74
88
|
readonly knock: Knock;
|
|
75
89
|
readonly channelId: string;
|
|
76
90
|
readonly targetParams: TargetParams;
|
|
91
|
+
readonly options: ConstructorOpts;
|
|
77
92
|
store: Store<StoreState, (state: StoreState) => StoreState>;
|
|
78
93
|
private socket;
|
|
79
94
|
private socketChannel;
|
|
80
95
|
private socketChannelTopic;
|
|
81
96
|
private socketEventTypes;
|
|
82
|
-
|
|
97
|
+
private pushStateFn;
|
|
98
|
+
private replaceStateFn;
|
|
99
|
+
constructor(knock: Knock, channelId: string, targetParams?: TargetParams, options?: ConstructorOpts);
|
|
100
|
+
cleanup(): void;
|
|
83
101
|
fetch(opts?: {
|
|
84
102
|
filters?: QueryFilterParams;
|
|
85
103
|
}): Promise<QueryStatus>;
|
|
@@ -98,6 +116,9 @@ export declare class KnockGuideClient {
|
|
|
98
116
|
private addGuide;
|
|
99
117
|
private replaceOrAddGuide;
|
|
100
118
|
private removeGuide;
|
|
119
|
+
private handleLocationChange;
|
|
120
|
+
private listenForLocationChangesFromWindow;
|
|
121
|
+
private removeEventListeners;
|
|
101
122
|
}
|
|
102
123
|
export {};
|
|
103
124
|
//# sourceMappingURL=client.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../../src/clients/guide/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../../src/clients/guide/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/helpers.ts"],"names":[],"mappings":"AAGA,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,WAEvC"}
|
package/dist/types/knock.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"knock.d.ts","sourceRoot":"","sources":["../../src/knock.ts"],"names":[],"mappings":"AAEA,OAAO,SAAS,MAAM,OAAO,CAAC;AAC9B,OAAO,UAAU,MAAM,gBAAgB,CAAC;AACxC,OAAO,aAAa,MAAM,oBAAoB,CAAC;AAC/C,OAAO,aAAa,MAAM,oBAAoB,CAAC;AAC/C,OAAO,YAAY,MAAM,mBAAmB,CAAC;AAC7C,OAAO,WAAW,MAAM,uBAAuB,CAAC;AAChD,OAAO,WAAW,MAAM,iBAAiB,CAAC;AAC1C,OAAO,UAAU,MAAM,iBAAiB,CAAC;AACzC,OAAO,EACL,mBAAmB,EACnB,YAAY,EACZ,QAAQ,EAET,MAAM,cAAc,CAAC;AAItB,cAAM,KAAK;IAgBP,QAAQ,CAAC,MAAM,EAAE,MAAM;IAflB,IAAI,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,SAAS,CAA0B;IACpC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IAC3B,OAAO,CAAC,oBAAoB,CAA8C;IAC1E,QAAQ,CAAC,KAAK,aAAwB;IACtC,QAAQ,CAAC,OAAO,eAA0B;IAC1C,QAAQ,CAAC,WAAW,cAAyB;IAC7C,QAAQ,CAAC,KAAK,cAAyB;IACvC,QAAQ,CAAC,OAAO,gBAA2B;IAC3C,QAAQ,CAAC,IAAI,aAAwB;IACrC,QAAQ,CAAC,QAAQ,gBAA2B;gBAGjC,MAAM,EAAE,MAAM,EACvB,OAAO,GAAE,YAAiB;IAe5B,MAAM;IAaN,YAAY,CACV,MAAM,EAAE,KAAK,CAAC,QAAQ,CAAC,EACvB,SAAS,CAAC,EAAE,KAAK,CAAC,WAAW,CAAC,EAC9B,OAAO,CAAC,EAAE,mBAAmB;IAyC/B,sBAAsB;IAUtB,eAAe,CAAC,cAAc,UAAQ;IAKtC,QAAQ;IASR,GAAG,CAAC,OAAO,EAAE,MAAM;
|
|
1
|
+
{"version":3,"file":"knock.d.ts","sourceRoot":"","sources":["../../src/knock.ts"],"names":[],"mappings":"AAEA,OAAO,SAAS,MAAM,OAAO,CAAC;AAC9B,OAAO,UAAU,MAAM,gBAAgB,CAAC;AACxC,OAAO,aAAa,MAAM,oBAAoB,CAAC;AAC/C,OAAO,aAAa,MAAM,oBAAoB,CAAC;AAC/C,OAAO,YAAY,MAAM,mBAAmB,CAAC;AAC7C,OAAO,WAAW,MAAM,uBAAuB,CAAC;AAChD,OAAO,WAAW,MAAM,iBAAiB,CAAC;AAC1C,OAAO,UAAU,MAAM,iBAAiB,CAAC;AACzC,OAAO,EACL,mBAAmB,EACnB,YAAY,EACZ,QAAQ,EAET,MAAM,cAAc,CAAC;AAItB,cAAM,KAAK;IAgBP,QAAQ,CAAC,MAAM,EAAE,MAAM;IAflB,IAAI,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,SAAS,CAA0B;IACpC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IAC3B,OAAO,CAAC,oBAAoB,CAA8C;IAC1E,QAAQ,CAAC,KAAK,aAAwB;IACtC,QAAQ,CAAC,OAAO,eAA0B;IAC1C,QAAQ,CAAC,WAAW,cAAyB;IAC7C,QAAQ,CAAC,KAAK,cAAyB;IACvC,QAAQ,CAAC,OAAO,gBAA2B;IAC3C,QAAQ,CAAC,IAAI,aAAwB;IACrC,QAAQ,CAAC,QAAQ,gBAA2B;gBAGjC,MAAM,EAAE,MAAM,EACvB,OAAO,GAAE,YAAiB;IAe5B,MAAM;IAaN,YAAY,CACV,MAAM,EAAE,KAAK,CAAC,QAAQ,CAAC,EACvB,SAAS,CAAC,EAAE,KAAK,CAAC,WAAW,CAAC,EAC9B,OAAO,CAAC,EAAE,mBAAmB;IAyC/B,sBAAsB;IAUtB,eAAe,CAAC,cAAc,UAAQ;IAKtC,QAAQ;IASR,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,UAAQ;IAMlC;;OAEG;IACH,OAAO,CAAC,eAAe;YAQT,gCAAgC;CA8B/C;AAED,eAAe,KAAK,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@knocklabs/client",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.5",
|
|
4
4
|
"description": "The clientside library for interacting with Knock",
|
|
5
5
|
"homepage": "https://github.com/knocklabs/javascript/tree/main/packages/client",
|
|
6
6
|
"author": "@knocklabs",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"@babel/plugin-proposal-object-rest-spread": "^7.16.7",
|
|
54
54
|
"@babel/plugin-transform-runtime": "^7.25.4",
|
|
55
55
|
"@babel/preset-env": "^7.26.0",
|
|
56
|
-
"@babel/preset-typescript": "^7.
|
|
56
|
+
"@babel/preset-typescript": "^7.27.0",
|
|
57
57
|
"@types/jsonwebtoken": "^9.0.9",
|
|
58
58
|
"@typescript-eslint/eslint-plugin": "^8.19.1",
|
|
59
59
|
"@typescript-eslint/parser": "^8.27.0",
|
|
@@ -65,8 +65,8 @@
|
|
|
65
65
|
"rimraf": "^6.0.1",
|
|
66
66
|
"rollup": "^4.34.8",
|
|
67
67
|
"typescript": "^5.8.3",
|
|
68
|
-
"vite": "^5.
|
|
69
|
-
"vitest": "^
|
|
68
|
+
"vite": "^5.4.18",
|
|
69
|
+
"vitest": "^3.1.1"
|
|
70
70
|
},
|
|
71
71
|
"dependencies": {
|
|
72
72
|
"@babel/runtime": "^7.27.0",
|
|
@@ -78,6 +78,7 @@
|
|
|
78
78
|
"eventemitter2": "^6.4.5",
|
|
79
79
|
"jwt-decode": "^4.0.0",
|
|
80
80
|
"phoenix": "1.7.19",
|
|
81
|
+
"urlpattern-polyfill": "^10.0.0",
|
|
81
82
|
"zustand": "^4.5.6"
|
|
82
83
|
}
|
|
83
84
|
}
|
package/src/clients/feed/feed.ts
CHANGED
|
@@ -3,6 +3,7 @@ import EventEmitter from "eventemitter2";
|
|
|
3
3
|
import { Channel } from "phoenix";
|
|
4
4
|
import type { StoreApi } from "zustand";
|
|
5
5
|
|
|
6
|
+
import { isValidUuid } from "../../helpers";
|
|
6
7
|
import Knock from "../../knock";
|
|
7
8
|
import { NetworkStatus, isRequestInFlight } from "../../networkStatus";
|
|
8
9
|
import {
|
|
@@ -57,6 +58,13 @@ class Feed {
|
|
|
57
58
|
readonly feedId: string,
|
|
58
59
|
options: FeedClientOptions,
|
|
59
60
|
) {
|
|
61
|
+
if (!feedId || !isValidUuid(feedId)) {
|
|
62
|
+
this.knock.log(
|
|
63
|
+
"[Feed] Invalid or missing feedId provided to the Feed constructor. The feed should be a UUID of an in-app feed channel (`in_app_feed`) found in the Knock dashboard. Please provide a valid feedId to the Feed constructor.",
|
|
64
|
+
true,
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
60
68
|
this.feedId = feedId;
|
|
61
69
|
this.userFeedId = this.buildUserFeedId();
|
|
62
70
|
this.store = createStore();
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { GenericData } from "@knocklabs/types";
|
|
2
2
|
import { Store } from "@tanstack/store";
|
|
3
3
|
import { Channel, Socket } from "phoenix";
|
|
4
|
+
import { URLPattern } from "urlpattern-polyfill";
|
|
4
5
|
|
|
5
6
|
import Knock from "../../knock";
|
|
6
7
|
|
|
@@ -38,6 +39,11 @@ interface GuideStepData {
|
|
|
38
39
|
content: any;
|
|
39
40
|
}
|
|
40
41
|
|
|
42
|
+
interface GuideActivationLocationRuleData {
|
|
43
|
+
directive: "allow" | "block";
|
|
44
|
+
pathname: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
41
47
|
interface GuideData {
|
|
42
48
|
__typename: "Guide";
|
|
43
49
|
channel_id: string;
|
|
@@ -47,6 +53,7 @@ interface GuideData {
|
|
|
47
53
|
type: string;
|
|
48
54
|
semver: string;
|
|
49
55
|
steps: GuideStepData[];
|
|
56
|
+
activation_location_rules: GuideActivationLocationRuleData[];
|
|
50
57
|
inserted_at: string;
|
|
51
58
|
updated_at: string;
|
|
52
59
|
}
|
|
@@ -57,8 +64,14 @@ export interface KnockGuideStep extends GuideStepData {
|
|
|
57
64
|
markAsArchived: () => void;
|
|
58
65
|
}
|
|
59
66
|
|
|
67
|
+
interface KnockGuideActivationLocationRule
|
|
68
|
+
extends GuideActivationLocationRuleData {
|
|
69
|
+
pattern: URLPattern;
|
|
70
|
+
}
|
|
71
|
+
|
|
60
72
|
export interface KnockGuide extends GuideData {
|
|
61
73
|
steps: KnockGuideStep[];
|
|
74
|
+
activation_location_rules: KnockGuideActivationLocationRule[];
|
|
62
75
|
}
|
|
63
76
|
|
|
64
77
|
type GetGuidesQueryParams = {
|
|
@@ -133,6 +146,7 @@ type QueryStatus = {
|
|
|
133
146
|
type StoreState = {
|
|
134
147
|
guides: KnockGuide[];
|
|
135
148
|
queries: Record<QueryKey, QueryStatus>;
|
|
149
|
+
location: string | undefined;
|
|
136
150
|
};
|
|
137
151
|
|
|
138
152
|
type QueryFilterParams = Pick<GetGuidesQueryParams, "type">;
|
|
@@ -147,6 +161,10 @@ export type TargetParams = {
|
|
|
147
161
|
tenant?: string | undefined;
|
|
148
162
|
};
|
|
149
163
|
|
|
164
|
+
type ConstructorOpts = {
|
|
165
|
+
trackLocationFromWindow?: boolean;
|
|
166
|
+
};
|
|
167
|
+
|
|
150
168
|
export class KnockGuideClient {
|
|
151
169
|
public store: Store<StoreState, (state: StoreState) => StoreState>;
|
|
152
170
|
|
|
@@ -156,14 +174,26 @@ export class KnockGuideClient {
|
|
|
156
174
|
private socketChannelTopic: string;
|
|
157
175
|
private socketEventTypes = ["guide.added", "guide.updated", "guide.removed"];
|
|
158
176
|
|
|
177
|
+
// Original history methods to monkey patch, or restore in cleanups.
|
|
178
|
+
private pushStateFn: History["pushState"] | undefined;
|
|
179
|
+
private replaceStateFn: History["replaceState"] | undefined;
|
|
180
|
+
|
|
159
181
|
constructor(
|
|
160
182
|
readonly knock: Knock,
|
|
161
183
|
readonly channelId: string,
|
|
162
184
|
readonly targetParams: TargetParams = {},
|
|
185
|
+
readonly options: ConstructorOpts = {},
|
|
163
186
|
) {
|
|
187
|
+
const { trackLocationFromWindow = true } = options;
|
|
188
|
+
|
|
189
|
+
const location = trackLocationFromWindow
|
|
190
|
+
? window?.location.href
|
|
191
|
+
: undefined;
|
|
192
|
+
|
|
164
193
|
this.store = new Store<StoreState>({
|
|
165
194
|
guides: [],
|
|
166
195
|
queries: {},
|
|
196
|
+
location,
|
|
167
197
|
});
|
|
168
198
|
|
|
169
199
|
// In server environments we might not have a socket connection.
|
|
@@ -171,9 +201,18 @@ export class KnockGuideClient {
|
|
|
171
201
|
this.socket = maybeSocket;
|
|
172
202
|
this.socketChannelTopic = `guides:${channelId}`;
|
|
173
203
|
|
|
204
|
+
if (trackLocationFromWindow) {
|
|
205
|
+
this.listenForLocationChangesFromWindow();
|
|
206
|
+
}
|
|
207
|
+
|
|
174
208
|
this.knock.log("[Guide] Initialized a guide client");
|
|
175
209
|
}
|
|
176
210
|
|
|
211
|
+
cleanup() {
|
|
212
|
+
this.unsubscribe();
|
|
213
|
+
this.removeEventListeners();
|
|
214
|
+
}
|
|
215
|
+
|
|
177
216
|
async fetch(opts?: { filters?: QueryFilterParams }) {
|
|
178
217
|
this.knock.failIfNotAuthenticated();
|
|
179
218
|
this.knock.log("[Guide] Loading all eligible guides");
|
|
@@ -291,8 +330,6 @@ export class KnockGuideClient {
|
|
|
291
330
|
//
|
|
292
331
|
|
|
293
332
|
select(state: StoreState, filters: SelectFilterParams = {}) {
|
|
294
|
-
// TODO(KNO-7790): Need to evaluate activation rules also.
|
|
295
|
-
|
|
296
333
|
return state.guides.filter((guide) => {
|
|
297
334
|
if (filters.type && filters.type !== guide.type) {
|
|
298
335
|
return false;
|
|
@@ -302,6 +339,42 @@ export class KnockGuideClient {
|
|
|
302
339
|
return false;
|
|
303
340
|
}
|
|
304
341
|
|
|
342
|
+
const locationRules = guide.activation_location_rules || [];
|
|
343
|
+
|
|
344
|
+
if (locationRules.length > 0 && state.location) {
|
|
345
|
+
const allowed = locationRules.reduce<boolean | undefined>(
|
|
346
|
+
(acc, rule) => {
|
|
347
|
+
// Any matched block rule prevails so no need to evaluate further
|
|
348
|
+
// as soon as there is one.
|
|
349
|
+
if (acc === false) return false;
|
|
350
|
+
|
|
351
|
+
// At this point we either have a matched allow rule (acc is true),
|
|
352
|
+
// or no matched rule found yet (acc is undefined).
|
|
353
|
+
|
|
354
|
+
switch (rule.directive) {
|
|
355
|
+
case "allow": {
|
|
356
|
+
// No need to evaluate more allow rules once we matched one
|
|
357
|
+
// since any matched allowed rule means allow.
|
|
358
|
+
if (acc === true) return true;
|
|
359
|
+
|
|
360
|
+
const matched = rule.pattern.test(state.location);
|
|
361
|
+
return matched ? true : undefined;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
case "block": {
|
|
365
|
+
// Always test block rules (unless already matched to block)
|
|
366
|
+
// because they'd prevail over matched allow rules.
|
|
367
|
+
const matched = rule.pattern.test(state.location);
|
|
368
|
+
return matched ? false : acc;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
},
|
|
372
|
+
undefined,
|
|
373
|
+
);
|
|
374
|
+
|
|
375
|
+
if (!allowed) return false;
|
|
376
|
+
}
|
|
377
|
+
|
|
305
378
|
return true;
|
|
306
379
|
});
|
|
307
380
|
}
|
|
@@ -427,6 +500,14 @@ export class KnockGuideClient {
|
|
|
427
500
|
return localStep;
|
|
428
501
|
});
|
|
429
502
|
|
|
503
|
+
localGuide.activation_location_rules =
|
|
504
|
+
remoteGuide.activation_location_rules.map((rule) => {
|
|
505
|
+
return {
|
|
506
|
+
...rule,
|
|
507
|
+
pattern: new URLPattern({ pathname: rule.pathname }),
|
|
508
|
+
};
|
|
509
|
+
});
|
|
510
|
+
|
|
430
511
|
return localGuide as KnockGuide;
|
|
431
512
|
}
|
|
432
513
|
|
|
@@ -538,4 +619,67 @@ export class KnockGuideClient {
|
|
|
538
619
|
return { ...state, guides };
|
|
539
620
|
});
|
|
540
621
|
}
|
|
622
|
+
|
|
623
|
+
private handleLocationChange() {
|
|
624
|
+
const href = window.location.href;
|
|
625
|
+
if (this.store.state.location === href) return;
|
|
626
|
+
|
|
627
|
+
this.knock.log(`[Guide] Handle Location change: ${href}`);
|
|
628
|
+
|
|
629
|
+
this.store.setState((state) => ({ ...state, location: href }));
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
private listenForLocationChangesFromWindow() {
|
|
633
|
+
if (window?.history) {
|
|
634
|
+
// 1. Listen for browser back/forward button clicks.
|
|
635
|
+
window.addEventListener("popstate", this.handleLocationChange);
|
|
636
|
+
|
|
637
|
+
// 2. Listen for hash changes in case it's used for routing.
|
|
638
|
+
window.addEventListener("hashchange", this.handleLocationChange);
|
|
639
|
+
|
|
640
|
+
// 3. Monkey-patch history methods to catch programmatic navigation.
|
|
641
|
+
const pushStateFn = window.history.pushState;
|
|
642
|
+
const replaceStateFn = window.history.replaceState;
|
|
643
|
+
|
|
644
|
+
// Use setTimeout to allow the browser state to potentially settle.
|
|
645
|
+
window.history.pushState = new Proxy(pushStateFn, {
|
|
646
|
+
apply: (target, history, args) => {
|
|
647
|
+
Reflect.apply(target, history, args);
|
|
648
|
+
setTimeout(() => {
|
|
649
|
+
this.handleLocationChange();
|
|
650
|
+
}, 0);
|
|
651
|
+
},
|
|
652
|
+
});
|
|
653
|
+
window.history.replaceState = new Proxy(replaceStateFn, {
|
|
654
|
+
apply: (target, history, args) => {
|
|
655
|
+
Reflect.apply(target, history, args);
|
|
656
|
+
setTimeout(() => {
|
|
657
|
+
this.handleLocationChange();
|
|
658
|
+
}, 0);
|
|
659
|
+
},
|
|
660
|
+
});
|
|
661
|
+
|
|
662
|
+
// 4. Keep refs to the original handlers so we can restore during cleanup.
|
|
663
|
+
this.pushStateFn = pushStateFn;
|
|
664
|
+
this.replaceStateFn = replaceStateFn;
|
|
665
|
+
} else {
|
|
666
|
+
this.knock.log(
|
|
667
|
+
"[Guide] Unable to access the `window.history` object to detect location changes",
|
|
668
|
+
);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
private removeEventListeners() {
|
|
673
|
+
window.removeEventListener("popstate", this.handleLocationChange);
|
|
674
|
+
window.removeEventListener("hashchange", this.handleLocationChange);
|
|
675
|
+
|
|
676
|
+
if (this.pushStateFn) {
|
|
677
|
+
window.history.pushState = this.pushStateFn;
|
|
678
|
+
this.pushStateFn = undefined;
|
|
679
|
+
}
|
|
680
|
+
if (this.replaceStateFn) {
|
|
681
|
+
window.history.replaceState = this.replaceStateFn;
|
|
682
|
+
this.replaceStateFn = undefined;
|
|
683
|
+
}
|
|
684
|
+
}
|
|
541
685
|
}
|
package/src/helpers.ts
ADDED
package/src/knock.ts
CHANGED