@applicaster/zapp-react-native-ui-components 15.0.0-rc.1 → 15.0.0-rc.100
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/Components/AnimatedInOut/index.tsx +69 -26
- package/Components/BaseFocusable/index.ios.ts +12 -2
- package/Components/Cell/Cell.tsx +14 -3
- package/Components/Cell/CellWithFocusable.tsx +9 -0
- package/Components/Cell/FocusableWrapper.tsx +47 -0
- package/Components/Cell/TvOSCellComponent.tsx +106 -19
- package/Components/Focusable/Focusable.tsx +4 -2
- package/Components/Focusable/FocusableTvOS.tsx +18 -1
- package/Components/Focusable/__tests__/__snapshots__/FocusableTvOS.test.tsx.snap +1 -0
- package/Components/FocusableGroup/FocusableTvOS.tsx +32 -1
- package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +1 -1
- package/Components/GeneralContentScreen/utils/useCurationAPI.ts +31 -17
- package/Components/HandlePlayable/HandlePlayable.tsx +17 -65
- package/Components/HandlePlayable/const.ts +3 -0
- package/Components/HandlePlayable/utils.ts +74 -0
- package/Components/Layout/TV/LayoutBackground.tsx +5 -2
- package/Components/Layout/TV/ScreenContainer.tsx +2 -6
- package/Components/Layout/TV/index.tsx +3 -4
- package/Components/Layout/TV/index.web.tsx +3 -4
- package/Components/LinkHandler/LinkHandler.tsx +2 -2
- package/Components/MasterCell/DefaultComponents/BorderContainerView/__tests__/index.test.tsx +16 -1
- package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +30 -2
- package/Components/MasterCell/DefaultComponents/Image/Image.android.tsx +5 -1
- package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +11 -3
- package/Components/MasterCell/DefaultComponents/Image/Image.web.tsx +9 -1
- package/Components/MasterCell/DefaultComponents/Image/hooks/useImage.ts +15 -14
- package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +10 -6
- package/Components/MasterCell/DefaultComponents/SecondaryImage/Image.tsx +40 -39
- package/Components/MasterCell/DefaultComponents/SecondaryImage/__tests__/Image.test.tsx +95 -0
- package/Components/MasterCell/DefaultComponents/SecondaryImage/__tests__/__snapshots__/Image.test.tsx.snap +86 -0
- package/Components/MasterCell/DefaultComponents/SecondaryImage/__tests__/index.test.ts +141 -0
- package/Components/MasterCell/DefaultComponents/SecondaryImage/hooks/__tests__/useGetImageDimensions.test.ts +7 -6
- package/Components/MasterCell/DefaultComponents/SecondaryImage/index.ts +1 -1
- package/Components/MasterCell/DefaultComponents/Text/index.tsx +8 -8
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/index.ts +6 -2
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/__tests__/getPluginIdentifier.test.ts +233 -11
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/index.ts +19 -15
- package/Components/MasterCell/hoc/__tests__/withAsyncRender.test.tsx +219 -0
- package/Components/MasterCell/hoc/withAsyncRender.tsx +9 -7
- package/Components/MasterCell/index.tsx +2 -0
- package/Components/MasterCell/utils/__tests__/resolveColor.test.js +82 -3
- package/Components/MasterCell/utils/index.ts +61 -31
- package/Components/MeasurmentsPortal/MeasurementsPortal.tsx +102 -87
- package/Components/MeasurmentsPortal/__tests__/MeasurementsPortal.test.tsx +355 -0
- package/Components/OfflineHandler/NotificationView/NotificationView.lg.tsx +17 -9
- package/Components/OfflineHandler/NotificationView/NotificationView.samsung.tsx +16 -8
- package/Components/OfflineHandler/NotificationView/NotificationView.tsx +2 -2
- package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +17 -18
- package/Components/OfflineHandler/NotificationView/utils.ts +34 -0
- package/Components/OfflineHandler/__tests__/index.test.tsx +27 -18
- package/Components/PlayerContainer/PlayerContainer.tsx +51 -55
- package/Components/PlayerContainer/useRestrictMobilePlayback.tsx +101 -0
- package/Components/PlayerImageBackground/index.tsx +3 -22
- package/Components/River/ComponentsMap/ComponentsMap.tsx +65 -42
- package/Components/River/ComponentsMap/ContextProviders/ComponentsMapHeightContext.ts +8 -0
- package/Components/River/ComponentsMap/ContextProviders/ComponentsMapRefContext.ts +8 -0
- package/Components/River/ComponentsMap/hooks/__tests__/useLoadingState.test.ts +1 -1
- package/Components/River/TV/River.tsx +31 -14
- package/Components/River/TV/index.tsx +8 -4
- package/Components/River/TV/utils/__tests__/toStringOrEmpty.test.ts +30 -0
- package/Components/River/TV/utils/index.ts +4 -0
- package/Components/River/TV/withFocusableGroupForContent.tsx +71 -0
- package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +2 -0
- package/Components/River/__tests__/componentsMap.test.js +38 -0
- package/Components/Screen/TV/hooks/useInitialFocus.ts +14 -4
- package/Components/Screen/TV/index.web.tsx +4 -2
- package/Components/Screen/__tests__/Screen.test.tsx +65 -42
- package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +68 -42
- package/Components/Screen/hooks.ts +2 -3
- package/Components/Screen/index.tsx +24 -8
- package/Components/Screen/navigationHandler.ts +49 -24
- package/Components/Screen/orientationHandler.ts +10 -13
- package/Components/ScreenResolver/index.tsx +21 -9
- package/Components/ScreenRevealManager/ScreenRevealManager.ts +40 -8
- package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +86 -69
- package/Components/ScreenRevealManager/utils/index.ts +23 -0
- package/Components/ScreenRevealManager/withScreenRevealManager.tsx +54 -24
- package/Components/Tabs/TV/Tabs.tsx +20 -3
- package/Components/Tabs/TabContent.tsx +7 -4
- package/Components/Transitioner/Scene.tsx +15 -2
- package/Components/Transitioner/index.js +3 -3
- package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +1 -0
- package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +118 -171
- package/Components/VideoModal/ModalAnimation/index.ts +2 -13
- package/Components/VideoModal/ModalAnimation/utils.ts +1 -327
- package/Components/VideoModal/PlayerDetails.tsx +5 -5
- package/Components/VideoModal/PlayerWrapper.tsx +14 -88
- package/Components/VideoModal/VideoModal.tsx +1 -5
- package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +1 -0
- package/Components/VideoModal/hooks/__tests__/useDelayedPlayerDetails.test.ts +15 -7
- package/Components/VideoModal/hooks/useModalSize.ts +10 -5
- package/Components/VideoModal/playerWrapperStyle.ts +70 -0
- package/Components/VideoModal/playerWrapperUtils.ts +91 -0
- package/Components/VideoModal/utils.ts +19 -9
- package/Components/Viewport/ViewportEvents/__tests__/viewportEvents.test.js +1 -1
- package/Components/ZappFrameworkComponents/BarView/BarView.tsx +4 -6
- package/Components/ZappFrameworkComponents/BarView/__tests__/BarView.test.tsx +2 -2
- package/Contexts/ScreenContext/index.tsx +25 -18
- package/Contexts/ScreenTrackedViewPositionsContext/__tests__/index.test.tsx +1 -1
- package/Contexts/ZappHookModalContext/index.tsx +37 -61
- package/Contexts/index.ts +0 -2
- package/Decorators/Analytics/index.tsx +6 -5
- package/Decorators/ConfigurationWrapper/__tests__/__snapshots__/withConfigurationProvider.test.tsx.snap +1 -0
- package/Decorators/ConfigurationWrapper/const.ts +1 -0
- package/Decorators/RiverFeedLoader/utils/getDatasourceUrl.ts +6 -10
- package/Decorators/ZappPipesDataConnector/__tests__/zappPipesDataConnector.test.js +1 -1
- package/Decorators/ZappPipesDataConnector/index.tsx +2 -2
- package/Decorators/ZappPipesDataConnector/resolvers/StaticFeedResolver.tsx +1 -1
- package/Helpers/DataSourceHelper/__tests__/itemLimitForData.test.ts +80 -0
- package/Helpers/DataSourceHelper/index.ts +19 -0
- package/events/index.ts +3 -0
- package/events/scrollEndReached.ts +15 -0
- package/index.d.ts +7 -0
- package/package.json +6 -5
- package/Components/River/TV/withTVEventHandler.tsx +0 -27
- package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +0 -60
- package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +0 -415
- package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.web.tsx +0 -294
- package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +0 -176
- package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.web.tsx +0 -93
- package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +0 -500
- package/Components/VideoModal/ModalAnimation/__tests__/getMoveUpValue.test.ts +0 -108
- package/Helpers/DataSourceHelper/index.js +0 -19
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { Dimensions, DimensionValue, Platform, ViewStyle } from "react-native";
|
|
2
|
+
import { Edge } from "react-native-safe-area-context";
|
|
3
|
+
|
|
4
|
+
export type DimensionsT = {
|
|
5
|
+
width: number | string;
|
|
6
|
+
height: number | string | undefined;
|
|
7
|
+
aspectRatio?: number;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export type Configuration = {
|
|
11
|
+
[key: string]: any;
|
|
12
|
+
tablet_landscape_sidebar_width?: string;
|
|
13
|
+
tablet_landscape_player_container_background_color?: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// This is safe, remembering screen dimensions once as they do not change during runtime
|
|
17
|
+
// TODO: consider sharing screen orientation as a shared function for the app
|
|
18
|
+
const { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } = Dimensions.get("screen");
|
|
19
|
+
|
|
20
|
+
export const getWindowDimensions = () => {
|
|
21
|
+
const { width, height } = Dimensions.get("window");
|
|
22
|
+
|
|
23
|
+
return { width, height };
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const getMaxWindowDimension = () => {
|
|
27
|
+
const { width, height } = getWindowDimensions();
|
|
28
|
+
|
|
29
|
+
return Math.max(width, height);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const getMinWindowDimension = () => {
|
|
33
|
+
const { width, height } = getWindowDimensions();
|
|
34
|
+
|
|
35
|
+
return Math.min(width, height);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const getScreenAspectRatio = () => {
|
|
39
|
+
const longEdge = Math.max(SCREEN_WIDTH, SCREEN_HEIGHT);
|
|
40
|
+
const shortEdge = Math.min(SCREEN_WIDTH, SCREEN_HEIGHT);
|
|
41
|
+
|
|
42
|
+
return longEdge / shortEdge;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const getEdges = (
|
|
46
|
+
isTablet: boolean,
|
|
47
|
+
isInlineModal: boolean
|
|
48
|
+
): readonly Edge[] => {
|
|
49
|
+
if (isTablet) {
|
|
50
|
+
return ["top"];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (!isInlineModal && Platform.OS === "android") {
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return ["top"];
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const getBaseDimensions = (
|
|
61
|
+
isInlineModal: boolean,
|
|
62
|
+
isTabletLandscape: boolean,
|
|
63
|
+
tabletWidth: DimensionValue
|
|
64
|
+
): ViewStyle => ({
|
|
65
|
+
width: isInlineModal && isTabletLandscape ? tabletWidth : "100%",
|
|
66
|
+
height: undefined,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
export const calculateAspectRatio = (
|
|
70
|
+
isInlineModal: boolean,
|
|
71
|
+
pip?: boolean
|
|
72
|
+
): number => {
|
|
73
|
+
return !isInlineModal && !pip ? getScreenAspectRatio() : 16 / 9;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export const getPlayerDimensions = (
|
|
77
|
+
baseDimensions: ViewStyle,
|
|
78
|
+
aspectRatio: number
|
|
79
|
+
): ViewStyle => ({
|
|
80
|
+
...(baseDimensions as any),
|
|
81
|
+
width: baseDimensions.width,
|
|
82
|
+
aspectRatio,
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
export const getContainerDimensions = (
|
|
86
|
+
baseDimensions: ViewStyle,
|
|
87
|
+
aspectRatio?: string | number
|
|
88
|
+
): ViewStyle => ({
|
|
89
|
+
...baseDimensions,
|
|
90
|
+
aspectRatio,
|
|
91
|
+
});
|
|
@@ -1,31 +1,35 @@
|
|
|
1
1
|
import { mergeRight } from "ramda";
|
|
2
2
|
import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
useAppSelector,
|
|
5
|
+
useContentTypes,
|
|
6
|
+
} from "@applicaster/zapp-react-native-redux/hooks";
|
|
4
7
|
import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks/navigation/useNavigation";
|
|
5
8
|
import { useIsTablet } from "@applicaster/zapp-react-native-utils/reactHooks/device/useIsTablet";
|
|
6
9
|
import { playerManager } from "@applicaster/zapp-react-native-utils/appUtils";
|
|
10
|
+
import { create } from "zustand";
|
|
11
|
+
import { useRivers } from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
12
|
+
import { selectPluginConfigurationsByPluginId } from "@applicaster/zapp-react-native-redux";
|
|
7
13
|
|
|
8
14
|
export const useConfiguration = () => {
|
|
9
15
|
const {
|
|
10
16
|
videoModalState: { item },
|
|
11
17
|
} = useNavigation();
|
|
12
18
|
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
"contentTypes",
|
|
16
|
-
"pluginConfigurations",
|
|
17
|
-
]);
|
|
19
|
+
const rivers = useRivers();
|
|
20
|
+
const contentTypes = useContentTypes();
|
|
18
21
|
|
|
19
22
|
const targetScreenId = contentTypes?.[item?.type?.value]?.screen_id;
|
|
20
23
|
const targetScreenConfiguration = rivers?.[targetScreenId];
|
|
21
24
|
|
|
22
|
-
const
|
|
23
|
-
|
|
25
|
+
const { configuration_json } = useAppSelector((state) =>
|
|
26
|
+
selectPluginConfigurationsByPluginId(state, targetScreenConfiguration?.type)
|
|
27
|
+
);
|
|
24
28
|
|
|
25
29
|
const playerPluginConfig = playerManager.getPluginConfiguration();
|
|
26
30
|
|
|
27
31
|
const config = mergeRight(playerPluginConfig, {
|
|
28
|
-
...
|
|
32
|
+
...configuration_json,
|
|
29
33
|
...targetScreenConfiguration?.general,
|
|
30
34
|
...targetScreenConfiguration?.styles,
|
|
31
35
|
});
|
|
@@ -52,6 +56,12 @@ export const useConfiguration = () => {
|
|
|
52
56
|
};
|
|
53
57
|
};
|
|
54
58
|
|
|
59
|
+
export const useAnimationStateStore = create<{
|
|
60
|
+
isAnimationInProgress: boolean;
|
|
61
|
+
}>(() => ({
|
|
62
|
+
isAnimationInProgress: false,
|
|
63
|
+
}));
|
|
64
|
+
|
|
55
65
|
const fullSize = {
|
|
56
66
|
width: "100%",
|
|
57
67
|
height: "100%",
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { StyleSheet } from "react-native";
|
|
3
|
-
import { SafeAreaView } from "react-native-safe-area-context";
|
|
2
|
+
import { StyleSheet, View } from "react-native";
|
|
4
3
|
|
|
5
4
|
import { useIsRTL } from "@applicaster/zapp-react-native-utils/localizationUtils";
|
|
6
5
|
import CloseButton from "./Buttons/CloseButton";
|
|
@@ -45,15 +44,14 @@ const BarView = ({ screenStyles, barState = defaultState }: Props) => {
|
|
|
45
44
|
);
|
|
46
45
|
|
|
47
46
|
return (
|
|
48
|
-
<
|
|
49
|
-
edges={["top", "left", "right"]}
|
|
47
|
+
<View
|
|
50
48
|
style={[style.view, isRTL ? style.rtlStyle : {}]}
|
|
51
|
-
testID="BarView-
|
|
49
|
+
testID="BarView-Container"
|
|
52
50
|
>
|
|
53
51
|
{backButton}
|
|
54
52
|
<BarTitle title={barState.title} screenStyles={screenStyles} />
|
|
55
53
|
{closeButton}
|
|
56
|
-
</
|
|
54
|
+
</View>
|
|
57
55
|
);
|
|
58
56
|
};
|
|
59
57
|
|
|
@@ -46,7 +46,7 @@ describe("BarView", () => {
|
|
|
46
46
|
<BarView screenStyles={screenStyles} barState={barState} />
|
|
47
47
|
);
|
|
48
48
|
|
|
49
|
-
expect(getByTestId("BarView-
|
|
49
|
+
expect(getByTestId("BarView-Container").props.style).toContainEqual({
|
|
50
50
|
transform: [{ scaleX: -1 }],
|
|
51
51
|
});
|
|
52
52
|
});
|
|
@@ -58,7 +58,7 @@ describe("BarView", () => {
|
|
|
58
58
|
<BarView screenStyles={screenStyles} barState={barState} />
|
|
59
59
|
);
|
|
60
60
|
|
|
61
|
-
expect(getByTestId("BarView-
|
|
61
|
+
expect(getByTestId("BarView-Container").props.style).not.toContainEqual({
|
|
62
62
|
transform: [{ scaleX: -1 }],
|
|
63
63
|
});
|
|
64
64
|
});
|
|
@@ -16,6 +16,7 @@ import { useNestedNavigationContext } from "@applicaster/zapp-react-native-ui-co
|
|
|
16
16
|
import { create } from "zustand";
|
|
17
17
|
import { subscribeWithSelector } from "zustand/middleware";
|
|
18
18
|
import { useShallow } from "zustand/react/shallow";
|
|
19
|
+
|
|
19
20
|
import { Animated } from "react-native";
|
|
20
21
|
|
|
21
22
|
interface NavBarStoreState {
|
|
@@ -29,6 +30,8 @@ interface NavBarStoreState {
|
|
|
29
30
|
scrollState: number;
|
|
30
31
|
contentPosition: string;
|
|
31
32
|
scrollYAnimated: Animated.Value;
|
|
33
|
+
hideOnScrollAnimated: Animated.Value;
|
|
34
|
+
hideOnScroll: boolean;
|
|
32
35
|
}
|
|
33
36
|
|
|
34
37
|
interface NavBarState {
|
|
@@ -62,24 +65,28 @@ const createStateStore = () =>
|
|
|
62
65
|
);
|
|
63
66
|
|
|
64
67
|
const createStore = () =>
|
|
65
|
-
create
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
68
|
+
create(
|
|
69
|
+
subscribeWithSelector<NavBarStoreState>((set) => ({
|
|
70
|
+
title: "",
|
|
71
|
+
summary: "",
|
|
72
|
+
visible: true,
|
|
73
|
+
height: 0,
|
|
74
|
+
scrollState: 0,
|
|
75
|
+
contentPosition: "",
|
|
76
|
+
scrollYAnimated: new Animated.Value(0),
|
|
77
|
+
hideOnScrollAnimated: new Animated.Value(0),
|
|
78
|
+
hideOnScroll: false,
|
|
79
|
+
setTitle(title) {
|
|
80
|
+
set({ title });
|
|
81
|
+
},
|
|
82
|
+
setSummary(summary) {
|
|
83
|
+
set({ summary });
|
|
84
|
+
},
|
|
85
|
+
setVisible(visible) {
|
|
86
|
+
set({ visible });
|
|
87
|
+
},
|
|
88
|
+
}))
|
|
89
|
+
);
|
|
83
90
|
|
|
84
91
|
type ScreenContextType = {
|
|
85
92
|
_navBarStore: ReturnType<typeof createStore>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { create } from "zustand";
|
|
2
2
|
|
|
3
3
|
type HookModalState = {
|
|
4
4
|
path: string;
|
|
@@ -10,11 +10,12 @@ export type HookModalContextT = {
|
|
|
10
10
|
setIsHooksExecutionInProgress: (hookExecutionState?: boolean) => void;
|
|
11
11
|
isRunningInBackground: boolean;
|
|
12
12
|
isPresentationFullScreen: boolean;
|
|
13
|
-
setIsRunningInBackground: (
|
|
14
|
-
setIsPresentingFullScreen: (
|
|
13
|
+
setIsRunningInBackground: () => void;
|
|
14
|
+
setIsPresentingFullScreen: () => void;
|
|
15
15
|
state: HookModalState;
|
|
16
16
|
setState: (state: HookModalState) => void;
|
|
17
17
|
resetState: () => void;
|
|
18
|
+
hookPresentationMode: HookPresentationMode;
|
|
18
19
|
};
|
|
19
20
|
|
|
20
21
|
const initialState = {
|
|
@@ -24,68 +25,43 @@ const initialState = {
|
|
|
24
25
|
|
|
25
26
|
type HookPresentationMode = "background" | "fullScreen";
|
|
26
27
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
isRunningInBackground: null,
|
|
31
|
-
setIsRunningInBackground: () => {},
|
|
32
|
-
setIsPresentingFullScreen: () => {},
|
|
33
|
-
isPresentationFullScreen: null,
|
|
28
|
+
// Use useZappHookModalStore() in React components (hooks)
|
|
29
|
+
// Use zappHookModalStore.getState() in non-React functions (utility functions, etc.)
|
|
30
|
+
export const useZappHookModalStore = create<HookModalContextT>()((set) => ({
|
|
34
31
|
state: initialState,
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const Provider = ({ children }: { children: React.ReactNode }) => {
|
|
40
|
-
const [state, setState] = React.useState<HookModalState>(initialState);
|
|
41
|
-
|
|
42
|
-
const [hookPresentationMode, setHookPresentationMode] =
|
|
43
|
-
React.useState<HookPresentationMode>(null);
|
|
44
|
-
|
|
45
|
-
const resetState = useCallback(() => {
|
|
46
|
-
setState(initialState);
|
|
47
|
-
}, [setState]);
|
|
48
|
-
|
|
49
|
-
const [isHooksExecutionInProgress, setIsHooksExecutionInProgress] =
|
|
50
|
-
React.useState(false);
|
|
32
|
+
isHooksExecutionInProgress: false,
|
|
33
|
+
hookPresentationMode: null as HookPresentationMode,
|
|
34
|
+
isRunningInBackground: false,
|
|
35
|
+
isPresentationFullScreen: false,
|
|
51
36
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
37
|
+
setState: (newState: HookModalState) => {
|
|
38
|
+
set({ state: newState });
|
|
39
|
+
},
|
|
55
40
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
41
|
+
setIsHooksExecutionInProgress: (hookExecutionState?: boolean) => {
|
|
42
|
+
set({ isHooksExecutionInProgress: hookExecutionState ?? false });
|
|
43
|
+
},
|
|
59
44
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
isHooksExecutionInProgress,
|
|
68
|
-
setIsHooksExecutionInProgress,
|
|
69
|
-
state,
|
|
70
|
-
setState,
|
|
71
|
-
resetState,
|
|
72
|
-
}}
|
|
73
|
-
>
|
|
74
|
-
{children}
|
|
75
|
-
</ReactContext.Provider>
|
|
76
|
-
);
|
|
77
|
-
};
|
|
45
|
+
setIsRunningInBackground: () => {
|
|
46
|
+
set({
|
|
47
|
+
hookPresentationMode: "background",
|
|
48
|
+
isRunningInBackground: true,
|
|
49
|
+
isPresentationFullScreen: false,
|
|
50
|
+
});
|
|
51
|
+
},
|
|
78
52
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
};
|
|
53
|
+
setIsPresentingFullScreen: () => {
|
|
54
|
+
set({
|
|
55
|
+
hookPresentationMode: "fullScreen",
|
|
56
|
+
isRunningInBackground: false,
|
|
57
|
+
isPresentationFullScreen: true,
|
|
58
|
+
});
|
|
59
|
+
},
|
|
87
60
|
|
|
88
|
-
|
|
89
|
-
};
|
|
61
|
+
resetState: () => {
|
|
62
|
+
set({ state: initialState });
|
|
63
|
+
},
|
|
64
|
+
}));
|
|
90
65
|
|
|
91
|
-
|
|
66
|
+
// Export an alias for clearer non-React usage (utility functions, etc.)
|
|
67
|
+
export const zappHookModalStore = useZappHookModalStore;
|
package/Contexts/index.ts
CHANGED
|
@@ -12,8 +12,6 @@ export { HorizontalScrollContext } from "./HorizontalScrollContext";
|
|
|
12
12
|
|
|
13
13
|
export { RiverOffsetContext } from "./RiverOffsetContext";
|
|
14
14
|
|
|
15
|
-
export { ZappHookModalContext } from "./ZappHookModalContext";
|
|
16
|
-
|
|
17
15
|
export { MeasurementContext } from "./MeasurementContext";
|
|
18
16
|
|
|
19
17
|
export * from "./ZappPipesContext";
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
|
|
3
3
|
import { getAnalyticsFunctions } from "@applicaster/zapp-react-native-utils/analyticsUtils";
|
|
4
|
-
import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
|
|
5
4
|
|
|
6
5
|
type ComponentProps = {
|
|
7
6
|
component: {
|
|
@@ -21,11 +20,13 @@ type ComponentProps = {
|
|
|
21
20
|
|
|
22
21
|
function withAnalytics(Component) {
|
|
23
22
|
return function WithAnalytics(props: ComponentProps) {
|
|
24
|
-
const { appData } = usePickFromState(["appData"]);
|
|
25
|
-
|
|
26
23
|
const analyticsFunctions = React.useMemo(
|
|
27
|
-
() =>
|
|
28
|
-
|
|
24
|
+
() =>
|
|
25
|
+
getAnalyticsFunctions({
|
|
26
|
+
component: props.component,
|
|
27
|
+
zappPipesData: props.zappPipesData,
|
|
28
|
+
} as any),
|
|
29
|
+
[props.component, props.zappPipesData]
|
|
29
30
|
);
|
|
30
31
|
|
|
31
32
|
return (
|
|
@@ -88,6 +88,7 @@ exports[`withConfigurationProvider correctly passes all the configuration keys c
|
|
|
88
88
|
tab_cell_padding_right={10}
|
|
89
89
|
tab_cell_padding_top={14}
|
|
90
90
|
tablet_theme={false}
|
|
91
|
+
tabs_screen_background_color="transparent"
|
|
91
92
|
target={false}
|
|
92
93
|
target_screen_switch={false}
|
|
93
94
|
text_label_active_font_color="rgba(239, 239, 239, 0.5)"
|
|
@@ -208,4 +208,5 @@ export const keysMap: Record<string, Function> = {
|
|
|
208
208
|
tab_bar_item_margin_right: castOrDefaultValue(Number, 0),
|
|
209
209
|
tab_bar_item_margin_bottom: castOrDefaultValue(Number, 0),
|
|
210
210
|
tab_bar_item_margin_left: castOrDefaultValue(Number, 0),
|
|
211
|
+
tabs_screen_background_color: castOrDefaultValue(R.identity, "transparent"),
|
|
211
212
|
};
|
|
@@ -24,16 +24,12 @@ export const getDatasourceUrl: (
|
|
|
24
24
|
) => {
|
|
25
25
|
const { source, mapping } = R.propOr({}, ["data"], component);
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
};
|
|
27
|
+
const contexts = {
|
|
28
|
+
entry: entryContext,
|
|
29
|
+
screen: screenContext || screenData,
|
|
30
|
+
search: getSearchContext(searchContext, mapping),
|
|
31
|
+
};
|
|
33
32
|
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return source;
|
|
33
|
+
return getInflatedDataSourceUrl({ source, mapping, contexts });
|
|
38
34
|
}
|
|
39
35
|
);
|
|
@@ -3,7 +3,7 @@ import { renderWithProviders } from "@applicaster/zapp-react-native-utils/testUt
|
|
|
3
3
|
|
|
4
4
|
import * as zappPipesRedux from "@applicaster/zapp-react-native-redux/ZappPipes";
|
|
5
5
|
import configureStore from "redux-mock-store";
|
|
6
|
-
import thunk from "redux-thunk";
|
|
6
|
+
import { thunk } from "redux-thunk";
|
|
7
7
|
|
|
8
8
|
import { zappPipesDataConnector } from "../index";
|
|
9
9
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/// <reference types="@applicaster/zapp-react-native-ui-components" />
|
|
3
3
|
import React from "react";
|
|
4
4
|
import { useRoute } from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
|
|
5
|
-
import {
|
|
5
|
+
import { usePlugins } from "@applicaster/zapp-react-native-redux/hooks";
|
|
6
6
|
import { ZappPipesSearchContext } from "@applicaster/zapp-react-native-ui-components/Contexts";
|
|
7
7
|
import { useScreenContext } from "@applicaster/zapp-react-native-utils/reactHooks/screen/useScreenContext";
|
|
8
8
|
import { ResolverSelector } from "./ResolverSelector";
|
|
@@ -23,7 +23,7 @@ export function zappPipesDataConnector(
|
|
|
23
23
|
) {
|
|
24
24
|
return function WrappedWithZappPipesData(props: Props) {
|
|
25
25
|
const { screenData } = useRoute();
|
|
26
|
-
const
|
|
26
|
+
const plugins = usePlugins();
|
|
27
27
|
const screenContextData = useScreenContext();
|
|
28
28
|
|
|
29
29
|
const {
|
|
@@ -76,7 +76,7 @@ export function StaticFeedResolver({
|
|
|
76
76
|
|
|
77
77
|
const zappPipesDataProps = useMemo(
|
|
78
78
|
() => ({
|
|
79
|
-
zappPipesData: { url, loading, data, error },
|
|
79
|
+
zappPipesData: { url, loading, data, error }, // todo: add applyItemLimit
|
|
80
80
|
reloadData,
|
|
81
81
|
loadNextData: undefined, // Static resolver doesn't support pagination
|
|
82
82
|
}),
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { itemLimitForData } from "..";
|
|
2
|
+
|
|
3
|
+
describe("itemLimitForData (no mocks)", () => {
|
|
4
|
+
test("returns full array when item_limit is undefined (default Infinity)", () => {
|
|
5
|
+
// @ts-ignore
|
|
6
|
+
const result = itemLimitForData([1, 2, 3], {
|
|
7
|
+
rules: { item_limit: undefined },
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
expect(result).toEqual([1, 2, 3]);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test("enforces positive numeric item_limit", () => {
|
|
14
|
+
// @ts-ignore
|
|
15
|
+
const result = itemLimitForData([1, 2, 3, 4], {
|
|
16
|
+
rules: { item_limit: 2 },
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
expect(result).toEqual([1, 2]);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test("returns empty array when entry is empty", () => {
|
|
23
|
+
const result = itemLimitForData([], {
|
|
24
|
+
rules: { item_limit: 3 },
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
expect(result).toEqual([]);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("defaults entry to empty array when it is undefined", () => {
|
|
31
|
+
const result = itemLimitForData(undefined, {
|
|
32
|
+
rules: { item_limit: 5 },
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
expect(result).toEqual([]);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("handles missing component", () => {
|
|
39
|
+
// @ts-ignore
|
|
40
|
+
const result = itemLimitForData([10, 20, 30], undefined);
|
|
41
|
+
// missing component → item_limit = undefined → fallback to Infinity
|
|
42
|
+
expect(result).toEqual([10, 20, 30]);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test("handles missing rules object", () => {
|
|
46
|
+
// @ts-ignore
|
|
47
|
+
const result = itemLimitForData([10, 20, 30], {});
|
|
48
|
+
|
|
49
|
+
expect(result).toEqual([10, 20, 30]);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("non-positive item_limit should fall back to Infinity", () => {
|
|
53
|
+
// @ts-ignore
|
|
54
|
+
const result = itemLimitForData([1, 2, 3], {
|
|
55
|
+
rules: { item_limit: -10 },
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// -10 → invalid → fallback to Infinity → no limit
|
|
59
|
+
expect(result).toEqual([1, 2, 3]);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("zero item_limit should fall back to Infinity", () => {
|
|
63
|
+
// @ts-ignore
|
|
64
|
+
const result = itemLimitForData([1, 2, 3], {
|
|
65
|
+
rules: { item_limit: 0 },
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// 0 → invalid → fallback to Infinity
|
|
69
|
+
expect(result).toEqual([1, 2, 3]);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("NaN item_limit should fall back to Infinity", () => {
|
|
73
|
+
// @ts-ignore
|
|
74
|
+
const result = itemLimitForData([1, 2, 3], {
|
|
75
|
+
rules: { item_limit: NaN },
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
expect(result).toEqual([1, 2, 3]);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { toPositiveNumberWithDefault } from "@applicaster/zapp-react-native-utils/numberUtils";
|
|
2
|
+
|
|
3
|
+
export function itemLimitForData(
|
|
4
|
+
entry: ZappEntry[],
|
|
5
|
+
component: {
|
|
6
|
+
rules?: { item_limit?: number | string };
|
|
7
|
+
}
|
|
8
|
+
) {
|
|
9
|
+
if (!component?.rules?.item_limit) {
|
|
10
|
+
return entry;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const itemLimit = toPositiveNumberWithDefault(
|
|
14
|
+
Infinity,
|
|
15
|
+
component?.rules?.item_limit
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
return (entry || []).slice(0, itemLimit);
|
|
19
|
+
}
|
package/events/index.ts
CHANGED
|
@@ -5,4 +5,7 @@ export enum QBUIComponentEvents {
|
|
|
5
5
|
scrollVerticallyToInitialOffset = "scrollVerticallyToInitialOffset",
|
|
6
6
|
focusOnSelectedTab = "focusOnSelectedTab",
|
|
7
7
|
focusOnSelectedTopMenuItem = "focusOnSelectedTopMenuItem",
|
|
8
|
+
scrollToTopForScreenWrappedInContainer = "scrollToTopForScreenWrappedInContainer",
|
|
8
9
|
}
|
|
10
|
+
|
|
11
|
+
export { scrollEndReached$, emitScrollEndReached } from "./scrollEndReached";
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Subject } from "rxjs";
|
|
2
|
+
import { throttleTime } from "rxjs/operators";
|
|
3
|
+
|
|
4
|
+
const SCROLL_END_THROTTLE_MS = 1000;
|
|
5
|
+
const scrollEndReachedSubject = new Subject<void>();
|
|
6
|
+
|
|
7
|
+
/* Throttle so we only emit at most once per second; RN often fires onEndReached repeatedly (e.g. on Android) when near the bottom. */
|
|
8
|
+
export const scrollEndReached$ = scrollEndReachedSubject.pipe(
|
|
9
|
+
throttleTime(SCROLL_END_THROTTLE_MS)
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
/* Call from scroll container (ComponentsMap or Tabs) when scroll reaches end. */
|
|
13
|
+
export const emitScrollEndReached = (): void => {
|
|
14
|
+
scrollEndReachedSubject.next();
|
|
15
|
+
};
|
package/index.d.ts
CHANGED
|
@@ -228,6 +228,12 @@ declare type TabsSelectionContextType = {
|
|
|
228
228
|
selectedEntryIndex: number;
|
|
229
229
|
};
|
|
230
230
|
|
|
231
|
+
declare type TabsAccessibility = {
|
|
232
|
+
announcement?: string;
|
|
233
|
+
selectedHint?: string;
|
|
234
|
+
hint?: string;
|
|
235
|
+
};
|
|
236
|
+
|
|
231
237
|
declare type TabsProps = {
|
|
232
238
|
entry: ZappEntry[];
|
|
233
239
|
groupId: string;
|
|
@@ -235,6 +241,7 @@ declare type TabsProps = {
|
|
|
235
241
|
focused?: boolean;
|
|
236
242
|
parentFocus?: ParentFocus;
|
|
237
243
|
style?: ReactViewStyle;
|
|
244
|
+
accessibility?: TabsAccessibility;
|
|
238
245
|
};
|
|
239
246
|
|
|
240
247
|
declare type TabContentProps = {
|