@applicaster/zapp-react-native-utils 13.0.0-rc.14 → 13.0.0-rc.140
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 +214 -16
- package/actionsExecutor/consts.ts +4 -0
- 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/__tests__/__snapshots__/localizationsHelper.test.ts.snap +151 -0
- package/appUtils/__tests__/allZappLocales.ts +79 -0
- package/appUtils/__tests__/{localizationsHelper.test.js → localizationsHelper.test.ts} +11 -0
- package/appUtils/accessibilityManager/const.ts +18 -0
- package/appUtils/accessibilityManager/index.ts +46 -2
- 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 +9 -6
- package/appUtils/focusManager/index.ios.ts +14 -4
- package/appUtils/focusManager/index.ts +108 -69
- package/appUtils/focusManager/treeDataStructure/Tree/index.js +23 -0
- package/appUtils/focusManager/utils/__tests__/findChild.test.ts +35 -0
- package/appUtils/focusManager/utils/enums.ts +4 -0
- package/appUtils/focusManager/utils/index.ts +5 -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/index.ts +11 -7
- package/appUtils/keyCodes/keys/keysByPlatform.ts +4 -0
- package/appUtils/keyInputHandler/KeyInputHandler.ts +68 -0
- package/appUtils/localizationsHelper.ts +31 -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/playerHooks/usePlayerCurrentTime.tsx +11 -7
- package/appUtils/playerManager/playerNative.ts +18 -2
- package/appUtils/playerManager/usePlayerControllerSetup.tsx +3 -3
- package/appUtils/playerManager/usePlayerTitleSummaryOverlay.tsx +7 -1
- package/arrayUtils/__tests__/__snapshots__/arrayUtils.test.ts.snap +4 -4
- package/arrayUtils/__tests__/isEmptyArray.test.ts +63 -0
- package/arrayUtils/__tests__/isFilledArray.test.ts +1 -1
- package/arrayUtils/index.ts +7 -2
- package/audioPlayerUtils/__tests__/getArtworkImage.test.ts +144 -0
- package/audioPlayerUtils/__tests__/getBackgroundImage.test.ts +72 -0
- package/audioPlayerUtils/__tests__/getImageFromEntry.test.ts +110 -0
- package/audioPlayerUtils/assets/index.ts +2 -0
- package/audioPlayerUtils/index.ts +138 -0
- package/cellUtils/index.ts +9 -5
- package/componentsUtils/__tests__/isFirstComponentGallery.test.ts +33 -0
- package/componentsUtils/__tests__/isFirstComponentScreenPicker.test.ts +33 -0
- package/componentsUtils/__tests__/isGallery.test.ts +33 -0
- package/componentsUtils/__tests__/isScreenPicker.test.ts +33 -0
- package/componentsUtils/index.ts +39 -0
- package/configurationUtils/__tests__/configurationUtils.test.js +0 -31
- package/configurationUtils/__tests__/getMediaItems.test.ts +65 -0
- package/configurationUtils/__tests__/imageSrcFromMediaItem.test.ts +34 -0
- package/configurationUtils/index.ts +63 -34
- package/dateUtils/index.ts +19 -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/localizationUtils/index.ts +8 -13
- package/logger/const.ts +9 -67
- package/manifestUtils/__tests__/createConfig.test.js +16 -16
- package/manifestUtils/defaultManifestConfigurations/generalContent.js +13 -0
- package/manifestUtils/defaultManifestConfigurations/player.js +9 -3
- package/manifestUtils/fonts/index.js +5 -6
- package/manifestUtils/index.js +2 -0
- package/manifestUtils/keys.js +27 -2
- package/manifestUtils/sharedConfiguration/screenPicker/__tests__/utils.test.js +304 -0
- package/manifestUtils/sharedConfiguration/screenPicker/stylesFields.js +874 -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 +7 -6
- package/navigationUtils/__tests__/itemTypes.test.js +2 -2
- package/navigationUtils/__tests__/navigationUtils.test.js +2 -2
- package/navigationUtils/index.ts +6 -4
- package/numberUtils/__tests__/toPositiveNumberWithDefault.test.ts +23 -3
- package/numberUtils/index.ts +11 -5
- package/package.json +5 -10
- package/playerUtils/configurationGenerator.ts +9 -11
- package/playerUtils/index.ts +23 -0
- package/reactHooks/advertising/index.ts +5 -5
- package/reactHooks/app/useAppState.ts +2 -2
- 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/useBatchLoading.ts +12 -14
- package/reactHooks/feed/useFeedLoader.tsx +18 -9
- 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/getTabBarHeight.ts +10 -0
- package/reactHooks/navigation/index.ts +34 -236
- package/reactHooks/navigation/useGetBottomTabBarHeight.ts +19 -0
- package/reactHooks/navigation/useGetNavBarTopBorderWidth.ts +14 -0
- package/reactHooks/navigation/useIsScreenActive.ts +17 -0
- package/reactHooks/navigation/useNavigationPluginData.ts +19 -0
- package/reactHooks/navigation/useNavigationType.ts +17 -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/screen/__tests__/useScreenBackgroundColor.test.tsx +69 -0
- package/reactHooks/screen/useScreenBackgroundColor.ts +3 -15
- package/reactHooks/state/README.md +79 -0
- package/reactHooks/state/ZStoreProvider.tsx +71 -0
- package/reactHooks/state/__tests__/ZStoreProvider.test.tsx +66 -0
- package/reactHooks/state/index.ts +2 -0
- package/reactHooks/state/useRefWithInitialValue.ts +14 -0
- package/reactHooks/state/useStateFromSubscribe.ts +23 -0
- package/reactHooks/useListenEventBusEvent.ts +1 -1
- 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 +9 -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/typeGuards/index.ts +3 -0
- package/types.ts +1 -0
- package/utils/README.md +30 -0
- package/utils/chunk.ts +5 -0
- package/utils/index.ts +12 -0
- package/utils/times.ts +5 -0
- package/zappFrameworkUtils/localStorageHelper.ts +38 -10
- 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
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import * as R from "ramda";
|
|
2
|
+
import { imageSrcFromMediaItem } from "../";
|
|
3
|
+
import { entry } from "./testEntry";
|
|
4
|
+
|
|
5
|
+
describe("imageSrcFromMediaItem", () => {
|
|
6
|
+
it("when the matching key is found and the src is not empty", () => {
|
|
7
|
+
const result = imageSrcFromMediaItem(entry as ZappEntry, [
|
|
8
|
+
"logo_thumbnail",
|
|
9
|
+
]);
|
|
10
|
+
|
|
11
|
+
expect(result).toEqual(entry.media_group[1].media_item[0].src);
|
|
12
|
+
expect(result).not.toEqual("");
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("returns a media item with the 'image_base' key as a fallback", () => {
|
|
16
|
+
const result = imageSrcFromMediaItem(entry as ZappEntry, [
|
|
17
|
+
"does_not_exist",
|
|
18
|
+
]);
|
|
19
|
+
|
|
20
|
+
const fallback = entry.media_group[0].media_item[0];
|
|
21
|
+
expect(result).toEqual(fallback.src);
|
|
22
|
+
expect(fallback.key).toBe("image_base");
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("returns undefined if the key was found but the source was empty", () => {
|
|
26
|
+
const badEntry: ZappEntry = R.set(
|
|
27
|
+
R.lensPath(["media_group", 0, "media_item", 0, "src"]),
|
|
28
|
+
"",
|
|
29
|
+
entry
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
expect(imageSrcFromMediaItem(badEntry, ["image_base"])).toBeUndefined();
|
|
33
|
+
});
|
|
34
|
+
});
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
/// <reference types="@applicaster/applicaster-types" />
|
|
2
2
|
import * as R from "ramda";
|
|
3
3
|
import { Platform } from "react-native";
|
|
4
|
+
import {
|
|
5
|
+
isFilledArray,
|
|
6
|
+
isEmptyArray,
|
|
7
|
+
} from "@applicaster/zapp-react-native-utils/arrayUtils";
|
|
8
|
+
import { isEmpty } from "@applicaster/zapp-react-native-utils/utils";
|
|
9
|
+
|
|
4
10
|
import { applyTransform } from "../transform";
|
|
5
11
|
import { ManifestField } from "../types";
|
|
6
12
|
import { isNilOrEmpty } from "../reactUtils/helpers";
|
|
@@ -122,54 +128,77 @@ export function flattenFields(
|
|
|
122
128
|
}
|
|
123
129
|
|
|
124
130
|
/**
|
|
125
|
-
* Retrieves the
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
* @
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
131
|
+
* Retrieves all media items from the entry's media_group that are of type "image" or "thumbnail".
|
|
132
|
+
*
|
|
133
|
+
* @param {ZappEntry} entry - The entry object containing the media_group array.
|
|
134
|
+
* @returns {Option<ZappMediaItem[]>} An array of media items of type "image" or "thumbnail", or undefined if no media_group is present.
|
|
135
|
+
*/
|
|
136
|
+
export function getMediaItems(entry: ZappEntry): Option<ZappMediaItem[]> {
|
|
137
|
+
const mediaGroup = entry?.media_group || [];
|
|
138
|
+
|
|
139
|
+
if (isEmptyArray(mediaGroup)) {
|
|
140
|
+
return undefined;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const mediaItems = mediaGroup
|
|
144
|
+
.filter((group) => ["image", "thumbnail"].includes(group.type))
|
|
145
|
+
.flatMap((group) =>
|
|
146
|
+
Array.isArray(group.media_item) ? group.media_item : [group.media_item]
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
if (isFilledArray(mediaItems)) {
|
|
150
|
+
return mediaItems;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return undefined;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Retrieves the "src" value from a media item in the entry's media group,
|
|
158
|
+
* based on a provided key, with fallback logic.
|
|
159
|
+
*
|
|
160
|
+
* Fallback order:
|
|
161
|
+
* 1. Attempts to find a media item with the specified key (or "image_base" if none provided).
|
|
162
|
+
* 2. If not found, attempts to find a media item with the key "image_base".
|
|
163
|
+
* 3. If still not found, falls back to the first available media item.
|
|
164
|
+
*
|
|
165
|
+
* Special handling: If the resolved item's "src" is an empty string, returns undefined,
|
|
166
|
+
* since empty URIs are invalid in some platforms (e.g., React Native).
|
|
167
|
+
*
|
|
168
|
+
* @param {ZappEntry} entry - The entry object containing a media group.
|
|
169
|
+
* @param {string[] | unknown} arg - A single-element array containing the key to look up, or any unknown value.
|
|
170
|
+
* @returns {?string} The "src" URI from the matched media item, or undefined if not found or empty.
|
|
132
171
|
*/
|
|
133
172
|
export function imageSrcFromMediaItem(
|
|
134
173
|
entry: ZappEntry,
|
|
135
174
|
arg: string[] | unknown
|
|
136
|
-
) {
|
|
175
|
+
): Option<string> {
|
|
137
176
|
const args: unknown = R.unless(Array.isArray, Array)(arg || []);
|
|
138
177
|
const imageKey: string = args?.[0] || "image_base"; // always a single key in this function
|
|
139
|
-
const mediaGroup = R.path<ZappMediaGroup[]>(["media_group"], entry);
|
|
140
178
|
|
|
141
|
-
|
|
179
|
+
const mediaItems = getMediaItems(entry);
|
|
180
|
+
|
|
181
|
+
if (!mediaItems) {
|
|
142
182
|
return undefined;
|
|
143
183
|
}
|
|
144
184
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
R.propEq("type", "thumbnail")
|
|
148
|
-
);
|
|
149
|
-
|
|
150
|
-
const pickMediaItemProp = R.prop<ZappMediaItem>("media_item");
|
|
185
|
+
// Try to find the item with the given key
|
|
186
|
+
let foundItem = mediaItems.find((item) => item.key === imageKey);
|
|
151
187
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
)(mediaGroup);
|
|
188
|
+
// If not found and key was not "image_base", try to find "image_base"
|
|
189
|
+
if (!foundItem && imageKey !== "image_base") {
|
|
190
|
+
foundItem = mediaItems.find((item) => item.key === "image_base");
|
|
191
|
+
}
|
|
157
192
|
|
|
158
|
-
|
|
159
|
-
|
|
193
|
+
// If still not found, default to first item
|
|
194
|
+
if (!foundItem) {
|
|
195
|
+
foundItem = mediaItems[0];
|
|
160
196
|
}
|
|
161
197
|
|
|
162
|
-
const src =
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
R.find(R.propEq("key", imageKey))
|
|
167
|
-
)(mediaItems);
|
|
168
|
-
|
|
169
|
-
// Special case for react native - uri cannot be an empty string (yellow warning).
|
|
170
|
-
// R.isEmpty is tailored specifically for checks like these,
|
|
171
|
-
// it returns false for undefined values.
|
|
172
|
-
return R.isEmpty(src) ? undefined : src;
|
|
198
|
+
const src = foundItem?.src;
|
|
199
|
+
|
|
200
|
+
// React Native quirk: empty string is invalid for URIs
|
|
201
|
+
return isEmpty(src) ? undefined : src;
|
|
173
202
|
}
|
|
174
203
|
|
|
175
204
|
/**
|
package/dateUtils/index.ts
CHANGED
|
@@ -2,10 +2,29 @@ import dayjs from "dayjs";
|
|
|
2
2
|
import utc from "dayjs/plugin/utc";
|
|
3
3
|
import timezone from "dayjs/plugin/timezone";
|
|
4
4
|
import isoWeek from "dayjs/plugin/isoWeek";
|
|
5
|
+
import duration from "dayjs/plugin/duration";
|
|
6
|
+
import relativeTime from "dayjs/plugin/relativeTime";
|
|
5
7
|
|
|
6
8
|
// Extend dayjs with plugins
|
|
7
9
|
dayjs.extend(utc);
|
|
8
10
|
dayjs.extend(timezone);
|
|
9
11
|
dayjs.extend(isoWeek);
|
|
12
|
+
dayjs.extend(duration);
|
|
13
|
+
|
|
14
|
+
dayjs.extend(relativeTime, {
|
|
15
|
+
thresholds: [
|
|
16
|
+
{ l: "s", r: 1 },
|
|
17
|
+
{ l: "m", r: 1 },
|
|
18
|
+
{ l: "mm", r: 59, d: "minute" },
|
|
19
|
+
{ l: "h", r: 1 },
|
|
20
|
+
{ l: "hh", r: 23, d: "hour" },
|
|
21
|
+
{ l: "d", r: 1 },
|
|
22
|
+
{ l: "dd", r: 29, d: "day" },
|
|
23
|
+
{ l: "M", r: 1 },
|
|
24
|
+
{ l: "MM", r: 11, d: "month" },
|
|
25
|
+
{ l: "y", r: 1 },
|
|
26
|
+
{ l: "yy", d: "year" },
|
|
27
|
+
],
|
|
28
|
+
});
|
|
10
29
|
|
|
11
30
|
export { dayjs };
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import { isEqual } from "../..";
|
|
2
|
+
|
|
3
|
+
const array = [1, 2, 3];
|
|
4
|
+
const sameProp = "sameProp";
|
|
5
|
+
|
|
6
|
+
describe("isEqual - comprehensive tests", () => {
|
|
7
|
+
describe("all conditions together", () => {
|
|
8
|
+
it("all conditions checked and not equal", () => {
|
|
9
|
+
const prev = {
|
|
10
|
+
byValueProp: { a: 1, b: 2 },
|
|
11
|
+
byRefProp: array,
|
|
12
|
+
byComparatorProp: "test",
|
|
13
|
+
extraProp1: 123,
|
|
14
|
+
extraProp2: sameProp,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const next = {
|
|
18
|
+
byValueProp: { a: 1, b: 2 },
|
|
19
|
+
byRefProp: array,
|
|
20
|
+
byComparatorProp: "different",
|
|
21
|
+
extraProp1: 456,
|
|
22
|
+
extraProp2: sameProp,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const customComparator = jest.fn().mockReturnValue(true);
|
|
26
|
+
|
|
27
|
+
const result = isEqual({
|
|
28
|
+
byValue: ["byValueProp"],
|
|
29
|
+
byReference: ["byRefProp"],
|
|
30
|
+
byComparator: {
|
|
31
|
+
comparator: customComparator,
|
|
32
|
+
keys: ["byComparatorProp"],
|
|
33
|
+
},
|
|
34
|
+
compareRemainingKeysBySameValue: true,
|
|
35
|
+
})(prev, next);
|
|
36
|
+
|
|
37
|
+
expect(result).toBe(false);
|
|
38
|
+
|
|
39
|
+
expect(customComparator).toHaveBeenCalledWith(
|
|
40
|
+
prev.byComparatorProp,
|
|
41
|
+
next.byComparatorProp
|
|
42
|
+
);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("all conditions pass with exact remaining keys check", () => {
|
|
46
|
+
const prev = {
|
|
47
|
+
byValueProp: { a: 1, b: 2 },
|
|
48
|
+
byRefProp: array,
|
|
49
|
+
byComparatorProp: "test",
|
|
50
|
+
extraProp: sameProp,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const next = {
|
|
54
|
+
byValueProp: { a: 1, b: 2 },
|
|
55
|
+
byRefProp: array,
|
|
56
|
+
byComparatorProp: "test",
|
|
57
|
+
extraProp: sameProp,
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const customComparator = jest.fn().mockReturnValue(true);
|
|
61
|
+
|
|
62
|
+
const result = isEqual({
|
|
63
|
+
byValue: ["byValueProp"],
|
|
64
|
+
byReference: ["byRefProp"],
|
|
65
|
+
byComparator: {
|
|
66
|
+
comparator: customComparator,
|
|
67
|
+
keys: ["byComparatorProp"],
|
|
68
|
+
},
|
|
69
|
+
compareRemainingKeysBySameValue: true,
|
|
70
|
+
})(prev, next);
|
|
71
|
+
|
|
72
|
+
expect(result).toBe(true);
|
|
73
|
+
|
|
74
|
+
expect(customComparator).toHaveBeenCalledWith(
|
|
75
|
+
prev.byComparatorProp,
|
|
76
|
+
next.byComparatorProp
|
|
77
|
+
);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("byReference check fails first (early return)", () => {
|
|
81
|
+
const prev = {
|
|
82
|
+
byValueProp: { a: 1, b: 2 },
|
|
83
|
+
byRefProp: array,
|
|
84
|
+
byComparatorProp: sameProp,
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const next = {
|
|
88
|
+
byValueProp: { a: 1, b: 2 },
|
|
89
|
+
byRefProp: [1, 2, 3],
|
|
90
|
+
byComparatorProp: sameProp,
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const customComparator = jest.fn().mockReturnValue(true);
|
|
94
|
+
|
|
95
|
+
const result = isEqual({
|
|
96
|
+
byValue: ["byValueProp"],
|
|
97
|
+
byReference: ["byRefProp"],
|
|
98
|
+
byComparator: {
|
|
99
|
+
comparator: customComparator,
|
|
100
|
+
keys: ["byComparatorProp"],
|
|
101
|
+
},
|
|
102
|
+
})(prev, next);
|
|
103
|
+
|
|
104
|
+
expect(result).toBe(false);
|
|
105
|
+
expect(customComparator).not.toHaveBeenCalled();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("byValue check fails after byReference passes", () => {
|
|
109
|
+
const prev = {
|
|
110
|
+
byValueProp: { a: 1, b: 2 },
|
|
111
|
+
byRefProp: array,
|
|
112
|
+
byComparatorProp: sameProp,
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const next = {
|
|
116
|
+
byValueProp: { a: 1, b: 3 },
|
|
117
|
+
byRefProp: array,
|
|
118
|
+
byComparatorProp: sameProp,
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const customComparator = jest.fn().mockReturnValue(true);
|
|
122
|
+
|
|
123
|
+
const result = isEqual({
|
|
124
|
+
byValue: ["byValueProp"],
|
|
125
|
+
byReference: ["byRefProp"],
|
|
126
|
+
byComparator: {
|
|
127
|
+
comparator: customComparator,
|
|
128
|
+
keys: ["byComparatorProp"],
|
|
129
|
+
},
|
|
130
|
+
})(prev, next);
|
|
131
|
+
|
|
132
|
+
expect(result).toBe(false);
|
|
133
|
+
expect(customComparator).not.toHaveBeenCalled();
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("byComparator check fails after other checks pass", () => {
|
|
137
|
+
const prev = {
|
|
138
|
+
byValueProp: { a: 1, b: 2 },
|
|
139
|
+
byRefProp: array,
|
|
140
|
+
byComparatorProp: "test",
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const next = {
|
|
144
|
+
byValueProp: { a: 1, b: 2 },
|
|
145
|
+
byRefProp: array,
|
|
146
|
+
byComparatorProp: "different",
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const customComparator = jest.fn().mockReturnValue(false);
|
|
150
|
+
|
|
151
|
+
const result = isEqual({
|
|
152
|
+
byValue: ["byValueProp"],
|
|
153
|
+
byReference: ["byRefProp"],
|
|
154
|
+
byComparator: {
|
|
155
|
+
comparator: customComparator,
|
|
156
|
+
keys: ["byComparatorProp"],
|
|
157
|
+
},
|
|
158
|
+
})(prev, next);
|
|
159
|
+
|
|
160
|
+
expect(result).toBe(false);
|
|
161
|
+
|
|
162
|
+
expect(customComparator).toHaveBeenCalledWith(
|
|
163
|
+
prev.byComparatorProp,
|
|
164
|
+
next.byComparatorProp
|
|
165
|
+
);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it("remaining keys comparison fails", () => {
|
|
169
|
+
const sharedArray = [1, 2, 3];
|
|
170
|
+
|
|
171
|
+
const prev = {
|
|
172
|
+
byValueProp: { a: 1, b: 2 },
|
|
173
|
+
byRefProp: sharedArray,
|
|
174
|
+
byComparatorProp: "test",
|
|
175
|
+
extraProp: "original",
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const next = {
|
|
179
|
+
byValueProp: { a: 1, b: 2 },
|
|
180
|
+
byRefProp: sharedArray,
|
|
181
|
+
byComparatorProp: "different",
|
|
182
|
+
extraProp: "changed",
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
const customComparator = jest.fn().mockReturnValue(true);
|
|
186
|
+
|
|
187
|
+
const result = isEqual({
|
|
188
|
+
byValue: ["byValueProp"],
|
|
189
|
+
byReference: ["byRefProp"],
|
|
190
|
+
byComparator: {
|
|
191
|
+
comparator: customComparator,
|
|
192
|
+
keys: ["byComparatorProp"],
|
|
193
|
+
},
|
|
194
|
+
compareRemainingKeysBySameValue: true,
|
|
195
|
+
})(prev, next);
|
|
196
|
+
|
|
197
|
+
expect(result).toBe(false);
|
|
198
|
+
expect(customComparator).toHaveBeenCalled();
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it("with debug flag", () => {
|
|
202
|
+
const consoleSpy = jest.spyOn(console, "log").mockImplementation();
|
|
203
|
+
|
|
204
|
+
const prev = {
|
|
205
|
+
byValueProp: { a: 1, b: 2 },
|
|
206
|
+
byRefProp: array,
|
|
207
|
+
byComparatorProp: "test",
|
|
208
|
+
extraProp: sameProp,
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
const next = {
|
|
212
|
+
byValueProp: { a: 1, b: 2 },
|
|
213
|
+
byRefProp: array,
|
|
214
|
+
byComparatorProp: "different",
|
|
215
|
+
extraProp: sameProp,
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
const customComparator = jest.fn().mockReturnValue(true);
|
|
219
|
+
|
|
220
|
+
const result = isEqual({
|
|
221
|
+
byValue: ["byValueProp"],
|
|
222
|
+
byReference: ["byRefProp"],
|
|
223
|
+
byComparator: {
|
|
224
|
+
comparator: customComparator,
|
|
225
|
+
keys: ["byComparatorProp"],
|
|
226
|
+
},
|
|
227
|
+
compareRemainingKeysBySameValue: true,
|
|
228
|
+
debug: true,
|
|
229
|
+
})(prev, next);
|
|
230
|
+
|
|
231
|
+
expect(result).toBe(true);
|
|
232
|
+
expect(consoleSpy).toHaveBeenCalled();
|
|
233
|
+
|
|
234
|
+
consoleSpy.mockRestore();
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it("handles missing properties", () => {
|
|
238
|
+
const prev = {
|
|
239
|
+
byValueProp: { a: 1, b: 2 },
|
|
240
|
+
byRefProp: [1, 2, 3],
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
const next = {
|
|
244
|
+
byValueProp: { a: 1, b: 2 },
|
|
245
|
+
byComparatorProp: "test",
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
const customComparator = jest.fn().mockReturnValue(true);
|
|
249
|
+
|
|
250
|
+
const result = isEqual({
|
|
251
|
+
byValue: ["byValueProp"],
|
|
252
|
+
byReference: ["byRefProp"],
|
|
253
|
+
byComparator: {
|
|
254
|
+
comparator: customComparator,
|
|
255
|
+
keys: ["byComparatorProp"],
|
|
256
|
+
},
|
|
257
|
+
})(prev, next);
|
|
258
|
+
|
|
259
|
+
expect(result).toBe(false);
|
|
260
|
+
expect(customComparator).not.toHaveBeenCalled();
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
});
|
package/equalUtils/index.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/* eslint no-console: off */
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import { equals } from "ramda";
|
|
4
|
+
|
|
5
|
+
const alwaysTrue = () => true;
|
|
4
6
|
|
|
5
7
|
export const isParentFocusEqual = (
|
|
6
8
|
prevParentFocus: ParentFocus,
|
|
@@ -18,7 +20,7 @@ export const isEqual =
|
|
|
18
20
|
({
|
|
19
21
|
byValue = [],
|
|
20
22
|
byReference = [],
|
|
21
|
-
byComparator = { comparator:
|
|
23
|
+
byComparator = { comparator: alwaysTrue, keys: [] },
|
|
22
24
|
debug = false,
|
|
23
25
|
compareRemainingKeysBySameValue = false,
|
|
24
26
|
}: {
|
|
@@ -29,42 +31,59 @@ export const isEqual =
|
|
|
29
31
|
compareRemainingKeysBySameValue?: boolean;
|
|
30
32
|
}) =>
|
|
31
33
|
(prev, next) => {
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
for (const key of byReference) {
|
|
35
|
+
if (prev[key] !== next[key]) {
|
|
36
|
+
if (debug) console.log({ isEqualByReference: false });
|
|
35
37
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
39
41
|
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
(key) => byComparator.comparator(prev[key], next[key]),
|
|
44
|
-
byComparator.keys
|
|
45
|
-
);
|
|
42
|
+
for (const key of byValue) {
|
|
43
|
+
if (!equals(prev[key], next[key])) {
|
|
44
|
+
if (debug) console.log({ isEqualByValue: false });
|
|
46
45
|
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
for (const key of byComparator.keys) {
|
|
51
|
+
if (!byComparator.comparator(prev[key], next[key])) {
|
|
52
|
+
if (debug) console.log({ isEqualByComparator: false });
|
|
53
|
+
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
49
56
|
}
|
|
50
57
|
|
|
51
58
|
if (compareRemainingKeysBySameValue) {
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
59
|
+
const allKeys = new Set([...Object.keys(prev), ...Object.keys(next)]);
|
|
60
|
+
|
|
61
|
+
const declaredKeys = new Set([
|
|
62
|
+
...byValue,
|
|
63
|
+
...byReference,
|
|
64
|
+
...byComparator.keys,
|
|
65
|
+
]);
|
|
56
66
|
|
|
57
|
-
const
|
|
58
|
-
Object.is(prev[key], next[key])
|
|
59
|
-
|
|
67
|
+
for (const key of Array.from(allKeys)) {
|
|
68
|
+
if (!declaredKeys.has(key) && !Object.is(prev[key], next[key])) {
|
|
69
|
+
if (debug) console.log({ isRestKeysEqualAllBySameValue: false });
|
|
60
70
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// if we made it here, everything matched
|
|
77
|
+
if (debug) {
|
|
78
|
+
console.log({
|
|
79
|
+
isEqualByValue: true,
|
|
80
|
+
isEqualByReference: true,
|
|
81
|
+
isEqualByComparator: true,
|
|
82
|
+
isRestKeysEqualAllBySameValue: compareRemainingKeysBySameValue
|
|
83
|
+
? true
|
|
84
|
+
: undefined,
|
|
85
|
+
});
|
|
67
86
|
}
|
|
68
87
|
|
|
69
|
-
return
|
|
88
|
+
return true;
|
|
70
89
|
};
|
|
@@ -227,7 +227,9 @@ class FocusManager {
|
|
|
227
227
|
|
|
228
228
|
const nextNextFocus = FocusManager.getNextFocusable(direction, nextFocus);
|
|
229
229
|
|
|
230
|
-
|
|
230
|
+
if (nextNextFocus) {
|
|
231
|
+
FocusManager.instance.setFocus(nextNextFocus, options);
|
|
232
|
+
}
|
|
231
233
|
} else {
|
|
232
234
|
FocusManager.instance.setFocusLocal(nextFocus);
|
|
233
235
|
|
|
@@ -250,7 +252,17 @@ class FocusManager {
|
|
|
250
252
|
}
|
|
251
253
|
|
|
252
254
|
onDisableFocusChange = (id) => {
|
|
253
|
-
|
|
255
|
+
const isFocused = FocusManager.instance.isFocused(id);
|
|
256
|
+
|
|
257
|
+
const isDescendantFocused = (() => {
|
|
258
|
+
try {
|
|
259
|
+
return FocusManager.instance.isAnyDescendantFocused(id);
|
|
260
|
+
} catch (_e) {
|
|
261
|
+
return false;
|
|
262
|
+
}
|
|
263
|
+
})();
|
|
264
|
+
|
|
265
|
+
if (isFocused || isDescendantFocused) {
|
|
254
266
|
// Move focus to next one
|
|
255
267
|
const nextFocus = FocusManager.instance.focused?.props
|
|
256
268
|
?.nextFocusDown as string;
|
package/focusManager/index.ts
CHANGED
|
@@ -91,6 +91,7 @@ type Props = {
|
|
|
91
91
|
|
|
92
92
|
export const useIsFocusable = (props: Props): void => {
|
|
93
93
|
const { component, zappPipesData } = props;
|
|
94
|
+
|
|
94
95
|
const { setIsFocusable } = useFocusable();
|
|
95
96
|
|
|
96
97
|
const componentCellsFocusable = toBooleanWithDefaultTrue(
|
|
@@ -101,15 +102,33 @@ export const useIsFocusable = (props: Props): void => {
|
|
|
101
102
|
component.rules.hide_component_if_data_is_empty
|
|
102
103
|
);
|
|
103
104
|
|
|
105
|
+
const isLoadingData = !!zappPipesData?.loading;
|
|
106
|
+
|
|
104
107
|
const hasNoData =
|
|
105
108
|
isNilOrEmpty(zappPipesData?.data) ||
|
|
106
109
|
isNilOrEmpty(zappPipesData?.data?.entry);
|
|
107
110
|
|
|
111
|
+
const hasDataError = !!zappPipesData?.error;
|
|
112
|
+
|
|
113
|
+
const isHiddenDueToEmptyData = !isLoadingData && hasNoData && hideIfDataEmpty;
|
|
114
|
+
|
|
115
|
+
const defaultFocusableLogic =
|
|
116
|
+
componentCellsFocusable && (!hasNoData || !hideIfDataEmpty);
|
|
117
|
+
|
|
108
118
|
React.useEffect(() => {
|
|
109
119
|
if (isAndroidTVPlatform()) {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
)
|
|
120
|
+
let isFocusableNew = false;
|
|
121
|
+
|
|
122
|
+
if (isFocusable(component)) {
|
|
123
|
+
isFocusableNew = defaultFocusableLogic;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
setIsFocusable(isFocusableNew);
|
|
113
127
|
}
|
|
114
|
-
}, [
|
|
128
|
+
}, [
|
|
129
|
+
defaultFocusableLogic,
|
|
130
|
+
isHiddenDueToEmptyData,
|
|
131
|
+
setIsFocusable,
|
|
132
|
+
hasDataError,
|
|
133
|
+
]);
|
|
115
134
|
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
2
|
|
|
3
3
|
exports[`functionUtils subscriber can be invoked without args 1`] = `
|
|
4
|
-
|
|
5
|
-
"handlers":
|
|
4
|
+
{
|
|
5
|
+
"handlers": {},
|
|
6
6
|
"invokeHandler": [Function],
|
|
7
7
|
"on": [Function],
|
|
8
8
|
"removeHandler": [Function],
|
|
@@ -10,9 +10,9 @@ Object {
|
|
|
10
10
|
`;
|
|
11
11
|
|
|
12
12
|
exports[`functionUtils subscriber returns the original object with additional methods 1`] = `
|
|
13
|
-
|
|
13
|
+
{
|
|
14
14
|
"foo": "bar",
|
|
15
|
-
"handlers":
|
|
15
|
+
"handlers": {},
|
|
16
16
|
"invokeHandler": [Function],
|
|
17
17
|
"method": [Function],
|
|
18
18
|
"on": [Function],
|