@applicaster/zapp-react-native-utils 12.0.0-alpha.4201443043 → 12.0.0-alpha.4223432180

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 (64) hide show
  1. package/analyticsUtils/AnalyticPlayerListener.ts +141 -0
  2. package/analyticsUtils/AnalyticsEvents/index.ts +7 -0
  3. package/analyticsUtils/AnalyticsEvents/sendShareEvent.ts +21 -0
  4. package/analyticsUtils/PlayerAnalyticsManager.ts +90 -0
  5. package/analyticsUtils/events.ts +6 -0
  6. package/analyticsUtils/index.tsx +6 -0
  7. package/analyticsUtils/{analyticsTracker.ts → playerAnalyticsTracker.ts} +19 -10
  8. package/appUtils/accessibilityManager/const.ts +50 -0
  9. package/appUtils/accessibilityManager/hooks.ts +40 -0
  10. package/appUtils/accessibilityManager/index.ts +155 -0
  11. package/appUtils/accessibilityManager/logger.ts +5 -0
  12. package/appUtils/focusManager/index.ts +0 -1
  13. package/appUtils/focusManager/treeDataStructure/Utils/index.js +1 -1
  14. package/appUtils/index.js +2 -0
  15. package/appUtils/keyCodes/keys.web.js +8 -0
  16. package/appUtils/keyCodes/keysByPlatform.js +11 -1
  17. package/appUtils/platform/const.ts +25 -0
  18. package/appUtils/platform/index.ts +3 -0
  19. package/appUtils/platform/platformUtils.ts +218 -0
  20. package/appUtils/playerManager/conts.ts +1 -0
  21. package/appUtils/playerManager/index.ts +20 -32
  22. package/appUtils/playerManager/nativePlayerManager.ts +12 -0
  23. package/appUtils/playerManager/player.ts +9 -3
  24. package/appUtils/playerManager/playerHooks/usePlayerCurrentTime.tsx +57 -0
  25. package/appUtils/playerManager/playerNative.ts +6 -15
  26. package/appUtils/playerManager/usePlayerControllerSetup.tsx +2 -1
  27. package/appUtils/playerManager/usePlayerState.tsx +19 -34
  28. package/arrayUtils/index.ts +11 -0
  29. package/booleanUtils/index.ts +4 -4
  30. package/cellUtils/index.ts +38 -5
  31. package/focusManager/FocusManager.ts +20 -5
  32. package/focusManager/__tests__/FocusManager.test.ts +0 -1
  33. package/functionUtils/index.ts +38 -0
  34. package/index.d.ts +15 -0
  35. package/keyboardState/index.ts +0 -0
  36. package/manifestUtils/defaultManifestConfigurations/player.js +121 -0
  37. package/manifestUtils/keys.js +2 -1
  38. package/navigationUtils/index.ts +49 -1
  39. package/numberUtils/__tests__/toNumber.test.ts +2 -2
  40. package/numberUtils/__tests__/toPositiveNumberWithDefault.test.ts +66 -0
  41. package/numberUtils/__tests__/toPositiveNumberWithDefaultZero.test.ts +66 -0
  42. package/numberUtils/index.ts +20 -0
  43. package/package.json +2 -2
  44. package/playerUtils/configurationGenerator.ts +120 -0
  45. package/playerUtils/imaScriptDom.ts +59 -0
  46. package/pluginUtils/index.ts +9 -0
  47. package/reactHooks/cell-click/__tests__/index.test.js +3 -3
  48. package/reactHooks/cell-click/index.ts +17 -3
  49. package/reactHooks/debugging/__tests__/index.test.js +81 -11
  50. package/reactHooks/feed/__tests__/useBatchLoading.test.tsx +69 -6
  51. package/reactHooks/feed/__tests__/useFeedRefresh.test.tsx +6 -2
  52. package/reactHooks/feed/useBatchLoading.ts +52 -28
  53. package/reactHooks/feed/useFeedRefresh.tsx +9 -7
  54. package/reactHooks/layout/useDimensions/__tests__/useDimensions.test.ts +134 -0
  55. package/reactHooks/layout/useDimensions/useDimensions.ts +2 -0
  56. package/reactHooks/navigation/index.ts +14 -8
  57. package/reactHooks/player/useAutoSeek.tsx +9 -7
  58. package/reactHooks/resolvers/useCellResolver.ts +5 -1
  59. package/reactHooks/useConfiguration.ts +2 -2
  60. package/reactUtils/index.ts +104 -0
  61. package/testUtils/index.tsx +42 -9
  62. package/appUtils/focusManager/FocusableEvents.ts +0 -8
  63. package/appUtils/focusManager/treeDataStructure/Utils/__tests__/Utils_android.test.js +0 -1094
  64. package/appUtils/focusManager/treeDataStructure/Utils/index.android.js +0 -261
