@applicaster/zapp-react-native-utils 12.0.0-alpha.6115034951 → 12.0.0-alpha.6158046395

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 (44) hide show
  1. package/analyticsUtils/AnalyticPlayerListener.ts +141 -0
  2. package/analyticsUtils/PlayerAnalyticsManager.ts +90 -0
  3. package/analyticsUtils/{analyticsTracker.ts → playerAnalyticsTracker.ts} +19 -10
  4. package/appUtils/accessibilityManager/const.ts +50 -0
  5. package/appUtils/accessibilityManager/hooks.ts +40 -0
  6. package/appUtils/accessibilityManager/index.ts +155 -0
  7. package/appUtils/accessibilityManager/logger.ts +5 -0
  8. package/appUtils/focusManager/index.ts +0 -1
  9. package/appUtils/index.js +2 -0
  10. package/appUtils/platform/platformUtils.ts +74 -79
  11. package/appUtils/playerManager/index.ts +9 -27
  12. package/appUtils/playerManager/player.ts +1 -0
  13. package/appUtils/playerManager/playerHooks/usePlayerCurrentTime.tsx +57 -0
  14. package/appUtils/playerManager/playerNative.ts +1 -1
  15. package/appUtils/playerManager/usePlayerControllerSetup.tsx +2 -1
  16. package/appUtils/playerManager/usePlayerState.tsx +19 -34
  17. package/arrayUtils/index.ts +11 -0
  18. package/cellUtils/index.ts +38 -5
  19. package/focusManager/FocusManager.ts +20 -5
  20. package/focusManager/__tests__/FocusManager.test.ts +0 -1
  21. package/functionUtils/index.ts +38 -0
  22. package/index.d.ts +15 -0
  23. package/manifestUtils/defaultManifestConfigurations/player.js +121 -0
  24. package/manifestUtils/keys.js +2 -1
  25. package/navigationUtils/index.ts +1 -1
  26. package/numberUtils/__tests__/toNumber.test.ts +2 -2
  27. package/numberUtils/__tests__/toPositiveNumberWithDefault.test.ts +66 -0
  28. package/numberUtils/__tests__/toPositiveNumberWithDefaultZero.test.ts +66 -0
  29. package/numberUtils/index.ts +20 -0
  30. package/package.json +2 -2
  31. package/playerUtils/configurationGenerator.ts +120 -0
  32. package/playerUtils/imaScriptDom.ts +59 -0
  33. package/reactHooks/cell-click/index.ts +17 -3
  34. package/reactHooks/debugging/__tests__/index.test.js +81 -11
  35. package/reactHooks/feed/__tests__/useBatchLoading.test.tsx +69 -6
  36. package/reactHooks/feed/useBatchLoading.ts +52 -28
  37. package/reactHooks/layout/useDimensions/__tests__/useDimensions.test.ts +134 -0
  38. package/reactHooks/layout/useDimensions/useDimensions.ts +2 -0
  39. package/reactHooks/navigation/index.ts +1 -1
  40. package/reactHooks/player/useAutoSeek.tsx +9 -7
  41. package/reactHooks/resolvers/useCellResolver.ts +5 -1
  42. package/reactHooks/useConfiguration.ts +2 -2
  43. package/reactUtils/index.ts +4 -2
  44. package/testUtils/index.tsx +42 -9
@@ -4,38 +4,77 @@ import {
4
4
  timers,
5
5
  } from "@applicaster/zapp-react-native-utils/reactHooks/debugging";
6
6
 
