@applicaster/zapp-react-native-utils 15.0.0-alpha.4153840309 → 15.0.0-alpha.4200413542

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.
Files changed (125) hide show
  1. package/actionsExecutor/ActionExecutorContext.tsx +3 -6
  2. package/actionsExecutor/feedDecorator.ts +6 -6
  3. package/adsUtils/__tests__/createVMAP.test.ts +419 -0
  4. package/adsUtils/index.ts +2 -2
  5. package/analyticsUtils/README.md +1 -1
  6. package/analyticsUtils/analyticsMapper.ts +10 -2
  7. package/appDataUtils/__tests__/urlScheme.test.ts +678 -0
  8. package/appUtils/HooksManager/__tests__/__snapshots__/hooksManager.test.js.snap +0 -188
  9. package/appUtils/HooksManager/__tests__/hooksManager.test.js +16 -2
  10. package/appUtils/HooksManager/index.ts +10 -10
  11. package/appUtils/RiverFocusManager/{index.js → index.ts} +25 -18
  12. package/appUtils/accessibilityManager/__tests__/utils.test.ts +360 -0
  13. package/appUtils/accessibilityManager/const.ts +4 -0
  14. package/appUtils/accessibilityManager/utils.ts +25 -5
  15. package/appUtils/contextKeysManager/__tests__/getKeys/failure.test.ts +7 -2
  16. package/appUtils/contextKeysManager/__tests__/getKeys/success.test.ts +48 -0
  17. package/appUtils/contextKeysManager/contextResolver.ts +51 -22
  18. package/appUtils/contextKeysManager/index.ts +65 -10
  19. package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +3 -0
  20. package/appUtils/focusManager/index.ios.ts +43 -4
  21. package/appUtils/focusManagerAux/utils/index.ios.ts +122 -0
  22. package/appUtils/focusManagerAux/utils/index.ts +1 -1
  23. package/appUtils/focusManagerAux/utils/utils.ios.ts +199 -3
  24. package/appUtils/keyCodes/keys/keys.web.ts +1 -4
  25. package/appUtils/orientationHelper.ts +2 -4
  26. package/appUtils/platform/platformUtils.ts +1 -1
  27. package/appUtils/playerManager/player.ts +4 -0
  28. package/appUtils/playerManager/playerNative.ts +29 -16
  29. package/appUtils/playerManager/usePlayerState.tsx +14 -2
  30. package/cloudEventsUtils/__tests__/index.test.ts +529 -0
  31. package/cloudEventsUtils/index.ts +65 -1
  32. package/configurationUtils/__tests__/imageSrcFromMediaItem.test.ts +38 -0
  33. package/configurationUtils/__tests__/manifestKeyParser.test.ts +26 -26
  34. package/configurationUtils/index.ts +17 -11
  35. package/dateUtils/__tests__/dayjs.test.ts +330 -0
  36. package/enumUtils/__tests__/getEnumKeyByEnumValue.test.ts +207 -0
  37. package/errorUtils/__tests__/GeneralError.test.ts +97 -0
  38. package/errorUtils/__tests__/HttpStatusCode.test.ts +344 -0
  39. package/errorUtils/__tests__/MissingPluginError.test.ts +113 -0
  40. package/errorUtils/__tests__/NetworkError.test.ts +202 -0
  41. package/errorUtils/__tests__/getParsedResponse.test.ts +188 -0
  42. package/errorUtils/__tests__/invariant.test.ts +112 -0
  43. package/focusManager/aux/index.ts +1 -1
  44. package/headersUtils/__tests__/headersUtils.test.js +11 -1
  45. package/headersUtils/index.ts +2 -1
  46. package/manifestUtils/defaultManifestConfigurations/player.js +125 -11
  47. package/manifestUtils/platformIsTV.js +13 -0
  48. package/manifestUtils/tvAction/container/index.js +1 -1
  49. package/navigationUtils/index.ts +15 -5
  50. package/numberUtils/__tests__/toNumber.test.ts +12 -0
  51. package/numberUtils/__tests__/toPositiveNumber.test.ts +165 -0
  52. package/numberUtils/index.ts +19 -1
  53. package/package.json +4 -4
  54. package/pluginUtils/index.ts +4 -0
  55. package/reactHooks/advertising/index.ts +2 -2
  56. package/reactHooks/analytics/__tests__/useSendAnalyticsOnPress.test.ts +537 -0
  57. package/reactHooks/app/__tests__/useAppState.test.ts +1 -1
  58. package/reactHooks/autoscrolling/__tests__/useTrackCurrentAutoScrollingElement.test.ts +1 -1
  59. package/reactHooks/autoscrolling/__tests__/useTrackedView.test.tsx +1 -2
  60. package/reactHooks/cell-click/__tests__/index.test.js +1 -3
  61. package/reactHooks/configuration/__tests__/index.test.tsx +1 -1
  62. package/reactHooks/connection/__tests__/index.test.js +1 -1
  63. package/reactHooks/debugging/__tests__/index.test.js +4 -4
  64. package/reactHooks/dev/__tests__/useReRenderLog.test.ts +188 -0
  65. package/reactHooks/device/useIsTablet.tsx +14 -19
  66. package/reactHooks/device/useMemoizedIsTablet.ts +3 -3
  67. package/reactHooks/events/index.ts +20 -0
  68. package/reactHooks/feed/__tests__/useBatchLoading.test.tsx +32 -23
  69. package/reactHooks/feed/__tests__/useBuildPipesUrl.test.tsx +19 -19
  70. package/reactHooks/feed/__tests__/useEntryScreenId.test.tsx +4 -1
  71. package/reactHooks/feed/__tests__/useFeedLoader.test.tsx +42 -30
  72. package/reactHooks/feed/__tests__/useFeedRefresh.test.tsx +1 -1
  73. package/reactHooks/feed/__tests__/{useInflatedUrl.test.ts → useInflatedUrl.test.tsx} +62 -7
  74. package/reactHooks/feed/useEntryScreenId.ts +2 -2
  75. package/reactHooks/feed/useInflatedUrl.ts +43 -17
  76. package/reactHooks/flatList/useLoadNextPageIfNeeded.ts +13 -16
  77. package/reactHooks/hookModal/hooks/useHookModalScreenData.ts +12 -8
  78. package/reactHooks/index.ts +2 -0
  79. package/reactHooks/layout/__tests__/index.test.tsx +1 -1
  80. package/reactHooks/layout/__tests__/useLayoutVersion.test.tsx +1 -1
  81. package/reactHooks/layout/useDimensions/__tests__/{useDimensions.test.ts → useDimensions.test.tsx} +105 -25
  82. package/reactHooks/layout/useDimensions/useDimensions.ts +2 -2
  83. package/reactHooks/navigation/__tests__/index.test.tsx +40 -9
  84. package/reactHooks/navigation/index.ts +27 -11
  85. package/reactHooks/navigation/useRoute.ts +11 -7
  86. package/reactHooks/player/TVSeekControlller/TVSeekController.ts +27 -10
  87. package/reactHooks/player/__tests__/useAutoSeek._test.tsx +1 -1
  88. package/reactHooks/player/__tests__/useTapSeek._test.ts +1 -1
  89. package/reactHooks/resolvers/__tests__/useCellResolver.test.tsx +1 -1
  90. package/reactHooks/resolvers/__tests__/useComponentResolver.test.tsx +1 -1
  91. package/reactHooks/resolvers/useCellResolver.ts +6 -2
  92. package/reactHooks/resolvers/useComponentResolver.ts +8 -2
  93. package/reactHooks/screen/__tests__/useCurrentScreenData.test.tsx +2 -2
  94. package/reactHooks/screen/__tests__/useScreenBackgroundColor.test.tsx +1 -1
  95. package/reactHooks/screen/__tests__/useScreenData.test.tsx +1 -1
  96. package/reactHooks/screen/__tests__/useTargetScreenData.test.tsx +12 -4
  97. package/reactHooks/screen/useTargetScreenData.ts +4 -2
  98. package/reactHooks/state/useRefWithInitialValue.ts +10 -0
  99. package/reactHooks/state/useRivers.ts +1 -1
  100. package/reactHooks/ui/__tests__/useFadeOutWhenBlurred.test.ts +580 -0
  101. package/reactHooks/usePluginConfiguration.ts +2 -2
  102. package/reactHooks/utils/__tests__/index.test.js +1 -1
  103. package/rectUtils/__tests__/index.test.ts +549 -0
  104. package/rectUtils/index.ts +2 -2
  105. package/screenPickerUtils/__tests__/index.test.ts +333 -0
  106. package/screenState/__tests__/index.test.ts +1 -1
  107. package/searchUtils/const.ts +7 -0
  108. package/searchUtils/index.ts +3 -0
  109. package/services/storageServiceSync.web.ts +1 -1
  110. package/stringUtils/index.ts +1 -1
  111. package/testUtils/index.tsx +30 -21
  112. package/time/__tests__/BackgroundTimer.test.ts +156 -0
  113. package/time/__tests__/Timer.test.ts +236 -0
  114. package/typeGuards/__tests__/isString.test.ts +21 -0
  115. package/typeGuards/index.ts +4 -0
  116. package/utils/__tests__/mergeRight.test.ts +48 -0
  117. package/utils/__tests__/selectors.test.ts +124 -0
  118. package/utils/index.ts +13 -0
  119. package/utils/mergeRight.ts +5 -0
  120. package/utils/path.ts +6 -3
  121. package/utils/pathOr.ts +5 -1
  122. package/utils/selectors.ts +46 -0
  123. package/zappFrameworkUtils/HookCallback/callbackNavigationAction.ts +34 -11
  124. package/zappFrameworkUtils/HookCallback/hookCallbackManifestExtensions.config.js +1 -1
  125. package/reactHooks/componentsMap/index.ts +0 -55
