@applicaster/zapp-react-native-utils 13.0.0-alpha.7330390362 → 13.0.0-alpha.7429912110
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.
- package/README.md +0 -10
- package/actionsExecutor/ActionExecutorContext.tsx +159 -12
- package/actionsExecutor/consts.ts +4 -0
- package/analyticsUtils/currentScreenProperities.ts +6 -4
- package/appUtils/HooksManager/Hook.ts +4 -0
- package/appUtils/HooksManager/index.ts +3 -1
- package/appUtils/__tests__/__snapshots__/localizationsHelper.test.ts.snap +151 -0
- package/appUtils/__tests__/allZappLocales.ts +79 -0
- package/appUtils/__tests__/{localizationsHelper.test.js → localizationsHelper.test.ts} +11 -0
- package/appUtils/accessibilityManager/const.ts +18 -0
- package/appUtils/contextKeysManager/contextResolver.ts +1 -12
- package/appUtils/contextKeysManager/index.ts +2 -2
- package/appUtils/contextKeysManager/utils/index.ts +2 -1
- package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +2 -0
- package/appUtils/focusManager/index.ios.ts +14 -4
- package/appUtils/focusManager/index.ts +14 -10
- package/appUtils/focusManager/utils/__tests__/findChild.test.ts +35 -0
- package/appUtils/focusManager/utils/index.ts +5 -0
- package/appUtils/keyCodes/index.ts +11 -7
- package/appUtils/localizationsHelper.ts +31 -0
- package/appUtils/orientationHelper.ts +11 -13
- package/appUtils/playerManager/index.ts +1 -1
- package/appUtils/playerManager/player.ts +24 -12
- package/appUtils/playerManager/playerHooks/usePlayerCurrentTime.tsx +11 -7
- package/appUtils/playerManager/playerNative.ts +18 -3
- package/appUtils/playerManager/usePlayerTitleSummaryOverlay.tsx +7 -1
- package/cellUtils/index.ts +9 -5
- package/componentsUtils/__tests__/isFirstComponentGallery.test.ts +33 -0
- package/componentsUtils/__tests__/isFirstComponentScreenPicker.test.ts +33 -0
- package/componentsUtils/__tests__/isGallery.test.ts +33 -0
- package/componentsUtils/__tests__/isScreenPicker.test.ts +33 -0
- package/componentsUtils/index.ts +39 -0
- package/dateUtils/index.ts +19 -0
- package/equalUtils/__tests__/isEqual/combination.test.ts +263 -0
- package/equalUtils/index.ts +49 -30
- package/focusManager/FocusManager.ts +14 -2
- package/focusManager/index.ts +23 -4
- package/functionUtils/index.ts +46 -8
- package/index.d.ts +0 -12
- package/localizationUtils/index.ts +8 -13
- package/manifestUtils/defaultManifestConfigurations/generalContent.js +13 -0
- package/manifestUtils/defaultManifestConfigurations/player.js +9 -3
- package/manifestUtils/fonts/index.js +5 -6
- package/manifestUtils/index.js +2 -0
- package/manifestUtils/keys.js +27 -2
- package/manifestUtils/sharedConfiguration/screenPicker/__tests__/utils.test.js +304 -0
- package/manifestUtils/sharedConfiguration/screenPicker/stylesFields.js +875 -0
- package/manifestUtils/sharedConfiguration/screenPicker/utils.js +130 -0
- package/modalState/__tests__/index.test.ts +51 -0
- package/modalState/index.ts +32 -2
- package/navigationUtils/__tests__/__snapshots__/navigationUtils.test.js.snap +1 -0
- package/navigationUtils/index.ts +3 -1
- package/numberUtils/__tests__/toPositiveNumberWithDefault.test.ts +23 -3
- package/numberUtils/index.ts +4 -0
- package/package.json +4 -10
- package/playerUtils/configurationGenerator.ts +9 -11
- package/playerUtils/index.ts +23 -0
- package/reactHooks/advertising/index.ts +5 -5
- package/reactHooks/app/useAppState.ts +2 -2
- package/reactHooks/feed/useBatchLoading.ts +10 -12
- package/reactHooks/feed/useFeedLoader.tsx +17 -8
- package/reactHooks/hookModal/hooks/useHookModalScreenData.ts +39 -0
- package/reactHooks/navigation/__mocks__/useNavigation.ts +3 -0
- package/reactHooks/navigation/__tests__/index.test.tsx +6 -17
- package/reactHooks/navigation/{useGetTabBarHeight.ts → getTabBarHeight.ts} +1 -1
- package/reactHooks/navigation/index.ts +13 -23
- package/reactHooks/navigation/useGetBottomTabBarHeight.ts +10 -3
- package/reactHooks/navigation/useNavigationPluginData.ts +8 -4
- package/reactHooks/navigation/useNavigationType.ts +4 -2
- package/reactHooks/navigation/useProfilerLogging.ts +15 -0
- package/reactHooks/navigation/useRoute.ts +4 -6
- package/reactHooks/screen/__tests__/useScreenBackgroundColor.test.tsx +69 -0
- package/reactHooks/screen/useScreenBackgroundColor.ts +3 -15
- package/reactHooks/state/README.md +79 -0
- package/reactHooks/state/ZStoreProvider.tsx +71 -0
- package/reactHooks/state/__tests__/ZStoreProvider.test.tsx +66 -0
- package/reactHooks/state/index.ts +2 -0
- package/reactHooks/useListenEventBusEvent.ts +1 -1
- package/reactUtils/__tests__/__snapshots__/reactUtilsHelpers.test.js.snap +16 -4
- package/reactUtils/__tests__/reactUtils.test.js +1 -19
- package/reactUtils/__tests__/reactUtilsHelpers.test.js +72 -71
- package/reactUtils/createContext/__tests__/__snapshots__/helpers.test.js.snap +6 -72
- package/reactUtils/createContext/__tests__/__snapshots__/provider.test.js.snap +3 -28
- package/reactUtils/createContext/__tests__/__snapshots__/withConsumer.test.js.snap +3 -4
- package/reactUtils/createContext/__tests__/__snapshots__/withProvider.test.js.snap +7 -3
- package/reactUtils/createContext/__tests__/helpers.test.js +107 -121
- package/reactUtils/createContext/__tests__/{index.test.js → index.test.ts} +2 -2
- package/reactUtils/createContext/__tests__/provider.test.js +4 -6
- package/reactUtils/createContext/__tests__/withConsumer.test.js +12 -12
- package/reactUtils/createContext/__tests__/withProvider.test.js +4 -4
- package/reactUtils/createContext/helpers.ts +6 -1
- package/reactUtils/createContext/index.ts +11 -25
- package/reactUtils/index.ts +9 -11
- package/services/js2native.ts +673 -0
- package/testUtils/index.tsx +0 -10
- package/time/BackgroundTimer.ts +55 -0
- package/time/Timer.ts +6 -6
- package/typeGuards/index.ts +3 -0
- package/utils/README.md +30 -0
- package/utils/chunk.ts +5 -0
- package/utils/index.ts +5 -0
- package/utils/times.ts +5 -0
- package/zappFrameworkUtils/localStorageHelper.ts +38 -10
- package/zappFrameworkUtils/loginPluginUtils.ts +6 -2
- package/zappFrameworkUtils/types.ts +11 -0
- package/reactUtils/createContext/__tests__/index-no-react.test.js +0 -27
- /package/reactUtils/createContext/__tests__/__snapshots__/{index.test.js.snap → index.test.ts.snap} +0 -0
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { NativeModules } from "react-native";
|
|
2
2
|
import * as R from "ramda";
|
|
3
|
+
|
|
3
4
|
import { Tree } from "./treeDataStructure/Tree";
|
|
4
5
|
import { findFocusableNode } from "./treeDataStructure/Utils";
|
|
5
6
|
import { subscriber } from "../../functionUtils";
|
|
7
|
+
import { findChild } from "./utils";
|
|
6
8
|
|
|
7
9
|
const { FocusableManagerModule } = NativeModules;
|
|
8
10
|
|
|
@@ -52,10 +54,7 @@ export const focusManager = (function () {
|
|
|
52
54
|
const node = focusableTree.findInTree(groupId);
|
|
53
55
|
|
|
54
56
|
if (node?.children?.length > 0) {
|
|
55
|
-
const preferredFocus =
|
|
56
|
-
R.find(R.pathEq(["component", "props", "preferredFocus"], true)),
|
|
57
|
-
R.prop("children")
|
|
58
|
-
)(node);
|
|
57
|
+
const preferredFocus = findChild(true, node);
|
|
59
58
|
|
|
60
59
|
if (preferredFocus?.component?.isGroup) {
|
|
61
60
|
return getPreferredFocusInGroup({ groupId: preferredFocus?.id });
|
|
@@ -382,6 +381,16 @@ export const focusManager = (function () {
|
|
|
382
381
|
forceFocusOnItem({ focusableItem, callback });
|
|
383
382
|
}
|
|
384
383
|
|
|
384
|
+
function getPreferredFocusChild(id) {
|
|
385
|
+
const node = focusableTree.findInTree(id);
|
|
386
|
+
|
|
387
|
+
if (node?.children?.length > 0) {
|
|
388
|
+
return findChild(true, node) || findChild(false, node);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
return node;
|
|
392
|
+
}
|
|
393
|
+
|
|
385
394
|
return {
|
|
386
395
|
on,
|
|
387
396
|
invokeHandler,
|
|
@@ -402,5 +411,6 @@ export const focusManager = (function () {
|
|
|
402
411
|
getCurrentGroup,
|
|
403
412
|
getGroupRootById,
|
|
404
413
|
isGroupItemFocused,
|
|
414
|
+
getPreferredFocusChild,
|
|
405
415
|
};
|
|
406
416
|
})();
|
|
@@ -334,16 +334,7 @@ export const focusManager = (function () {
|
|
|
334
334
|
return { success: false };
|
|
335
335
|
}
|
|
336
336
|
|
|
337
|
-
function treeLoaded(
|
|
338
|
-
logger.log({
|
|
339
|
-
message: "treeLoaded",
|
|
340
|
-
data: {
|
|
341
|
-
val,
|
|
342
|
-
node,
|
|
343
|
-
currentFocusNode,
|
|
344
|
-
},
|
|
345
|
-
});
|
|
346
|
-
}
|
|
337
|
+
function treeLoaded(_val, _node) {}
|
|
347
338
|
|
|
348
339
|
function pressIn() {
|
|
349
340
|
const currentFocusable = getCurrentFocus();
|
|
@@ -543,6 +534,18 @@ export const focusManager = (function () {
|
|
|
543
534
|
|
|
544
535
|
const { on, invokeHandler, removeHandler } = eventHandler;
|
|
545
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
|
+
|
|
546
549
|
/**
|
|
547
550
|
* this is the list of the functions available externally
|
|
548
551
|
* when importing the focus manager
|
|
@@ -572,5 +575,6 @@ export const focusManager = (function () {
|
|
|
572
575
|
isGroupItemFocused,
|
|
573
576
|
recoverFocus,
|
|
574
577
|
isCurrentFocusOnTheTopScreen,
|
|
578
|
+
findPreferredFocusChild,
|
|
575
579
|
};
|
|
576
580
|
})();
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { findChild } from "../index";
|
|
2
|
+
|
|
3
|
+
describe("findChild", () => {
|
|
4
|
+
const makeNode = (children = []) => ({ children });
|
|
5
|
+
|
|
6
|
+
const child = (preferredFocus: boolean) => ({
|
|
7
|
+
component: { props: { preferredFocus } },
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it("returns the child with preferredFocus=true", () => {
|
|
11
|
+
const node = makeNode([child(false), child(true), child(false)]);
|
|
12
|
+
expect(findChild(true, node)).toBe(node.children[1]);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("returns the child with preferredFocus=false", () => {
|
|
16
|
+
const node = makeNode([child(false), child(true), child(false)]);
|
|
17
|
+
expect(findChild(false, node)).toBe(node.children[0]);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("returns undefined if no child matches preferredFocus", () => {
|
|
21
|
+
const node = makeNode([child(false), child(false)]);
|
|
22
|
+
expect(findChild(true, node)).toBeUndefined();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("returns undefined if node has no children", () => {
|
|
26
|
+
const node = makeNode();
|
|
27
|
+
expect(findChild(true, node)).toBeUndefined();
|
|
28
|
+
expect(findChild(false, node)).toBeUndefined();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("returns undefined if children is not an array", () => {
|
|
32
|
+
const node = { children: null };
|
|
33
|
+
expect(findChild(true, node)).toBeUndefined();
|
|
34
|
+
});
|
|
35
|
+
});
|
|
@@ -98,3 +98,8 @@ export const waitForContent = (focusableTree) => {
|
|
|
98
98
|
conditionFn: contentHasAnyChildren,
|
|
99
99
|
});
|
|
100
100
|
};
|
|
101
|
+
|
|
102
|
+
export const findChild = (preferredFocus: boolean, node) =>
|
|
103
|
+
(node?.children || []).find(
|
|
104
|
+
(child) => child?.component?.props?.preferredFocus === preferredFocus
|
|
105
|
+
);
|
|
@@ -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 =
|
|
38
|
-
|
|
39
|
-
|
|
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) =>
|
|
46
|
+
const matchesAny = (...args) => args.some(matches);
|
|
43
47
|
|
|
44
48
|
const direction = () =>
|
|
45
49
|
matchesAny(...ARROW_KEYS) ? directionHelper(key) : null;
|
|
@@ -65,6 +65,37 @@ export function getUILanguage() {
|
|
|
65
65
|
return getLanguageCode();
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
/**
|
|
69
|
+
* fallback to en for missing dayjslocalization
|
|
70
|
+
* converts the locale code to a dayjs compatible locale code
|
|
71
|
+
* lowercase input code is expected, e.g. "en-GB" => "en-gb"
|
|
72
|
+
* @param {string} code - locale code, e.g. "en", "fr", "es-LA"
|
|
73
|
+
* */
|
|
74
|
+
export const toDayJSLocaleMap = (code) => {
|
|
75
|
+
const map = {
|
|
76
|
+
hy: "hy-am",
|
|
77
|
+
fj: null,
|
|
78
|
+
la: null,
|
|
79
|
+
no: null,
|
|
80
|
+
pa: "pa-in",
|
|
81
|
+
qu: null,
|
|
82
|
+
sm: null,
|
|
83
|
+
to: null,
|
|
84
|
+
xh: null,
|
|
85
|
+
tt: null,
|
|
86
|
+
"es-LA": "es",
|
|
87
|
+
"en-UK": "en-gb",
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
if (map[code] === null) {
|
|
91
|
+
return "en";
|
|
92
|
+
} else if (map[code]) {
|
|
93
|
+
return map[code];
|
|
94
|
+
} else {
|
|
95
|
+
return code.toLowerCase();
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
68
99
|
/**
|
|
69
100
|
* returns the localization for the app based on the locale. If the locale is null
|
|
70
101
|
* or undefined, or if the given locale doesn't exist in the localizations,
|
|
@@ -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
|
-
|
|
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()
|
|
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
|
-
|
|
205
|
-
|
|
206
|
-
|
|
204
|
+
this.updateContentDuration({ seekableDuration, duration, currentTime });
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
207
|
|
|
208
|
-
|
|
209
|
-
|
|
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
|
-
|
|
213
|
-
|
|
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
|
|
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
|
|
@@ -12,7 +12,7 @@ import { useThrottle } from "../../../functionUtils";
|
|
|
12
12
|
export const usePlayerCurrentTime = (
|
|
13
13
|
listenerId: string,
|
|
14
14
|
playerId?: string
|
|
15
|
-
): number | null => {
|
|
15
|
+
): { currentTime: number | null; cancel: () => void } => {
|
|
16
16
|
const [currentTime, setCurrentTime] = useState<number | null>(null);
|
|
17
17
|
const player: Player = usePlayer(playerId);
|
|
18
18
|
|
|
@@ -20,15 +20,17 @@ export const usePlayerCurrentTime = (
|
|
|
20
20
|
if (player) {
|
|
21
21
|
const newCurrentTime = player.getContentPosition();
|
|
22
22
|
|
|
23
|
-
setCurrentTime(
|
|
24
|
-
prevTime !== newCurrentTime ? newCurrentTime : prevTime
|
|
25
|
-
);
|
|
23
|
+
setCurrentTime(newCurrentTime);
|
|
26
24
|
}
|
|
27
25
|
}, [player]);
|
|
28
26
|
|
|
27
|
+
// We update the current time every ~250ms since player can have rate set to 2x.
|
|
28
|
+
// We update more often than 2x a second to avoid noticiable 'pulsation' in update,
|
|
29
|
+
// since native level also updates the current time every ~250ms,
|
|
30
|
+
// and combined with assorted delays it creates interference pattern.
|
|
29
31
|
const { throttledFunction: throttledUpdate, cancel } = useThrottle(
|
|
30
32
|
updateCurrentTime,
|
|
31
|
-
1000
|
|
33
|
+
1000 / 4
|
|
32
34
|
);
|
|
33
35
|
|
|
34
36
|
useEffect(() => {
|
|
@@ -36,7 +38,9 @@ export const usePlayerCurrentTime = (
|
|
|
36
38
|
throttledUpdate();
|
|
37
39
|
|
|
38
40
|
const listeners = {
|
|
39
|
-
onVideoProgress:
|
|
41
|
+
onVideoProgress: () => {
|
|
42
|
+
setCurrentTime(player.getContentPosition());
|
|
43
|
+
},
|
|
40
44
|
};
|
|
41
45
|
|
|
42
46
|
const id = `${listenerId}_player_current_time`;
|
|
@@ -53,5 +57,5 @@ export const usePlayerCurrentTime = (
|
|
|
53
57
|
}
|
|
54
58
|
}, [player, listenerId, throttledUpdate, cancel]);
|
|
55
59
|
|
|
56
|
-
return currentTime;
|
|
60
|
+
return { currentTime, cancel };
|
|
57
61
|
};
|
|
@@ -54,6 +54,10 @@ export class PlayerNative extends Player {
|
|
|
54
54
|
});
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
public hasResumePosition() {
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
|
|
57
61
|
getContentPosition = () => {
|
|
58
62
|
return this.playerState.contentPosition;
|
|
59
63
|
};
|
|
@@ -221,11 +225,13 @@ export class PlayerNative extends Player {
|
|
|
221
225
|
};
|
|
222
226
|
|
|
223
227
|
onVideoLoad = (event) => {
|
|
224
|
-
|
|
225
|
-
this.seekTo(this.startPosition);
|
|
226
|
-
this.startPosition = null;
|
|
228
|
+
this.updateContentDuration(event);
|
|
227
229
|
|
|
230
|
+
if (this.startPosition && !this.playerState.isInAdBreak) {
|
|
228
231
|
super.onVideoLoad({ ...event, ignoreContinueWatching: true });
|
|
232
|
+
|
|
233
|
+
this.seekTo(this.startPosition);
|
|
234
|
+
this.startPosition = null;
|
|
229
235
|
} else {
|
|
230
236
|
super.onVideoLoad(event);
|
|
231
237
|
}
|
|
@@ -249,6 +255,15 @@ export class PlayerNative extends Player {
|
|
|
249
255
|
}
|
|
250
256
|
};
|
|
251
257
|
|
|
258
|
+
onAdBreakEnd = (event) => {
|
|
259
|
+
super.onAdBreakEnd(event);
|
|
260
|
+
|
|
261
|
+
if (this.startPosition) {
|
|
262
|
+
this.seekTo(this.startPosition);
|
|
263
|
+
this.startPosition = null;
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
|
|
252
267
|
// This function is called when native player used directly without wrapped component,
|
|
253
268
|
// Since event from native comes with wrapped in NativeEvent property
|
|
254
269
|
getNativeViewListener = (): QuickBrickPlayer.SharedPlayerCallBacks => {
|
|
@@ -3,12 +3,18 @@ import { OverlaysObserver } from "./OverlayObserver/OverlaysObserver";
|
|
|
3
3
|
import { useFeedLoader } from "../../reactHooks";
|
|
4
4
|
import { usePlayer } from "./usePlayer";
|
|
5
5
|
|
|
6
|
+
const getLiveUrl = (url: string) => {
|
|
7
|
+
// Use the replace method with a regular expression to replace all instances of subClass with livePage
|
|
8
|
+
return url.replace(new RegExp("subClass", "g"), "livePage");
|
|
9
|
+
};
|
|
10
|
+
|
|
6
11
|
export const useTitleSummaryOverlay = (
|
|
7
|
-
feedUrl,
|
|
8
12
|
entry,
|
|
9
13
|
isLive = false,
|
|
10
14
|
interval: number
|
|
11
15
|
) => {
|
|
16
|
+
// TODO: Have generic place for live url from configuration. Currently its hardcoded for client.
|
|
17
|
+
const feedUrl = getLiveUrl(entry?.extensions?.live?.channel_src ?? "");
|
|
12
18
|
const player = usePlayer();
|
|
13
19
|
const [title, setTitle] = useState<string | number>(entry?.title || "");
|
|
14
20
|
const [summary, setSummary] = useState<string | number>(entry?.summary || "");
|
package/cellUtils/index.ts
CHANGED
|
@@ -2,12 +2,16 @@ import * as R from "ramda";
|
|
|
2
2
|
import dayjs from "dayjs";
|
|
3
3
|
import validateColor from "validate-color";
|
|
4
4
|
|
|
5
|
+
import { transformColorCode as fixColorHexCode } from "@applicaster/zapp-react-native-utils/transform";
|
|
6
|
+
import {
|
|
7
|
+
capitalize,
|
|
8
|
+
isString,
|
|
9
|
+
} from "@applicaster/zapp-react-native-utils/stringUtils";
|
|
10
|
+
import { cellUtilsLogger } from "@applicaster/zapp-react-native-utils/cellUtils/logger";
|
|
11
|
+
import { isWeb } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
12
|
+
import { isNotNil } from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
|
|
13
|
+
|
|
5
14
|
import { toNumberWithDefault, toNumberWithDefaultZero } from "../numberUtils";
|
|
6
|
-
import { transformColorCode as fixColorHexCode } from "../transform";
|
|
7
|
-
import { capitalize, isString } from "../stringUtils";
|
|
8
|
-
import { cellUtilsLogger } from "./logger";
|
|
9
|
-
import { isWeb } from "../reactUtils";
|
|
10
|
-
import { isNotNil } from "../reactUtils/helpers";
|
|
11
15
|
|
|
12
16
|
const CUSTOM_KEY = "other";
|
|
13
17
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { isFirstComponentGallery } from "../index";
|
|
2
|
+
|
|
3
|
+
describe("isFirstComponentGallery", () => {
|
|
4
|
+
it("should return true if the first component is a gallery", () => {
|
|
5
|
+
const components = [
|
|
6
|
+
{ component_type: "gallery-qb" },
|
|
7
|
+
{ component_type: "other-component" },
|
|
8
|
+
];
|
|
9
|
+
|
|
10
|
+
expect(isFirstComponentGallery(components)).toBe(true);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("should return false if the first component is not a gallery", () => {
|
|
14
|
+
const components = [
|
|
15
|
+
{ component_type: "other-component" },
|
|
16
|
+
{ component_type: "gallery-qb" },
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
expect(isFirstComponentGallery(components)).toBe(false);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("should return false if the components array is empty", () => {
|
|
23
|
+
const components = [];
|
|
24
|
+
|
|
25
|
+
expect(isFirstComponentGallery(components)).toBe(false);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("should return false if the components array is undefined", () => {
|
|
29
|
+
const components = undefined;
|
|
30
|
+
|
|
31
|
+
expect(isFirstComponentGallery(components)).toBe(false);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { isFirstComponentScreenPicker } from "..";
|
|
2
|
+
|
|
3
|
+
describe("isFirstComponentScreenPicker", () => {
|
|
4
|
+
it("should return true if the first component screen-picker", () => {
|
|
5
|
+
const items = [{ component_type: "screen-picker-qb-tv" }];
|
|
6
|
+
expect(isFirstComponentScreenPicker(items)).toBe(true);
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it("should return false if the first component type is not screen-picker", () => {
|
|
10
|
+
const items = [{ component_type: "other-component" }];
|
|
11
|
+
expect(isFirstComponentScreenPicker(items)).toBe(false);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("should return false if the component type is undefined", () => {
|
|
15
|
+
const items = [{ component_type: undefined }];
|
|
16
|
+
expect(isFirstComponentScreenPicker(items)).toBe(false);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("should return false if the item is null", () => {
|
|
20
|
+
const items = null;
|
|
21
|
+
expect(isFirstComponentScreenPicker(items)).toBe(false);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("should return false if the item is undefined", () => {
|
|
25
|
+
const items = undefined;
|
|
26
|
+
expect(isFirstComponentScreenPicker(items)).toBe(false);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("should return false if the item is an empty object", () => {
|
|
30
|
+
const items = {};
|
|
31
|
+
expect(isFirstComponentScreenPicker(items)).toBe(false);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { isGallery } from "..";
|
|
2
|
+
|
|
3
|
+
describe("isGallery", () => {
|
|
4
|
+
it("should return true if the component type is 'gallery-qb'", () => {
|
|
5
|
+
const item = { component_type: "gallery-qb" };
|
|
6
|
+
expect(isGallery(item)).toBe(true);
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it("should return false if the component type is not 'gallery-qb'", () => {
|
|
10
|
+
const item = { component_type: "other-component" };
|
|
11
|
+
expect(isGallery(item)).toBe(false);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("should return false if the component type is undefined", () => {
|
|
15
|
+
const item = { component_type: undefined };
|
|
16
|
+
expect(isGallery(item)).toBe(false);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("should return false if the item is null", () => {
|
|
20
|
+
const item = null;
|
|
21
|
+
expect(isGallery(item)).toBe(false);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("should return false if the item is undefined", () => {
|
|
25
|
+
const item = undefined;
|
|
26
|
+
expect(isGallery(item)).toBe(false);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("should return false if the item is an empty object", () => {
|
|
30
|
+
const item = {};
|
|
31
|
+
expect(isGallery(item)).toBe(false);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { isScreenPicker } from "..";
|
|
2
|
+
|
|
3
|
+
describe("isScreenPicker", () => {
|
|
4
|
+
it("should return true if the component type is 'screen-picker-qb-tv'", () => {
|
|
5
|
+
const item = { component_type: "screen-picker-qb-tv" };
|
|
6
|
+
expect(isScreenPicker(item)).toBe(true);
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it("should return false if the component type is not 'screen-picker-qb-tv'", () => {
|
|
10
|
+
const item = { component_type: "other-component" };
|
|
11
|
+
expect(isScreenPicker(item)).toBe(false);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("should return false if the component type is undefined", () => {
|
|
15
|
+
const item = { component_type: undefined };
|
|
16
|
+
expect(isScreenPicker(item)).toBe(false);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("should return false if the item is null", () => {
|
|
20
|
+
const item = null;
|
|
21
|
+
expect(isScreenPicker(item)).toBe(false);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("should return false if the item is undefined", () => {
|
|
25
|
+
const item = undefined;
|
|
26
|
+
expect(isScreenPicker(item)).toBe(false);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("should return false if the item is an empty object", () => {
|
|
30
|
+
const item = {};
|
|
31
|
+
expect(isScreenPicker(item)).toBe(false);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const GROUP = "group-qb";
|
|
2
|
+
const GROUP_INFO = "group-info-qb";
|
|
3
|
+
const GROUP_INFO_OLD = "group-info";
|
|
4
|
+
const EMPTY_GROUP_COMPONENT = "empty_group_component";
|
|
5
|
+
|
|
6
|
+
const GALLERY = "gallery-qb";
|
|
7
|
+
|
|
8
|
+
const SCREEN_PICKER = "screen-picker-qb-tv";
|
|
9
|
+
|
|
10
|
+
const HORIZONTAL_LIST = "horizontal_list_qb";
|
|
11
|
+
|
|
12
|
+
export const isGallery = (item): boolean => item?.component_type === GALLERY;
|
|
13
|
+
|
|
14
|
+
export const isHorizontalList = (item): boolean =>
|
|
15
|
+
item?.component_type === HORIZONTAL_LIST;
|
|
16
|
+
|
|
17
|
+
export const isScreenPicker = (item): boolean =>
|
|
18
|
+
item?.component_type === SCREEN_PICKER;
|
|
19
|
+
|
|
20
|
+
export const isFirstComponentScreenPicker = (
|
|
21
|
+
components: ZappUIComponent[]
|
|
22
|
+
): boolean => {
|
|
23
|
+
return isScreenPicker(components?.[0]);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const isFirstComponentGallery = (
|
|
27
|
+
components: ZappUIComponent[]
|
|
28
|
+
): boolean => {
|
|
29
|
+
return isGallery(components?.[0]);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const isGroup = (item): boolean => item?.component_type === GROUP;
|
|
33
|
+
|
|
34
|
+
export const isEmptyGroup = (item): boolean =>
|
|
35
|
+
item?.component_type === EMPTY_GROUP_COMPONENT;
|
|
36
|
+
|
|
37
|
+
export const isGroupInfo = (item): boolean =>
|
|
38
|
+
item?.component_type === GROUP_INFO ||
|
|
39
|
+
item?.component_type === GROUP_INFO_OLD;
|
package/dateUtils/index.ts
CHANGED
|
@@ -2,10 +2,29 @@ import dayjs from "dayjs";
|
|
|
2
2
|
import utc from "dayjs/plugin/utc";
|
|
3
3
|
import timezone from "dayjs/plugin/timezone";
|
|
4
4
|
import isoWeek from "dayjs/plugin/isoWeek";
|
|
5
|
+
import duration from "dayjs/plugin/duration";
|
|
6
|
+
import relativeTime from "dayjs/plugin/relativeTime";
|
|
5
7
|
|
|
6
8
|
// Extend dayjs with plugins
|
|
7
9
|
dayjs.extend(utc);
|
|
8
10
|
dayjs.extend(timezone);
|
|
9
11
|
dayjs.extend(isoWeek);
|
|
12
|
+
dayjs.extend(duration);
|
|
13
|
+
|
|
14
|
+
dayjs.extend(relativeTime, {
|
|
15
|
+
thresholds: [
|
|
16
|
+
{ l: "s", r: 1 },
|
|
17
|
+
{ l: "m", r: 1 },
|
|
18
|
+
{ l: "mm", r: 59, d: "minute" },
|
|
19
|
+
{ l: "h", r: 1 },
|
|
20
|
+
{ l: "hh", r: 23, d: "hour" },
|
|
21
|
+
{ l: "d", r: 1 },
|
|
22
|
+
{ l: "dd", r: 29, d: "day" },
|
|
23
|
+
{ l: "M", r: 1 },
|
|
24
|
+
{ l: "MM", r: 11, d: "month" },
|
|
25
|
+
{ l: "y", r: 1 },
|
|
26
|
+
{ l: "yy", d: "year" },
|
|
27
|
+
],
|
|
28
|
+
});
|
|
10
29
|
|
|
11
30
|
export { dayjs };
|