@applicaster/zapp-react-native-utils 12.0.0-alpha.4837064762 → 12.0.0-alpha.4840257570
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/AnalyticsEvents/index.ts +6 -0
- package/analyticsUtils/AnalyticsEvents/searchEvents.ts +21 -0
- package/analyticsUtils/events.ts +6 -0
- package/analyticsUtils/index.tsx +6 -0
- package/appUtils/focusManager/index.ts +24 -127
- package/appUtils/focusManager/treeDataStructure/Utils/index.js +42 -97
- package/appUtils/keyCodes/keys.web.js +8 -0
- package/appUtils/keyCodes/keysByPlatform.js +11 -1
- package/cellUtils/__tests__/cellUtils.test.ts +9 -20
- package/cellUtils/index.ts +6 -12
- package/dateUtils/index.ts +11 -0
- package/headersUtils/index.ts +1 -0
- package/inflatedUrlUtils/index.ts +4 -4
- package/keyboardState/index.ts +0 -0
- package/localizationUtils/__tests__/localizationUtils.test.ts +72 -53
- package/localizationUtils/index.ts +13 -22
- package/numberUtils/index.ts +4 -0
- package/package.json +2 -2
- package/reactHooks/feed/__tests__/useInflatedUrl.test.ts +18 -3
- package/reactHooks/feed/useInflatedUrl.ts +44 -15
- package/reactHooks/navigation/index.ts +0 -15
- package/reactHooks/screen/useScreenContext.ts +2 -3
- package/reactUtils/helpers.tsx +1 -1
- package/stringUtils/index.ts +5 -0
- package/types.ts +4 -0
|
@@ -7,6 +7,10 @@ import { sendHeaderClickEvent } from "./sendHeaderClickEvent";
|
|
|
7
7
|
import { sendHardwareBackButtonClickEvent } from "./sendHardwareBackButtonClickEvent";
|
|
8
8
|
import { sendDownloadsEvent } from "./sendDownloadsEvent";
|
|
9
9
|
import { sendScreenEvent } from "./sendScreenEvent";
|
|
10
|
+
import {
|
|
11
|
+
sendSearchCanceledEvent,
|
|
12
|
+
sendSearchExecutedEvent,
|
|
13
|
+
} from "./searchEvents";
|
|
10
14
|
|
|
11
15
|
export {
|
|
12
16
|
sendScreenEvent,
|
|
@@ -18,4 +22,6 @@ export {
|
|
|
18
22
|
sendHeaderClickEvent,
|
|
19
23
|
sendHardwareBackButtonClickEvent,
|
|
20
24
|
sendDownloadsEvent,
|
|
25
|
+
sendSearchCanceledEvent,
|
|
26
|
+
sendSearchExecutedEvent,
|
|
21
27
|
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { log_debug } from "../logger";
|
|
2
|
+
import { postAnalyticEvent } from "../manager";
|
|
3
|
+
import { ANALYTICS_CORE_EVENTS } from "../events";
|
|
4
|
+
|
|
5
|
+
export const sendSearchCanceledEvent = (analyticsProps) => {
|
|
6
|
+
const eventName = ANALYTICS_CORE_EVENTS.SEARCH_CANCELED;
|
|
7
|
+
postAnalyticEvent(eventName, analyticsProps);
|
|
8
|
+
|
|
9
|
+
log_debug(`sendSearchCanceledEvent: send event: ${eventName}`, {
|
|
10
|
+
analyticsProps,
|
|
11
|
+
});
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const sendSearchExecutedEvent = (analyticsProps) => {
|
|
15
|
+
const eventName = ANALYTICS_CORE_EVENTS.SEARCH_EXECUTED;
|
|
16
|
+
postAnalyticEvent(eventName, analyticsProps);
|
|
17
|
+
|
|
18
|
+
log_debug(`sendSearchExecutedEvent: send event: ${eventName}`, {
|
|
19
|
+
analyticsProps,
|
|
20
|
+
});
|
|
21
|
+
};
|
package/analyticsUtils/events.ts
CHANGED
|
@@ -23,10 +23,16 @@ export const TAPPING_EVENTS = {
|
|
|
23
23
|
TAP_NAVBAR_BACK_BUTTON: "tap_navbar_back_button",
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
+
export const SEARCH_EVENTS = {
|
|
27
|
+
SEARCH_EXECUTED: "search_executed",
|
|
28
|
+
SEARCH_CANCELED: "search_canceled",
|
|
29
|
+
};
|
|
30
|
+
|
|
26
31
|
export const ANALYTICS_CORE_EVENTS = {
|
|
27
32
|
...LEGACY_EVENTS,
|
|
28
33
|
...SCREEN_VIEW_EVENTS,
|
|
29
34
|
...TAPPING_EVENTS,
|
|
35
|
+
...SEARCH_EVENTS,
|
|
30
36
|
};
|
|
31
37
|
|
|
32
38
|
export const WEBVIEW_EVENTS = {
|
package/analyticsUtils/index.tsx
CHANGED
|
@@ -18,6 +18,8 @@ import {
|
|
|
18
18
|
sendHardwareBackButtonClickEvent,
|
|
19
19
|
sendDownloadsEvent,
|
|
20
20
|
sendScreenEvent,
|
|
21
|
+
sendSearchCanceledEvent,
|
|
22
|
+
sendSearchExecutedEvent,
|
|
21
23
|
} from "./AnalyticsEvents";
|
|
22
24
|
|
|
23
25
|
import { postAnalyticEvent } from "./manager";
|
|
@@ -66,6 +68,8 @@ type AnalyticsFunctions = {
|
|
|
66
68
|
item: ZappEntry;
|
|
67
69
|
state: OfflineItemState;
|
|
68
70
|
}) => void;
|
|
71
|
+
sendSearchCanceledEvent: (props: AnalyticsDefaultProperties) => void;
|
|
72
|
+
sendSearchExecutedEvent: (props: AnalyticsDefaultProperties) => void;
|
|
69
73
|
};
|
|
70
74
|
|
|
71
75
|
type GetAnalyticsFunctions = (
|
|
@@ -102,6 +106,8 @@ export function getAnalyticsFunctions({
|
|
|
102
106
|
sendHardwareBackButtonClickEvent,
|
|
103
107
|
sendScreenEvent,
|
|
104
108
|
sendDownloadsEvent,
|
|
109
|
+
sendSearchCanceledEvent,
|
|
110
|
+
sendSearchExecutedEvent,
|
|
105
111
|
};
|
|
106
112
|
}
|
|
107
113
|
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import * as R from "ramda";
|
|
2
|
-
import { isNilOrEmpty } from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
|
|
3
|
-
|
|
4
2
|
import { Tree } from "./treeDataStructure/Tree";
|
|
5
3
|
import { findFocusableNode, findPriorityItem } from "./treeDataStructure/Utils";
|
|
6
4
|
import { subscriber } from "../../functionUtils";
|
|
@@ -19,22 +17,6 @@ const isFocusEnabled = (focusableItem): boolean => {
|
|
|
19
17
|
return true;
|
|
20
18
|
};
|
|
21
19
|
|
|
22
|
-
const waitUntil = (conditionFn) => {
|
|
23
|
-
let interval;
|
|
24
|
-
return new Promise((resolve, reject) => {
|
|
25
|
-
interval = setInterval(() => {
|
|
26
|
-
if (conditionFn()) {
|
|
27
|
-
clearInterval(interval);
|
|
28
|
-
resolve(undefined);
|
|
29
|
-
}
|
|
30
|
-
}, 1000);
|
|
31
|
-
|
|
32
|
-
setTimeout(() => {
|
|
33
|
-
reject();
|
|
34
|
-
}, 2500);
|
|
35
|
-
});
|
|
36
|
-
};
|
|
37
|
-
|
|
38
20
|
/**
|
|
39
21
|
* The focusManager is meant to keep references of focusables elements in the UI, and provide
|
|
40
22
|
* ways to assign the focus to one of the focusables.
|
|
@@ -64,7 +46,7 @@ export const focusManager = (function () {
|
|
|
64
46
|
let lastFocusedId = null;
|
|
65
47
|
let initialFocusOnContent = null;
|
|
66
48
|
let focusDisabled = false;
|
|
67
|
-
let focusAutoEnabled =
|
|
49
|
+
let focusAutoEnabled = true;
|
|
68
50
|
const eventHandler = subscriber();
|
|
69
51
|
|
|
70
52
|
function disableAutomaticFocus() {
|
|
@@ -111,7 +93,7 @@ export const focusManager = (function () {
|
|
|
111
93
|
!currentFocusable.isGroup &&
|
|
112
94
|
currentFocusable.isMounted()
|
|
113
95
|
) {
|
|
114
|
-
currentFocusable.blur(direction);
|
|
96
|
+
currentFocusable.blur(currentFocusable, direction);
|
|
115
97
|
currentFocusable.hasLostFocus(getCurrentFocus(), direction);
|
|
116
98
|
}
|
|
117
99
|
}
|
|
@@ -244,41 +226,31 @@ export const focusManager = (function () {
|
|
|
244
226
|
|
|
245
227
|
logger.log({ message: "setInitialFocus", data: { preferredFocus } });
|
|
246
228
|
|
|
247
|
-
if (
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
229
|
+
if (preferredFocus && preferredFocus.length > 0) {
|
|
230
|
+
const focusCandidate = preferredFocus[0];
|
|
231
|
+
const focusableItem = focusCandidate.component;
|
|
232
|
+
|
|
233
|
+
if (
|
|
234
|
+
focusableItem &&
|
|
235
|
+
!focusableItem.isGroup &&
|
|
236
|
+
isFocusEnabled(focusableItem)
|
|
237
|
+
) {
|
|
238
|
+
if (initialFocusOnContent) {
|
|
239
|
+
blur();
|
|
240
|
+
initialFocusOnContent = false;
|
|
241
|
+
}
|
|
256
242
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
243
|
+
logger.log({
|
|
244
|
+
message: "setInitialFocus – currentFocusNode",
|
|
245
|
+
data: {
|
|
246
|
+
focusableItem,
|
|
247
|
+
},
|
|
248
|
+
});
|
|
261
249
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
isFocusEnabled(focusableItem)
|
|
266
|
-
) {
|
|
267
|
-
if (initialFocusOnContent) {
|
|
268
|
-
blur();
|
|
269
|
-
initialFocusOnContent = false;
|
|
250
|
+
currentFocusNode = focusCandidate;
|
|
251
|
+
focusableItem.willReceiveFocus(focusableItem, null);
|
|
252
|
+
focusableItem && setFocus(focusCandidate.id, null);
|
|
270
253
|
}
|
|
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);
|
|
282
254
|
}
|
|
283
255
|
}
|
|
284
256
|
|
|
@@ -351,8 +323,6 @@ export const focusManager = (function () {
|
|
|
351
323
|
* @returns {React.Component} the registered component
|
|
352
324
|
*/
|
|
353
325
|
function registerGroup(id, component) {
|
|
354
|
-
// console.log("debug_2", "FM registerGroup", { id, component });
|
|
355
|
-
|
|
356
326
|
focusableTree.addNode({ id, component });
|
|
357
327
|
|
|
358
328
|
return component;
|
|
@@ -368,8 +338,6 @@ export const focusManager = (function () {
|
|
|
368
338
|
* @returns {React.Component} the registered component
|
|
369
339
|
*/
|
|
370
340
|
function registerItem(id, component) {
|
|
371
|
-
// console.log("debug_2", "FM registerItem", { id, component });
|
|
372
|
-
|
|
373
341
|
focusableTree.addNode({ id, component });
|
|
374
342
|
|
|
375
343
|
if (!currentFocusNode && id === lastFocusedId && !id.includes("menu")) {
|
|
@@ -386,8 +354,6 @@ export const focusManager = (function () {
|
|
|
386
354
|
* @param {String} id of the group to unregister
|
|
387
355
|
*/
|
|
388
356
|
function unregisterGroup(id) {
|
|
389
|
-
// console.log("debug_2", "FM unregisterGroup", { id });
|
|
390
|
-
|
|
391
357
|
focusableTree.findAndRemoveNode(id);
|
|
392
358
|
}
|
|
393
359
|
|
|
@@ -399,8 +365,6 @@ export const focusManager = (function () {
|
|
|
399
365
|
* @param {String} id of the Focusable to unregister
|
|
400
366
|
*/
|
|
401
367
|
function unregisterItem(id) {
|
|
402
|
-
// console.log("debug_2", "FM unregisterItem", { id });
|
|
403
|
-
|
|
404
368
|
focusableTree.findAndRemoveNode(id);
|
|
405
369
|
|
|
406
370
|
if (currentFocusNode && currentFocusNode.id === id) {
|
|
@@ -439,8 +403,6 @@ export const focusManager = (function () {
|
|
|
439
403
|
}
|
|
440
404
|
|
|
441
405
|
function setupInitialFocus(id: string, focusOnContent?: boolean) {
|
|
442
|
-
console.log("debug_2", "FM - setupInitialFocus", { id, focusOnContent });
|
|
443
|
-
|
|
444
406
|
if (focusOnContent) {
|
|
445
407
|
initialFocusOnContent = focusOnContent;
|
|
446
408
|
} else {
|
|
@@ -448,62 +410,6 @@ export const focusManager = (function () {
|
|
|
448
410
|
}
|
|
449
411
|
}
|
|
450
412
|
|
|
451
|
-
function setFocusOnMenu() {
|
|
452
|
-
const activeScreen = R.last(focusableTree.root.children);
|
|
453
|
-
const navbar = R.find(
|
|
454
|
-
(child) => R.startsWith("navbar", child.id),
|
|
455
|
-
activeScreen.children
|
|
456
|
-
);
|
|
457
|
-
|
|
458
|
-
const selectedMenuItemId = R.find(
|
|
459
|
-
(child) => child.component.props.selected,
|
|
460
|
-
navbar?.children || []
|
|
461
|
-
)?.id;
|
|
462
|
-
|
|
463
|
-
if (selectedMenuItemId) {
|
|
464
|
-
// set focus on selected menu item
|
|
465
|
-
setFocus(selectedMenuItemId);
|
|
466
|
-
return;
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
const firstMenuItemId = R.path(["children", 0, "id"], navbar);
|
|
470
|
-
|
|
471
|
-
if (firstMenuItemId) {
|
|
472
|
-
// set focus on first menu item
|
|
473
|
-
setFocus(firstMenuItemId);
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
function setFocusOnContent() {
|
|
478
|
-
// we need to put focus on content here
|
|
479
|
-
// For doing it we have to wait registration of first focusable node into focusableTree
|
|
480
|
-
// this is the temporary hack with waiting until content node starts having children
|
|
481
|
-
const getContentNode = () => {
|
|
482
|
-
const activeScreen = R.last(focusableTree.root.children);
|
|
483
|
-
const content = R.find(
|
|
484
|
-
(child) => R.startsWith("content", child.id),
|
|
485
|
-
activeScreen.children
|
|
486
|
-
);
|
|
487
|
-
|
|
488
|
-
return content;
|
|
489
|
-
};
|
|
490
|
-
|
|
491
|
-
const contentHasAnyChildren = () => {
|
|
492
|
-
return getContentNode()?.children.length > 0;
|
|
493
|
-
};
|
|
494
|
-
|
|
495
|
-
waitUntil(contentHasAnyChildren)
|
|
496
|
-
.then(() => {
|
|
497
|
-
const content = getContentNode();
|
|
498
|
-
|
|
499
|
-
setInitialFocus(content);
|
|
500
|
-
})
|
|
501
|
-
.catch(() => {
|
|
502
|
-
// we failed to wait any content, put focus on menu as a last chance
|
|
503
|
-
setFocusOnMenu();
|
|
504
|
-
});
|
|
505
|
-
}
|
|
506
|
-
|
|
507
413
|
/**
|
|
508
414
|
* moves the focus in a provided direction
|
|
509
415
|
* @public
|
|
@@ -516,13 +422,6 @@ export const focusManager = (function () {
|
|
|
516
422
|
currentFocusNode.component.willLoseFocus(currentFocusNode, direction);
|
|
517
423
|
const nextFocusable = findFocusableNode(direction, currentFocusNode);
|
|
518
424
|
|
|
519
|
-
console.log("debug_2", "FM moveFocus", {
|
|
520
|
-
direction: direction?.value,
|
|
521
|
-
currentFocusNode,
|
|
522
|
-
focusableTree,
|
|
523
|
-
nextFocusable,
|
|
524
|
-
});
|
|
525
|
-
|
|
526
425
|
if (nextFocusable?.component?.isMounted?.()) {
|
|
527
426
|
const nextRect = nextFocusable.component.getRect();
|
|
528
427
|
|
|
@@ -619,7 +518,5 @@ export const focusManager = (function () {
|
|
|
619
518
|
enableAutomaticFocus,
|
|
620
519
|
getGroupRootById,
|
|
621
520
|
isGroupItemFocused,
|
|
622
|
-
setFocusOnMenu,
|
|
623
|
-
setFocusOnContent,
|
|
624
521
|
};
|
|
625
522
|
})();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as R from "ramda";
|
|
2
2
|
import { isNilOrEmpty } from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
|
|
3
3
|
|
|
4
4
|
import { rejectItemByPath } from "../../../../objectUtils";
|
|
@@ -143,49 +143,7 @@ const hasChildren = (target) =>
|
|
|
143
143
|
|
|
144
144
|
const hasComponent = (target) => !!target?.component;
|
|
145
145
|
|
|
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
|
-
};
|
|
146
|
+
const hasComponentAndChildren = R.allPass([hasComponent, hasChildren]);
|
|
189
147
|
|
|
190
148
|
/**
|
|
191
149
|
* Get target in shortest distance in specified direction
|
|
@@ -215,32 +173,52 @@ export function getTargetInDirection(
|
|
|
215
173
|
|
|
216
174
|
const distances = targets
|
|
217
175
|
.filter(hasComponentAndChildren)
|
|
218
|
-
.
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
176
|
+
.map((target) => {
|
|
177
|
+
const componentRect = target.component?.getRect();
|
|
178
|
+
|
|
179
|
+
const distance = measureDistance(
|
|
180
|
+
direction,
|
|
181
|
+
comparisonRect,
|
|
182
|
+
componentRect
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
if (distance === -1) {
|
|
186
|
+
// ignores offscreen targets when moving up/down
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
224
189
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
190
|
+
const orientationDistance = measureDistance(
|
|
191
|
+
direction,
|
|
192
|
+
comparisonRect,
|
|
193
|
+
componentRect
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
const euclidDistance = measureEuclidianDistanceToCenter(
|
|
197
|
+
comparisonRect,
|
|
198
|
+
componentRect
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
return {
|
|
202
|
+
distance:
|
|
203
|
+
orientationDistance > 0 ? euclidDistance : orientationDistance,
|
|
204
|
+
target,
|
|
205
|
+
};
|
|
206
|
+
})
|
|
207
|
+
.filter((obj) => obj && obj.distance >= 0);
|
|
228
208
|
|
|
229
209
|
const match = distances
|
|
230
210
|
.sort((a, b) => a.distance - b.distance) // sorts the distances
|
|
231
211
|
.filter(ignoreMenuIfMultipleTargets)[0]; // filters out multiple targets if necessary // gets the first element
|
|
232
212
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
return match?.target;
|
|
213
|
+
return distances && distances.length > 0 ? match.target : null;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function ignoreMenuIfMultipleTargets(nodes) {
|
|
217
|
+
if (nodes.length > 1 && nodes.map((n) => n?.target?.id).includes("navbar")) {
|
|
218
|
+
return nodes.filter((n) => n?.target?.id !== "navbar");
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return nodes;
|
|
244
222
|
}
|
|
245
223
|
|
|
246
224
|
/**
|
|
@@ -331,36 +309,3 @@ export function hasPriorityInDirection(direction, component) {
|
|
|
331
309
|
|
|
332
310
|
return (prioritiseFocusOn & mappedDirection) > 0;
|
|
333
311
|
}
|
|
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
|
-
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/* global tizen */
|
|
2
2
|
const isTizen = typeof tizen !== "undefined";
|
|
3
3
|
|
|
4
|
+
import { isVizioPlatform } from "@applicaster/zapp-react-dom-app/App/Loader/utils/platform";
|
|
4
5
|
import * as platformKeys from "./keysByPlatform";
|
|
5
6
|
import { Platform } from "react-native";
|
|
6
7
|
|
|
@@ -21,6 +22,13 @@ export const ARROW_KEYS = [
|
|
|
21
22
|
KEYS.ArrowRight,
|
|
22
23
|
];
|
|
23
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Platform.OS currently identifies Vizio as LG_TV, therefore we are using isVizioPlatform to identify platform and add keys.
|
|
27
|
+
*/
|
|
28
|
+
if (isVizioPlatform()) {
|
|
29
|
+
Object.assign(KEYS, platformKeys.vizio);
|
|
30
|
+
}
|
|
31
|
+
|
|
24
32
|
/**
|
|
25
33
|
* In TizenOS we need to register all of the keys that we are going to use.
|
|
26
34
|
* You need to register using the name Tizen has for the key.
|
|
@@ -102,4 +102,14 @@ const tvos = {
|
|
|
102
102
|
Menu: { code: "Menu", eventType: "menu" },
|
|
103
103
|
};
|
|
104
104
|
|
|
105
|
-
|
|
105
|
+
const vizio = {
|
|
106
|
+
...samsung_tv,
|
|
107
|
+
Exit: { code: "Exit", keyCode: 27 },
|
|
108
|
+
PageUp: { code: "PageUp", keyCode: 33 },
|
|
109
|
+
PageDown: { code: "PageDown", keyCode: 34 },
|
|
110
|
+
MediaTrackNext: { code: "MediaTrackNext", keyCode: 418 },
|
|
111
|
+
MediaTrackPrevious: { code: "MediaTrackPrevious", keyCode: 419 },
|
|
112
|
+
PrevCh: { code: "PrevCh", keyCode: 500 },
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
export { lg_tv, samsung_tv, web, tvos, android_tv, vizio };
|
|
@@ -24,7 +24,6 @@ import {
|
|
|
24
24
|
EMPTY,
|
|
25
25
|
FALLBACK,
|
|
26
26
|
TRANSFORM,
|
|
27
|
-
defaultColor,
|
|
28
27
|
} from "./fixtures";
|
|
29
28
|
|
|
30
29
|
describe("getKeyForLabel", () => {
|
|
@@ -395,9 +394,7 @@ describe("getColorFromData", () => {
|
|
|
395
394
|
it("should return value directly if valid color", () => {
|
|
396
395
|
const result = getColorFromData({
|
|
397
396
|
data: entry,
|
|
398
|
-
|
|
399
|
-
getValue: getValue(componentStyle),
|
|
400
|
-
defaultColor,
|
|
397
|
+
valueFromLayout: "#123456",
|
|
401
398
|
});
|
|
402
399
|
|
|
403
400
|
expect(result).toBe("#123456");
|
|
@@ -406,9 +403,7 @@ describe("getColorFromData", () => {
|
|
|
406
403
|
it("should return valid rgba color name", () => {
|
|
407
404
|
const result = getColorFromData({
|
|
408
405
|
data: entry,
|
|
409
|
-
|
|
410
|
-
getValue: getValue(componentStyle),
|
|
411
|
-
defaultColor,
|
|
406
|
+
valueFromLayout: "rgba(255, 255, 255, 0)",
|
|
412
407
|
});
|
|
413
408
|
|
|
414
409
|
expect(result).toBe("rgba(255, 255, 255, 0)");
|
|
@@ -417,33 +412,27 @@ describe("getColorFromData", () => {
|
|
|
417
412
|
it("should return path value if path value is set and is valid color", () => {
|
|
418
413
|
const result = getColorFromData({
|
|
419
414
|
data: entry,
|
|
420
|
-
|
|
421
|
-
getValue: getValue(componentStyle),
|
|
422
|
-
defaultColor,
|
|
415
|
+
valueFromLayout: "background_color",
|
|
423
416
|
});
|
|
424
417
|
|
|
425
418
|
expect(result).toBe("#123456");
|
|
426
419
|
});
|
|
427
420
|
|
|
428
|
-
it("should return
|
|
421
|
+
it("should return path value if path value is set, nested and is valid color", () => {
|
|
429
422
|
const result = getColorFromData({
|
|
430
423
|
data: entry,
|
|
431
|
-
|
|
432
|
-
getValue: getValue(componentStyle),
|
|
433
|
-
defaultColor,
|
|
424
|
+
valueFromLayout: "extensions.background_color",
|
|
434
425
|
});
|
|
435
426
|
|
|
436
|
-
expect(result).toBe(
|
|
427
|
+
expect(result).toBe("#123456");
|
|
437
428
|
});
|
|
438
429
|
|
|
439
|
-
it("should return
|
|
430
|
+
it("should return valueFromLayout if path does not exist", () => {
|
|
440
431
|
const result = getColorFromData({
|
|
441
432
|
data: entry,
|
|
442
|
-
|
|
443
|
-
getValue: getValue(componentStyle),
|
|
444
|
-
defaultColor,
|
|
433
|
+
valueFromLayout: "invalid_color",
|
|
445
434
|
});
|
|
446
435
|
|
|
447
|
-
expect(result).toBe(
|
|
436
|
+
expect(result).toBe("invalid_color");
|
|
448
437
|
});
|
|
449
438
|
});
|
package/cellUtils/index.ts
CHANGED
|
@@ -447,30 +447,24 @@ export const toResizeMode = (
|
|
|
447
447
|
|
|
448
448
|
type GetColorFromData = {
|
|
449
449
|
data: Record<string, any>;
|
|
450
|
-
|
|
451
|
-
getValue: Function;
|
|
452
|
-
defaultColor: string;
|
|
450
|
+
valueFromLayout: string;
|
|
453
451
|
};
|
|
454
452
|
|
|
455
453
|
export const getColorFromData = ({
|
|
456
454
|
data,
|
|
457
|
-
|
|
458
|
-
getValue,
|
|
459
|
-
defaultColor,
|
|
455
|
+
valueFromLayout,
|
|
460
456
|
}: GetColorFromData): string => {
|
|
461
|
-
const value = getValue(key);
|
|
462
|
-
|
|
463
457
|
// Temporary hack to fix color validation when alpha is floating point number
|
|
464
458
|
// https://github.com/dreamyguy/validate-color/issues/44
|
|
465
|
-
if (validateColor(
|
|
466
|
-
return
|
|
459
|
+
if (validateColor(valueFromLayout.replace(".00", ""))) {
|
|
460
|
+
return valueFromLayout;
|
|
467
461
|
}
|
|
468
462
|
|
|
469
|
-
const pathValue = R.path(
|
|
463
|
+
const pathValue = R.path(valueFromLayout.split("."), data);
|
|
470
464
|
|
|
471
465
|
if (pathValue && validateColor(pathValue)) {
|
|
472
466
|
return pathValue;
|
|
473
467
|
}
|
|
474
468
|
|
|
475
|
-
return
|
|
469
|
+
return valueFromLayout;
|
|
476
470
|
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import dayjs from "dayjs";
|
|
2
|
+
import utc from "dayjs/plugin/utc";
|
|
3
|
+
import timezone from "dayjs/plugin/timezone";
|
|
4
|
+
import isoWeek from "dayjs/plugin/isoWeek";
|
|
5
|
+
|
|
6
|
+
// Extend dayjs with plugins
|
|
7
|
+
dayjs.extend(utc);
|
|
8
|
+
dayjs.extend(timezone);
|
|
9
|
+
dayjs.extend(isoWeek);
|
|
10
|
+
|
|
11
|
+
export { dayjs };
|
package/headersUtils/index.ts
CHANGED
|
@@ -28,5 +28,6 @@ export const getAppDataHeaders = (screenId: string) => {
|
|
|
28
28
|
"x-applicaster-signed-device-info-token": appData.signedDeviceInfoToken,
|
|
29
29
|
"x-applicaster-device-make": appData.deviceMake,
|
|
30
30
|
"x-applicaster-device-model": appData.deviceModel,
|
|
31
|
+
"x-applicaster-uuid": appData?.uuid,
|
|
31
32
|
};
|
|
32
33
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { last, head, isNil, includes,
|
|
1
|
+
import { last, head, isNil, includes, pathOr } from "ramda";
|
|
2
2
|
|
|
3
3
|
const stripBrackets = (path) => {
|
|
4
4
|
const withoutPrepened = last(path.split("{{"));
|
|
@@ -13,15 +13,15 @@ const hasValue = (context) => (path) => {
|
|
|
13
13
|
return !isNil(value);
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
-
export const
|
|
16
|
+
export const getMissingValues = (context, string): string[] => {
|
|
17
17
|
if (!includes("{{", string)) {
|
|
18
|
-
return
|
|
18
|
+
return [];
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
const SEARCH_REGEX = new RegExp(/{{.*?}}/g);
|
|
22
22
|
const paths = string.toString().match(SEARCH_REGEX);
|
|
23
23
|
|
|
24
|
-
return
|
|
24
|
+
return paths.filter((path) => !hasValue(context)(path));
|
|
25
25
|
};
|
|
26
26
|
|
|
27
27
|
export const isNonInflatedUrl = (url: string): boolean =>
|
|
File without changes
|
|
@@ -1,59 +1,59 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
const mocked_state = {
|
|
4
|
-
remoteConfigurations: { localizations: {} },
|
|
5
|
-
appData: {
|
|
6
|
-
languageCode: "en",
|
|
7
|
-
countryCode: "US",
|
|
8
|
-
},
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
let mocked_locale = ["en-US"];
|
|
12
|
-
|
|
13
|
-
jest.doMock("react-redux", () => ({
|
|
14
|
-
useSelector: jest.fn((fn) => fn(mocked_state)),
|
|
15
|
-
}));
|
|
1
|
+
import { NativeModules } from "react-native";
|
|
16
2
|
|
|
17
|
-
const
|
|
3
|
+
const settingsManagerMock = {
|
|
18
4
|
getConstants: jest.fn(() => ({
|
|
19
|
-
settings: { AppleLanguages:
|
|
5
|
+
settings: { AppleLanguages: ["en-US"] },
|
|
20
6
|
})),
|
|
21
7
|
};
|
|
22
8
|
|
|
23
|
-
const
|
|
9
|
+
const qbCommunicationModuleMock = {
|
|
24
10
|
languageLocale: "en",
|
|
25
11
|
countryLocale: "US",
|
|
12
|
+
uiLanguage: "en-US",
|
|
26
13
|
};
|
|
27
14
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
ReactNative.NativeModules.QuickBrickCommunicationModule =
|
|
31
|
-
QuickBrickCommunicationModule;
|
|
32
|
-
|
|
33
|
-
const { useIsRTL } = require("../index");
|
|
15
|
+
NativeModules.SettingsManager = settingsManagerMock;
|
|
16
|
+
NativeModules.QuickBrickCommunicationModule = qbCommunicationModuleMock;
|
|
34
17
|
|
|
35
18
|
describe("localizationUtils", () => {
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
jest.resetModules();
|
|
21
|
+
});
|
|
22
|
+
|
|
36
23
|
describe("when the app has a single locale", () => {
|
|
37
24
|
describe("when the app has only a RTL language as locale", () => {
|
|
38
25
|
beforeEach(() => {
|
|
39
|
-
|
|
40
|
-
|
|
26
|
+
jest.resetModules();
|
|
27
|
+
const RN = require("react-native");
|
|
28
|
+
|
|
29
|
+
RN.NativeModules.QuickBrickCommunicationModule = {
|
|
30
|
+
languageLocale: "he",
|
|
31
|
+
countryLocale: "IL",
|
|
32
|
+
uiLanguage: "he-HE",
|
|
41
33
|
};
|
|
42
34
|
});
|
|
43
35
|
|
|
44
36
|
it("useIsRTL returns true", () => {
|
|
37
|
+
const { useIsRTL } = require("../index");
|
|
45
38
|
expect(useIsRTL()).toBe(true);
|
|
46
39
|
});
|
|
47
40
|
});
|
|
48
41
|
|
|
49
42
|
describe("when the app has only a LTR language as locale", () => {
|
|
50
43
|
beforeEach(() => {
|
|
51
|
-
|
|
52
|
-
|
|
44
|
+
jest.resetModules();
|
|
45
|
+
|
|
46
|
+
const RN = require("react-native");
|
|
47
|
+
|
|
48
|
+
RN.NativeModules.QuickBrickCommunicationModule = {
|
|
49
|
+
languageLocale: "en",
|
|
50
|
+
countryLocale: "US",
|
|
51
|
+
uiLanguage: "en",
|
|
53
52
|
};
|
|
54
53
|
});
|
|
55
54
|
|
|
56
55
|
it("useIsRTL returns false", () => {
|
|
56
|
+
const { useIsRTL } = require("../index");
|
|
57
57
|
expect(useIsRTL()).toBe(false);
|
|
58
58
|
});
|
|
59
59
|
});
|
|
@@ -63,45 +63,56 @@ describe("localizationUtils", () => {
|
|
|
63
63
|
describe("when device locale is in the app's locales", () => {
|
|
64
64
|
describe("when device locale is LTR", () => {
|
|
65
65
|
beforeEach(() => {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
};
|
|
66
|
+
jest.resetModules();
|
|
67
|
+
|
|
68
|
+
const RN = require("react-native");
|
|
70
69
|
|
|
71
|
-
|
|
70
|
+
RN.NativeModules.QuickBrickCommunicationModule = {
|
|
71
|
+
languageLocale: "en",
|
|
72
|
+
countryLocale: "US",
|
|
73
|
+
uiLanguage: "en-US",
|
|
74
|
+
};
|
|
72
75
|
});
|
|
73
76
|
|
|
74
77
|
it("useIsRTL returns false", () => {
|
|
78
|
+
const { useIsRTL } = require("../index");
|
|
75
79
|
expect(useIsRTL()).toBe(false);
|
|
76
80
|
});
|
|
77
81
|
});
|
|
78
82
|
|
|
79
83
|
describe("when device locale is RTL", () => {
|
|
80
84
|
beforeEach(() => {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
en: "http://url.com",
|
|
84
|
-
};
|
|
85
|
+
jest.resetModules();
|
|
86
|
+
const RN = require("react-native");
|
|
85
87
|
|
|
86
|
-
|
|
88
|
+
RN.NativeModules.QuickBrickCommunicationModule = {
|
|
89
|
+
languageLocale: "he",
|
|
90
|
+
countryLocale: "IL",
|
|
91
|
+
uiLanguage: "he",
|
|
92
|
+
};
|
|
87
93
|
});
|
|
88
94
|
|
|
89
95
|
it("useIsRTL returns true", () => {
|
|
96
|
+
const { useIsRTL } = require("../index");
|
|
90
97
|
expect(useIsRTL()).toBe(true);
|
|
91
98
|
});
|
|
92
99
|
});
|
|
93
100
|
|
|
94
101
|
describe("when a device has a secondary language in a supported language", () => {
|
|
95
102
|
beforeEach(() => {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
};
|
|
103
|
+
jest.resetModules();
|
|
104
|
+
|
|
105
|
+
const RN = require("react-native");
|
|
100
106
|
|
|
101
|
-
|
|
107
|
+
RN.NativeModules.QuickBrickCommunicationModule = {
|
|
108
|
+
languageLocale: "he",
|
|
109
|
+
countryLocale: "IL",
|
|
110
|
+
uiLanguage: "he",
|
|
111
|
+
};
|
|
102
112
|
});
|
|
103
113
|
|
|
104
114
|
it("useIsRTL returns true", () => {
|
|
115
|
+
const { useIsRTL } = require("../index");
|
|
105
116
|
expect(useIsRTL()).toBe(true);
|
|
106
117
|
});
|
|
107
118
|
});
|
|
@@ -110,30 +121,38 @@ describe("localizationUtils", () => {
|
|
|
110
121
|
describe("when device locale is not in the app's locales", () => {
|
|
111
122
|
describe("when first app locale is LTR", () => {
|
|
112
123
|
beforeEach(() => {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
};
|
|
124
|
+
jest.resetModules();
|
|
125
|
+
|
|
126
|
+
const RN = require("react-native");
|
|
117
127
|
|
|
118
|
-
|
|
128
|
+
RN.NativeModules.QuickBrickCommunicationModule = {
|
|
129
|
+
languageLocale: "fr",
|
|
130
|
+
countryLocale: "FR",
|
|
131
|
+
uiLanguage: "fr-FR",
|
|
132
|
+
};
|
|
119
133
|
});
|
|
120
134
|
|
|
121
135
|
it("useIsRTL returns false", () => {
|
|
136
|
+
const { useIsRTL } = require("../index");
|
|
122
137
|
expect(useIsRTL()).toBe(false);
|
|
123
138
|
});
|
|
124
139
|
});
|
|
125
140
|
|
|
126
141
|
describe("when first app locale is RTL", () => {
|
|
127
142
|
beforeEach(() => {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
};
|
|
143
|
+
jest.resetModules();
|
|
144
|
+
|
|
145
|
+
const RN = require("react-native");
|
|
132
146
|
|
|
133
|
-
|
|
147
|
+
RN.NativeModules.QuickBrickCommunicationModule = {
|
|
148
|
+
languageLocale: "he",
|
|
149
|
+
countryLocale: "HE",
|
|
150
|
+
uiLanguage: "he",
|
|
151
|
+
};
|
|
134
152
|
});
|
|
135
153
|
|
|
136
154
|
it("useIsRTL returns true", () => {
|
|
155
|
+
const { useIsRTL } = require("../index");
|
|
137
156
|
expect(useIsRTL()).toBe(true);
|
|
138
157
|
});
|
|
139
158
|
});
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import * as R from "ramda";
|
|
2
2
|
import { ViewStyle } from "react-native";
|
|
3
|
-
import { useSelector } from "react-redux";
|
|
4
3
|
|
|
5
|
-
import { getDeviceLocales } from "./deviceLocales";
|
|
6
4
|
import { useMemo } from "react";
|
|
7
5
|
import { getUILanguage } from "../appUtils/localizationsHelper";
|
|
6
|
+
import { utilsLogger } from "../logger";
|
|
8
7
|
|
|
9
8
|
export {
|
|
10
9
|
getLocale,
|
|
@@ -45,34 +44,26 @@ export function useLocalizedStrings({ localizations }) {
|
|
|
45
44
|
return result;
|
|
46
45
|
}
|
|
47
46
|
|
|
48
|
-
function
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
)
|
|
47
|
+
export function getIsRTL(): boolean {
|
|
48
|
+
const uiLanguage = getUILanguage();
|
|
49
|
+
|
|
50
|
+
if (!uiLanguage) {
|
|
51
|
+
utilsLogger.error({ message: "getUILanguage returned null or undefined" });
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
return R.head(matchingLanguages);
|
|
53
|
+
return false;
|
|
55
54
|
}
|
|
56
55
|
|
|
57
|
-
|
|
58
|
-
}
|
|
56
|
+
const [languageCode] = uiLanguage.split("-");
|
|
59
57
|
|
|
60
|
-
|
|
61
|
-
const deviceLocales = getDeviceLocales() || [];
|
|
62
|
-
const localeToUse = getLocaleToUse(deviceLocales, R.keys(localizations));
|
|
58
|
+
const lowerCaseLanguageCode = languageCode.toLocaleLowerCase();
|
|
63
59
|
|
|
64
|
-
|
|
65
|
-
}
|
|
60
|
+
const isRTL = R.contains(lowerCaseLanguageCode, RTL_LOCALES);
|
|
66
61
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
"localizations",
|
|
70
|
-
]);
|
|
62
|
+
return isRTL;
|
|
63
|
+
}
|
|
71
64
|
|
|
72
65
|
export function useIsRTL(): boolean {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
return getIsRTLFromLocalizations(localizations);
|
|
66
|
+
return getIsRTL();
|
|
76
67
|
}
|
|
77
68
|
|
|
78
69
|
export const applyRTLStylesIfNeeded = (
|
package/numberUtils/index.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/zapp-react-native-utils",
|
|
3
|
-
"version": "12.0.0-alpha.
|
|
3
|
+
"version": "12.0.0-alpha.4840257570",
|
|
4
4
|
"description": "Applicaster Zapp React Native utilities package",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
},
|
|
29
29
|
"homepage": "https://github.com/applicaster/quickbrick#readme",
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@applicaster/applicaster-types": "12.0.0-alpha.
|
|
31
|
+
"@applicaster/applicaster-types": "12.0.0-alpha.4840257570",
|
|
32
32
|
"buffer": "^5.2.1",
|
|
33
33
|
"camelize": "^1.0.0",
|
|
34
34
|
"create-react-context": "^0.2.3",
|
|
@@ -12,9 +12,21 @@ const {
|
|
|
12
12
|
|
|
13
13
|
const { reactHooksLogger } = require("../../logger");
|
|
14
14
|
|
|
15
|
+
let mockStore;
|
|
16
|
+
|
|
15
17
|
describe("getInflatedDataSourceUrl", () => {
|
|
16
18
|
beforeEach(() => {
|
|
17
19
|
reactHooksLogger.warning.mockClear();
|
|
20
|
+
|
|
21
|
+
const {
|
|
22
|
+
appStore,
|
|
23
|
+
} = require("@applicaster/zapp-react-native-redux/AppStore");
|
|
24
|
+
|
|
25
|
+
mockStore = {
|
|
26
|
+
getState: jest.fn().mockReturnValue({ pipesEndpoints: {} }),
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
appStore.set(mockStore);
|
|
18
30
|
});
|
|
19
31
|
|
|
20
32
|
it("returns the url inflated with the right params", () => {
|
|
@@ -72,7 +84,8 @@ describe("getInflatedDataSourceUrl", () => {
|
|
|
72
84
|
expect(reactHooksLogger.warning).toHaveBeenCalledTimes(1);
|
|
73
85
|
|
|
74
86
|
expect(reactHooksLogger.warning).toHaveBeenCalledWith({
|
|
75
|
-
message:
|
|
87
|
+
message:
|
|
88
|
+
"Not enough data to fully inflate url: {{entry.id}} source: http://foo.com/show/seriesId={{seriesId}}",
|
|
76
89
|
data: { source, contexts, mapping },
|
|
77
90
|
});
|
|
78
91
|
});
|
|
@@ -107,7 +120,8 @@ describe("getInflatedDataSourceUrl", () => {
|
|
|
107
120
|
expect(reactHooksLogger.warning).toHaveBeenCalledTimes(1);
|
|
108
121
|
|
|
109
122
|
expect(reactHooksLogger.warning).toHaveBeenCalledWith({
|
|
110
|
-
message:
|
|
123
|
+
message:
|
|
124
|
+
"Not enough data to fully inflate url: {{entry.extensions.groupName}} source: https://zapp-1689-emmys.web.app/emmys/group-categories/{{groupName}}",
|
|
111
125
|
data: { source, contexts, mapping },
|
|
112
126
|
});
|
|
113
127
|
});
|
|
@@ -148,7 +162,8 @@ describe("getInflatedDataSourceUrl", () => {
|
|
|
148
162
|
expect(reactHooksLogger.warning).toHaveBeenCalledTimes(1);
|
|
149
163
|
|
|
150
164
|
expect(reactHooksLogger.warning).toHaveBeenCalledWith({
|
|
151
|
-
message:
|
|
165
|
+
message:
|
|
166
|
+
"Not enough data to fully inflate url: {{entry.extensions.groupName2}} source: https://zapp-1689-emmys.web.app/emmys/group-categories/{{groupName1}}?test={{groupName2}}",
|
|
152
167
|
data: { source, contexts, mapping },
|
|
153
168
|
});
|
|
154
169
|
});
|
|
@@ -9,9 +9,15 @@ import {
|
|
|
9
9
|
|
|
10
10
|
import { inflateString } from "../../stringUtils";
|
|
11
11
|
import { useRoute } from "../navigation";
|
|
12
|
-
import {
|
|
12
|
+
import { getMissingValues } from "../../inflatedUrlUtils";
|
|
13
13
|
import { reactHooksLogger as logger } from "../logger";
|
|
14
14
|
import { isTV } from "../../reactUtils";
|
|
15
|
+
import {
|
|
16
|
+
findEndpointForURL,
|
|
17
|
+
HTTP_METHODS,
|
|
18
|
+
} from "@applicaster/zapp-pipes-v2-client";
|
|
19
|
+
import { appStore } from "@applicaster/zapp-react-native-redux/AppStore";
|
|
20
|
+
import { ENDPOINT_TAGS } from "../../types";
|
|
15
21
|
|
|
16
22
|
/**
|
|
17
23
|
* will match any occurrence in a string of one or more word characters
|
|
@@ -105,24 +111,47 @@ export const getInflatedDataSourceUrl: GetInflatedDataSourceUrl = ({
|
|
|
105
111
|
R.keys(mapping)
|
|
106
112
|
);
|
|
107
113
|
|
|
108
|
-
|
|
109
|
-
const message = "Not enough data to fully inflate url";
|
|
114
|
+
const missingValues = getMissingValues(contexts, string);
|
|
110
115
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
116
|
+
if (missingValues && missingValues.length > 0) {
|
|
117
|
+
const pipesEndpoints = appStore.get("pipesEndpoints");
|
|
118
|
+
|
|
119
|
+
const endpointURL = findEndpointForURL(
|
|
120
|
+
source,
|
|
121
|
+
pipesEndpoints,
|
|
122
|
+
HTTP_METHODS.GET
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
const tags = pipesEndpoints?.[endpointURL]?.tags;
|
|
126
|
+
const allowMissingKeys = tags?.includes(ENDPOINT_TAGS.allow_missing_keys);
|
|
127
|
+
|
|
128
|
+
if (!allowMissingKeys) {
|
|
129
|
+
const message = `Not enough data to fully inflate url: ${missingValues.join(
|
|
130
|
+
","
|
|
131
|
+
)} source: ${source}`;
|
|
132
|
+
|
|
133
|
+
// todo: add js only payload instead
|
|
134
|
+
if (__DEV__) {
|
|
135
|
+
logger.warning({
|
|
136
|
+
message,
|
|
137
|
+
data: { source, contexts, mapping },
|
|
138
|
+
});
|
|
139
|
+
} else {
|
|
140
|
+
// todo: log values we've failed to resolve
|
|
141
|
+
logger.warning({
|
|
142
|
+
message,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// return null in order to prevent start fetching data with invalid url
|
|
147
|
+
return null;
|
|
117
148
|
} else {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
149
|
+
logger.debug({
|
|
150
|
+
message: `Ignoring missing values in url because ${
|
|
151
|
+
ENDPOINT_TAGS.allow_missing_keys
|
|
152
|
+
} endpoint tag, keys: ${missingValues.join(",")}`,
|
|
121
153
|
});
|
|
122
154
|
}
|
|
123
|
-
|
|
124
|
-
// return null in order to prevent start fetching data with invalid url
|
|
125
|
-
return null;
|
|
126
155
|
}
|
|
127
156
|
|
|
128
157
|
return inflateString({ string, data: contexts });
|
|
@@ -496,18 +496,3 @@ export const useIsScreenActive = () => {
|
|
|
496
496
|
|
|
497
497
|
return pathname === currentRoute;
|
|
498
498
|
};
|
|
499
|
-
|
|
500
|
-
type Return = (prefix: string) => string;
|
|
501
|
-
|
|
502
|
-
export const useUniqueRouteSuffix = (): Return => {
|
|
503
|
-
const pathname = usePathname();
|
|
504
|
-
const { currentRoute } = useNavigation();
|
|
505
|
-
|
|
506
|
-
const suffix = `currentRoute:${currentRoute}_pathname:${pathname}`;
|
|
507
|
-
|
|
508
|
-
const withUniqueRouteSuffix = React.useCallback((prefix: string) => {
|
|
509
|
-
return `${prefix}___${suffix}`;
|
|
510
|
-
}, []);
|
|
511
|
-
|
|
512
|
-
return withUniqueRouteSuffix;
|
|
513
|
-
};
|
|
@@ -5,7 +5,6 @@ import { useNestedNavigationContext } from "@applicaster/zapp-react-native-ui-co
|
|
|
5
5
|
import { useNavigation } from "../navigation/useNavigation";
|
|
6
6
|
|
|
7
7
|
import { ScreenContext } from "@applicaster/zapp-react-native-ui-components/Contexts/ScreenContext";
|
|
8
|
-
import { ScreenDataContext } from "@applicaster/zapp-react-native-ui-components/Components/Layout/TV";
|
|
9
8
|
|
|
10
9
|
export function useScreenContextV2() {
|
|
11
10
|
return useContext(ScreenContext);
|
|
@@ -46,8 +45,8 @@ export function useSetNavbarState() {
|
|
|
46
45
|
export function useScreenContext(): NavigationScreenData {
|
|
47
46
|
const navigator = useNavigation();
|
|
48
47
|
|
|
49
|
-
const
|
|
50
|
-
|
|
48
|
+
const screenKey = useMemo(() => navigator?.key, [navigator]);
|
|
49
|
+
const screenData = useMemo(() => navigator?.data, [screenKey]);
|
|
51
50
|
const modalScreenData = navigator?.modalData;
|
|
52
51
|
|
|
53
52
|
const isModal = useModalNavigationContext();
|
package/reactUtils/helpers.tsx
CHANGED
|
@@ -7,7 +7,7 @@ export const isAnyNil = R.any(R.isNil);
|
|
|
7
7
|
|
|
8
8
|
export const isNilOrEmpty = R.either(R.isNil, R.isEmpty);
|
|
9
9
|
|
|
10
|
-
export const isNotEmpty = R.
|
|
10
|
+
export const isNotEmpty = R.complement(R.isEmpty);
|
|
11
11
|
|
|
12
12
|
export const withoutNil = R.reject(R.isNil);
|
|
13
13
|
|
package/stringUtils/index.ts
CHANGED
|
@@ -152,3 +152,8 @@ export function objectToCamelCase(obj) {
|
|
|
152
152
|
export function isString(value: unknown): value is string {
|
|
153
153
|
return typeof value === "string" || value instanceof String;
|
|
154
154
|
}
|
|
155
|
+
|
|
156
|
+
/** Checks if a value is a non-empty string */
|
|
157
|
+
export function isNotEmptyString(value: unknown): value is string {
|
|
158
|
+
return isString(value) && value.length > 0;
|
|
159
|
+
}
|