@@ -0,0 +1,236 @@
1
+ import { Timer } from "../Timer";
2
+ import BackgroundTimer from "../BackgroundTimer";
3
+
4
+ // Mock BackgroundTimer
5
+ jest.mock("../BackgroundTimer", () => ({
6
+ __esModule: true,
7
+ default: {
8
+ setTimeout: jest.fn((callback, delay) => {
9
+ return setTimeout(callback, delay);
10
+ }),
11
+ clearTimeout: jest.fn((id) => {
12
+ clearTimeout(id as NodeJS.Timeout);
13
+ }),
14
+ },
15
+ }));
16
+
17
+ describe("Timer", () => {
18
+ let mockCallback: jest.Mock;
19
+
20
+ beforeEach(() => {
21
+ jest.clearAllMocks();
22
+ jest.useFakeTimers();
23
+ mockCallback = jest.fn();
24
+ });
25
+
26
+ afterEach(() => {
27
+ jest.useRealTimers();
28
+ });
29
+
30
+ describe("constructor", () => {
31
+ it("should create timer with callback and remaining time", () => {
32
+ const timer = new Timer(mockCallback, 1000);
33
+
34
+ expect(timer).toBeInstanceOf(Timer);
35
+ });
36
+
37
+ it("should create timer with callback only (optional remaining param)", () => {
38
+ const timer = new Timer(mockCallback);
39
+
40
+ expect(timer).toBeInstanceOf(Timer);
41
+ });
42
+
43
+ it("should not start automatically", () => {
44
+ // eslint-disable-next-line no-new
45
+ new Timer(mockCallback, 1000);
46
+
47
+ expect(BackgroundTimer.setTimeout).not.toHaveBeenCalled();
48
+ });
49
+ });
50
+
51
+ describe("start", () => {
52
+ it("should call setTimeout with callback and remaining time", () => {
53
+ const timer = new Timer(mockCallback, 1000);
54
+
55
+ timer.start();
56
+
57
+ expect(BackgroundTimer.setTimeout).toHaveBeenCalledWith(
58
+ mockCallback,
59
+ 1000
60
+ );
61
+ });
62
+
63
+ it("should reset remaining time to 0 after starting", () => {
64
+ const timer = new Timer(mockCallback, 5000);
65
+
66
+ timer.start();
67
+ timer.clear();
68
+ timer.start();
69
+
70
+ expect(BackgroundTimer.setTimeout).toHaveBeenCalledTimes(2);
71
+ });
72
+ });
73
+
74
+ describe("pause", () => {
75
+ it("should clear timeout and calculate remaining time correctly", () => {
76
+ const startTime = 1000;
77
+ const pauseTime = 1500;
78
+
79
+ jest
80
+ .spyOn(Date, "now")
81
+ .mockReturnValueOnce(startTime) // start
82
+ .mockReturnValueOnce(pauseTime); // pause
83
+
84
+ const timer = new Timer(mockCallback, 2000);
85
+
86
+ timer.start();
87
+ timer.pause();
88
+
89
+ expect(BackgroundTimer.clearTimeout).toHaveBeenCalled();
90
+ });
91
+
92
+ it("should be safe to call pause multiple times", () => {
93
+ const timer = new Timer(mockCallback, 1000);
94
+
95
+ timer.start();
96
+ timer.pause();
97
+ timer.pause();
98
+
99
+ expect(BackgroundTimer.clearTimeout).toHaveBeenCalledTimes(2);
100
+ });
101
+ });
102
+
103
+ describe("resume", () => {
104
+ it("should restart timer with remaining time after pause", () => {
105
+ const startTime = 1000;
106
+ const pauseTime = 1500;
107
+
108
+ jest
109
+ .spyOn(Date, "now")
110
+ .mockReturnValueOnce(startTime)
111
+ .mockReturnValueOnce(pauseTime);
112
+
113
+ const timer = new Timer(mockCallback, 2000);
114
+
115
+ timer.start();
116
+ timer.pause();
117
+ timer.resume();
118
+
119
+ expect(BackgroundTimer.setTimeout).toHaveBeenCalledTimes(2);
120
+ });
121
+
122
+ it("should be callable without prior pause", () => {
123
+ const timer = new Timer(mockCallback, 1000);
124
+
125
+ timer.resume();
126
+
127
+ expect(BackgroundTimer.setTimeout).toHaveBeenCalled();
128
+ });
129
+ });
130
+
131
+ describe("clear", () => {
132
+ it("should clear timeout and be safe to call without starting timer", () => {
133
+ const timer = new Timer(mockCallback, 1000);
134
+
135
+ expect(() => {
136
+ timer.clear();
137
+ }).not.toThrow();
138
+
139
+ expect(BackgroundTimer.clearTimeout).toHaveBeenCalled();
140
+ });
141
+
142
+ it("should be callable multiple times", () => {
143
+ const timer = new Timer(mockCallback, 1000);
144
+
145
+ timer.start();
146
+ timer.clear();
147
+ timer.clear();
148
+
149
+ expect(BackgroundTimer.clearTimeout).toHaveBeenCalledTimes(2);
150
+ });
151
+ });
152
+
153
+ describe("timer lifecycle", () => {
154
+ it("should support complete lifecycle: start -> pause -> resume -> clear", () => {
155
+ const timer = new Timer(mockCallback, 1000);
156
+
157
+ timer.start();
158
+ expect(BackgroundTimer.setTimeout).toHaveBeenCalledTimes(1);
159
+
160
+ timer.pause();
161
+ expect(BackgroundTimer.clearTimeout).toHaveBeenCalledTimes(1);
162
+
163
+ timer.resume();
164
+ expect(BackgroundTimer.setTimeout).toHaveBeenCalledTimes(2);
165
+
166
+ timer.clear();
167
+ expect(BackgroundTimer.clearTimeout).toHaveBeenCalledTimes(2);
168
+ });
169
+ });
170
+
171
+ describe("edge cases", () => {
172
+ it("should handle zero delay", () => {
173
+ const timer = new Timer(mockCallback, 0);
174
+
175
+ timer.start();
176
+
177
+ expect(BackgroundTimer.setTimeout).toHaveBeenCalledWith(mockCallback, 0);
178
+ });
179
+
180
+ it("should handle very long delays", () => {
181
+ const longDelay = Number.MAX_SAFE_INTEGER;
182
+ const timer = new Timer(mockCallback, longDelay);
183
+
184
+ timer.start();
185
+
186
+ expect(BackgroundTimer.setTimeout).toHaveBeenCalledWith(
187
+ mockCallback,
188
+ longDelay
189
+ );
190
+ });
191
+
192
+ it("should handle negative delays gracefully", () => {
193
+ const timer = new Timer(mockCallback, -1000);
194
+
195
+ expect(() => {
196
+ timer.start();
197
+ }).not.toThrow();
198
+ });
199
+
200
+ it("should maintain callback reference through lifecycle", () => {
201
+ const specificCallback = jest.fn();
202
+ const timer = new Timer(specificCallback, 1000);
203
+
204
+ timer.start();
205
+
206
+ expect(BackgroundTimer.setTimeout).toHaveBeenCalledWith(
207
+ specificCallback,
208
+ 1000
209
+ );
210
+ });
211
+ });
212
+
213
+ describe("callback execution safety", () => {
214
+ it("should not execute callback immediately on start", () => {
215
+ const timer = new Timer(mockCallback, 1000);
216
+
217
+ timer.start();
218
+
219
+ expect(mockCallback).not.toHaveBeenCalled();
220
+ });
221
+
222
+ it("should not execute callback when paused or cleared", () => {
223
+ const timer1 = new Timer(mockCallback, 100);
224
+ const timer2 = new Timer(mockCallback, 100);
225
+
226
+ timer1.start();
227
+ timer1.pause();
228
+ timer2.start();
229
+ timer2.clear();
230
+
231
+ jest.advanceTimersByTime(200);
232
+
233
+ expect(mockCallback).not.toHaveBeenCalled();
234
+ });
235
+ });
236
+ });
@@ -0,0 +1,21 @@
1
+ import { isString } from "../index";
2
+
3
+ describe("isString", () => {
4
+ it("should return true for string primitives", () => {
5
+ expect(isString("hello")).toBe(true);
6
+ expect(isString("")).toBe(true);
7
+ });
8
+
9
+ it("should return true for String object instances", () => {
10
+ // eslint-disable-next-line no-new-wrappers
11
+ expect(isString(new String("test"))).toBe(true);
12
+ });
13
+
14
+ it("should return false for non-string values", () => {
15
+ expect(isString(123)).toBe(false);
16
+ expect(isString(null)).toBe(false);
17
+ expect(isString(undefined)).toBe(false);
18
+ expect(isString({})).toBe(false);
19
+ expect(isString([])).toBe(false);
20
+ });
21
+ });
@@ -1,3 +1,7 @@
1
1
  export const isString = (value: unknown) => {
2
+ if (typeof value === "object" && value instanceof String) {
3
+ return true;
4
+ }
5
+
2
6
  return typeof value === "string";
3
7
  };
