@applicaster/zapp-react-native-utils 13.0.0-alpha.7019824418 → 13.0.0-alpha.7048001189

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 (49) hide show
  1. package/actionsExecutor/ActionExecutorContext.tsx +104 -6
  2. package/analyticsUtils/currentScreenProperities.ts +6 -4
  3. package/appUtils/HooksManager/Hook.ts +4 -0
  4. package/appUtils/HooksManager/index.ts +3 -1
  5. package/appUtils/contextKeysManager/contextResolver.ts +1 -12
  6. package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +1 -1
  7. package/appUtils/focusManager/__tests__/focusManager.test.js +0 -82
  8. package/appUtils/focusManager/__tests__/index.test.ts +0 -1
  9. package/appUtils/focusManager/index.ts +17 -26
  10. package/appUtils/focusManager/utils/index.ts +0 -14
  11. package/appUtils/focusManagerAux/index.ts +2 -2
  12. package/appUtils/keyCodes/index.ts +11 -7
  13. package/appUtils/orientationHelper.ts +11 -13
  14. package/appUtils/playerManager/index.ts +1 -1
  15. package/appUtils/playerManager/player.ts +24 -12
  16. package/appUtils/playerManager/playerNative.ts +18 -3
  17. package/appUtils/playerManager/usePlayerTitleSummaryOverlay.tsx +7 -1
  18. package/dateUtils/index.ts +2 -0
  19. package/focusManager/FocusManager.ts +3 -1
  20. package/focusManager/index.ts +3 -21
  21. package/functionUtils/index.ts +46 -8
  22. package/index.d.ts +0 -12
  23. package/manifestUtils/defaultManifestConfigurations/player.js +8 -0
  24. package/manifestUtils/fonts/index.js +5 -6
  25. package/manifestUtils/sharedConfiguration/screenPicker/stylesFields.js +1 -1
  26. package/modalState/__tests__/index.test.ts +51 -0
  27. package/modalState/index.ts +32 -2
  28. package/numberUtils/__tests__/toPositiveNumberWithDefault.test.ts +23 -3
  29. package/numberUtils/index.ts +11 -5
  30. package/package.json +2 -6
  31. package/playerUtils/configurationGenerator.ts +8 -0
  32. package/reactHooks/advertising/index.ts +5 -5
  33. package/reactHooks/autoscrolling/__tests__/useTrackedView.test.tsx +5 -7
  34. package/reactHooks/autoscrolling/useTrackedView.ts +12 -1
  35. package/reactHooks/feed/useFeedLoader.tsx +4 -2
  36. package/reactHooks/hookModal/hooks/useHookModalScreenData.ts +39 -0
  37. package/reactHooks/navigation/__tests__/index.test.tsx +6 -17
  38. package/reactHooks/navigation/index.ts +6 -4
  39. package/reactHooks/navigation/useRoute.ts +4 -6
  40. package/services/js2native.ts +673 -0
  41. package/time/BackgroundTimer.ts +55 -0
  42. package/time/Timer.ts +6 -6
  43. package/utils/README.md +30 -0
  44. package/utils/chunk.ts +5 -0
  45. package/utils/index.ts +5 -0
  46. package/utils/times.ts +5 -0
  47. package/zappFrameworkUtils/localStorageHelper.ts +6 -0
  48. package/zappFrameworkUtils/loginPluginUtils.ts +6 -2
  49. package/zappFrameworkUtils/types.ts +11 -0
@@ -1,6 +1,6 @@
1
1
  /// <reference types="@applicaster/applicaster-types" />
2
2
  import { localStorage } from "@applicaster/zapp-react-native-bridge/ZappStorage/LocalStorage";
