@applicaster/zapp-react-native-ui-components 15.0.0-rc.143 → 15.0.0-rc.145

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.
Files changed (53) hide show
  1. package/Components/FocusableGroup/index.tsx +3 -2
  2. package/Components/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +5 -0
  3. package/Components/MasterCell/CONFIG_BUILDER_TO_REACT_COMPONENT.md +144 -0
  4. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/ActionButtonController.tsx +165 -0
  5. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/__tests__/ActionButtonController.test.tsx +405 -0
  6. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/index.ts +1 -0
  7. package/Components/MasterCell/DefaultComponents/ButtonContainerView/components/HorizontalSeparator.tsx +8 -0
  8. package/Components/MasterCell/DefaultComponents/ButtonContainerView/index.tsx +15 -0
  9. package/Components/MasterCell/DefaultComponents/ButtonContainerView/index.tv.android.tsx +58 -0
  10. package/Components/MasterCell/DefaultComponents/{tv/ButtonContainerView/index.tsx → ButtonContainerView/index.tv.tsx} +3 -11
  11. package/Components/MasterCell/DefaultComponents/ButtonContainerView/index.web.ts +1 -0
  12. package/Components/MasterCell/DefaultComponents/ButtonContainerView/types.ts +40 -0
  13. package/Components/MasterCell/DefaultComponents/DataProvider/index.tsx +163 -0
  14. package/Components/MasterCell/DefaultComponents/FocusableView/index.android.tsx +2 -23
  15. package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +4 -22
  16. package/Components/MasterCell/DefaultComponents/PressableView.tsx +7 -234
  17. package/Components/MasterCell/DefaultComponents/Text/hooks/useText.ts +11 -0
  18. package/Components/MasterCell/DefaultComponents/__tests__/DataProvider.test.tsx +141 -0
  19. package/Components/MasterCell/DefaultComponents/index.ts +7 -3
  20. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/ActionButton.tsx +135 -0
  21. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Asset.ts +20 -29
  22. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/AssetComponent.tsx +22 -0
  23. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Button.ts +67 -69
  24. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabelsContainer.ts +21 -16
  25. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/PressableView.test.tsx +207 -9
  26. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/builders.test.ts +56 -55
  27. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/index.test.ts +137 -16
  28. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/index.ts +49 -31
  29. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/index.ts +165 -0
  30. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/Asset.ts +4 -18
  31. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/Button.ts +24 -73
  32. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/TextLabelsContainer.ts +37 -18
  33. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/TvActionButton.tsx +27 -0
  34. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/__tests__/index.test.ts +24 -21
  35. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/__tests__/renderedTree.test.tsx +231 -0
  36. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/index.ts +24 -12
  37. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/index.ts +62 -0
  38. package/Components/MasterCell/MappingFunctions/index.js +3 -2
  39. package/Components/MasterCell/README.md +4 -0
  40. package/Components/MasterCell/__tests__/__snapshots__/dataAdapter.test.js.snap +24 -0
  41. package/Components/MasterCell/__tests__/configInflater.test.js +1 -0
  42. package/Components/MasterCell/__tests__/elementMapper.test.js +46 -0
  43. package/Components/MasterCell/dataAdapter.ts +4 -1
  44. package/Components/MasterCell/elementMapper.tsx +51 -7
  45. package/Components/MasterCell/utils/__tests__/cloneChildrenWithIds.test.tsx +43 -0
  46. package/Components/MasterCell/utils/__tests__/useFilterChildren.test.tsx +80 -0
  47. package/Components/MasterCell/utils/index.ts +85 -15
  48. package/Components/Navigator/StackNavigator.tsx +6 -0
  49. package/Components/ScreenRevealManager/withScreenRevealManager.tsx +4 -1
  50. package/package.json +5 -5
  51. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/ButtonContainerView.ts +0 -23
  52. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.android.tsx +0 -135
  53. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/types.ts +0 -25
