chayns-api 3.1.0-beta.1 → 3.1.0-beta.2
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/dist/cjs/utils/history/BlockRegistry.js +30 -44
- package/dist/cjs/utils/history/NavigationQueue.js +2 -3
- package/dist/cjs/utils/history/navigationIndex.js +0 -32
- package/dist/cjs/utils/history/rootLayer.js +10 -48
- package/dist/esm/utils/history/BlockRegistry.js +30 -44
- package/dist/esm/utils/history/NavigationQueue.js +2 -3
- package/dist/esm/utils/history/navigationIndex.js +0 -29
- package/dist/esm/utils/history/rootLayer.js +12 -50
- package/dist/types/utils/history/BlockRegistry.d.ts +27 -7
- package/dist/types/utils/history/NavigationQueue.d.ts +0 -9
- package/dist/types/utils/history/navigationIndex.d.ts +0 -3
- package/package.json +1 -1
- package/dist/cjs/utils/history/nativeBackHandling.js +0 -61
- package/dist/esm/utils/history/nativeBackHandling.js +0 -59
- package/dist/types/utils/history/nativeBackHandling.d.ts +0 -47
|
@@ -4,16 +4,17 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.BlockRegistry = void 0;
|
|
7
|
+
var _calls = require("../../calls");
|
|
7
8
|
const BLOCK_TIMEOUT_MS = 30000;
|
|
8
9
|
let _nextId = 1;
|
|
9
10
|
class BlockRegistry {
|
|
10
11
|
layerBlocks = new Map();
|
|
11
|
-
changeListeners = new Set();
|
|
12
12
|
beforeUnloadCount = 0;
|
|
13
13
|
beforeUnloadHandler = e => {
|
|
14
14
|
e.preventDefault();
|
|
15
|
-
|
|
15
|
+
e.returnValue = '';
|
|
16
16
|
};
|
|
17
|
+
totalBlockCount = 0;
|
|
17
18
|
add(layer, callback, opts = {}) {
|
|
18
19
|
var _opts$scope, _opts$isBeforeUnload;
|
|
19
20
|
const entry = {
|
|
@@ -33,34 +34,27 @@ class BlockRegistry {
|
|
|
33
34
|
if (entry.opts.isBeforeUnload) {
|
|
34
35
|
this.incrementBeforeUnload();
|
|
35
36
|
}
|
|
36
|
-
this.
|
|
37
|
+
this.incrementTotalBlocks();
|
|
37
38
|
return () => this.remove(layer.id, entry);
|
|
38
39
|
}
|
|
39
40
|
remove(layerId, entry) {
|
|
40
41
|
const set = this.layerBlocks.get(layerId);
|
|
41
42
|
if (!set) return;
|
|
42
|
-
|
|
43
|
-
if (!didDelete) return;
|
|
43
|
+
if (!set.delete(entry)) return;
|
|
44
44
|
if (set.size === 0) this.layerBlocks.delete(layerId);
|
|
45
45
|
if (entry.opts.isBeforeUnload) {
|
|
46
46
|
this.decrementBeforeUnload();
|
|
47
47
|
}
|
|
48
|
-
this.
|
|
48
|
+
this.decrementTotalBlocks();
|
|
49
49
|
}
|
|
50
50
|
removeAllForLayer(layerId) {
|
|
51
51
|
const set = this.layerBlocks.get(layerId);
|
|
52
52
|
if (!set) return;
|
|
53
53
|
for (const entry of set) {
|
|
54
54
|
if (entry.opts.isBeforeUnload) this.decrementBeforeUnload();
|
|
55
|
+
this.decrementTotalBlocks();
|
|
55
56
|
}
|
|
56
57
|
this.layerBlocks.delete(layerId);
|
|
57
|
-
this.notifyChange();
|
|
58
|
-
}
|
|
59
|
-
subscribeToChanges(listener) {
|
|
60
|
-
this.changeListeners.add(listener);
|
|
61
|
-
return () => {
|
|
62
|
-
this.changeListeners.delete(listener);
|
|
63
|
-
};
|
|
64
58
|
}
|
|
65
59
|
collectApplicableBlocks(targetLayer) {
|
|
66
60
|
const result = [];
|
|
@@ -73,20 +67,6 @@ class BlockRegistry {
|
|
|
73
67
|
this.collectGlobalFromActiveDescendants(targetLayer, result);
|
|
74
68
|
return result;
|
|
75
69
|
}
|
|
76
|
-
hasActiveBlocks(rootLayer) {
|
|
77
|
-
return this.collectActiveChainBlocks(rootLayer).length > 0;
|
|
78
|
-
}
|
|
79
|
-
async checkActiveBlocks(rootLayer) {
|
|
80
|
-
const blocks = this.collectActiveChainBlocks(rootLayer);
|
|
81
|
-
if (blocks.length === 0) return true;
|
|
82
|
-
const results = await Promise.all(blocks.map(b => this.runBlock(b)));
|
|
83
|
-
return results.every(Boolean);
|
|
84
|
-
}
|
|
85
|
-
collectActiveChainBlocks(rootLayer) {
|
|
86
|
-
const result = [];
|
|
87
|
-
this.collectFromActiveChain(rootLayer, result);
|
|
88
|
-
return result;
|
|
89
|
-
}
|
|
90
70
|
collectGlobalFromActiveDescendants(layer, out) {
|
|
91
71
|
const activeChildId = layer.getActiveChildId();
|
|
92
72
|
if (!activeChildId) return;
|
|
@@ -102,19 +82,6 @@ class BlockRegistry {
|
|
|
102
82
|
}
|
|
103
83
|
this.collectGlobalFromActiveDescendants(child, out);
|
|
104
84
|
}
|
|
105
|
-
collectFromActiveChain(layer, out) {
|
|
106
|
-
const set = this.layerBlocks.get(layer.id);
|
|
107
|
-
if (set) {
|
|
108
|
-
for (const entry of set) {
|
|
109
|
-
out.push(entry);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
const activeChildId = layer.getActiveChildId();
|
|
113
|
-
if (!activeChildId) return;
|
|
114
|
-
const child = layer.getChildLayer(activeChildId);
|
|
115
|
-
if (!child) return;
|
|
116
|
-
this.collectFromActiveChain(child, out);
|
|
117
|
-
}
|
|
118
85
|
async checkBlocks(targetLayer) {
|
|
119
86
|
const blocks = this.collectApplicableBlocks(targetLayer);
|
|
120
87
|
if (blocks.length === 0) return true;
|
|
@@ -127,7 +94,7 @@ class BlockRegistry {
|
|
|
127
94
|
resolve(false);
|
|
128
95
|
}, BLOCK_TIMEOUT_MS))]);
|
|
129
96
|
return result;
|
|
130
|
-
} catch {
|
|
97
|
+
} catch (err) {
|
|
131
98
|
return false;
|
|
132
99
|
}
|
|
133
100
|
}
|
|
@@ -144,10 +111,29 @@ class BlockRegistry {
|
|
|
144
111
|
window.removeEventListener('beforeunload', this.beforeUnloadHandler);
|
|
145
112
|
}
|
|
146
113
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
114
|
+
incrementTotalBlocks() {
|
|
115
|
+
this.totalBlockCount++;
|
|
116
|
+
if (this.totalBlockCount > 0) {
|
|
117
|
+
this.setNativeNavigationEnabled(true);
|
|
150
118
|
}
|
|
151
119
|
}
|
|
120
|
+
decrementTotalBlocks() {
|
|
121
|
+
if (this.totalBlockCount === 0) return;
|
|
122
|
+
this.totalBlockCount--;
|
|
123
|
+
if (this.totalBlockCount === 0) {
|
|
124
|
+
this.setNativeNavigationEnabled(false);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
setNativeNavigationEnabled(enabled) {
|
|
128
|
+
try {
|
|
129
|
+
void (0, _calls.invokeCall)({
|
|
130
|
+
action: 249,
|
|
131
|
+
value: {
|
|
132
|
+
enabled
|
|
133
|
+
}
|
|
134
|
+
}, this.nativeNavigationCallback);
|
|
135
|
+
} catch {}
|
|
136
|
+
}
|
|
137
|
+
nativeNavigationCallback = () => {};
|
|
152
138
|
}
|
|
153
139
|
exports.BlockRegistry = BlockRegistry;
|
|
@@ -60,6 +60,7 @@ class NavigationQueue {
|
|
|
60
60
|
return this.processPopstate(op);
|
|
61
61
|
default:
|
|
62
62
|
{
|
|
63
|
+
const _exhaustive = op;
|
|
63
64
|
return {
|
|
64
65
|
isOk: false,
|
|
65
66
|
reason: 'error',
|
|
@@ -295,7 +296,7 @@ class NavigationQueue {
|
|
|
295
296
|
changedLayerIds.add(this.deps.getRoot().id);
|
|
296
297
|
}
|
|
297
298
|
const target = this.resolveLowestCommonLayer(changedLayerIds);
|
|
298
|
-
if (target
|
|
299
|
+
if (target) {
|
|
299
300
|
const allowed = await this.deps.checkBlocks(target);
|
|
300
301
|
if (!allowed) {
|
|
301
302
|
await this.deps.silentGo(+1);
|
|
@@ -366,7 +367,6 @@ class NavigationQueue {
|
|
|
366
367
|
return lca;
|
|
367
368
|
}
|
|
368
369
|
commit(isReplace) {
|
|
369
|
-
var _this$deps$onCommit, _this$deps;
|
|
370
370
|
if (!(0, _window.hasWindowHistory)()) return;
|
|
371
371
|
const url = this.deps.projectUrl();
|
|
372
372
|
const state = this.deps.projectState();
|
|
@@ -383,7 +383,6 @@ class NavigationQueue {
|
|
|
383
383
|
} else {
|
|
384
384
|
window.history.pushState(stateWithMeta, '', url);
|
|
385
385
|
}
|
|
386
|
-
(_this$deps$onCommit = (_this$deps = this.deps).onCommit) === null || _this$deps$onCommit === void 0 || _this$deps$onCommit.call(_this$deps);
|
|
387
386
|
}
|
|
388
387
|
}
|
|
389
388
|
exports.NavigationQueue = NavigationQueue;
|
|
@@ -4,14 +4,10 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.consumeSilent = consumeSilent;
|
|
7
|
-
exports.extractHistoryIndex = extractHistoryIndex;
|
|
8
7
|
exports.getCurrentIdx = getCurrentIdx;
|
|
9
8
|
exports.incrementIdx = incrementIdx;
|
|
10
|
-
exports.setCurrentIdx = setCurrentIdx;
|
|
11
9
|
exports.silentGo = silentGo;
|
|
12
|
-
exports.syncCurrentIdxFromState = syncCurrentIdxFromState;
|
|
13
10
|
var _window = require("./window");
|
|
14
|
-
const CHAYNS_HISTORY_STATE_KEY = '__chaynsHistory';
|
|
15
11
|
let currentIdx = 0;
|
|
16
12
|
let pendingSilentCount = 0;
|
|
17
13
|
let silentResolve = null;
|
|
@@ -21,34 +17,6 @@ function incrementIdx() {
|
|
|
21
17
|
function getCurrentIdx() {
|
|
22
18
|
return currentIdx;
|
|
23
19
|
}
|
|
24
|
-
function setCurrentIdx(idx) {
|
|
25
|
-
if (!Number.isInteger(idx) || idx < 0) {
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
currentIdx = idx;
|
|
29
|
-
}
|
|
30
|
-
function extractHistoryIndex(raw) {
|
|
31
|
-
if (!raw || typeof raw !== 'object') {
|
|
32
|
-
return null;
|
|
33
|
-
}
|
|
34
|
-
const chaynsHistory = raw[CHAYNS_HISTORY_STATE_KEY];
|
|
35
|
-
if (!chaynsHistory || typeof chaynsHistory !== 'object') {
|
|
36
|
-
return null;
|
|
37
|
-
}
|
|
38
|
-
const idx = chaynsHistory.__idx;
|
|
39
|
-
if (typeof idx !== 'number' || !Number.isInteger(idx) || idx < 0) {
|
|
40
|
-
return null;
|
|
41
|
-
}
|
|
42
|
-
return idx;
|
|
43
|
-
}
|
|
44
|
-
function syncCurrentIdxFromState(raw) {
|
|
45
|
-
const idx = extractHistoryIndex(raw);
|
|
46
|
-
if (idx === null) {
|
|
47
|
-
return null;
|
|
48
|
-
}
|
|
49
|
-
currentIdx = idx;
|
|
50
|
-
return idx;
|
|
51
|
-
}
|
|
52
20
|
function silentGo(delta) {
|
|
53
21
|
if (!(0, _window.hasWindowHistory)()) return Promise.resolve();
|
|
54
22
|
return new Promise(resolve => {
|
|
@@ -17,7 +17,6 @@ var _window = require("./window");
|
|
|
17
17
|
var _equality = require("../equality");
|
|
18
18
|
var _calls = require("../../calls");
|
|
19
19
|
var _segments = require("./segments");
|
|
20
|
-
var _nativeBackHandling = require("./nativeBackHandling");
|
|
21
20
|
function getInitialPathname(overrideUrl) {
|
|
22
21
|
if (overrideUrl) {
|
|
23
22
|
try {
|
|
@@ -49,30 +48,21 @@ function resolveSegmentsFrom(overrideUrl, startIndex) {
|
|
|
49
48
|
function initRootChaynsHistoryLayer(opts = {}) {
|
|
50
49
|
var _opts$segmentCount;
|
|
51
50
|
const blockRegistry = new _BlockRegistry.BlockRegistry();
|
|
52
|
-
let
|
|
51
|
+
let rootLayer;
|
|
52
|
+
let queue;
|
|
53
53
|
const deps = {
|
|
54
54
|
getRoot: () => rootLayer,
|
|
55
|
-
getQueue: () =>
|
|
56
|
-
if (!queueRef) {
|
|
57
|
-
throw new Error('[chaynsHistory] NavigationQueue not initialized yet.');
|
|
58
|
-
}
|
|
59
|
-
return queueRef;
|
|
60
|
-
},
|
|
55
|
+
getQueue: () => queue,
|
|
61
56
|
getBlockRegistry: () => blockRegistry
|
|
62
57
|
};
|
|
63
|
-
|
|
58
|
+
rootLayer = new _HistoryLayer.ChaynsHistoryLayer({
|
|
64
59
|
id: 'root',
|
|
65
60
|
parent: null,
|
|
66
61
|
deps,
|
|
67
62
|
segmentCount: (_opts$segmentCount = opts.segmentCount) !== null && _opts$segmentCount !== void 0 ? _opts$segmentCount : 0,
|
|
68
63
|
segments: opts.segmentCount ? resolveInitialSegments(opts.url, opts.segmentCount) : []
|
|
69
64
|
});
|
|
70
|
-
|
|
71
|
-
rootLayer,
|
|
72
|
-
blockRegistry
|
|
73
|
-
});
|
|
74
|
-
const syncNativeHandling = nativeBackHandler.sync;
|
|
75
|
-
const queue = new _NavigationQueue.NavigationQueue({
|
|
65
|
+
queue = new _NavigationQueue.NavigationQueue({
|
|
76
66
|
getRoot: () => rootLayer,
|
|
77
67
|
findLayer: id => (0, _layerTree.findChaynsHistoryLayerById)(rootLayer, id),
|
|
78
68
|
checkBlocks: target => blockRegistry.checkBlocks(target),
|
|
@@ -90,7 +80,6 @@ function initRootChaynsHistoryLayer(opts = {}) {
|
|
|
90
80
|
silentGo: delta => (0, _navigationIndex.silentGo)(delta),
|
|
91
81
|
getCurrentIdx: () => (0, _navigationIndex.getCurrentIdx)(),
|
|
92
82
|
incrementIdx: () => (0, _navigationIndex.incrementIdx)(),
|
|
93
|
-
onCommit: syncNativeHandling,
|
|
94
83
|
applyUrlSegments: () => {
|
|
95
84
|
if (!(0, _window.hasWindowHistory)()) return {
|
|
96
85
|
changedLayerIds: new Set()
|
|
@@ -114,9 +103,7 @@ function initRootChaynsHistoryLayer(opts = {}) {
|
|
|
114
103
|
};
|
|
115
104
|
}
|
|
116
105
|
});
|
|
117
|
-
queueRef = queue;
|
|
118
106
|
const existingState = (0, _window.hasWindowHistory)() ? window.history.state : null;
|
|
119
|
-
(0, _navigationIndex.syncCurrentIdxFromState)(existingState);
|
|
120
107
|
if (!(0, _stateProjector.hasChaynsHistoryState)(existingState)) {
|
|
121
108
|
var _opts$segmentCount2, _opts$url;
|
|
122
109
|
const segmentCount = (_opts$segmentCount2 = opts.segmentCount) !== null && _opts$segmentCount2 !== void 0 ? _opts$segmentCount2 : 0;
|
|
@@ -150,7 +137,7 @@ function initRootChaynsHistoryLayer(opts = {}) {
|
|
|
150
137
|
};
|
|
151
138
|
delete foreign.__chaynsHistory;
|
|
152
139
|
const initialState = (0, _stateProjector.projectToState)(rootLayer, foreign);
|
|
153
|
-
const idx = (0, _navigationIndex.
|
|
140
|
+
const idx = (0, _navigationIndex.incrementIdx)();
|
|
154
141
|
window.history.replaceState({
|
|
155
142
|
...initialState,
|
|
156
143
|
__chaynsHistory: {
|
|
@@ -160,42 +147,17 @@ function initRootChaynsHistoryLayer(opts = {}) {
|
|
|
160
147
|
}, '', window.location.href);
|
|
161
148
|
} else {
|
|
162
149
|
(0, _stateProjector.applyStateToTree)(rootLayer, existing);
|
|
163
|
-
if ((0, _navigationIndex.syncCurrentIdxFromState)(existing) === null) {
|
|
164
|
-
const foreign = {
|
|
165
|
-
...(existing !== null && existing !== void 0 ? existing : {})
|
|
166
|
-
};
|
|
167
|
-
delete foreign.__chaynsHistory;
|
|
168
|
-
const currentState = (0, _stateProjector.projectToState)(rootLayer, foreign);
|
|
169
|
-
const idx = (0, _navigationIndex.getCurrentIdx)();
|
|
170
|
-
window.history.replaceState({
|
|
171
|
-
...currentState,
|
|
172
|
-
__chaynsHistory: {
|
|
173
|
-
...currentState.__chaynsHistory,
|
|
174
|
-
__idx: idx
|
|
175
|
-
}
|
|
176
|
-
}, '', window.location.href);
|
|
177
|
-
}
|
|
178
150
|
}
|
|
179
|
-
blockRegistry.subscribeToChanges(syncNativeHandling);
|
|
180
151
|
window.addEventListener('popstate', event => {
|
|
181
|
-
(0, _navigationIndex.
|
|
182
|
-
if ((0, _navigationIndex.consumeSilent)()) {
|
|
183
|
-
syncNativeHandling();
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
152
|
+
if ((0, _navigationIndex.consumeSilent)()) return;
|
|
186
153
|
const raw = event.state;
|
|
187
|
-
if (!(0, _stateProjector.hasChaynsHistoryState)(raw)) {
|
|
188
|
-
syncNativeHandling();
|
|
189
|
-
} else {
|
|
190
|
-
const skipBlockCheck = nativeBackHandler.consumeBypassFlag();
|
|
154
|
+
if (!(0, _stateProjector.hasChaynsHistoryState)(raw)) {} else {
|
|
191
155
|
void queue.enqueue({
|
|
192
156
|
kind: 'popstate',
|
|
193
|
-
rawState: raw
|
|
194
|
-
|
|
195
|
-
}).finally(syncNativeHandling);
|
|
157
|
+
rawState: raw
|
|
158
|
+
});
|
|
196
159
|
}
|
|
197
160
|
});
|
|
198
|
-
syncNativeHandling();
|
|
199
161
|
}
|
|
200
162
|
return {
|
|
201
163
|
rootLayer
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
|
|
2
2
|
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
|
|
3
3
|
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
4
|
+
import { invokeCall } from '../../calls';
|
|
4
5
|
const BLOCK_TIMEOUT_MS = 30000;
|
|
5
6
|
let _nextId = 1;
|
|
6
7
|
export class BlockRegistry {
|
|
7
8
|
constructor() {
|
|
8
9
|
_defineProperty(this, "layerBlocks", new Map());
|
|
9
|
-
_defineProperty(this, "changeListeners", new Set());
|
|
10
10
|
_defineProperty(this, "beforeUnloadCount", 0);
|
|
11
11
|
_defineProperty(this, "beforeUnloadHandler", e => {
|
|
12
12
|
e.preventDefault();
|
|
13
|
-
|
|
13
|
+
e.returnValue = '';
|
|
14
14
|
});
|
|
15
|
+
_defineProperty(this, "totalBlockCount", 0);
|
|
16
|
+
_defineProperty(this, "nativeNavigationCallback", () => {});
|
|
15
17
|
}
|
|
16
18
|
add(layer, callback, opts = {}) {
|
|
17
19
|
var _opts$scope, _opts$isBeforeUnload;
|
|
@@ -32,34 +34,27 @@ export class BlockRegistry {
|
|
|
32
34
|
if (entry.opts.isBeforeUnload) {
|
|
33
35
|
this.incrementBeforeUnload();
|
|
34
36
|
}
|
|
35
|
-
this.
|
|
37
|
+
this.incrementTotalBlocks();
|
|
36
38
|
return () => this.remove(layer.id, entry);
|
|
37
39
|
}
|
|
38
40
|
remove(layerId, entry) {
|
|
39
41
|
const set = this.layerBlocks.get(layerId);
|
|
40
42
|
if (!set) return;
|
|
41
|
-
|
|
42
|
-
if (!didDelete) return;
|
|
43
|
+
if (!set.delete(entry)) return;
|
|
43
44
|
if (set.size === 0) this.layerBlocks.delete(layerId);
|
|
44
45
|
if (entry.opts.isBeforeUnload) {
|
|
45
46
|
this.decrementBeforeUnload();
|
|
46
47
|
}
|
|
47
|
-
this.
|
|
48
|
+
this.decrementTotalBlocks();
|
|
48
49
|
}
|
|
49
50
|
removeAllForLayer(layerId) {
|
|
50
51
|
const set = this.layerBlocks.get(layerId);
|
|
51
52
|
if (!set) return;
|
|
52
53
|
for (const entry of set) {
|
|
53
54
|
if (entry.opts.isBeforeUnload) this.decrementBeforeUnload();
|
|
55
|
+
this.decrementTotalBlocks();
|
|
54
56
|
}
|
|
55
57
|
this.layerBlocks.delete(layerId);
|
|
56
|
-
this.notifyChange();
|
|
57
|
-
}
|
|
58
|
-
subscribeToChanges(listener) {
|
|
59
|
-
this.changeListeners.add(listener);
|
|
60
|
-
return () => {
|
|
61
|
-
this.changeListeners.delete(listener);
|
|
62
|
-
};
|
|
63
58
|
}
|
|
64
59
|
collectApplicableBlocks(targetLayer) {
|
|
65
60
|
const result = [];
|
|
@@ -72,20 +67,6 @@ export class BlockRegistry {
|
|
|
72
67
|
this.collectGlobalFromActiveDescendants(targetLayer, result);
|
|
73
68
|
return result;
|
|
74
69
|
}
|
|
75
|
-
hasActiveBlocks(rootLayer) {
|
|
76
|
-
return this.collectActiveChainBlocks(rootLayer).length > 0;
|
|
77
|
-
}
|
|
78
|
-
async checkActiveBlocks(rootLayer) {
|
|
79
|
-
const blocks = this.collectActiveChainBlocks(rootLayer);
|
|
80
|
-
if (blocks.length === 0) return true;
|
|
81
|
-
const results = await Promise.all(blocks.map(b => this.runBlock(b)));
|
|
82
|
-
return results.every(Boolean);
|
|
83
|
-
}
|
|
84
|
-
collectActiveChainBlocks(rootLayer) {
|
|
85
|
-
const result = [];
|
|
86
|
-
this.collectFromActiveChain(rootLayer, result);
|
|
87
|
-
return result;
|
|
88
|
-
}
|
|
89
70
|
collectGlobalFromActiveDescendants(layer, out) {
|
|
90
71
|
const activeChildId = layer.getActiveChildId();
|
|
91
72
|
if (!activeChildId) return;
|
|
@@ -101,19 +82,6 @@ export class BlockRegistry {
|
|
|
101
82
|
}
|
|
102
83
|
this.collectGlobalFromActiveDescendants(child, out);
|
|
103
84
|
}
|
|
104
|
-
collectFromActiveChain(layer, out) {
|
|
105
|
-
const set = this.layerBlocks.get(layer.id);
|
|
106
|
-
if (set) {
|
|
107
|
-
for (const entry of set) {
|
|
108
|
-
out.push(entry);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
const activeChildId = layer.getActiveChildId();
|
|
112
|
-
if (!activeChildId) return;
|
|
113
|
-
const child = layer.getChildLayer(activeChildId);
|
|
114
|
-
if (!child) return;
|
|
115
|
-
this.collectFromActiveChain(child, out);
|
|
116
|
-
}
|
|
117
85
|
async checkBlocks(targetLayer) {
|
|
118
86
|
const blocks = this.collectApplicableBlocks(targetLayer);
|
|
119
87
|
if (blocks.length === 0) return true;
|
|
@@ -126,7 +94,7 @@ export class BlockRegistry {
|
|
|
126
94
|
resolve(false);
|
|
127
95
|
}, BLOCK_TIMEOUT_MS))]);
|
|
128
96
|
return result;
|
|
129
|
-
} catch {
|
|
97
|
+
} catch (err) {
|
|
130
98
|
return false;
|
|
131
99
|
}
|
|
132
100
|
}
|
|
@@ -143,9 +111,27 @@ export class BlockRegistry {
|
|
|
143
111
|
window.removeEventListener('beforeunload', this.beforeUnloadHandler);
|
|
144
112
|
}
|
|
145
113
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
114
|
+
incrementTotalBlocks() {
|
|
115
|
+
this.totalBlockCount++;
|
|
116
|
+
if (this.totalBlockCount > 0) {
|
|
117
|
+
this.setNativeNavigationEnabled(true);
|
|
149
118
|
}
|
|
150
119
|
}
|
|
120
|
+
decrementTotalBlocks() {
|
|
121
|
+
if (this.totalBlockCount === 0) return;
|
|
122
|
+
this.totalBlockCount--;
|
|
123
|
+
if (this.totalBlockCount === 0) {
|
|
124
|
+
this.setNativeNavigationEnabled(false);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
setNativeNavigationEnabled(enabled) {
|
|
128
|
+
try {
|
|
129
|
+
void invokeCall({
|
|
130
|
+
action: 249,
|
|
131
|
+
value: {
|
|
132
|
+
enabled
|
|
133
|
+
}
|
|
134
|
+
}, this.nativeNavigationCallback);
|
|
135
|
+
} catch {}
|
|
136
|
+
}
|
|
151
137
|
}
|
|
@@ -58,6 +58,7 @@ export class NavigationQueue {
|
|
|
58
58
|
return this.processPopstate(op);
|
|
59
59
|
default:
|
|
60
60
|
{
|
|
61
|
+
const _exhaustive = op;
|
|
61
62
|
return {
|
|
62
63
|
isOk: false,
|
|
63
64
|
reason: 'error',
|
|
@@ -293,7 +294,7 @@ export class NavigationQueue {
|
|
|
293
294
|
changedLayerIds.add(this.deps.getRoot().id);
|
|
294
295
|
}
|
|
295
296
|
const target = this.resolveLowestCommonLayer(changedLayerIds);
|
|
296
|
-
if (target
|
|
297
|
+
if (target) {
|
|
297
298
|
const allowed = await this.deps.checkBlocks(target);
|
|
298
299
|
if (!allowed) {
|
|
299
300
|
await this.deps.silentGo(+1);
|
|
@@ -364,7 +365,6 @@ export class NavigationQueue {
|
|
|
364
365
|
return lca;
|
|
365
366
|
}
|
|
366
367
|
commit(isReplace) {
|
|
367
|
-
var _this$deps$onCommit, _this$deps;
|
|
368
368
|
if (!hasWindowHistory()) return;
|
|
369
369
|
const url = this.deps.projectUrl();
|
|
370
370
|
const state = this.deps.projectState();
|
|
@@ -381,6 +381,5 @@ export class NavigationQueue {
|
|
|
381
381
|
} else {
|
|
382
382
|
window.history.pushState(stateWithMeta, '', url);
|
|
383
383
|
}
|
|
384
|
-
(_this$deps$onCommit = (_this$deps = this.deps).onCommit) === null || _this$deps$onCommit === void 0 || _this$deps$onCommit.call(_this$deps);
|
|
385
384
|
}
|
|
386
385
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { hasWindowHistory } from './window';
|
|
2
|
-
const CHAYNS_HISTORY_STATE_KEY = '__chaynsHistory';
|
|
3
2
|
let currentIdx = 0;
|
|
4
3
|
let pendingSilentCount = 0;
|
|
5
4
|
let silentResolve = null;
|
|
@@ -9,34 +8,6 @@ export function incrementIdx() {
|
|
|
9
8
|
export function getCurrentIdx() {
|
|
10
9
|
return currentIdx;
|
|
11
10
|
}
|
|
12
|
-
export function setCurrentIdx(idx) {
|
|
13
|
-
if (!Number.isInteger(idx) || idx < 0) {
|
|
14
|
-
return;
|
|
15
|
-
}
|
|
16
|
-
currentIdx = idx;
|
|
17
|
-
}
|
|
18
|
-
export function extractHistoryIndex(raw) {
|
|
19
|
-
if (!raw || typeof raw !== 'object') {
|
|
20
|
-
return null;
|
|
21
|
-
}
|
|
22
|
-
const chaynsHistory = raw[CHAYNS_HISTORY_STATE_KEY];
|
|
23
|
-
if (!chaynsHistory || typeof chaynsHistory !== 'object') {
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
26
|
-
const idx = chaynsHistory.__idx;
|
|
27
|
-
if (typeof idx !== 'number' || !Number.isInteger(idx) || idx < 0) {
|
|
28
|
-
return null;
|
|
29
|
-
}
|
|
30
|
-
return idx;
|
|
31
|
-
}
|
|
32
|
-
export function syncCurrentIdxFromState(raw) {
|
|
33
|
-
const idx = extractHistoryIndex(raw);
|
|
34
|
-
if (idx === null) {
|
|
35
|
-
return null;
|
|
36
|
-
}
|
|
37
|
-
currentIdx = idx;
|
|
38
|
-
return idx;
|
|
39
|
-
}
|
|
40
11
|
export function silentGo(delta) {
|
|
41
12
|
if (!hasWindowHistory()) return Promise.resolve();
|
|
42
13
|
return new Promise(resolve => {
|
|
@@ -4,12 +4,11 @@ import { BlockRegistry } from './BlockRegistry';
|
|
|
4
4
|
import { findChaynsHistoryLayerById } from './layerTree';
|
|
5
5
|
import { projectToUrl, parseFromUrl } from './url';
|
|
6
6
|
import { projectToState, applyStateToTree, diffIncomingState, hasChaynsHistoryState } from './stateProjector';
|
|
7
|
-
import { silentGo, consumeSilent, incrementIdx, getCurrentIdx
|
|
7
|
+
import { silentGo, consumeSilent, incrementIdx, getCurrentIdx } from './navigationIndex';
|
|
8
8
|
import { hasWindowHistory } from './window';
|
|
9
9
|
import { shallowEqualArr } from '../equality';
|
|
10
|
-
import { getSite } from
|
|
10
|
+
import { getSite } from "../../calls";
|
|
11
11
|
import { normalizeHistorySegments } from './segments';
|
|
12
|
-
import { NativeBackHandler } from './nativeBackHandling';
|
|
13
12
|
function getInitialPathname(overrideUrl) {
|
|
14
13
|
if (overrideUrl) {
|
|
15
14
|
try {
|
|
@@ -41,30 +40,21 @@ export function resolveSegmentsFrom(overrideUrl, startIndex) {
|
|
|
41
40
|
export function initRootChaynsHistoryLayer(opts = {}) {
|
|
42
41
|
var _opts$segmentCount;
|
|
43
42
|
const blockRegistry = new BlockRegistry();
|
|
44
|
-
let
|
|
43
|
+
let rootLayer;
|
|
44
|
+
let queue;
|
|
45
45
|
const deps = {
|
|
46
46
|
getRoot: () => rootLayer,
|
|
47
|
-
getQueue: () =>
|
|
48
|
-
if (!queueRef) {
|
|
49
|
-
throw new Error('[chaynsHistory] NavigationQueue not initialized yet.');
|
|
50
|
-
}
|
|
51
|
-
return queueRef;
|
|
52
|
-
},
|
|
47
|
+
getQueue: () => queue,
|
|
53
48
|
getBlockRegistry: () => blockRegistry
|
|
54
49
|
};
|
|
55
|
-
|
|
50
|
+
rootLayer = new ChaynsHistoryLayer({
|
|
56
51
|
id: 'root',
|
|
57
52
|
parent: null,
|
|
58
53
|
deps,
|
|
59
54
|
segmentCount: (_opts$segmentCount = opts.segmentCount) !== null && _opts$segmentCount !== void 0 ? _opts$segmentCount : 0,
|
|
60
55
|
segments: opts.segmentCount ? resolveInitialSegments(opts.url, opts.segmentCount) : []
|
|
61
56
|
});
|
|
62
|
-
|
|
63
|
-
rootLayer,
|
|
64
|
-
blockRegistry
|
|
65
|
-
});
|
|
66
|
-
const syncNativeHandling = nativeBackHandler.sync;
|
|
67
|
-
const queue = new NavigationQueue({
|
|
57
|
+
queue = new NavigationQueue({
|
|
68
58
|
getRoot: () => rootLayer,
|
|
69
59
|
findLayer: id => findChaynsHistoryLayerById(rootLayer, id),
|
|
70
60
|
checkBlocks: target => blockRegistry.checkBlocks(target),
|
|
@@ -82,7 +72,6 @@ export function initRootChaynsHistoryLayer(opts = {}) {
|
|
|
82
72
|
silentGo: delta => silentGo(delta),
|
|
83
73
|
getCurrentIdx: () => getCurrentIdx(),
|
|
84
74
|
incrementIdx: () => incrementIdx(),
|
|
85
|
-
onCommit: syncNativeHandling,
|
|
86
75
|
applyUrlSegments: () => {
|
|
87
76
|
if (!hasWindowHistory()) return {
|
|
88
77
|
changedLayerIds: new Set()
|
|
@@ -106,9 +95,7 @@ export function initRootChaynsHistoryLayer(opts = {}) {
|
|
|
106
95
|
};
|
|
107
96
|
}
|
|
108
97
|
});
|
|
109
|
-
queueRef = queue;
|
|
110
98
|
const existingState = hasWindowHistory() ? window.history.state : null;
|
|
111
|
-
syncCurrentIdxFromState(existingState);
|
|
112
99
|
if (!hasChaynsHistoryState(existingState)) {
|
|
113
100
|
var _opts$segmentCount2, _opts$url;
|
|
114
101
|
const segmentCount = (_opts$segmentCount2 = opts.segmentCount) !== null && _opts$segmentCount2 !== void 0 ? _opts$segmentCount2 : 0;
|
|
@@ -142,7 +129,7 @@ export function initRootChaynsHistoryLayer(opts = {}) {
|
|
|
142
129
|
};
|
|
143
130
|
delete foreign.__chaynsHistory;
|
|
144
131
|
const initialState = projectToState(rootLayer, foreign);
|
|
145
|
-
const idx =
|
|
132
|
+
const idx = incrementIdx();
|
|
146
133
|
window.history.replaceState({
|
|
147
134
|
...initialState,
|
|
148
135
|
__chaynsHistory: {
|
|
@@ -152,42 +139,17 @@ export function initRootChaynsHistoryLayer(opts = {}) {
|
|
|
152
139
|
}, '', window.location.href);
|
|
153
140
|
} else {
|
|
154
141
|
applyStateToTree(rootLayer, existing);
|
|
155
|
-
if (syncCurrentIdxFromState(existing) === null) {
|
|
156
|
-
const foreign = {
|
|
157
|
-
...(existing !== null && existing !== void 0 ? existing : {})
|
|
158
|
-
};
|
|
159
|
-
delete foreign.__chaynsHistory;
|
|
160
|
-
const currentState = projectToState(rootLayer, foreign);
|
|
161
|
-
const idx = getCurrentIdx();
|
|
162
|
-
window.history.replaceState({
|
|
163
|
-
...currentState,
|
|
164
|
-
__chaynsHistory: {
|
|
165
|
-
...currentState.__chaynsHistory,
|
|
166
|
-
__idx: idx
|
|
167
|
-
}
|
|
168
|
-
}, '', window.location.href);
|
|
169
|
-
}
|
|
170
142
|
}
|
|
171
|
-
blockRegistry.subscribeToChanges(syncNativeHandling);
|
|
172
143
|
window.addEventListener('popstate', event => {
|
|
173
|
-
|
|
174
|
-
if (consumeSilent()) {
|
|
175
|
-
syncNativeHandling();
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
144
|
+
if (consumeSilent()) return;
|
|
178
145
|
const raw = event.state;
|
|
179
|
-
if (!hasChaynsHistoryState(raw)) {
|
|
180
|
-
syncNativeHandling();
|
|
181
|
-
} else {
|
|
182
|
-
const skipBlockCheck = nativeBackHandler.consumeBypassFlag();
|
|
146
|
+
if (!hasChaynsHistoryState(raw)) {} else {
|
|
183
147
|
void queue.enqueue({
|
|
184
148
|
kind: 'popstate',
|
|
185
|
-
rawState: raw
|
|
186
|
-
|
|
187
|
-
}).finally(syncNativeHandling);
|
|
149
|
+
rawState: raw
|
|
150
|
+
});
|
|
188
151
|
}
|
|
189
152
|
});
|
|
190
|
-
syncNativeHandling();
|
|
191
153
|
}
|
|
192
154
|
return {
|
|
193
155
|
rootLayer
|
|
@@ -8,15 +8,16 @@ interface BlockEntry {
|
|
|
8
8
|
export declare class BlockRegistry {
|
|
9
9
|
/** Per-layer block list. */
|
|
10
10
|
private readonly layerBlocks;
|
|
11
|
-
private readonly changeListeners;
|
|
12
11
|
/** Number of blocks with `isBeforeUnload: true`. When > 0, listener is attached. */
|
|
13
12
|
private beforeUnloadCount;
|
|
14
13
|
private readonly beforeUnloadHandler;
|
|
14
|
+
/** Total number of currently registered blocks across all layers.
|
|
15
|
+
* Used to toggle the native navigation handling (chayns app, action 249). */
|
|
16
|
+
private totalBlockCount;
|
|
15
17
|
add(layer: ChaynsHistoryLayer, callback: () => Promise<boolean>, opts?: ChaynsHistoryBlockOptions): () => void;
|
|
16
18
|
remove(layerId: string, entry: BlockEntry): void;
|
|
17
19
|
/** Remove all blocks registered for a layer (called on destroy). */
|
|
18
20
|
removeAllForLayer(layerId: string): void;
|
|
19
|
-
subscribeToChanges(listener: () => void): () => void;
|
|
20
21
|
/**
|
|
21
22
|
* Collects all blocks applicable to a navigation targeting `targetLayer`.
|
|
22
23
|
*
|
|
@@ -26,11 +27,7 @@ export declare class BlockRegistry {
|
|
|
26
27
|
* **active-chain descendants** (not inactive subtrees).
|
|
27
28
|
*/
|
|
28
29
|
collectApplicableBlocks(targetLayer: ChaynsHistoryLayer): BlockEntry[];
|
|
29
|
-
hasActiveBlocks(rootLayer: ChaynsHistoryLayer): boolean;
|
|
30
|
-
checkActiveBlocks(rootLayer: ChaynsHistoryLayer): Promise<boolean>;
|
|
31
|
-
private collectActiveChainBlocks;
|
|
32
30
|
private collectGlobalFromActiveDescendants;
|
|
33
|
-
private collectFromActiveChain;
|
|
34
31
|
/**
|
|
35
32
|
* Runs all applicable block callbacks in parallel.
|
|
36
33
|
* Returns `true` if navigation is allowed (no block), `false` otherwise.
|
|
@@ -40,6 +37,29 @@ export declare class BlockRegistry {
|
|
|
40
37
|
private runBlock;
|
|
41
38
|
private incrementBeforeUnload;
|
|
42
39
|
private decrementBeforeUnload;
|
|
43
|
-
private
|
|
40
|
+
private incrementTotalBlocks;
|
|
41
|
+
private decrementTotalBlocks;
|
|
42
|
+
/**
|
|
43
|
+
* Toggles the host app's native navigation handling so that a registered
|
|
44
|
+
* block actually prevents the user from navigating natively (e.g. via the
|
|
45
|
+
* Android back button or the swipe gesture in the chayns app).
|
|
46
|
+
*
|
|
47
|
+
* Maps to chayns invokeCall action `249`:
|
|
48
|
+
* ```
|
|
49
|
+
* chayns.invokeCall({ action: 249, value: { enabled, callback } });
|
|
50
|
+
* ```
|
|
51
|
+
*
|
|
52
|
+
* `enabled: true` here means: "a block is active, take over navigation" —
|
|
53
|
+
* the host disables its own native back handling and instead forwards the
|
|
54
|
+
* event to us via the provided callback, where we can run our block logic.
|
|
55
|
+
*/
|
|
56
|
+
private setNativeNavigationEnabled;
|
|
57
|
+
/**
|
|
58
|
+
* Invoked by the host app when the user attempts a native navigation
|
|
59
|
+
* gesture while blocks are active. We run the block callbacks against the
|
|
60
|
+
* current root layer; if any of them denies, navigation stays cancelled.
|
|
61
|
+
* When all blocks allow the navigation we trigger a normal back.
|
|
62
|
+
*/
|
|
63
|
+
private readonly nativeNavigationCallback;
|
|
44
64
|
}
|
|
45
65
|
export {};
|
|
@@ -48,13 +48,6 @@ export type NavOp = {
|
|
|
48
48
|
} | {
|
|
49
49
|
kind: 'popstate';
|
|
50
50
|
rawState: unknown;
|
|
51
|
-
/**
|
|
52
|
-
* @internal Skips the block check pipeline for this popstate. Used when
|
|
53
|
-
* the popstate was triggered by us right after a native-back callback
|
|
54
|
-
* that already ran `checkActiveBlocks` — re-running it would prompt the
|
|
55
|
-
* user twice.
|
|
56
|
-
*/
|
|
57
|
-
skipBlockCheck?: boolean;
|
|
58
51
|
};
|
|
59
52
|
export type NavResult = ChaynsHistoryActionResult;
|
|
60
53
|
export interface NavigationQueueDeps {
|
|
@@ -85,8 +78,6 @@ export interface NavigationQueueDeps {
|
|
|
85
78
|
getCurrentIdx: () => number;
|
|
86
79
|
/** Increments the navigation index and returns the new value. */
|
|
87
80
|
incrementIdx: () => number;
|
|
88
|
-
/** Called after a browser history commit finished. */
|
|
89
|
-
onCommit?: () => void;
|
|
90
81
|
/**
|
|
91
82
|
* Re-parse segments from `window.location.pathname` and apply them to each
|
|
92
83
|
* layer in the active chain based on its `segmentCount`. Called after
|
|
@@ -2,9 +2,6 @@
|
|
|
2
2
|
export declare function incrementIdx(): number;
|
|
3
3
|
/** Get current index for stamping real entries or direction comparison. */
|
|
4
4
|
export declare function getCurrentIdx(): number;
|
|
5
|
-
export declare function setCurrentIdx(idx: number): void;
|
|
6
|
-
export declare function extractHistoryIndex(raw: unknown): number | null;
|
|
7
|
-
export declare function syncCurrentIdxFromState(raw: unknown): number | null;
|
|
8
5
|
/**
|
|
9
6
|
* Performs a `history.go(delta)` that is silently ignored by our popstate handler.
|
|
10
7
|
* Returns a promise that resolves when the popstate for this go() fires.
|
package/package.json
CHANGED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.NativeBackHandler = void 0;
|
|
7
|
-
var _calls = require("../../calls");
|
|
8
|
-
var _IChaynsReact = require("../../types/IChaynsReact");
|
|
9
|
-
var _navigationIndex = require("./navigationIndex");
|
|
10
|
-
var _window = require("./window");
|
|
11
|
-
const DISABLE_SWIPE_BACK_GESTURE_ACTION = 249;
|
|
12
|
-
class NativeBackHandler {
|
|
13
|
-
bypassNextPopstateBlockCheck = false;
|
|
14
|
-
constructor(opts) {
|
|
15
|
-
this.opts = opts;
|
|
16
|
-
}
|
|
17
|
-
sync = () => {
|
|
18
|
-
if (!(0, _window.hasWindowHistory)() || !NativeBackHandler.isSupported()) {
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
const next = this.shouldEnableInterception();
|
|
22
|
-
if (this.isInterceptionEnabled === next) {
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
void (0, _calls.invokeCall)({
|
|
26
|
-
action: DISABLE_SWIPE_BACK_GESTURE_ACTION,
|
|
27
|
-
value: {
|
|
28
|
-
enabled: next
|
|
29
|
-
}
|
|
30
|
-
}, next ? this.handleNativeBack : undefined);
|
|
31
|
-
this.isInterceptionEnabled = next;
|
|
32
|
-
};
|
|
33
|
-
consumeBypassFlag() {
|
|
34
|
-
if (!this.bypassNextPopstateBlockCheck) return false;
|
|
35
|
-
this.bypassNextPopstateBlockCheck = false;
|
|
36
|
-
return true;
|
|
37
|
-
}
|
|
38
|
-
static isSupported() {
|
|
39
|
-
try {
|
|
40
|
-
var _getDevice$app;
|
|
41
|
-
return ((_getDevice$app = (0, _calls.getDevice)().app) === null || _getDevice$app === void 0 ? void 0 : _getDevice$app.flavor) === _IChaynsReact.AppFlavor.Chayns;
|
|
42
|
-
} catch {
|
|
43
|
-
return false;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
shouldEnableInterception() {
|
|
47
|
-
return this.opts.blockRegistry.hasActiveBlocks(this.opts.rootLayer) || (0, _navigationIndex.getCurrentIdx)() > 0;
|
|
48
|
-
}
|
|
49
|
-
handleNativeBack = () => {
|
|
50
|
-
void this.runNativeBack();
|
|
51
|
-
};
|
|
52
|
-
async runNativeBack() {
|
|
53
|
-
const isAllowed = await this.opts.blockRegistry.checkActiveBlocks(this.opts.rootLayer);
|
|
54
|
-
if (!isAllowed || !(0, _window.hasWindowHistory)()) {
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
this.bypassNextPopstateBlockCheck = true;
|
|
58
|
-
window.history.back();
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
exports.NativeBackHandler = NativeBackHandler;
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
|
|
2
|
-
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
|
|
3
|
-
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
4
|
-
import { getDevice, invokeCall } from '../../calls';
|
|
5
|
-
import { AppFlavor } from '../../types/IChaynsReact';
|
|
6
|
-
import { getCurrentIdx } from './navigationIndex';
|
|
7
|
-
import { hasWindowHistory } from './window';
|
|
8
|
-
const DISABLE_SWIPE_BACK_GESTURE_ACTION = 249;
|
|
9
|
-
export class NativeBackHandler {
|
|
10
|
-
constructor(opts) {
|
|
11
|
-
_defineProperty(this, "opts", void 0);
|
|
12
|
-
_defineProperty(this, "isInterceptionEnabled", void 0);
|
|
13
|
-
_defineProperty(this, "bypassNextPopstateBlockCheck", false);
|
|
14
|
-
_defineProperty(this, "sync", () => {
|
|
15
|
-
if (!hasWindowHistory() || !NativeBackHandler.isSupported()) {
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
const next = this.shouldEnableInterception();
|
|
19
|
-
if (this.isInterceptionEnabled === next) {
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
void invokeCall({
|
|
23
|
-
action: DISABLE_SWIPE_BACK_GESTURE_ACTION,
|
|
24
|
-
value: {
|
|
25
|
-
enabled: next
|
|
26
|
-
}
|
|
27
|
-
}, next ? this.handleNativeBack : undefined);
|
|
28
|
-
this.isInterceptionEnabled = next;
|
|
29
|
-
});
|
|
30
|
-
_defineProperty(this, "handleNativeBack", () => {
|
|
31
|
-
void this.runNativeBack();
|
|
32
|
-
});
|
|
33
|
-
this.opts = opts;
|
|
34
|
-
}
|
|
35
|
-
consumeBypassFlag() {
|
|
36
|
-
if (!this.bypassNextPopstateBlockCheck) return false;
|
|
37
|
-
this.bypassNextPopstateBlockCheck = false;
|
|
38
|
-
return true;
|
|
39
|
-
}
|
|
40
|
-
static isSupported() {
|
|
41
|
-
try {
|
|
42
|
-
var _getDevice$app;
|
|
43
|
-
return ((_getDevice$app = getDevice().app) === null || _getDevice$app === void 0 ? void 0 : _getDevice$app.flavor) === AppFlavor.Chayns;
|
|
44
|
-
} catch {
|
|
45
|
-
return false;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
shouldEnableInterception() {
|
|
49
|
-
return this.opts.blockRegistry.hasActiveBlocks(this.opts.rootLayer) || getCurrentIdx() > 0;
|
|
50
|
-
}
|
|
51
|
-
async runNativeBack() {
|
|
52
|
-
const isAllowed = await this.opts.blockRegistry.checkActiveBlocks(this.opts.rootLayer);
|
|
53
|
-
if (!isAllowed || !hasWindowHistory()) {
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
this.bypassNextPopstateBlockCheck = true;
|
|
57
|
-
window.history.back();
|
|
58
|
-
}
|
|
59
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import type { ChaynsHistoryLayer } from '../../handler/history/HistoryLayer';
|
|
2
|
-
import type { BlockRegistry } from './BlockRegistry';
|
|
3
|
-
export interface NativeBackHandlerOptions {
|
|
4
|
-
rootLayer: ChaynsHistoryLayer;
|
|
5
|
-
blockRegistry: BlockRegistry;
|
|
6
|
-
}
|
|
7
|
-
/**
|
|
8
|
-
* Coordinates the native swipe-back gesture (Chayns app action 249) with the
|
|
9
|
-
* JS history queue.
|
|
10
|
-
*
|
|
11
|
-
* Why we disable the gesture as soon as the user has navigated inside the app
|
|
12
|
-
* (`getCurrentIdx() > 0`) and not only while a block is registered:
|
|
13
|
-
* The native swipe animation runs independently of our JS handling. Without
|
|
14
|
-
* intercepting it, a swipe-back would (a) play the native pop animation,
|
|
15
|
-
* (b) pop the browser entry, (c) fire popstate, and only THEN would the
|
|
16
|
-
* queue evaluate the block and silently push forward again — producing the
|
|
17
|
-
* "navigate back, then snap forward" jitter described in the bug report.
|
|
18
|
-
* By intercepting from the first own history entry on, every back must come
|
|
19
|
-
* through the registered callback where we can resolve blocks before
|
|
20
|
-
* mutating history.
|
|
21
|
-
*
|
|
22
|
-
* Instances are scoped to a single root layer; module-level state is avoided
|
|
23
|
-
* so re-inits (HMR, tests, multiple roots) cannot desynchronise.
|
|
24
|
-
*/
|
|
25
|
-
export declare class NativeBackHandler {
|
|
26
|
-
private readonly opts;
|
|
27
|
-
private isInterceptionEnabled;
|
|
28
|
-
/**
|
|
29
|
-
* Set to `true` right before `history.back()` is triggered from
|
|
30
|
-
* {@link handleNativeBack}. Consumed by the popstate listener so the
|
|
31
|
-
* queue can skip the duplicate block check (we already ran it).
|
|
32
|
-
*/
|
|
33
|
-
private bypassNextPopstateBlockCheck;
|
|
34
|
-
constructor(opts: NativeBackHandlerOptions);
|
|
35
|
-
/** Re-evaluates the desired native gesture state and pushes it to the app. */
|
|
36
|
-
sync: () => void;
|
|
37
|
-
/**
|
|
38
|
-
* Returns `true` exactly once after a {@link handleNativeBack}-initiated
|
|
39
|
-
* `history.back()`. The popstate listener uses this so the queue can skip
|
|
40
|
-
* the (already performed) block check.
|
|
41
|
-
*/
|
|
42
|
-
consumeBypassFlag(): boolean;
|
|
43
|
-
private static isSupported;
|
|
44
|
-
private shouldEnableInterception;
|
|
45
|
-
private handleNativeBack;
|
|
46
|
-
private runNativeBack;
|
|
47
|
-
}
|