@applicaster/zapp-react-native-utils 15.0.0-alpha.3752284136 → 15.0.0-alpha.3842304615
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/README.md +0 -6
- package/actionUtils/index.ts +7 -0
- package/actionsExecutor/ActionExecutorContext.tsx +83 -6
- package/adsUtils/__tests__/createVMAP.test.ts +419 -0
- package/analyticsUtils/analyticsMapper.ts +10 -2
- package/appDataUtils/__tests__/urlScheme.test.ts +678 -0
- package/appUtils/HooksManager/__tests__/__snapshots__/hooksManager.test.js.snap +0 -188
- package/appUtils/HooksManager/__tests__/hooksManager.test.js +16 -2
- package/appUtils/HooksManager/index.ts +35 -0
- package/appUtils/contextKeysManager/__tests__/getKeys/failure.test.ts +7 -2
- package/appUtils/contextKeysManager/__tests__/getKeys/success.test.ts +48 -0
- package/appUtils/contextKeysManager/contextResolver.ts +51 -22
- package/appUtils/contextKeysManager/index.ts +65 -10
- package/appUtils/focusManager/treeDataStructure/Tree/__tests__/Tree.test.js +46 -0
- package/appUtils/focusManager/treeDataStructure/Tree/index.js +18 -18
- package/appUtils/focusManagerAux/utils/index.ts +12 -6
- package/appUtils/focusManagerAux/utils/utils.ios.ts +6 -3
- package/appUtils/localizationsHelper.ts +4 -0
- package/appUtils/playerManager/index.ts +9 -0
- package/appUtils/playerManager/player.ts +1 -1
- package/appUtils/playerManager/playerNative.ts +2 -1
- package/appUtils/playerManager/usePlayer.tsx +5 -3
- package/cellUtils/__tests__/cellUtils.test.ts +39 -0
- package/cellUtils/index.ts +11 -1
- package/cloudEventsUtils/__tests__/index.test.ts +529 -0
- package/cloudEventsUtils/index.ts +65 -1
- package/componentsUtils/index.ts +8 -0
- package/configurationUtils/__tests__/imageSrcFromMediaItem.test.ts +38 -0
- package/configurationUtils/index.ts +17 -11
- package/dateUtils/__tests__/dayjs.test.ts +327 -0
- package/dateUtils/index.ts +2 -0
- package/enumUtils/__tests__/getEnumKeyByEnumValue.test.ts +207 -0
- package/errorUtils/__tests__/GeneralError.test.ts +97 -0
- package/errorUtils/__tests__/HttpStatusCode.test.ts +344 -0
- package/errorUtils/__tests__/MissingPluginError.test.ts +113 -0
- package/errorUtils/__tests__/NetworkError.test.ts +202 -0
- package/errorUtils/__tests__/getParsedResponse.test.ts +188 -0
- package/errorUtils/__tests__/invariant.test.ts +112 -0
- package/headersUtils/__tests__/headersUtils.test.js +11 -1
- package/headersUtils/index.ts +2 -1
- package/manifestUtils/_internals/__tests__/index.test.js +41 -0
- package/manifestUtils/_internals/index.js +33 -0
- package/manifestUtils/defaultManifestConfigurations/player.js +35 -16
- package/manifestUtils/fieldUtils/__tests__/fieldUtils.test.js +49 -0
- package/manifestUtils/fieldUtils/index.js +54 -0
- package/manifestUtils/index.js +2 -0
- package/manifestUtils/keys.js +228 -0
- package/manifestUtils/mobileAction/button/__tests__/mobileActionButton.test.js +168 -0
- package/manifestUtils/mobileAction/button/index.js +140 -0
- package/manifestUtils/mobileAction/container/__tests__/mobileActionButtonsContainer.test.js +102 -0
- package/manifestUtils/mobileAction/container/index.js +73 -0
- package/manifestUtils/mobileAction/groups/__tests__/buildMobileActionButtonGroups.test.js +127 -0
- package/manifestUtils/mobileAction/groups/defaults.js +76 -0
- package/manifestUtils/mobileAction/groups/index.js +80 -0
- package/manifestUtils/platformIsTV.js +13 -0
- package/navigationUtils/index.ts +15 -5
- package/numberUtils/__tests__/toNumber.test.ts +27 -0
- package/numberUtils/__tests__/toPositiveNumber.test.ts +193 -0
- package/numberUtils/index.ts +23 -1
- package/package.json +3 -3
- package/pluginUtils/index.ts +4 -5
- package/reactHooks/analytics/__tests__/useSendAnalyticsOnPress.test.ts +537 -0
- package/reactHooks/app/__tests__/useAppState.test.ts +1 -1
- package/reactHooks/autoscrolling/__tests__/useTrackCurrentAutoScrollingElement.test.ts +1 -1
- package/reactHooks/autoscrolling/__tests__/useTrackedView.test.tsx +1 -2
- package/reactHooks/casting/index.ts +1 -0
- package/reactHooks/casting/useIsCasting.tsx +57 -0
- package/reactHooks/cell-click/__tests__/index.test.js +1 -3
- package/reactHooks/cell-click/index.ts +2 -1
- package/reactHooks/configuration/__tests__/index.test.tsx +1 -1
- package/reactHooks/connection/__tests__/index.test.js +1 -1
- package/reactHooks/dev/__tests__/useReRenderLog.test.ts +188 -0
- package/reactHooks/device/useIsTablet.tsx +14 -19
- package/reactHooks/events/index.ts +20 -0
- package/reactHooks/feed/__tests__/useBatchLoading.test.tsx +32 -23
- package/reactHooks/feed/__tests__/useBuildPipesUrl.test.tsx +19 -19
- package/reactHooks/feed/__tests__/useEntryScreenId.test.tsx +1 -1
- package/reactHooks/feed/__tests__/useFeedLoader.test.tsx +42 -30
- package/reactHooks/feed/__tests__/useInflatedUrl.test.tsx +1 -1
- package/reactHooks/feed/index.ts +0 -2
- package/reactHooks/feed/useInflatedUrl.ts +1 -1
- package/reactHooks/hookModal/hooks/useHookModalScreenData.ts +12 -8
- package/reactHooks/index.ts +2 -0
- package/reactHooks/layout/__tests__/index.test.tsx +1 -1
- package/reactHooks/layout/__tests__/useLayoutVersion.test.tsx +1 -1
- package/reactHooks/navigation/__tests__/index.test.tsx +40 -9
- package/reactHooks/navigation/index.ts +15 -4
- package/reactHooks/navigation/useRoute.ts +3 -1
- package/reactHooks/player/__tests__/useAutoSeek._test.tsx +1 -1
- package/reactHooks/player/__tests__/useTapSeek._test.ts +1 -1
- package/reactHooks/resolvers/__tests__/useCellResolver.test.tsx +1 -1
- package/reactHooks/resolvers/__tests__/useComponentResolver.test.tsx +1 -1
- package/reactHooks/resolvers/useComponentResolver.ts +13 -3
- package/reactHooks/screen/__tests__/useCurrentScreenData.test.tsx +1 -1
- package/reactHooks/screen/__tests__/useCurrentScreenIsHook.test.ts +103 -0
- package/reactHooks/screen/__tests__/useCurrentScreenIsStartupHook.test.ts +94 -0
- package/reactHooks/screen/__tests__/useScreenBackgroundColor.test.tsx +1 -1
- package/reactHooks/screen/__tests__/useScreenData.test.tsx +1 -1
- package/reactHooks/screen/__tests__/useTargetScreenData.test.tsx +1 -1
- package/reactHooks/screen/index.ts +4 -0
- package/reactHooks/screen/useCurrentScreenIsHook.ts +9 -0
- package/reactHooks/screen/useCurrentScreenIsStartupHook.ts +8 -0
- package/reactHooks/state/__tests__/useComponentScreenState.test.ts +246 -0
- package/reactHooks/state/index.ts +2 -0
- package/reactHooks/state/useComponentScreenState.ts +45 -0
- package/reactHooks/state/useRefWithInitialValue.ts +10 -0
- package/reactHooks/ui/__tests__/useFadeOutWhenBlurred.test.ts +580 -0
- package/reactHooks/utils/__tests__/index.test.js +1 -1
- package/rectUtils/__tests__/index.test.ts +549 -0
- package/rectUtils/index.ts +2 -2
- package/refreshUtils/RefreshCoordinator/__tests__/refreshCoordinator.test.ts +206 -0
- package/refreshUtils/RefreshCoordinator/index.ts +245 -0
- package/refreshUtils/RefreshCoordinator/utils/__tests__/getDataRefreshConfig.test.ts +104 -0
- package/refreshUtils/RefreshCoordinator/utils/index.ts +29 -0
- package/screenPickerUtils/__tests__/index.test.ts +333 -0
- package/screenPickerUtils/index.ts +5 -0
- package/screenState/__tests__/index.test.ts +1 -1
- package/screenUtils/index.ts +3 -0
- package/services/storageServiceSync.web.ts +1 -1
- package/time/__tests__/BackgroundTimer.test.ts +156 -0
- package/time/__tests__/Timer.test.ts +236 -0
- package/typeGuards/__tests__/isString.test.ts +21 -0
- package/typeGuards/index.ts +4 -0
- package/utils/__tests__/clone.test.ts +158 -0
- package/utils/__tests__/path.test.ts +7 -0
- package/utils/clone.ts +7 -0
- package/utils/index.ts +2 -1
- package/reactHooks/componentsMap/index.ts +0 -55
- package/reactHooks/feed/__tests__/useFeedRefresh.test.tsx +0 -75
- package/reactHooks/feed/useFeedRefresh.tsx +0 -65
|
@@ -1,193 +1,5 @@
|
|
|
1
1
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
2
|
|
|
3
|
-
exports[`HooksManager when target route is playable executes the player hook 1`] = `
|
|
4
|
-
[
|
|
5
|
-
{
|
|
6
|
-
"callback": [Function],
|
|
7
|
-
"hookPlugin": Hook {
|
|
8
|
-
"configuration": {
|
|
9
|
-
"config_field": "bar",
|
|
10
|
-
},
|
|
11
|
-
"identifier": "headless_player_hook",
|
|
12
|
-
"lastHook": true,
|
|
13
|
-
"manager": {
|
|
14
|
-
"executeHook": [Function],
|
|
15
|
-
"handleHooks": [Function],
|
|
16
|
-
"presentScreenHook": [Function],
|
|
17
|
-
"runInBackground": [Function],
|
|
18
|
-
"subscriber": {
|
|
19
|
-
"handlers": {
|
|
20
|
-
"complete": [
|
|
21
|
-
[MockFunction] {
|
|
22
|
-
"calls": [
|
|
23
|
-
[
|
|
24
|
-
{
|
|
25
|
-
"callback": [Function],
|
|
26
|
-
"hookPlugin": [Circular],
|
|
27
|
-
"payload": {
|
|
28
|
-
"foo": "bar",
|
|
29
|
-
},
|
|
30
|
-
},
|
|
31
|
-
[Function],
|
|
32
|
-
],
|
|
33
|
-
],
|
|
34
|
-
"results": [
|
|
35
|
-
{
|
|
36
|
-
"type": "return",
|
|
37
|
-
"value": undefined,
|
|
38
|
-
},
|
|
39
|
-
],
|
|
40
|
-
},
|
|
41
|
-
],
|
|
42
|
-
"presentScreenHook": [
|
|
43
|
-
[MockFunction],
|
|
44
|
-
],
|
|
45
|
-
"success": [
|
|
46
|
-
[MockFunction] {
|
|
47
|
-
"calls": [
|
|
48
|
-
[Circular],
|
|
49
|
-
],
|
|
50
|
-
"results": [
|
|
51
|
-
{
|
|
52
|
-
"type": "return",
|
|
53
|
-
"value": undefined,
|
|
54
|
-
},
|
|
55
|
-
],
|
|
56
|
-
},
|
|
57
|
-
],
|
|
58
|
-
},
|
|
59
|
-
"invokeHandler": [Function],
|
|
60
|
-
"on": [Function],
|
|
61
|
-
"removeHandler": [Function],
|
|
62
|
-
},
|
|
63
|
-
},
|
|
64
|
-
"module": {
|
|
65
|
-
"hasPlayerHook": true,
|
|
66
|
-
"run": [MockFunction] {
|
|
67
|
-
"calls": [
|
|
68
|
-
[
|
|
69
|
-
{
|
|
70
|
-
"foo": "bar",
|
|
71
|
-
},
|
|
72
|
-
[Function],
|
|
73
|
-
{
|
|
74
|
-
"config_field": "bar",
|
|
75
|
-
},
|
|
76
|
-
],
|
|
77
|
-
],
|
|
78
|
-
"results": [
|
|
79
|
-
{
|
|
80
|
-
"type": "return",
|
|
81
|
-
"value": undefined,
|
|
82
|
-
},
|
|
83
|
-
],
|
|
84
|
-
},
|
|
85
|
-
},
|
|
86
|
-
"state": 7,
|
|
87
|
-
"weight": 1,
|
|
88
|
-
},
|
|
89
|
-
"payload": {
|
|
90
|
-
"foo": "bar",
|
|
91
|
-
},
|
|
92
|
-
},
|
|
93
|
-
[Function],
|
|
94
|
-
]
|
|
95
|
-
`;
|
|
96
|
-
|
|
97
|
-
exports[`HooksManager when target route is playable executes the player hook 2`] = `
|
|
98
|
-
[
|
|
99
|
-
{
|
|
100
|
-
"callback": [Function],
|
|
101
|
-
"hookPlugin": Hook {
|
|
102
|
-
"configuration": {
|
|
103
|
-
"config_field": "bar",
|
|
104
|
-
},
|
|
105
|
-
"identifier": "headless_player_hook",
|
|
106
|
-
"lastHook": true,
|
|
107
|
-
"manager": {
|
|
108
|
-
"executeHook": [Function],
|
|
109
|
-
"handleHooks": [Function],
|
|
110
|
-
"presentScreenHook": [Function],
|
|
111
|
-
"runInBackground": [Function],
|
|
112
|
-
"subscriber": {
|
|
113
|
-
"handlers": {
|
|
114
|
-
"complete": [
|
|
115
|
-
[MockFunction] {
|
|
116
|
-
"calls": [
|
|
117
|
-
[Circular],
|
|
118
|
-
],
|
|
119
|
-
"results": [
|
|
120
|
-
{
|
|
121
|
-
"type": "return",
|
|
122
|
-
"value": undefined,
|
|
123
|
-
},
|
|
124
|
-
],
|
|
125
|
-
},
|
|
126
|
-
],
|
|
127
|
-
"presentScreenHook": [
|
|
128
|
-
[MockFunction],
|
|
129
|
-
],
|
|
130
|
-
"success": [
|
|
131
|
-
[MockFunction] {
|
|
132
|
-
"calls": [
|
|
133
|
-
[
|
|
134
|
-
{
|
|
135
|
-
"callback": [Function],
|
|
136
|
-
"hookPlugin": [Circular],
|
|
137
|
-
"payload": {
|
|
138
|
-
"foo": "bar",
|
|
139
|
-
},
|
|
140
|
-
},
|
|
141
|
-
[Function],
|
|
142
|
-
],
|
|
143
|
-
],
|
|
144
|
-
"results": [
|
|
145
|
-
{
|
|
146
|
-
"type": "return",
|
|
147
|
-
"value": undefined,
|
|
148
|
-
},
|
|
149
|
-
],
|
|
150
|
-
},
|
|
151
|
-
],
|
|
152
|
-
},
|
|
153
|
-
"invokeHandler": [Function],
|
|
154
|
-
"on": [Function],
|
|
155
|
-
"removeHandler": [Function],
|
|
156
|
-
},
|
|
157
|
-
},
|
|
158
|
-
"module": {
|
|
159
|
-
"hasPlayerHook": true,
|
|
160
|
-
"run": [MockFunction] {
|
|
161
|
-
"calls": [
|
|
162
|
-
[
|
|
163
|
-
{
|
|
164
|
-
"foo": "bar",
|
|
165
|
-
},
|
|
166
|
-
[Function],
|
|
167
|
-
{
|
|
168
|
-
"config_field": "bar",
|
|
169
|
-
},
|
|
170
|
-
],
|
|
171
|
-
],
|
|
172
|
-
"results": [
|
|
173
|
-
{
|
|
174
|
-
"type": "return",
|
|
175
|
-
"value": undefined,
|
|
176
|
-
},
|
|
177
|
-
],
|
|
178
|
-
},
|
|
179
|
-
},
|
|
180
|
-
"state": 7,
|
|
181
|
-
"weight": 1,
|
|
182
|
-
},
|
|
183
|
-
"payload": {
|
|
184
|
-
"foo": "bar",
|
|
185
|
-
},
|
|
186
|
-
},
|
|
187
|
-
[Function],
|
|
188
|
-
]
|
|
189
|
-
`;
|
|
190
|
-
|
|
191
3
|
exports[`HooksManager when there are preload hooks hook with screen: executes 1`] = `
|
|
192
4
|
[
|
|
193
5
|
{
|
|
@@ -116,9 +116,23 @@ describe("HooksManager", () => {
|
|
|
116
116
|
headlessPlayerHook.configuration
|
|
117
117
|
);
|
|
118
118
|
|
|
119
|
-
expect(successHandler
|
|
119
|
+
expect(successHandler).toHaveBeenCalledTimes(1);
|
|
120
|
+
expect(successHandler.mock.calls[0][0].payload).toEqual(payload);
|
|
120
121
|
|
|
121
|
-
expect(
|
|
122
|
+
expect(successHandler.mock.calls[0][0].hookPlugin.identifier).toBe(
|
|
123
|
+
"headless_player_hook"
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
expect(typeof successHandler.mock.calls[0][1]).toBe("function");
|
|
127
|
+
|
|
128
|
+
expect(completeHandler).toHaveBeenCalledTimes(1);
|
|
129
|
+
expect(completeHandler.mock.calls[0][0].payload).toEqual(payload);
|
|
130
|
+
|
|
131
|
+
expect(completeHandler.mock.calls[0][0].hookPlugin.identifier).toBe(
|
|
132
|
+
"headless_player_hook"
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
expect(typeof completeHandler.mock.calls[0][1]).toBe("function");
|
|
122
136
|
});
|
|
123
137
|
});
|
|
124
138
|
|
|
@@ -11,6 +11,10 @@ import { HOOKS_EVENTS, HOOKS_TYPE } from "./constants";
|
|
|
11
11
|
|
|
12
12
|
import { hooksManagerLogger } from "./logger";
|
|
13
13
|
import { HookManager, HookManagerArgs } from "./types";
|
|
14
|
+
import {
|
|
15
|
+
actionExecutor,
|
|
16
|
+
ActionResult,
|
|
17
|
+
} from "../../actionsExecutor/ActionExecutor";
|
|
14
18
|
|
|
15
19
|
/**
|
|
16
20
|
* orders the hooks according to their weight
|
|
@@ -280,6 +284,8 @@ export function HooksManager({
|
|
|
280
284
|
{}
|
|
281
285
|
);
|
|
282
286
|
|
|
287
|
+
actionExecutor.unregisterAction("finishHook");
|
|
288
|
+
|
|
283
289
|
return;
|
|
284
290
|
}
|
|
285
291
|
|
|
@@ -294,6 +300,8 @@ export function HooksManager({
|
|
|
294
300
|
}
|
|
295
301
|
);
|
|
296
302
|
|
|
303
|
+
actionExecutor.unregisterAction("finishHook");
|
|
304
|
+
|
|
297
305
|
return hookPlugin.setStateAndNotify(HOOKS_EVENTS.ERROR, {
|
|
298
306
|
error,
|
|
299
307
|
hookPlugin,
|
|
@@ -316,6 +324,8 @@ export function HooksManager({
|
|
|
316
324
|
// TODO: Temporary hack to pass getLoginProtocol to other plugins to refresh in case token expired, need be deleted later
|
|
317
325
|
delete payload.getLoginProtocol;
|
|
318
326
|
|
|
327
|
+
actionExecutor.unregisterAction("finishHook");
|
|
328
|
+
|
|
319
329
|
hookPlugin.setStateAndNotify(HOOKS_EVENTS.CANCEL, {
|
|
320
330
|
hookPlugin,
|
|
321
331
|
payload,
|
|
@@ -381,6 +391,27 @@ export function HooksManager({
|
|
|
381
391
|
};
|
|
382
392
|
}
|
|
383
393
|
|
|
394
|
+
function registerFinishHookAction(payload, callback) {
|
|
395
|
+
// Ensure no stale finishHook remains (e.g. presentUI re-entry from runInBackground)
|
|
396
|
+
actionExecutor.unregisterAction("finishHook");
|
|
397
|
+
|
|
398
|
+
actionExecutor.registerAction("finishHook", async (action: ActionType) => {
|
|
399
|
+
const { success, errorMessage, abort } = action.options;
|
|
400
|
+
|
|
401
|
+
actionExecutor.unregisterAction("finishHook");
|
|
402
|
+
|
|
403
|
+
if (errorMessage) {
|
|
404
|
+
callback({ success, error: new Error(errorMessage), payload, abort });
|
|
405
|
+
} else {
|
|
406
|
+
callback({ success, error: null, payload, abort });
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
hooksManagerLogger.info("finishHook action executed, finishing flow");
|
|
410
|
+
|
|
411
|
+
return ActionResult.Success;
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
|
|
384
415
|
/**
|
|
385
416
|
* presents a screen hook by triggering an event invoking the handler with
|
|
386
417
|
* the appropriate route & payload
|
|
@@ -408,6 +439,8 @@ export function HooksManager({
|
|
|
408
439
|
}
|
|
409
440
|
);
|
|
410
441
|
|
|
442
|
+
registerFinishHookAction(payload, callback);
|
|
443
|
+
|
|
411
444
|
hookPlugin.setStateAndNotify(HOOKS_EVENTS.PRESENT_SCREEN_HOOK, {
|
|
412
445
|
hookPlugin,
|
|
413
446
|
route: targetScreenRoute,
|
|
@@ -467,6 +500,8 @@ export function HooksManager({
|
|
|
467
500
|
}
|
|
468
501
|
);
|
|
469
502
|
|
|
503
|
+
registerFinishHookAction(payload, callback);
|
|
504
|
+
|
|
470
505
|
hookPlugin.module.runInBackground(
|
|
471
506
|
payload,
|
|
472
507
|
callback,
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
+
import { NativeModules } from "react-native";
|
|
1
2
|
import { ContextKeysManager } from "../..";
|
|
2
3
|
|
|
3
4
|
describe("Context Keys Manager - getKeys", () => {
|
|
4
|
-
|
|
5
|
+
beforeEach(() => {
|
|
6
|
+
delete NativeModules.ContextResolverBridge;
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it("returns null if all keys are invalid 1", async () => {
|
|
5
10
|
// setup
|
|
6
11
|
const keys = [null];
|
|
7
12
|
|
|
@@ -24,7 +29,7 @@ describe("Context Keys Manager - getKeys", () => {
|
|
|
24
29
|
expect(getKey.mock.calls).toEqual([[null]]);
|
|
25
30
|
});
|
|
26
31
|
|
|
27
|
-
it("returns null if all keys are invalid", async () => {
|
|
32
|
+
it("returns null if all keys are invalid 2", async () => {
|
|
28
33
|
// setup
|
|
29
34
|
const keys = [null, undefined];
|
|
30
35
|
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
+
import { NativeModules } from "react-native";
|
|
1
2
|
import { ContextKeysManager } from "../..";
|
|
2
3
|
|
|
3
4
|
describe("Context Keys Manager - getKeys", () => {
|
|
5
|
+
beforeEach(() => {
|
|
6
|
+
delete NativeModules.ContextResolverBridge;
|
|
7
|
+
});
|
|
8
|
+
|
|
4
9
|
it("returns value if found by getKey", async () => {
|
|
5
10
|
// setup
|
|
6
11
|
const keys = ["namespace.key"];
|
|
@@ -165,4 +170,47 @@ describe("Context Keys Manager - getKeys", () => {
|
|
|
165
170
|
expect(getKey).toHaveBeenCalledTimes(2);
|
|
166
171
|
expect(getKey.mock.calls).toEqual([[keys[0]], [keys[1]]]);
|
|
167
172
|
});
|
|
173
|
+
|
|
174
|
+
it("returns values from native bridge when available", async () => {
|
|
175
|
+
// setup
|
|
176
|
+
const keys = ["namespace.key1", "namespace.key2"];
|
|
177
|
+
|
|
178
|
+
const contextObj = [
|
|
179
|
+
{ key: keys[0], required: true },
|
|
180
|
+
{ key: keys[1], required: false },
|
|
181
|
+
];
|
|
182
|
+
|
|
183
|
+
const resolved = {
|
|
184
|
+
[keys[0]]: "value1",
|
|
185
|
+
[keys[1]]: "value2",
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
const reactNative = require("react-native");
|
|
189
|
+
|
|
190
|
+
const bridgeMock = {
|
|
191
|
+
resolveContextKeys: jest.fn().mockResolvedValue(resolved),
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
reactNative.__setContextResolverBridgeMock(bridgeMock);
|
|
195
|
+
|
|
196
|
+
const contextManager = new ContextKeysManager({});
|
|
197
|
+
const getKey = jest.spyOn(contextManager, "getKey");
|
|
198
|
+
|
|
199
|
+
// run
|
|
200
|
+
const result = await contextManager.getKeys(keys, contextObj);
|
|
201
|
+
|
|
202
|
+
// verify
|
|
203
|
+
const map = new Map();
|
|
204
|
+
map.set(keys[0], "value1");
|
|
205
|
+
map.set(keys[1], "value2");
|
|
206
|
+
|
|
207
|
+
expect(result).toEqual(map);
|
|
208
|
+
|
|
209
|
+
expect(bridgeMock.resolveContextKeys).toHaveBeenCalledWith({
|
|
210
|
+
[keys[0]]: true,
|
|
211
|
+
[keys[1]]: false,
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
expect(getKey).not.toHaveBeenCalled();
|
|
215
|
+
});
|
|
168
216
|
});
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { ContextKeysManager } from "./index";
|
|
2
|
-
import
|
|
3
|
-
import * as _ from "lodash";
|
|
2
|
+
import { get, values } from "@applicaster/zapp-react-native-utils/utils";
|
|
4
3
|
import { useScreenStateStore } from "../../reactHooks/navigation/useScreenStateStore";
|
|
5
4
|
|
|
5
|
+
const contextKeyPattern = /@{([^/]+)\/([^}]*)}/;
|
|
6
|
+
const contextVariablePattern = /@\{([^}]*)\}/g;
|
|
7
|
+
|
|
6
8
|
export interface IResolver {
|
|
7
9
|
resolve: (string) => Promise<string | number | object>;
|
|
8
10
|
}
|
|
@@ -20,7 +22,7 @@ export class EntryResolver implements IResolver {
|
|
|
20
22
|
return this.entry;
|
|
21
23
|
}
|
|
22
24
|
|
|
23
|
-
return
|
|
25
|
+
return get(this.entry, key.split("."));
|
|
24
26
|
}
|
|
25
27
|
}
|
|
26
28
|
|
|
@@ -39,7 +41,7 @@ export class ScreenStateResolver implements IResolver {
|
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
if (key.includes(".")) {
|
|
42
|
-
return
|
|
44
|
+
return get(screenState, key.split("."));
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
return screenState?.[key];
|
|
@@ -61,21 +63,16 @@ export const resolveObjectValues = async (
|
|
|
61
63
|
|
|
62
64
|
const resolvedEntries = await Promise.all(
|
|
63
65
|
entries.map(async ([key, value]) => {
|
|
64
|
-
if (typeof value !== "string") {
|
|
66
|
+
if (typeof value !== "string" || !value.startsWith("@")) {
|
|
65
67
|
return [key, value];
|
|
66
68
|
}
|
|
67
69
|
|
|
68
|
-
|
|
69
|
-
return [key, value];
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const regex = /@{([^/]+)\/([^}]*)}/;
|
|
70
|
+
const regex = contextKeyPattern;
|
|
73
71
|
|
|
74
72
|
const match = (value as string).match(regex);
|
|
75
73
|
|
|
76
74
|
if (match) {
|
|
77
|
-
const contextResolverName = match
|
|
78
|
-
const compositeKey = match[2];
|
|
75
|
+
const [_, contextResolverName, compositeKey] = match;
|
|
79
76
|
|
|
80
77
|
const resolver = exResolvers[contextResolverName] || exResolvers.ctx;
|
|
81
78
|
const resolvedValue = await resolver.resolve(compositeKey);
|
|
@@ -90,18 +87,50 @@ export const resolveObjectValues = async (
|
|
|
90
87
|
return Object.fromEntries(resolvedEntries);
|
|
91
88
|
};
|
|
92
89
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
90
|
+
// Simple memoization cache
|
|
91
|
+
const extractAtValuesCache = new Map<any, string[]>();
|
|
92
|
+
|
|
93
|
+
const flatMapDeep = <T, U>(arr: T[], fn: (value: T) => U | U[]): U[] => {
|
|
94
|
+
const result: U[] = [];
|
|
97
95
|
|
|
98
|
-
|
|
96
|
+
const flatten = (items: any) => {
|
|
97
|
+
for (const item of items) {
|
|
98
|
+
if (Array.isArray(item)) {
|
|
99
|
+
flatten(item);
|
|
100
|
+
} else {
|
|
101
|
+
result.push(item);
|
|
102
|
+
}
|
|
99
103
|
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
flatten(arr.map(fn));
|
|
100
107
|
|
|
101
|
-
|
|
102
|
-
|
|
108
|
+
return result;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
export const extractAtValues = (input: any): string[] => {
|
|
112
|
+
if (extractAtValuesCache.has(input)) {
|
|
113
|
+
return extractAtValuesCache.get(input)!;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const result = flatMapDeep(
|
|
117
|
+
Array.isArray(input) ? input : [input],
|
|
118
|
+
(value: any) => {
|
|
119
|
+
if (typeof value === "string") {
|
|
120
|
+
const matches = value.match(contextVariablePattern);
|
|
121
|
+
|
|
122
|
+
return matches ? matches.map((match) => match.slice(2, -1)) : [];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (typeof value === "object" && value !== null) {
|
|
126
|
+
return extractAtValues(values(value));
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return [];
|
|
103
130
|
}
|
|
131
|
+
);
|
|
104
132
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
133
|
+
extractAtValuesCache.set(input, result);
|
|
134
|
+
|
|
135
|
+
return result;
|
|
136
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { isNil } from "@applicaster/zapp-react-native-utils/utils";
|
|
2
2
|
|
|
3
3
|
import { StorageLevel, StorageType } from "./consts";
|
|
4
4
|
import {
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
localStorage as LocalStorage,
|
|
14
14
|
secureStorage as SecureStorage,
|
|
15
15
|
} from "./storage";
|
|
16
|
+
import { NativeModules } from "react-native";
|
|
16
17
|
|
|
17
18
|
export { StorageLevel };
|
|
18
19
|
|
|
@@ -154,11 +155,65 @@ export class ContextKeysManager {
|
|
|
154
155
|
}
|
|
155
156
|
|
|
156
157
|
public async getKeys(
|
|
157
|
-
keys: Array<KeyName
|
|
158
|
+
keys: Array<KeyName>,
|
|
159
|
+
contextObj?: { key: string; required?: boolean }[]
|
|
158
160
|
): Promise<Map<KeyName, ValueOrNothing>> {
|
|
159
|
-
|
|
161
|
+
if (NativeModules.ContextResolverBridge) {
|
|
162
|
+
const keysObj: Record<string, boolean> = {};
|
|
160
163
|
|
|
161
|
-
|
|
164
|
+
const requiredByKey = new Map(
|
|
165
|
+
contextObj?.map((item) => [item.key, item.required ?? false]) ?? []
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
let hasResolvableKeys = false;
|
|
169
|
+
|
|
170
|
+
const normalizedKeys: Array<{
|
|
171
|
+
original: KeyName;
|
|
172
|
+
normalized: string | null;
|
|
173
|
+
}> = [];
|
|
174
|
+
|
|
175
|
+
for (const key of keys) {
|
|
176
|
+
const {
|
|
177
|
+
isValid: isValidKey,
|
|
178
|
+
key: parsedKey,
|
|
179
|
+
namespace: parsedNamespace,
|
|
180
|
+
} = this.parseKey(key);
|
|
181
|
+
|
|
182
|
+
if (!isValidKey) {
|
|
183
|
+
normalizedKeys.push({ original: key, normalized: null });
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const normalized = buildNamespaceKey(parsedKey, parsedNamespace);
|
|
188
|
+
|
|
189
|
+
keysObj[normalized] = requiredByKey.get(normalized) ?? false;
|
|
190
|
+
hasResolvableKeys = true;
|
|
191
|
+
normalizedKeys.push({ original: key, normalized });
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (!hasResolvableKeys) {
|
|
195
|
+
return new Map(
|
|
196
|
+
normalizedKeys.map(({ original }) => [original, NOTHING])
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const res =
|
|
201
|
+
await NativeModules.ContextResolverBridge.resolveContextKeys(keysObj);
|
|
202
|
+
|
|
203
|
+
const resolved = res ?? {};
|
|
204
|
+
|
|
205
|
+
return new Map(
|
|
206
|
+
normalizedKeys.map(({ original, normalized }) => {
|
|
207
|
+
if (!normalized) return [original, NOTHING];
|
|
208
|
+
|
|
209
|
+
return [original, resolved[normalized] ?? NOTHING];
|
|
210
|
+
})
|
|
211
|
+
);
|
|
212
|
+
} else {
|
|
213
|
+
const values = await Promise.all(keys.map((key) => this.getKey(key)));
|
|
214
|
+
|
|
215
|
+
return new Map(keys.map((key, index) => [key, values[index]]));
|
|
216
|
+
}
|
|
162
217
|
}
|
|
163
218
|
|
|
164
219
|
public async getKey(key: KeyName): Promise<ValueOrNothing> {
|
|
@@ -181,7 +236,7 @@ export class ContextKeysManager {
|
|
|
181
236
|
parsedNamespace
|
|
182
237
|
);
|
|
183
238
|
|
|
184
|
-
if (!
|
|
239
|
+
if (!isNil(resultByReference)) {
|
|
185
240
|
return resultByReference;
|
|
186
241
|
}
|
|
187
242
|
|
|
@@ -192,7 +247,7 @@ export class ContextKeysManager {
|
|
|
192
247
|
parsedNamespace
|
|
193
248
|
);
|
|
194
249
|
|
|
195
|
-
if (!
|
|
250
|
+
if (!isNil(resultFromSessionStorage)) {
|
|
196
251
|
return resultFromSessionStorage;
|
|
197
252
|
}
|
|
198
253
|
|
|
@@ -201,7 +256,7 @@ export class ContextKeysManager {
|
|
|
201
256
|
parsedNamespace
|
|
202
257
|
);
|
|
203
258
|
|
|
204
|
-
if (!
|
|
259
|
+
if (!isNil(resultFromLocalStorage)) {
|
|
205
260
|
return resultFromLocalStorage;
|
|
206
261
|
}
|
|
207
262
|
|
|
@@ -210,7 +265,7 @@ export class ContextKeysManager {
|
|
|
210
265
|
parsedNamespace
|
|
211
266
|
);
|
|
212
267
|
|
|
213
|
-
if (!
|
|
268
|
+
if (!isNil(resultFromSecureStorage)) {
|
|
214
269
|
return resultFromSecureStorage;
|
|
215
270
|
}
|
|
216
271
|
|
|
@@ -275,7 +330,7 @@ export class ContextKeysManager {
|
|
|
275
330
|
keysProp.map((keyProp) => this.setKey(keyProp))
|
|
276
331
|
);
|
|
277
332
|
|
|
278
|
-
return new Map(
|
|
333
|
+
return new Map(keys.map((key, index) => [key, values[index]]));
|
|
279
334
|
}
|
|
280
335
|
|
|
281
336
|
// when succeed saving - return true
|
|
@@ -384,7 +439,7 @@ export class ContextKeysManager {
|
|
|
384
439
|
public async removeKeys(keys: KeyName[]): Promise<Map<KeyName, boolean>> {
|
|
385
440
|
const values = await Promise.all(keys.map((key) => this.removeKey(key)));
|
|
386
441
|
|
|
387
|
-
return new Map(
|
|
442
|
+
return new Map(keys.map((key, index) => [key, values[index]]));
|
|
388
443
|
}
|
|
389
444
|
// REMOVE
|
|
390
445
|
}
|
|
@@ -373,3 +373,49 @@ describe("addNode", () => {
|
|
|
373
373
|
checkParents(tree.root);
|
|
374
374
|
});
|
|
375
375
|
});
|
|
376
|
+
|
|
377
|
+
describe("findInTree", () => {
|
|
378
|
+
function createNode(id, children) {
|
|
379
|
+
return {
|
|
380
|
+
id,
|
|
381
|
+
children,
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
it("returns a direct child match from root children", () => {
|
|
386
|
+
const tree = new Tree(treeLoaded);
|
|
387
|
+
const direct = createNode("direct-node");
|
|
388
|
+
|
|
389
|
+
tree.root.children = [direct];
|
|
390
|
+
|
|
391
|
+
expect(tree.findInTree("direct-node")).toEqual(direct);
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
it("returns a nested descendant match", () => {
|
|
395
|
+
const tree = new Tree(treeLoaded);
|
|
396
|
+
const nested = createNode("nested-node");
|
|
397
|
+
const intermediate = createNode("intermediate-node", [nested]);
|
|
398
|
+
const rootNode = createNode("root-node", [intermediate]);
|
|
399
|
+
|
|
400
|
+
tree.root.children = [rootNode];
|
|
401
|
+
|
|
402
|
+
expect(tree.findInTree("nested-node")).toEqual(nested);
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
it("returns null when node id does not exist", () => {
|
|
406
|
+
const tree = new Tree(treeLoaded);
|
|
407
|
+
const leaf = createNode("leaf-node");
|
|
408
|
+
const rootNode = createNode("root-node", [leaf]);
|
|
409
|
+
|
|
410
|
+
tree.root.children = [rootNode];
|
|
411
|
+
|
|
412
|
+
expect(tree.findInTree("missing-node")).toEqual(null);
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
it("returns null when tree has no children", () => {
|
|
416
|
+
const tree = new Tree(treeLoaded);
|
|
417
|
+
tree.root.children = null;
|
|
418
|
+
|
|
419
|
+
expect(tree.findInTree("any-node")).toEqual(null);
|
|
420
|
+
});
|
|
421
|
+
});
|