@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.
- package/actionsExecutor/ActionExecutorContext.tsx +104 -6
- package/analyticsUtils/currentScreenProperities.ts +6 -4
- package/appUtils/HooksManager/Hook.ts +4 -0
- package/appUtils/HooksManager/index.ts +3 -1
- package/appUtils/contextKeysManager/contextResolver.ts +1 -12
- package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +1 -1
- package/appUtils/focusManager/__tests__/focusManager.test.js +0 -82
- package/appUtils/focusManager/__tests__/index.test.ts +0 -1
- package/appUtils/focusManager/index.ts +17 -26
- package/appUtils/focusManager/utils/index.ts +0 -14
- package/appUtils/focusManagerAux/index.ts +2 -2
- package/appUtils/keyCodes/index.ts +11 -7
- package/appUtils/orientationHelper.ts +11 -13
- package/appUtils/playerManager/index.ts +1 -1
- package/appUtils/playerManager/player.ts +24 -12
- package/appUtils/playerManager/playerNative.ts +18 -3
- package/appUtils/playerManager/usePlayerTitleSummaryOverlay.tsx +7 -1
- package/dateUtils/index.ts +2 -0
- package/focusManager/FocusManager.ts +3 -1
- package/focusManager/index.ts +3 -21
- package/functionUtils/index.ts +46 -8
- package/index.d.ts +0 -12
- package/manifestUtils/defaultManifestConfigurations/player.js +8 -0
- package/manifestUtils/fonts/index.js +5 -6
- package/manifestUtils/sharedConfiguration/screenPicker/stylesFields.js +1 -1
- package/modalState/__tests__/index.test.ts +51 -0
- package/modalState/index.ts +32 -2
- package/numberUtils/__tests__/toPositiveNumberWithDefault.test.ts +23 -3
- package/numberUtils/index.ts +11 -5
- package/package.json +2 -6
- package/playerUtils/configurationGenerator.ts +8 -0
- package/reactHooks/advertising/index.ts +5 -5
- package/reactHooks/autoscrolling/__tests__/useTrackedView.test.tsx +5 -7
- package/reactHooks/autoscrolling/useTrackedView.ts +12 -1
- package/reactHooks/feed/useFeedLoader.tsx +4 -2
- package/reactHooks/hookModal/hooks/useHookModalScreenData.ts +39 -0
- package/reactHooks/navigation/__tests__/index.test.tsx +6 -17
- package/reactHooks/navigation/index.ts +6 -4
- package/reactHooks/navigation/useRoute.ts +4 -6
- package/services/js2native.ts +673 -0
- package/time/BackgroundTimer.ts +55 -0
- package/time/Timer.ts +6 -6
- 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 +6 -0
- package/zappFrameworkUtils/loginPluginUtils.ts +6 -2
- 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
|
-
|
|
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
|
-
|
|
91
|
+
const dataSource =
|
|
92
|
+
context?.component?.data?.source ||
|
|
93
|
+
findParentComponent(context?.component.id, context?.screenData)?.data
|
|
94
|
+
?.source;
|
|
69
95
|
|
|
70
|
-
|
|
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
|
-
|
|
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
|
});
|
|
@@ -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(
|
|
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
|
|
486
|
+
function recoverFocus() {
|
|
497
487
|
if (!isCurrentFocusOnTheTopScreen()) {
|
|
498
|
-
// We've failed to set focused node on the new screen => run focus
|
|
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()
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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;
|
|
@@ -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
|