@applicaster/zapp-react-native-ui-components 15.0.0-rc.98 → 15.1.0-rc.1

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 (115) hide show
  1. package/Components/BaseFocusable/index.ios.ts +2 -12
  2. package/Components/Cell/FocusableWrapper.tsx +0 -3
  3. package/Components/Cell/TvOSCellComponent.tsx +0 -5
  4. package/Components/Focusable/Focusable.tsx +2 -4
  5. package/Components/Focusable/FocusableTvOS.tsx +1 -18
  6. package/Components/Focusable/__tests__/__snapshots__/FocusableTvOS.test.tsx.snap +0 -1
  7. package/Components/FocusableGroup/FocusableTvOS.tsx +1 -30
  8. package/Components/GeneralContentScreen/GeneralContentScreen.tsx +39 -28
  9. package/Components/GeneralContentScreen/__tests__/GeneralContentScreen.test.tsx +104 -0
  10. package/Components/GeneralContentScreen/utils/__tests__/getScreenDataSource.test.ts +19 -0
  11. package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +1 -1
  12. package/Components/GeneralContentScreen/utils/getScreenDataSource.ts +9 -0
  13. package/Components/HandlePlayable/HandlePlayable.tsx +24 -42
  14. package/Components/HandlePlayable/utils.ts +31 -0
  15. package/Components/HookRenderer/HookRenderer.tsx +40 -10
  16. package/Components/HookRenderer/__tests__/HookRenderer.test.tsx +60 -0
  17. package/Components/Layout/TV/LayoutBackground.tsx +2 -5
  18. package/Components/Layout/TV/ScreenContainer.tsx +6 -2
  19. package/Components/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +5 -0
  20. package/Components/Layout/TV/index.tsx +4 -3
  21. package/Components/Layout/TV/index.web.tsx +4 -3
  22. package/Components/LinkHandler/LinkHandler.tsx +2 -2
  23. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +10 -4
  24. package/Components/MasterCell/DefaultComponents/Image/Image.android.tsx +1 -5
  25. package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +3 -11
  26. package/Components/MasterCell/DefaultComponents/Image/Image.web.tsx +1 -9
  27. package/Components/MasterCell/DefaultComponents/Image/hooks/useImage.ts +14 -15
  28. package/Components/MasterCell/DefaultComponents/LiveImage/__tests__/prepareEntry.test.ts +352 -0
  29. package/Components/MasterCell/DefaultComponents/LiveImage/executePreloadHooks.ts +136 -0
  30. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +34 -16
  31. package/Components/MasterCell/DefaultComponents/SecondaryImage/hooks/__tests__/useGetImageDimensions.test.ts +6 -7
  32. package/Components/MasterCell/DefaultComponents/Text/index.tsx +2 -6
  33. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/index.ts +2 -6
  34. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/__tests__/getPluginIdentifier.test.ts +11 -233
  35. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/index.ts +15 -19
  36. package/Components/Navigator/StackNavigator.tsx +6 -0
  37. package/Components/OfflineHandler/NotificationView/NotificationView.tsx +2 -2
  38. package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +18 -17
  39. package/Components/OfflineHandler/__tests__/index.test.tsx +18 -27
  40. package/Components/PlayerContainer/PlayerContainer.tsx +14 -32
  41. package/Components/PreloaderWrapper/__tests__/index.test.tsx +26 -0
  42. package/Components/PreloaderWrapper/index.tsx +15 -0
  43. package/Components/River/ComponentsMap/ComponentsMap.tsx +15 -0
  44. package/Components/River/ComponentsMap/hooks/__tests__/useLoadingState.test.ts +1 -1
  45. package/Components/River/RefreshControl.tsx +9 -3
  46. package/Components/River/RiverItem.tsx +26 -20
  47. package/Components/River/TV/River.tsx +14 -31
  48. package/Components/River/TV/index.tsx +4 -8
  49. package/Components/River/TV/withTVEventHandler.tsx +36 -0
  50. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +1 -0
  51. package/Components/Screen/TV/index.web.tsx +2 -4
  52. package/Components/Screen/__tests__/Screen.test.tsx +43 -65
  53. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +44 -68
  54. package/Components/Screen/hooks.ts +76 -5
  55. package/Components/Screen/index.tsx +10 -3
  56. package/Components/Screen/orientationHandler.ts +3 -3
  57. package/Components/ScreenFeedLoader/ScreenFeedLoader.tsx +46 -0
  58. package/Components/ScreenFeedLoader/__tests__/ScreenFeedLoader.test.tsx +94 -0
  59. package/Components/ScreenFeedLoader/index.ts +1 -0
  60. package/Components/ScreenResolver/__tests__/screenResolver.test.js +24 -0
  61. package/Components/ScreenResolver/hooks/index.ts +3 -0
  62. package/Components/ScreenResolver/hooks/useGetComponent.ts +15 -0
  63. package/Components/ScreenResolver/hooks/useScreenComponentResolver.tsx +90 -0
  64. package/Components/ScreenResolver/index.tsx +9 -115
  65. package/Components/ScreenResolver/utils/__tests__/getScreenTypeProps.test.ts +45 -0
  66. package/Components/ScreenResolver/utils/getScreenTypeProps.ts +43 -0
  67. package/Components/ScreenResolver/utils/index.ts +1 -0
  68. package/Components/ScreenResolver/withDefaultScreenContext.tsx +16 -0
  69. package/Components/ScreenResolverFeedProvider/ScreenResolverFeedProvider.tsx +25 -0
  70. package/Components/ScreenResolverFeedProvider/__tests__/ScreenResolverFeedProvider.test.tsx +44 -0
  71. package/Components/ScreenResolverFeedProvider/index.ts +1 -0
  72. package/Components/ScreenRevealManager/ScreenRevealManager.ts +8 -40
  73. package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +69 -86
  74. package/Components/ScreenRevealManager/withScreenRevealManager.tsx +4 -1
  75. package/Components/Tabs/TabContent.tsx +4 -7
  76. package/Components/Transitioner/Scene.tsx +9 -15
  77. package/Components/Transitioner/index.js +3 -3
  78. package/Components/VideoLive/LiveImageManager.ts +199 -54
  79. package/Components/VideoLive/PlayerLiveImageComponent.tsx +31 -33
  80. package/Components/VideoLive/__tests__/PlayerLiveImageComponent.test.tsx +2 -17
  81. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +5 -5
  82. package/Components/VideoModal/hooks/__tests__/useDelayedPlayerDetails.test.ts +7 -15
  83. package/Components/VideoModal/utils.ts +9 -12
  84. package/Components/Viewport/ViewportEvents/__tests__/viewportEvents.test.js +1 -1
  85. package/Components/ZappFrameworkComponents/BarView/BarView.tsx +6 -4
  86. package/Components/ZappFrameworkComponents/BarView/__tests__/BarView.test.tsx +2 -2
  87. package/Components/ZappUIComponent/index.tsx +12 -6
  88. package/Components/index.js +1 -1
  89. package/Contexts/ScreenContext/__tests__/index.test.tsx +57 -0
  90. package/Contexts/ScreenContext/index.tsx +64 -26
  91. package/Contexts/ScreenTrackedViewPositionsContext/__tests__/index.test.tsx +1 -1
  92. package/Contexts/ZappPipesContext/ZappPipesContextFactory.tsx +18 -7
  93. package/Decorators/Analytics/index.tsx +5 -6
  94. package/Decorators/ConfigurationWrapper/__tests__/__snapshots__/withConfigurationProvider.test.tsx.snap +0 -1
  95. package/Decorators/ConfigurationWrapper/const.ts +0 -1
  96. package/Decorators/ZappPipesDataConnector/ResolverSelector.tsx +25 -7
  97. package/Decorators/ZappPipesDataConnector/__tests__/ResolverSelector.test.tsx +212 -5
  98. package/Decorators/ZappPipesDataConnector/__tests__/zappPipesDataConnector.test.js +1 -1
  99. package/Decorators/ZappPipesDataConnector/index.tsx +2 -2
  100. package/Decorators/ZappPipesDataConnector/resolvers/StaticFeedResolver.tsx +1 -1
  101. package/Helpers/DataSourceHelper/index.js +19 -0
  102. package/events/index.ts +2 -0
  103. package/events/scrollEndReached.ts +15 -0
  104. package/package.json +5 -5
  105. package/Components/MasterCell/DefaultComponents/Text/utils/__tests__/withAdjustedLineHeight.test.ts +0 -46
  106. package/Components/MasterCell/DefaultComponents/Text/utils/index.ts +0 -21
  107. package/Components/PlayerContainer/ErrorDisplay/ErrorDisplay.tsx +0 -57
  108. package/Components/PlayerContainer/ErrorDisplay/index.ts +0 -9
  109. package/Components/PlayerContainer/useRestrictMobilePlayback.tsx +0 -101
  110. package/Components/River/TV/utils/__tests__/toStringOrEmpty.test.ts +0 -30
  111. package/Components/River/TV/utils/index.ts +0 -4
  112. package/Components/River/TV/withFocusableGroupForContent.tsx +0 -71
  113. package/Helpers/DataSourceHelper/__tests__/itemLimitForData.test.ts +0 -80
  114. package/Helpers/DataSourceHelper/index.ts +0 -19
  115. /package/Components/HookRenderer/{index.tsx → index.ts} +0 -0