@@ -0,0 +1,163 @@
1
+ import React from "react";
2
+ import { View } from "react-native";
3
+
4
+ type Props = Record<string, any> & {
5
+ children?: React.ReactNode;
6
+ };
7
+
8
+ /**
9
+ * Creates the provider payload from the resolved runtime props.
10
+ * `children` is structural and is therefore excluded. `_dataKey` is produced
11
+ * by `configInflater` from the data mapping propName and identifies the prop
12
+ * that carries the resolved entry value.
13
+ */
14
+ const createDataProviderProps = ({ children: _children, ...props }: Props) => ({
15
+ _dataKey: props._dataKey,
16
+ [props._dataKey as string]: props[props._dataKey as string],
17
+ });
18
+
19
+ /**
20
+ * Merges provider values into an element's existing `dataProviderProps`
21
+ * without overwriting keys that were supplied explicitly.
22
+ */
23
+ const mergeDataProviderProps = (
24
+ targetProps: Record<string, unknown>,
25
+ forwardedProps: Record<string, unknown>
26
+ ) => {
27
+ const dataProviderProps = Object.keys(forwardedProps).reduce(
28
+ (acc, key) => {
29
+ if (typeof acc[key] === "undefined") {
30
+ acc[key] = forwardedProps[key];
31
+ }
32
+
33
+ return acc;
34
+ },
35
+ { ...(targetProps.dataProviderProps as Record<string, unknown>) } as Record<
36
+ string,
37
+ unknown
38
+ >
39
+ );
40
+
41
+ return {
42
+ dataProviderProps: dataProviderProps,
43
+ ...(targetProps[dataProviderProps._dataKey as string] === undefined
44
+ ? {
45
+ [dataProviderProps._dataKey as string]:
46
+ forwardedProps[dataProviderProps._dataKey as string],
47
+ }
48
+ : {}),
49
+ };
50
+ };
51
+
52
+ /**
53
+ * Applies the provider props to the wrapped child's direct children only.
54
+ * The shallow stop is intentional: descendants below this level must receive
55
+ * data explicitly from their parent component instead of implicit propagation.
56
+ */
57
+ const cloneSecondLevel = (
58
+ nodes: React.ReactNode,
59
+ forwardedProps: Record<string, unknown>
60
+ ) =>
61
+ React.Children.map(nodes, (child) => {
62
+ if (!React.isValidElement(child)) {
63
+ return child;
64
+ }
65
+
66
+ if (child.type === View) {
67
+ return child;
68
+ }
69
+
70
+ return React.cloneElement(
71
+ child,
72
+ mergeDataProviderProps(
73
+ child.props as Record<string, unknown>,
74
+ forwardedProps
75
+ ) as never
76
+ );
77
+ });
78
+
79
+ /**
80
+ * Specification:
81
+ * `DataProvider` bridges MasterCell node-tree `data` mappings into runtime
82
+ * React props for a shallow wrapped subtree under `dataProviderProps`.
83
+ *
84
+ * Purpose:
85
+ * Use `DataProvider` when a subtree root and that root's direct children must
86
+ * receive the same resolved runtime prop without propagating that prop deeper
87
+ * into the tree.
88
+ *
89
+ * Input contract:
90
+ * - `DataProvider` receives runtime props from normal MasterCell inflation.
91
+ * - The values under `dataProviderProps` are derived from the resolved props
92
+ * it receives.
93
+ * - The inner keys are not hardcoded and may include `entry`, `item`, or any
94
+ * other prop name produced by the node-tree `data` mapping.
95
+ *
96
+ * Forwarding contract:
97
+ * - inject the mapped value as a direct prop on the wrapped child
98
+ * - inject `dataProviderProps` into the wrapped child
99
+ * - inject both the direct prop and `dataProviderProps` into that child's
100
+ * direct children
101
+ * - stop at that level
102
+ * - do not overwrite explicit keys that already exist inside
103
+ * `dataProviderProps` on wrapped elements
104
+ *
105
+ * Non-forwarded props:
106
+ * - `children`
107
+ *
108
+ * Example:
109
+ *
110
+ * ```ts
111
+ * {
112
+ * type: "DataProvider",
113
+ * data: [
114
+ * {
115
+ * func: "identity",
116
+ * args: [],
117
+ * propName: "entry",
118
+ * },
119
+ * ],
120
+ * elements: [
121
+ * {
122
+ * type: "ButtonContainerView",
123
+ * elements: [Button({ ... })],
124
+ * },
125
+ * ],
126
+ * }
127
+ * ```
128
+ *
129
+ * Expected runtime result:
130
+ * - `ButtonContainerView` receives `entry` and `dataProviderProps.entry`
131
+ * - each direct `Button` child receives `entry` and `dataProviderProps.entry`
132
+ * - deeper descendants such as `Asset` or `TextLabel` do not receive
133
+ * `entry` or `dataProviderProps.entry` unless their parent passes it
134
+ * explicitly
135
+ */
136
+ export const DataProvider = ({ children, ...props }: Props) => {
137
+ const forwardedProps = createDataProviderProps(props);
138
+
139
+ return (
140
+ <>
141
+ {React.Children.map(children, (child) => {
142
+ if (!React.isValidElement(child)) {
143
+ return child;
144
+ }
145
+
146
+ const injectedChildren = cloneSecondLevel(
147
+ child.props.children,
148
+ forwardedProps
149
+ );
150
+
151
+ return React.cloneElement(child, {
152
+ ...mergeDataProviderProps(
153
+ child.props as Record<string, unknown>,
154
+ forwardedProps
155
+ ),
156
+ ...(typeof injectedChildren !== "undefined"
157
+ ? { children: injectedChildren }
158
+ : {}),
159
+ });
160
+ })}
161
+ </>
162
+ );
163
+ };
@@ -1,17 +1,11 @@
1
1
  import React from "react";
