@applicaster/zapp-react-native-utils 15.0.0-rc.98 → 15.1.0-alpha.1852552014
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/actionsExecutor/ActionExecutorContext.tsx +51 -5
- package/actionsExecutor/feedDecorator.ts +6 -6
- package/adsUtils/index.ts +2 -2
- package/analyticsUtils/AnalyticsEvents/sendMenuClickEvent.ts +2 -2
- package/analyticsUtils/PlayerAnalyticsManager.ts +11 -1
- package/analyticsUtils/README.md +1 -1
- package/analyticsUtils/__tests__/analyticsMapper.test.ts +35 -0
- package/analyticsUtils/__tests__/fixtures/analytics_mapper_testACP_events.json +1 -1
- package/analyticsUtils/__tests__/fixtures/analytics_mapper_testBlockUnlistedParams_rules.json +1 -1
- package/analyticsUtils/__tests__/fixtures/analytics_mapper_testEmptyRenameKeepsName_events.json +4 -0
- package/analyticsUtils/__tests__/fixtures/analytics_mapper_testEmptyRenameKeepsName_rules.json +6 -0
- package/analyticsUtils/__tests__/fixtures/analytics_mapper_testEventRegex_events.json +3 -9
- package/analyticsUtils/__tests__/fixtures/analytics_mapper_testIgnoreOrdering_events.json +18 -0
- package/analyticsUtils/__tests__/fixtures/analytics_mapper_testIgnoreOrdering_rules.json +16 -0
- package/analyticsUtils/__tests__/fixtures/analytics_mapper_testRegexEventNoFallback_events.json +4 -0
- package/analyticsUtils/__tests__/fixtures/analytics_mapper_testRegexEventNoFallback_rules.json +6 -0
- package/analyticsUtils/__tests__/fixtures/analytics_mapper_testRegexMultiGroupRename_events.json +4 -0
- package/analyticsUtils/__tests__/fixtures/analytics_mapper_testRegexMultiGroupRename_rules.json +6 -0
- package/analyticsUtils/__tests__/fixtures/analytics_mapper_testRegexNonNamedFullMatch_events.json +4 -0
- package/analyticsUtils/__tests__/fixtures/analytics_mapper_testRegexNonNamedFullMatch_rules.json +6 -0
- package/analyticsUtils/__tests__/fixtures/index.js +20 -0
- package/analyticsUtils/analyticsMapper.ts +4 -1
- package/analyticsUtils/playerAnalyticsTracker.ts +26 -3
- package/appUtils/HooksManager/__tests__/__snapshots__/hooksManager.test.js.snap +188 -0
- package/appUtils/HooksManager/__tests__/hooksManager.test.js +2 -16
- package/appUtils/HooksManager/index.ts +47 -17
- package/appUtils/RiverFocusManager/{index.ts → index.js} +18 -25
- package/appUtils/accessibilityManager/const.ts +0 -4
- package/appUtils/accessibilityManager/utils.ts +1 -0
- package/appUtils/contextKeysManager/__tests__/getKeys/failure.test.ts +2 -7
- package/appUtils/contextKeysManager/__tests__/getKeys/success.test.ts +0 -48
- package/appUtils/contextKeysManager/contextResolver.ts +22 -51
- package/appUtils/contextKeysManager/index.ts +10 -65
- package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +0 -3
- package/appUtils/focusManager/index.ios.ts +4 -43
- package/appUtils/focusManagerAux/utils/index.ts +3 -5
- package/appUtils/focusManagerAux/utils/utils.ios.ts +3 -199
- package/appUtils/keyCodes/keys/keys.web.ts +4 -1
- package/appUtils/localizationsHelper.ts +4 -0
- package/appUtils/orientationHelper.ts +4 -2
- package/appUtils/platform/platformUtils.ts +1 -1
- package/appUtils/playerManager/__tests__/playerFactory.test.ts +150 -0
- package/appUtils/playerManager/index.ts +9 -0
- package/appUtils/playerManager/player.ts +1 -5
- package/appUtils/playerManager/playerFactory.ts +17 -34
- package/appUtils/playerManager/playerNative.ts +22 -30
- package/appUtils/playerManager/usePlayer.tsx +5 -3
- package/appUtils/playerManager/usePlayerState.tsx +2 -14
- package/cellUtils/__tests__/cellUtils.test.ts +39 -0
- package/cellUtils/index.ts +14 -6
- package/cloudEventsUtils/index.ts +1 -65
- package/colorUtils/__tests__/isTransparentColor.test.ts +76 -0
- package/colorUtils/__tests__/isValidColor.test.ts +70 -0
- package/colorUtils/index.ts +50 -0
- package/configurationUtils/__tests__/manifestKeyParser.test.ts +26 -26
- package/dateUtils/__tests__/dayjs.test.ts +0 -3
- package/dateUtils/index.ts +2 -0
- package/focusManager/aux/index.ts +1 -1
- package/headersUtils/__tests__/headersUtils.test.js +1 -11
- package/headersUtils/index.ts +1 -2
- package/manifestUtils/defaultManifestConfigurations/player.js +7 -75
- package/manifestUtils/keys.js +1 -0
- package/manifestUtils/platformIsTV.js +14 -0
- package/manifestUtils/tvAction/container/index.js +1 -1
- package/numberUtils/__tests__/toNumber.test.ts +0 -12
- package/numberUtils/index.ts +1 -19
- package/package.json +4 -4
- package/pipesUtils/__tests__/buildUrlWithQuery.test.ts +33 -0
- package/pipesUtils/__tests__/withPipesEndpoint.test.tsx +56 -0
- package/pipesUtils/index.ts +1 -0
- package/pipesUtils/withPipesEndpoint.tsx +54 -0
- package/playerUtils/index.ts +24 -2
- package/pluginUtils/index.ts +4 -9
- package/reactHooks/advertising/index.ts +2 -2
- package/reactHooks/app/__tests__/useAppState.test.ts +1 -1
- package/reactHooks/autoscrolling/__tests__/useTrackCurrentAutoScrollingElement.test.ts +1 -1
- package/reactHooks/autoscrolling/__tests__/useTrackedView.test.tsx +2 -1
- package/reactHooks/casting/index.ts +1 -0
- package/reactHooks/casting/useIsCasting.tsx +57 -0
- package/reactHooks/cell-click/__tests__/index.test.js +3 -1
- package/reactHooks/componentsMap/index.ts +55 -0
- package/reactHooks/configuration/__tests__/index.test.tsx +1 -1
- package/reactHooks/connection/__tests__/index.test.js +1 -1
- package/reactHooks/debugging/__tests__/index.test.js +4 -4
- package/reactHooks/device/useIsTablet.tsx +19 -14
- package/reactHooks/device/useMemoizedIsTablet.ts +3 -3
- package/reactHooks/events/index.ts +20 -0
- package/reactHooks/feed/__tests__/useBatchLoading.test.tsx +23 -32
- package/reactHooks/feed/__tests__/useBuildPipesUrl.test.tsx +19 -19
- package/reactHooks/feed/__tests__/useEntryScreenId.test.tsx +1 -4
- package/reactHooks/feed/__tests__/useFeedLoader.test.tsx +30 -42
- package/reactHooks/feed/__tests__/useFeedRefresh.test.tsx +1 -1
- package/reactHooks/feed/__tests__/useInflatedUrl.test.tsx +1 -1
- package/reactHooks/feed/useBatchLoading.ts +1 -7
- package/reactHooks/feed/useEntryScreenId.ts +2 -2
- package/reactHooks/feed/useInflatedUrl.ts +1 -1
- package/reactHooks/feed/usePipesCacheReset.ts +1 -3
- package/reactHooks/flatList/useLoadNextPageIfNeeded.ts +16 -13
- package/reactHooks/index.ts +2 -0
- package/reactHooks/layout/__tests__/index.test.tsx +1 -1
- package/reactHooks/layout/__tests__/useLayoutVersion.test.tsx +1 -1
- package/reactHooks/layout/useDimensions/__tests__/{useDimensions.test.tsx → useDimensions.test.ts} +25 -105
- package/reactHooks/layout/useDimensions/useDimensions.ts +2 -2
- package/reactHooks/navigation/__tests__/index.test.tsx +4 -2
- package/reactHooks/navigation/index.ts +6 -7
- package/reactHooks/navigation/useRoute.ts +6 -8
- package/reactHooks/player/TVSeekControlller/TVSeekController.ts +10 -27
- package/reactHooks/player/__tests__/useAutoSeek._test.tsx +1 -1
- package/reactHooks/player/__tests__/useTapSeek._test.ts +1 -1
- package/reactHooks/resolvers/__tests__/useCellResolver.test.tsx +1 -1
- package/reactHooks/resolvers/__tests__/useComponentResolver.test.tsx +1 -1
- package/reactHooks/resolvers/useCellResolver.ts +2 -6
- package/reactHooks/resolvers/useComponentResolver.ts +13 -9
- package/reactHooks/screen/__tests__/useCurrentScreenData.test.tsx +2 -2
- package/reactHooks/screen/__tests__/useCurrentScreenIsHook.test.ts +103 -0
- package/reactHooks/screen/__tests__/useCurrentScreenIsStartupHook.test.ts +94 -0
- package/reactHooks/screen/__tests__/useScreenBackgroundColor.test.tsx +1 -1
- package/reactHooks/screen/__tests__/useScreenData.test.tsx +1 -1
- package/reactHooks/screen/__tests__/useTargetScreenData.test.tsx +4 -12
- package/reactHooks/screen/index.ts +6 -0
- package/reactHooks/screen/useCurrentScreenIsHook.ts +9 -0
- package/reactHooks/screen/useCurrentScreenIsStartupHook.ts +8 -0
- package/reactHooks/screen/useIsStandaloneFullscreen.ts +12 -0
- package/reactHooks/screen/useTargetScreenData.ts +2 -4
- package/reactHooks/state/__tests__/useComponentScreenState.test.ts +246 -0
- package/reactHooks/state/index.ts +2 -0
- package/reactHooks/state/useComponentScreenState.ts +45 -0
- package/reactHooks/state/useRivers.ts +1 -1
- package/reactHooks/usePluginConfiguration.ts +2 -2
- package/reactHooks/utils/__tests__/index.test.js +1 -1
- package/reactHooks/videoModal/hooks/useVideoModalScreenData.tsx +22 -4
- package/rectUtils/index.ts +2 -2
- package/riverComponetsMeasurementProvider/index.tsx +12 -8
- package/screenPickerUtils/index.ts +5 -0
- package/screenState/__tests__/index.test.ts +1 -1
- package/stringUtils/index.ts +1 -1
- package/testUtils/index.tsx +21 -30
- package/typeGuards/index.ts +0 -4
- package/utils/__tests__/clone.test.ts +158 -0
- package/utils/clone.ts +7 -0
- package/utils/index.ts +1 -13
- package/utils/path.ts +3 -6
- package/utils/pathOr.ts +1 -5
- package/zappFrameworkUtils/HookCallback/callbackNavigationAction.ts +2 -2
- package/zappFrameworkUtils/HookCallback/hookCallbackManifestExtensions.config.js +1 -1
- package/zappFrameworkUtils/loginPluginUtils.ts +1 -1
- package/adsUtils/__tests__/createVMAP.test.ts +0 -419
- package/appDataUtils/__tests__/urlScheme.test.ts +0 -678
- package/appUtils/focusManagerAux/utils/index.ios.ts +0 -122
- package/cloudEventsUtils/__tests__/index.test.ts +0 -529
- package/enumUtils/__tests__/getEnumKeyByEnumValue.test.ts +0 -207
- package/errorUtils/__tests__/GeneralError.test.ts +0 -97
- package/errorUtils/__tests__/HttpStatusCode.test.ts +0 -344
- package/errorUtils/__tests__/MissingPluginError.test.ts +0 -113
- package/errorUtils/__tests__/NetworkError.test.ts +0 -202
- package/errorUtils/__tests__/getParsedResponse.test.ts +0 -188
- package/errorUtils/__tests__/invariant.test.ts +0 -112
- package/numberUtils/__tests__/toPositiveNumber.test.ts +0 -165
- package/reactHooks/analytics/__tests__/useSendAnalyticsOnPress.test.ts +0 -537
- package/reactHooks/dev/__tests__/useReRenderLog.test.ts +0 -188
- package/reactHooks/ui/__tests__/useFadeOutWhenBlurred.test.ts +0 -580
- package/rectUtils/__tests__/index.test.ts +0 -549
- package/screenPickerUtils/__tests__/index.test.ts +0 -333
- package/searchUtils/const.ts +0 -7
- package/searchUtils/index.ts +0 -3
- package/time/__tests__/BackgroundTimer.test.ts +0 -156
- package/time/__tests__/Timer.test.ts +0 -236
- package/typeGuards/__tests__/isString.test.ts +0 -21
- package/utils/__tests__/mergeRight.test.ts +0 -48
- package/utils/__tests__/selectors.test.ts +0 -124
- package/utils/mergeRight.ts +0 -5
- package/utils/selectors.ts +0 -46
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import React, { forwardRef, RefObject } from "react";
|
|
2
|
+
import URL from "url";
|
|
3
|
+
|
|
4
|
+
import { useBuildPipesUrl } from "@applicaster/zapp-react-native-utils/reactHooks/feed";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Merges the query params resolved from a pipes endpoint context into the url.
|
|
8
|
+
* Returns the url unchanged when there is nothing to merge.
|
|
9
|
+
*/
|
|
10
|
+
export function buildUrlWithQuery(url: string, requestParams): string {
|
|
11
|
+
if (!url || !requestParams?.params) {
|
|
12
|
+
return url;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const parsedURL = URL.parse(url, true);
|
|
16
|
+
|
|
17
|
+
parsedURL.query = { ...parsedURL.query, ...requestParams.params };
|
|
18
|
+
parsedURL.search = null;
|
|
19
|
+
|
|
20
|
+
return URL.format(parsedURL);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
type Props = {
|
|
24
|
+
uri: string;
|
|
25
|
+
} & Record<string, unknown>;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* HOC that resolves the wrapped component's `uri` against its matching Zapp
|
|
29
|
+
* Pipes endpoint. Endpoint context keys are added as query params (merged into
|
|
30
|
+
* the uri) and as request `headers`. Rendering is deferred until the context
|
|
31
|
+
* has been resolved so the component never loads without its required headers.
|
|
32
|
+
*/
|
|
33
|
+
export function withPipesEndpoint(Component) {
|
|
34
|
+
function WithPipesEndpoint(props: Props, ref: RefObject<unknown>) {
|
|
35
|
+
const { requestParams } = useBuildPipesUrl({ url: props.uri });
|
|
36
|
+
|
|
37
|
+
if (requestParams !== null) {
|
|
38
|
+
const urlWithQuery = buildUrlWithQuery(props.uri, requestParams);
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<Component
|
|
42
|
+
ref={ref}
|
|
43
|
+
{...props}
|
|
44
|
+
uri={urlWithQuery}
|
|
45
|
+
headers={requestParams.headers || {}}
|
|
46
|
+
/>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return forwardRef(WithPipesEndpoint);
|
|
54
|
+
}
|
package/playerUtils/index.ts
CHANGED
|
@@ -9,6 +9,16 @@ import { Dimensions } from "react-native";
|
|
|
9
9
|
|
|
10
10
|
export { getPlayerActionButtons } from "./getPlayerActionButtons";
|
|
11
11
|
|
|
12
|
+
export const HLS_MIME_TYPES = [
|
|
13
|
+
"application/x-mpegURL",
|
|
14
|
+
"application/vnd.apple.mpegurl",
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
export const STREAMING_VIDEO_MIME_TYPES = [
|
|
18
|
+
...HLS_MIME_TYPES,
|
|
19
|
+
"application/dash+xml",
|
|
20
|
+
];
|
|
21
|
+
|
|
12
22
|
/**
|
|
13
23
|
* Gets duration value from player manager, and from extensions
|
|
14
24
|
* then checks whether the value from either is a not a valid number
|
|
@@ -27,7 +37,7 @@ export function isLiveLegacy(content) {
|
|
|
27
37
|
]);
|
|
28
38
|
|
|
29
39
|
const durationFromExt = R.path(["extensions", "duration"], content);
|
|
30
|
-
const durationFromMgr = playerManager.getDuration();
|
|
40
|
+
const durationFromMgr = playerManager.getInstanceController()?.getDuration();
|
|
31
41
|
const duration = Math.floor(durationFromExt || durationFromMgr);
|
|
32
42
|
|
|
33
43
|
const isLive = R.anyPass([isNotaValidNumber, R.lte(R.__, 0)])(duration);
|
|
@@ -71,7 +81,9 @@ function isLiveByManager(): boolean {
|
|
|
71
81
|
return true;
|
|
72
82
|
}
|
|
73
83
|
|
|
74
|
-
const durationFromPlayerManager = playerManager
|
|
84
|
+
const durationFromPlayerManager = playerManager
|
|
85
|
+
.getInstanceController()
|
|
86
|
+
?.getDuration();
|
|
75
87
|
|
|
76
88
|
return isLiveByDuration(durationFromPlayerManager);
|
|
77
89
|
}
|
|
@@ -80,6 +92,16 @@ export function isLive(entry: ZappEntry): boolean {
|
|
|
80
92
|
return isEntryLive(entry) || isLiveByManager();
|
|
81
93
|
}
|
|
82
94
|
|
|
95
|
+
export const isVideoItem = (item: Option<ZappEntry>) => {
|
|
96
|
+
const contentType = item?.content?.type;
|
|
97
|
+
|
|
98
|
+
return (
|
|
99
|
+
isString(contentType) &&
|
|
100
|
+
(contentType.includes("video") ||
|
|
101
|
+
STREAMING_VIDEO_MIME_TYPES.includes(contentType))
|
|
102
|
+
);
|
|
103
|
+
};
|
|
104
|
+
|
|
83
105
|
export const isAudioItem = (item: Option<ZappEntry>) => {
|
|
84
106
|
if (
|
|
85
107
|
isString(item?.content?.type) &&
|
package/pluginUtils/index.ts
CHANGED
|
@@ -6,7 +6,6 @@ import { deprecationMessage } from "../appUtils";
|
|
|
6
6
|
|
|
7
7
|
import { pluginUtilsLogger } from "./logger";
|
|
8
8
|
import { platformSelect } from "../reactUtils";
|
|
9
|
-
import { get } from "../utils";
|
|
10
9
|
|
|
11
10
|
type PluginModule = any;
|
|
12
11
|
type Plugin = {
|
|
@@ -30,10 +29,7 @@ export function getComponentModule({
|
|
|
30
29
|
components: ComponentsMap;
|
|
31
30
|
plugins: Plugin[] | QuickBrickPlugin[];
|
|
32
31
|
}) {
|
|
33
|
-
const component =
|
|
34
|
-
R.prop(R.__, components),
|
|
35
|
-
toPascalCase
|
|
36
|
-
)(componentType);
|
|
32
|
+
const component = components[toPascalCase(componentType)];
|
|
37
33
|
|
|
38
34
|
if (component) {
|
|
39
35
|
return component;
|
|
@@ -60,7 +56,9 @@ export function findComponentByType({
|
|
|
60
56
|
? R.compose(...R.reverse(decorators))
|
|
61
57
|
: decorators;
|
|
62
58
|
|
|
63
|
-
const
|
|
59
|
+
const module = getComponentModule({ componentType, components, plugins });
|
|
60
|
+
|
|
61
|
+
const component = module?.Component || module;
|
|
64
62
|
|
|
65
63
|
return R.unless(R.isNil, applyDecorators)(component);
|
|
66
64
|
}
|
|
@@ -255,6 +253,3 @@ const getPluginType = R.pathOr("unknown_plugin_type", [
|
|
|
255
253
|
|
|
256
254
|
export const isPlayerPlugin = (routeState): boolean =>
|
|
257
255
|
getPluginType(routeState) === "player";
|
|
258
|
-
|
|
259
|
-
export const getPluginModuleUrlScheme = (plugin) =>
|
|
260
|
-
get(plugin, ["module", "urlScheme"]);
|
|
@@ -3,7 +3,7 @@ import { View } from "react-native";
|
|
|
3
3
|
import * as R from "ramda";
|
|
4
4
|
|
|
5
5
|
import { useNavigation } from "../navigation";
|
|
6
|
-
import {
|
|
6
|
+
import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
|
|
7
7
|
import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
8
8
|
import { dismissModal, openModal } from "../../modalState";
|
|
9
9
|
|
|
@@ -16,7 +16,7 @@ const ModalContainer = platformSelect({
|
|
|
16
16
|
|
|
17
17
|
export function useAdvertisingInterstitial() {
|
|
18
18
|
const { screenData, currentRoute } = useNavigation();
|
|
19
|
-
const plugins =
|
|
19
|
+
const { plugins } = usePickFromState();
|
|
20
20
|
|
|
21
21
|
return useEffect(() => {
|
|
22
22
|
// TODD: typing problem: fix any type
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
|
|
3
|
-
import { renderHook
|
|
3
|
+
import { renderHook } from "@testing-library/react-hooks";
|
|
4
|
+
import { act, waitFor } from "@testing-library/react-native";
|
|
4
5
|
import { Provider } from "react-redux";
|
|
5
6
|
import configureStore from "redux-mock-store";
|
|
6
7
|
import { useTrackedView } from "../useTrackedView";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { useIsCasting } from "./useIsCasting";
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
|
|
3
|
+
import { useAppState } from "@applicaster/zapp-react-native-utils/reactHooks/app";
|
|
4
|
+
import { isApplePlatform } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
5
|
+
|
|
6
|
+
const CHROMECAST_IDENTIFIER = "quick-brick-chromecast-action";
|
|
7
|
+
|
|
8
|
+
const fakeEntry: any = {
|
|
9
|
+
title: "Chromecast use is casting fake entry",
|
|
10
|
+
id: "quick-brick-use-is-casting-fake-entry",
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Returns the cast action state connection status, does not update when app is in background on Apple platforms
|
|
15
|
+
* On other platforms, it continues to check the cast action state even when the app is in background
|
|
16
|
+
*/
|
|
17
|
+
export const useIsCasting = () => {
|
|
18
|
+
const actionContext = useActions(CHROMECAST_IDENTIFIER);
|
|
19
|
+
const appState = useAppState();
|
|
20
|
+
const isAppInForeground = appState === "active";
|
|
21
|
+
const shouldOnlyUpdateInForeground = isApplePlatform();
|
|
22
|
+
|
|
23
|
+
const [castActionState, setCastActionState] = React.useState(
|
|
24
|
+
!actionContext ? null : actionContext.initialEntryState(fakeEntry)
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
React.useEffect(() => {
|
|
28
|
+
if (!shouldOnlyUpdateInForeground || isAppInForeground) {
|
|
29
|
+
if (actionContext && castActionState === null) {
|
|
30
|
+
setCastActionState(actionContext.initialEntryState(fakeEntry));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}, [
|
|
34
|
+
actionContext,
|
|
35
|
+
setCastActionState,
|
|
36
|
+
isAppInForeground,
|
|
37
|
+
shouldOnlyUpdateInForeground,
|
|
38
|
+
]);
|
|
39
|
+
|
|
40
|
+
React.useEffect(() => {
|
|
41
|
+
if (typeof actionContext?.addListener === "function") {
|
|
42
|
+
return actionContext?.addListener(String(fakeEntry.id), (state) => {
|
|
43
|
+
// Only check foreground state on Apple platforms
|
|
44
|
+
if (!shouldOnlyUpdateInForeground || isAppInForeground) {
|
|
45
|
+
setCastActionState(state);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}, [
|
|
50
|
+
setCastActionState,
|
|
51
|
+
actionContext,
|
|
52
|
+
isAppInForeground,
|
|
53
|
+
shouldOnlyUpdateInForeground,
|
|
54
|
+
]);
|
|
55
|
+
|
|
56
|
+
return !!castActionState?.connected;
|
|
57
|
+
};
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import { cleanup, renderHook
|
|
2
|
+
import { cleanup, renderHook } from "@testing-library/react-hooks";
|
|
3
3
|
import { CellTapContext } from "@applicaster/zapp-react-native-ui-components/Contexts/CellTapContext";
|
|
4
4
|
import { ActionExecutorContext } from "@applicaster/zapp-react-native-utils/actionsExecutor/ActionExecutorContext";
|
|
5
5
|
|
|
6
|
+
import { waitFor } from "@testing-library/react-native";
|
|
7
|
+
|
|
6
8
|
const mockNavigator = {
|
|
7
9
|
getPathname: () => "pathnameMock",
|
|
8
10
|
push: jest.fn(),
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
const layoutReducer = (state, { payload }) => {
|
|
4
|
+
return state.map((item, index, _state) => ({
|
|
5
|
+
height: index === payload.index ? payload.height : item.height,
|
|
6
|
+
y:
|
|
7
|
+
index > 0
|
|
8
|
+
? _state.slice(0, index).reduce((acc, value) => acc + value.height, 0)
|
|
9
|
+
: 0,
|
|
10
|
+
}));
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const useComponentsLayout = (count, onFinishLoadingVisible) => {
|
|
14
|
+
const [itemsLayout, dispatchItemLayout] = React.useReducer(
|
|
15
|
+
layoutReducer,
|
|
16
|
+
Array.from({ length: count }, () => ({ height: 0, y: 0 }))
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
const notified = React.useRef(false);
|
|
20
|
+
const [listHeight, setListHeight] = React.useState<number>(0);
|
|
21
|
+
|
|
22
|
+
const onItemLayout = React.useCallback(
|
|
23
|
+
(index) => (event) => {
|
|
24
|
+
const { height } = event.nativeEvent.layout;
|
|
25
|
+
dispatchItemLayout({ payload: { index, height } });
|
|
26
|
+
},
|
|
27
|
+
[]
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
const onListLayout = React.useCallback((event) => {
|
|
31
|
+
const { height } = event.nativeEvent.layout;
|
|
32
|
+
setListHeight(height);
|
|
33
|
+
}, []);
|
|
34
|
+
|
|
35
|
+
React.useEffect(() => {
|
|
36
|
+
if (!notified.current) {
|
|
37
|
+
const finishLoadingVisible = !!itemsLayout.find(
|
|
38
|
+
(item) => item.y > listHeight
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
if (finishLoadingVisible) {
|
|
42
|
+
notified.current = true;
|
|
43
|
+
onFinishLoadingVisible?.();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}, [itemsLayout, listHeight]);
|
|
47
|
+
|
|
48
|
+
return React.useMemo(
|
|
49
|
+
() => ({
|
|
50
|
+
onItemLayout,
|
|
51
|
+
onListLayout,
|
|
52
|
+
}),
|
|
53
|
+
[]
|
|
54
|
+
);
|
|
55
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable react/prop-types */
|
|
2
2
|
import React from "react";
|
|
3
|
-
import { renderHook, cleanup } from "@testing-library/react-
|
|
3
|
+
import { renderHook, cleanup } from "@testing-library/react-hooks";
|
|
4
4
|
import { ConfigurationContext } from "@applicaster/zapp-react-native-ui-components/Contexts/ConfigutaionContext";
|
|
5
5
|
import { useConfiguration } from "@applicaster/zapp-react-native-utils/reactHooks/configuration";
|
|
6
6
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import { renderHook, cleanup } from "@testing-library/react-
|
|
2
|
+
import { renderHook, cleanup } from "@testing-library/react-hooks";
|
|
3
3
|
import { NetworkStatusContext } from "@applicaster/zapp-react-native-ui-components/Contexts/NetworkStatusContext";
|
|
4
4
|
|
|
5
5
|
const MockNetInfo = {
|
|
@@ -27,20 +27,20 @@ describe("Debug utils", () => {
|
|
|
27
27
|
it("should create new timer if timer with a label does not exist", () => {
|
|
28
28
|
performanceNowMock.mockReturnValue(1000);
|
|
29
29
|
time("timer1");
|
|
30
|
-
expect(timers
|
|
31
|
-
expect(timers
|
|
30
|
+
expect(timers["timer1"]).toBeDefined();
|
|
31
|
+
expect(timers["timer1"].startTime).toBe(1000);
|
|
32
32
|
});
|
|
33
33
|
|
|
34
34
|
it("should update start time of timer if timer with a label exists", () => {
|
|
35
35
|
// First call to time()
|
|
36
36
|
performanceNowMock.mockReturnValue(1000);
|
|
37
37
|
time("timer1");
|
|
38
|
-
const previousStartTime = timers
|
|
38
|
+
const previousStartTime = timers["timer1"].startTime;
|
|
39
39
|
|
|
40
40
|
// Second call to time()
|
|
41
41
|
performanceNowMock.mockReturnValue(2000);
|
|
42
42
|
time("timer1");
|
|
43
|
-
const currentStartTime = timers
|
|
43
|
+
const currentStartTime = timers["timer1"].startTime;
|
|
44
44
|
|
|
45
45
|
expect(previousStartTime).toBe(1000);
|
|
46
46
|
expect(currentStartTime).toBe(2000);
|
|
@@ -1,23 +1,28 @@
|
|
|
1
1
|
/* eslint react-native/split-platform-components: off */
|
|
2
2
|
|
|
3
3
|
import { Platform, PlatformIOSStatic, NativeModules } from "react-native";
|
|
4
|
+
import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
5
|
+
import { F as alwaysFalse } from "ramda";
|
|
4
6
|
import { toBooleanWithDefaultFalse } from "@applicaster/zapp-react-native-utils/booleanUtils";
|
|
5
7
|
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
+
const useIsTabletIos = (): boolean => {
|
|
9
|
+
const { isPad } = Platform as PlatformIOSStatic;
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
return Boolean(isPad);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
if (platform === "android") {
|
|
16
|
-
const initialProps =
|
|
17
|
-
NativeModules.QuickBrickCommunicationModule?.initialProps;
|
|
11
|
+
return isPad;
|
|
12
|
+
};
|
|
18
13
|
|
|
19
|
-
|
|
20
|
-
}
|
|
14
|
+
const useIsTabletAndroid = (): boolean => {
|
|
15
|
+
const { initialProps } = NativeModules.QuickBrickCommunicationModule;
|
|
21
16
|
|
|
22
|
-
return
|
|
17
|
+
return toBooleanWithDefaultFalse(initialProps?.is_tablet);
|
|
23
18
|
};
|
|
19
|
+
|
|
20
|
+
export const useIsTablet = platformSelect({
|
|
21
|
+
ios: useIsTabletIos,
|
|
22
|
+
android: useIsTabletAndroid,
|
|
23
|
+
lg_tv: alwaysFalse,
|
|
24
|
+
samsung_tv: alwaysFalse,
|
|
25
|
+
android_tv: alwaysFalse,
|
|
26
|
+
tvos: alwaysFalse,
|
|
27
|
+
default: alwaysFalse,
|
|
28
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
+
import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
|
|
2
3
|
import { useIsTablet } from "./useIsTablet";
|
|
3
|
-
import { useAppData } from "@applicaster/zapp-react-native-redux";
|
|
4
4
|
|
|
5
5
|
export const useMemoizedIsTablet = () => {
|
|
6
6
|
const isTablet = useIsTablet();
|
|
@@ -14,9 +14,9 @@ export const useMemoizedIsTablet = () => {
|
|
|
14
14
|
};
|
|
15
15
|
|
|
16
16
|
export const useIsTabletLandscape = (): boolean => {
|
|
17
|
-
const {
|
|
17
|
+
const { appData } = usePickFromState(["appData"]);
|
|
18
18
|
|
|
19
19
|
const isTablet = useIsTablet();
|
|
20
20
|
|
|
21
|
-
return isTablet && !isTabletPortrait;
|
|
21
|
+
return isTablet && !appData.isTabletPortrait;
|
|
22
22
|
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { scrollEndReached$ } from "@applicaster/zapp-react-native-ui-components/events";
|
|
3
|
+
import { filter } from "rxjs/operators";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Subscribe to scroll-end-reached and call loadNextData only when this item is last in the river.
|
|
7
|
+
* Use in Grid/List so they load next page when the user scrolls to the end (river or tabs scroll container).
|
|
8
|
+
*/
|
|
9
|
+
export function useScrollEndReachedLoadNext(
|
|
10
|
+
isLast: boolean,
|
|
11
|
+
loadNextData: () => void
|
|
12
|
+
): void {
|
|
13
|
+
React.useEffect(() => {
|
|
14
|
+
const sub = scrollEndReached$.pipe(filter(() => isLast)).subscribe(() => {
|
|
15
|
+
loadNextData();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
return () => sub.unsubscribe();
|
|
19
|
+
}, [isLast, loadNextData]);
|
|
20
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { renderHook
|
|
1
|
+
import { renderHook } from "@testing-library/react-hooks";
|
|
2
2
|
import { allFeedsIsReady, useBatchLoading } from "../useBatchLoading";
|
|
3
3
|
import { WrappedWithProviders } from "@applicaster/zapp-react-native-utils/testUtils";
|
|
4
4
|
import { appStore } from "@applicaster/zapp-react-native-redux/AppStore";
|
|
5
|
+
import { waitFor } from "@testing-library/react-native";
|
|
5
6
|
|
|
6
7
|
jest.mock("../../navigation");
|
|
7
8
|
|
|
@@ -15,11 +16,7 @@ jest.mock(
|
|
|
15
16
|
})
|
|
16
17
|
);
|
|
17
18
|
|
|
18
|
-
const
|
|
19
|
-
(store) =>
|
|
20
|
-
({ children }) => (
|
|
21
|
-
<WrappedWithProviders store={store}>{children}</WrappedWithProviders>
|
|
22
|
-
);
|
|
19
|
+
const wrapper = WrappedWithProviders;
|
|
23
20
|
|
|
24
21
|
describe("useBatchLoading", () => {
|
|
25
22
|
const data = [
|
|
@@ -66,7 +63,8 @@ describe("useBatchLoading", () => {
|
|
|
66
63
|
const riverId = "123";
|
|
67
64
|
|
|
68
65
|
renderHook(() => useBatchLoading(data, { initialBatchSize, riverId }), {
|
|
69
|
-
wrapper
|
|
66
|
+
wrapper,
|
|
67
|
+
initialProps: { store },
|
|
70
68
|
});
|
|
71
69
|
|
|
72
70
|
const actions = (appStore.getStore() as any).getActions();
|
|
@@ -86,7 +84,7 @@ describe("useBatchLoading", () => {
|
|
|
86
84
|
});
|
|
87
85
|
});
|
|
88
86
|
|
|
89
|
-
it("loadPipesData start loading new feed when 1 feed is done loading and 1 is in loading state",
|
|
87
|
+
it("loadPipesData start loading new feed when 1 feed is done loading and 1 is in loading state", () => {
|
|
90
88
|
const store = {
|
|
91
89
|
zappPipes: {
|
|
92
90
|
url1: {
|
|
@@ -112,14 +110,13 @@ describe("useBatchLoading", () => {
|
|
|
112
110
|
const riverId = "123";
|
|
113
111
|
|
|
114
112
|
renderHook(() => useBatchLoading(data, { initialBatchSize, riverId }), {
|
|
115
|
-
wrapper
|
|
113
|
+
wrapper,
|
|
114
|
+
initialProps: { store },
|
|
116
115
|
});
|
|
117
116
|
|
|
118
117
|
const actions = (appStore.getStore() as any).getActions();
|
|
119
118
|
|
|
120
|
-
|
|
121
|
-
expect(actions).toHaveLength(1);
|
|
122
|
-
});
|
|
119
|
+
expect(actions).toHaveLength(1);
|
|
123
120
|
|
|
124
121
|
expect(actions[0]).toMatchObject({
|
|
125
122
|
type: "ZAPP_PIPES_REQUEST_START",
|
|
@@ -127,7 +124,7 @@ describe("useBatchLoading", () => {
|
|
|
127
124
|
});
|
|
128
125
|
});
|
|
129
126
|
|
|
130
|
-
it("loadPipesData has been called when no data cached",
|
|
127
|
+
it("loadPipesData has been called when no data cached", () => {
|
|
131
128
|
const store = {
|
|
132
129
|
zappPipes: {},
|
|
133
130
|
test: "true",
|
|
@@ -137,17 +134,16 @@ describe("useBatchLoading", () => {
|
|
|
137
134
|
const riverId = "123";
|
|
138
135
|
|
|
139
136
|
renderHook(() => useBatchLoading(data, { initialBatchSize, riverId }), {
|
|
140
|
-
wrapper
|
|
137
|
+
wrapper,
|
|
138
|
+
initialProps: { store },
|
|
141
139
|
});
|
|
142
140
|
|
|
143
141
|
const actions = (appStore.getStore() as any).getActions();
|
|
144
142
|
|
|
145
|
-
|
|
146
|
-
expect(actions).toHaveLength(3);
|
|
147
|
-
});
|
|
143
|
+
expect(actions).toHaveLength(3);
|
|
148
144
|
});
|
|
149
145
|
|
|
150
|
-
it("initial batch ready when all initial items loaded",
|
|
146
|
+
it("initial batch ready when all initial items loaded", () => {
|
|
151
147
|
const store = {
|
|
152
148
|
zappPipes: {
|
|
153
149
|
url1: {
|
|
@@ -173,15 +169,13 @@ describe("useBatchLoading", () => {
|
|
|
173
169
|
|
|
174
170
|
const { result } = renderHook(
|
|
175
171
|
() => useBatchLoading(data, { initialBatchSize, riverId }),
|
|
176
|
-
{ wrapper:
|
|
172
|
+
{ wrapper, initialProps: { store } }
|
|
177
173
|
);
|
|
178
174
|
|
|
179
|
-
|
|
180
|
-
expect(result.current).toBe(true);
|
|
181
|
-
});
|
|
175
|
+
expect(result.current).toBe(true);
|
|
182
176
|
});
|
|
183
177
|
|
|
184
|
-
it("gallery-qb: loadPipesData should be called only once for first component in the gallery",
|
|
178
|
+
it("gallery-qb: loadPipesData should be called only once for first component in the gallery", () => {
|
|
185
179
|
const store = {
|
|
186
180
|
zappPipes: {},
|
|
187
181
|
test: "true",
|
|
@@ -204,17 +198,16 @@ describe("useBatchLoading", () => {
|
|
|
204
198
|
];
|
|
205
199
|
|
|
206
200
|
renderHook(() => useBatchLoading(data, { initialBatchSize, riverId }), {
|
|
207
|
-
wrapper
|
|
201
|
+
wrapper,
|
|
202
|
+
initialProps: { store },
|
|
208
203
|
});
|
|
209
204
|
|
|
210
205
|
const actions = (appStore.getStore() as any).getActions();
|
|
211
206
|
|
|
212
|
-
|
|
213
|
-
expect(actions).toHaveLength(1);
|
|
214
|
-
});
|
|
207
|
+
expect(actions).toHaveLength(1);
|
|
215
208
|
});
|
|
216
209
|
|
|
217
|
-
it("gallery-qb: initial batch ready when all initial items loaded",
|
|
210
|
+
it("gallery-qb: initial batch ready when all initial items loaded", () => {
|
|
218
211
|
const store = {
|
|
219
212
|
zappPipes: {
|
|
220
213
|
url1: {
|
|
@@ -240,12 +233,10 @@ describe("useBatchLoading", () => {
|
|
|
240
233
|
|
|
241
234
|
const { result } = renderHook(
|
|
242
235
|
() => useBatchLoading(data, { initialBatchSize, riverId }),
|
|
243
|
-
{ wrapper:
|
|
236
|
+
{ wrapper, initialProps: { store } }
|
|
244
237
|
);
|
|
245
238
|
|
|
246
|
-
|
|
247
|
-
expect(result.current).toBe(true);
|
|
248
|
-
});
|
|
239
|
+
expect(result.current).toBe(true);
|
|
249
240
|
});
|
|
250
241
|
});
|
|
251
242
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { renderHook
|
|
1
|
+
import { renderHook } from "@testing-library/react-hooks";
|
|
2
2
|
import { encodeParams } from "@applicaster/zapp-pipes-v2-client/src/utils";
|
|
3
3
|
|
|
4
4
|
const appData = {
|
|
@@ -42,26 +42,26 @@ describe("useBuildPipesUrl", () => {
|
|
|
42
42
|
build: jest.fn(() => Promise.resolve(buildResult)),
|
|
43
43
|
};
|
|
44
44
|
|
|
45
|
-
const { result } = renderHook(() =>
|
|
45
|
+
const { result, waitForNextUpdate } = renderHook(() =>
|
|
46
46
|
useBuildPipesUrl({ url, mapping, requestBuilder: mockedRequestBuilder })
|
|
47
47
|
);
|
|
48
48
|
|
|
49
49
|
expect(result.current.url).toBe("http://foo.com/show/show-1");
|
|
50
50
|
expect(result.current.requestParams).toBeNull();
|
|
51
51
|
|
|
52
|
-
await
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
},
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
63
|
-
)
|
|
64
|
-
|
|
52
|
+
await waitForNextUpdate();
|
|
53
|
+
|
|
54
|
+
expect(result.current.requestParams).toEqual(
|
|
55
|
+
expect.objectContaining({
|
|
56
|
+
params: {
|
|
57
|
+
version: appData.version_name,
|
|
58
|
+
ctx: encodeParams({ platform: appData.platform }),
|
|
59
|
+
},
|
|
60
|
+
headers: {
|
|
61
|
+
SDK: appData.sdk_version,
|
|
62
|
+
},
|
|
63
|
+
})
|
|
64
|
+
);
|
|
65
65
|
});
|
|
66
66
|
|
|
67
67
|
it("returns empty object if url is not in endpoints", async () => {
|
|
@@ -81,15 +81,15 @@ describe("useBuildPipesUrl", () => {
|
|
|
81
81
|
build: jest.fn(() => Promise.resolve(buildResult)),
|
|
82
82
|
};
|
|
83
83
|
|
|
84
|
-
const { result } = renderHook(() =>
|
|
84
|
+
const { result, waitForNextUpdate } = renderHook(() =>
|
|
85
85
|
useBuildPipesUrl({ url, mapping, requestBuilder: mockedRequestBuilder })
|
|
86
86
|
);
|
|
87
87
|
|
|
88
88
|
expect(result.current.url).toBe("http://foobar.com/show-1");
|
|
89
89
|
expect(result.current.requestParams).toBeNull();
|
|
90
90
|
|
|
91
|
-
await
|
|
92
|
-
|
|
93
|
-
});
|
|
91
|
+
await waitForNextUpdate();
|
|
92
|
+
|
|
93
|
+
expect(result.current.requestParams).toEqual({});
|
|
94
94
|
});
|
|
95
95
|
});
|