@@ -7,15 +7,30 @@ import { UrlFeedResolver } from "../resolvers/UrlFeedResolver";
7
7
  import { NullFeedResolver } from "../resolvers/NullFeedResolver";
8
8
 
9
9
  jest.mock("../resolvers/StaticFeedResolver", () => ({
10
- StaticFeedResolver: jest.fn(() => null),
10
+ StaticFeedResolver: jest.fn(({ children }) =>
11
+ children({
12
+ zappPipesData: { loading: true, data: null, error: null },
13
+ reloadData: jest.fn(),
14
+ })
15
+ ),
11
16
  }));
12
17
 
13
18
  jest.mock("../resolvers/UrlFeedResolver", () => ({
14
- UrlFeedResolver: jest.fn(() => null),
19
+ UrlFeedResolver: jest.fn(({ children }) =>
20
+ children({
21
+ zappPipesData: { loading: true, data: null, error: null },
22
+ reloadData: jest.fn(),
23
+ })
24
+ ),
15
25
  }));
16
26
 
17
27
  jest.mock("../resolvers/NullFeedResolver", () => ({
18
- NullFeedResolver: jest.fn(() => null),
28
+ NullFeedResolver: jest.fn(({ children }) =>
29
+ children({
30
+ zappPipesData: { loading: false, data: null, error: null },
31
+ reloadData: jest.fn(),
32
+ })
33
+ ),
19
34
  }));
