@applicaster/zapp-react-native-ui-components 14.0.0-alpha.5114565431 → 14.0.0-alpha.5203410334

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 (50) hide show
  1. package/Components/BaseFocusable/index.tsx +23 -12
  2. package/Components/Cell/__tests__/CellWIthFocusable.test.js +3 -2
  3. package/Components/Cell/index.js +1 -1
  4. package/Components/ComponentResolver/index.ts +1 -1
  5. package/Components/FeedLoader/FeedLoader.tsx +6 -15
  6. package/Components/FeedLoader/FeedLoaderHOC.tsx +21 -0
  7. package/Components/FeedLoader/index.js +2 -8
  8. package/Components/FreezeWithCallback/__tests__/index.test.tsx +67 -43
  9. package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +42 -59
  10. package/Components/GeneralContentScreen/utils/useCurationAPI.ts +13 -10
  11. package/Components/Layout/TV/LayoutBackground.tsx +1 -1
  12. package/Components/Layout/TV/__tests__/index.test.tsx +0 -1
  13. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +2 -0
  14. package/Components/MasterCell/DefaultComponents/Button.tsx +1 -1
  15. package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +1 -1
  16. package/Components/MasterCell/DefaultComponents/__tests__/image.test.js +10 -10
  17. package/Components/MasterCell/DefaultComponents/__tests__/text.test.tsx +18 -18
  18. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/__tests__/index.test.tsx +10 -10
  19. package/Components/MasterCell/utils/behaviorProvider.ts +82 -14
  20. package/Components/MasterCell/utils/index.ts +11 -5
  21. package/Components/OfflineHandler/__tests__/index.test.tsx +26 -35
  22. package/Components/PlayerContainer/ErrorDisplay/index.ts +1 -1
  23. package/Components/PlayerContainer/ProgramInfo/index.tsx +1 -1
  24. package/Components/PlayerContainer/index.ts +1 -1
  25. package/Components/River/ComponentsMap/hooks/__tests__/useLoadingState.test.ts +378 -0
  26. package/Components/River/ComponentsMap/hooks/useLoadingState.ts +2 -2
  27. package/Components/River/RefreshControl.tsx +11 -17
  28. package/Components/River/__tests__/river.test.js +12 -26
  29. package/Components/River/index.tsx +1 -1
  30. package/Components/Screen/__tests__/Screen.test.tsx +28 -29
  31. package/Components/Tabs/Tabs.tsx +2 -3
  32. package/Components/Touchable/__tests__/touchable.test.tsx +12 -17
  33. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +3 -9
  34. package/Components/VideoModal/__tests__/PlayerDetails.test.tsx +5 -5
  35. package/Contexts/ConfigutaionContext/__tests__/ConfigurationProvider.test.tsx +3 -3
  36. package/Contexts/ScreenContext/index.tsx +46 -6
  37. package/Decorators/ConfigurationWrapper/__tests__/withConfigurationProvider.test.tsx +3 -3
  38. package/Decorators/RiverFeedLoader/__tests__/__snapshots__/riverFeedLoader.test.tsx.snap +221 -209
  39. package/Decorators/RiverFeedLoader/__tests__/riverFeedLoader.test.tsx +14 -16
  40. package/Decorators/RiverFeedLoader/__tests__/utils.test.ts +0 -20
  41. package/Decorators/RiverFeedLoader/index.tsx +22 -4
  42. package/Decorators/RiverFeedLoader/utils/index.ts +0 -18
  43. package/Decorators/RiverResolver/__tests__/riverResolver.test.tsx +3 -6
  44. package/Decorators/ZappPipesDataConnector/__tests__/UrlFeedResolver.test.tsx +27 -27
  45. package/Decorators/ZappPipesDataConnector/resolvers/UrlFeedResolver.tsx +35 -10
  46. package/Decorators/ZappPipesDataConnector/utils/mongoFilter.ts +738 -0
  47. package/Decorators/ZappPipesDataConnector/utils/useFilter.tsx +157 -0
  48. package/events/index.ts +1 -0
  49. package/package.json +5 -6
  50. package/Components/River/__tests__/__snapshots__/river.test.js.snap +0 -27