2
2
  import { View, ImageStyle } from "react-native";
3
3
  import { Focusable } from "@applicaster/zapp-react-native-ui-components/Components/Focusable";
4
- import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
5
- import { getXray } from "@applicaster/zapp-react-native-utils/logger";
6
4
  import { useFocusable } from "@applicaster/zapp-react-native-ui-components/Components/Focusable/index.android";
7
5
  import { useIsRTL } from "@applicaster/zapp-react-native-utils/localizationUtils";
8
6
 
9
- const { Logger } = getXray();
10
-
11
7
  import { recursiveCloneElementsWithState } from "../../utils";
12
8
 
13
- const logger = new Logger("plugin", "plugins/navigation-action");
14
-
15
9
  type ButtonProps = Record<string, any> & {
16
10
  style: ImageStyle;
17
11
  };
@@ -33,7 +27,7 @@ const getNextFocusRight = ({ nextFocusRight, parentFocus, isRTL }) => {
33
27
  };
34
28
 
35
29
  export function FocusableViewComponent(
36
- { style, children, item, ...otherProps }: ButtonProps,
30
+ { style, children, ...otherProps }: ButtonProps,
37
31
  ref
38
32
  ) {
39
33
  const {
@@ -44,7 +38,6 @@ export function FocusableViewComponent(
44
38
  focusedStyles,
45
39
  nextFocusLeft,
46
40
  nextFocusRight,
47
- pluginIdentifier,
48
41
  disableFocus,
49
42
  onButtonFocus,
50
43
  } = otherProps;
@@ -52,20 +45,6 @@ export function FocusableViewComponent(
52
45
  const parentFocus = useFocusable();
53
46
  const isRTL = useIsRTL();
54
47
 
55
- const actionContext = useActions(pluginIdentifier);
56
-
57
- const onPress = () => {
58
- if (!actionContext) {
59
- logger.warning(
60
- `Cannot resolve action context for ${pluginIdentifier} - please make sure the plugin is installed and up to date`
61
- );
62
-
63
- return;
64
- }
65
-
66
- actionContext?.invokeAction?.(item);
67
- };
68
-
69
48
  const onFocus = React.useCallback(
70
49
  (ref) => {
71
50
  onButtonFocus?.(ref);
@@ -82,7 +61,7 @@ export function FocusableViewComponent(
82
61
  id={generateId(cellUUID, suffixId)}
83
62
  disableFocus={disableFocus}
84
63
  groupId={groupId}
85
- onPress={onPress}
64
+ onPress={otherProps.onPress}
86
65
  nextFocusUp={parentFocus?.nextFocusUp}
87
66
  nextFocusDown={parentFocus?.nextFocusDown}
88
67
  nextFocusLeft={getNextFocusLeft({ nextFocusLeft, parentFocus, isRTL })}
@@ -1,26 +1,20 @@
1
1
  import React, { useMemo } from "react";
2
2
  import { ImageStyle } from "react-native";
3
3
  import { Focusable } from "@applicaster/zapp-react-native-ui-components/Components/Focusable";
4
- import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
5
- import { getXray } from "@applicaster/zapp-react-native-utils/logger";
6
4
  import { toBooleanWithDefaultFalse } from "@applicaster/zapp-react-native-utils/booleanUtils";
7
5
 
8
- const { Logger } = getXray();
9
-
10
6
  import {
11
7
  recursiveCloneElementsWithState,
12
8
  getFocusedButtonId,
13
9
  } from "../../utils";
14
10
 
15
- const logger = new Logger("plugin", "plugins/navigation-action");
16
-
17
11
  type Props = Record<string, any> & {
18
12
  style: ImageStyle;
19
13
  };
20
14
 
21
15
  const getFocusableId = (prefixId, suffixId) => `${prefixId}___${suffixId}`;
22
16
 
23
- export function FocusableView({ style, children, item, ...otherProps }: Props) {
17
+ export function FocusableView({ style, children, ...otherProps }: Props) {
24
18
  const {
25
19
  groupId,
26
20
  prefixId,
@@ -29,7 +23,6 @@ export function FocusableView({ style, children, item, ...otherProps }: Props) {
29
23
  focusedButtonId,
30
24
  normalStyles,
31
25
  focusedStyles,
32
- pluginIdentifier,
33
26
  preferredFocus,
34
27
  } = otherProps;
35
28
 
@@ -41,20 +34,9 @@ export function FocusableView({ style, children, item, ...otherProps }: Props) {
41
34
 
42
35
  const additionalStyles = focused ? focusedStyles : normalStyles;
43
36
 
44
- const actionContext = useActions(pluginIdentifier);
45
-
46
- const onPress = (event) => {
37
+ const handlePress = (event) => {
47
38
  event?.preventDefault?.();
48
-
49
- if (!actionContext) {
50
- logger.warning(
51
- `Cannot resolve action context for ${pluginIdentifier} - please make sure the plugin is installed and up to date`
52
- );
53
-
54
- return;
55
- }
56
-
57
- actionContext?.invokeAction?.(item);
39
+ otherProps?.onPress?.(event);
58
40
  };
59
41
 
60
42
  const handleFocus = (focusable) => {
@@ -83,7 +65,7 @@ export function FocusableView({ style, children, item, ...otherProps }: Props) {
83
65
  style={styles}
84
66
  onFocus={handleFocus}
85
67
  onBlur={handleBlur}
86
- onPress={onPress}
68
+ onPress={handlePress}
87
69
  preferredFocus={preferredFocus}
88
70
  skipFocusManagerRegistration={otherProps.skipFocusManagerRegistration}
89
71
  isFocusable={otherProps.isFocusable}
@@ -1,261 +1,34 @@
1
- import React, { useCallback, useEffect, useMemo, useState } from "react";
1
+ import React from "react";
2
2
  import { TouchableOpacity } from "react-native";
3
3
 
4
- import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
5
-
6
- import { masterCellLogger } from "../logger";
7
- import {
8
- buildLegacySelection,
9
- resolveIsActive,
10
- resolveLabelText,
11
- } from "./mobile/MobileActionButtons/helpers";
12
-
13
- type ChildElementProps = {
14
- children?: React.ReactNode;
15
- mobileActionRole?: string;
16
- uri?: string;
17
- state?: "default" | "focused";
18
- };
19
-
20
4
  type Props = {
21
5
  children?: React.ReactNode;
22
- item: ZappEntry | ZappFeed;
23
- action?: {
24
- identifier?: string;
25
- flavour?: "flavour_1" | "flavour_2";
26
- };
27
6
  style?: Record<string, unknown>;
28
- focusedStyles?: Record<string, unknown>;
29
7
  testID?: string;
30
8
  accessibilityLabel?: string;
31
9
  accessibilityHint?: string;
32
- };
33
-
34
- const isValidElement = (
35
- child: React.ReactNode
36
- ): child is React.ReactElement<ChildElementProps> =>
37
- React.isValidElement(child);
38
-
39
- /** retrieves asset uri for a given flavour,
40
- * if flavour is not provided, returns the default asset from `asset` or selected state asset (if available)
41
- * asset can be:
42
- * provided as asset path,
43
- * provided as [default || flavour_1, alternative || flavour_2] array.
44
- * mobileButtonAssets asset can be:
45
- * provided as asset path,
46
- * provided as [default || flavour_1, alternative || flavour_2] array,
47
- * provided as [[] as flavour_1, [] as flavour_2] array for multiple flavours, where each flavour can be either a path or [default, alternative] array.
48
- *
49
- * isActive reflect the state of the action and can be used to render different asset for active/inactive state if asset is provided as array
50
- *
51
- * */
52
- const selectByAssetFlavour = (
53
- actionState: {
54
- asset?: string | [string, string];
55
- mobileButtonAssets?: [string, string] | [string, string][];
56
- },
57
- flavour?: "flavour_1" | "flavour_2",
58
- isActive?: boolean
59
- ) => {
60
- if (actionState.mobileButtonAssets) {
61
- if (flavour) {
62
- if (flavour === "flavour_1") {
63
- if (typeof actionState.mobileButtonAssets[0] === "string") {
64
- return actionState.mobileButtonAssets[0];
65
- }
66
-
67
- if (Array.isArray(actionState.mobileButtonAssets[0])) {
68
- return actionState.mobileButtonAssets[0][isActive ? 1 : 0];
69
- }
70
-
71
- return actionState.mobileButtonAssets[0];
72
- } else if (flavour === "flavour_2") {
73
- if (typeof actionState.mobileButtonAssets[1] === "string") {
74
- return actionState.mobileButtonAssets[1];
75
- }
76
-
77
- if (Array.isArray(actionState.mobileButtonAssets[1])) {
78
- return actionState.mobileButtonAssets[1][isActive ? 1 : 0];
79
- }
80
-
81
- return actionState.mobileButtonAssets[1];
82
- }
83
- }
84
-
85
- return Array.isArray(actionState.mobileButtonAssets[0])
86
- ? actionState.mobileButtonAssets[0][isActive ? 1 : 0]
87
- : actionState.mobileButtonAssets[0];
88
- } else {
89
- return typeof actionState?.asset === "string"
90
- ? actionState.asset
91
- : actionState.asset[isActive ? 1 : 0];
92
- }
10
+ onPress?: () => void;
93
11
  };
94
12
 
95
13
  export function PressableView({
96
14
  children,
97
- item,
98
- action,
99
15
  style = {},
100
- focusedStyles = {},
101
16
  testID,
102
17
  accessibilityLabel,
103
18
  accessibilityHint,
19
+ onPress,
104
20
  }: Props) {
105
- const actionContext = useActions(action?.identifier);
106
-
107
- const actionDisabled =
108
- typeof actionContext?.isActionAvailable === "function" &&
109
- !actionContext.isActionAvailable(item);
110
-
111
- const supportsEntryState =
112
- typeof actionContext?.initialEntryState === "function" && !actionDisabled;
113
-
114
- const [actionState, setActionState] = useState(() =>
115
- supportsEntryState ? actionContext.initialEntryState(item) : null
116
- );
117
-
118
- useEffect(() => {
119
- if (supportsEntryState) {
120
- setActionState(actionContext.initialEntryState(item));
121
- }
122
- }, [supportsEntryState, item, actionContext]);
123
-
124
- useEffect(() => {
125
- if (typeof actionContext?.addListener === "function") {
126
- return actionContext.addListener(String(item?.id), (nextState) => {
127
- setActionState(nextState);
128
- });
129
- }
130
-
131
- if (typeof actionContext?.addListeners === "function") {
132
- return actionContext.addListeners(({ entryState, entry }) => {
133
- if (entry?.id === item?.id) {
134
- setActionState(entryState);
135
- }
136
- });
137
- }
138
-
139
- return undefined;
140
- }, [actionContext, item?.id]);
141
-
142
- const legacySelected = useMemo(() => {
143
- if (!actionContext || supportsEntryState) {
144
- return false;
145
- }
146
-
147
- return buildLegacySelection(item, actionContext);
148
- }, [actionContext, supportsEntryState, item]);
149
-
150
- const isActive = resolveIsActive(actionState, legacySelected);
151
-
152
- const labelText = supportsEntryState
153
- ? resolveLabelText(actionState?.label)
154
- : "";
155
-
156
- const shouldRenderAsset = Boolean(
157
- supportsEntryState
158
- ? actionState?.asset && actionState?.mobileButtonAssets
159
- : false
160
- );
161
-
162
- const shouldRenderLabel = Boolean(labelText);
163
-
164
- const cloneChildrenWithState = (nodes?: React.ReactNode): React.ReactNode => {
165
- return React.Children.map(nodes, (child) => {
166
- if (!isValidElement(child)) {
167
- return child;
168
- }
169
-
170
- const role = child.props.mobileActionRole;
171
- const nextChildren = cloneChildrenWithState(child.props.children);
172
-
173
- if (role === "asset" && !shouldRenderAsset) {
174
- return null;
175
- }
176
-
177
- if (role === "label" && !shouldRenderLabel) {
178
- return null;
179
- }
180
-
181
- if (
182
- role === "label_container" &&
183
- React.Children.count(nextChildren) === 0
184
- ) {
185
- return null;
186
- }
187
-
188
- const nextProps: Partial<ChildElementProps> = {};
189
-
190
- if (role === "asset") {
191
- if (action.flavour) {
192
- nextProps.uri = selectByAssetFlavour(
193
- actionState,
194
- action.flavour,
195
- isActive
196
- );
197
- }
198
- }
199
-
200
- if (role === "label") {
201
- nextProps.state = isActive ? "focused" : "default";
202
- }
203
-
204
- if (nextChildren !== child.props.children) {
205
- nextProps.children = nextChildren;
206
- }
207
-
208
- return React.cloneElement(child, nextProps);
209
- });
210
- };
211
-
212
- const onPress = useCallback(async () => {
213
- if (!actionContext) {
214
- return;
215
- }
216
-
217
- if (supportsEntryState) {
218
- return actionContext.invokeAction(item, {
219
- updateState: setActionState,
220
- });
221
- }
222
-
223
- const favouritesAction = legacySelected
224
- ? actionContext.removeFavourite
225
- : actionContext.addFavourite;
226
-
227
- const toggleAction = actionContext?.invokeAction ?? favouritesAction;
228
-
229
- return toggleAction?.(item);
230
- }, [actionContext, supportsEntryState, item, legacySelected]);
231
-
232
- if (!actionContext) {
233
- masterCellLogger.warning(
234
- `You're missing an action plugin(${action?.identifier}) required by your mobile action button.`
235
- );
236
-
237
- return null;
238
- }
239
-
240
- if (actionDisabled) {
241
- return null;
242
- }
243
-
244
- if (!shouldRenderAsset && !shouldRenderLabel) {
245
- return null;
246
- }
247
-
248
21
  return (
249
22
  <TouchableOpacity
250
23
  activeOpacity={1}
251
24
  onPress={onPress}
252
- testID={testID || `${item?.id}`}
253
- accessibilityLabel={accessibilityLabel || `${item?.id}`}
25
+ testID={testID}
26
+ accessibilityLabel={accessibilityLabel}
254
27
  accessibilityHint={accessibilityHint}
255
28
  accessible={!!(testID || accessibilityLabel)}
256
- style={isActive ? { ...style, ...focusedStyles } : style}
29
+ style={style}
257
30
  >
258
- {cloneChildrenWithState(children)}
31
+ {children}
259
32
  </TouchableOpacity>
260
33
  );
261
34
  }
@@ -45,6 +45,7 @@ export const useTextLabel = ({ label, entry }): string => {
45
45
  const [entryStateLocal, setEntryStateLocal] =
46
46
  React.useState(initialEntryState);
47
47
 
48
+ // For favourites
48
49
  React.useEffect(() => {
49
50
  return action?.addListeners?.(({ entryState, entry: actionEntry }) => {
50
51
  if (entry.id === actionEntry.id) {
@@ -53,6 +54,16 @@ export const useTextLabel = ({ label, entry }): string => {
53
54
  });
54
55
  }, []);
55
56
 
57
+ // For rest actions
58
+ React.useEffect(() => {
59
+ // Update entryStateLocal when action state changes. Example: state change when pressing the download button.
60
+ if (typeof action?.addListener === "function") {
61
+ return action.addListener(String(entry?.id), (nextState) => {
62
+ setEntryStateLocal(nextState);
63
+ });
64
+ }
65
+ }, []);
66
+
56
67
  if (context && name && action) {
57
68
  return prepareHebrewText(extractLabel(entryStateLocal.label, name), isRTL);
58
69
  }