20
35
 
21
36
  const testPlugin = {
@@ -52,7 +67,7 @@ describe("ResolverSelector", () => {
52
67
  expect.objectContaining({
53
68
  getStaticComponentFeed: props.getStaticComponentFeed,
54
69
  component: mockComponent,
55
- children: mockChildren,
70
+ children: expect.any(Function),
56
71
  }),
57
72
  expect.anything()
58
73
  );
@@ -151,7 +166,10 @@ describe("ResolverSelector", () => {
151
166
  render(<ResolverSelector {...props} />);
152
167
 
153
168
  expect(StaticFeedResolver).toHaveBeenCalledWith(
154
- expect.objectContaining(props),
169
+ expect.objectContaining({
170
+ ...props,
171
+ children: expect.any(Function),
172
+ }),
155
173
  expect.anything()
156
174
  );
157
175
  });
@@ -202,4 +220,193 @@ describe("ResolverSelector", () => {
202
220
  expect(UrlFeedResolver).not.toHaveBeenCalled();
203
221
  expect(NullFeedResolver).not.toHaveBeenCalled();
204
222
  });
223
+
224
+ describe("Fallback Logic", () => {
225
+ it("should fallback to UrlFeedResolver when StaticFeedResolver returns null and feedUrl is present", () => {
226
+ const props = {
227
+ getStaticComponentFeed: jest.fn(),
228
+ feedUrl: "https://example.com/feed",
229
+ component: mockComponent,
230
+ children: mockChildren,
231
+ riverId: "test-river",
232
+ };
233
+
234
+ (StaticFeedResolver as jest.Mock).mockImplementation(({ children }) =>
235
+ children({
236
+ zappPipesData: { loading: false, data: null, error: null },
237
+ reloadData: jest.fn(),
238
+ })
239
+ );
240
+
241
+ render(<ResolverSelector {...props} />);
242
+
243
+ expect(StaticFeedResolver).toHaveBeenCalled();
244
+
245
+ expect(UrlFeedResolver).toHaveBeenCalledWith(
246
+ expect.objectContaining({
247
+ feedUrl: props.feedUrl,
248
+ component: mockComponent,
249
+ children: mockChildren,
250
+ }),
251
+ expect.anything()
252
+ );
253
+ });
254
+
255
+ it("should fallback to UrlFeedResolver when StaticFeedResolver returns null and component.data.source is present", () => {
256
+ const componentWithSource = {
257
+ ...mockComponent,
258
+ data: {
259
+ source: "data-source",
260
+ },
261
+ };
262
+
263
+ const props = {
264
+ getStaticComponentFeed: jest.fn(),
265
+ component: componentWithSource,
266
+ children: mockChildren,
267
+ riverId: "test-river",
268
+ };
269
+
270
+ (StaticFeedResolver as jest.Mock).mockImplementation(({ children }) =>
271
+ children({
272
+ zappPipesData: { loading: false, data: null, error: null },
273
+ reloadData: jest.fn(),
274
+ })
275
+ );
276
+
277
+ render(<ResolverSelector {...props} />);
278
+
279
+ expect(StaticFeedResolver).toHaveBeenCalled();
280
+
281
+ expect(UrlFeedResolver).toHaveBeenCalledWith(
282
+ expect.objectContaining({
283
+ component: componentWithSource,
284
+ children: mockChildren,
285
+ }),
286
+ expect.anything()
287
+ );
288
+ });
289
+
290
+ it("should fallback to NullFeedResolver when StaticFeedResolver returns null and no feedUrl/source is present", () => {
291
+ const props = {
292
+ getStaticComponentFeed: jest.fn(),
293
+ component: mockComponent,
294
+ children: mockChildren,
295
+ riverId: "test-river",
296
+ };
297
+
298
+ (StaticFeedResolver as jest.Mock).mockImplementation(({ children }) =>
299
+ children({
300
+ zappPipesData: { loading: false, data: null, error: null },
301
+ reloadData: jest.fn(),
302
+ })
303
+ );
304
+
305
+ render(<ResolverSelector {...props} />);
306
+
307
+ expect(StaticFeedResolver).toHaveBeenCalled();
308
+ expect(UrlFeedResolver).not.toHaveBeenCalled();
309
+
310
+ expect(NullFeedResolver).toHaveBeenCalledWith(
311
+ expect.objectContaining({
312
+ children: mockChildren,
313
+ }),
314
+ expect.anything()
315
+ );
316
+ });
317
+
318
+ it("should NOT fallback and call children with static data when StaticFeedResolver returns data", () => {
319
+ const staticData = { entry: [{ id: "1" }] };
320
+
321
+ const props = {
322
+ getStaticComponentFeed: jest.fn(),
323
+ feedUrl: "https://example.com/feed",
324
+ component: mockComponent,
325
+ children: mockChildren,
326
+ riverId: "test-river",
327
+ };
328
+
329
+ (StaticFeedResolver as jest.Mock).mockImplementation(({ children }) =>
330
+ children({
331
+ zappPipesData: { loading: false, data: staticData, error: null },
332
+ reloadData: jest.fn(),
333
+ })
334
+ );
335
+
336
+ render(<ResolverSelector {...props} />);
337
+
338
+ expect(StaticFeedResolver).toHaveBeenCalled();
339
+ expect(UrlFeedResolver).not.toHaveBeenCalled();
340
+
341
+ expect(mockChildren).toHaveBeenCalledWith(
342
+ expect.objectContaining({
343
+ zappPipesData: expect.objectContaining({
344
+ data: staticData,
345
+ }),
346
+ })
347
+ );
348
+ });
349
+
350
+ it("should NOT fallback and call children with loading state when StaticFeedResolver is loading", () => {
351
+ const props = {
352
+ getStaticComponentFeed: jest.fn(),
353
+ feedUrl: "https://example.com/feed",
354
+ component: mockComponent,
355
+ children: mockChildren,
356
+ riverId: "test-river",
357
+ };
358
+
359
+ (StaticFeedResolver as jest.Mock).mockImplementation(({ children }) =>
360
+ children({
361
+ zappPipesData: { loading: true, data: null, error: null },
362
+ reloadData: jest.fn(),
363
+ })
364
+ );
365
+
366
+ render(<ResolverSelector {...props} />);
367
+
368
+ expect(StaticFeedResolver).toHaveBeenCalled();
369
+ expect(UrlFeedResolver).not.toHaveBeenCalled();
370
+
371
+ expect(mockChildren).toHaveBeenCalledWith(
372
+ expect.objectContaining({
373
+ zappPipesData: expect.objectContaining({
374
+ loading: true,
375
+ }),
376
+ })
377
+ );
378
+ });
379
+
380
+ it("should NOT fallback and call children with error when StaticFeedResolver returns error", () => {
381
+ const error = new Error("Static error");
382
+
383
+ const props = {
384
+ getStaticComponentFeed: jest.fn(),
385
+ feedUrl: "https://example.com/feed",
386
+ component: mockComponent,
387
+ children: mockChildren,
388
+ riverId: "test-river",
389
+ };
390
+
391
+ (StaticFeedResolver as jest.Mock).mockImplementation(({ children }) =>
392
+ children({
393
+ zappPipesData: { loading: false, data: null, error },
394
+ reloadData: jest.fn(),
395
+ })
396
+ );
397
+
398
+ render(<ResolverSelector {...props} />);
399
+
400
+ expect(StaticFeedResolver).toHaveBeenCalled();
401
+ expect(UrlFeedResolver).not.toHaveBeenCalled();
402
+
403
+ expect(mockChildren).toHaveBeenCalledWith(
404
+ expect.objectContaining({
405
+ zappPipesData: expect.objectContaining({
406
+ error,
407
+ }),
408
+ })
409
+ );
410
+ });
411
+ });
205
412
  });
