From 61b3b63000da40b6e12f5c68db3bb462195471e2 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Mon, 21 Aug 2023 18:38:53 +0530 Subject: [PATCH 1/3] fix the race-condition of history.go --- patches/@react-navigation+native+6.1.6.patch | 84 +++++++++++++++----- 1 file changed, 65 insertions(+), 19 deletions(-) diff --git a/patches/@react-navigation+native+6.1.6.patch b/patches/@react-navigation+native+6.1.6.patch index 61e5eb9892e1..57683a837054 100644 --- a/patches/@react-navigation+native+6.1.6.patch +++ b/patches/@react-navigation+native+6.1.6.patch @@ -1,5 +1,5 @@ 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 +index 16fdbef..8fca56f 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 @@ @@ -73,6 +73,15 @@ index 16fdbef..bc2c96a 100644 // `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. +@@ -133,7 +156,7 @@ export default function createMemoryHistory() { + index = nextIndex; + } + if (n === 0) { +- return; ++ return Promise.resolve(); + } + + // When we call `history.go`, `popstate` will fire when there's history to go back to @@ -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 @@ -133,7 +142,7 @@ index 0000000..16da117 +//# 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..a6d0670 100644 +index 5bf2a88..da708ba 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 @@ -144,7 +153,37 @@ index 5bf2a88..a6d0670 100644 import ServerContext from './ServerContext'; /** * Find the matching navigation state that changed between 2 navigation states -@@ -60,6 +61,44 @@ const series = cb => { +@@ -34,32 +35,53 @@ 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 = []; @@ -172,6 +211,7 @@ index 5bf2a88..a6d0670 100644 + } + return -1; +}; ++ +const getHistoryDeltaByKeys = (focusedState, previousFocusedState) => { + const focusedStateKeys = focusedState.routes.map(r => r.key); + const previousFocusedStateKeys = previousFocusedState.routes.map(r => r.key); @@ -189,17 +229,15 @@ index 5bf2a88..a6d0670 100644 export default function useLinking(ref, _ref) { let { independent, -@@ -251,6 +290,9 @@ export default function useLinking(ref, _ref) { +@@ -251,6 +273,7 @@ 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 +304,55 @@ export default function useLinking(ref, _ref) { +@@ -262,34 +285,63 @@ 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({ @@ -212,6 +250,7 @@ index 5bf2a88..a6d0670 100644 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); ++ // Store the updated state as well as fix the path if incorrect + history.replace({ + path, + state @@ -221,6 +260,15 @@ index 5bf2a88..a6d0670 100644 // 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); +- } + +- // Store the updated state as well as fix the path if incorrect +- history.replace({ +- path, +- state +- }); ++ // 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); + await history.go(historyDeltaByKeys); + if (historyDeltaByKeys + 1 === historyDelta) { + history.push({ @@ -233,13 +281,7 @@ index 5bf2a88..a6d0670 100644 + state + }); + } - } -- -- // Store the updated state as well as fix the path if incorrect -- history.replace({ -- path, -- state -- }); ++ } } catch (e) { // The navigation was interrupted } @@ -258,11 +300,15 @@ index 5bf2a88..a6d0670 100644 + state + }); + } else { -+ await history.go(-staleHistoryDiff); -+ history.push({ -+ path, -+ state -+ }); ++ try { ++ await history.go(-staleHistoryDiff); ++ history.push({ ++ path, ++ state ++ }); ++ } catch (e) { ++ // The navigation was interrupted ++ } + } } } else { From 877c28d85c5311bc11f0f0305ca27ab2c4ee9429 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Thu, 24 Aug 2023 23:18:50 +0530 Subject: [PATCH 2/3] changes to patch to include change in series function --- patches/@react-navigation+native+6.1.6.patch | 39 ++++++-------------- 1 file changed, 12 insertions(+), 27 deletions(-) diff --git a/patches/@react-navigation+native+6.1.6.patch b/patches/@react-navigation+native+6.1.6.patch index 57683a837054..40d0372df67d 100644 --- a/patches/@react-navigation+native+6.1.6.patch +++ b/patches/@react-navigation+native+6.1.6.patch @@ -1,5 +1,5 @@ diff --git a/node_modules/@react-navigation/native/lib/module/createMemoryHistory.js b/node_modules/@react-navigation/native/lib/module/createMemoryHistory.js -index 16fdbef..8fca56f 100644 +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 @@ @@ -73,15 +73,6 @@ index 16fdbef..8fca56f 100644 // `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. -@@ -133,7 +156,7 @@ export default function createMemoryHistory() { - index = nextIndex; - } - if (n === 0) { -- return; -+ return Promise.resolve(); - } - - // When we call `history.go`, `popstate` will fire when there's history to go back to @@ -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 @@ -142,7 +133,7 @@ index 0000000..16da117 +//# 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..da708ba 100644 +index 5bf2a88..abfa7d7 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 @@ -153,7 +144,7 @@ index 5bf2a88..da708ba 100644 import ServerContext from './ServerContext'; /** * Find the matching navigation state that changed between 2 navigation states -@@ -34,32 +35,53 @@ const findMatchingState = (a, b) => { +@@ -34,32 +35,52 @@ const findMatchingState = (a, b) => { /** * Run async function in series as it's called. */ @@ -211,7 +202,6 @@ index 5bf2a88..da708ba 100644 + } + return -1; +}; -+ +const getHistoryDeltaByKeys = (focusedState, previousFocusedState) => { + const focusedStateKeys = focusedState.routes.map(r => r.key); + const previousFocusedStateKeys = previousFocusedState.routes.map(r => r.key); @@ -229,15 +219,17 @@ index 5bf2a88..da708ba 100644 export default function useLinking(ref, _ref) { let { independent, -@@ -251,6 +273,7 @@ export default function useLinking(ref, _ref) { +@@ -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 +285,63 @@ export default function useLinking(ref, _ref) { +@@ -262,34 +286,56 @@ 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({ @@ -250,7 +242,6 @@ index 5bf2a88..da708ba 100644 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); -+ // Store the updated state as well as fix the path if incorrect + history.replace({ + path, + state @@ -267,8 +258,6 @@ index 5bf2a88..da708ba 100644 - path, - state - }); -+ // 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); + await history.go(historyDeltaByKeys); + if (historyDeltaByKeys + 1 === historyDelta) { + history.push({ @@ -300,15 +289,11 @@ index 5bf2a88..da708ba 100644 + state + }); + } else { -+ try { -+ await history.go(-staleHistoryDiff); -+ history.push({ -+ path, -+ state -+ }); -+ } catch (e) { -+ // The navigation was interrupted -+ } ++ await history.go(-staleHistoryDiff); ++ history.push({ ++ path, ++ state ++ }); + } } } else { From e17f33adf6131d116d1a0cff289dd08735749d8d Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Thu, 24 Aug 2023 23:31:34 +0530 Subject: [PATCH 3/3] small change leads to different patch --- patches/@react-navigation+native+6.1.6.patch | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/patches/@react-navigation+native+6.1.6.patch b/patches/@react-navigation+native+6.1.6.patch index 40d0372df67d..eb933683c850 100644 --- a/patches/@react-navigation+native+6.1.6.patch +++ b/patches/@react-navigation+native+6.1.6.patch @@ -133,7 +133,7 @@ index 0000000..16da117 +//# 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..abfa7d7 100644 +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 @@ -229,7 +229,7 @@ index 5bf2a88..abfa7d7 100644 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,56 @@ export default function useLinking(ref, _ref) { +@@ -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({ @@ -251,13 +251,6 @@ index 5bf2a88..abfa7d7 100644 // 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); -- } - -- // Store the updated state as well as fix the path if incorrect -- history.replace({ -- path, -- state -- }); + await history.go(historyDeltaByKeys); + if (historyDeltaByKeys + 1 === historyDelta) { + history.push({ @@ -270,7 +263,13 @@ index 5bf2a88..abfa7d7 100644 + state + }); + } -+ } + } +- +- // Store the updated state as well as fix the path if incorrect +- history.replace({ +- path, +- state +- }); } catch (e) { // The navigation was interrupted }