3
- import React, { createContext, useMemo } from "react";
3
+ import React, { createContext, useEffect, useMemo } from "react";
4
4
  import {
5
5
  actionExecutor as _actionExecutor,
6
6
  ActionResult,
@@ -19,8 +19,15 @@ import {
19
19
  EntryResolver,
20
20
  resolveObjectValues,
21
21
  } from "../appUtils/contextKeysManager/contextResolver";
22
+ import { useNavigation } from "../reactHooks";
23
+ import { get } from "lodash";
22
24
 
23
- const { log_error, log_info } = createLogger({
25
+ import {
26
+ useContentTypes,
27
+ usePickFromState,
28
+ } from "@applicaster/zapp-react-native-redux/hooks";
29
+
30
+ export const { log_error, log_info, log_debug } = createLogger({
24
31
  subsystem: "ActionExecutorContext",
25
32
  category: "General",
26
33
  });
@@ -51,6 +58,22 @@ type Props = {
51
58
  children: React.ReactNode;
52
59
  };
53
60
 
61
+ function findParentComponent(
62
+ childId: string,
63
+ parent: ZappRiver | ZappUIComponent
64
+ ): ZappRiver | ZappUIComponent | null {
65
+ for (const child of parent.ui_components) {
66
+ if (child.id === childId) return parent;
67
+
68
+ if (child.ui_components) {
69
+ const found = findParentComponent(childId, child);
70
+ if (found) return found;
71
+ }
72
+ }
73
+
74
+ return null;
75
+ }
76
+
54
77
  const prepareDefaultActions = (actionExecutor) => {
55
78
  actionExecutor.registerAction("localStorageSet", async (action) => {
56
79
  const namespaces = action.options.content;
@@ -65,11 +88,13 @@ const prepareDefaultActions = (actionExecutor) => {
65
88
  async (_action, context) => {
66
89
  const dispatch = appStore.getDispatch();
67
90
 
68
- log_info("handleAction: refreshComponent event");
91
+ const dataSource =
92
+ context?.component?.data?.source ||
93
+ findParentComponent(context?.component.id, context?.screenData)?.data
94
+ ?.source;
69
95
 
70
- const dataSource = context?.component?.data?.source;
96
+ log_info(`handleAction: refreshComponent for dataSource:${dataSource}`);
71
97
 
72
- // TODO: Later we can pass component ids to do refresh instead of current one
73
98
  // TODO: In theory we should wait callback to complete, before completing the action, but now it's not needed
74
99
  // TODO: handle focused item removal
75
100
  dispatch(
@@ -169,8 +194,29 @@ const prepareDefaultActions = (actionExecutor) => {
169
194
  action: ActionType,
170
195
  context?: Record<string, any>
171
196
  ): Promise<ActionResult> => {
197
+ if (!context) {
198
+ log_error(
199
+ "handleAction: localStorageToggleFlag action missing context"
200
+ );
201
+
202
+ return ActionResult.Error;
203
+ }
204
+
205
+ const entry = context?.entry as ZappEntry;
206
+
207
+ if (!entry) {
208
+ log_error(
209
+ "handleAction: localStorageToggleFlag action missing entry. Entry is required to get the tag."
210
+ );
211
+
212
+ return ActionResult.Error;
213
+ }
214
+
215
+ const tag = action.options?.selector
216
+ ? get(entry, action.options.selector)
217
+ : (entry.extensions?.tag ?? entry.id);
218
+
172
219
  const keyNamespace = action.options?.key;
173
- const tag = context?.entry?.extensions?.tag;
174
220
 
175
221
  if (keyNamespace && tag) {
176
222
  const multiSelectProvider =
@@ -188,6 +234,16 @@ const prepareDefaultActions = (actionExecutor) => {
188
234
  if (selectedItems.includes(tag)) {
189
235
  await multiSelectProvider.removeItem(tag);
190
236
  } else {
237
+ const maxItems = action.options?.max_items;
238
+
239
+ if (maxItems && selectedItems.length >= maxItems) {
240
+ log_info(
241
+ `handleAction: localStorageToggleFlag event reached max items limit: ${maxItems}, cannot add tag: ${tag}`
242
+ );
243
+
244
+ return ActionResult.Cancel;
245
+ }
246
+
191
247
  await multiSelectProvider.addItem(tag);
192
248
  }
193
249
  } else {
@@ -211,6 +267,10 @@ export function withActionExecutor(Component) {
211
267
  prepareDefaultActions(_actionExecutor);
212
268
 
213
269
  return function ActionExecutorComponent(props: Props) {
270
+ const navigator = useNavigation();
271
+ const { rivers } = usePickFromState(["rivers"]);
272
+ const contentTypes = useContentTypes();
273
+
214
274
  const handlers = useMemo(() => {
215
275
  return {
216
276
  unregisterAction: _actionExecutor.unregisterAction,
@@ -221,6 +281,44 @@ export function withActionExecutor(Component) {
221
281
  };
222
282
  }, []);
223
283
 
284
+ useEffect(() => {
285
+ return _actionExecutor.registerAction(
286
+ "navigateToScreen",
287
+ async (action: ActionType, context?: Record<string, any>) => {
288
+ const screenType = action.options?.typeMapping;
289
+
290
+ if (!screenType) {
291
+ log_error("navigateToScreen: typeMapping option is missing");
292
+
293
+ return ActionResult.Error;
294
+ }
295
+
296
+ const screenId = contentTypes?.[screenType]?.screen_id || null;
297
+
298
+ if (!screenId) {
299
+ log_error(
300
+ `navigateToScreen: can not resolve screen type mapping: ${screenType}`
301
+ );
302
+
303
+ return ActionResult.Error;
304
+ }
305
+
306
+ const river = rivers[screenId];
307
+
308
+ if (!river) {
309
+ log_error("navigateToScreen: can not resolve river");
310
+
311
+ return ActionResult.Error;
312
+ }
313
+
314
+ context?.callback?.({ success: false, error: null, abort: true });
315
+ navigator.replace(river);
316
+
317
+ return ActionResult.Success;
318
+ }
319
+ );
320
+ }, [navigator, rivers, contentTypes]);
321
+
224
322
  return (
225
323
  <ActionExecutorContext.Provider value={handlers}>
226
324
  <Component {...props} />
@@ -72,12 +72,14 @@ const mappedScreenPropertiesScreen = (
72
72
  screenData
73
73
  ): AnalyticsScreenProperties => {
74
74
  return {
75
- screen_name: screenData?.targetScreen?.name,
75
+ screen_name: screenData?.targetScreen?.name || screenData?.name,
76
76
  screen_entry_title: screenData?.title,
77
77
  screen_entry_id: R.isNil(screenData?.type) ? null : screenData?.id,
78
- screen_layout_id: screenData?.targetScreen?.id,
79
- screen_type: screenData?.targetScreen?.type,
80
- home_screen: screenData?.targetScreen?.home,
78
+ screen_layout_id: screenData?.targetScreen?.id || screenData?.id,
79
+ screen_type: screenData?.targetScreen?.type || screenData?.type,
80
+ home_screen: !R.isNil(screenData?.targetScreen?.home)
81
+ ? screenData?.targetScreen?.home
82
+ : screenData?.home,
81
83
  };
82
84
  };
83
85
 
@@ -65,6 +65,10 @@ export class Hook implements HookInterface {
65
65
  event: (typeof HOOKS_EVENTS)[keyof typeof HOOKS_EVENTS],
66
66
  ...args
67
67
  ) {
68
+ if (this.state === hookState(HOOKS_EVENTS.CANCEL)) {
69
+ return;
70
+ }
71
+
68
72
  this.state = hookState(event);
69
73
  this.manager.subscriber.invokeHandler(event, ...args);
70
74
  }
@@ -84,7 +84,7 @@ export function HooksManager({
84
84
  plugins,
85
85
  startUpHooksData = {},
86
86
  }: HookManagerArgs): HookManager {
87
- hooksManagerLogger.addContext({ targetScreen });
87
+ hooksManagerLogger.addContext({ targetScreenId: targetScreen.id });
88
88
 
89
89
  function logHookEvent(func, message, data) {
90
90
  func({
@@ -269,6 +269,7 @@ export function HooksManager({
269
269
  payload = initialPayload,
270
270
  callback: callbackArg,
271
271
  cancelled,
272
+ abort,
272
273
  }) {
273
274
  let callback = callbackArg;
274
275
 
@@ -308,6 +309,7 @@ export function HooksManager({
308
309
  hookPlugin.setStateAndNotify(HOOKS_EVENTS.CANCEL, {
309
310
  hookPlugin,
310
311
  payload,
312
+ abort,
311
313
  });
312
314
 
313
315
  const isHookInHomescreen = payload?.home;
@@ -1,12 +1,6 @@
1
- import { createLogger } from "../../logger";
2
1
  import { ContextKeysManager } from "./index";
3
2
  import * as R from "ramda";
4
3
 
5
- const { log_error } = createLogger({
6
- subsystem: "contextResolver",
7
- category: "General",
8
- });
9
-
10
4
  interface IResolver {
11
5
  resolve: (string) => Promise<string | number | object>;
12
6
  }
@@ -64,12 +58,7 @@ export const resolveObjectValues = async (
64
58
  return [key, resolvedValue];
65
59
  }
66
60
 
67
- log_error("resolveObjectValues: invalid context string", {
68
- key,
69
- value,
70
- });
71
-
72
- return [key, null];
61
+ return [key, value];
73
62
  })
74
63
  );
75
64
 
@@ -5,6 +5,7 @@ exports[`focusManager should be defined 1`] = `
5
5
  "blur": [Function],
6
6
  "disableFocus": [Function],
7
7
  "enableFocus": [Function],
8
+ "findPreferredFocusChild": [Function],
8
9
  "focusableTree": Tree {
9
10
  "loadingCounter": 0,
10
11
  "root": {
@@ -33,7 +34,6 @@ exports[`focusManager should be defined 1`] = `
33
34
  "recoverFocus": [Function],
34
35
  "register": [Function],
35
36
  "removeHandler": [Function],
36
- "resetFocus": [Function],
37
37
  "setFocus": [Function],
38
38
  "setInitialFocus": [Function],
39
39
  "unregister": [Function],
@@ -1,6 +1,5 @@
1
1
  import { focusManager } from "../index.ts";
2
2
  import { focusManager as focusManagerIOS } from "../index.ios.ts";
3
- import { findFirstFocusableItem } from "../utils";
4
3
 
5
4
  describe("focusManagerIOS", () => {
6
5
  it("should be defined", () => {
@@ -9,67 +8,6 @@ describe("focusManagerIOS", () => {
9
8
  });
10
9
  });
11
10
 
12
- const focusableTree = {
13
- root: {
14
- children: [
15
- {
16
- children: [
17
- {
18
- children: [
19
- {
20
- children: [],
21
- component: {
22
- isGroup: true,
23
- },
24
- id: "route/1/1",
25
- },
26
- {
27
- children: [],
28
- component: {
29
- isGroup: true,
30
- },
31
- id: "route/1/2",
32
- },
33
- ],
34
- component: {
35
- isGroup: true,
36
- },
37
- id: "route/1",
38
- },
39
- {
40
- children: [
41
- {
42
- children: [],
43
- component: {
44
- isGroup: true,
45
- },
46
- id: "route/2/1",
47
- },
48
- {
49
- children: [],
50
- component: {
51
- isGroup: false,
52
- },
53
- id: "route/2/2",
54
- },
55
- ],
56
- component: {
57
- isGroup: true,
58
- },
59
- id: "route/2",
60
- },
61
- ],
62
- component: {
63
- isGroup: true,
64
- },
65
- id: "/river/1234",
66
- },
67
- ],
68
- component: null,
69
- id: "root",
70
- },
71
- };
72
-
73
11
  describe("focusManager", () => {
74
12
  it("should be defined", () => {
75
13
  expect(focusManager).toBeDefined();
@@ -107,24 +45,4 @@ describe("focusManager", () => {
107
45
  it("placeholder test", () => {
108
46
  expect(true).toBe(true);
109
47
  });
110
-
111
- it("findFirstFocusableItem returns focusableItem", () => {
112
- const expectedItemId =
113
- focusableTree.root.children[0].children[1].children[1].id;
114
-
115
- const focusableItem = findFirstFocusableItem(focusableTree.root.children);
116
-
117
- expect(
118
- focusableItem !== undefined && expectedItemId === focusableItem.id
119
- ).toBe(true);
120
- });
121
-
122
- it("findFirstFocusableItem does not return focusableItem", () => {
123
- focusableTree.root.children[0].children[1].children[1].component.isGroup =
124
- true;
125
-
126
- const focusableItem = findFirstFocusableItem(focusableTree.root.children);
127
-
128
- expect(focusableItem === undefined).toBe(true);
129
- });
130
48
  });
@@ -30,7 +30,6 @@ describe("focusManager", () => {
30
30
  "isGroupItemFocused",
31
31
  "recoverFocus",
32
32
  "isCurrentFocusOnTheTopScreen",
33
- "resetFocus",
34
33
  ];
35
34
 
36
35
  expectedMethods.forEach((method) => {
@@ -13,7 +13,6 @@ import {
13
13
  import { subscriber } from "../../functionUtils";
14
14
  import { coreLogger } from "../../logger";
15
15
  import { ACTION } from "./utils/enums";
16
- import { findFirstFocusableItem } from "./utils";
17
16
 
18
17
  const logger = coreLogger.addSubsystem("focusManager");
19
18
 
@@ -335,16 +334,7 @@ export const focusManager = (function () {
335
334
  return { success: false };
336
335
  }
337
336
 
338
- function treeLoaded(val, node) {
339
- logger.log({
340
- message: "treeLoaded",
341
- data: {
342
- val,
343
- node,
344
- currentFocusNode,
345
- },
346
- });
347
- }
337
+ function treeLoaded(_val, _node) {}
348
338
 
349
339
  function pressIn() {
350
340
  const currentFocusable = getCurrentFocus();
@@ -493,29 +483,20 @@ export const focusManager = (function () {
493
483
  return haveSameParentBeforeRoot(currentFocusNode, R.last(routes));
494
484
  }
495
485
 
496
- function resetFocus() {
486
+ function recoverFocus() {
497
487
  if (!isCurrentFocusOnTheTopScreen()) {
498
- // We've failed to set focused node on the new screen => run focus reset
488
+ // We've failed to set focused node on the new screen => run focus recovery
499
489
  setInitialFocus();
500
490
  }
501
491
  }
502
492
 
503
- /**
504
- * sets the focus on first focusable item
505
- * @public
506
- */
507
- function recoverFocus() {
508
- const focusableItem = findFirstFocusableItem(focusableTree.root.children);
509
- focusableItem && setFocus(focusableItem.id);
510
- }
511
-
512
493
  /**
513
494
  * moves the focus in a provided direction
514
495
  * @public
515
496
  * @param {Object} direction of the scroll
516
497
  */
517
498
  function moveFocus(direction) {
518
- if (!isCurrentFocusOnTheTopScreen() && currentFocusNode) {
499
+ if (!isCurrentFocusOnTheTopScreen()) {
519
500
  // We are opening new screen. We need to ignore movement of focus.
520
501
  return false;
521
502
  }
@@ -538,8 +519,6 @@ export const focusManager = (function () {
538
519
  } else {
539
520
  currentFocusNode.component.failedLostFocus(currentFocusNode, direction);
540
521
  }
541
- } else {
542
- recoverFocus();
543
522
  }
544
523
  }
545
524
 
@@ -555,6 +534,18 @@ export const focusManager = (function () {
555
534
 
556
535
  const { on, invokeHandler, removeHandler } = eventHandler;
557
536
 
537
+ function findPreferredFocusChild(id) {
538
+ const node = getCurrentFocusNode(id);
539
+
540
+ const preferredFocus = findPriorityItem([node]);
541
+
542
+ if (isNilOrEmpty(preferredFocus)) {
543
+ return undefined;
544
+ }
545
+
546
+ return preferredFocus[0];
547
+ }
548
+
558
549
  /**
559
550
  * this is the list of the functions available externally
560
551
  * when importing the focus manager
@@ -584,6 +575,6 @@ export const focusManager = (function () {
584
575
  isGroupItemFocused,
585
576
  recoverFocus,
586
577
  isCurrentFocusOnTheTopScreen,
587
- resetFocus,
578
+ findPreferredFocusChild,
588
579
  };
589
580
  })();
@@ -98,17 +98,3 @@ export const waitForContent = (focusableTree) => {
98
98
  conditionFn: contentHasAnyChildren,
99
99
  });
100
100
  };
101
-
102
- export const findFirstFocusableItem = (items) => {
103
- for (const item of items) {
104
- if (!item.component.isGroup) {
105
- return item;
106
- } else {
107
- if (item.children) {
108
- const focusableItem = findFirstFocusableItem(item.children);
109
-
110
- if (focusableItem) return focusableItem;
111
- }
112
- }
113
- }
114
- };
@@ -54,7 +54,7 @@ export async function setFocusOnContent(currentRoute: string) {
54
54
  // For doing it we have to wait registration of first focusable node into focusableTree
55
55
  // we wait until content node starts having children
56
56
 
57
- const { setInitialFocus, focusableTree, getCurrentFocus } = focusManager;
57
+ const { setInitialFocus, focusableTree } = focusManager;
58
58
 
59
59
  try {
60
60
  await waitForActiveScreen(currentRoute, focusableTree);
@@ -71,6 +71,6 @@ export async function setFocusOnContent(currentRoute: string) {
71
71
  );
72
72
  }
73
73
  } catch (error) {
74
- !getCurrentFocus() && setFocusOnContent(currentRoute);
74
+ setFocusOnContent(currentRoute);
75
75
  }
76
76
  }
@@ -1,5 +1,3 @@
1
- import * as R from "ramda";
2
-
3
1
  // more keys can be added here. Simply make sure
4
2
  // the key matches the code value
5
3
 
@@ -34,12 +32,18 @@ export function keyCode(key) {
34
32
  keyCode(keyEvent).matchesAny("Enter", "Backspace", KEYS.ArrowLeft, ...)
35
33
  */
36
34
 
37
- const matches = R.compose(
38
- R.either(R.eqProps("code", key), R.eqProps("keyCode", key)),
39
- R.when(R.is(String), R.prop(R.__, KEYS))
40
- );
35
+ const matches = (compareKey) => {
36
+ // If compareKey is a string, look it up in KEYS object
37
+ const keyToCompare =
38
+ typeof compareKey === "string" ? KEYS[compareKey] : compareKey;
39
+
40
+ // Compare either code or keyCode properties
41
+ return (
42
+ keyToCompare?.code === key.code || keyToCompare?.keyCode === key.keyCode
43
+ );
44
+ };
41
45
 
42
- const matchesAny = (...args) => R.any(matches, args);
46
+ const matchesAny = (...args) => args.some(matches);
43
47
 
44
48
  const direction = () =>
45
49
  matchesAny(...ARROW_KEYS) ? directionHelper(key) : null;
@@ -1,8 +1,8 @@
1
- /// <reference types="@types/react-native" />
2
1
  import * as ReactNative from "react-native";
3
- import { isTV, platformSelect } from "../reactUtils";
4
2
  import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
5
- import { useIsTablet } from "@applicaster/zapp-react-native-utils/reactHooks/device/useIsTablet";
3
+
4
+ import { isTV, platformSelect } from "../reactUtils";
5
+ import { useIsTablet } from "../reactHooks";
6
6
 
7
7
  import {
8
8
  QUICK_BRICK_EVENTS,
@@ -121,8 +121,15 @@ export const removeOrientationChangeListener = (listener: any): void => {
121
121
  */
122
122
  export const isOrientationCompatible = ({
123
123
  orientation,
124
- layoutData: { width, height },
124
+ layoutData: { width, height, isTablet, isTabletPortrait },
125
125
  }) => {
126
+ // We count portrait as compatible, because if default in zapp for mobile layout
127
+ // We count landscape as compatible, because if isTabletPortrait is false,
128
+ // it defaults for device
129
+ if (isTablet && !isTabletPortrait) {
130
+ return true;
131
+ }
132
+
126
133
  const isLandscape = width > height;
127
134
 
128
135
  if (
@@ -146,15 +153,6 @@ export const isOrientationCompatible = ({
146
153
  return true;
147
154
  };
148
155
 
149
- export const useIsOrientationCompatible = (orientation) => {
150
- const { height, width } = ReactNative.useWindowDimensions();
151
-
152
- return isOrientationCompatible({
153
- orientation,
154
- layoutData: { width, height },
155
- });
156
- };
157
-
158
156
  /**
159
157
  * Determine final screen orientation based on device type,
160
158
  * settings and orientation value provided by screen data settings
@@ -452,7 +452,7 @@ export class PlayerManager {
452
452
  if (this.castingReceiver) {
453
453
  this.castingReceiver.closeNativePlayer();
454
454
  } else {
455
- this.getActivePlayer().closeNativePlayer();
455
+ this.getActivePlayer()?.closeNativePlayer();
456
456
  this.invokeHandler("close");
457
457
  }
458
458
  }
@@ -97,7 +97,7 @@ export class Player {
97
97
  this.onVideoLoad = this.onVideoLoad.bind(this);
98
98
  this.isFullScreenSupported = this.isFullScreenSupported.bind(this);
99
99
  this.onPlayerSeekComplete = this.onPlayerSeekComplete.bind(this);
100
-
100
+ this.onAdBreakEnd = this.onAdBreakEnd.bind(this);
101
101
  this.entryObservable = null;
102
102
  }
103
103
 
@@ -201,17 +201,25 @@ export class Player {
201
201
  adPosition: currentTime,
202
202
  };
203
203
  } else {
204
- if (!isEmptyOrNil(seekableDuration) && seekableDuration >= 0) {
205
- this.playerState.seekableDuration = seekableDuration;
206
- }
204
+ this.updateContentDuration({ seekableDuration, duration, currentTime });
205
+ }
206
+ };
207
207
 
208
- if (!isEmptyOrNil(duration) && duration >= 0) {
209
- this.playerState.contentDuration = duration;
210
- }
208
+ protected updateContentDuration = ({
209
+ seekableDuration = null,
210
+ duration = null,
211
+ currentTime = null,
212
+ }) => {
213
+ if (!isEmptyOrNil(seekableDuration) && seekableDuration >= 0) {
214
+ this.playerState.seekableDuration = seekableDuration;
215
+ }
211
216
 
212
- if (!isEmptyOrNil(currentTime) && currentTime >= 0) {
213
- this.playerState.contentPosition = currentTime;
214
- }
217
+ if (!isEmptyOrNil(duration) && duration >= 0) {
218
+ this.playerState.contentDuration = duration;
219
+ }
220
+
221
+ if (!isEmptyOrNil(currentTime) && currentTime >= 0) {
222
+ this.playerState.contentPosition = currentTime;
215
223
  }
216
224
  };
217
225
 
@@ -559,7 +567,7 @@ export class Player {
559
567
  });
560
568
  };
561
569
 
562
- onAdBreakEnd = (event) => {
570
+ onAdBreakEnd(event) {
563
571
  this.playerState.adState = null;
564
572
  this.playerState.isInAdBreak = false;
565
573
 
@@ -567,7 +575,7 @@ export class Player {
567
575
  callbackName: SharedPlayerCallBacksKeys.OnAdBreakEnd,
568
576
  event,
569
577
  });
570
- };
578
+ }
571
579
 
572
580
  onAdEnd = (event) => {
573
581
  this.playerState.adState = null;
@@ -736,6 +744,10 @@ export class Player {
736
744
  return null;
737
745
  };
738
746
 
747
+ public hasResumePosition() {
748
+ return false;
749
+ }
750
+
739
751
  public getConfig = () => this.config;
740
752
 
741
753
  // TODO: replace with enum of supported orientations