@@ -3,7 +3,7 @@ import { renderWithProviders } from "@applicaster/zapp-react-native-utils/testUt
3
3
 
4
4
  import * as zappPipesRedux from "@applicaster/zapp-react-native-redux/ZappPipes";
5
5
  import configureStore from "redux-mock-store";
6
- import { thunk } from "redux-thunk";
6
+ import thunk from "redux-thunk";
7
7
 
8
8
  import { zappPipesDataConnector } from "../index";
9
9
 
@@ -2,7 +2,7 @@
2
2
  /// <reference types="@applicaster/zapp-react-native-ui-components" />
3
3
  import React from "react";
4
4
  import { useRoute } from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
5
- import { usePlugins } from "@applicaster/zapp-react-native-redux/hooks";
5
+ import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
6
6
  import { ZappPipesSearchContext } from "@applicaster/zapp-react-native-ui-components/Contexts";
7
7
  import { useScreenContext } from "@applicaster/zapp-react-native-utils/reactHooks/screen/useScreenContext";
8
8
  import { ResolverSelector } from "./ResolverSelector";
@@ -23,7 +23,7 @@ export function zappPipesDataConnector(
23
23
  ) {
24
24
  return function WrappedWithZappPipesData(props: Props) {
25
25
  const { screenData } = useRoute();
26
- const plugins = usePlugins();
26
+ const { plugins } = usePickFromState(["plugins"]);
27
27
  const screenContextData = useScreenContext();
28
28
 
29
29
  const {
@@ -76,7 +76,7 @@ export function StaticFeedResolver({
76
76
 
77
77
  const zappPipesDataProps = useMemo(
78
78
  () => ({
79
- zappPipesData: { url, loading, data, error }, // todo: add applyItemLimit
79
+ zappPipesData: { url, loading, data, error },
80
80
  reloadData,
81
81
  loadNextData: undefined, // Static resolver doesn't support pagination
82
82
  }),
@@ -0,0 +1,19 @@
1
+ import * as R from "ramda";
2
+
3
+ export function itemLimitForData(entry = [], component) {
4
+ const itemLimitValue = Number(R.path(["rules", "item_limit"], component));
5
+
6
+ const itemLimit =
7
+ itemLimitValue && itemLimitValue > 0
8
+ ? itemLimitValue
9
+ : Number.MAX_SAFE_INTEGER;
10
+
11
+ const isInRange = (min, max) => R.both(R.gte(R.__, min), R.lt(R.__, max));
12
+
13
+ const entryShouldBeSliced = (entry) =>
14
+ isInRange(0, R.length(entry))(itemLimit);
15
+
16
+ const sliceEntriesUpToItemLimit = R.slice(0, itemLimit);
17
+
18
+ return R.when(entryShouldBeSliced, sliceEntriesUpToItemLimit)(entry);
19
+ }
package/events/index.ts CHANGED
@@ -7,3 +7,5 @@ export enum QBUIComponentEvents {
7
7
  focusOnSelectedTopMenuItem = "focusOnSelectedTopMenuItem",
8
8
  scrollToTopForScreenWrappedInContainer = "scrollToTopForScreenWrappedInContainer",
9
9
  }
10
+
11
+ export { scrollEndReached$, emitScrollEndReached } from "./scrollEndReached";
@@ -0,0 +1,15 @@
1
+ import { Subject } from "rxjs";
2
+ import { throttleTime } from "rxjs/operators";
3
+
4
+ const SCROLL_END_THROTTLE_MS = 1000;
5
+ const scrollEndReachedSubject = new Subject<void>();
6
+
7
+ /* Throttle so we only emit at most once per second; RN often fires onEndReached repeatedly (e.g. on Android) when near the bottom. */
8
+ export const scrollEndReached$ = scrollEndReachedSubject.pipe(
9
+ throttleTime(SCROLL_END_THROTTLE_MS)
10
+ );
11
+
12
+ /* Call from scroll container (ComponentsMap or Tabs) when scroll reaches end. */
13
+ export const emitScrollEndReached = (): void => {
14
+ scrollEndReachedSubject.next();
15
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applicaster/zapp-react-native-ui-components",
3
- "version": "15.0.0-rc.98",
3
+ "version": "15.1.0-rc.1",
4
4
  "description": "Applicaster Zapp React Native ui components for the Quick Brick App",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -28,10 +28,10 @@
28
28
  },
29
29
  "homepage": "https://github.com/applicaster/quickbrick#readme",
30
30
  "dependencies": {
31
- "@applicaster/applicaster-types": "15.0.0-rc.98",
32
- "@applicaster/zapp-react-native-bridge": "15.0.0-rc.98",
33
- "@applicaster/zapp-react-native-redux": "15.0.0-rc.98",
34
- "@applicaster/zapp-react-native-utils": "15.0.0-rc.98",
31
+ "@applicaster/applicaster-types": "15.1.0-rc.1",
32
+ "@applicaster/zapp-react-native-bridge": "15.1.0-rc.1",
33
+ "@applicaster/zapp-react-native-redux": "15.1.0-rc.1",
34
+ "@applicaster/zapp-react-native-utils": "15.1.0-rc.1",
35
35
  "fast-json-stable-stringify": "^2.1.0",
36
36
  "promise": "^8.3.0",
37
37
  "url": "^0.11.0",
@@ -1,46 +0,0 @@
1
- import { PixelRatio } from "react-native";
2
- import { withAdjustedLineHeight } from "..";
3
-
4
- const FONT_SCALE = 1.118;
5
- jest.spyOn(PixelRatio, "getFontScale").mockReturnValue(FONT_SCALE);
6
-
7
- describe("withAdjustedLineHeight", () => {
8
- it("with provided fontScale and styles", () => {
9
- // setup
10
- const styles = {
11
- lineHeight: 10,
12
- };
13
-
14
- // run
15
- const result = withAdjustedLineHeight(styles);
16
-
17
- // verify
18
- expect(result).toEqual({
19
- lineHeight: styles.lineHeight * FONT_SCALE,
20
- });
21
- });
22
-
23
- it("with provided fontScale and empty styles", () => {
24
- // setup
25
- const styles = {};
26
-
27
- // run
28
- const result = withAdjustedLineHeight(styles);
29
-
30
- // verify
31
- expect(result).toEqual(styles);
32
- });
33
-
34
- it("with non-number lineHeight", () => {
35
- // setup
36
- const styles = {
37
- lineHeight: NaN,
38
- };
39
-
40
- // run
41
- const result = withAdjustedLineHeight(styles);
42
-
43
- // verify
44
- expect(result).toEqual(styles);
45
- });
46
- });
@@ -1,21 +0,0 @@
1
- import { PixelRatio } from "react-native";
2
- import { isNil, identity } from "ramda";
3
- import { toNumber } from "@applicaster/zapp-react-native-utils/numberUtils";
4
- import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
5
-
6
- // HACK - to prevent text flickering
7
- export const withAdjustedLineHeight = (styles) => {
8
- const fontScale = PixelRatio.getFontScale();
9
- const lineHeight = toNumber(styles.lineHeight);
10
-
11
- if (isNil(lineHeight)) {
12
- return styles;
13
- }
14
-
15
- return { ...styles, lineHeight: lineHeight * fontScale };
16
- };
17
-
18
- export const withScaledLineHeight = platformSelect({
19
- ios: withAdjustedLineHeight,
20
- default: identity,
21
- });
@@ -1,57 +0,0 @@
1
- import * as React from "react";
2
- import * as R from "ramda";
3
- import { Text, TextStyle, View, ViewStyle } from "react-native";
4
-
5
- import { getLocalizations } from "@applicaster/zapp-react-native-utils/localizationUtils";
6
- import { getAppStylesColor } from "@applicaster/zapp-react-native-utils/stylesUtils";
7
- import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
8
- import { styleKeys } from "@applicaster/zapp-react-native-utils/styleKeysUtils";
9
-
10
- type Props = {
11
- styles: {};
12
- error: {};
13
- remoteConfigurations: { localizations: {} };
14
- };
15
-
16
- const defaultAppStyles = {
17
- loading_error_label: {
18
- color: "#aaa",
19
- },
20
- };
21
-
22
- const textStyles = (appStyles = defaultAppStyles): TextStyle => ({
23
- color: getAppStylesColor("loading_error_label", appStyles),
24
- fontSize: 36,
25
- textAlign: "center",
26
- });
27
-
28
- const errorStyles = ({ backgroundColor }): ViewStyle => ({
29
- flex: 1,
30
- width: "100%",
31
- height: "100%",
32
- justifyContent: "center",
33
- alignItems: "center",
34
- position: "absolute",
35
- zIndex: 100,
36
- backgroundColor,
37
- });
38
-
39
- export function ErrorDisplayComponent({
40
- styles,
41
- remoteConfigurations: { localizations },
42
- }: Props) {
43
- const theme = useTheme();
44
- const backgroundColor = theme?.app_background_color;
45
-
46
- const { stream_error_message = "Cannot play stream" } = getLocalizations({
47
- localizations,
48
- });
49
-
50
- const appStyles = R.prop(styleKeys.style_namespace, styles);
51
-
52
- return (
53
- <View style={errorStyles({ backgroundColor })}>
54
- <Text style={textStyles(appStyles)}>{stream_error_message}</Text>
55
- </View>
56
- );
57
- }
@@ -1,9 +0,0 @@
1
- import * as R from "ramda";
2
-
3
- import { connectToStore } from "@applicaster/zapp-react-native-redux/utils/connectToStore";
4
-
5
- import { ErrorDisplayComponent } from "./ErrorDisplay";
6
-
7
- export const ErrorDisplay = R.compose(
8
- connectToStore(R.pick(["remoteConfigurations"]))
9
- )(ErrorDisplayComponent);
@@ -1,101 +0,0 @@
1
- import { Player } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/player";
2
- import NetInfo from "@react-native-community/netinfo";
3
-
4
- import { useEffect, useMemo, useRef, useState } from "react";
5
- import { showAlertDialog } from "@applicaster/zapp-react-native-utils/alertUtils";
6
- import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
7
- import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils";
8
- import { log_info } from "./logger";
9
-
10
- type RestrictMobilePlaybackProps = {
11
- player?: Player;
12
- entry?: ZappEntry;
13
- pluginConfiguration?: Record<string, string>;
14
- close: () => void;
15
- };
16
-
17
- export const useRestrictMobilePlayback = ({
18
- player,
19
- entry,
20
- pluginConfiguration,
21
- close,
22
- }: RestrictMobilePlaybackProps): { isRestricted: boolean } => {
23
- const dialogVisibleRef = useRef<boolean>(false);
24
- const theme = useTheme();
25
-
26
- useEffect(() => {
27
- return () => {
28
- if (isTV()) {
29
- return;
30
- }
31
-
32
- dialogVisibleRef.current = false;
33
- };
34
- }, []);
35
-
36
- const isConnectionRestricted = useMemo(() => {
37
- if (isTV()) {
38
- return false;
39
- }
40
-
41
- return player && entry?.extensions?.connection_restricted;
42
- }, [player, entry]);
43
-
44
- const [isRestricted, setIsRestricted] = useState<boolean>(
45
- isConnectionRestricted
46
- );
47
-
48
- useEffect(() => {
49
- if (!isConnectionRestricted) {
50
- return;
51
- }
52
-
53
- const stopPlayer = () => {
54
- log_info(
55
- "Stopping player due to mobile restriction, connection_restricted: true"
56
- );
57
-
58
- player?.close();
59
-
60
- dialogVisibleRef.current = true;
61
-
62
- showAlertDialog({
63
- title:
64
- pluginConfiguration?.mobile_connection_restricted_alert_title ||
65
- "Restricted Connection Type",
66
- message:
67
- pluginConfiguration?.mobile_connection_restricted_alert_message ||
68
- "This content can only be viewed over a Wi-Fi or LAN network.",
69
- okButtonText: theme.ok_button || "OK",
70
- completion: () => {
71
- dialogVisibleRef.current = false;
72
-
73
- close();
74
- },
75
- });
76
- };
77
-
78
- return NetInfo.addEventListener((state) => {
79
- if (state.type === "cellular") {
80
- setIsRestricted(true);
81
-
82
- if (dialogVisibleRef.current) {
83
- return;
84
- }
85
-
86
- stopPlayer();
87
- } else {
88
- setIsRestricted(false);
89
- }
90
- });
91
- }, [
92
- close,
93
- entry?.extensions?.connection_restricted,
94
- pluginConfiguration,
95
- player,
96
- theme.ok_button,
97
- isConnectionRestricted,
98
- ]);
99
-
100
- return { isRestricted };
101
- };
@@ -1,30 +0,0 @@
1
- import { toStringOrEmpty } from "..";
2
-
3
- describe("toStringOrEmpty", () => {
4
- test("returns empty string for undefined", () => {
5
- expect(toStringOrEmpty(undefined)).toBe("");
6
- });
7
-
8
- test("returns empty string for null", () => {
9
- expect(toStringOrEmpty(null)).toBe("");
10
- });
11
-
12
- test("converts number to string", () => {
13
- expect(toStringOrEmpty(0)).toBe("0");
14
- expect(toStringOrEmpty(123)).toBe("123");
15
- expect(toStringOrEmpty(-42)).toBe("-42");
16
- });
17
-
18
- test("returns string as is", () => {
19
- expect(toStringOrEmpty("hello")).toBe("hello");
20
- expect(toStringOrEmpty("")).toBe("");
21
- });
22
-
23
- test("works with numeric strings", () => {
24
- expect(toStringOrEmpty("123")).toBe("123");
25
- });
26
-
27
- test("does not throw on falsy values like 0", () => {
28
- expect(toStringOrEmpty(0)).toBe("0");
29
- });
30
- });
@@ -1,4 +0,0 @@
1
- import { isNil } from "@applicaster/zapp-react-native-utils/utils";
2
-
3
- export const toStringOrEmpty = (value: unknown): string =>
4
- isNil(value) ? "" : String(value);