@applicaster/zapp-react-native-ui-components 16.0.0-rc.12 → 16.0.0-rc.120
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/AudioPlayer/tv/__tests__/audioPlayer.test.js +19 -0
- package/Components/AudioPlayer/tv/__tests__/index.test.tsx +120 -0
- package/Components/AudioPlayer/tv/index.tsx +5 -1
- package/Components/Cell/Cell.tsx +3 -6
- package/Components/Cell/TvOSCellComponent.tsx +0 -1
- package/Components/CellRendererResolver/index.ts +1 -1
- package/Components/FocusableGroup/FocusableTvOS.tsx +11 -7
- package/Components/GeneralContentScreen/GeneralContentScreenHookAdapter.tsx +54 -0
- package/Components/GeneralContentScreen/__tests__/GeneralContentScreenHookAdapter.test.tsx +64 -0
- package/Components/GeneralContentScreen/__tests__/HookContentFocusGroup.web.test.tsx +91 -0
- package/Components/GeneralContentScreen/hookAdapter/__tests__/networkService.test.ts +74 -0
- package/Components/GeneralContentScreen/hookAdapter/__tests__/runInBackground.test.ts +139 -0
- package/Components/GeneralContentScreen/hookAdapter/__tests__/skipExpression.test.ts +294 -0
- package/Components/GeneralContentScreen/hookAdapter/__tests__/validationHelper.test.ts +188 -0
- package/Components/GeneralContentScreen/hookAdapter/logger.ts +6 -0
- package/Components/GeneralContentScreen/hookAdapter/networkService.ts +53 -0
- package/Components/GeneralContentScreen/hookAdapter/runInBackground.ts +48 -0
- package/Components/GeneralContentScreen/hookAdapter/skipExpression.ts +252 -0
- package/Components/GeneralContentScreen/hookAdapter/validationHelper.ts +90 -0
- package/Components/GeneralContentScreen/hookFocus/index.tsx +13 -0
- package/Components/GeneralContentScreen/hookFocus/index.web.tsx +69 -0
- package/Components/GeneralContentScreen/index.ts +2 -0
- package/Components/Layout/FullWidthRow.tsx +38 -0
- package/Components/LinkHandler/LinkHandler.tsx +2 -7
- package/Components/MasterCell/DefaultComponents/ActionButton.tsx +5 -1
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/placement.test.ts +47 -21
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/ActionButtonController.tsx +19 -4
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/__tests__/ActionButtonController.test.tsx +43 -1
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/placement.ts +34 -6
- package/Components/MasterCell/DefaultComponents/Button.tsx +1 -0
- package/Components/MasterCell/DefaultComponents/Image/hoc/utils/__tests__/withDimensions.test.ts +420 -125
- package/Components/MasterCell/DefaultComponents/Image/hoc/utils/index.ts +114 -19
- package/Components/MasterCell/DefaultComponents/Image/hoc/withDimensions.tsx +19 -8
- package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +5 -3
- package/Components/MasterCell/DefaultComponents/LiveImage/executePreloadHooks.ts +2 -4
- package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +14 -6
- package/Components/MasterCell/DefaultComponents/PressableView.tsx +29 -9
- package/Components/MasterCell/DefaultComponents/Text/hooks/useText.ts +4 -0
- package/Components/MasterCell/DefaultComponents/Toggle.tsx +36 -14
- package/Components/MasterCell/DefaultComponents/__tests__/Toggle.test.tsx +167 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/ActionButton.tsx +154 -100
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/AssetComponent.tsx +15 -4
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Button.ts +56 -22
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Spacer.ts +6 -4
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabelsContainer.ts +3 -1
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/PressableView.test.tsx +34 -21
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/index.test.ts +15 -4
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/index.ts +23 -16
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/__tests__/insertButtons.test.ts +8 -3
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/index.ts +2 -2
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/__tests__/insertButtonsBetweenLabels.test.ts +45 -13
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/index.ts +2 -2
- package/Components/MasterCell/SharedUI/CollapsibleTextContainer/__tests__/factory.test.ts +32 -0
- package/Components/MasterCell/__tests__/masterCell.test.js +60 -0
- package/Components/MasterCell/contexts/PressedStateContext.ts +3 -0
- package/Components/MasterCell/dataAdapter.ts +15 -5
- package/Components/MasterCell/hooks/index.ts +2 -0
- package/Components/MasterCell/hooks/usePressedState.ts +4 -0
- package/Components/MasterCell/index.tsx +57 -8
- package/Components/MasterCell/utils/__tests__/resolveColor.test.js +169 -132
- package/Components/MasterCell/utils/__tests__/resolveColorForProp.test.js +92 -0
- package/Components/MasterCell/utils/__tests__/withoutFocusedState.test.js +20 -0
- package/Components/MasterCell/utils/index.ts +103 -46
- package/Components/ModalComponent/ActionsBottomSheet/ActionButton.tsx +56 -0
- package/Components/ModalComponent/ActionsBottomSheet/__tests__/openActionsBottomSheet.test.ts +38 -0
- package/Components/ModalComponent/ActionsBottomSheet/boundActionButton.tsx +24 -0
- package/Components/ModalComponent/ActionsBottomSheet/index.ts +9 -0
- package/Components/ModalComponent/ActionsBottomSheet/openActionsBottomSheet.ts +61 -0
- package/Components/ModalComponent/AudioPlayer/Components/Button.tsx +392 -0
- package/Components/ModalComponent/AudioPlayer/Components/Header.tsx +772 -0
- package/Components/ModalComponent/AudioPlayer/Components/Input.tsx +728 -0
- package/Components/ModalComponent/AudioPlayer/Components/Item.tsx +1028 -0
- package/Components/ModalComponent/AudioPlayer/Components/NowPlayingHeaderSection.tsx +205 -0
- package/Components/ModalComponent/AudioPlayer/Components/__tests__/Item.test.tsx +164 -0
- package/Components/ModalComponent/AudioPlayer/Components/__tests__/NowPlayingHeaderSection.test.tsx +36 -0
- package/Components/ModalComponent/AudioPlayer/Components/index.ts +9 -0
- package/Components/ModalComponent/AudioPlayer/utils/__tests__/mergeStyles.test.ts +433 -0
- package/Components/ModalComponent/AudioPlayer/utils/mergeStyles.ts +563 -0
- package/Components/ModalComponent/BottomSheetDragArea.tsx +33 -0
- package/Components/ModalComponent/BottomSheetModalContent.tsx +440 -50
- package/Components/ModalComponent/Button/index.tsx +5 -3
- package/Components/ModalComponent/Header/CloseButton.tsx +12 -0
- package/Components/ModalComponent/Header/__tests__/CloseButton.test.tsx +72 -0
- package/Components/ModalComponent/Header/index.tsx +20 -7
- package/Components/ModalComponent/Header/utils.ts +1 -1
- package/Components/ModalComponent/ModalBlocksStyleContext.tsx +10 -0
- package/Components/ModalComponent/SortableList.tsx +156 -0
- package/Components/ModalComponent/SortableList.web.tsx +34 -0
- package/Components/ModalComponent/TextInputContent.tsx +135 -0
- package/Components/ModalComponent/__tests__/BottomSheetDragArea.test.tsx +74 -0
- package/Components/ModalComponent/__tests__/BottomSheetModalContent.test.tsx +343 -0
- package/Components/ModalComponent/__tests__/SortableList.web.test.tsx +16 -0
- package/Components/ModalComponent/__tests__/TextInputContent.test.tsx +169 -0
- package/Components/ModalComponent/__tests__/showTextInput.test.ts +120 -0
- package/Components/ModalComponent/index.tsx +18 -9
- package/Components/ModalComponent/presets/__tests__/getCell.test.ts +90 -0
- package/Components/ModalComponent/presets/dynamicCollectionCell.tsx +78 -0
- package/Components/ModalComponent/presets/index.ts +27 -0
- package/Components/ModalComponent/presets/selectableCell.tsx +28 -0
- package/Components/ModalComponent/presets/standardCell.tsx +41 -0
- package/Components/ModalComponent/presets/types.ts +35 -0
- package/Components/ModalComponent/showTextInput.ts +54 -0
- package/Components/ModalComponent/utils.ts +82 -21
- package/Components/OfflineHandler/__tests__/__snapshots__/index.test.tsx.snap +4 -85
- package/Components/OfflineHandler/__tests__/index.test.tsx +186 -17
- package/Components/OfflineHandler/hooks.ts +111 -0
- package/Components/OfflineHandler/index.tsx +91 -75
- package/Components/OfflineHandler/utils/index.ts +1 -13
- package/Components/PlayerContainer/PlayerContainer.tsx +28 -11
- package/Components/PlayerContainer/ProgramInfo/index.tsx +3 -4
- package/Components/PlayerContainer/__tests__/PlayerContainer.test.tsx +337 -0
- package/Components/PreloaderWrapper/index.tsx +1 -1
- package/Components/River/ComponentsMap/ComponentsMap.tsx +91 -62
- package/Components/River/River.tsx +4 -0
- package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +142 -89
- package/Components/River/__tests__/componentsMap.test.js +8 -3
- package/Components/River/__tests__/resultCallback.test.tsx +47 -0
- package/Components/Screen/__tests__/Screen.test.tsx +24 -0
- package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +2 -0
- package/Components/Screen/index.tsx +13 -4
- package/Components/Screen/navigationHandler.ts +8 -8
- package/Components/ScreenFeedLoader/ScreenFeedLoader.tsx +5 -3
- package/Components/ScreenResolverFeedProvider/ScreenResolverFeedProvider.tsx +1 -1
- package/Components/ScreenRevealManager/ScreenRevealManager.ts +18 -2
- package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +36 -0
- package/Components/ScreenRevealManager/__tests__/withScreenRevealManager.test.tsx +56 -0
- package/Components/ScreenRevealManager/withScreenRevealManager.tsx +21 -4
- package/Components/TopCutoffOverlay/index.tsx +2 -2
- package/Components/Transitioner/AnimationManager.js +1 -1
- package/Components/Transitioner/Scene.tsx +4 -3
- package/Components/Transitioner/Transitioner.tsx +98 -49
- package/Components/Transitioner/__tests__/transitioner.test.js +372 -3
- package/Components/VideoLive/LiveImageManager.ts +9 -3
- package/Components/VideoLive/__tests__/LiveImageManager.test.ts +59 -0
- package/Components/VideoModal/PlayerWrapper.tsx +4 -1
- package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +1 -0
- package/Components/VideoModal/__tests__/playerWrapperStyle.test.ts +58 -0
- package/Components/VideoModal/__tests__/playerWrapperUtils.test.ts +16 -0
- package/Components/VideoModal/playerWrapperStyle.ts +21 -3
- package/Components/VideoModal/playerWrapperUtils.ts +7 -1
- package/Components/ZappFrameworkComponents/BarView/BarView.tsx +12 -5
- package/Components/ZappFrameworkComponents/BarView/__tests__/BarView.test.tsx +2 -2
- package/Contexts/CachedDimensionsContext/__tests__/index.test.ts +154 -0
- package/Contexts/CachedDimensionsContext/index.ts +17 -2
- package/Contexts/ModalNavigationContext/__tests__/index.test.tsx +15 -0
- package/Contexts/ModalNavigationContext/index.tsx +2 -2
- package/Decorators/ZappPipesDataConnector/__tests__/screenToolbox.test.ts +347 -0
- package/Decorators/ZappPipesDataConnector/__tests__/screenToolboxUnavailable.test.ts +53 -0
- package/Decorators/ZappPipesDataConnector/index.tsx +19 -4
- package/Decorators/ZappPipesDataConnector/screenToolbox.ts +384 -0
- package/Helpers/ComponentCellSelectionHelper/index.js +0 -6
- package/Helpers/index.js +0 -39
- package/Hooks/useEntryActionsExpander.ts +63 -0
- package/assets/empty-Info.png +0 -0
- package/package.json +6 -5
- package/Components/OfflineHandler/NotificationView/NotificationView.lg.tsx +0 -112
- package/Components/OfflineHandler/NotificationView/NotificationView.samsung.tsx +0 -107
- package/Components/OfflineHandler/NotificationView/NotificationView.tsx +0 -153
- package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +0 -66
- package/Components/OfflineHandler/NotificationView/utils.ts +0 -34
- package/Components/Screen/utils.ts +0 -16
- package/Helpers/Analytics/index.js +0 -95
- /package/Components/BackgroundImage/{BackgroundImage.tv.android.tsx → BackgroundImage.android.tv.tsx} +0 -0
- /package/Components/BackgroundImage/{BackgroundImage.tv.ios.tsx → BackgroundImage.ios.tv.tsx} +0 -0
- /package/Components/MasterCell/DefaultComponents/ButtonContainerView/{index.tv.android.tsx → index.android.tv.tsx} +0 -0
- /package/Decorators/ZappPipesDataConnector/__tests__/{Hero.js → Hero.tsx} +0 -0
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
import React, { useMemo } from "react";
|
|
2
2
|
import * as R from "ramda";
|
|
3
|
-
import
|
|
3
|
+
import memoizee from "memoizee";
|
|
4
|
+
|
|
4
5
|
import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
5
6
|
import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
|
|
6
|
-
|
|
7
|
-
import { masterCellLogger } from "../logger";
|
|
8
|
-
import { getCellState } from "../../Cell/utils";
|
|
7
|
+
import { isValidColor } from "@applicaster/zapp-react-native-utils/colorUtils";
|
|
9
8
|
import { getColorFromData } from "@applicaster/zapp-react-native-utils/cellUtils";
|
|
10
|
-
import { get } from "@applicaster/zapp-react-native-utils/utils";
|
|
11
|
-
import {
|
|
9
|
+
import { pathOr, get, isNil } from "@applicaster/zapp-react-native-utils/utils";
|
|
10
|
+
import { isNilOrEmpty } from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
|
|
12
11
|
import { useRoute } from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
13
12
|
import { useScreenStateStore } from "@applicaster/zapp-react-native-utils/reactHooks/navigation/useScreenStateStore";
|
|
14
|
-
import
|
|
15
|
-
import
|
|
13
|
+
import { isNotEmptyString } from "@applicaster/zapp-react-native-utils/stringUtils";
|
|
14
|
+
import { masterCellLogger } from "../logger";
|
|
15
|
+
import { getCellState } from "../../Cell/utils";
|
|
16
|
+
import { isCellSelected, useBehaviorUpdate } from "./behaviorProvider";
|
|
16
17
|
import { DataProvider } from "../DefaultComponents/DataProvider";
|
|
17
18
|
|
|
18
19
|
const hasElementSpecificViewType = (viewType) => (element) => {
|
|
19
|
-
if (
|
|
20
|
+
if (isNil(element)) {
|
|
20
21
|
return false;
|
|
21
22
|
}
|
|
22
23
|
|
|
@@ -28,12 +29,20 @@ const hasElementSpecificViewType = (viewType) => (element) => {
|
|
|
28
29
|
return hasElementsSpecificViewType(viewType)(element.elements);
|
|
29
30
|
};
|
|
30
31
|
|
|
32
|
+
export const hasElementsSpecificViewType = (viewType) => (elements) => {
|
|
33
|
+
if (isNilOrEmpty(elements)) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return R.any(hasElementSpecificViewType(viewType))(elements);
|
|
38
|
+
};
|
|
39
|
+
|
|
31
40
|
const logWarning = (
|
|
32
41
|
colorValueFromCellStyle,
|
|
33
42
|
colorFromProp,
|
|
34
43
|
colorValueFromEntry
|
|
35
44
|
) => {
|
|
36
|
-
if (
|
|
45
|
+
if (isNil(colorValueFromEntry)) {
|
|
37
46
|
masterCellLogger.warn({
|
|
38
47
|
message: `Cannot resolve property ${colorValueFromCellStyle} from the entry.`,
|
|
39
48
|
data: {
|
|
@@ -44,15 +53,10 @@ const logWarning = (
|
|
|
44
53
|
}
|
|
45
54
|
};
|
|
46
55
|
|
|
47
|
-
export
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
return R.any(hasElementSpecificViewType(viewType))(elements);
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
function resolveColorForProp(entry: any, colorFromProp: string | undefined) {
|
|
56
|
+
export function resolveColorForProp(
|
|
57
|
+
entry: any,
|
|
58
|
+
colorFromProp: string | undefined
|
|
59
|
+
) {
|
|
56
60
|
if (!colorFromProp) {
|
|
57
61
|
return undefined;
|
|
58
62
|
}
|
|
@@ -62,9 +66,7 @@ function resolveColorForProp(entry: any, colorFromProp: string | undefined) {
|
|
|
62
66
|
colorFromProp.split(".")
|
|
63
67
|
);
|
|
64
68
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
if (nestedEntryValue === undefined && !validateColor(color)) {
|
|
69
|
+
if (nestedEntryValue === undefined && !isValidColor(colorFromProp)) {
|
|
68
70
|
logWarning(colorFromProp, colorFromProp, nestedEntryValue);
|
|
69
71
|
|
|
70
72
|
return undefined;
|
|
@@ -86,36 +88,73 @@ function resolveColorForProp(entry: any, colorFromProp: string | undefined) {
|
|
|
86
88
|
|
|
87
89
|
const getColorKeys = memoizee((style) => {
|
|
88
90
|
const styleKeys = Object.keys(style);
|
|
89
|
-
const colorKeys = styleKeys.filter((key) => /color/i.test(key));
|
|
90
91
|
|
|
91
|
-
return
|
|
92
|
+
return styleKeys.filter((key) => /color/i.test(key));
|
|
92
93
|
});
|
|
93
94
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
95
|
+
/**
|
|
96
|
+
* A color style value is either a literal color (e.g. "#FFFFFF") or a
|
|
97
|
+
* data-mapping path into the entry. Data mappings always point at the entry
|
|
98
|
+
* `extensions` object (e.g. "extensions.brandColor"), so we only need to do a
|
|
99
|
+
* path lookup when the value starts with "extensions" — everything else is a
|
|
100
|
+
* literal color and can be returned as-is.
|
|
101
|
+
*
|
|
102
|
+
* This avoids the previous, very expensive approach that ran `validateColor`
|
|
103
|
+
* on every value and memoized on a full `fast-json-stable-stringify` of the
|
|
104
|
+
* entire entry.
|
|
105
|
+
*/
|
|
106
|
+
const EXTENSIONS_PREFIX = "extensions";
|
|
107
|
+
|
|
108
|
+
const isDataMappingPath = (value: string): boolean =>
|
|
109
|
+
typeof value === "string" && value.startsWith(EXTENSIONS_PREFIX);
|
|
99
110
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
111
|
+
export const resolveColor = (
|
|
112
|
+
entry,
|
|
113
|
+
style,
|
|
114
|
+
allowDynamicColorsOutsideExtensions
|
|
115
|
+
) => {
|
|
116
|
+
if (style === null || style === undefined) {
|
|
117
|
+
return style;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const colorKeys = getColorKeys(style);
|
|
121
|
+
|
|
122
|
+
if (colorKeys.length === 0) {
|
|
123
|
+
return style;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return colorKeys.reduce(
|
|
127
|
+
(acc, key) => {
|
|
128
|
+
const value = acc[key];
|
|
104
129
|
|
|
105
|
-
|
|
106
|
-
|
|
130
|
+
// 1. The Expensive Edge-Case (Only for the app with root mappings)
|
|
131
|
+
if (allowDynamicColorsOutsideExtensions && isNotEmptyString(value)) {
|
|
132
|
+
const possibleColor = resolveColorForProp(entry, value);
|
|
133
|
+
|
|
134
|
+
acc[key] = isValidColor(possibleColor) ? possibleColor : null;
|
|
107
135
|
|
|
108
136
|
return acc;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// 2. The Fast Path (For 99% of apps)
|
|
140
|
+
if (isDataMappingPath(value)) {
|
|
141
|
+
// resolve the mapped color from the entry; fall back to null
|
|
142
|
+
// (RN ignores null style values) when the path is missing
|
|
143
|
+
const possibleColor = pathOr(null, value.split("."), entry);
|
|
144
|
+
|
|
145
|
+
acc[key] = possibleColor;
|
|
146
|
+
|
|
147
|
+
return acc;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// 3. Default Case: Treat as a raw color string
|
|
151
|
+
acc[key] = value;
|
|
152
|
+
|
|
153
|
+
return acc;
|
|
116
154
|
},
|
|
117
|
-
|
|
118
|
-
);
|
|
155
|
+
{ ...style }
|
|
156
|
+
);
|
|
157
|
+
};
|
|
119
158
|
|
|
120
159
|
export function isVideoPreviewEnabled({
|
|
121
160
|
enable_video_preview = false,
|
|
@@ -238,7 +277,7 @@ const recursiveCloneElement = (focused: boolean) => (element) => {
|
|
|
238
277
|
};
|
|
239
278
|
|
|
240
279
|
export const recursiveCloneElementsWithState = (focused: boolean, children) => {
|
|
241
|
-
if (
|
|
280
|
+
if (isNilOrEmpty(children)) {
|
|
242
281
|
return undefined;
|
|
243
282
|
}
|
|
244
283
|
|
|
@@ -252,7 +291,7 @@ export const cloneChildrenWithIds = (
|
|
|
252
291
|
ids: string[],
|
|
253
292
|
children: React.ReactElement[]
|
|
254
293
|
) => {
|
|
255
|
-
if (
|
|
294
|
+
if (isNilOrEmpty(children)) {
|
|
256
295
|
return undefined;
|
|
257
296
|
}
|
|
258
297
|
|
|
@@ -347,3 +386,21 @@ export function getEntryState(state, selected) {
|
|
|
347
386
|
|
|
348
387
|
return selected ? "selected" : "default";
|
|
349
388
|
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Drops focused (pressed) variants of a cell state, so a non-interactive cell
|
|
392
|
+
* always renders its idle look. `selected` is kept – it is not touch feedback.
|
|
393
|
+
*/
|
|
394
|
+
export function withoutFocusedState(
|
|
395
|
+
state?: CellState | null
|
|
396
|
+
): CellState | null | undefined {
|
|
397
|
+
if (state === "focused_selected") {
|
|
398
|
+
return "selected";
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
if (state === "focused") {
|
|
402
|
+
return "default";
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
return state;
|
|
406
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { useEntryActionState } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
|
|
3
|
+
import {
|
|
4
|
+
isActionAvailableFor,
|
|
5
|
+
RegisteredAction,
|
|
6
|
+
} from "@applicaster/zapp-react-native-utils/uiActionsRegistrator";
|
|
7
|
+
|
|
8
|
+
import { Button } from "../Button";
|
|
9
|
+
|
|
10
|
+
export type ActionButtonProps = {
|
|
11
|
+
entry: ZappEntry;
|
|
12
|
+
item: RegisteredAction;
|
|
13
|
+
configuration: any;
|
|
14
|
+
width: number;
|
|
15
|
+
component?: ZappUIComponent & { parent?: ZappUIComponent };
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
type Props = ActionButtonProps & {
|
|
19
|
+
onPress?: (item: any) => void;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Unified action button for the modal bottom sheet.
|
|
24
|
+
*
|
|
25
|
+
* Every item is a `RegisteredAction` resolved from the registry (entry actions,
|
|
26
|
+
* registry actions and legacy context-provider actions alike), so the button
|
|
27
|
+
* simply reads `item.action` and drives it through `useEntryActionState`
|
|
28
|
+
* (backed by the action's observable / `addListener`).
|
|
29
|
+
*/
|
|
30
|
+
export function ActionButton(props: Props) {
|
|
31
|
+
const { item, entry, onPress: onItemPress, component } = props;
|
|
32
|
+
|
|
33
|
+
const action = item?.action;
|
|
34
|
+
|
|
35
|
+
const { state, invokeAction } = useEntryActionState(action, entry);
|
|
36
|
+
|
|
37
|
+
const onPress = React.useCallback(
|
|
38
|
+
(_item) => {
|
|
39
|
+
invokeAction({
|
|
40
|
+
context: "toast",
|
|
41
|
+
dismiss: () => onItemPress?.(item),
|
|
42
|
+
...(component ? { component } : {}),
|
|
43
|
+
});
|
|
44
|
+
},
|
|
45
|
+
[invokeAction, item, onItemPress, component]
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
if (!isActionAvailableFor(action, entry as ZappEntry)) return null;
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<Button {...props} item={state} onPress={onPress} label={state?.label} />
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Explicitly set display name for the component
|
|
56
|
+
ActionButton.displayName = "ActionButton";
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { openBottomSheetModal } from "@applicaster/zapp-react-native-utils/modalState";
|
|
2
|
+
import { boundActionButton } from "../boundActionButton";
|
|
3
|
+
import { openActionsBottomSheet } from "../openActionsBottomSheet";
|
|
4
|
+
|
|
5
|
+
jest.mock("@applicaster/zapp-react-native-utils/modalState", () => ({
|
|
6
|
+
openBottomSheetModal: jest.fn(),
|
|
7
|
+
}));
|
|
8
|
+
|
|
9
|
+
jest.mock("../boundActionButton", () => ({
|
|
10
|
+
boundActionButton: jest.fn(() => jest.fn()),
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
describe("openActionsBottomSheet", () => {
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
jest.clearAllMocks();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("binds the List component into the sheet row button", () => {
|
|
19
|
+
const entry = { id: "entry-1" } as ZappEntry;
|
|
20
|
+
|
|
21
|
+
const component = {
|
|
22
|
+
data: { source: "https://example.com/user/collections/abc" },
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const actions = [
|
|
26
|
+
{ identifier: "entry_action_0", action: { invokeAction: jest.fn() } },
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
openActionsBottomSheet({
|
|
30
|
+
entry,
|
|
31
|
+
actions: actions as any,
|
|
32
|
+
component: component as any,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
expect(boundActionButton).toHaveBeenCalledWith(entry, component);
|
|
36
|
+
expect(openBottomSheetModal).toHaveBeenCalledTimes(1);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
import { ActionButton, ActionButtonProps } from "./ActionButton";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Binds an entry to the sheet's row button.
|
|
7
|
+
*
|
|
8
|
+
* `BottomSheetModalContent` renders `buttonComponent` per item and does not
|
|
9
|
+
* know about entries, so the entry is closed over here instead.
|
|
10
|
+
*/
|
|
11
|
+
export const boundActionButton = (
|
|
12
|
+
entry: ZappEntry | ZappFeed,
|
|
13
|
+
component?: ZappUIComponent & { parent?: ZappUIComponent }
|
|
14
|
+
) => {
|
|
15
|
+
const ActionButtonWithEntry = (props: ActionButtonProps) => (
|
|
16
|
+
<ActionButton {...props} entry={entry as ZappEntry} component={component} />
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
ActionButtonWithEntry.displayName = "ActionButtonWithEntry";
|
|
20
|
+
|
|
21
|
+
return ActionButtonWithEntry;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
boundActionButton.displayName = "boundActionButton";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { ActionButton } from "./ActionButton";
|
|
2
|
+
|
|
3
|
+
export type { ActionButtonProps } from "./ActionButton";
|
|
4
|
+
|
|
5
|
+
export { boundActionButton } from "./boundActionButton";
|
|
6
|
+
|
|
7
|
+
export { openActionsBottomSheet } from "./openActionsBottomSheet";
|
|
8
|
+
|
|
9
|
+
export type { OpenActionsBottomSheetArgs } from "./openActionsBottomSheet";
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { createLogger } from "@applicaster/zapp-react-native-utils/logger";
|
|
2
|
+
import { openBottomSheetModal } from "@applicaster/zapp-react-native-utils/modalState";
|
|
3
|
+
import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
|
|
4
|
+
import { RegisteredAction } from "@applicaster/zapp-react-native-utils/uiActionsRegistrator";
|
|
5
|
+
|
|
6
|
+
import { boundActionButton } from "./boundActionButton";
|
|
7
|
+
|
|
8
|
+
const { log_warning } = createLogger({
|
|
9
|
+
subsystem: "ActionsBottomSheet",
|
|
10
|
+
category: "Presentation",
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
export type OpenActionsBottomSheetArgs = {
|
|
14
|
+
/** Entry the actions act on; bound into every row button. */
|
|
15
|
+
entry: ZappEntry | ZappFeed;
|
|
16
|
+
/** Already resolved and merged actions, in the order they should appear. */
|
|
17
|
+
actions: RegisteredAction[];
|
|
18
|
+
title?: string;
|
|
19
|
+
summary?: string;
|
|
20
|
+
/** List that opened the sheet; forwarded into row invokeAction context. */
|
|
21
|
+
component?: ZappUIComponent & { parent?: ZappUIComponent };
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Presents a list of resolved actions in the shared bottom sheet.
|
|
26
|
+
*
|
|
27
|
+
* This is the core entry point for "show these actions in a sheet": the
|
|
28
|
+
* open-modal-bottom-sheet plugin uses it for its configured list, and the
|
|
29
|
+
* player overflow uses it for the actions that did not fit on the overlay.
|
|
30
|
+
* Neither has to know about the other, and neither needs the plugin installed.
|
|
31
|
+
*/
|
|
32
|
+
export function openActionsBottomSheet({
|
|
33
|
+
entry,
|
|
34
|
+
actions,
|
|
35
|
+
title,
|
|
36
|
+
summary,
|
|
37
|
+
component,
|
|
38
|
+
}: OpenActionsBottomSheetArgs): void {
|
|
39
|
+
if (!actions?.length) {
|
|
40
|
+
// Every action resolved away - unavailable for this entry, or not
|
|
41
|
+
// invokable. An empty sheet looks broken, so say why and stay put.
|
|
42
|
+
log_warning(
|
|
43
|
+
"openActionsBottomSheet: nothing to present - not opening the sheet",
|
|
44
|
+
{ entryId: (entry as ZappEntry)?.id }
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
openBottomSheetModal({
|
|
51
|
+
modalBottomSheetContentProps: {
|
|
52
|
+
// TODO: should be optional - the button handles its own tap, but
|
|
53
|
+
// BottomSheetModalContent still requires an onPress.
|
|
54
|
+
onPress: noop,
|
|
55
|
+
items: actions,
|
|
56
|
+
buttonComponent: boundActionButton(entry, component),
|
|
57
|
+
title,
|
|
58
|
+
summary,
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
}
|