forked from Expensify/App
-
Notifications
You must be signed in to change notification settings - Fork 0
/
@react-navigation+native+6.1.6.patch
299 lines (298 loc) · 11.5 KB
/
@react-navigation+native+6.1.6.patch
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
diff --git a/node_modules/@react-navigation/native/lib/module/createMemoryHistory.js b/node_modules/@react-navigation/native/lib/module/createMemoryHistory.js
index 16fdbef..bc2c96a 100644
--- a/node_modules/@react-navigation/native/lib/module/createMemoryHistory.js
+++ b/node_modules/@react-navigation/native/lib/module/createMemoryHistory.js
@@ -1,8 +1,23 @@
import { nanoid } from 'nanoid/non-secure';
+import { findFocusedRouteKey } from './findFocusedRouteKey';
export default function createMemoryHistory() {
let index = 0;
let items = [];
-
+ const log = () => {
+ console.log(JSON.stringify({
+ index,
+ indexGetter: history.index,
+ items: items.map((item, i) => {
+ var _item$state;
+ return {
+ selected: history.index === i ? '<<<<<<<' : undefined,
+ path: item.path,
+ id: item.id,
+ state: ((_item$state = item.state) === null || _item$state === void 0 ? void 0 : _item$state.key) || null
+ };
+ })
+ }, null, 4));
+ };
// Pending callbacks for `history.go(n)`
// We might modify the callback stored if it was interrupted, so we have a ref to identify it
const pending = [];
@@ -16,6 +31,9 @@ export default function createMemoryHistory() {
});
};
const history = {
+ get items() {
+ return items;
+ },
get index() {
var _window$history$state;
// We store an id in the state instead of an index
@@ -32,12 +50,13 @@ export default function createMemoryHistory() {
},
backIndex(_ref) {
let {
- path
+ path,
+ state
} = _ref;
// We need to find the index from the element before current to get closest path to go back to
for (let i = index - 1; i >= 0; i--) {
const item = items[i];
- if (item.path === path) {
+ if (item.path === path && findFocusedRouteKey(item.state) === findFocusedRouteKey(state)) {
return i;
}
}
@@ -68,7 +87,9 @@ export default function createMemoryHistory() {
window.history.pushState({
id
}, '', path);
+ // log();
},
+
replace(_ref3) {
var _window$history$state2;
let {
@@ -108,7 +129,9 @@ export default function createMemoryHistory() {
window.history.replaceState({
id
}, '', pathWithHash);
+ // log();
},
+
// `history.go(n)` is asynchronous, there are couple of things to keep in mind:
// - it won't do anything if we can't go `n` steps, the `popstate` event won't fire.
// - each `history.go(n)` call will trigger a separate `popstate` event with correct location.
@@ -175,20 +198,17 @@ export default function createMemoryHistory() {
// But on Firefox, it seems to take much longer, around 50ms from our testing
// We're using a hacky timeout since there doesn't seem to be way to know for sure
const timer = setTimeout(() => {
- const index = pending.findIndex(it => it.ref === done);
- if (index > -1) {
- pending[index].cb();
- pending.splice(index, 1);
+ const foundIndex = pending.findIndex(it => it.ref === done);
+ if (foundIndex > -1) {
+ pending[foundIndex].cb();
+ pending.splice(foundIndex, 1);
}
+ index = this.index;
}, 100);
const onPopState = () => {
- var _window$history$state3;
- const id = (_window$history$state3 = window.history.state) === null || _window$history$state3 === void 0 ? void 0 : _window$history$state3.id;
- const currentIndex = items.findIndex(item => item.id === id);
-
// Fix createMemoryHistory.index variable's value
// as it may go out of sync when navigating in the browser.
- index = Math.max(currentIndex, 0);
+ index = this.index;
const last = pending.pop();
window.removeEventListener('popstate', onPopState);
last === null || last === void 0 ? void 0 : last.cb();
@@ -202,12 +222,17 @@ export default function createMemoryHistory() {
// Here we normalize it so that only external changes (e.g. user pressing back/forward) trigger the listener
listen(listener) {
const onPopState = () => {
+ // Fix createMemoryHistory.index variable's value
+ // as it may go out of sync when navigating in the browser.
+ index = this.index;
if (pending.length) {
// This was triggered by `history.go(n)`, we shouldn't call the listener
return;
}
listener();
+ // log();
};
+
window.addEventListener('popstate', onPopState);
return () => window.removeEventListener('popstate', onPopState);
}
diff --git a/node_modules/@react-navigation/native/lib/module/findFocusedRouteKey.js b/node_modules/@react-navigation/native/lib/module/findFocusedRouteKey.js
new file mode 100644
index 0000000..16da117
--- /dev/null
+++ b/node_modules/@react-navigation/native/lib/module/findFocusedRouteKey.js
@@ -0,0 +1,7 @@
+import { findFocusedRoute } from '@react-navigation/core';
+export const findFocusedRouteKey = state => {
+ var _findFocusedRoute;
+ // @ts-ignore
+ return (_findFocusedRoute = findFocusedRoute(state)) === null || _findFocusedRoute === void 0 ? void 0 : _findFocusedRoute.key;
+};
+//# sourceMappingURL=findFocusedRouteKey.js.map
\ No newline at end of file
diff --git a/node_modules/@react-navigation/native/lib/module/useLinking.js b/node_modules/@react-navigation/native/lib/module/useLinking.js
index 5bf2a88..a4318ef 100644
--- a/node_modules/@react-navigation/native/lib/module/useLinking.js
+++ b/node_modules/@react-navigation/native/lib/module/useLinking.js
@@ -2,6 +2,7 @@ import { findFocusedRoute, getActionFromState as getActionFromStateDefault, getP
import isEqual from 'fast-deep-equal';
import * as React from 'react';
import createMemoryHistory from './createMemoryHistory';
+import { findFocusedRouteKey } from './findFocusedRouteKey';
import ServerContext from './ServerContext';
/**
* Find the matching navigation state that changed between 2 navigation states
@@ -34,32 +35,52 @@ const findMatchingState = (a, b) => {
/**
* Run async function in series as it's called.
*/
-const series = cb => {
- // Whether we're currently handling a callback
- let handling = false;
- let queue = [];
- const callback = async () => {
- try {
- if (handling) {
- // If we're currently handling a previous event, wait before handling this one
- // Add the callback to the beginning of the queue
- queue.unshift(callback);
- return;
- }
- handling = true;
- await cb();
- } finally {
- handling = false;
- if (queue.length) {
- // If we have queued items, handle the last one
- const last = queue.pop();
- last === null || last === void 0 ? void 0 : last();
- }
- }
+const series = (cb) => {
+ let queue = Promise.resolve();
+ const callback = () => {
+ queue = queue.then(cb);
};
return callback;
};
let linkingHandlers = [];
+const getAllStateKeys = state => {
+ let current = state;
+ const keys = [];
+ if (current.routes) {
+ for (let route of current.routes) {
+ keys.push(route.key);
+ if (route.state) {
+ // @ts-ignore
+ keys.push(...getAllStateKeys(route.state));
+ }
+ }
+ }
+ return keys;
+};
+const getStaleHistoryDiff = (items, newState) => {
+ const newStateKeys = getAllStateKeys(newState);
+ for (let i = items.length - 1; i >= 0; i--) {
+ const itemFocusedKey = findFocusedRouteKey(items[i].state);
+ if (newStateKeys.includes(itemFocusedKey)) {
+ return items.length - i - 1;
+ }
+ }
+ return -1;
+};
+const getHistoryDeltaByKeys = (focusedState, previousFocusedState) => {
+ const focusedStateKeys = focusedState.routes.map(r => r.key);
+ const previousFocusedStateKeys = previousFocusedState.routes.map(r => r.key);
+ const minLength = Math.min(focusedStateKeys.length, previousFocusedStateKeys.length);
+ let matchingKeys = 0;
+ for (let i = 0; i < minLength; i++) {
+ if (focusedStateKeys[i] === previousFocusedStateKeys[i]) {
+ matchingKeys++;
+ } else {
+ break;
+ }
+ }
+ return -(previousFocusedStateKeys.length - matchingKeys);
+};
export default function useLinking(ref, _ref) {
let {
independent,
@@ -251,6 +272,9 @@ export default function useLinking(ref, _ref) {
// Otherwise it's likely a change triggered by `popstate`
path !== pendingPath) {
const historyDelta = (focusedState.history ? focusedState.history.length : focusedState.routes.length) - (previousFocusedState.history ? previousFocusedState.history.length : previousFocusedState.routes.length);
+
+ // The historyDelta and historyDeltaByKeys may differ if the new state has an entry that didn't exist in previous state
+ const historyDeltaByKeys = getHistoryDeltaByKeys(focusedState, previousFocusedState);
if (historyDelta > 0) {
// If history length is increased, we should pushState
// Note that path might not actually change here, for example, drawer open should pushState
@@ -262,34 +286,55 @@ export default function useLinking(ref, _ref) {
// If history length is decreased, i.e. entries were removed, we want to go back
const nextIndex = history.backIndex({
- path
+ path,
+ state
});
const currentIndex = history.index;
try {
if (nextIndex !== -1 && nextIndex < currentIndex) {
// An existing entry for this path exists and it's less than current index, go back to that
await history.go(nextIndex - currentIndex);
+ history.replace({
+ path,
+ state
+ });
} else {
// We couldn't find an existing entry to go back to, so we'll go back by the delta
// This won't be correct if multiple routes were pushed in one go before
// Usually this shouldn't happen and this is a fallback for that
- await history.go(historyDelta);
+ await history.go(historyDeltaByKeys);
+ if (historyDeltaByKeys + 1 === historyDelta) {
+ history.push({
+ path,
+ state
+ });
+ } else {
+ history.replace({
+ path,
+ state
+ });
+ }
}
-
- // Store the updated state as well as fix the path if incorrect
- history.replace({
- path,
- state
- });
} catch (e) {
// The navigation was interrupted
}
} else {
// If history length is unchanged, we want to replaceState
- history.replace({
- path,
- state
- });
+ // and remove any entries from history which focued route no longer exists in state
+ // That may happen if we replace a whole navigator.
+ const staleHistoryDiff = getStaleHistoryDiff(history.items.slice(0, history.index + 1), state);
+ if (staleHistoryDiff <= 0) {
+ history.replace({
+ path,
+ state
+ });
+ } else {
+ await history.go(-staleHistoryDiff);
+ history.push({
+ path,
+ state
+ });
+ }
}
} else {
// If no common navigation state was found, assume it's a replace