-
Notifications
You must be signed in to change notification settings - Fork 3k
/
getPartialStateDiff.ts
86 lines (76 loc) · 5.02 KB
/
getPartialStateDiff.ts
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
import getTopmostBottomTabRoute from '@libs/Navigation/getTopmostBottomTabRoute';
import getTopmostCentralPaneRoute from '@libs/Navigation/getTopmostCentralPaneRoute';
import getTopmostFullScreenRoute from '@libs/Navigation/getTopmostFullScreenRoute';
import type {Metainfo} from '@libs/Navigation/linkingConfig/getAdaptedStateFromPath';
import type {NavigationPartialRoute, RootStackParamList, State} from '@libs/Navigation/types';
import shallowCompare from '@libs/ObjectUtils';
import NAVIGATORS from '@src/NAVIGATORS';
type GetPartialStateDiffReturnType = {
[NAVIGATORS.BOTTOM_TAB_NAVIGATOR]?: NavigationPartialRoute;
[NAVIGATORS.CENTRAL_PANE_NAVIGATOR]?: NavigationPartialRoute;
[NAVIGATORS.FULL_SCREEN_NAVIGATOR]?: NavigationPartialRoute;
};
/**
* This function returns partial additive diff between the two states.
*
* Example: Let's start with state A on route /r/123. If the screen is wide we will have a HOME opened on bottom tab and REPORT on central pane.
* Now let's say we want to navigate to /workspace/345/profile. We will generate state B from this path.
* State B will have WORKSPACE_INITIAL on the bottom tab and WORKSPACE_PROFILE on the central pane.
* Now we will generate partial diff between state A and state B. The diff will tell us that we need to push WORKSPACE_INITIAL on the bottom tab and WORKSPACE_PROFILE on the central pane.
*
* Then we can generate actions from this diff and dispatch them to the linkTo function.
*
* It's named partial diff because we don't cover RHP and LHP navigators yet. In the future we can improve this function to handle all navigators to help us clean and simplify the linkTo function.
*
* The partial diff has information which bottom tab, central pane and full screen screens we need to push to go from state to templateState.
* @param state - Current state.
* @param templateState - Desired state generated with getAdaptedStateFromPath.
* @param metainfo - Additional info from getAdaptedStateFromPath function.
* @returns The screen options object
*/
function getPartialStateDiff(state: State<RootStackParamList>, templateState: State<RootStackParamList>, metainfo: Metainfo): GetPartialStateDiffReturnType {
const diff: GetPartialStateDiffReturnType = {};
// If it is mandatory we need to compare both central pane and bottom tab of states.
if (metainfo.isCentralPaneAndBottomTabMandatory) {
const stateTopmostBottomTab = getTopmostBottomTabRoute(state);
const templateStateTopmostBottomTab = getTopmostBottomTabRoute(templateState);
// Bottom tab navigator
if (stateTopmostBottomTab && templateStateTopmostBottomTab && stateTopmostBottomTab.name !== templateStateTopmostBottomTab.name) {
diff[NAVIGATORS.BOTTOM_TAB_NAVIGATOR] = templateStateTopmostBottomTab;
}
const stateTopmostCentralPane = getTopmostCentralPaneRoute(state);
const templateStateTopmostCentralPane = getTopmostCentralPaneRoute(templateState);
if (
// If the central pane is only in the template state, it's diff.
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
(!stateTopmostCentralPane && templateStateTopmostCentralPane) ||
(stateTopmostCentralPane &&
templateStateTopmostCentralPane &&
stateTopmostCentralPane.name !== templateStateTopmostCentralPane.name &&
!shallowCompare(stateTopmostCentralPane.params as Record<string, unknown> | undefined, templateStateTopmostCentralPane.params as Record<string, unknown> | undefined))
) {
// We need to wrap central pane routes in the central pane navigator.
diff[NAVIGATORS.CENTRAL_PANE_NAVIGATOR] = templateStateTopmostCentralPane;
}
}
// This one is heuristic and may need to be improved if we will be able to navigate from modal screen with full screen in background to another modal screen with full screen in background.
// For now this simple check is enough.
if (metainfo.isFullScreenNavigatorMandatory) {
const stateTopmostFullScreen = getTopmostFullScreenRoute(state);
const templateStateTopmostFullScreen = getTopmostFullScreenRoute(templateState);
const fullScreenDiff = templateState.routes.filter((route) => route.name === NAVIGATORS.FULL_SCREEN_NAVIGATOR).at(-1) as NavigationPartialRoute;
if (
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
(!stateTopmostFullScreen && templateStateTopmostFullScreen) ||
(stateTopmostFullScreen &&
templateStateTopmostFullScreen &&
stateTopmostFullScreen.name !== templateStateTopmostFullScreen.name &&
!shallowCompare(stateTopmostFullScreen.params as Record<string, unknown> | undefined, templateStateTopmostFullScreen.params as Record<string, unknown> | undefined))
) {
diff[NAVIGATORS.FULL_SCREEN_NAVIGATOR] = fullScreenDiff;
}
}
return diff;
}
export default getPartialStateDiff;
export type {GetPartialStateDiffReturnType};