@applicaster/zapp-react-native-utils 13.0.0-alpha.1338463240 → 13.0.0-alpha.1360301199
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/ActionExecutor.ts +16 -9
- package/actionsExecutor/ActionExecutorContext.tsx +59 -8
- 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/helpers/hooks.ts +1 -1
- 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/types.ts +3 -0
- package/appUtils/accessibilityManager/index.ts +42 -1
- package/appUtils/contextKeysManager/contextResolver.ts +73 -0
- package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +6 -6
- package/appUtils/focusManager/index.ts +92 -64
- package/appUtils/focusManager/treeDataStructure/Tree/index.js +23 -0
- package/appUtils/focusManager/utils/enums.ts +4 -0
- 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/keys/keysByPlatform.ts +4 -0
- package/appUtils/keyInputHandler/KeyInputHandler.ts +68 -0
- package/appUtils/orientationHelper.ts +1 -1
- package/appUtils/platform/const.ts +14 -0
- package/appUtils/platform/platformUtils.ts +17 -1
- package/appUtils/playerManager/player.ts +17 -5
- package/appUtils/playerManager/playerNative.ts +3 -2
- package/appUtils/playerManager/usePlayerControllerSetup.tsx +3 -3
- package/arrayUtils/__tests__/__snapshots__/arrayUtils.test.ts.snap +4 -4
- package/functionUtils/__tests__/__snapshots__/functionUtils.test.js.snap +4 -4
- package/idleUtils/__tests__/withTimeout$.test.ts +37 -0
- package/idleUtils/index.ts +1 -1
- package/idleUtils/withTimeout.ts +15 -2
- package/logger/const.ts +9 -67
- package/manifestUtils/__tests__/createConfig.test.js +18 -16
- 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/package.json +3 -2
- package/reactHooks/autoscrolling/__tests__/useTrackCurrentAutoScrollingElement.test.ts +1 -1
- package/reactHooks/autoscrolling/__tests__/useTrackedView.test.tsx +1 -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 +1 -1
- package/reactHooks/feed/usePipesCacheReset.ts +5 -5
- package/reactHooks/flatList/__tests__/index.test.tsx +30 -21
- package/reactHooks/layout/__tests__/index.test.tsx +10 -10
- package/reactHooks/layout/useDimensions/helpers/__tests__/getInitialDimensions.native.test.js +14 -28
- package/reactHooks/navigation/__tests__/index.test.tsx +0 -4
- package/reactHooks/navigation/index.ts +22 -214
- package/reactHooks/navigation/useGetBottomTabBarHeight.ts +12 -0
- package/reactHooks/navigation/useGetNavBarTopBorderWidth.ts +14 -0
- package/reactHooks/navigation/useGetTabBarHeight.ts +12 -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/useRoute.ts +100 -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/reactUtils/__tests__/reactUtils.test.js +10 -28
- package/reactUtils/createContext/__tests__/__snapshots__/helpers.test.js.snap +19 -19
- package/reactUtils/createContext/__tests__/__snapshots__/index.test.js.snap +2 -2
- package/reactUtils/createContext/__tests__/__snapshots__/provider.test.js.snap +3 -3
- package/reactUtils/createContext/__tests__/helpers.test.js +8 -8
- package/theme/__mocks__/index.ts +4 -0
- package/time/Timer.ts +1 -1
- package/types.ts +1 -0
- package/manifestUtils/sharedConfiguration/screenPicker/__tests__/utils.test.js +0 -304
- package/manifestUtils/sharedConfiguration/screenPicker/stylesFields.js +0 -875
- package/manifestUtils/sharedConfiguration/screenPicker/utils.js +0 -130
- package/reactHooks/player/useAutoSeek.tsx +0 -87
- package/reactHooks/player/useSeek.tsx +0 -66
- package/reactHooks/player/useTapSeek.tsx +0 -58
- /package/reactHooks/player/__tests__/{useTapSeek.test.ts → useTapSeek._test.ts} +0 -0
|
@@ -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 {
|
|
@@ -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,73 @@
|
|
|
1
|
+
import { createLogger } from "../../logger";
|
|
2
|
+
import { ContextKeysManager } from "./index";
|
|
3
|
+
import * as R from "ramda";
|
|
4
|
+
|
|
5
|
+
const { log_error } = createLogger({
|
|
6
|
+
subsystem: "contextResolver",
|
|
7
|
+
category: "General",
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
interface IResolver {
|
|
11
|
+
resolve: (string) => Promise<string | number | object>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export class EntryResolver implements IResolver {
|
|
15
|
+
entry: ZappEntry;
|
|
16
|
+
|
|
17
|
+
constructor(entry: ZappEntry) {
|
|
18
|
+
this.entry = entry;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async resolve(key: string) {
|
|
22
|
+
if (!key || key.length === 0) {
|
|
23
|
+
return this.entry;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return R.view(R.lensPath(key.split(".")), this.entry);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export class ContextResolver implements IResolver {
|
|
31
|
+
resolve = async (compositeKey: string) =>
|
|
32
|
+
ContextKeysManager.instance.getKey(compositeKey);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const resolveObjectValues = async (
|
|
36
|
+
data: Record<string, any>,
|
|
37
|
+
resolvers: Record<string, IResolver> = null
|
|
38
|
+
) => {
|
|
39
|
+
const entries = Object.entries(data);
|
|
40
|
+
|
|
41
|
+
const exResolvers = { ctx: new ContextResolver(), ...resolvers };
|
|
42
|
+
|
|
43
|
+
const resolvedEntries = await Promise.all(
|
|
44
|
+
entries.map(async ([key, value]) => {
|
|
45
|
+
const regex = /@{([^/]+)\/([^}]*)}/;
|
|
46
|
+
|
|
47
|
+
if (typeof value !== "string") {
|
|
48
|
+
return [key, value];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const match = (value as string).match(regex);
|
|
52
|
+
|
|
53
|
+
if (match) {
|
|
54
|
+
const contextResolverName = match[1];
|
|
55
|
+
const compositeKey = match[2];
|
|
56
|
+
|
|
57
|
+
const resolver = exResolvers[contextResolverName] || exResolvers.ctx;
|
|
58
|
+
const resolvedValue = await resolver.resolve(compositeKey);
|
|
59
|
+
|
|
60
|
+
return [key, resolvedValue];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
log_error("resolveObjectValues: invalid context string", {
|
|
64
|
+
key,
|
|
65
|
+
value,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
return [key, null];
|
|
69
|
+
})
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
return Object.fromEntries(resolvedEntries);
|
|
73
|
+
};
|
|
@@ -1,14 +1,14 @@
|
|
|
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
8
|
"focusableTree": Tree {
|
|
9
9
|
"loadingCounter": 0,
|
|
10
|
-
"root":
|
|
11
|
-
"children":
|
|
10
|
+
"root": {
|
|
11
|
+
"children": [],
|
|
12
12
|
"component": null,
|
|
13
13
|
"id": "root",
|
|
14
14
|
"lastFocusedItem": null,
|
|
@@ -39,12 +39,12 @@ Object {
|
|
|
39
39
|
`;
|
|
40
40
|
|
|
41
41
|
exports[`focusManagerIOS should be defined 1`] = `
|
|
42
|
-
|
|
42
|
+
{
|
|
43
43
|
"focus": [Function],
|
|
44
44
|
"focusableTree": Tree {
|
|
45
45
|
"loadingCounter": 0,
|
|
46
|
-
"root":
|
|
47
|
-
"children":
|
|
46
|
+
"root": {
|
|
47
|
+
"children": [],
|
|
48
48
|
"component": null,
|
|
49
49
|
"id": "root",
|
|
50
50
|
"lastFocusedItem": null,
|
|
@@ -11,8 +11,9 @@ import {
|
|
|
11
11
|
haveSameParentBeforeRoot,
|
|
12
12
|
} from "./treeDataStructure/Utils";
|
|
13
13
|
import { subscriber } from "../../functionUtils";
|
|
14
|
-
import * as FOCUS_EVENTS from "./events";
|
|
15
14
|
import { coreLogger } from "../../logger";
|
|
15
|
+
import { ACTION } from "./utils/enums";
|
|
16
|
+
|
|
16
17
|
const logger = coreLogger.addSubsystem("focusManager");
|
|
17
18
|
|
|
18
19
|
const isFocusEnabled = (focusableItem): boolean => {
|
|
@@ -167,13 +168,82 @@ export const focusManager = (function () {
|
|
|
167
168
|
}
|
|
168
169
|
|
|
169
170
|
/**
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
*
|
|
171
|
+
* Computes the difference in parent groups between two nodes.
|
|
172
|
+
* It returns the IDs of parents that are present in the current node but not in the previous node,
|
|
173
|
+
* as well as the IDs of parents that are present in the previous node but not in the current node.
|
|
174
|
+
*
|
|
175
|
+
* @param {string} currentNodeID - The ID of the current node.
|
|
176
|
+
* @param {string} previousNodeID - The ID of the previous node.
|
|
177
|
+
* @returns {Object} An object containing the differences in parent IDs:
|
|
178
|
+
* - {Array<string>} currentNodeParentsIDs: Parent IDs present in the current node but not in the previous node.
|
|
179
|
+
* - {Array<string>} previousNodeParentsIDs: Parent IDs present in the previous node but not in the current node.
|
|
173
180
|
*/
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
181
|
+
const getParentDiffs = (currentNodeID, previousNodeID) => {
|
|
182
|
+
const currentNodeParentsIDs =
|
|
183
|
+
focusableTree.getParentGroupIds(currentNodeID);
|
|
184
|
+
|
|
185
|
+
const previousNodeParentsIDs =
|
|
186
|
+
focusableTree.getParentGroupIds(previousNodeID);
|
|
187
|
+
|
|
188
|
+
return {
|
|
189
|
+
currentNodeParentsIDs: R.difference(
|
|
190
|
+
currentNodeParentsIDs,
|
|
191
|
+
previousNodeParentsIDs
|
|
192
|
+
),
|
|
193
|
+
previousNodeParentsIDs: R.difference(
|
|
194
|
+
previousNodeParentsIDs,
|
|
195
|
+
currentNodeParentsIDs
|
|
196
|
+
),
|
|
197
|
+
};
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Updates the focus state of nodes identified by their IDs.
|
|
202
|
+
* For each ID, if `setFocus` is true, the node will receive focus;
|
|
203
|
+
* otherwise, it will lose focus.
|
|
204
|
+
*
|
|
205
|
+
* @param {Array<string>} ids - An array of node IDs to update.
|
|
206
|
+
* @param {boolean} setFocus - A flag indicating whether to set focus (true) or blur (false) on the nodes.
|
|
207
|
+
*/
|
|
208
|
+
const updateNodeFocus = (ids, action) => {
|
|
209
|
+
if (!ids || ids.length === 0) {
|
|
210
|
+
return; // Nothing to do
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const startNode = focusableTree.findInTree(ids[0]);
|
|
214
|
+
|
|
215
|
+
if (!startNode) {
|
|
216
|
+
return; // Start node not found
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const endNodeId = ids[ids.length - 1]; // Get the ID of the end node
|
|
220
|
+
|
|
221
|
+
let currentNode = startNode;
|
|
222
|
+
|
|
223
|
+
// Function to apply the action (focus or blur)
|
|
224
|
+
const applyAction = (node) => {
|
|
225
|
+
if (node && node.component) {
|
|
226
|
+
if (action === "focus") {
|
|
227
|
+
node.component.setFocus();
|
|
228
|
+
} else if (action === "blur") {
|
|
229
|
+
node.component.setBlur();
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
// Apply action to the starting node
|
|
235
|
+
applyAction(currentNode);
|
|
236
|
+
|
|
237
|
+
// Traverse upwards
|
|
238
|
+
while (currentNode && currentNode.id !== endNodeId) {
|
|
239
|
+
currentNode = currentNode.parent;
|
|
240
|
+
|
|
241
|
+
if (currentNode) {
|
|
242
|
+
// check if current node exists, as parent might be root
|
|
243
|
+
applyAction(currentNode);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
};
|
|
177
247
|
|
|
178
248
|
/**
|
|
179
249
|
* Sets the current focus to the provided id. If there is an existing focus, it will
|
|
@@ -181,34 +251,29 @@ export const focusManager = (function () {
|
|
|
181
251
|
* @private
|
|
182
252
|
* @param {String} id of the focusable to set as currentFocus
|
|
183
253
|
* @param {Object} direction of the navigation, which led to this focus change
|
|
184
|
-
* @param {Object} options
|
|
185
|
-
* @param {Boolean} options.groupFocusedChanged tells wether the focus has moved
|
|
186
254
|
* to another group or not. defaults to false
|
|
187
255
|
*/
|
|
188
|
-
function setFocus(
|
|
189
|
-
id: string,
|
|
190
|
-
direction?: FocusManager.Web.Direction,
|
|
191
|
-
options?: Partial<{ groupFocusedChanged: boolean }>
|
|
192
|
-
) {
|
|
256
|
+
function setFocus(id: string, direction?: FocusManager.Web.Direction) {
|
|
193
257
|
if (focusDisabled) return false;
|
|
194
258
|
|
|
195
|
-
const { groupFocusedChanged = false } = options || {};
|
|
196
|
-
|
|
197
259
|
// due to optimisiation it's recommanded to set currentFocusNode before setFocus
|
|
198
260
|
// is called, so there will be no delays on tree search.
|
|
199
261
|
// If currentFocusNode is null or undefined we still want it to findInTree
|
|
200
262
|
if (currentFocusNode?.id !== id) {
|
|
263
|
+
const { currentNodeParentsIDs, previousNodeParentsIDs } = getParentDiffs(
|
|
264
|
+
id,
|
|
265
|
+
currentFocusNode?.id
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
// Set focus on current node parents and blur on previous node parents
|
|
269
|
+
updateNodeFocus(currentNodeParentsIDs, ACTION.FOCUS);
|
|
270
|
+
updateNodeFocus(previousNodeParentsIDs, ACTION.BLUR);
|
|
271
|
+
|
|
201
272
|
currentFocusNode = focusableTree.findInTree(id);
|
|
202
273
|
}
|
|
203
274
|
|
|
204
275
|
setLastFocusOnParentNode(currentFocusNode);
|
|
205
276
|
|
|
206
|
-
const currentGroup = getCurrentGroup();
|
|
207
|
-
|
|
208
|
-
if (groupFocusedChanged && currentGroup) {
|
|
209
|
-
currentGroup.hasLostFocus(currentGroup, direction);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
277
|
focus(direction);
|
|
213
278
|
}
|
|
214
279
|
|
|
@@ -251,6 +316,7 @@ export const focusManager = (function () {
|
|
|
251
316
|
!focusableItem.isGroup &&
|
|
252
317
|
isFocusEnabled(focusableItem)
|
|
253
318
|
) {
|
|
319
|
+
// TODO: Move this to setFocus method
|
|
254
320
|
blur();
|
|
255
321
|
|
|
256
322
|
logger.log({
|
|
@@ -261,7 +327,7 @@ export const focusManager = (function () {
|
|
|
261
327
|
});
|
|
262
328
|
|
|
263
329
|
currentFocusNode = focusCandidate;
|
|
264
|
-
|
|
330
|
+
|
|
265
331
|
focusableItem && setFocus(focusCandidate.id, null);
|
|
266
332
|
|
|
267
333
|
return { success: true };
|
|
@@ -412,7 +478,7 @@ export const focusManager = (function () {
|
|
|
412
478
|
function isCurrentFocusOnTheTopScreen() {
|
|
413
479
|
const routes = R.pathOr([], ["root", "children"], focusableTree);
|
|
414
480
|
|
|
415
|
-
if (routes.length <= 1) {
|
|
481
|
+
if (currentFocusNode && routes.length <= 1) {
|
|
416
482
|
// there is only one or zero routes
|
|
417
483
|
return true;
|
|
418
484
|
}
|
|
@@ -448,20 +514,10 @@ export const focusManager = (function () {
|
|
|
448
514
|
const nextRect = nextFocusable.component.getRect();
|
|
449
515
|
|
|
450
516
|
if (nextRect && nextRect.width > 0) {
|
|
451
|
-
|
|
452
|
-
nextFocusable.component,
|
|
453
|
-
direction
|
|
454
|
-
);
|
|
455
|
-
|
|
517
|
+
// TODO: Move this to setFocus method
|
|
456
518
|
blur(direction);
|
|
457
519
|
|
|
458
|
-
|
|
459
|
-
nextFocusable.component.props.groupId !==
|
|
460
|
-
currentFocusNode?.component?.props?.groupId;
|
|
461
|
-
|
|
462
|
-
setFocus(nextFocusable.id, direction, {
|
|
463
|
-
groupFocusedChanged: groupChanged,
|
|
464
|
-
});
|
|
520
|
+
setFocus(nextFocusable.id, direction);
|
|
465
521
|
}
|
|
466
522
|
} else {
|
|
467
523
|
currentFocusNode.component.failedLostFocus(currentFocusNode, direction);
|
|
@@ -481,34 +537,6 @@ export const focusManager = (function () {
|
|
|
481
537
|
|
|
482
538
|
const { on, invokeHandler, removeHandler } = eventHandler;
|
|
483
539
|
|
|
484
|
-
on(FOCUS_EVENTS.FOCUS, (focusable, direction) => {
|
|
485
|
-
if (focusable.props.groupId) {
|
|
486
|
-
const parent = focusableTree.findInTree(focusable.props.groupId);
|
|
487
|
-
parent?.component?.onFocus(focusable.component, direction);
|
|
488
|
-
}
|
|
489
|
-
});
|
|
490
|
-
|
|
491
|
-
on(FOCUS_EVENTS.BLUR, (focusable, direction) => {
|
|
492
|
-
if (focusable.props.groupId) {
|
|
493
|
-
const parent = focusableTree.findInTree(focusable.props.groupId);
|
|
494
|
-
parent?.component?.onBlur(focusable.component, direction);
|
|
495
|
-
}
|
|
496
|
-
});
|
|
497
|
-
|
|
498
|
-
on(FOCUS_EVENTS.HAS_LOST_FOCUS, (focusable, direction) => {
|
|
499
|
-
if (focusable.props.groupId) {
|
|
500
|
-
const parent = focusableTree.findInTree(focusable.props.groupId);
|
|
501
|
-
parent?.component?.hasLostFocus(parent.component, direction);
|
|
502
|
-
}
|
|
503
|
-
});
|
|
504
|
-
|
|
505
|
-
on(FOCUS_EVENTS.WILL_RECEIVE_FOCUS, (focusable, direction) => {
|
|
506
|
-
if (focusable.props.groupId) {
|
|
507
|
-
const parent = focusableTree.findInTree(focusable.props.groupId);
|
|
508
|
-
parent?.component?.willReceiveFocus(focusable.component, direction);
|
|
509
|
-
}
|
|
510
|
-
});
|
|
511
|
-
|
|
512
540
|
/**
|
|
513
541
|
* this is the list of the functions available externally
|
|
514
542
|
* when importing the focus manager
|
|
@@ -231,4 +231,27 @@ export class Tree {
|
|
|
231
231
|
|
|
232
232
|
return retVal;
|
|
233
233
|
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Gets the IDs of all parent groups for a given node id.
|
|
237
|
+
* @public
|
|
238
|
+
* @param {String} nodeId - The id of the node to get parent IDs for.
|
|
239
|
+
* @returns {Array} An array of parent group IDs or an empty array if the node is not found.
|
|
240
|
+
*/
|
|
241
|
+
getParentGroupIds(nodeId) {
|
|
242
|
+
const parentIds = [];
|
|
243
|
+
let currentNode = this.findInTree(nodeId);
|
|
244
|
+
|
|
245
|
+
if (!currentNode) {
|
|
246
|
+
return []; // Node not found, return an empty array
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Traverse up the tree, collecting parent IDs
|
|
250
|
+
while (currentNode.parent && currentNode.parent.id !== "root") {
|
|
251
|
+
parentIds.push(currentNode.parent.id);
|
|
252
|
+
currentNode = currentNode.parent;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return parentIds;
|
|
256
|
+
}
|
|
234
257
|
}
|
|
@@ -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
|
},
|
|
@@ -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
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import * as ReactNative from "react-native";
|
|
3
3
|
import { isTV, platformSelect } from "../reactUtils";
|
|
4
4
|
import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
|
|
5
|
-
import { useIsTablet } from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
5
|
+
import { useIsTablet } from "@applicaster/zapp-react-native-utils/reactHooks/device/useIsTablet";
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
8
|
QUICK_BRICK_EVENTS,
|
|
@@ -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 */
|