@@ -45,19 +45,23 @@ const filterEmptyData = (data) => {
45
45
  return true;
46
46
  };
47
47
 
48
- const extractData = compose(uniq, map(dataOf));
48
+ const getData = (rawData) =>
49
+ rawData.component_type === "gallery-qb"
50
+ ? rawData.ui_components[0].data
51
+ : rawData.data;
52
+
53
+ const extractData = compose(uniq, map(getData));
49
54
 
50
55
  export const allFeedsIsReady = (
51
56
  feeds: ZappPipesCollection,
52
57
  urls: (ZappDataSourceSource | null)[]
53
58
  ): boolean =>
54
- urls
55
- .filter(complement(isNil))
56
- .map((url) => (url ? checkFeedIsReady(feeds[url]) : false))
57
- .every(Boolean);
59
+ urls.every((url) =>
60
+ isNil(url) ? true : url && feeds[url] ? checkFeedIsReady(feeds[url]) : false
61
+ );
58
62
 
59
63
  export const useBatchLoading = (
60
- data: { data?: ZappDataSource }[],
64
+ componentsToRender: { data?: ZappDataSource; component_type: string }[],
61
65
  options: Options
62
66
  ) => {
63
67
  const dispatch = useDispatch();
@@ -65,10 +69,18 @@ export const useBatchLoading = (
65
69
  const [searchContext] = ZappPipesSearchContext.useZappPipesContext();
66
70
  const [hasEverBeenReady, setHasEverBeenReady] = React.useState(false);
67
71
 
68
- const takeBatchSize = take(
69
- min(options.initialBatchSize ?? DEFAULT_BATCH_SIZE, data.length)
70
- );
72
+ // if first component is gallery-qb, take only one component for initial load
73
+ const takeSize =
74
+ componentsToRender?.[0]?.component_type === "gallery-qb"
75
+ ? 1
76
+ : min(
77
+ options.initialBatchSize ?? DEFAULT_BATCH_SIZE,
78
+ componentsToRender.length
79
+ );
80
+
81
+ const takeBatchSize = React.useCallback(take(takeSize), [takeSize]);
71
82
 
83
+ // Prepare inflated url for datasource
72
84
  const getUrl = React.useCallback(
73
85
  (data) => {
74
86
  const mappedFeed = data.mapping
@@ -88,13 +100,16 @@ export const useBatchLoading = (
88
100
  [entryContext, screenContext, searchContext]
89
101
  );
90
102
 
91
- const batchData = takeBatchSize(data);
103
+ // components for initial load
104
+ const batchComponents = takeBatchSize(componentsToRender);
92
105
 
106
+ // contains datasources from batch components loading
93
107
  const feedData: ZappDataSource[] = React.useMemo(
94
- () => extractData(batchData).filter(filterEmptyData),
95
- [batchData]
108
+ () => extractData(batchComponents).filter(filterEmptyData),
109
+ [batchComponents]
96
110
  );
97
111
 
112
+ // contains inflated urls from batch components loading
98
113
  const feedUrls: ZappDataSourceSource[] = React.useMemo(
99
114
  () =>
100
115
  feedData.map((data) => {
@@ -102,34 +117,43 @@ export const useBatchLoading = (
102
117
 
103
118
  return mappedFeedUrl;
104
119
  }),
105
- [] // No watchers as batch loaders is used only for initial load
120
+ []
106
121
  );
107
122
 
108
123
  const feeds = useZappPipesFeeds(feedUrls);
109
124
 
110
- React.useEffect(() => {
111
- const datasources = map(dataOf, batchData)
112
- .filter(filterEmptyData)
113
- .filter((data) => {
114
- const feed = feeds[data.source];
125
+ // dispatch loadPipesData for each feed that is not loaded
126
+ const runBatchLoading = React.useCallback(() => {
127
+ batchComponents.forEach((rawData: any) => {
128
+ // 1. get data (dataOf)
115
129
 
116
- if (!feed) return true;
130
+ const data = getData(rawData);
117
131
 
118
- return !feedHasData(feed) && !feedIsLoading(feed);
119
- });
132
+ // 2. filter out empty data filterEmptyData
133
+ if (!filterEmptyData(data)) return false;
120
134
 
121
- datasources.forEach((data) => {
122
- const mappedFeedUrl = getUrl(data);
135
+ // 3. filter out data that is already loaded (feeds[data.source]) & !feedHasData(feed) && !feedIsLoading(feed);
136
+ const feed = feeds[data?.source];
123
137
 
124
- if (mappedFeedUrl) {
125
- return dispatch(
126
- loadPipesData(mappedFeedUrl, { riverId: options.riverId })
127
- );
138
+ if (!feed || (!feedHasData(feed) && !feedIsLoading(feed))) {
139
+ const mappedFeedUrl = getUrl(data);
140
+
141
+ if (mappedFeedUrl) {
142
+ // 4. load data
143
+ return dispatch(
144
+ loadPipesData(mappedFeedUrl, { riverId: options.riverId })
145
+ );
146
+ }
128
147
  }
129
148
  });
130
- }, []); // No watchers as batch loaders is used only for initial load
149
+ }, [feedUrls]);
150
+
151
+ React.useEffect(() => {
152
+ runBatchLoading();
153
+ }, []);
131
154
 
132
155
  React.useEffect(() => {
156
+ // check if all feeds are ready and set hasEverBeenReady to true
133
157
  if (allFeedsIsReady(feeds, feedUrls) && !hasEverBeenReady) {
134
158
  setHasEverBeenReady(true);
135
159
  }
@@ -1,7 +1,8 @@
1
1
  import * as React from "react";
2
- import * as R from "ramda";
2
+ import { path, max } from "ramda";
3
3
  import { isTrue } from "@applicaster/zapp-react-native-utils/booleanUtils";
4
4
  import { toNumberWithDefault } from "@applicaster/zapp-react-native-utils/numberUtils";
5
+ import { useIsScreenActive } from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
5
6
 
6
7
  import { reactHooksLogger } from "../logger";
7
8
  import { ReloadDataFunction } from "./useFeedLoader";
@@ -18,9 +19,8 @@ const minimumRefreshingInterval = 5;
18
19
  const defaultRefreshInterval = 60;
19
20
 
20
21
  export const useFeedRefresh = ({ reloadData, component }: Props): void => {
21
- const enable_data_refreshing = R.path(
22
- ["rules", "enable_data_refreshing"],
23
- component
22
+ const isDataRefreshingEnabled = isTrue(
23
+ path(["rules", "enable_data_refreshing"], component)
24
24
  );
25
25
 
26
26
  const refreshing_interval = toNumberWithDefault(
@@ -29,10 +29,12 @@ export const useFeedRefresh = ({ reloadData, component }: Props): void => {
29
29
  );
30
30
 
31
31
  const refreshingIntervalInMilliseconds =
32
- R.max(refreshing_interval, minimumRefreshingInterval) * 1000;
32
+ max(refreshing_interval, minimumRefreshingInterval) * 1000;
33
+
34
+ const isScreenActive = useIsScreenActive();
33
35
 
34
36
  React.useEffect(() => {
35
- if (isTrue(enable_data_refreshing) && refreshing_interval) {
37
+ if (isDataRefreshingEnabled && refreshing_interval && isScreenActive) {
36
38
  if (refreshing_interval < minimumRefreshingInterval) {
37
39
  feedRefreshLogger.warning({
38
40
  message: `You set your feed refresh interval to ${refreshing_interval} when minimum value is ${minimumRefreshingInterval}seconds.Your feed will refresh after ${minimumRefreshingInterval}seconds.`,
@@ -59,5 +61,5 @@ export const useFeedRefresh = ({ reloadData, component }: Props): void => {
59
61
  clearInterval(refreshInterval);
60
62
  };
61
63
  }
62
- }, [reloadData]);
64
+ }, [reloadData, isScreenActive]);
63
65
  };
@@ -0,0 +1,134 @@
1
+ import { renderHook } from "@testing-library/react-hooks";
2
+ import { Dimensions, StatusBar } from "react-native";
3
+
4
+ const mockUsePickFromState = jest.fn();
5
+ const mockUseIsScreenActive = jest.fn();
6
+ const mockGetInitialDimensions = jest.fn();
7
+ const mockGetDeviceInfo = jest.fn();
8
+
9
+ jest.mock("@applicaster/zapp-react-native-redux/hooks", () => ({
10
+ ...(jest.requireActual("@applicaster/zapp-react-native-redux/hooks") as {}),
11
+ usePickFromState: mockUsePickFromState,
12
+ }));
13
+
14
+ jest.mock("../../../navigation", () => ({
15
+ useIsScreenActive: mockUseIsScreenActive,
16
+ }));
17
+
18
+ jest.mock("../helpers", () => ({
19
+ getInitialDimensions: mockGetInitialDimensions,
20
+ }));
21
+
22
+ jest.mock("../../getDeviceInfo", () => ({
23
+ getDeviceInfo: mockGetDeviceInfo,
24
+ }));
25
+
26
+ const { useDimensions } = require("../useDimensions");
27
+
28
+ describe("useDimensions", () => {
29
+ const mockDimensions = { width: 100, height: 200, scale: 1, fontScale: 1 };
30
+ const mockAppData = { someData: "test" };
31
+
32
+ beforeEach(() => {
33
+ jest.clearAllMocks();
34
+ Dimensions.get = jest.fn().mockReturnValue(mockDimensions);
35
+
36
+ Dimensions.addEventListener = jest.fn().mockReturnValue({
37
+ remove: jest.fn(),
38
+ });
39
+
40
+ mockUsePickFromState.mockReturnValue({ appData: mockAppData });
41
+ mockUseIsScreenActive.mockReturnValue(true);
42
+ mockGetInitialDimensions.mockReturnValue(mockDimensions);
43
+ mockGetDeviceInfo.mockReturnValue({ deviceInfo: "testDeviceInfo" });
44
+ });
45
+
46
+ it("returns correct initial dimensions", () => {
47
+ const { result } = renderHook(() =>
48
+ useDimensions("window", { fullDimensions: false })
49
+ );
50
+
51
+ expect(result.current).toEqual({
52
+ ...mockDimensions,
53
+ statusBarHeight: StatusBar.currentHeight,
54
+ });
55
+
56
+ expect(mockGetInitialDimensions).toHaveBeenCalledWith("window");
57
+ });
58
+
59
+ it("calls handler on mount", () => {
60
+ renderHook(() => useDimensions("window", { fullDimensions: false }));
61
+
62
+ expect(Dimensions.addEventListener).toHaveBeenCalledWith(
63
+ "change",
64
+ expect.any(Function)
65
+ );
66
+ });
67
+
68
+ it("calls handler on isActive change", () => {
69
+ const { rerender } = renderHook(() =>
70
+ useDimensions("window", { fullDimensions: false })
71
+ );
72
+
73
+ mockUseIsScreenActive.mockReturnValue(false);
74
+ rerender();
75
+
76
+ expect(Dimensions.addEventListener).toHaveBeenCalledWith(
77
+ "change",
78
+ expect.any(Function)
79
+ );
80
+ });
81
+
82
+ it("handles fullDimensions option", () => {
83
+ const { result } = renderHook(() =>
84
+ useDimensions("window", { fullDimensions: true })
85
+ );
86
+
87
+ expect(result.current).toEqual({
88
+ ...mockDimensions,
89
+ scale: 1,
90
+ fontScale: 1,
91
+ statusBarHeight: StatusBar.currentHeight,
92
+ });
93
+ });
94
+
95
+ it("handles excludeStatusBar option", () => {
96
+ const { result } = renderHook(() =>
97
+ useDimensions("window", { excludeStatusBar: true })
98
+ );
99
+
100
+ expect(result.current.height).toBe(
101
+ mockDimensions.height - StatusBar.currentHeight ?? 0
102
+ );
103
+ });
104
+
105
+ it("handles rounded option", () => {
106
+ const { result } = renderHook(() =>
107
+ useDimensions("window", { rounded: true })
108
+ );
109
+
110
+ expect(result.current.width).toBe(Math.ceil(mockDimensions.width));
111
+ expect(result.current.height).toBe(Math.ceil(mockDimensions.height));
112
+ });
113
+
114
+ it("handles deviceInfo option", () => {
115
+ const { result } = renderHook(() =>
116
+ useDimensions("window", { deviceInfo: true })
117
+ );
118
+
119
+ expect(result.current.deviceInfo).toMatchObject({
120
+ deviceInfo: "testDeviceInfo",
121
+ });
122
+ });
123
+
124
+ it("logs deprecation warning when fullDimensions is boolean", () => {
125
+ const consoleWarnSpy = jest.spyOn(console, "warn").mockImplementation();
126
+ renderHook(() => useDimensions("window", true));
127
+
128
+ expect(consoleWarnSpy).toHaveBeenCalledWith(
129
+ "Deprecation Warning\nSecond argument is now the options object. {fullDimensions: boolean, ...}"
130
+ );
131
+
132
+ consoleWarnSpy.mockRestore();
133
+ });
134
+ });
@@ -95,6 +95,8 @@ export const useDimensions: UseDimensions = (
95
95
 
96
96
  const activeSubscription = Dimensions.addEventListener("change", handler);
97
97
 
98
+ handler({ [context]: Dimensions.get(context) });
99
+
98
100
  return () => {
99
101
  activeSubscription.remove();
100
102
  };
@@ -4,7 +4,6 @@
4
4
  import React, { useContext, useEffect, useMemo } from "react";
5
5
  import { BackHandler, InteractionManager, NativeModules } from "react-native";
6
6
  import { useSafeAreaInsets } from "react-native-safe-area-context";
7
- import { isEmpty } from "ramda";
8
7
 
9
8
  import {
10
9
  useContentTypes,
@@ -18,7 +17,7 @@ import {
18
17
  QUICK_BRICK_NAVBAR,
19
18
  QUICK_BRICK_CONTENT,
20
19
  } from "@applicaster/quick-brick-core/const";
21
- import { useZappHooksModalState } from "@applicaster/zapp-react-dom-ui-components/Components/ZappHooksModal/ZappHooksModal";
20
+ import { useZappHooksModalState } from "@applicaster/zapp-react-dom-ui-components/Components/ZappHooksModal/hooks/useZappHooksModalState";
22
21
  import { ZappHookModalContext } from "@applicaster/zapp-react-native-ui-components/Contexts";
23
22
  import { PathnameContext } from "@applicaster/zapp-react-native-ui-components/Contexts/PathnameContext";
24
23
  import { HookModalContextT } from "@applicaster/zapp-react-native-ui-components/Contexts/ZappHookModalContext";
@@ -32,7 +31,7 @@ import { platformSelect } from "../../reactUtils";
32
31
  import { useConnectionInfo } from "../connection";
33
32
  import { getTargetScreenData } from "../screen/useTargetScreenData";
34
33
 
35
- import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils";
34
+ import { isTV, isWeb } from "@applicaster/zapp-react-native-utils/reactUtils";
36
35
  import { useNavbarState } from "../screen";
37
36
  import { useNavigation } from "./useNavigation";
38
37
  import { useModalStore } from "../../modalState";
@@ -79,6 +78,10 @@ export function isNavBarVisible(
79
78
  return false;
80
79
  }
81
80
 
81
+ if (screenData?.plugin_type === "login" && isTV()) {
82
+ return false;
83
+ }
84
+
82
85
  /* Match if screen is Hook */
83
86
  if (route.startsWith("/hooks/")) {
84
87
  const module = screenData?.module;
@@ -211,10 +214,7 @@ export const useRoute = (): {
211
214
  return { screenData: screenDataContext, pathname };
212
215
  }
213
216
 
214
- // Cover cases where screenContext is used before navigation is initialized
215
- const screenData = isEmpty(screenDataContext)
216
- ? navigator.screenData
217
- : screenDataContext;
217
+ const screenData = screenDataContext;
218
218
 
219
219
  return { screenData, pathname };
220
220
  };
@@ -466,7 +466,13 @@ export const useUniqueRouteSuffix = (): Return => {
466
466
  const suffix = `currentRoute:${currentRoute}_pathname:${pathname}`;
467
467
 
468
468
  const withUniqueRouteSuffix = React.useCallback((prefix: string) => {
469
- return `${prefix}___${suffix}`;
469
+ if (isWeb()) {
470
+ // we should use extended route prefix only on Web, where we have stack-navigator
471
+ return `${prefix}___${suffix}`;
472
+ }
473
+
474
+ // for other platforms use previous version
475
+ return prefix;
470
476
  }, []);
471
477
 
472
478
  return withUniqueRouteSuffix;
@@ -1,5 +1,4 @@
1
1
  import React, { useEffect, useState } from "react";
2
- import * as R from "ramda";
3
2
  import { usePrevious } from "@applicaster/zapp-react-native-utils/reactHooks/utils";
4
3
  import { playerManager } from "@applicaster/zapp-react-native-utils/appUtils/playerManager";
5
4
  import {
@@ -17,10 +16,6 @@ export const useAutoSeek = (resetHUDTimer = noop) => {
17
16
  const intervalTimer = React.useRef(null);
18
17
  const prevRunning = usePrevious(isRunning);
19
18
 
20
- const setIntervalTimer = (cb) => {
21
- intervalTimer.current = setInterval(cb, ON_HOLD_INTERVAL);
22
- };
23
-
24
19
  const clearIntervalTimer = () => {
25
20
  try {
26
21
  clearInterval(intervalTimer.current);
@@ -29,6 +24,11 @@ export const useAutoSeek = (resetHUDTimer = noop) => {
29
24
  }
30
25
  };
31
26
 
27
+ const setIntervalTimer = (cb) => {
28
+ clearIntervalTimer();
29
+ intervalTimer.current = setInterval(cb, ON_HOLD_INTERVAL);
30
+ };
31
+
32
32
  const resetState = () => {
33
33
  setIsRunning(false);
34
34
  setDirection(null);
@@ -42,7 +42,7 @@ export const useAutoSeek = (resetHUDTimer = noop) => {
42
42
  if (!player) return;
43
43
 
44
44
  if (direction) {
45
- player[direction](R.min(MAX_SEEK_TIME, SKIP_TIME_BASE * multiplier));
45
+ player[direction](Math.min(MAX_SEEK_TIME, SKIP_TIME_BASE * multiplier));
46
46
  resetHUDTimer();
47
47
  }
48
48
  }, [multiplier, direction]);
@@ -50,7 +50,9 @@ export const useAutoSeek = (resetHUDTimer = noop) => {
50
50
  useEffect(() => {
51
51
  if (isRunning && isRunning !== prevRunning) {
52
52
  setIntervalTimer(() => {
53
- setMultiplier(R.add(1));
53
+ setMultiplier((prev) => {
54
+ return prev + 1;
55
+ });
54
56
  });
55
57
  } else if (!isRunning && isRunning !== prevRunning) {
56
58
  resetState();
@@ -48,7 +48,11 @@ export function useCellResolver({
48
48
  ...cellResolverOptions
49
49
  }: Props) {
50
50
  let _component = Object.assign({}, component);
51
- const { width, height } = useDimensions("screen");
51
+
52
+ const { width, height } = useDimensions("screen", {
53
+ updateForInactiveScreens: false,
54
+ });
55
+
52
56
  const { plugins, cellStyles } = usePickFromState(["plugins", "cellStyles"]);
53
57
  const isRTL = useIsRTL();
54
58
 
@@ -67,7 +67,7 @@ export const useConfiguration = ({
67
67
  if (component) {
68
68
  const remappedStylesConfiguration = remapUpdatedKeys(
69
69
  pluginManifest,
70
- component.styles
70
+ component
71
71
  );
72
72
 
73
73
  R.forEachObjIndexed((value, key) => {
@@ -83,7 +83,7 @@ export const useConfiguration = ({
83
83
 
84
84
  const styles = populateConfigurationValues(
85
85
  pluginManifest?.styles?.fields,
86
- remappedStylesConfiguration,
86
+ remappedStylesConfiguration.styles,
87
87
  skipDefaults
88
88
  );
89
89
 
@@ -108,3 +108,107 @@ export const isAndroidPlatform = () => Platform.OS === "android";
108
108
  export const isTvOSPlatform = () => getPlatform() === "tvos";
109
109
 
110
110
  export const isAndroidTVPlatform = () => getPlatform() === "android_tv";
111
+
112
+ declare global {
113
+ interface Window {
114
+ VIZIO?: {
115
+ Chromevox: {
116
+ play: (text: string) => void;
117
+ };
118
+ exitApplication: () => void;
119
+ setClosedCaptionHandler: (
120
+ callback: (isCCEnabled: boolean) => void
121
+ ) => void;
122
+ };
123
+ }
124
+ }
125
+
126
+ /**
127
+ * Platforms found in userAgent string, please note that simulators
128
+ * might not have the same agents that the devices have
129
+ */
130
+ export const PLATFORMS = {
131
+ webos: "Web0S",
132
+ tizen: "Tizen",
133
+ samsung: "SMART-TV",
134
+ mac: "Mac",
135
+ win: "Win",
136
+ linux: "Linux",
137
+ iphone: "iPhone",
138
+ android: "Android",
139
+ mobile: "Mobile",
140
+ vizio: "VIZIO",
141
+ smartcast: "SmartCast",
142
+ conjure: "Conjure",
143
+ };
144
+
145
+ /**
146
+ * Simple way of identifying if we have access to the tizen APIs
147
+ */
148
+ export const hasTizen = typeof window?.tizen !== "undefined";
149
+
150
+ /**
151
+ * Simple way of identifying if we have access to the webOS APIs
152
+ */
153
+ export const hasWebOS = typeof window?.webOS !== "undefined";
154
+
155
+ export const getUserAgent = () => window?.navigator?.userAgent;
156
+
157
+ export const isLgPlatform = () => {
158
+ const userAgent = getUserAgent();
159
+
160
+ if (!userAgent) {
161
+ return false;
162
+ }
163
+
164
+ // Note: LG uses the number 0 in their userAgent, possibly because of Palm WebOS
165
+ return hasWebOS || userAgent.includes(PLATFORMS.webos);
166
+ };
167
+
168
+ export const isSamsungPlatform = () => {
169
+ const userAgent = getUserAgent();
170
+
171
+ if (!userAgent) {
172
+ return false;
173
+ }
174
+
175
+ return (
176
+ hasTizen ||
177
+ userAgent.includes(PLATFORMS.samsung) ||
178
+ userAgent.includes(PLATFORMS.tizen)
179
+ );
180
+ };
181
+
182
+ /**
183
+ * Checks if we are on the Vizio platform by using the userAgent string
184
+ *
185
+ * Use this method when you need to identify the platform on runtime
186
+ * before the VIZIO Companion Library has loaded
187
+ */
188
+ export const isVizioPlatform = () => {
189
+ const userAgent = getUserAgent();
190
+
191
+ if (!userAgent) {
192
+ return false;
193
+ }
194
+
195
+ const isVizioAgent =
196
+ userAgent.includes(PLATFORMS.vizio) ||
197
+ userAgent.includes(PLATFORMS.smartcast) ||
198
+ userAgent.includes(PLATFORMS.conjure);
199
+
200
+ return isVizioAgent;
201
+ };
202
+
203
+ /**
204
+ * Checks if we are on the Vizio platform by checking for the global VIZIO object
205
+ * and the VIZIO Companion Library. This method does not use the userAgent string,
206
+ * because it mainly checks whether we have access to apis, which may not be available yet
207
+ */
208
+ export const hasVizioAPIs = () => {
209
+ const hasAPIs =
210
+ typeof window.VIZIO !== "undefined" &&
211
+ window?.applicaster?.vizioLibraryDidLoad;
212
+
213
+ return hasAPIs;
214
+ };
@@ -7,9 +7,11 @@ import { Provider } from "react-redux";
7
7
  import thunk from "redux-thunk";
8
8
  import configureStore from "redux-mock-store";
9
9
  import { SafeAreaProvider } from "react-native-safe-area-context";
10
+ import { appStore } from "@applicaster/zapp-react-native-redux/AppStore";
10
11
 
11
12
  import { render } from "@testing-library/react-native";
12
13
  import { AnalyticsProvider } from "../analyticsUtils";
14
+ import { ThemeContext } from "../theme";
13
15
 
14
16
  export { getByTestId } from "./getByTestId";
15
17
 
@@ -59,7 +61,9 @@ const initialState = {
59
61
  appSettings: {},
60
62
  plugins: [],
61
63
  appState: { appReady: true, appLaunched: true },
64
+ pluginConfigurations: {},
62
65
  appData: { layoutVersion: "v1" },
66
+ zappPipes: {},
63
67
  contentTypes: null,
64
68
  };
65
69
 
@@ -68,20 +72,49 @@ export const WrappedWithProviders = ({
68
72
  store: storeObj = {},
69
73
  }: any) => {
70
74
  const _store = configureStore([thunk])(
71
- R.mergeDeepWith(R.concat, { ...initialState }, storeObj)
75
+ R.mergeDeepRight(initialState, { ...storeObj })
72
76
  );
73
77
 
78
+ appStore.set(_store);
79
+
74
80
  return (
75
- <SafeAreaProvider
76
- initialMetrics={{
77
- insets: { top: 0, left: 0, right: 0, bottom: 0 },
78
- frame: { x: 0, y: 0, width: 0, height: 0 },
81
+ <ThemeContext.Provider
82
+ value={{
83
+ themes: {
84
+ light: {
85
+ component_margin_top: 0,
86
+ component_margin_bottom: 0,
87
+ component_margin_left: 0,
88
+ component_margin_right: 0,
89
+ component_padding_top: 0,
90
+ component_padding_bottom: 0,
91
+ component_padding_left: 0,
92
+ component_padding_right: 0,
93
+ component_gutter_vertical: 0,
94
+ component_gutter_horizontal: 0,
95
+ component_corner_radius: 0,
96
+ app_background_color: "rgba(0,0,0,0)",
97
+ screen_margin_top: 0,
98
+ screen_margin_bottom: 0,
99
+ component_anchor_point_y: 0,
100
+ assets: "",
101
+ },
102
+ },
103
+ selectedThemeId: "light",
104
+ setSelectedThemeId: () => {},
79
105
  }}
80
106
  >
81
- <Provider store={_store}>
82
- <AnalyticsProvider>{children}</AnalyticsProvider>
83
- </Provider>
84
- </SafeAreaProvider>
107
+ <SafeAreaProvider
108
+ initialMetrics={{
109
+ insets: { top: 0, left: 0, right: 0, bottom: 0 },
110
+ frame: { x: 0, y: 0, width: 0, height: 0 },
111
+ }}
112
+ >
113
+ <Provider store={_store}>
114
+ <AnalyticsProvider>{children}</AnalyticsProvider>
115
+ </Provider>
116
+ </SafeAreaProvider>
117
+ </ThemeContext.Provider>
85
118
  );
86
119
  };
87
120
 
@@ -1,8 +0,0 @@
1
- export const FOCUSABLE_EVENTS = {
2
- IS_REGISTERED: "isRegistered",
3
- WILL_RECEIVE_FOCUS: "willReceiveFocus",
4
- HAS_RECEIVED_FOCUS: "hasReceivedFocus",
5
- WILL_LOSE_FOCUS: "willLoseFocus",
6
- HAS_LOST_FOCUS: "hasLostFocus",
7
- WILL_UNREGISTER: "willUnregister",
8
- };