@frontegg/react-hooks 7.105.0 → 7.106.0-alpha.0
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/FronteggProvider/FronteggProvider.js +10 -26
- package/common/index.d.ts +2 -0
- package/common/index.js +3 -1
- package/common/patchHistoryEvents.d.ts +41 -0
- package/common/patchHistoryEvents.js +114 -0
- package/common/useSubscribeToLocationChanges.d.ts +8 -0
- package/common/useSubscribeToLocationChanges.js +16 -0
- package/index.js +1 -1
- package/node/FronteggProvider/FronteggProvider.js +9 -25
- package/node/common/index.js +24 -0
- package/node/common/patchHistoryEvents.js +124 -0
- package/node/common/useSubscribeToLocationChanges.js +21 -0
- package/node/index.js +1 -1
- package/package.json +3 -3
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import _extends from "@babel/runtime/helpers/esm/extends";
|
|
2
2
|
import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
|
|
3
3
|
const _excluded = ["authenticatedUrl", "signUpSuccessUrl", "openAppUrl"];
|
|
4
|
-
import React, { useCallback,
|
|
4
|
+
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
|
5
5
|
import { createStore } from '@frontegg/redux-store';
|
|
6
6
|
import { ContextHolder } from '@frontegg/rest-api';
|
|
7
7
|
import { Provider } from '../FronteggStoreContext';
|
|
8
8
|
import { useAuth, useAuthRoutes } from '../auth';
|
|
9
|
+
import { getCurrentUri } from '../common/patchHistoryEvents';
|
|
10
|
+
import { useSubscribeToLocationChanges } from '../common/useSubscribeToLocationChanges';
|
|
9
11
|
import { Fragment as _Fragment } from "react/jsx-runtime";
|
|
10
12
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
11
13
|
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
@@ -21,34 +23,16 @@ const HideChildrenIfFronteggRoutes = ({
|
|
|
21
23
|
basename
|
|
22
24
|
}) => {
|
|
23
25
|
const routes = useAuthRoutes();
|
|
24
|
-
const
|
|
25
|
-
const animationFrameRef = useRef(0);
|
|
26
|
+
const fullUriRef = useRef(getCurrentUri());
|
|
26
27
|
const [uri, setUri] = useState(window.location.pathname);
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
const syncUri = useCallback(() => {
|
|
29
|
+
const currentFullUri = getCurrentUri();
|
|
30
|
+
if (fullUriRef.current !== currentFullUri) {
|
|
31
|
+
fullUriRef.current = currentFullUri;
|
|
32
|
+
setUri(window.location.pathname);
|
|
31
33
|
}
|
|
32
|
-
animationFrameRef.current = window.requestAnimationFrame(checker);
|
|
33
34
|
}, []);
|
|
34
|
-
|
|
35
|
-
document.addEventListener('frontegg_onRedirectTo_fired', () => {
|
|
36
|
-
if (uriRef.current !== window.location.pathname) {
|
|
37
|
-
uriRef.current = window.location.pathname;
|
|
38
|
-
setUri(document.location.pathname);
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
window.addEventListener('popstate', () => {
|
|
42
|
-
if (uriRef.current !== window.location.pathname) {
|
|
43
|
-
uriRef.current = window.location.pathname;
|
|
44
|
-
setUri(document.location.pathname);
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
animationFrameRef.current = window.requestAnimationFrame(checker);
|
|
48
|
-
return () => {
|
|
49
|
-
window.cancelAnimationFrame(animationFrameRef.current);
|
|
50
|
-
};
|
|
51
|
-
}, [checker, setUri]);
|
|
35
|
+
useSubscribeToLocationChanges(syncUri);
|
|
52
36
|
const calculatedBasename = basename ? basename.endsWith('/') ? basename.substring(0, basename.length - 1) : '' : '';
|
|
53
37
|
const {
|
|
54
38
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
package/common/index.d.ts
CHANGED
|
@@ -22,3 +22,5 @@ export declare const ShadowDomContext: import("react").Context<ShadowDomContextD
|
|
|
22
22
|
export declare const useShadowDom: () => DomContext;
|
|
23
23
|
export declare const useRootState: () => FronteggState["root"];
|
|
24
24
|
export * from './CustomComponentHolder';
|
|
25
|
+
export * from './patchHistoryEvents';
|
|
26
|
+
export * from './useSubscribeToLocationChanges';
|
package/common/index.js
CHANGED
|
@@ -46,4 +46,6 @@ export const useShadowDom = () => {
|
|
|
46
46
|
export const useRootState = () => {
|
|
47
47
|
return useSnapshot(useStore().store.root);
|
|
48
48
|
};
|
|
49
|
-
export * from './CustomComponentHolder';
|
|
49
|
+
export * from './CustomComponentHolder';
|
|
50
|
+
export * from './patchHistoryEvents';
|
|
51
|
+
export * from './useSubscribeToLocationChanges';
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export declare const FRONTEGG_LOCATION_CHANGE: "frontegg_locationchange";
|
|
2
|
+
/**
|
|
3
|
+
* Returns the full client-side URL (pathname + search + hash).
|
|
4
|
+
* Used as the change-detection fingerprint so that URL changes
|
|
5
|
+
* in any segment (path, query, hash) are captured.
|
|
6
|
+
*/
|
|
7
|
+
export declare function getCurrentUri(): string;
|
|
8
|
+
/**
|
|
9
|
+
* Monkey-patches `history.pushState` and `history.replaceState` to dispatch
|
|
10
|
+
* a {@link FRONTEGG_LOCATION_CHANGE} event so components can react to
|
|
11
|
+
* programmatic navigation without polling.
|
|
12
|
+
*
|
|
13
|
+
* @returns `true` if the patch was applied (or was already applied),
|
|
14
|
+
* `false` if patching failed (e.g. non-writable History methods
|
|
15
|
+
* in hardened/frozen environments).
|
|
16
|
+
*
|
|
17
|
+
* Idempotent across multiple bundle instances — patch state is stored on
|
|
18
|
+
* `window` via `Symbol.for`, so even if several copies of this module are
|
|
19
|
+
* loaded (microfrontends, version mismatches, local dev) the History API
|
|
20
|
+
* is only patched once.
|
|
21
|
+
*
|
|
22
|
+
* Limitation: if another library overwrites `history.pushState`/`replaceState`
|
|
23
|
+
* AFTER this patch runs, the custom event will stop firing.
|
|
24
|
+
*/
|
|
25
|
+
export declare function patchHistoryEvents(): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Subscribes `callback` to all location-change sources:
|
|
28
|
+
*
|
|
29
|
+
* 1. `popstate` — browser back/forward
|
|
30
|
+
* 2. `hashchange` — hash fragment changes
|
|
31
|
+
* 3. `frontegg_onRedirectTo_fired` — Frontegg internal redirects
|
|
32
|
+
* 4. `FRONTEGG_LOCATION_CHANGE` — our patched pushState/replaceState
|
|
33
|
+
* 5. Low-frequency polling fallback — only enabled when the History patch
|
|
34
|
+
* fails (non-writable / hardened environment)
|
|
35
|
+
*
|
|
36
|
+
* Calls `callback` once after setup to catch navigations that occurred
|
|
37
|
+
* between render and listener registration.
|
|
38
|
+
*
|
|
39
|
+
* @returns A cleanup function that removes all listeners and timers.
|
|
40
|
+
*/
|
|
41
|
+
export declare function subscribeToLocationChanges(callback: () => void): () => void;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
export const FRONTEGG_LOCATION_CHANGE = 'frontegg_locationchange';
|
|
2
|
+
const PATCH_STATE_KEY = Symbol.for('frontegg.history.patch');
|
|
3
|
+
const FALLBACK_POLL_MS = 1000;
|
|
4
|
+
/**
|
|
5
|
+
* Returns the full client-side URL (pathname + search + hash).
|
|
6
|
+
* Used as the change-detection fingerprint so that URL changes
|
|
7
|
+
* in any segment (path, query, hash) are captured.
|
|
8
|
+
*/
|
|
9
|
+
export function getCurrentUri() {
|
|
10
|
+
return window.location.pathname + window.location.search + window.location.hash;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Monkey-patches `history.pushState` and `history.replaceState` to dispatch
|
|
15
|
+
* a {@link FRONTEGG_LOCATION_CHANGE} event so components can react to
|
|
16
|
+
* programmatic navigation without polling.
|
|
17
|
+
*
|
|
18
|
+
* @returns `true` if the patch was applied (or was already applied),
|
|
19
|
+
* `false` if patching failed (e.g. non-writable History methods
|
|
20
|
+
* in hardened/frozen environments).
|
|
21
|
+
*
|
|
22
|
+
* Idempotent across multiple bundle instances — patch state is stored on
|
|
23
|
+
* `window` via `Symbol.for`, so even if several copies of this module are
|
|
24
|
+
* loaded (microfrontends, version mismatches, local dev) the History API
|
|
25
|
+
* is only patched once.
|
|
26
|
+
*
|
|
27
|
+
* Limitation: if another library overwrites `history.pushState`/`replaceState`
|
|
28
|
+
* AFTER this patch runs, the custom event will stop firing.
|
|
29
|
+
*/
|
|
30
|
+
export function patchHistoryEvents() {
|
|
31
|
+
var _win$PATCH_STATE_KEY;
|
|
32
|
+
if (typeof window === 'undefined') {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
const win = window;
|
|
36
|
+
if ((_win$PATCH_STATE_KEY = win[PATCH_STATE_KEY]) != null && _win$PATCH_STATE_KEY.patched) {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
const originalPushState = window.history.pushState;
|
|
40
|
+
const originalReplaceState = window.history.replaceState;
|
|
41
|
+
const wrappedPushState = function (...args) {
|
|
42
|
+
const result = originalPushState.apply(this, args);
|
|
43
|
+
window.dispatchEvent(new Event(FRONTEGG_LOCATION_CHANGE));
|
|
44
|
+
return result;
|
|
45
|
+
};
|
|
46
|
+
const wrappedReplaceState = function (...args) {
|
|
47
|
+
const result = originalReplaceState.apply(this, args);
|
|
48
|
+
window.dispatchEvent(new Event(FRONTEGG_LOCATION_CHANGE));
|
|
49
|
+
return result;
|
|
50
|
+
};
|
|
51
|
+
try {
|
|
52
|
+
window.history.pushState = wrappedPushState;
|
|
53
|
+
window.history.replaceState = wrappedReplaceState;
|
|
54
|
+
|
|
55
|
+
// Verify assignments took effect (non-writable properties silently fail in non-strict mode)
|
|
56
|
+
if (window.history.pushState === originalPushState || window.history.replaceState === originalReplaceState) {
|
|
57
|
+
window.history.pushState = originalPushState;
|
|
58
|
+
window.history.replaceState = originalReplaceState;
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
} catch {
|
|
62
|
+
// history methods may be non-writable in hardened environments — rollback any partial patch
|
|
63
|
+
try {
|
|
64
|
+
window.history.pushState = originalPushState;
|
|
65
|
+
window.history.replaceState = originalReplaceState;
|
|
66
|
+
} catch {
|
|
67
|
+
/* best-effort rollback */
|
|
68
|
+
}
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
win[PATCH_STATE_KEY] = {
|
|
72
|
+
patched: true,
|
|
73
|
+
originalPushState,
|
|
74
|
+
originalReplaceState
|
|
75
|
+
};
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Subscribes `callback` to all location-change sources:
|
|
81
|
+
*
|
|
82
|
+
* 1. `popstate` — browser back/forward
|
|
83
|
+
* 2. `hashchange` — hash fragment changes
|
|
84
|
+
* 3. `frontegg_onRedirectTo_fired` — Frontegg internal redirects
|
|
85
|
+
* 4. `FRONTEGG_LOCATION_CHANGE` — our patched pushState/replaceState
|
|
86
|
+
* 5. Low-frequency polling fallback — only enabled when the History patch
|
|
87
|
+
* fails (non-writable / hardened environment)
|
|
88
|
+
*
|
|
89
|
+
* Calls `callback` once after setup to catch navigations that occurred
|
|
90
|
+
* between render and listener registration.
|
|
91
|
+
*
|
|
92
|
+
* @returns A cleanup function that removes all listeners and timers.
|
|
93
|
+
*/
|
|
94
|
+
export function subscribeToLocationChanges(callback) {
|
|
95
|
+
const patched = patchHistoryEvents();
|
|
96
|
+
document.addEventListener('frontegg_onRedirectTo_fired', callback);
|
|
97
|
+
window.addEventListener('popstate', callback);
|
|
98
|
+
window.addEventListener('hashchange', callback);
|
|
99
|
+
window.addEventListener(FRONTEGG_LOCATION_CHANGE, callback);
|
|
100
|
+
let fallbackInterval;
|
|
101
|
+
if (!patched) {
|
|
102
|
+
fallbackInterval = setInterval(callback, FALLBACK_POLL_MS);
|
|
103
|
+
}
|
|
104
|
+
callback();
|
|
105
|
+
return () => {
|
|
106
|
+
document.removeEventListener('frontegg_onRedirectTo_fired', callback);
|
|
107
|
+
window.removeEventListener('popstate', callback);
|
|
108
|
+
window.removeEventListener('hashchange', callback);
|
|
109
|
+
window.removeEventListener(FRONTEGG_LOCATION_CHANGE, callback);
|
|
110
|
+
if (fallbackInterval !== undefined) {
|
|
111
|
+
clearInterval(fallbackInterval);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React hook that subscribes to all location-change sources and calls
|
|
3
|
+
* `callback` whenever the URL changes.
|
|
4
|
+
*
|
|
5
|
+
* Delegates to {@link subscribeToLocationChanges} inside a `useEffect`
|
|
6
|
+
* so the subscription lifecycle is tied to the component mount/unmount cycle.
|
|
7
|
+
*/
|
|
8
|
+
export declare function useSubscribeToLocationChanges(callback: () => void): void;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { subscribeToLocationChanges } from './patchHistoryEvents';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* React hook that subscribes to all location-change sources and calls
|
|
6
|
+
* `callback` whenever the URL changes.
|
|
7
|
+
*
|
|
8
|
+
* Delegates to {@link subscribeToLocationChanges} inside a `useEffect`
|
|
9
|
+
* so the subscription lifecycle is tied to the component mount/unmount cycle.
|
|
10
|
+
*/
|
|
11
|
+
export function useSubscribeToLocationChanges(callback) {
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
const unsubscribe = subscribeToLocationChanges(callback);
|
|
14
|
+
return unsubscribe;
|
|
15
|
+
}, [callback]);
|
|
16
|
+
}
|
package/index.js
CHANGED
|
@@ -12,6 +12,8 @@ var _reduxStore = require("@frontegg/redux-store");
|
|
|
12
12
|
var _restApi = require("@frontegg/rest-api");
|
|
13
13
|
var _FronteggStoreContext = require("../FronteggStoreContext");
|
|
14
14
|
var _auth = require("../auth");
|
|
15
|
+
var _patchHistoryEvents = require("../common/patchHistoryEvents");
|
|
16
|
+
var _useSubscribeToLocationChanges = require("../common/useSubscribeToLocationChanges");
|
|
15
17
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
16
18
|
const _excluded = ["authenticatedUrl", "signUpSuccessUrl", "openAppUrl"];
|
|
17
19
|
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
@@ -28,34 +30,16 @@ const HideChildrenIfFronteggRoutes = ({
|
|
|
28
30
|
basename
|
|
29
31
|
}) => {
|
|
30
32
|
const routes = (0, _auth.useAuthRoutes)();
|
|
31
|
-
const
|
|
32
|
-
const animationFrameRef = (0, _react.useRef)(0);
|
|
33
|
+
const fullUriRef = (0, _react.useRef)((0, _patchHistoryEvents.getCurrentUri)());
|
|
33
34
|
const [uri, setUri] = (0, _react.useState)(window.location.pathname);
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
const syncUri = (0, _react.useCallback)(() => {
|
|
36
|
+
const currentFullUri = (0, _patchHistoryEvents.getCurrentUri)();
|
|
37
|
+
if (fullUriRef.current !== currentFullUri) {
|
|
38
|
+
fullUriRef.current = currentFullUri;
|
|
39
|
+
setUri(window.location.pathname);
|
|
38
40
|
}
|
|
39
|
-
animationFrameRef.current = window.requestAnimationFrame(checker);
|
|
40
41
|
}, []);
|
|
41
|
-
(0,
|
|
42
|
-
document.addEventListener('frontegg_onRedirectTo_fired', () => {
|
|
43
|
-
if (uriRef.current !== window.location.pathname) {
|
|
44
|
-
uriRef.current = window.location.pathname;
|
|
45
|
-
setUri(document.location.pathname);
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
window.addEventListener('popstate', () => {
|
|
49
|
-
if (uriRef.current !== window.location.pathname) {
|
|
50
|
-
uriRef.current = window.location.pathname;
|
|
51
|
-
setUri(document.location.pathname);
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
animationFrameRef.current = window.requestAnimationFrame(checker);
|
|
55
|
-
return () => {
|
|
56
|
-
window.cancelAnimationFrame(animationFrameRef.current);
|
|
57
|
-
};
|
|
58
|
-
}, [checker, setUri]);
|
|
42
|
+
(0, _useSubscribeToLocationChanges.useSubscribeToLocationChanges)(syncUri);
|
|
59
43
|
const calculatedBasename = basename ? basename.endsWith('/') ? basename.substring(0, basename.length - 1) : '' : '';
|
|
60
44
|
const {
|
|
61
45
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
package/node/common/index.js
CHANGED
|
@@ -28,6 +28,30 @@ Object.keys(_CustomComponentHolder).forEach(function (key) {
|
|
|
28
28
|
}
|
|
29
29
|
});
|
|
30
30
|
});
|
|
31
|
+
var _patchHistoryEvents = require("./patchHistoryEvents");
|
|
32
|
+
Object.keys(_patchHistoryEvents).forEach(function (key) {
|
|
33
|
+
if (key === "default" || key === "__esModule") return;
|
|
34
|
+
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
|
|
35
|
+
if (key in exports && exports[key] === _patchHistoryEvents[key]) return;
|
|
36
|
+
Object.defineProperty(exports, key, {
|
|
37
|
+
enumerable: true,
|
|
38
|
+
get: function () {
|
|
39
|
+
return _patchHistoryEvents[key];
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
var _useSubscribeToLocationChanges = require("./useSubscribeToLocationChanges");
|
|
44
|
+
Object.keys(_useSubscribeToLocationChanges).forEach(function (key) {
|
|
45
|
+
if (key === "default" || key === "__esModule") return;
|
|
46
|
+
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
|
|
47
|
+
if (key in exports && exports[key] === _useSubscribeToLocationChanges[key]) return;
|
|
48
|
+
Object.defineProperty(exports, key, {
|
|
49
|
+
enumerable: true,
|
|
50
|
+
get: function () {
|
|
51
|
+
return _useSubscribeToLocationChanges[key];
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
});
|
|
31
55
|
const _excluded = ["urlStrategy", "onRedirectTo", "renderByRoute", "customLoader", "customStyles", "contextOptions"];
|
|
32
56
|
const ShadowDomContext = /*#__PURE__*/(0, _react.createContext)({});
|
|
33
57
|
exports.ShadowDomContext = ShadowDomContext;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.FRONTEGG_LOCATION_CHANGE = void 0;
|
|
7
|
+
exports.getCurrentUri = getCurrentUri;
|
|
8
|
+
exports.patchHistoryEvents = patchHistoryEvents;
|
|
9
|
+
exports.subscribeToLocationChanges = subscribeToLocationChanges;
|
|
10
|
+
const FRONTEGG_LOCATION_CHANGE = 'frontegg_locationchange';
|
|
11
|
+
exports.FRONTEGG_LOCATION_CHANGE = FRONTEGG_LOCATION_CHANGE;
|
|
12
|
+
const PATCH_STATE_KEY = Symbol.for('frontegg.history.patch');
|
|
13
|
+
const FALLBACK_POLL_MS = 1000;
|
|
14
|
+
/**
|
|
15
|
+
* Returns the full client-side URL (pathname + search + hash).
|
|
16
|
+
* Used as the change-detection fingerprint so that URL changes
|
|
17
|
+
* in any segment (path, query, hash) are captured.
|
|
18
|
+
*/
|
|
19
|
+
function getCurrentUri() {
|
|
20
|
+
return window.location.pathname + window.location.search + window.location.hash;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Monkey-patches `history.pushState` and `history.replaceState` to dispatch
|
|
25
|
+
* a {@link FRONTEGG_LOCATION_CHANGE} event so components can react to
|
|
26
|
+
* programmatic navigation without polling.
|
|
27
|
+
*
|
|
28
|
+
* @returns `true` if the patch was applied (or was already applied),
|
|
29
|
+
* `false` if patching failed (e.g. non-writable History methods
|
|
30
|
+
* in hardened/frozen environments).
|
|
31
|
+
*
|
|
32
|
+
* Idempotent across multiple bundle instances — patch state is stored on
|
|
33
|
+
* `window` via `Symbol.for`, so even if several copies of this module are
|
|
34
|
+
* loaded (microfrontends, version mismatches, local dev) the History API
|
|
35
|
+
* is only patched once.
|
|
36
|
+
*
|
|
37
|
+
* Limitation: if another library overwrites `history.pushState`/`replaceState`
|
|
38
|
+
* AFTER this patch runs, the custom event will stop firing.
|
|
39
|
+
*/
|
|
40
|
+
function patchHistoryEvents() {
|
|
41
|
+
var _win$PATCH_STATE_KEY;
|
|
42
|
+
if (typeof window === 'undefined') {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
const win = window;
|
|
46
|
+
if ((_win$PATCH_STATE_KEY = win[PATCH_STATE_KEY]) != null && _win$PATCH_STATE_KEY.patched) {
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
const originalPushState = window.history.pushState;
|
|
50
|
+
const originalReplaceState = window.history.replaceState;
|
|
51
|
+
const wrappedPushState = function (...args) {
|
|
52
|
+
const result = originalPushState.apply(this, args);
|
|
53
|
+
window.dispatchEvent(new Event(FRONTEGG_LOCATION_CHANGE));
|
|
54
|
+
return result;
|
|
55
|
+
};
|
|
56
|
+
const wrappedReplaceState = function (...args) {
|
|
57
|
+
const result = originalReplaceState.apply(this, args);
|
|
58
|
+
window.dispatchEvent(new Event(FRONTEGG_LOCATION_CHANGE));
|
|
59
|
+
return result;
|
|
60
|
+
};
|
|
61
|
+
try {
|
|
62
|
+
window.history.pushState = wrappedPushState;
|
|
63
|
+
window.history.replaceState = wrappedReplaceState;
|
|
64
|
+
|
|
65
|
+
// Verify assignments took effect (non-writable properties silently fail in non-strict mode)
|
|
66
|
+
if (window.history.pushState === originalPushState || window.history.replaceState === originalReplaceState) {
|
|
67
|
+
window.history.pushState = originalPushState;
|
|
68
|
+
window.history.replaceState = originalReplaceState;
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
} catch {
|
|
72
|
+
// history methods may be non-writable in hardened environments — rollback any partial patch
|
|
73
|
+
try {
|
|
74
|
+
window.history.pushState = originalPushState;
|
|
75
|
+
window.history.replaceState = originalReplaceState;
|
|
76
|
+
} catch {
|
|
77
|
+
/* best-effort rollback */
|
|
78
|
+
}
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
win[PATCH_STATE_KEY] = {
|
|
82
|
+
patched: true,
|
|
83
|
+
originalPushState,
|
|
84
|
+
originalReplaceState
|
|
85
|
+
};
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Subscribes `callback` to all location-change sources:
|
|
91
|
+
*
|
|
92
|
+
* 1. `popstate` — browser back/forward
|
|
93
|
+
* 2. `hashchange` — hash fragment changes
|
|
94
|
+
* 3. `frontegg_onRedirectTo_fired` — Frontegg internal redirects
|
|
95
|
+
* 4. `FRONTEGG_LOCATION_CHANGE` — our patched pushState/replaceState
|
|
96
|
+
* 5. Low-frequency polling fallback — only enabled when the History patch
|
|
97
|
+
* fails (non-writable / hardened environment)
|
|
98
|
+
*
|
|
99
|
+
* Calls `callback` once after setup to catch navigations that occurred
|
|
100
|
+
* between render and listener registration.
|
|
101
|
+
*
|
|
102
|
+
* @returns A cleanup function that removes all listeners and timers.
|
|
103
|
+
*/
|
|
104
|
+
function subscribeToLocationChanges(callback) {
|
|
105
|
+
const patched = patchHistoryEvents();
|
|
106
|
+
document.addEventListener('frontegg_onRedirectTo_fired', callback);
|
|
107
|
+
window.addEventListener('popstate', callback);
|
|
108
|
+
window.addEventListener('hashchange', callback);
|
|
109
|
+
window.addEventListener(FRONTEGG_LOCATION_CHANGE, callback);
|
|
110
|
+
let fallbackInterval;
|
|
111
|
+
if (!patched) {
|
|
112
|
+
fallbackInterval = setInterval(callback, FALLBACK_POLL_MS);
|
|
113
|
+
}
|
|
114
|
+
callback();
|
|
115
|
+
return () => {
|
|
116
|
+
document.removeEventListener('frontegg_onRedirectTo_fired', callback);
|
|
117
|
+
window.removeEventListener('popstate', callback);
|
|
118
|
+
window.removeEventListener('hashchange', callback);
|
|
119
|
+
window.removeEventListener(FRONTEGG_LOCATION_CHANGE, callback);
|
|
120
|
+
if (fallbackInterval !== undefined) {
|
|
121
|
+
clearInterval(fallbackInterval);
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.useSubscribeToLocationChanges = useSubscribeToLocationChanges;
|
|
7
|
+
var _react = require("react");
|
|
8
|
+
var _patchHistoryEvents = require("./patchHistoryEvents");
|
|
9
|
+
/**
|
|
10
|
+
* React hook that subscribes to all location-change sources and calls
|
|
11
|
+
* `callback` whenever the URL changes.
|
|
12
|
+
*
|
|
13
|
+
* Delegates to {@link subscribeToLocationChanges} inside a `useEffect`
|
|
14
|
+
* so the subscription lifecycle is tied to the component mount/unmount cycle.
|
|
15
|
+
*/
|
|
16
|
+
function useSubscribeToLocationChanges(callback) {
|
|
17
|
+
(0, _react.useEffect)(() => {
|
|
18
|
+
const unsubscribe = (0, _patchHistoryEvents.subscribeToLocationChanges)(callback);
|
|
19
|
+
return unsubscribe;
|
|
20
|
+
}, [callback]);
|
|
21
|
+
}
|
package/node/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frontegg/react-hooks",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.106.0-alpha.0",
|
|
4
4
|
"main": "./node/index.js",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Frontegg LTD",
|
|
7
7
|
"dependencies": {
|
|
8
8
|
"@babel/runtime": "^7.18.6",
|
|
9
|
-
"@frontegg/redux-store": "7.
|
|
10
|
-
"@frontegg/types": "7.
|
|
9
|
+
"@frontegg/redux-store": "7.106.0-alpha.0",
|
|
10
|
+
"@frontegg/types": "7.106.0-alpha.0",
|
|
11
11
|
"@types/react": "*",
|
|
12
12
|
"@types/react-is": "^17.0.7",
|
|
13
13
|
"get-value": "^3.0.1",
|