@applicaster/zapp-react-native-utils 14.0.0-alpha.5333112458 → 14.0.0-alpha.5351122050

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 (58) hide show
  1. package/actionsExecutor/ActionExecutorContext.tsx +0 -1
  2. package/actionsExecutor/ScreenActions.ts +20 -19
  3. package/analyticsUtils/AnalyticsEvents/sendHeaderClickEvent.ts +1 -1
  4. package/analyticsUtils/AnalyticsEvents/sendMenuClickEvent.ts +2 -1
  5. package/analyticsUtils/__tests__/analyticsUtils.test.js +0 -11
  6. package/analyticsUtils/index.tsx +3 -4
  7. package/analyticsUtils/manager.ts +1 -1
  8. package/appUtils/HooksManager/Hook.ts +4 -4
  9. package/appUtils/HooksManager/index.ts +11 -1
  10. package/appUtils/accessibilityManager/index.ts +3 -12
  11. package/appUtils/contextKeysManager/contextResolver.ts +29 -1
  12. package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +5 -0
  13. package/appUtils/focusManager/__tests__/focusManager.test.js +1 -1
  14. package/appUtils/focusManager/index.ios.ts +10 -0
  15. package/appUtils/focusManager/index.ts +82 -11
  16. package/appUtils/focusManager/treeDataStructure/Tree/index.js +1 -1
  17. package/appUtils/focusManagerAux/utils/index.ts +106 -3
  18. package/arrayUtils/index.ts +1 -1
  19. package/componentsUtils/__tests__/isTabsScreen.test.ts +38 -0
  20. package/componentsUtils/index.ts +4 -1
  21. package/configurationUtils/__tests__/manifestKeyParser.test.ts +0 -1
  22. package/index.d.ts +0 -12
  23. package/navigationUtils/__tests__/mapContentTypesToRivers.test.ts +130 -0
  24. package/navigationUtils/index.ts +7 -5
  25. package/package.json +2 -3
  26. package/reactHooks/autoscrolling/__tests__/useTrackedView.test.tsx +15 -14
  27. package/reactHooks/cell-click/__tests__/index.test.js +3 -0
  28. package/reactHooks/debugging/__tests__/index.test.js +0 -1
  29. package/reactHooks/feed/__tests__/useBatchLoading.test.tsx +47 -90
  30. package/reactHooks/feed/__tests__/useFeedLoader.test.tsx +57 -37
  31. package/reactHooks/feed/index.ts +2 -0
  32. package/reactHooks/feed/useBatchLoading.ts +15 -8
  33. package/reactHooks/feed/useFeedLoader.tsx +39 -44
  34. package/reactHooks/feed/useLoadPipesDataDispatch.ts +57 -0
  35. package/reactHooks/feed/usePipesCacheReset.ts +2 -2
  36. package/reactHooks/flatList/useSequentialRenderItem.tsx +3 -3
  37. package/reactHooks/layout/__tests__/index.test.tsx +3 -1
  38. package/reactHooks/layout/useDimensions/__tests__/useDimensions.test.ts +34 -36
  39. package/reactHooks/layout/useDimensions/useDimensions.ts +2 -3
  40. package/reactHooks/layout/useLayoutVersion.ts +5 -5
  41. package/reactHooks/navigation/index.ts +5 -7
  42. package/reactHooks/navigation/useScreenStateStore.ts +3 -3
  43. package/reactHooks/resolvers/__tests__/useCellResolver.test.tsx +4 -0
  44. package/reactHooks/state/index.ts +1 -1
  45. package/reactHooks/state/useHomeRiver.ts +4 -2
  46. package/reactHooks/state/useRivers.ts +7 -8
  47. package/screenPickerUtils/index.ts +7 -0
  48. package/storage/ScreenSingleValueProvider.ts +25 -22
  49. package/storage/ScreenStateMultiSelectProvider.ts +26 -23
  50. package/testUtils/index.tsx +7 -8
  51. package/time/BackgroundTimer.ts +1 -1
  52. package/utils/__tests__/find.test.ts +36 -0
  53. package/utils/__tests__/pathOr.test.ts +37 -0
  54. package/utils/__tests__/startsWith.test.ts +30 -0
  55. package/utils/find.ts +3 -0
  56. package/utils/index.ts +8 -0
  57. package/utils/pathOr.ts +5 -0
  58. package/utils/startsWith.ts +9 -0
