@applicaster/zapp-react-native-utils 12.0.0-alpha.5251355865 → 12.0.0-alpha.5350212300
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/analyticsUtils/helpers/hooks.ts +1 -1
- package/appUtils/HooksManager/index.js +3 -1
- package/appUtils/focusManager/index.ts +128 -25
- package/appUtils/focusManager/treeDataStructure/Utils/index.js +97 -33
- package/appUtils/focusManager/utils/index.ts +65 -0
- package/appUtils/playerManager/conts.ts +5 -0
- package/appUtils/playerManager/player.ts +35 -0
- package/appUtils/playerManager/playerFactory.ts +12 -10
- package/appUtils/playerManager/playerNative.ts +9 -0
- package/cellUtils/index.ts +5 -3
- package/focusManager/index.ts +1 -1
- package/localizationUtils/index.ts +6 -3
- package/logger/index.ts +1 -1
- package/manifestUtils/defaultManifestConfigurations/generalContent.js +6 -0
- package/manifestUtils/defaultManifestConfigurations/player.js +2 -1
- package/manifestUtils/index.js +5 -1
- package/manifestUtils/secondaryImage/index.js +18 -15
- package/package.json +2 -2
- package/playerUtils/configurationGenerator.ts +3 -1
- package/reactHooks/autoscrolling/__tests__/useTrackCurrentAutoScrollingElement.test.ts +2 -0
- package/reactHooks/autoscrolling/__tests__/{useTrackedView.test.ts → useTrackedView.test.tsx} +18 -1
- package/reactHooks/flatList/useLoadNextPageIfNeeded.ts +16 -9
- package/reactHooks/layout/__tests__/{index.test.js → index.test.tsx} +44 -29
- package/reactHooks/layout/getDeviceInfo/index.ts +4 -2
- package/reactHooks/layout/types/index.ts +2 -0
- package/reactHooks/layout/useDimensions/useDimensions.ts +13 -7
- package/reactHooks/navigation/__mocks__/index.ts +15 -0
- package/reactHooks/navigation/__mocks__/useNavigation.ts +3 -0
- package/reactHooks/navigation/index.ts +84 -19
- package/reactHooks/resolvers/__tests__/useCellResolver.test.tsx +3 -1
- package/reactHooks/screen/useScreenContext.ts +4 -3
- package/reactHooks/screen/useScreenData.ts +1 -1
- package/reactHooks/screen/useTargetScreenData.ts +7 -0
- package/reactHooks/state/useRivers.ts +3 -1
- package/reactHooks/videoModal/hooks/useVideoModalScreenData.tsx +10 -9
- package/reactUtils/helpers.tsx +1 -1
- package/riverComponetsMeasurementProvider/index.tsx +2 -2
- package/screenState/index.ts +0 -8
- package/stringUtils/index.ts +5 -0
- package/testUtils/getByTestId.ts +14 -0
- package/testUtils/index.tsx +24 -22
|
@@ -26,7 +26,7 @@ export const useHookAnalytics = (props): void => {
|
|
|
26
26
|
if (screenData?.hookPlugin?.module?.runInBackground) {
|
|
27
27
|
sendScreenEvent({ analyticsScreenData });
|
|
28
28
|
}
|
|
29
|
-
}, [hookPlugin.screen_id]);
|
|
29
|
+
}, [sendScreenEvent, screenData, hookPlugin.screen_id]);
|
|
30
30
|
};
|
|
31
31
|
|
|
32
32
|
export const useScreenAnalytics = (props) => {
|
|
@@ -270,7 +270,9 @@ export function HooksManager({
|
|
|
270
270
|
});
|
|
271
271
|
|
|
272
272
|
const isHookInHomescreen = payload?.home;
|
|
273
|
-
|
|
273
|
+
|
|
274
|
+
const isHookFlowBlocker =
|
|
275
|
+
hookPlugin?.module?.isFlowBlocker?.() ?? false;
|
|
274
276
|
|
|
275
277
|
// TODO: We technically need to also support the case where there is more than one hook in the stack,
|
|
276
278
|
// if needed we could add differentiation explicit user cancellation (when we should go to background) and other types of cancellation
|
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
import * as R from "ramda";
|
|
2
|
+
import {
|
|
3
|
+
isNilOrEmpty,
|
|
4
|
+
isNotNil,
|
|
5
|
+
} from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
|
|
6
|
+
|
|
2
7
|
import { Tree } from "./treeDataStructure/Tree";
|
|
3
8
|
import { findFocusableNode, findPriorityItem } from "./treeDataStructure/Utils";
|
|
4
9
|
import { subscriber } from "../../functionUtils";
|
|
5
10
|
import * as FOCUS_EVENTS from "./events";
|
|
6
11
|
import { coreLogger } from "../../logger";
|
|
12
|
+
import {
|
|
13
|
+
waitForActiveScreen,
|
|
14
|
+
waitForContent,
|
|
15
|
+
getContentNode,
|
|
16
|
+
getActiveScreen,
|
|
17
|
+
} from "./utils";
|
|
7
18
|
|
|
8
19
|
const logger = coreLogger.addSubsystem("focusManager");
|
|
9
20
|
|
|
@@ -46,7 +57,7 @@ export const focusManager = (function () {
|
|
|
46
57
|
let lastFocusedId = null;
|
|
47
58
|
let initialFocusOnContent = null;
|
|
48
59
|
let focusDisabled = false;
|
|
49
|
-
let focusAutoEnabled =
|
|
60
|
+
let focusAutoEnabled = false;
|
|
50
61
|
const eventHandler = subscriber();
|
|
51
62
|
|
|
52
63
|
function disableAutomaticFocus() {
|
|
@@ -93,7 +104,7 @@ export const focusManager = (function () {
|
|
|
93
104
|
!currentFocusable.isGroup &&
|
|
94
105
|
currentFocusable.isMounted()
|
|
95
106
|
) {
|
|
96
|
-
currentFocusable.blur(
|
|
107
|
+
currentFocusable.blur(direction);
|
|
97
108
|
currentFocusable.hasLostFocus(getCurrentFocus(), direction);
|
|
98
109
|
}
|
|
99
110
|
}
|
|
@@ -219,38 +230,55 @@ export const focusManager = (function () {
|
|
|
219
230
|
/**
|
|
220
231
|
* sets the initial focus when the screen loads, or when focus is lost
|
|
221
232
|
*/
|
|
222
|
-
function setInitialFocus(lastAddedParentNode?: any) {
|
|
233
|
+
function setInitialFocus(lastAddedParentNode?: any, fallback?: () => void) {
|
|
223
234
|
const preferredFocus = findPriorityItem(
|
|
224
235
|
lastAddedParentNode?.children || focusableTree.root.children
|
|
225
236
|
);
|
|
226
237
|
|
|
227
238
|
logger.log({ message: "setInitialFocus", data: { preferredFocus } });
|
|
228
239
|
|
|
229
|
-
if (preferredFocus
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
if (
|
|
234
|
-
focusableItem &&
|
|
235
|
-
!focusableItem.isGroup &&
|
|
236
|
-
isFocusEnabled(focusableItem)
|
|
237
|
-
) {
|
|
238
|
-
if (initialFocusOnContent) {
|
|
239
|
-
blur();
|
|
240
|
-
initialFocusOnContent = false;
|
|
241
|
-
}
|
|
240
|
+
if (isNilOrEmpty(preferredFocus)) {
|
|
241
|
+
fallback?.();
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
242
244
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
data: {
|
|
246
|
-
focusableItem,
|
|
247
|
-
},
|
|
248
|
-
});
|
|
245
|
+
const focusCandidate = preferredFocus[0];
|
|
246
|
+
const focusableItem = focusCandidate.component;
|
|
249
247
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
248
|
+
const currentFocusedId = R.path(["props", "id"], getCurrentFocus());
|
|
249
|
+
const nextFocusedId = R.path(["props", "id"], focusableItem);
|
|
250
|
+
|
|
251
|
+
if (currentFocusedId === nextFocusedId) {
|
|
252
|
+
// we are going to move focus to the same node(already focused) -> nothing to do
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (preferredFocus.length === 1 && focusableItem && focusableItem.isGroup) {
|
|
257
|
+
// we've found only one FocusableGroup as a candidate to focus, try to use fallback
|
|
258
|
+
fallback?.();
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (
|
|
263
|
+
focusableItem &&
|
|
264
|
+
!focusableItem.isGroup &&
|
|
265
|
+
isFocusEnabled(focusableItem)
|
|
266
|
+
) {
|
|
267
|
+
if (initialFocusOnContent) {
|
|
268
|
+
blur();
|
|
269
|
+
initialFocusOnContent = false;
|
|
253
270
|
}
|
|
271
|
+
|
|
272
|
+
logger.log({
|
|
273
|
+
message: "setInitialFocus – currentFocusNode",
|
|
274
|
+
data: {
|
|
275
|
+
focusableItem,
|
|
276
|
+
},
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
currentFocusNode = focusCandidate;
|
|
280
|
+
focusableItem.willReceiveFocus(focusableItem, null);
|
|
281
|
+
focusableItem && setFocus(focusCandidate.id, null);
|
|
254
282
|
}
|
|
255
283
|
}
|
|
256
284
|
|
|
@@ -323,6 +351,8 @@ export const focusManager = (function () {
|
|
|
323
351
|
* @returns {React.Component} the registered component
|
|
324
352
|
*/
|
|
325
353
|
function registerGroup(id, component) {
|
|
354
|
+
// console.log("debug_2", "FM registerGroup", { id, component });
|
|
355
|
+
|
|
326
356
|
focusableTree.addNode({ id, component });
|
|
327
357
|
|
|
328
358
|
return component;
|
|
@@ -338,6 +368,8 @@ export const focusManager = (function () {
|
|
|
338
368
|
* @returns {React.Component} the registered component
|
|
339
369
|
*/
|
|
340
370
|
function registerItem(id, component) {
|
|
371
|
+
// console.log("debug_2", "FM registerItem", { id, component });
|
|
372
|
+
|
|
341
373
|
focusableTree.addNode({ id, component });
|
|
342
374
|
|
|
343
375
|
if (!currentFocusNode && id === lastFocusedId && !id.includes("menu")) {
|
|
@@ -354,6 +386,8 @@ export const focusManager = (function () {
|
|
|
354
386
|
* @param {String} id of the group to unregister
|
|
355
387
|
*/
|
|
356
388
|
function unregisterGroup(id) {
|
|
389
|
+
// console.log("debug_2", "FM unregisterGroup", { id });
|
|
390
|
+
|
|
357
391
|
focusableTree.findAndRemoveNode(id);
|
|
358
392
|
}
|
|
359
393
|
|
|
@@ -365,6 +399,8 @@ export const focusManager = (function () {
|
|
|
365
399
|
* @param {String} id of the Focusable to unregister
|
|
366
400
|
*/
|
|
367
401
|
function unregisterItem(id) {
|
|
402
|
+
// console.log("debug_2", "FM unregisterItem", { id });
|
|
403
|
+
|
|
368
404
|
focusableTree.findAndRemoveNode(id);
|
|
369
405
|
|
|
370
406
|
if (currentFocusNode && currentFocusNode.id === id) {
|
|
@@ -403,6 +439,8 @@ export const focusManager = (function () {
|
|
|
403
439
|
}
|
|
404
440
|
|
|
405
441
|
function setupInitialFocus(id: string, focusOnContent?: boolean) {
|
|
442
|
+
console.log("debug_2", "FM - setupInitialFocus", { id, focusOnContent });
|
|
443
|
+
|
|
406
444
|
if (focusOnContent) {
|
|
407
445
|
initialFocusOnContent = focusOnContent;
|
|
408
446
|
} else {
|
|
@@ -410,6 +448,62 @@ export const focusManager = (function () {
|
|
|
410
448
|
}
|
|
411
449
|
}
|
|
412
450
|
|
|
451
|
+
async function setFocusOnMenu(currentRoute: string) {
|
|
452
|
+
console.log("debug_2", "FM - setFocusOnMenu", { currentRoute });
|
|
453
|
+
|
|
454
|
+
try {
|
|
455
|
+
await waitForActiveScreen(currentRoute, focusableTree);
|
|
456
|
+
|
|
457
|
+
const navbar = R.find(
|
|
458
|
+
(child) => R.startsWith("navbar", child.id),
|
|
459
|
+
getActiveScreen(focusableTree).children
|
|
460
|
+
);
|
|
461
|
+
|
|
462
|
+
const selectedMenuItemId = R.find(
|
|
463
|
+
(child) => child.component.props.selected,
|
|
464
|
+
navbar?.children || []
|
|
465
|
+
)?.id;
|
|
466
|
+
|
|
467
|
+
if (selectedMenuItemId) {
|
|
468
|
+
// set focus on selected menu item
|
|
469
|
+
setFocus(selectedMenuItemId);
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
const firstMenuItemId = R.path(["children", 0, "id"], navbar);
|
|
474
|
+
|
|
475
|
+
if (firstMenuItemId) {
|
|
476
|
+
// set focus on first menu item
|
|
477
|
+
setFocus(firstMenuItemId);
|
|
478
|
+
}
|
|
479
|
+
} catch (error) {
|
|
480
|
+
// failed to put focus on top-menu => log it
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
async function setFocusOnContent(currentRoute: string) {
|
|
485
|
+
console.log("debug_2", "FM - setFocusOnContent", { currentRoute });
|
|
486
|
+
|
|
487
|
+
// we need to put focus on content here
|
|
488
|
+
// For doing it we have to wait registration of first focusable node into focusableTree
|
|
489
|
+
// this is the temporary hack with waiting until content node starts having children
|
|
490
|
+
try {
|
|
491
|
+
await waitForActiveScreen(currentRoute, focusableTree);
|
|
492
|
+
await waitForContent(focusableTree);
|
|
493
|
+
|
|
494
|
+
const content = getContentNode(focusableTree);
|
|
495
|
+
|
|
496
|
+
const fallback = () => {
|
|
497
|
+
throw Error("Failed to land focus on content");
|
|
498
|
+
};
|
|
499
|
+
|
|
500
|
+
setInitialFocus(content, fallback);
|
|
501
|
+
} catch (e) {
|
|
502
|
+
// we've failed to put initial focus on content, put focus on menu as a last chance
|
|
503
|
+
setFocusOnMenu(currentRoute);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
413
507
|
/**
|
|
414
508
|
* moves the focus in a provided direction
|
|
415
509
|
* @public
|
|
@@ -422,6 +516,13 @@ export const focusManager = (function () {
|
|
|
422
516
|
currentFocusNode.component.willLoseFocus(currentFocusNode, direction);
|
|
423
517
|
const nextFocusable = findFocusableNode(direction, currentFocusNode);
|
|
424
518
|
|
|
519
|
+
// console.log("debug_2", "FM moveFocus", {
|
|
520
|
+
// direction: direction?.value,
|
|
521
|
+
// currentFocusNode,
|
|
522
|
+
// focusableTree,
|
|
523
|
+
// nextFocusable,
|
|
524
|
+
// });
|
|
525
|
+
|
|
425
526
|
if (nextFocusable?.component?.isMounted?.()) {
|
|
426
527
|
const nextRect = nextFocusable.component.getRect();
|
|
427
528
|
|
|
@@ -518,5 +619,7 @@ export const focusManager = (function () {
|
|
|
518
619
|
enableAutomaticFocus,
|
|
519
620
|
getGroupRootById,
|
|
520
621
|
isGroupItemFocused,
|
|
622
|
+
setFocusOnMenu,
|
|
623
|
+
setFocusOnContent,
|
|
521
624
|
};
|
|
522
625
|
})();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { allPass, pathOr, startsWith, isNil } from "ramda";
|
|
2
2
|
import { isNilOrEmpty } from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
|
|
3
3
|
|
|
4
4
|
import { rejectItemByPath } from "../../../../objectUtils";
|
|
@@ -143,7 +143,49 @@ const hasChildren = (target) =>
|
|
|
143
143
|
|
|
144
144
|
const hasComponent = (target) => !!target?.component;
|
|
145
145
|
|
|
146
|
-
const hasComponentAndChildren =
|
|
146
|
+
const hasComponentAndChildren = allPass([hasComponent, hasChildren]);
|
|
147
|
+
|
|
148
|
+
const positiveOrZero = (value) => value >= 0;
|
|
149
|
+
|
|
150
|
+
const excludeFromFocusSearching = pathOr(false, [
|
|
151
|
+
"component",
|
|
152
|
+
"props",
|
|
153
|
+
"excludeFromFocusSearching",
|
|
154
|
+
]);
|
|
155
|
+
|
|
156
|
+
// const isAllScreens = all(excludeFromFocusSearching);
|
|
157
|
+
|
|
158
|
+
const isNavBar = (target) => startsWith("navbar", target?.id);
|
|
159
|
+
|
|
160
|
+
const ignoreMenuIfMultipleTargets = (nodes) => {
|
|
161
|
+
if (nodes.length > 1 && nodes.map((n) => n?.target?.id).includes("navbar")) {
|
|
162
|
+
return nodes.filter((n) => !isNavBar(n?.target));
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return nodes;
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const toDistance = ({ target, direction, comparisonRect }) => {
|
|
169
|
+
const componentRect = target.component?.getRect();
|
|
170
|
+
|
|
171
|
+
const orientationDistance = measureDistance(
|
|
172
|
+
direction,
|
|
173
|
+
comparisonRect,
|
|
174
|
+
componentRect
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
if (orientationDistance === -1) {
|
|
178
|
+
// ignores offscreen targets when moving up/down
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const euclidDistance = measureEuclidianDistanceToCenter(
|
|
183
|
+
comparisonRect,
|
|
184
|
+
componentRect
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
return orientationDistance > 0 ? euclidDistance : orientationDistance;
|
|
188
|
+
};
|
|
147
189
|
|
|
148
190
|
/**
|
|
149
191
|
* Get target in shortest distance in specified direction
|
|
@@ -173,43 +215,32 @@ export function getTargetInDirection(
|
|
|
173
215
|
|
|
174
216
|
const distances = targets
|
|
175
217
|
.filter(hasComponentAndChildren)
|
|
176
|
-
.
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
componentRect
|
|
183
|
-
);
|
|
184
|
-
|
|
185
|
-
if (distance === -1) {
|
|
186
|
-
// ignores offscreen targets when moving up/down
|
|
187
|
-
return null;
|
|
188
|
-
}
|
|
218
|
+
.filter((target) => !excludeFromFocusSearching(target))
|
|
219
|
+
.map((target) => ({
|
|
220
|
+
distance: toDistance({ target, direction, comparisonRect }),
|
|
221
|
+
target,
|
|
222
|
+
}))
|
|
223
|
+
.filter(({ distance }) => positiveOrZero(distance));
|
|
189
224
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
measureEuclidianDistanceToCenter(comparisonRect, componentRect) /
|
|
194
|
-
10000,
|
|
195
|
-
target,
|
|
196
|
-
};
|
|
197
|
-
})
|
|
198
|
-
.filter((obj) => obj && obj.distance >= 0);
|
|
225
|
+
// if (isAllScreens(targets)) {
|
|
226
|
+
// return last(targets);
|
|
227
|
+
// }
|
|
199
228
|
|
|
200
229
|
const match = distances
|
|
201
230
|
.sort((a, b) => a.distance - b.distance) // sorts the distances
|
|
202
231
|
.filter(ignoreMenuIfMultipleTargets)[0]; // filters out multiple targets if necessary // gets the first element
|
|
203
232
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
233
|
+
// console.log("debug_2", "FM getTargetInDirection", {
|
|
234
|
+
// direction: direction?.value,
|
|
235
|
+
// currentNode,
|
|
236
|
+
// targetNodes,
|
|
237
|
+
// targets,
|
|
238
|
+
// comparisonRect,
|
|
239
|
+
// distances,
|
|
240
|
+
// match,
|
|
241
|
+
// });
|
|
242
|
+
|
|
243
|
+
return match?.target;
|
|
213
244
|
}
|
|
214
245
|
|
|
215
246
|
/**
|
|
@@ -300,3 +331,36 @@ export function hasPriorityInDirection(direction, component) {
|
|
|
300
331
|
|
|
301
332
|
return (prioritiseFocusOn & mappedDirection) > 0;
|
|
302
333
|
}
|
|
334
|
+
|
|
335
|
+
export const getNodeBeforeRoot = (node) => {
|
|
336
|
+
if (isNil(node)) {
|
|
337
|
+
return undefined;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
if (node.parent.id === "root") {
|
|
341
|
+
return node;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
return getNodeBeforeRoot(node.parent);
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
export const isAllowedToMoveTo = (currentNode, nextNode) => {
|
|
348
|
+
const screenForCurrentNode = getNodeBeforeRoot(currentNode);
|
|
349
|
+
const screenForNextNode = getNodeBeforeRoot(nextNode);
|
|
350
|
+
|
|
351
|
+
return screenForCurrentNode.id === screenForNextNode.id;
|
|
352
|
+
|
|
353
|
+
// console.log("debug_2", "isAllowedToMoveTo", {
|
|
354
|
+
// nodeBeforeRoot,
|
|
355
|
+
// });
|
|
356
|
+
|
|
357
|
+
// if (nodeBeforeRoot?.id === "navbar") {
|
|
358
|
+
// return true;
|
|
359
|
+
// }
|
|
360
|
+
|
|
361
|
+
// if (nodeBeforeRoot?.component?.props?.isActive) {
|
|
362
|
+
// return true;
|
|
363
|
+
// }
|
|
364
|
+
|
|
365
|
+
// return false;
|
|
366
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { isNotNil } from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
|
|
2
|
+
import { find, last, startsWith } from "ramda";
|
|
3
|
+
|
|
4
|
+
// run check each 300 ms
|
|
5
|
+
// run this check too often could lead to performance penalty on low-end devices
|
|
6
|
+
const HOW_OFTEN_TO_CHECK_CONDITION = 300; //ms
|
|
7
|
+
|
|
8
|
+
type Props = {
|
|
9
|
+
maxTimeout: number;
|
|
10
|
+
conditionFn: () => boolean;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const waitUntil = ({ maxTimeout, conditionFn }: Props) => {
|
|
14
|
+
let interval;
|
|
15
|
+
return new Promise((resolve, reject) => {
|
|
16
|
+
interval = setInterval(() => {
|
|
17
|
+
if (conditionFn()) {
|
|
18
|
+
clearInterval(interval);
|
|
19
|
+
resolve(undefined);
|
|
20
|
+
}
|
|
21
|
+
}, HOW_OFTEN_TO_CHECK_CONDITION);
|
|
22
|
+
|
|
23
|
+
setTimeout(() => {
|
|
24
|
+
reject();
|
|
25
|
+
}, maxTimeout);
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const waitForActiveScreen = (currentRoute: string, focusableTree) => {
|
|
30
|
+
const isActiveScreenAvailableToFocus = (): boolean => {
|
|
31
|
+
const routes = focusableTree.root.children;
|
|
32
|
+
|
|
33
|
+
const route = find((route) => route.id === currentRoute, routes);
|
|
34
|
+
return isNotNil(route);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
return waitUntil({
|
|
38
|
+
maxTimeout: 2500, //ms
|
|
39
|
+
conditionFn: isActiveScreenAvailableToFocus,
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const getActiveScreen = (focusableTree) => {
|
|
44
|
+
return last(focusableTree.root.children);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export const getContentNode = (focusableTree) => {
|
|
48
|
+
const content = find(
|
|
49
|
+
(child) => startsWith("content", child.id),
|
|
50
|
+
getActiveScreen(focusableTree)?.children
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
return content;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const waitForContent = (focusableTree) => {
|
|
57
|
+
const contentHasAnyChildren = (): boolean => {
|
|
58
|
+
return getContentNode(focusableTree)?.children.length > 0;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
return waitUntil({
|
|
62
|
+
maxTimeout: 2500, //ms
|
|
63
|
+
conditionFn: contentHasAnyChildren,
|
|
64
|
+
});
|
|
65
|
+
};
|
|
@@ -20,6 +20,11 @@ export enum SharedPlayerCallBacksKeys {
|
|
|
20
20
|
OnPlayerMute = "onPlayerMute",
|
|
21
21
|
OnPlayerUnmute = "onPlayerUnmute",
|
|
22
22
|
|
|
23
|
+
onVideoFullscreenPlayerDidDismiss = "onVideoFullscreenPlayerDidDismiss",
|
|
24
|
+
onVideoFullscreenPlayerDidPresent = "onVideoFullscreenPlayerDidPresent",
|
|
25
|
+
onVideoFullscreenPlayerWillDismiss = "onVideoFullscreenPlayerWillDismiss",
|
|
26
|
+
onVideoFullscreenPlayerWillPresent = "onVideoFullscreenPlayerWillPresent",
|
|
27
|
+
|
|
23
28
|
OnAdBegin = "onAdBegin",
|
|
24
29
|
OnAdBreakBegin = "onAdBreakBegin",
|
|
25
30
|
OnAdBreakEnd = "onAdBreakEnd",
|
|
@@ -414,6 +414,32 @@ export class Player {
|
|
|
414
414
|
});
|
|
415
415
|
};
|
|
416
416
|
|
|
417
|
+
onVideoFullscreenPlayerWillDismiss = () => {
|
|
418
|
+
this.invokeListenersUpdate({
|
|
419
|
+
callbackName:
|
|
420
|
+
SharedPlayerCallBacksKeys.onVideoFullscreenPlayerWillDismiss,
|
|
421
|
+
});
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
onVideoFullscreenPlayerDidDismiss = () => {
|
|
425
|
+
this.invokeListenersUpdate({
|
|
426
|
+
callbackName: SharedPlayerCallBacksKeys.onVideoFullscreenPlayerDidDismiss,
|
|
427
|
+
});
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
onVideoFullscreenPlayerWillPresent = () => {
|
|
431
|
+
this.invokeListenersUpdate({
|
|
432
|
+
callbackName:
|
|
433
|
+
SharedPlayerCallBacksKeys.onVideoFullscreenPlayerWillPresent,
|
|
434
|
+
});
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
onVideoFullscreenPlayerDidPresent = () => {
|
|
438
|
+
this.invokeListenersUpdate({
|
|
439
|
+
callbackName: SharedPlayerCallBacksKeys.onVideoFullscreenPlayerDidPresent,
|
|
440
|
+
});
|
|
441
|
+
};
|
|
442
|
+
|
|
417
443
|
public invokeListenersUpdate = ({
|
|
418
444
|
callbackName,
|
|
419
445
|
event = {},
|
|
@@ -634,6 +660,12 @@ export class Player {
|
|
|
634
660
|
onBufferStart: this.onBufferStart,
|
|
635
661
|
onBufferComplete: this.onBufferComplete,
|
|
636
662
|
|
|
663
|
+
onPlayerClose: this.onPlayerClose,
|
|
664
|
+
onVideoFullscreenPlayerWillPresent: this.onVideoFullscreenPlayerWillPresent,
|
|
665
|
+
onVideoFullscreenPlayerDidPresent: this.onVideoFullscreenPlayerDidPresent,
|
|
666
|
+
onVideoFullscreenPlayerWillDismiss: this.onVideoFullscreenPlayerWillDismiss,
|
|
667
|
+
onVideoFullscreenPlayerDidDismiss: this.onVideoFullscreenPlayerDidDismiss,
|
|
668
|
+
|
|
637
669
|
// Ads Callbacks:
|
|
638
670
|
onAdBegin: this.onAdBegin,
|
|
639
671
|
onAdBreakBegin: this.onAdBreakBegin,
|
|
@@ -693,4 +725,7 @@ export class Player {
|
|
|
693
725
|
isFullScreenSupported(): boolean {
|
|
694
726
|
return true;
|
|
695
727
|
}
|
|
728
|
+
|
|
729
|
+
public supportsNativeControls = (): boolean => false;
|
|
730
|
+
public supportNativeCast = (): boolean => false;
|
|
696
731
|
}
|
|
@@ -9,21 +9,23 @@ type PlayerFactoryItem = {
|
|
|
9
9
|
};
|
|
10
10
|
|
|
11
11
|
type PlayerFactoryProps = {
|
|
12
|
-
player: any
|
|
13
|
-
playerId: string
|
|
14
|
-
autoplay: boolean
|
|
15
|
-
entry: ZappEntry
|
|
16
|
-
muted: boolean
|
|
17
|
-
playerPluginId: string
|
|
18
|
-
screenConfig: Record<string, any
|
|
19
|
-
playerRole: PlayerRole
|
|
20
|
-
}
|
|
12
|
+
player: any;
|
|
13
|
+
playerId: string;
|
|
14
|
+
autoplay: boolean;
|
|
15
|
+
entry: ZappEntry;
|
|
16
|
+
muted: boolean;
|
|
17
|
+
playerPluginId: string;
|
|
18
|
+
screenConfig: Record<string, any>;
|
|
19
|
+
playerRole: PlayerRole;
|
|
20
|
+
};
|
|
21
21
|
|
|
22
22
|
interface PlayerConstructor {
|
|
23
23
|
new (params: any): Player;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
export const playerFactory = (
|
|
26
|
+
export const playerFactory = (
|
|
27
|
+
config: PlayerFactoryProps
|
|
28
|
+
): PlayerFactoryItem | null => {
|
|
27
29
|
const IDENTIFIER = config?.playerPluginId;
|
|
28
30
|
|
|
29
31
|
if (!IDENTIFIER) {
|
|
@@ -5,6 +5,7 @@ import { Player } from "./player";
|
|
|
5
5
|
import { findNodeHandle, NativeModules } from "react-native";
|
|
6
6
|
import { isApplePlatform } from "../../reactUtils";
|
|
7
7
|
import { PlayerRole } from "./conts";
|
|
8
|
+
import { toBooleanWithDefaultFalse } from "../../booleanUtils";
|
|
8
9
|
|
|
9
10
|
const RNPlayerManager = {
|
|
10
11
|
QuickBrickPlayerPlugin: isApplePlatform()
|
|
@@ -291,4 +292,12 @@ export class PlayerNative extends Player {
|
|
|
291
292
|
|
|
292
293
|
return !(isFullScreenAudioPlayer && this.isAudioItem());
|
|
293
294
|
};
|
|
295
|
+
|
|
296
|
+
supportsNativeControls = (): boolean =>
|
|
297
|
+
toBooleanWithDefaultFalse(this.getConfig()?.["supports_native_controls"]);
|
|
298
|
+
|
|
299
|
+
supportNativeCast = (): boolean =>
|
|
300
|
+
toBooleanWithDefaultFalse(
|
|
301
|
+
this.playerPluginId === "quick-brick-player-brightcove"
|
|
302
|
+
);
|
|
294
303
|
}
|
package/cellUtils/index.ts
CHANGED
|
@@ -182,8 +182,8 @@ export const getHeight = (width, aspectRatio) => {
|
|
|
182
182
|
* @returns {string} duration
|
|
183
183
|
*/
|
|
184
184
|
|
|
185
|
-
export const getDurationInMinutes = (seconds
|
|
186
|
-
if (seconds
|
|
185
|
+
export const getDurationInMinutes = (seconds?: number): string => {
|
|
186
|
+
if (!seconds) return "00:00";
|
|
187
187
|
const minutes = Math.floor(Number(seconds) / 60);
|
|
188
188
|
const secondsReminder = Math.floor(Number(seconds)) % 60;
|
|
189
189
|
const pad = (num: number): string => `${num < 10 ? "0" : ""}${num}`;
|
|
@@ -460,7 +460,9 @@ export const getColorFromData = ({
|
|
|
460
460
|
}: GetColorFromData): string => {
|
|
461
461
|
const value = getValue(key);
|
|
462
462
|
|
|
463
|
-
|
|
463
|
+
// Temporary hack to fix color validation when alpha is floating point number
|
|
464
|
+
// https://github.com/dreamyguy/validate-color/issues/44
|
|
465
|
+
if (validateColor(value.replace(".00", ""))) {
|
|
464
466
|
return value;
|
|
465
467
|
}
|
|
466
468
|
|
package/focusManager/index.ts
CHANGED
|
@@ -86,7 +86,7 @@ export const isFocusable = (component: ZappEntry | ZappUIComponent | any) =>
|
|
|
86
86
|
|
|
87
87
|
type Props = {
|
|
88
88
|
component: ZappUIComponent;
|
|
89
|
-
zappPipesData
|
|
89
|
+
zappPipesData?: ZappPipesData;
|
|
90
90
|
} & Record<string, any>;
|
|
91
91
|
|
|
92
92
|
export const useIsFocusable = (props: Props): void => {
|
|
@@ -64,10 +64,13 @@ export function getIsRTLFromLocalizations(localizations): boolean {
|
|
|
64
64
|
return R.contains(localeToUse, RTL_LOCALES);
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
const remoteConfigLocalizationSelector = R.path([
|
|
68
|
+
"remoteConfigurations",
|
|
69
|
+
"localizations",
|
|
70
|
+
]);
|
|
71
|
+
|
|
67
72
|
export function useIsRTL(): boolean {
|
|
68
|
-
const localizations = useSelector(
|
|
69
|
-
R.path(["remoteConfigurations", "localizations"])
|
|
70
|
-
);
|
|
73
|
+
const localizations = useSelector(remoteConfigLocalizationSelector);
|
|
71
74
|
|
|
72
75
|
return getIsRTLFromLocalizations(localizations);
|
|
73
76
|
}
|
package/logger/index.ts
CHANGED