@@ -1,9 +1,8 @@
1
1
  import React from "react";
2
- import * as R from "ramda";
3
2
  import { View } from "react-native";
4
- import { render } from "@testing-library/react-native";
5
3
 
6
4
  import * as mock_fixtures from "./fixtures";
5
+ import { renderWithProviders } from "@applicaster/zapp-react-native-utils/testUtils";
7
6
 
8
7
  const mockUsePipesContexts = () => ({
9
8
  entry: mock_fixtures.entryContext,
@@ -11,8 +10,13 @@ const mockUsePipesContexts = () => ({
11
10
  search: mock_fixtures.searchContext,
12
11
  });
13
12
 
14
- const mockGetDatasourceUrl = jest.fn(
15
- R.curry((_, component) => component.data.source)
13
+ const mockDispatchLoadPipesData = jest.fn();
14
+
15
+ jest.mock(
16
+ "@applicaster/zapp-react-native-utils/reactHooks/feed/useLoadPipesDataDispatch",
17
+ () => ({
18
+ useLoadPipesDataDispatch: jest.fn(() => mockDispatchLoadPipesData),
19
+ })
16
20
  );
17
21
 
18
22
  jest.mock(
@@ -31,8 +35,6 @@ jest.mock(
31
35
 
32
36
  jest.mock("../utils", () => ({
33
37
  ...jest.requireActual("../utils"),
34
- loadDatasources: jest.fn(),
35
- getDatasourceUrl: mockGetDatasourceUrl,
36
38
  usePipesContexts: mockUsePipesContexts,
37
39
  }));
38
40
 
@@ -45,27 +47,23 @@ const riverProps = {
45
47
  dispatch: jest.fn(),
46
48
  };
47
49
 
48
- const { loadDatasources, getDatasourceUrl } = require("../utils");
49
-
50
50
  const { WithRiverFeedLoader } = require("..");
51
51
 
52
52
  describe("WithRiverFeedLoader", () => {
53
53
  const DecoratedRiver = WithRiverFeedLoader(River);
54
- const wrapper = render(<DecoratedRiver {...riverProps} />);
54
+ const wrapper = renderWithProviders(<DecoratedRiver {...riverProps} />);
55
55
 
56
56
  it("renders correctly", () => {
57
57
  expect(wrapper.toJSON()).toMatchSnapshot();
58
58
  });
59
59
 
60
60
  it("collects the datasources in the screen and loads them", () => {
61
- expect(getDatasourceUrl).toHaveBeenCalledTimes(1);
62
- expect(loadDatasources).toHaveBeenCalledTimes(1);
61
+ expect(mockDispatchLoadPipesData).toHaveBeenCalledTimes(23);
63
62
 
64
- expect(loadDatasources).toHaveBeenNthCalledWith(
65
- 1,
66
- mock_fixtures.riverDataSources,
67
- mock_fixtures.river.id,
68
- riverProps.dispatch
63
+ expect(mockDispatchLoadPipesData).toHaveBeenLastCalledWith(
64
+ "http://datasource24",
65
+ {},
66
+ { withResolvers: true, withScreenRouteMapping: true }
69
67
  );
70
68
  });
71
69
  });
@@ -14,31 +14,11 @@ jest.mock("@applicaster/zapp-react-native-redux/ZappPipes", () => ({
14
14
  }));
15
15
 
16
16
  const {
17
- loadDatasources,
18
17
  riverIsCurrentRoute,
19
18
  getDatasourceUrl,
20
19
  ignoreComponentsWithClearCacheFlag,
21
20
  } = require("../utils");
22
21
 
23
- const {
24
- loadPipesData,
25
- } = require("@applicaster/zapp-react-native-redux/ZappPipes");
26
-
27
- const dispatch = (value) => Promise.resolve(value);
28
-
29
- describe("loadDataSource", () => {
30
- it("calls loadPipesData for each provided urls", async () => {
31
- const urls = [
32
- ["http://datasource1", "http://datasource2"],
33
- ["http://datasource3", "http://datasource4"],
34
- ];
35
-
36
- await loadDatasources(urls, river.id, dispatch);
37
-
38
- expect(loadPipesData).toHaveBeenCalledTimes(R.flatten(urls).length);
39
- });
40
- });
41
-
42
22
  describe("riverIsCurrentRoute", () => {
43
23
  const route = "/river/homeId/river/screenId";
44
24
  const riverId = "screenId";
@@ -4,15 +4,19 @@ import { DispatchProp } from "react-redux";
4
4
 
5
5
  import { useRoute } from "@applicaster/zapp-react-native-utils/reactHooks/navigation/useRoute";
6
6
 
7
- import { mapAndSplit } from "@applicaster/zapp-react-native-utils/arrayUtils";
7
+ import {
8
+ mapAndSplit,
9
+ mapPromises,
10
+ reducePromises,
11
+ } from "@applicaster/zapp-react-native-utils/arrayUtils";
8
12
 
9
13
  import {
10
14
  DATASOURCE_CHUNKS,
11
15
  getDatasourceUrl,
12
16
  ignoreComponentsWithClearCacheFlag,
13
- loadDatasources,
14
17
  usePipesContexts,
15
18
  } from "./utils";
19
+ import { useLoadPipesDataDispatch } from "@applicaster/zapp-react-native-utils/reactHooks";
16
20
 
17
21
  type RiverProps = {
18
22
  dispatch: DispatchProp;
@@ -25,9 +29,10 @@ export function WithRiverFeedLoader(Component: ZappComponent) {
25
29
  return function WrappedWithRiverFeedLoader(props: RiverProps) {
26
30
  const { river } = props;
27
31
  const { screenData, pathname } = useRoute();
28
-
29
32
  const pipesContexts = usePipesContexts(river.id, pathname);
30
33
 
34
+ const loadPipesDataDispatcher = useLoadPipesDataDispatch();
35
+
31
36
  const componentsToLoad = ignoreComponentsWithClearCacheFlag(
32
37
  river?.ui_components || []
33
38
  );
@@ -49,7 +54,20 @@ export function WithRiverFeedLoader(Component: ZappComponent) {
49
54
  item?.filter((item2) => item2 !== undefined)
50
55
  );
51
56
 
52
- loadDatasources(nonEmptyDataSources, river?.id, props.dispatch);
57
+ reducePromises<string, void>(
58
+ mapPromises<string, void>((url) =>
59
+ loadPipesDataDispatcher(
60
+ url,
61
+ {},
62
+ {
63
+ withResolvers: true,
64
+ withScreenRouteMapping: true,
65
+ }
66
+ )
67
+ ),
68
+ undefined,
69
+ nonEmptyDataSources
70
+ );
53
71
  }, []);
54
72
 
55
73
  return <Component {...props} />;
@@ -1,10 +1,4 @@
1
1
  import * as R from "ramda";
2
- import { loadPipesData } from "@applicaster/zapp-react-native-redux/ZappPipes";
3
-
4
- import {
5
- mapPromises,
6
- reducePromises,
7
- } from "@applicaster/zapp-react-native-utils/arrayUtils";
8
2
 
9
3
  export { riverIsCurrentRoute, usePipesContexts } from "./usePipesContexts";
10
4
 
@@ -12,18 +6,6 @@ export { getDatasourceUrl } from "./getDatasourceUrl";
12
6
 
13
7
  export const DATASOURCE_CHUNKS = 10;
14
8
 
15
- export async function loadDatasources(urls: string[][], riverId, dispatch) {
16
- return reducePromises<string, void>(
17
- mapPromises<string, void>((url) => {
18
- if (url) {
19
- return dispatch(loadPipesData(url, { riverId }));
20
- }
21
- }),
22
- undefined,
23
- urls
24
- );
25
- }
26
-
27
9
  export const ignoreComponentsWithClearCacheFlag = R.reject(
28
10
  R.pathEq(["rules", "clear_cache_on_reload"], true)
29
11
  );
@@ -13,12 +13,9 @@ const rivers = {
13
13
  },
14
14
  };
15
15
 
16
- jest.doMock(
17
- "@applicaster/zapp-react-native-redux/hooks/usePickFromState",
18
- () => ({
19
- usePickFromState: jest.fn(() => ({ rivers })),
20
- })
21
- );
16
+ jest.doMock("@applicaster/zapp-react-native-redux/hooks", () => ({
17
+ usePickFromState: jest.fn(() => ({ rivers })),
18
+ }));
22
19
 
23
20
  jest.doMock(
24
21
  "@applicaster/zapp-react-native-utils/reactHooks/navigation/usePathname",
@@ -1,10 +1,16 @@
1
1
  import React from "react";
2
- import { render } from "@testing-library/react-native";
3
2
  import * as useFeedLoaderModule from "@applicaster/zapp-react-native-utils/reactHooks/feed/useFeedLoader";
4
3
  import * as useFeedRefreshModule from "@applicaster/zapp-react-native-utils/reactHooks/feed/useFeedRefresh";
5
- import * as zappPipesClientModule from "@applicaster/zapp-pipes-v2-client";
6
4
  import { favoritesListener } from "@applicaster/zapp-react-native-bridge/Favorites";
7
5
  import { UrlFeedResolver } from "../resolvers/UrlFeedResolver";
6
+ import { renderWithProviders } from "@applicaster/zapp-react-native-utils/testUtils";
7
+ import * as ZappPipesModule from "@applicaster/zapp-pipes-v2-client";
8
+
9
+ const mockSubscribeForKeyChanges = jest.fn();
10
+
11
+ jest
12
+ .spyOn(ZappPipesModule, "subscribeForKeyChanges")
13
+ .mockImplementation(mockSubscribeForKeyChanges);
8
14
 
9
15
  jest.mock("@applicaster/zapp-react-native-utils/reactHooks/feed/useFeedLoader");
10
16
 
@@ -35,9 +41,6 @@ describe("UrlFeedResolver", () => {
35
41
  const mockReloadData = jest.fn();
36
42
  const mockLoadNext = jest.fn();
37
43
 
38
- const mockSubscribeForUrlContextKeyChanges =
39
- zappPipesClientModule.subscribeForUrlContextKeyChanges as jest.Mock;
40
-
41
44
  const mockAddDataSourceListener = jest.fn().mockReturnValue(jest.fn());
42
45
 
43
46
  beforeEach(() => {
@@ -51,8 +54,6 @@ describe("UrlFeedResolver", () => {
51
54
  url: "test-url",
52
55
  data: { entry: [{ id: "1" }] },
53
56
  });
54
-
55
- mockSubscribeForUrlContextKeyChanges.mockReturnValue(jest.fn());
56
57
  });
57
58
 
58
59
  it("should use feedUrl directly when provided", () => {
@@ -63,7 +64,7 @@ describe("UrlFeedResolver", () => {
63
64
  riverId: "test-river",
64
65
  };
65
66
 
66
- render(<UrlFeedResolver {...props} />);
67
+ renderWithProviders(<UrlFeedResolver {...props} />);
67
68
 
68
69
  expect(useFeedLoaderModule.useFeedLoader).toHaveBeenCalledWith({
69
70
  feedUrl: "https://example.com/feed",
@@ -83,7 +84,7 @@ describe("UrlFeedResolver", () => {
83
84
  riverId: "test-river",
84
85
  };
85
86
 
86
- render(<UrlFeedResolver {...props} />);
87
+ renderWithProviders(<UrlFeedResolver {...props} />);
87
88
 
88
89
  expect(useFeedLoaderModule.useFeedLoader).toHaveBeenCalledWith({
89
90
  feedUrl: "https://example.com/source",
@@ -104,7 +105,7 @@ describe("UrlFeedResolver", () => {
104
105
  riverId: "test-river",
105
106
  };
106
107
 
107
- render(<UrlFeedResolver {...props} />);
108
+ renderWithProviders(<UrlFeedResolver {...props} />);
108
109
 
109
110
  expect(useFeedLoaderModule.useFeedLoader).toHaveBeenCalledWith({
110
111
  feedUrl: "favorites-source",
@@ -133,7 +134,7 @@ describe("UrlFeedResolver", () => {
133
134
  riverId: "test-river",
134
135
  };
135
136
 
136
- render(<UrlFeedResolver {...props} />);
137
+ renderWithProviders(<UrlFeedResolver {...props} />);
137
138
 
138
139
  expect(useFeedLoaderModule.useFeedLoader).toHaveBeenCalledWith({
139
140
  feedUrl:
@@ -156,7 +157,7 @@ describe("UrlFeedResolver", () => {
156
157
  plugins: [],
157
158
  };
158
159
 
159
- render(<UrlFeedResolver {...props} />);
160
+ renderWithProviders(<UrlFeedResolver {...props} />);
160
161
 
161
162
  expect(useFeedLoaderModule.useFeedLoader).toHaveBeenCalledWith({
162
163
  feedUrl: expect.stringContaining(
@@ -174,13 +175,12 @@ describe("UrlFeedResolver", () => {
174
175
  riverId: "test-river",
175
176
  };
176
177
 
177
- render(<UrlFeedResolver {...props} />);
178
+ renderWithProviders(<UrlFeedResolver {...props} />);
178
179
 
179
- expect(mockSubscribeForUrlContextKeyChanges).toHaveBeenCalledWith(
180
- "https://example.com/feed",
181
- {},
182
- mockReloadData
183
- );
180
+ expect(mockSubscribeForKeyChanges.mock.calls[0][0]).toMatchObject({
181
+ url: "https://example.com/feed",
182
+ callback: mockReloadData,
183
+ });
184
184
  });
185
185
 
186
186
  it("should set up pipesv2 URL listener from plugin if available", () => {
@@ -199,10 +199,10 @@ describe("UrlFeedResolver", () => {
199
199
  ] as any,
200
200
  };
201
201
 
202
- render(<UrlFeedResolver {...props} />);
202
+ renderWithProviders(<UrlFeedResolver {...props} />);
203
203
 
204
204
  expect(mockAddDataSourceListener).toHaveBeenCalledWith(mockReloadData);
205
- expect(mockSubscribeForUrlContextKeyChanges).not.toHaveBeenCalled();
205
+ expect(mockSubscribeForKeyChanges).not.toHaveBeenCalled();
206
206
  });
207
207
 
208
208
  it("should apply item limit from component rules", () => {
@@ -226,7 +226,7 @@ describe("UrlFeedResolver", () => {
226
226
  data: { entry: [{ id: "1" }, { id: "2" }, { id: "3" }] },
227
227
  });
228
228
 
229
- render(<UrlFeedResolver {...props} />);
229
+ renderWithProviders(<UrlFeedResolver {...props} />);
230
230
 
231
231
  expect(mockChildren).toHaveBeenCalledWith(
232
232
  expect.objectContaining({
@@ -252,7 +252,7 @@ describe("UrlFeedResolver", () => {
252
252
  isLast: true,
253
253
  };
254
254
 
255
- render(<UrlFeedResolver {...props1} />);
255
+ renderWithProviders(<UrlFeedResolver {...props1} />);
256
256
 
257
257
  expect(mockChildren).toHaveBeenCalledWith(
258
258
  expect.objectContaining({
@@ -274,7 +274,7 @@ describe("UrlFeedResolver", () => {
274
274
  isLast: false,
275
275
  };
276
276
 
277
- render(<UrlFeedResolver {...props2} />);
277
+ renderWithProviders(<UrlFeedResolver {...props2} />);
278
278
 
279
279
  expect(mockChildren).toHaveBeenCalledWith(
280
280
  expect.objectContaining({
@@ -296,7 +296,7 @@ describe("UrlFeedResolver", () => {
296
296
  isLast: false,
297
297
  };
298
298
 
299
- render(<UrlFeedResolver {...props3} />);
299
+ renderWithProviders(<UrlFeedResolver {...props3} />);
300
300
 
301
301
  expect(mockChildren).toHaveBeenCalledWith(
302
302
  expect.objectContaining({
@@ -313,7 +313,7 @@ describe("UrlFeedResolver", () => {
313
313
  riverId: "test-river",
314
314
  };
315
315
 
316
- render(<UrlFeedResolver {...props} />);
316
+ renderWithProviders(<UrlFeedResolver {...props} />);
317
317
 
318
318
  expect(mockChildren).toHaveBeenCalledWith({
319
319
  zappPipesData: {
@@ -335,7 +335,7 @@ describe("UrlFeedResolver", () => {
335
335
  riverId: "test-river",
336
336
  };
337
337
 
338
- render(<UrlFeedResolver {...props} />);
338
+ renderWithProviders(<UrlFeedResolver {...props} />);
339
339
 
340
340
  expect(useFeedRefreshModule.useFeedRefresh).toHaveBeenCalledWith({
341
341
  reloadData: mockReloadData,
@@ -356,7 +356,7 @@ describe("UrlFeedResolver", () => {
356
356
  riverId: "test-river",
357
357
  };
358
358
 
359
- const { unmount } = render(<UrlFeedResolver {...props} />);
359
+ const { unmount } = renderWithProviders(<UrlFeedResolver {...props} />);
360
360
 
361
361
  unmount();
362
362
 
@@ -10,10 +10,14 @@ import {
10
10
  getSearchContext,
11
11
  useFeedLoader,
12
12
  useFeedRefresh,
13
+ useRoute,
13
14
  } from "@applicaster/zapp-react-native-utils/reactHooks";
14
- import { subscribeForUrlContextKeyChanges } from "@applicaster/zapp-pipes-v2-client";
15
+
15
16
  import { ComponentDataSourceContext, ZappPipesDataProps } from "../types";
17
+ import { useScreenStateStore } from "@applicaster/zapp-react-native-utils/reactHooks/navigation/useScreenStateStore";
16
18
  import { isVerticalListOrGrid } from "../utils";
19
+ import { useFilter } from "../utils/useFilter";
20
+ import { subscribeForKeyChanges } from "@applicaster/zapp-pipes-v2-client";
17
21
 
18
22
  const FAVORITES_TYPE = "FAVOURITES";
19
23
 
@@ -192,18 +196,28 @@ export function UrlFeedResolver({
192
196
  pipesOptions,
193
197
  });
194
198
 
199
+ const { pathname } = useRoute();
200
+ const screenStateStore = useScreenStateStore();
201
+
195
202
  // Setup listeners for data source URL
196
203
  useEffect(() => {
197
- if (dataSourceUrl?.includes("pipesv2://") && reloadData) {
198
- const addListener = getListenerFromPlugin(dataSourceUrl, plugins);
204
+ if (!reloadData) {
205
+ return;
206
+ }
199
207
 
200
- if (addListener) {
201
- return addListener(reloadData);
202
- }
208
+ if (dataSourceUrl?.includes("pipesv2://")) {
209
+ (
210
+ getListenerFromPlugin(dataSourceUrl, plugins) as AddDataSourceListener
211
+ )?.(reloadData);
203
212
  } else {
204
- return subscribeForUrlContextKeyChanges(dataSourceUrl, {}, reloadData);
213
+ return subscribeForKeyChanges({
214
+ url: dataSourceUrl,
215
+ pathname,
216
+ screenStateStore,
217
+ callback: reloadData,
218
+ });
205
219
  }
206
- }, [dataSourceUrl, reloadData]);
220
+ }, [dataSourceUrl, reloadData, pathname, screenStateStore]);
207
221
 
208
222
  // Setup favorites listener
209
223
  useEffect(() => {
@@ -227,15 +241,26 @@ export function UrlFeedResolver({
227
241
  [isLast, component, loadNext]
228
242
  );
229
243
 
244
+ const applyItemFilter = useFilter({ url, loading, data, error }, component);
245
+
230
246
  const zappPipesDataProps = useMemo(() => {
231
247
  const pipeData = { url, loading, data, error };
232
248
 
233
249
  return {
234
- zappPipesData: applyItemLimit(pipeData, component),
250
+ zappPipesData: applyItemLimit(applyItemFilter(pipeData), component),
235
251
  reloadData,
236
252
  loadNextData,
237
253
  };
238
- }, [url, loading, data, error, component, reloadData, loadNextData]);
254
+ }, [
255
+ url,
256
+ loading,
257
+ data,
258
+ error,
259
+ component,
260
+ reloadData,
261
+ loadNextData,
262
+ applyItemFilter,
263
+ ]);
239
264
 
240
265
  return <>{children(zappPipesDataProps)}</>;
241
266
  }