@@ -1,14 +1,31 @@
1
- import { isNotNil } from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
2
- import { find, last, pathOr, startsWith } from "ramda";
1
+ import {
2
+ isNil,
3
+ last,
4
+ startsWith,
5
+ find,
6
+ pathOr,
7
+ } from "@applicaster/zapp-react-native-utils/utils";
8
+
3
9
  import {
4
10
  QUICK_BRICK_CONTENT,
5
11
  QUICK_BRICK_NAVBAR,
6
12
  } from "@applicaster/quick-brick-core/const";
7
13
 
14
+ import {
15
+ getFocusableId,
16
+ SCREEN_PICKER_CONTAINER,
17
+ } from "@applicaster/zapp-react-native-utils/screenPickerUtils";
18
+
8
19
  // run check each 300 ms
9
20
  // run this check too often could lead to performance penalty on low-end devices
10
21
  const HOW_OFTEN_TO_CHECK_CONDITION = 300; // ms
11
22
 
23
+ const isTopMenu = (node) => startsWith(QUICK_BRICK_NAVBAR, node?.id);
24
+ const isContent = (node) => startsWith(QUICK_BRICK_CONTENT, node?.id);
25
+ const isRoot = (node) => node?.id === "root";
26
+
27
+ const isScrenPicker = (node) => startsWith(SCREEN_PICKER_CONTAINER, node?.id);
28
+
12
29
  type Props = {
13
30
  maxTimeout: number;
14
31
  conditionFn: () => boolean;
@@ -49,7 +66,7 @@ export const waitForActiveScreen = (currentRoute: string, focusableTree) => {
49
66
 
50
67
  const route = find((route) => route.id === currentRoute, routes);
51
68
 
52
- return isNotNil(route);
69
+ return !isNil(route);
53
70
  };
54
71
 
55
72
  return waitUntil({
@@ -99,3 +116,89 @@ export const waitForContent = (focusableTree) => {
99
116
  conditionFn: contentHasAnyChildren,
100
117
  });
101
118
  };
119
+
120
+ export const findSelectedTabId = (item: ZappEntry): string => {
121
+ const selectedTabId = getFocusableId(item.id);
122
+
123
+ return selectedTabId;
124
+ };
125
+
126
+ export const findSelectedMenuId = (focusableTree) => {
127
+ // Set focus with back button context
128
+ const navbar = getNavbarNode(focusableTree);
129
+
130
+ const selectedMenuItemId = find(
131
+ (child) => child.component.props.selected,
132
+ navbar.children
133
+ )?.id;
134
+
135
+ return selectedMenuItemId;
136
+ };
137
+
138
+ export const isTabsScreenContentFocused = (node) => {
139
+ if (isRoot(node)) {
140
+ return false;
141
+ }
142
+
143
+ if (isTopMenu(node)) {
144
+ return false;
145
+ }
146
+
147
+ if (isContent(node)) {
148
+ return false;
149
+ }
150
+
151
+ if (isScrenPicker(node)) {
152
+ return true;
153
+ }
154
+
155
+ return isTabsScreenContentFocused(node.parent);
156
+ };
157
+
158
+ export const isCurrentFocusOnMenu = (node) => {
159
+ if (isRoot(node)) {
160
+ return false;
161
+ }
162
+
163
+ if (isTopMenu(node)) {
164
+ return true;
165
+ }
166
+
167
+ if (isContent(node)) {
168
+ return false;
169
+ }
170
+
171
+ return isCurrentFocusOnMenu(node.parent);
172
+ };
173
+
174
+ export const isCurrentFocusOnContent = (node) => {
175
+ if (isRoot(node)) {
176
+ return false;
177
+ }
178
+
179
+ if (isTopMenu(node)) {
180
+ return false;
181
+ }
182
+
183
+ if (isContent(node)) {
184
+ return true;
185
+ }
186
+
187
+ return isCurrentFocusOnContent(node.parent);
188
+ };
189
+
190
+ export const isCurrentFocusOn = (id, node) => {
191
+ if (!node) {
192
+ return false;
193
+ }
194
+
195
+ if (isRoot(node)) {
196
+ return false;
197
+ }
198
+
199
+ if (node?.id === id) {
200
+ return true;
201
+ }
202
+
203
+ return isCurrentFocusOn(id, node.parent);
204
+ };
@@ -93,7 +93,7 @@ export const isIndexInRange = (index: number, length: number): boolean => {
93
93
  export const makeListOfIndexes = (size: number): number[] =>
94
94
  Array.from({ length: size }, (_, index) => index);
95
95
 
96
- export const makeListOf = (value: unknown, size: number): number[] => {
96
+ export const makeListOf = <T>(value: T, size: number): T[] => {
97
97
  return Array(size).fill(value);
98
98
  };
99
99
 
@@ -0,0 +1,38 @@
1
+ import { isTabsScreen } from "..";
2
+
3
+ describe("isTabsScreen", () => {
4
+ it("should return true if the component type is 'screen-picker-qb-tv' and tabs_screen=true", () => {
5
+ const item = { component_type: "screen-picker-qb-tv", tabs_screen: true };
6
+ expect(isTabsScreen(item)).toBe(true);
7
+ });
8
+
9
+ it("should return true if the component type is 'screen-picker-qb-tv' and tabs_screen=false", () => {
10
+ const item = { component_type: "screen-picker-qb-tv", tabs_screen: false };
11
+ expect(isTabsScreen(item)).toBe(false);
12
+ });
13
+
14
+ it("should return false if the component type is not 'screen-picker-qb-tv'", () => {
15
+ const item = { component_type: "other-component" };
16
+ expect(isTabsScreen(item)).toBe(false);
17
+ });
18
+
19
+ it("should return false if the component type is undefined", () => {
20
+ const item = { component_type: undefined };
21
+ expect(isTabsScreen(item)).toBe(false);
22
+ });
23
+
24
+ it("should return false if the item is null", () => {
25
+ const item = null;
26
+ expect(isTabsScreen(item)).toBe(false);
27
+ });
28
+
29
+ it("should return false if the item is undefined", () => {
30
+ const item = undefined;
31
+ expect(isTabsScreen(item)).toBe(false);
32
+ });
33
+
34
+ it("should return false if the item is an empty object", () => {
35
+ const item = {};
36
+ expect(isTabsScreen(item)).toBe(false);
37
+ });
38
+ });
@@ -5,7 +5,7 @@ const EMPTY_GROUP_COMPONENT = "empty_group_component";
5
5
 
6
6
  const GALLERY = "gallery-qb";
7
7
 
8
- const SCREEN_PICKER = "screen-picker-qb-tv";
8
+ export const SCREEN_PICKER = "screen-picker-qb-tv";
9
9
 
10
10
  const HORIZONTAL_LIST = "horizontal_list_qb";
11
11
 
@@ -37,3 +37,6 @@ export const isEmptyGroup = (item): boolean =>
37
37
  export const isGroupInfo = (item): boolean =>
38
38
  item?.component_type === GROUP_INFO ||
39
39
  item?.component_type === GROUP_INFO_OLD;
40
+
41
+ export const isTabsScreen = (item): boolean =>
42
+ isScreenPicker(item) && item?.tabs_screen;
@@ -1,6 +1,5 @@
1
1
  import { getAllSpecificStyles } from "../manifestKeyParser";
2
2
 
3
- // Mock the dependencies
4
3
  jest.mock("@applicaster/zapp-react-native-utils/reactUtils", () => ({
5
4
  platformSelect: jest.fn((platforms) => platforms.samsung_tv), // Default to samsung for tests
6
5
  }));
package/index.d.ts CHANGED
@@ -137,15 +137,3 @@ declare type AccessibilityState = {
137
137
  reduceMotionEnabled: boolean;
138
138
  boldTextEnabled: boolean;
139
139
  };
140
-
141
- declare type AccessibilityProps = {
142
- accessible?: boolean;
143
- accessibilityLabel?: string;
144
- accessibilityHint?: string;
145
- "aria-label"?: string;
146
- "aria-description"?: string;
147
- accessibilityRole?: string;
148
- "aria-role"?: string;
149
- role?: string;
150
- tabindex?: number;
151
- };
@@ -0,0 +1,130 @@
1
+ import { mapContentTypesToRivers } from "../index";
2
+
3
+ describe("mapContentTypesToRivers", () => {
4
+ it("should return the correct content types mapped to rivers", () => {
5
+ const state = {
6
+ rivers: {
7
+ "river-1": {
8
+ plugin_type: "river",
9
+ },
10
+ },
11
+ contentTypes: {
12
+ "content-type-1": {
13
+ screen_id: "river-1",
14
+ },
15
+ },
16
+ };
17
+
18
+ const result = mapContentTypesToRivers(state);
19
+
20
+ expect(result).toEqual({
21
+ "content-type-1": {
22
+ screenType: "river",
23
+ screen_id: "river-1",
24
+ },
25
+ });
26
+ });
27
+
28
+ it("should return null if contentTypes is undefined", () => {
29
+ const state = {
30
+ rivers: {
31
+ "river-1": {
32
+ plugin_type: "river",
33
+ },
34
+ },
35
+ // contentTypes is missing
36
+ };
37
+
38
+ const result = mapContentTypesToRivers(state);
39
+
40
+ expect(result).toBeNull();
41
+ });
42
+
43
+ it("should skip content types whose screen does not exist in rivers", () => {
44
+ const state = {
45
+ rivers: {
46
+ "river-1": {
47
+ plugin_type: "river",
48
+ },
49
+ },
50
+ contentTypes: {
51
+ "content-type-1": {
52
+ screen_id: "river-1",
53
+ },
54
+ "content-type-2": {
55
+ screen_id: "river-2", // river-2 does not exist
56
+ },
57
+ },
58
+ };
59
+
60
+ const result = mapContentTypesToRivers(state);
61
+
62
+ expect(result).toEqual({
63
+ "content-type-1": {
64
+ screenType: "river",
65
+ screen_id: "river-1",
66
+ },
67
+ });
68
+
69
+ // result is not null, but may be undefined for missing keys
70
+ expect(result && result["content-type-2"]).toBeUndefined();
71
+ });
72
+
73
+ it("should use 'type' if 'plugin_type' is not present in river", () => {
74
+ const state = {
75
+ rivers: {
76
+ "river-1": {
77
+ type: "custom-type",
78
+ },
79
+ },
80
+ contentTypes: {
81
+ "content-type-1": {
82
+ screen_id: "river-1",
83
+ },
84
+ },
85
+ };
86
+
87
+ const result = mapContentTypesToRivers(state);
88
+
89
+ expect(result).toEqual({
90
+ "content-type-1": {
91
+ screenType: "custom-type",
92
+ screen_id: "river-1",
93
+ },
94
+ });
95
+ });
96
+
97
+ it("should skip content types if neither plugin_type nor type is present in river", () => {
98
+ const state = {
99
+ rivers: {
100
+ "river-1": {
101
+ // no plugin_type or type
102
+ },
103
+ },
104
+ contentTypes: {
105
+ "content-type-1": {
106
+ screen_id: "river-1",
107
+ },
108
+ },
109
+ };
110
+
111
+ const result = mapContentTypesToRivers(state);
112
+
113
+ expect(result).toEqual({});
114
+ });
115
+
116
+ it("should handle empty contentTypes object", () => {
117
+ const state = {
118
+ rivers: {
119
+ "river-1": {
120
+ plugin_type: "river",
121
+ },
122
+ },
123
+ contentTypes: {},
124
+ };
125
+
126
+ const result = mapContentTypesToRivers(state);
127
+
128
+ expect(result).toEqual({});
129
+ });
130
+ });
@@ -13,6 +13,7 @@ import {
13
13
  isPlayable,
14
14
  isV2River,
15
15
  } from "./itemTypeMatchers";
16
+ import { RootState } from "@applicaster/zapp-react-native-redux/store";
16
17
 
17
18
  type PathAttribute = {
18
19
  screenType: string;
@@ -41,7 +42,7 @@ export function getNavigationType(
41
42
  R.unless(R.isNil, R.prop("navigation_type")),
42
43
  R.defaultTo(undefined),
43
44
  R.find(R.propEq("category", category))
44
- )(navigations);
45
+ )(navigations || []);
45
46
  }
46
47
 
47
48
  /**
@@ -377,10 +378,11 @@ export const usesVideoModal = (
377
378
  return targetScreenConfiguration?.styles?.use_video_modal;
378
379
  };
379
380
 
380
- export const mapContentTypesToRivers = ({
381
- rivers,
382
- contentTypes,
383
- }): ZappContentTypesMapped | null => {
381
+ export const mapContentTypesToRivers = (
382
+ state: Partial<RootState>
383
+ ): ZappContentTypesMapped | null => {
384
+ const { rivers, contentTypes } = state;
385
+
384
386
  if (!contentTypes) {
385
387
  return null;
386
388
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applicaster/zapp-react-native-utils",
3
- "version": "14.0.0-alpha.5333112458",
3
+ "version": "14.0.0-alpha.5351122050",
4
4
  "description": "Applicaster Zapp React Native utilities package",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -27,7 +27,7 @@
27
27
  },
28
28
  "homepage": "https://github.com/applicaster/quickbrick#readme",
29
29
  "dependencies": {
30
- "@applicaster/applicaster-types": "14.0.0-alpha.5333112458",
30
+ "@applicaster/applicaster-types": "14.0.0-alpha.5351122050",
31
31
  "buffer": "^5.2.1",
32
32
  "camelize": "^1.0.0",
33
33
  "dayjs": "^1.11.10",
@@ -38,7 +38,6 @@
38
38
  "peerDependencies": {
39
39
  "@applicaster/zapp-pipes-v2-client": "*",
40
40
  "@react-native-community/netinfo": "*",
41
- "immer": "*",
42
41
  "react": "*",
43
42
  "react-native": "*",
44
43
  "uglify-js": "*",
@@ -1,27 +1,31 @@
1
1
  import React from "react";
2
2
 
3
- import { act, renderHook } from "@testing-library/react-hooks";
3
+ import { renderHook } from "@testing-library/react-hooks";
4
+ import { act, waitFor } from "@testing-library/react-native";
4
5
  import { Provider } from "react-redux";
5
6
  import configureStore from "redux-mock-store";
7
+ import { useTrackedView } from "../useTrackedView";
6
8
 
7
9
  const mockUpdateComponentsPositions = jest.fn();
8
10
 
9
11
  jest.mock(
10
12
  "@applicaster/zapp-react-native-ui-components/Contexts/ScreenTrackedViewPositionsContext",
11
13
  () => ({
12
- useScreenTrackedViewPositionsContext: jest.fn().mockReturnValue({
14
+ useScreenTrackedViewPositionsContext: jest.fn(() => ({
13
15
  updateComponentsPositions: mockUpdateComponentsPositions,
14
16
  value: {
15
17
  "123": { componentId: "123", centerX: 0.4, centerY: 0.5 },
16
18
  "124": { componentId: "124", centerX: 0.2, centerY: 0.3 },
17
19
  },
18
- }),
20
+ })),
19
21
  })
20
22
  );
21
23
 
22
- jest.useFakeTimers({ legacyFakeTimers: true });
24
+ jest.useFakeTimers();
23
25
 
24
- jest.mock("@applicaster/zapp-react-native-utils/reactHooks/navigation");
26
+ jest.mock(
27
+ "@applicaster/zapp-react-native-utils/reactHooks/navigation/useNavigation"
28
+ );
25
29
 
26
30
  const mockStore = configureStore();
27
31
 
@@ -32,10 +36,8 @@ const Wrapper = ({ children }: { children: React.ReactChild }) => (
32
36
  <Provider store={store}>{children}</Provider>
33
37
  );
34
38
 
35
- const { useTrackedView } = require("../useTrackedView");
36
-
37
39
  describe("useTrackCurrentAutoScrollingElement", () => {
38
- it("should update position for selected component - onViewportEnter", () => {
40
+ it("should update position for selected component - onViewportEnter", async () => {
39
41
  const { result } = renderHook(() => useTrackedView("123"), {
40
42
  wrapper: Wrapper,
41
43
  });
@@ -46,14 +48,13 @@ describe("useTrackCurrentAutoScrollingElement", () => {
46
48
  rect: { left: 1, right: 1, top: 1, bottom: 1 },
47
49
  };
48
50
 
49
- act(async () => {
50
- await result.current.onPositionUpdated(mockRect);
51
+ act(() => {
52
+ result.current.onPositionUpdated(mockRect);
51
53
  });
52
54
 
53
- // Fast-forward until all timers have been executed
54
- jest.runAllTimers();
55
-
56
- expect(result.current.inViewPort).toBe(true);
55
+ await waitFor(() => {
56
+ expect(result.current.inViewPort).toBe(true);
57
+ });
57
58
 
58
59
  expect(mockUpdateComponentsPositions).toHaveBeenCalledWith(
59
60
  "123",
@@ -26,6 +26,9 @@ jest.mock("@applicaster/zapp-react-native-utils/analyticsUtils/", () => ({
26
26
  }));
27
27
 
28
28
  jest.mock("@applicaster/zapp-react-native-utils/reactHooks/screen", () => ({
29
+ ...jest.requireActual(
30
+ "@applicaster/zapp-react-native-utils/reactHooks/screen"
31
+ ),
29
32
  useTargetScreenData: jest.fn(() => ({})),
30
33
  useCurrentScreenData: jest.fn(() => ({})),
31
34
  }));
@@ -12,7 +12,6 @@ describe("Debug utils", () => {
12
12
  // Clear the timers object
13
13
  Object.keys(timers).forEach((key) => delete timers[key]);
14
14
 
15
- // Mock performance.now()
16
15
  // eslint-disable-next-line no-undef
17
16
  performanceNowMock = jest.spyOn(performance, "now");
18
17
  performanceNowMock.mockReturnValue(0); // Initial value