@@ -0,0 +1,48 @@
1
+ import { mergeRight } from "../mergeRight";
2
+
3
+ describe("mergeRight", () => {
4
+ test("merges two objects with no overlapping keys", () => {
5
+ const a = { x: 1, y: 2 };
6
+ const b = { z: 3 };
7
+
8
+ const result = mergeRight(a, b);
9
+
10
+ expect(result).toEqual({ x: 1, y: 2, z: 3 });
11
+ });
12
+
13
+ test("overwrites keys from the second object", () => {
14
+ const a = { x: 1, y: 2 };
15
+ const b = { y: 10, z: 3 };
16
+
17
+ const result = mergeRight(a, b);
18
+
19
+ expect(result).toEqual({ x: 1, y: 10, z: 3 });
20
+ });
21
+
22
+ test("does not mutate the original objects", () => {
23
+ const a = { x: 1 };
24
+ const b = { y: 2 };
25
+
26
+ const result = mergeRight(a, b);
27
+
28
+ expect(result).not.toBe(a);
29
+ expect(result).not.toBe(b);
30
+ expect(a).toEqual({ x: 1 });
31
+ expect(b).toEqual({ y: 2 });
32
+ });
33
+
34
+ test("works with empty objects", () => {
35
+ expect(mergeRight({}, { a: 1 })).toEqual({ a: 1 });
36
+ expect(mergeRight({ a: 1 }, {})).toEqual({ a: 1 });
37
+ expect(mergeRight({}, {})).toEqual({});
38
+ });
39
+
40
+ test("works with nested objects (shallow merge only)", () => {
41
+ const a = { x: { nested: 1 }, y: 2 };
42
+ const b = { x: { nested: 10 }, z: 3 };
43
+
44
+ const result = mergeRight(a, b);
45
+
46
+ expect(result).toEqual({ x: { nested: 10 }, y: 2, z: 3 });
47
+ });
48
+ });
@@ -0,0 +1,124 @@
1
+ import {
2
+ createPluginsByPathSelector,
3
+ createPluginsByModuleSelector,
4
+ combinePluginSelectors,
5
+ } from "../selectors";
6
+
7
+ describe("Plugin Selectors", () => {
8
+ const mockPlugins = [
9
+ {
10
+ identifier: "plugin1",
11
+ module: {
12
+ urlScheme: { host: "test" },
13
+ player: { type: "default" },
14
+ },
15
+ customField: true,
16
+ },
17
+ {
18
+ identifier: "plugin2",
19
+ module: {
20
+ urlScheme: { host: "other" },
21
+ },
22
+ },
23
+ {
24
+ identifier: "plugin3",
25
+ module: {
26
+ player: { type: "custom" },
27
+ },
28
+ },
29
+ ];
30
+
31
+ const mockState = {
32
+ plugins: mockPlugins,
33
+ };
34
+
35
+ describe("createPluginsByPathSelector", () => {
36
+ it("filters plugins by string path", () => {
37
+ const selector = createPluginsByPathSelector("customField");
38
+ const result = selector(mockState);
39
+
40
+ expect(result).toHaveLength(1);
41
+ expect(result[0].identifier).toBe("plugin1");
42
+ });
43
+
44
+ it("filters plugins by array path", () => {
45
+ const selector = createPluginsByPathSelector(["module", "urlScheme"]);
46
+ const result = selector(mockState);
47
+
48
+ expect(result).toHaveLength(2);
49
+ expect(result.map((p) => p.identifier)).toEqual(["plugin1", "plugin2"]);
50
+ });
51
+
52
+ it("handles missing plugins array", () => {
53
+ const selector = createPluginsByPathSelector("customField");
54
+ const result = selector({});
55
+
56
+ expect(result).toEqual([]);
57
+ });
58
+
59
+ it("handles non-existent path", () => {
60
+ const selector = createPluginsByPathSelector("nonexistent");
61
+ const result = selector(mockState);
62
+
63
+ expect(result).toEqual([]);
64
+ });
65
+ });
66
+
67
+ describe("createPluginsByModuleSelector", () => {
68
+ it("filters plugins by module path", () => {
69
+ const selector = createPluginsByModuleSelector("player");
70
+ const result = selector(mockState);
71
+
72
+ expect(result).toHaveLength(2);
73
+ expect(result.map((p) => p.identifier)).toEqual(["plugin1", "plugin3"]);
74
+ });
75
+
76
+ it("handles non-existent module", () => {
77
+ const selector = createPluginsByModuleSelector("nonexistent");
78
+ const result = selector(mockState);
79
+
80
+ expect(result).toEqual([]);
81
+ });
82
+ });
83
+
84
+ describe("combinePluginSelectors", () => {
85
+ it("combines multiple selectors with AND logic", () => {
86
+ const urlSchemeSelector = createPluginsByModuleSelector("urlScheme");
87
+ const playerSelector = createPluginsByModuleSelector("player");
88
+
89
+ const combinedSelector = combinePluginSelectors(
90
+ urlSchemeSelector,
91
+ playerSelector
92
+ );
93
+
94
+ const result = combinedSelector(mockState);
95
+
96
+ expect(result).toHaveLength(1);
97
+ expect(result[0].identifier).toBe("plugin1");
98
+ });
99
+
100
+ it("returns empty array when no plugins match all conditions", () => {
101
+ const urlSchemeSelector = createPluginsByModuleSelector("urlScheme");
102
+ const customSelector = createPluginsByPathSelector("nonexistent");
103
+
104
+ const combinedSelector = combinePluginSelectors(
105
+ urlSchemeSelector,
106
+ customSelector
107
+ );
108
+
109
+ const result = combinedSelector(mockState);
110
+
111
+ expect(result).toEqual([]);
112
+ });
113
+
114
+ it("handles empty state", () => {
115
+ const selector = combinePluginSelectors(
116
+ createPluginsByModuleSelector("urlScheme")
117
+ );
118
+
119
+ const result = selector({});
120
+
121
+ expect(result).toEqual([]);
122
+ });
123
+ });
124
+ });
package/utils/index.ts CHANGED
@@ -18,6 +18,8 @@ export { take } from "./take";
18
18
 