7
- jest.useFakeTimers();
8
-
9
7
  describe("Debug utils", () => {
8
+ let performanceNowMock;
9
+
10
+ beforeEach(() => {
11
+ jest.useFakeTimers();
12
+ // Clear the timers object
13
+ Object.keys(timers).forEach((key) => delete timers[key]);
14
+
15
+ // Mock performance.now()
16
+ // eslint-disable-next-line no-undef
17
+ performanceNowMock = jest.spyOn(performance, "now");
18
+ performanceNowMock.mockReturnValue(0); // Initial value
19
+ });
20
+
21
+ afterEach(() => {
22
+ jest.useRealTimers();
23
+ jest.restoreAllMocks();
24
+ performanceNowMock.mockRestore();
25
+ });
26
+
10
27
  describe("Unit Test: time", () => {
11
28
  it("should create new timer if timer with a label does not exist", () => {
29
+ performanceNowMock.mockReturnValue(1000);
12
30
  time("timer1");
13
31
  expect(timers["timer1"]).toBeDefined();
32
+ expect(timers["timer1"].startTime).toBe(1000);
14
33
  });
15
34
 
16
35
  it("should update start time of timer if timer with a label exists", () => {
36
+ // First call to time()
37
+ performanceNowMock.mockReturnValue(1000);
38
+ time("timer1");
17
39
  const previousStartTime = timers["timer1"].startTime;
18
- jest.advanceTimersByTime(1000);
40
+
41
+ // Second call to time()
42
+ performanceNowMock.mockReturnValue(2000);
19
43
  time("timer1");
20
44
  const currentStartTime = timers["timer1"].startTime;
21
- expect(previousStartTime).not.toEqual(currentStartTime);
45
+
46
+ expect(previousStartTime).toBe(1000);
47
+ expect(currentStartTime).toBe(2000);
22
48
  });
23
49
  });
24
50
 
25
51
  describe("Unit Test: timeEnd", () => {
26
- beforeEach(() => jest.resetAllMocks());
52
+ beforeEach(() => {
53
+ jest.spyOn(console, "log").mockImplementation(() => {});
54
+ });
27
55
 
28
56
  it("should log elapsed time", () => {
29
57
  const logSpy = jest.spyOn(console, "log");
30
58
 
59
+ performanceNowMock.mockReturnValue(1000);
60
+ time("timer1");
61
+
62
+ performanceNowMock.mockReturnValue(2000);
31
63
  timeEnd("timer1");
32
64
 
33
- expect(logSpy).toHaveBeenCalled();
65
+ expect(logSpy).toHaveBeenCalledWith(
66
+ "timer1: 1000.000ms 2",
67
+ expect.any(Object)
68
+ );
34
69
  });
35
70
 
36
71
  it("should not log elapsed time if accumulative: true in settings", () => {
37
72
  const logSpy = jest.spyOn(console, "log");
38
73
 
74
+ performanceNowMock.mockReturnValue(1000);
75
+ time("timer1");
76
+
77
+ performanceNowMock.mockReturnValue(2000);
39
78
  timeEnd("timer1", { accumulative: true });
40
79
 
41
80
  expect(logSpy).not.toHaveBeenCalled();
@@ -43,21 +82,52 @@ describe("Debug utils", () => {
43
82
  });
44
83
 
45
84
  describe("Integration Test: time and timeEnd", () => {
46
- beforeEach(() => jest.resetAllMocks());
85
+ beforeEach(() => {
86
+ jest.spyOn(console, "log").mockImplementation(() => {});
87
+ });
47
88
 
48
89
  it("should log elapsed time on timeEnd call", () => {
49
90
  const logSpy = jest.spyOn(console, "log");
91
+
92
+ performanceNowMock.mockReturnValue(1000);
50
93
  time("timer1");
94
+
95
+ performanceNowMock.mockReturnValue(2000);
51
96
  timeEnd("timer1");
52
- expect(logSpy).toHaveBeenCalled();
97
+
98
+ expect(logSpy).toHaveBeenCalledWith(
99
+ "timer1: 1000.000ms 2",
100
+ expect.objectContaining({
101
+ label: "timer1",
102
+ settings: undefined,
103
+ })
104
+ );
53
105
  });
54
106
 
55
- it("should log total and average time on timeEnd call without accumulative option", () => {
107
+ it("should log total and average time when accumulative timing is complete", () => {
56
108
  const logSpy = jest.spyOn(console, "log");
109
+
110
+ // First timing
111
+ performanceNowMock.mockReturnValue(1000);
57
112
  time("timer2");
113
+
114
+ performanceNowMock.mockReturnValue(2000);
58
115
  timeEnd("timer2", { accumulative: true });
59
- timeEnd("timer2");
60
- expect(logSpy).toHaveBeenCalled();
116
+
117
+ // Second timing
118
+ performanceNowMock.mockReturnValue(3000);
119
+ time("timer2");
120
+
121
+ performanceNowMock.mockReturnValue(5000);
122
+ timeEnd("timer2"); // This will log because timer is accumulative (callCount > 1)
123
+
124
+ expect(logSpy).toHaveBeenCalledWith(
125
+ "timer2: 2000.000ms 2",
126
+ expect.objectContaining({
127
+ label: "timer2",
128
+ settings: undefined,
129
+ })
130
+ );
61
131
  });
62
132
  });
63
133
  });
@@ -77,8 +77,6 @@ describe("useBatchLoading", () => {
77
77
 
78
78
  const actions = store.getActions();
79
79
 
80
- expect(useDispatchSpy).toBeCalledTimes(1);
81
-
82
80
  expect(actions).toHaveLength(2);
83
81
 
84
82
  expect(actions[0]).toMatchObject({
@@ -129,6 +127,8 @@ describe("useBatchLoading", () => {
129
127
  // ... more items
130
128
  ];
131
129
 
130
+ expect(useDispatchSpy).toBeCalledTimes(0);
131
+
132
132
  renderHook(() => useBatchLoading(data, { initialBatchSize, riverId }), {
133
133
  wrapper,
134
134
  initialProps: { store },
@@ -136,8 +136,6 @@ describe("useBatchLoading", () => {
136
136
 
137
137
  const actions = store.getActions();
138
138
 
139
- expect(useDispatchSpy).toBeCalledTimes(1);
140
-
141
139
  expect(actions).toHaveLength(1);
142
140
 
143
141
  expect(actions[0]).toMatchObject({
@@ -172,8 +170,6 @@ describe("useBatchLoading", () => {
172
170
  initialProps: { store },
173
171
  });
174
172
 
175
- expect(useDispatchSpy).toBeCalledTimes(1);
176
-
177
173
  const actions = store.getActions();
178
174
 
179
175
  expect(actions).toHaveLength(3);
@@ -218,6 +214,73 @@ describe("useBatchLoading", () => {
218
214
 
219
215
  expect(result.current).toBe(true);
220
216
  });
217
+
218
+ it("gallery-qb: loadPipesData should be called only once for first component in the gallery", () => {
219
+ const store = mockStore({
220
+ zappPipes: {},
221
+ test: "true",
222
+ });
223
+
224
+ useDispatchSpy.mockReturnValue(store.dispatch);
225
+
226
+ const initialBatchSize = 3;
227
+ const riverId = "123";
228
+
229
+ const data = [
230
+ {
231
+ component_type: "gallery-qb",
232
+ ui_components: [{ data: { source: "url1" } }],
233
+ },
234
+ { data: { source: "url2" } },
235
+ { data: { source: "url3" } },
236
+ { data: { source: "url4" } },
237
+ { data: { source: "url5" } },
238
+ { data: { source: "url6" } },
239
+ // ... more items
240
+ ];
241
+
242
+ renderHook(() => useBatchLoading(data, { initialBatchSize, riverId }), {
243
+ wrapper,
244
+ initialProps: { store },
245
+ });
246
+
247
+ const actions = store.getActions();
248
+
249
+ expect(actions).toHaveLength(1);
250
+ });
251
+
252
+ it("gallery-qb: initial batch ready when all initial items loaded", () => {
253
+ const store = mockStore({
254
+ zappPipes: {
255
+ url1: {
256
+ loading: false,
257
+ error: false,
258
+ data: {},
259
+ },
260
+ },
261
+ });
262
+
263
+ useDispatchSpy.mockReturnValue(store.dispatch);
264
+
265
+ const initialBatchSize = 3;
266
+ const riverId = "123";
267
+
268
+ const data: Partial<ZappUIComponent>[] = [
269
+ {
270
+ component_type: "gallery-qb",
271
+ ui_components: [{ data: { source: "url1" } }],
272
+ },
273
+ { data: { source: "url2" } },
274
+ { data: { source: "url3" } },
275
+ ];
276
+
277
+ const { result } = renderHook(
278
+ () => useBatchLoading(data, { initialBatchSize, riverId }),
279
+ { wrapper, initialProps: { store } }
280
+ );
281
+
282
+ expect(result.current).toBe(true);
283
+ });
221
284
  });
222
285
 
223
286
  describe("allFeedsIsReady", () => {
@@ -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
  }
@@ -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
  };
@@ -17,7 +17,7 @@ import {
17
17
  QUICK_BRICK_NAVBAR,
18
18
  QUICK_BRICK_CONTENT,
19
19
  } from "@applicaster/quick-brick-core/const";
20
- 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";
21
21
  import { ZappHookModalContext } from "@applicaster/zapp-react-native-ui-components/Contexts";
22
22
  import { PathnameContext } from "@applicaster/zapp-react-native-ui-components/Contexts/PathnameContext";
23
23
  import { HookModalContextT } from "@applicaster/zapp-react-native-ui-components/Contexts/ZappHookModalContext";
@@ -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
 
@@ -112,6 +112,9 @@ export const isAndroidTVPlatform = () => getPlatform() === "android_tv";
112
112
  declare global {
113
113
  interface Window {
114
114
  VIZIO?: {
115
+ Chromevox: {
116
+ play: (text: string) => void;
117
+ };
115
118
  exitApplication: () => void;
116
119
  setClosedCaptionHandler: (
117
120
  callback: (isCCEnabled: boolean) => void
@@ -149,8 +152,7 @@ export const hasTizen = typeof window?.tizen !== "undefined";
149
152
  */
150
153
  export const hasWebOS = typeof window?.webOS !== "undefined";
151
154
 
152
- export const getUserAgent = () =>
153
- window?.navigator?.userAgent || global?.navigator?.userAgent;
155
+ export const getUserAgent = () => window?.navigator?.userAgent;
154
156
 
155
157
  export const isLgPlatform = () => {
156
158
  const userAgent = getUserAgent();