@applicaster/zapp-react-native-utils 13.0.0-alpha.1436249127 → 13.0.0-alpha.1486614302
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/ActionExecutor.ts +16 -9
- package/actionsExecutor/ActionExecutorContext.tsx +159 -10
- package/alertUtils/alert.ts +148 -0
- package/alertUtils/index.ts +1 -148
- package/analyticsUtils/AnalyticPlayerListener.ts +18 -1
- package/analyticsUtils/__tests__/__snapshots__/analyticsManagerNoModule.test.js.snap +9 -9
- package/analyticsUtils/currentScreenProperities.ts +6 -4
- package/analyticsUtils/helpers/hooks.ts +1 -1
- package/appUtils/HooksManager/Hook.ts +6 -2
- package/appUtils/HooksManager/__tests__/__snapshots__/hook.test.js.snap +7 -7
- package/appUtils/HooksManager/__tests__/__snapshots__/hooksManager.test.js.snap +186 -186
- package/appUtils/HooksManager/index.ts +3 -1
- package/appUtils/HooksManager/types.ts +5 -2
- package/appUtils/accessibilityManager/index.ts +42 -1
- package/appUtils/contextKeysManager/contextResolver.ts +66 -0
- package/appUtils/contextKeysManager/index.ts +2 -2
- package/appUtils/contextKeysManager/utils/index.ts +2 -1
- package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +8 -6
- package/appUtils/focusManager/index.ts +24 -12
- package/appUtils/focusManagerAux/utils/index.ts +4 -3
- package/appUtils/keyCodes/__tests__/__snapshots__/keyCodes.test.js.snap +2 -2
- package/appUtils/keyCodes/__tests__/keyCodes.test.js +1 -1
- package/appUtils/keyCodes/index.ts +11 -7
- package/appUtils/keyCodes/keys/keysByPlatform.ts +4 -0
- package/appUtils/keyInputHandler/KeyInputHandler.ts +68 -0
- package/appUtils/orientationHelper.ts +11 -13
- package/appUtils/platform/const.ts +14 -0
- package/appUtils/platform/platformUtils.ts +47 -4
- package/appUtils/playerManager/index.ts +1 -1
- package/appUtils/playerManager/player.ts +42 -18
- package/appUtils/playerManager/playerNative.ts +18 -2
- package/appUtils/playerManager/usePlayerControllerSetup.tsx +3 -3
- package/arrayUtils/__tests__/__snapshots__/arrayUtils.test.ts.snap +4 -4
- package/dateUtils/index.ts +2 -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/__tests__/__snapshots__/functionUtils.test.js.snap +4 -4
- package/functionUtils/index.ts +46 -8
- package/idleUtils/__tests__/withTimeout$.test.ts +37 -0
- package/idleUtils/index.ts +1 -1
- package/idleUtils/withTimeout.ts +15 -2
- package/index.d.ts +0 -12
- package/logger/const.ts +9 -67
- package/manifestUtils/__tests__/createConfig.test.js +16 -16
- package/manifestUtils/defaultManifestConfigurations/player.js +14 -0
- package/manifestUtils/fonts/index.js +5 -6
- 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 +6 -6
- package/navigationUtils/__tests__/itemTypes.test.js +2 -2
- package/navigationUtils/__tests__/navigationUtils.test.js +2 -2
- package/navigationUtils/index.ts +3 -3
- package/numberUtils/__tests__/toPositiveNumberWithDefault.test.ts +23 -3
- package/numberUtils/index.ts +11 -5
- package/package.json +5 -10
- package/playerUtils/configurationGenerator.ts +14 -0
- package/reactHooks/advertising/index.ts +5 -5
- package/reactHooks/autoscrolling/__tests__/useTrackCurrentAutoScrollingElement.test.ts +1 -1
- package/reactHooks/autoscrolling/__tests__/useTrackedView.test.tsx +6 -8
- package/reactHooks/autoscrolling/useTrackedView.ts +12 -1
- package/reactHooks/cell-click/__tests__/index.test.js +10 -2
- package/reactHooks/cell-click/index.ts +7 -1
- package/reactHooks/feed/__tests__/useBuildPipesUrl.test.tsx +4 -4
- package/reactHooks/feed/__tests__/useFeedLoader.test.tsx +10 -11
- package/reactHooks/feed/useFeedLoader.tsx +5 -3
- package/reactHooks/feed/usePipesCacheReset.ts +5 -5
- package/reactHooks/flatList/__tests__/index.test.tsx +30 -21
- package/reactHooks/hookModal/hooks/useHookModalScreenData.ts +39 -0
- package/reactHooks/layout/__tests__/index.test.tsx +10 -10
- package/reactHooks/layout/useDimensions/helpers/__tests__/getInitialDimensions.native.test.js +14 -28
- package/reactHooks/layout/useDimensions/helpers/__tests__/getInitialDimensions.test.js +1 -1
- package/reactHooks/layout/useDimensions/useDimensions.ts +8 -0
- package/reactHooks/navigation/__mocks__/useNavigation.ts +3 -0
- package/reactHooks/navigation/__tests__/index.test.tsx +6 -21
- package/reactHooks/navigation/index.ts +34 -236
- package/reactHooks/navigation/useGetBottomTabBarHeight.ts +12 -0
- package/reactHooks/navigation/useGetNavBarTopBorderWidth.ts +14 -0
- package/reactHooks/navigation/useGetTabBarHeight.ts +10 -0
- package/reactHooks/navigation/useIsScreenActive.ts +17 -0
- package/reactHooks/navigation/useNavigationPluginData.ts +15 -0
- package/reactHooks/navigation/useNavigationType.ts +15 -0
- package/reactHooks/navigation/usePathname.ts +4 -0
- package/reactHooks/navigation/useProfilerLogging.ts +15 -0
- package/reactHooks/navigation/useRoute.ts +98 -0
- package/reactHooks/navigation/useUniqueRouteSuffix.ts +41 -0
- package/reactHooks/player/TVSeekControlller/ISeekController.ts +32 -0
- package/reactHooks/player/TVSeekControlller/LongPressSeekController.ts +71 -0
- package/reactHooks/player/TVSeekControlller/TVSeekController.ts +258 -0
- package/reactHooks/player/TVSeekControlller/TapSeekController.ts +53 -0
- package/reactHooks/player/__tests__/{useAutoSeek.test.tsx → useAutoSeek._test.tsx} +2 -3
- package/reactHooks/player/const.ts +0 -4
- package/reactHooks/player/index.ts +0 -6
- package/reactHooks/state/useRefWithInitialValue.ts +14 -0
- package/reactHooks/state/useStateFromSubscribe.ts +23 -0
- package/reactHooks/usePluginConfiguration.ts +1 -1
- package/reactHooks/useSubscriberFor.ts +4 -3
- package/reactUtils/__tests__/__snapshots__/reactUtilsHelpers.test.js.snap +16 -4
- package/reactUtils/__tests__/reactUtils.test.js +1 -37
- package/reactUtils/__tests__/reactUtilsHelpers.test.js +72 -71
- package/reactUtils/createContext/__tests__/__snapshots__/helpers.test.js.snap +13 -79
- package/reactUtils/createContext/__tests__/__snapshots__/{index.test.js.snap → index.test.ts.snap} +2 -2
- 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 +0 -11
- package/screenState/index.ts +24 -10
- package/services/js2native.ts +673 -0
- package/testUtils/index.tsx +0 -10
- package/theme/__mocks__/index.ts +4 -0
- package/theme/index.ts +1 -1
- package/time/BackgroundTimer.ts +55 -0
- package/time/Timer.ts +9 -6
- package/types.ts +1 -0
- package/utils/README.md +30 -0
- package/utils/chunk.ts +5 -0
- package/utils/index.ts +17 -0
- package/utils/times.ts +5 -0
- package/zappFrameworkUtils/localStorageHelper.ts +6 -0
- package/zappFrameworkUtils/loginPluginUtils.ts +6 -2
- package/zappFrameworkUtils/presentPluginOneTime.ts +2 -3
- package/zappFrameworkUtils/types.ts +11 -0
- package/reactHooks/player/useAutoSeek.tsx +0 -87
- package/reactHooks/player/useSeek.tsx +0 -66
- package/reactHooks/player/useTapSeek.tsx +0 -58
- package/reactUtils/createContext/__tests__/index-no-react.test.js +0 -27
- /package/reactHooks/player/__tests__/{useTapSeek.test.ts → useTapSeek._test.ts} +0 -0
|
@@ -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;
|
|
@@ -4,6 +4,8 @@ import { HOOKS_EVENTS } from "./constants";
|
|
|
4
4
|
export interface HookConfiguration {}
|
|
5
5
|
|
|
6
6
|
/* existing Hook Plugn props: sample */
|
|
7
|
+
|
|
8
|
+
/*
|
|
7
9
|
interface _HookModulePlugin {
|
|
8
10
|
Component:
|
|
9
11
|
| React.FC<HookPluginProps>
|
|
@@ -23,6 +25,7 @@ interface _HookModulePlugin {
|
|
|
23
25
|
presentUI: (payload?: HookPluginProps["payload"]) => void
|
|
24
26
|
) => any;
|
|
25
27
|
}
|
|
28
|
+
*/
|
|
26
29
|
|
|
27
30
|
//* Derived from usage */
|
|
28
31
|
export interface HookModule {
|
|
@@ -100,7 +103,7 @@ export interface Hook<T extends HookModule = HookModule> {
|
|
|
100
103
|
identifier: string;
|
|
101
104
|
configuration: T["configuration"];
|
|
102
105
|
setStateAndNotify(
|
|
103
|
-
event: typeof HOOKS_EVENTS[keyof typeof HOOKS_EVENTS],
|
|
106
|
+
event: (typeof HOOKS_EVENTS)[keyof typeof HOOKS_EVENTS],
|
|
104
107
|
opts: {
|
|
105
108
|
error?: typeof Error;
|
|
106
109
|
route?: string;
|
|
@@ -109,7 +112,7 @@ export interface Hook<T extends HookModule = HookModule> {
|
|
|
109
112
|
}
|
|
110
113
|
);
|
|
111
114
|
general?: {
|
|
112
|
-
transition_type: typeof HOOKS_EVENTS[keyof typeof HOOKS_EVENTS];
|
|
115
|
+
transition_type: (typeof HOOKS_EVENTS)[keyof typeof HOOKS_EVENTS];
|
|
113
116
|
allow_screen_plugin_presentation: boolean;
|
|
114
117
|
is_flow_blocker: boolean;
|
|
115
118
|
};
|
|
@@ -6,6 +6,9 @@ import { AccessibilityRole } from "react-native";
|
|
|
6
6
|
|
|
7
7
|
export class AccessibilityManager {
|
|
8
8
|
private static _instance: AccessibilityManager | null = null;
|
|
9
|
+
private headingTimeout: NodeJS.Timeout | null = null;
|
|
10
|
+
private WORDS_PER_MINUTE = 160;
|
|
11
|
+
private MINIMUM_PAUSE = 500;
|
|
9
12
|
private state$ = new BehaviorSubject<AccessibilityState>({
|
|
10
13
|
screenReaderEnabled: false,
|
|
11
14
|
reduceMotionEnabled: false,
|
|
@@ -20,6 +23,7 @@ export class AccessibilityManager {
|
|
|
20
23
|
|
|
21
24
|
private ttsManager = TTSManager.getInstance();
|
|
22
25
|
private localizations: { [key: string]: string } = {};
|
|
26
|
+
private headingQueue: string[] = [];
|
|
23
27
|
|
|
24
28
|
private constructor() {}
|
|
25
29
|
|
|
@@ -52,6 +56,28 @@ export class AccessibilityManager {
|
|
|
52
56
|
return this.state$.asObservable();
|
|
53
57
|
}
|
|
54
58
|
|
|
59
|
+
/** Calculates the reading time for a given text
|
|
60
|
+
* This method is a bit of a hack because we don't have a callback, or promise from VIZIO API
|
|
61
|
+
* @param text - The text to calculate the reading time for
|
|
62
|
+
* @returns The reading time in milliseconds
|
|
63
|
+
*/
|
|
64
|
+
private calculateReadingTime(text: string): number {
|
|
65
|
+
const words = text.trim().split(/\s+/).length;
|
|
66
|
+
|
|
67
|
+
return Math.max(
|
|
68
|
+
this.MINIMUM_PAUSE,
|
|
69
|
+
(words / this.WORDS_PER_MINUTE) * 60 * 1000
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Adds a heading to the queue, headings will be read before the next text
|
|
75
|
+
* Each heading will be read once and removed from the queue
|
|
76
|
+
*/
|
|
77
|
+
public addHeading(heading: string) {
|
|
78
|
+
this.headingQueue.push(heading);
|
|
79
|
+
}
|
|
80
|
+
|
|
55
81
|
/**
|
|
56
82
|
* text you want to be read, if you want to use localized text pass keyOfLocalizedText instead
|
|
57
83
|
* keyOfLocalizedText is the key to the localized text
|
|
@@ -85,7 +111,22 @@ export class AccessibilityManager {
|
|
|
85
111
|
textToRead = localizedMessage;
|
|
86
112
|
}
|
|
87
113
|
|
|
88
|
-
this.
|
|
114
|
+
if (this.headingQueue.length > 0) {
|
|
115
|
+
const heading = this.headingQueue.shift()!;
|
|
116
|
+
this.ttsManager?.readText(heading);
|
|
117
|
+
|
|
118
|
+
if (this.headingTimeout) {
|
|
119
|
+
clearTimeout(this.headingTimeout);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const pauseTime = this.calculateReadingTime(heading);
|
|
123
|
+
|
|
124
|
+
this.headingTimeout = setTimeout(() => {
|
|
125
|
+
this.ttsManager?.readText(textToRead);
|
|
126
|
+
}, pauseTime);
|
|
127
|
+
} else {
|
|
128
|
+
this.ttsManager?.readText(textToRead);
|
|
129
|
+
}
|
|
89
130
|
|
|
90
131
|
this.announcements$.next({
|
|
91
132
|
message: textToRead,
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { ContextKeysManager } from "./index";
|
|
2
|
+
import * as R from "ramda";
|
|
3
|
+
|
|
4
|
+
interface IResolver {
|
|
5
|
+
resolve: (string) => Promise<string | number | object>;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export class EntryResolver implements IResolver {
|
|
9
|
+
entry: ZappEntry;
|
|
10
|
+
|
|
11
|
+
constructor(entry: ZappEntry) {
|
|
12
|
+
this.entry = entry;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async resolve(key: string) {
|
|
16
|
+
if (!key || key.length === 0) {
|
|
17
|
+
return this.entry;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return R.view(R.lensPath(key.split(".")), this.entry);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export class ContextResolver implements IResolver {
|
|
25
|
+
resolve = async (compositeKey: string) =>
|
|
26
|
+
ContextKeysManager.instance.getKey(compositeKey);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const resolveObjectValues = async (
|
|
30
|
+
data: Record<string, any>,
|
|
31
|
+
resolvers: Record<string, IResolver> = null
|
|
32
|
+
) => {
|
|
33
|
+
const entries = Object.entries(data);
|
|
34
|
+
|
|
35
|
+
const exResolvers = { ctx: new ContextResolver(), ...resolvers };
|
|
36
|
+
|
|
37
|
+
const resolvedEntries = await Promise.all(
|
|
38
|
+
entries.map(async ([key, value]) => {
|
|
39
|
+
if (typeof value !== "string") {
|
|
40
|
+
return [key, value];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (!value.startsWith("@")) {
|
|
44
|
+
return [key, value];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const regex = /@{([^/]+)\/([^}]*)}/;
|
|
48
|
+
|
|
49
|
+
const match = (value as string).match(regex);
|
|
50
|
+
|
|
51
|
+
if (match) {
|
|
52
|
+
const contextResolverName = match[1];
|
|
53
|
+
const compositeKey = match[2];
|
|
54
|
+
|
|
55
|
+
const resolver = exResolvers[contextResolverName] || exResolvers.ctx;
|
|
56
|
+
const resolvedValue = await resolver.resolve(compositeKey);
|
|
57
|
+
|
|
58
|
+
return [key, resolvedValue];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return [key, value];
|
|
62
|
+
})
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
return Object.fromEntries(resolvedEntries);
|
|
66
|
+
};
|
|
@@ -16,12 +16,12 @@ import {
|
|
|
16
16
|
|
|
17
17
|
export { StorageLevel };
|
|
18
18
|
|
|
19
|
-
type KeyNameObj = {
|
|
19
|
+
export type KeyNameObj = {
|
|
20
20
|
namespace: string;
|
|
21
21
|
key: string;
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
-
type KeyName = string | KeyNameObj;
|
|
24
|
+
export type KeyName = string | KeyNameObj;
|
|
25
25
|
|
|
26
26
|
type ParsedKeyAndNamespace = {
|
|
27
27
|
namespace?: string;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import * as R from "ramda";
|
|
2
2
|
import { DEFAULT_NAMESPACE } from "../consts";
|
|
3
|
+
import { KeyName, KeyNameObj } from "../index";
|
|
3
4
|
|
|
4
5
|
const lastDotRegex = /\.([^.]+)$/;
|
|
5
6
|
const splitByLastDot = R.compose(R.init, R.split(lastDotRegex));
|
|
6
7
|
|
|
7
|
-
export function getNamespaceAndKey(namespacedKey) {
|
|
8
|
+
export function getNamespaceAndKey(namespacedKey: KeyName): KeyNameObj {
|
|
8
9
|
if (typeof namespacedKey !== "string") return namespacedKey;
|
|
9
10
|
|
|
10
11
|
const namespacedKeyProps = namespacedKey.includes(".")
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
2
|
|
|
3
3
|
exports[`focusManager should be defined 1`] = `
|
|
4
|
-
|
|
4
|
+
{
|
|
5
5
|
"blur": [Function],
|
|
6
6
|
"disableFocus": [Function],
|
|
7
7
|
"enableFocus": [Function],
|
|
8
|
+
"findPreferredFocusChild": [Function],
|
|
8
9
|
"focusableTree": Tree {
|
|
9
10
|
"loadingCounter": 0,
|
|
10
|
-
"root":
|
|
11
|
-
"children":
|
|
11
|
+
"root": {
|
|
12
|
+
"children": [],
|
|
12
13
|
"component": null,
|
|
13
14
|
"id": "root",
|
|
14
15
|
"lastFocusedItem": null,
|
|
@@ -28,6 +29,7 @@ Object {
|
|
|
28
29
|
"moveFocus": [Function],
|
|
29
30
|
"on": [Function],
|
|
30
31
|
"press": [Function],
|
|
32
|
+
"pressIn": [Function],
|
|
31
33
|
"pressOut": [Function],
|
|
32
34
|
"recoverFocus": [Function],
|
|
33
35
|
"register": [Function],
|
|
@@ -39,12 +41,12 @@ Object {
|
|
|
39
41
|
`;
|
|
40
42
|
|
|
41
43
|
exports[`focusManagerIOS should be defined 1`] = `
|
|
42
|
-
|
|
44
|
+
{
|
|
43
45
|
"focus": [Function],
|
|
44
46
|
"focusableTree": Tree {
|
|
45
47
|
"loadingCounter": 0,
|
|
46
|
-
"root":
|
|
47
|
-
"children":
|
|
48
|
+
"root": {
|
|
49
|
+
"children": [],
|
|
48
50
|
"component": null,
|
|
49
51
|
"id": "root",
|
|
50
52
|
"lastFocusedItem": null,
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
import { subscriber } from "../../functionUtils";
|
|
14
14
|
import { coreLogger } from "../../logger";
|
|
15
15
|
import { ACTION } from "./utils/enums";
|
|
16
|
+
|
|
16
17
|
const logger = coreLogger.addSubsystem("focusManager");
|
|
17
18
|
|
|
18
19
|
const isFocusEnabled = (focusableItem): boolean => {
|
|
@@ -325,8 +326,6 @@ export const focusManager = (function () {
|
|
|
325
326
|
},
|
|
326
327
|
});
|
|
327
328
|
|
|
328
|
-
currentFocusNode = focusCandidate;
|
|
329
|
-
|
|
330
329
|
focusableItem && setFocus(focusCandidate.id, null);
|
|
331
330
|
|
|
332
331
|
return { success: true };
|
|
@@ -335,15 +334,14 @@ export const focusManager = (function () {
|
|
|
335
334
|
return { success: false };
|
|
336
335
|
}
|
|
337
336
|
|
|
338
|
-
function treeLoaded(
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
});
|
|
337
|
+
function treeLoaded(_val, _node) {}
|
|
338
|
+
|
|
339
|
+
function pressIn() {
|
|
340
|
+
const currentFocusable = getCurrentFocus();
|
|
341
|
+
|
|
342
|
+
if (currentFocusable) {
|
|
343
|
+
currentFocusable.pressIn(null);
|
|
344
|
+
}
|
|
347
345
|
}
|
|
348
346
|
|
|
349
347
|
/**
|
|
@@ -477,7 +475,7 @@ export const focusManager = (function () {
|
|
|
477
475
|
function isCurrentFocusOnTheTopScreen() {
|
|
478
476
|
const routes = R.pathOr([], ["root", "children"], focusableTree);
|
|
479
477
|
|
|
480
|
-
if (routes.length <= 1) {
|
|
478
|
+
if (currentFocusNode && routes.length <= 1) {
|
|
481
479
|
// there is only one or zero routes
|
|
482
480
|
return true;
|
|
483
481
|
}
|
|
@@ -536,6 +534,18 @@ export const focusManager = (function () {
|
|
|
536
534
|
|
|
537
535
|
const { on, invokeHandler, removeHandler } = eventHandler;
|
|
538
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
|
+
|
|
539
549
|
/**
|
|
540
550
|
* this is the list of the functions available externally
|
|
541
551
|
* when importing the focus manager
|
|
@@ -553,6 +563,7 @@ export const focusManager = (function () {
|
|
|
553
563
|
invokeHandler,
|
|
554
564
|
removeHandler,
|
|
555
565
|
longPress,
|
|
566
|
+
pressIn,
|
|
556
567
|
pressOut,
|
|
557
568
|
enableFocus,
|
|
558
569
|
setInitialFocus,
|
|
@@ -564,5 +575,6 @@ export const focusManager = (function () {
|
|
|
564
575
|
isGroupItemFocused,
|
|
565
576
|
recoverFocus,
|
|
566
577
|
isCurrentFocusOnTheTopScreen,
|
|
578
|
+
findPreferredFocusChild,
|
|
567
579
|
};
|
|
568
580
|
})();
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { isNotNil } from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
|
|
2
|
-
import { find, last,
|
|
2
|
+
import { find, last, pathOr, startsWith } from "ramda";
|
|
3
3
|
import {
|
|
4
|
-
QUICK_BRICK_NAVBAR,
|
|
5
4
|
QUICK_BRICK_CONTENT,
|
|
5
|
+
QUICK_BRICK_NAVBAR,
|
|
6
6
|
} from "@applicaster/quick-brick-core/const";
|
|
7
7
|
|
|
8
8
|
// run check each 300 ms
|
|
@@ -66,7 +66,8 @@ export const getContentNode = (focusableTree) => {
|
|
|
66
66
|
const content = find(
|
|
67
67
|
(child) =>
|
|
68
68
|
startsWith(QUICK_BRICK_CONTENT, child.id) ||
|
|
69
|
-
startsWith("player", child.id)
|
|
69
|
+
startsWith("player", child.id) ||
|
|
70
|
+
startsWith("/hooks", child?.parent?.id), // For startup hooks case
|
|
70
71
|
getActiveScreenChildren(focusableTree)
|
|
71
72
|
);
|
|
72
73
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
2
|
|
|
3
3
|
exports[`keyCodes keyCode returns an object for a given key event 1`] = `
|
|
4
|
-
|
|
4
|
+
{
|
|
5
5
|
"direction": [Function],
|
|
6
6
|
"matches": [Function],
|
|
7
7
|
"matchesAny": [Function],
|
|
8
|
-
"value":
|
|
8
|
+
"value": {
|
|
9
9
|
"code": "Enter",
|
|
10
10
|
"keyCode": 13,
|
|
11
11
|
},
|
|
@@ -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;
|
|
@@ -77,6 +77,10 @@ const web = {
|
|
|
77
77
|
Exit: { code: "Exit", keyCode: null },
|
|
78
78
|
Escape: { code: "Escape", keyCode: 27 },
|
|
79
79
|
EscapeDevice: { code: "Escape", keyCode: 27 },
|
|
80
|
+
// For Debugging
|
|
81
|
+
Z: { code: "KeyZ", keyCode: 90 }, // Hardware Backward Button
|
|
82
|
+
X: { code: "KeyX", keyCode: 88 }, // Hardware Enter Button
|
|
83
|
+
C: { code: "KeyC", keyCode: 67 }, // Hardware Forward Button
|
|
80
84
|
};
|
|
81
85
|
|
|
82
86
|
const android_tv = {
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
export type SeekKeyHandlerListener = {
|
|
2
|
+
onRewindPress: () => boolean;
|
|
3
|
+
onForwardPress: () => boolean;
|
|
4
|
+
onRewindPressOut: () => void;
|
|
5
|
+
onForwardPressOut: () => void;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export class KeyInputHandler {
|
|
9
|
+
private static instance: KeyInputHandler;
|
|
10
|
+
|
|
11
|
+
public static getInstance() {
|
|
12
|
+
if (!KeyInputHandler.instance) {
|
|
13
|
+
KeyInputHandler.instance = new KeyInputHandler();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return KeyInputHandler.instance;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
constructor() {
|
|
20
|
+
this.listeners = [];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
private listeners: SeekKeyHandlerListener[] = [];
|
|
24
|
+
|
|
25
|
+
addListener = (listener: SeekKeyHandlerListener) => {
|
|
26
|
+
this.listeners.splice(0, 0, listener);
|
|
27
|
+
|
|
28
|
+
return () => this.removeListener(listener);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
removeListener = (listener: SeekKeyHandlerListener) => {
|
|
32
|
+
this.listeners = this.listeners.filter((l) => l !== listener);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
onRewindPress = () => {
|
|
36
|
+
for (const element of this.listeners) {
|
|
37
|
+
if (element.onRewindPress()) {
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
onForwardPress = () => {
|
|
44
|
+
for (const element of this.listeners) {
|
|
45
|
+
if (element.onForwardPress()) {
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// TODO: Event should be send to listeners who handled down event
|
|
52
|
+
onRewindPressOut = () => {
|
|
53
|
+
this.listeners.forEach((listener) => listener.onRewindPressOut());
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
onForwardPressOut = () => {
|
|
57
|
+
this.listeners.forEach((listener) => listener.onForwardPressOut());
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
getSeekMethods = () => {
|
|
61
|
+
return {
|
|
62
|
+
onForwardPress: this.onForwardPress,
|
|
63
|
+
onRewindPress: this.onRewindPress,
|
|
64
|
+
onForwardPressOut: this.onForwardPressOut,
|
|
65
|
+
onRewindPressOut: this.onRewindPressOut,
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
}
|
|
@@ -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
|
|
@@ -23,3 +23,17 @@ export const PLATFORMS = {
|
|
|
23
23
|
export const PLATFORM_KEYS = {
|
|
24
24
|
deviceClosedCaptioningEnabled: "device_closed_captioning_enabled",
|
|
25
25
|
};
|
|
26
|
+
|
|
27
|
+
export enum ZappPlatform {
|
|
28
|
+
Android = "android",
|
|
29
|
+
AndroidTV = "android_tv",
|
|
30
|
+
AmazonFireTV = "amazon",
|
|
31
|
+
iOS = "ios",
|
|
32
|
+
tvOS = "tvos",
|
|
33
|
+
Roku = "roku",
|
|
34
|
+
Tizen = "tizen",
|
|
35
|
+
Web = "web",
|
|
36
|
+
WebOS = "webos",
|
|
37
|
+
Vizio = "vizio",
|
|
38
|
+
Unknown = "unknown",
|
|
39
|
+
}
|
|
@@ -6,8 +6,10 @@ import {
|
|
|
6
6
|
isSamsungPlatform,
|
|
7
7
|
} from "@applicaster/zapp-react-dom-app/App/Loader/utils/platform";
|
|
8
8
|
|
|
9
|
-
import { PLATFORM_KEYS, PLATFORMS } from "./const";
|
|
9
|
+
import { PLATFORM_KEYS, PLATFORMS, ZappPlatform } from "./const";
|
|
10
10
|
import { createLogger, utilsLogger } from "../../logger";
|
|
11
|
+
import { getPlatform } from "../../reactUtils";
|
|
12
|
+
import { appStore } from "@applicaster/zapp-react-native-redux/AppStore";
|
|
11
13
|
|
|
12
14
|
const { log_debug } = createLogger({
|
|
13
15
|
category: "General",
|
|
@@ -15,6 +17,20 @@ const { log_debug } = createLogger({
|
|
|
15
17
|
parent: utilsLogger,
|
|
16
18
|
});
|
|
17
19
|
|
|
20
|
+
export const getZappPlatform = (): ZappPlatform => {
|
|
21
|
+
const platform = appStore.get("appData")?.platform;
|
|
22
|
+
|
|
23
|
+
switch (platform) {
|
|
24
|
+
case "amazon_fire_tv":
|
|
25
|
+
return ZappPlatform.AmazonFireTV;
|
|
26
|
+
case ZappPlatform.Vizio:
|
|
27
|
+
return ZappPlatform.Vizio;
|
|
28
|
+
default:
|
|
29
|
+
// TODO: Use only ZappPlatform enum
|
|
30
|
+
return getPlatform() as ZappPlatform;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
18
34
|
export const getUserAgent = () => window?.navigator?.userAgent;
|
|
19
35
|
|
|
20
36
|
/** VIZIO UTILS */
|
|
@@ -61,13 +77,22 @@ export class ClosedCaptioningManager {
|
|
|
61
77
|
|
|
62
78
|
private constructor() {
|
|
63
79
|
this.initialize();
|
|
80
|
+
|
|
81
|
+
window["vizioDebug"] = {
|
|
82
|
+
setCCEnabled: (isCCEnabled: boolean) => {
|
|
83
|
+
this.ccState$.next(isCCEnabled);
|
|
84
|
+
},
|
|
85
|
+
};
|
|
64
86
|
}
|
|
65
87
|
|
|
66
88
|
static getInstance(): ClosedCaptioningManager {
|
|
67
89
|
if (!ClosedCaptioningManager.ccManagerInstance) {
|
|
90
|
+
log_debug("ClosedCaptioningManager: Creating new instance");
|
|
68
91
|
ClosedCaptioningManager.ccManagerInstance = new ClosedCaptioningManager();
|
|
69
92
|
}
|
|
70
93
|
|
|
94
|
+
log_debug("ClosedCaptioningManager: Returning existing instance");
|
|
95
|
+
|
|
71
96
|
return ClosedCaptioningManager.ccManagerInstance;
|
|
72
97
|
}
|
|
73
98
|
|
|
@@ -76,23 +101,41 @@ export class ClosedCaptioningManager {
|
|
|
76
101
|
PLATFORM_KEYS.deviceClosedCaptioningEnabled
|
|
77
102
|
);
|
|
78
103
|
|
|
79
|
-
log_debug("Initializing closed captioning manager", { existingState });
|
|
80
|
-
|
|
81
104
|
if (existingState !== null) {
|
|
105
|
+
log_debug(
|
|
106
|
+
`ClosedCaptioningManager: Setting initial state to ${existingState}`
|
|
107
|
+
);
|
|
108
|
+
|
|
82
109
|
this.ccState$.next(existingState === "true" || existingState === true);
|
|
110
|
+
} else {
|
|
111
|
+
log_debug("ClosedCaptioningManager: No existing state found");
|
|
83
112
|
}
|
|
84
113
|
|
|
85
114
|
if (hasVizioAPIs()) {
|
|
115
|
+
log_debug("ClosedCaptioningManager: Setting closed captioning handler");
|
|
116
|
+
|
|
86
117
|
window.VIZIO!.setClosedCaptionHandler((isCCEnabled) => {
|
|
87
|
-
log_debug(
|
|
118
|
+
log_debug(
|
|
119
|
+
"ClosedCaptioningManager: There was a change in the CC state",
|
|
120
|
+
{ isCCEnabled }
|
|
121
|
+
);
|
|
88
122
|
|
|
89
123
|
sessionStorage.setItem(
|
|
90
124
|
PLATFORM_KEYS.deviceClosedCaptioningEnabled,
|
|
91
125
|
isCCEnabled
|
|
92
126
|
);
|
|
93
127
|
|
|
128
|
+
log_debug(
|
|
129
|
+
"ClosedCaptioningManager: Saved CC state to session storage",
|
|
130
|
+
{ isCCEnabled }
|
|
131
|
+
);
|
|
132
|
+
|
|
94
133
|
this.ccState$.next(isCCEnabled);
|
|
95
134
|
});
|
|
135
|
+
} else {
|
|
136
|
+
log_debug(
|
|
137
|
+
"ClosedCaptioningManager: Vizio APIs are not available, skipping setup"
|
|
138
|
+
);
|
|
96
139
|
}
|
|
97
140
|
}
|
|
98
141
|
|
|
@@ -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
|
}
|