19
19
  export { mapAccum } from "./mapAccum";
20
20
 
21
+ export { mergeRight } from "./mergeRight";
22
+
21
23
  export {
22
24
  cloneDeep as clone,
23
25
  flatten,
@@ -40,5 +42,16 @@ export {
40
42
  uniqWith,
41
43
  flowRight as compose,
42
44
  partial,
45
+ clamp,
43
46
  reverse,
47
+ takeRight,
48
+ fromPairs,
49
+ sortBy,
50
+ merge,
51
+ values,
52
+ head,
53
+ findIndex,
54
+ set,
55
+ compact,
56
+ identity,
44
57
  } from "lodash";
@@ -0,0 +1,5 @@
1
+ import { assign } from "lodash";
2
+
3
+ export const mergeRight = (a, b) => {
4
+ return assign({}, a, b);
5
+ };
package/utils/path.ts CHANGED
@@ -1,5 +1,8 @@
1
- import { get } from "lodash";
1
+ import { pathOr } from "./pathOr";
2
2
 
3
- export const path = (route, record) => {
4
- return get(record, route, undefined);
3
+ export const path = <T = any>(
4
+ route: (string | number) | (string | number)[],
5
+ record: any
6
+ ) => {
7
+ return pathOr<T>(undefined, route, record);
5
8
  };
package/utils/pathOr.ts CHANGED
@@ -1,5 +1,9 @@
1
1
  import { get } from "lodash";
2
2
 
3
- export const pathOr = (defaultValue, path, record) => {
3
+ export const pathOr = <T = any>(
4
+ defaultValue: T,
5
+ path: (number | string) | (number | string)[],
6
+ record: any
7
+ ): T => {
4
8
  return get(record, path, defaultValue);
5
9
  };
@@ -0,0 +1,46 @@
1
+ import { get } from "lodash";
2
+
3
+ export type Selector<T> = (state: any) => T;
4
+
5
+ /**
6
+ * Creates a selector for filtering plugins by path
7
+ * @param path - Path to check in plugin. Can be a dot notation string or array
8
+ * @returns A selector function that returns matching plugins
9
+ */
10
+ export const createPluginsByPathSelector = (
11
+ path: string | string[]
12
+ ): Selector<any[]> => {
13
+ return (state: any) => {
14
+ const plugins = state.plugins || [];
15
+
16
+ return plugins.filter((plugin: any) => get(plugin, path));
17
+ };
18
+ };
19
+
20
+ /**
21
+ * Creates a selector for filtering plugins by module path
22
+ * @param modulePath - Module path to check in plugin (e.g., "urlScheme", "player")
23
+ * @returns A selector function that returns plugins with specified module
24
+ */
25
+ export const createPluginsByModuleSelector = (
26
+ modulePath: string
27
+ ): Selector<any[]> => {
28
+ return createPluginsByPathSelector(["module", modulePath]);
29
+ };
30
+
31
+ /**
32
+ * Creates a selector that combines multiple plugin selectors
33
+ * @param selectors - Array of plugin selectors to combine
34
+ * @returns A selector function that returns plugins matching all conditions
35
+ */
36
+ export const combinePluginSelectors = (
37
+ ...selectors: Selector<any[]>[]
38
+ ): Selector<any[]> => {
39
+ return (state: any) => {
40
+ return selectors.reduce((filtered, selector) => {
41
+ const selected = selector(state);
42
+
43
+ return filtered.filter((plugin) => selected.includes(plugin));
44
+ }, state.plugins || []);
45
+ };
46
+ };
@@ -1,6 +1,6 @@
1
1
  import { useNavigation, useRivers, useScreenContext } from "../../reactHooks";
2
2
  import { createLogger } from "../../logger";
3
- import { useCallback, useMemo } from "react";
3
+ import { useCallback, useMemo, useRef, useEffect } from "react";
4
4
 
5
5
  export enum NavigationCallbackOptions {
6
6
  DEFAULT = "default",
@@ -17,6 +17,7 @@ export enum ResultType {
17
17
  export type CallbackResult = hookCallbackArgs & {
18
18
  options?: {
19
19
  resultType?: ResultType;
20
+ navigator?: QuickBrickAppNavigator;
20
21
  };
21
22
  };
22
23
 
@@ -45,7 +46,7 @@ const legacyMappingKeys = {
45
46
  actionType: "login_completion_action",
46
47
  targetScreen: "navigate_to_login_screen",
47
48
  },
48
- "quick-brick-user-account-ui-component": {
49
+ "quick-brick-user-account-ui-component.login": {
49
50
  actionType: "callbackAction",
50
51
  },
51
52
  "quick-brick-login-multi-login-providers.login": {
@@ -83,7 +84,8 @@ export const getNavigationKeys = (
83
84
  ): NavKeys => {
84
85
  const general = (item?.general ?? {}) as General;
85
86
 
86
- const pluginIdentifier = (item as any).identifier ?? item?.type ?? "";
87
+ const pluginIdentifier =
88
+ (item as any).identifier ?? item?.type ?? item?.component_type ?? "";
87
89
 
88
90
  const legacy =
89
91
  legacyMappingKeys[`${pluginIdentifier}.${resultType}`] ??
@@ -135,6 +137,12 @@ export const useCallbackNavigationAction = (
135
137
  const rivers = useRivers();
136
138
  const screenContext = useScreenContext();
137
139
 
140
+ const navigationRef = useRef(navigation);
141
+
142
+ useEffect(() => {
143
+ navigationRef.current = navigation;
144
+ }, [navigation]);
145
+
138
146
  const overrideCallbackFromComponent = useMemo(() => {
139
147
  log_verbose(`${LogPrefix}: overridden callbackAction by component`);
140
148
 
@@ -168,20 +176,35 @@ export const useCallbackNavigationAction = (
168
176
  return;
169
177
  }
170
178
 
171
- const data = getNavigationKeys(item, args.options?.resultType ?? null);
179
+ let data = getNavigationKeys(item, args.options?.resultType ?? null);
172
180
 
173
181
  if (!data) {
174
- hookCallback?.(args);
182
+ const isScreen = !hookCallback;
175
183
 
176
- return;
184
+ if (isScreen && args.options?.resultType === ResultType.login) {
185
+ log_debug(
186
+ `${LogPrefix} no navigation data found, applying GO BACK for login screen`
187
+ );
188
+
189
+ data = {
190
+ action: NavigationCallbackOptions.GO_BACK,
191
+ targetScreenId: null,
192
+ };
193
+ } else {
194
+ hookCallback?.(args);
195
+
196
+ return;
197
+ }
177
198
  }
178
199
 
179
200
  hookCallback?.({ ...args, success: false, cancelled: true });
201
+ const currentNavigation = navigationRef.current;
180
202
 
181
203
  switch (data.action) {
182
204
  case NavigationCallbackOptions.GO_BACK: {
183
- if (navigation.canGoBack()) {
184
- navigation.goBack();
205
+ if (currentNavigation.canGoBack()) {
206
+ currentNavigation.goBack();
207
+
185
208
  log_info(`${LogPrefix} performing 'GO BACK' action`);
186
209
  } else {
187
210
  log_info(`${LogPrefix} cannot perform 'GO BACK' action — ignoring`);
@@ -191,7 +214,7 @@ export const useCallbackNavigationAction = (
191
214
  }
192
215
 
193
216
  case NavigationCallbackOptions.GO_HOME: {
194
- navigation.goHome();
217
+ currentNavigation.goHome();
195
218
  log_info(`${LogPrefix} performing 'GO HOME' action`);
196
219
  break;
197
220
  }
@@ -207,7 +230,7 @@ export const useCallbackNavigationAction = (
207
230
  const screen = rivers[screenId];
208
231
 
209
232
  if (screen) {
210
- navigation.replace(screen);
233
+ currentNavigation.replace(screen);
211
234
 
212
235
  log_info(
213
236
  `${LogPrefix} performing 'GO TO SCREEN' action to screen: ${screenId}`
@@ -224,7 +247,7 @@ export const useCallbackNavigationAction = (
224
247
  }
225
248
  }
226
249
  },
227
- [item, navigation, rivers]
250
+ [item, rivers]
228
251
  );
229
252
 
230
253
  return overrideCallbackFromComponent || callbackAction;
@@ -20,7 +20,7 @@ const callbackKeyPrefix = (key, prefix, keySeparator = "_") =>
20
20
 
21
21
  const extendManifestWithHookCallback = (prefix = null) => ({
22
22
  group: true,
23
- label: "CallBack Navigation",
23
+ label: `CallBack Navigation: ${prefix}`,
24
24
  folded: true,
25
25
  fields: [
26
26
  {