@applicaster/zapp-react-native-ui-components 16.0.0-rc.72 → 16.0.0-rc.73
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.
|
@@ -2,22 +2,10 @@ import React, { useCallback, useEffect } from "react";
|
|
|
2
2
|
import { TouchableOpacity, ViewStyle } from "react-native";
|
|
3
3
|
|
|
4
4
|
import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
|
|
5
|
-
import {
|
|
6
|
-
observeEntryState,
|
|
7
|
-
RegisteredActionValue,
|
|
8
|
-
} from "@applicaster/zapp-react-native-utils/uiActionsRegistrator";
|
|
9
|
-
|
|
10
|
-
import { masterCellLogger } from "../logger";
|
|
11
5
|
|
|
12
6
|
import Image from "./Image";
|
|
13
7
|
type Props = {
|
|
14
8
|
item: ZappEntry | ZappFeed;
|
|
15
|
-
/**
|
|
16
|
-
* Already resolved action value. Pass it when the caller took the action out
|
|
17
|
-
* of `uiActionsRegistry` (entry actions have no plugin context to look up),
|
|
18
|
-
* and this skips the `useActions` lookup entirely.
|
|
19
|
-
*/
|
|
20
|
-
actionContext?: RegisteredActionValue;
|
|
21
9
|
flavour?: "flavour_1" | "flavour_2";
|
|
22
10
|
asset?: {
|
|
23
11
|
props: {};
|
|
@@ -44,19 +32,6 @@ function isStringAsset(asset) {
|
|
|
44
32
|
return typeof asset === "string" || Array.isArray(asset);
|
|
45
33
|
}
|
|
46
34
|
|
|
47
|
-
/**
|
|
48
|
-
* `CellActionEntryState["asset"]` is either something `Image` can take (a URI,
|
|
49
|
-
* or a per-flavour array) or a component to render. A plain object is neither -
|
|
50
|
-
* a locale/state map reaches us that way - and handing one to JSX throws
|
|
51
|
-
* "Element type is invalid", so it has to be recognised rather than assumed.
|
|
52
|
-
*/
|
|
53
|
-
function isComponentAsset(asset): boolean {
|
|
54
|
-
return (
|
|
55
|
-
typeof asset === "function" ||
|
|
56
|
-
(typeof asset === "object" && asset !== null && "$$typeof" in asset)
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
35
|
function getAssetValue(asset, flavour, fallbackAsset = null) {
|
|
61
36
|
if (!asset) {
|
|
62
37
|
return null;
|
|
@@ -80,52 +55,26 @@ function getAssetValue(asset, flavour, fallbackAsset = null) {
|
|
|
80
55
|
export const ActionButton = React.memo(function ActionButtonComponent(
|
|
81
56
|
props: Props
|
|
82
57
|
) {
|
|
83
|
-
const {
|
|
84
|
-
|
|
85
|
-
action,
|
|
86
|
-
asset,
|
|
87
|
-
flavour = "flavour_1",
|
|
88
|
-
cellUUID,
|
|
89
|
-
actionContext: providedActionContext,
|
|
90
|
-
} = props;
|
|
91
|
-
|
|
92
|
-
// Passing `undefined` makes `useActions` a no-op lookup instead of warning
|
|
93
|
-
// about a plugin that was never meant to be there. Callers are expected to
|
|
94
|
-
// either always or never provide the context for a given mounted button -
|
|
95
|
-
// `useActions` already branches on plugin presence internally, so a value
|
|
96
|
-
// that appears mid-life would change the hook count either way.
|
|
97
|
-
const lookedUpActionContext = useActions(
|
|
98
|
-
providedActionContext ? undefined : action?.identifier
|
|
99
|
-
);
|
|
100
|
-
|
|
101
|
-
const actionContext = providedActionContext ?? lookedUpActionContext;
|
|
58
|
+
const { item, action, asset, flavour = "flavour_1", cellUUID } = props;
|
|
59
|
+
const actionContext = useActions(action?.identifier);
|
|
102
60
|
|
|
103
61
|
// TODO: add subscription API for action availability
|
|
104
62
|
const actionDisabled =
|
|
105
63
|
typeof actionContext?.isActionAvailable === "function" &&
|
|
106
64
|
!actionContext.isActionAvailable(item);
|
|
107
65
|
|
|
108
|
-
|
|
109
|
-
|
|
66
|
+
// Note: in theory initialization is not needed anymore, we are using useEffect
|
|
67
|
+
const [actionState, setActionState] = React.useState(
|
|
68
|
+
actionDisabled || !actionContext
|
|
69
|
+
? null
|
|
70
|
+
: actionContext.initialEntryState(item)
|
|
110
71
|
);
|
|
111
72
|
|
|
112
|
-
// `observeEntryState` is the one way to follow an action's state: it uses the
|
|
113
|
-
// action's own stream when it has one, and otherwise builds an equivalent one
|
|
114
|
-
// from `initialEntryState` + `addListener`. Reading only the latter pair
|
|
115
|
-
// leaves stream-backed actions - playback speed, sleep timer - frozen at the
|
|
116
|
-
// value they had when the button mounted.
|
|
117
73
|
useEffect(() => {
|
|
118
|
-
if (actionDisabled || !actionContext) {
|
|
119
|
-
|
|
74
|
+
if (!((actionDisabled || !actionContext) && actionState !== null)) {
|
|
75
|
+
setActionState(actionContext.initialEntryState(item));
|
|
120
76
|
}
|
|
121
|
-
|
|
122
|
-
const subscription = observeEntryState(actionContext, item).subscribe(
|
|
123
|
-
setActionState
|
|
124
|
-
);
|
|
125
|
-
|
|
126
|
-
return () => subscription.unsubscribe();
|
|
127
|
-
// eslint-disable-next-line @wogns3623/better-exhaustive-deps/exhaustive-deps
|
|
128
|
-
}, [actionDisabled, item?.id, actionContext]);
|
|
77
|
+
}, [actionDisabled, item?.id, actionContext, action, setActionState]);
|
|
129
78
|
|
|
130
79
|
const onPress = useCallback(() => {
|
|
131
80
|
actionContext.invokeAction(item, {
|
|
@@ -133,30 +82,15 @@ export const ActionButton = React.memo(function ActionButtonComponent(
|
|
|
133
82
|
setActionState(state);
|
|
134
83
|
},
|
|
135
84
|
});
|
|
136
|
-
}, [actionContext, item]);
|
|
137
|
-
|
|
138
|
-
// An asset that is present but of a shape nothing can render (a locale or
|
|
139
|
-
// state map, say) drops the button for good - unlike a missing asset, which
|
|
140
|
-
// is usually just state that has not loaded yet. Reported from an effect so
|
|
141
|
-
// it is said once per action/entry rather than on every render.
|
|
142
|
-
const unrenderableAsset =
|
|
143
|
-
Boolean(actionState?.asset) &&
|
|
144
|
-
!isStringAsset(actionState?.asset) &&
|
|
145
|
-
!isComponentAsset(actionState?.asset);
|
|
85
|
+
}, [actionState, actionContext?.state, item?.id]);
|
|
146
86
|
|
|
147
87
|
useEffect(() => {
|
|
148
|
-
if (
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
data: {
|
|
152
|
-
identifier: action?.identifier,
|
|
153
|
-
entryId: item?.id,
|
|
154
|
-
assetType: typeof actionState?.asset,
|
|
155
|
-
},
|
|
88
|
+
if (typeof actionContext?.addListener === "function") {
|
|
89
|
+
return actionContext?.addListener(String(item.id), (state) => {
|
|
90
|
+
setActionState(state);
|
|
156
91
|
});
|
|
157
92
|
}
|
|
158
|
-
|
|
159
|
-
}, [unrenderableAsset, action?.identifier, item?.id]);
|
|
93
|
+
}, [item?.id, setActionState, actionContext]);
|
|
160
94
|
|
|
161
95
|
if (actionDisabled || !actionContext) return null;
|
|
162
96
|
const AssetComponent = actionState?.asset;
|
|
@@ -164,22 +98,12 @@ export const ActionButton = React.memo(function ActionButtonComponent(
|
|
|
164
98
|
// Default state is not yet ready
|
|
165
99
|
if (!AssetComponent) return null;
|
|
166
100
|
|
|
167
|
-
// Neither an image source nor a component - nothing renderable.
|
|
168
|
-
if (!isStringAsset(AssetComponent) && !isComponentAsset(AssetComponent)) {
|
|
169
|
-
return null;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
101
|
return (
|
|
173
102
|
<TouchableOpacity
|
|
174
103
|
activeOpacity={1}
|
|
175
104
|
onPress={onPress}
|
|
176
105
|
testID={props?.testID || `${item?.id}`}
|
|
177
|
-
|
|
178
|
-
accessibilityLabel={
|
|
179
|
-
props?.accessibilityLabel ||
|
|
180
|
-
(typeof actionState?.label === "string" ? actionState.label : null) ||
|
|
181
|
-
`${item?.id}`
|
|
182
|
-
}
|
|
106
|
+
accessibilityLabel={props?.accessibilityLabel || `${item?.id}`}
|
|
183
107
|
accessibilityHint={props?.accessibilityHint}
|
|
184
108
|
accessible={!!(props?.testID || props?.accessibilityLabel)}
|
|
185
109
|
style={props?.style}
|
|
@@ -13,8 +13,6 @@ import {
|
|
|
13
13
|
} from "@applicaster/zapp-react-native-utils/reactHooks/screen";
|
|
14
14
|
import { RegisteredAction } from "@applicaster/zapp-react-native-utils/uiActionsRegistrator";
|
|
15
15
|
|
|
16
|
-
import { ZappPipesEntryContext } from "../Contexts";
|
|
17
|
-
|
|
18
16
|
/**
|
|
19
17
|
* Expands `entry.extensions.entry_action` into invokable actions, against the
|
|
20
18
|
* screen this hook is called from.
|
|
@@ -33,12 +31,9 @@ export function useEntryActionsExpander(): (
|
|
|
33
31
|
) => RegisteredAction[] {
|
|
34
32
|
const { pathname } = useRoute();
|
|
35
33
|
|
|
36
|
-
const
|
|
37
|
-
ZappPipesEntryContext.Context
|
|
38
|
-
);
|
|
39
|
-
|
|
34
|
+
const screenContext = useScreenContext();
|
|
40
35
|
const screenData = useCurrentScreenData();
|
|
41
|
-
const screenState =
|
|
36
|
+
const screenState = screenContext?.options;
|
|
42
37
|
const screenStateStore = useScreenStateStore();
|
|
43
38
|
const actionExecutor = React.useContext(ActionExecutorContext);
|
|
44
39
|
|
|
@@ -54,7 +49,10 @@ export function useEntryActionsExpander(): (
|
|
|
54
49
|
screenState,
|
|
55
50
|
screenRoute: pathname,
|
|
56
51
|
screenStateStore,
|
|
57
|
-
|
|
52
|
+
// From the screen context, the way `useCellClick` builds the same bag.
|
|
53
|
+
// The ZappPipes entry context is a route-keyed map, so reading `.data`
|
|
54
|
+
// off it was always undefined.
|
|
55
|
+
screenEntry: screenContext?.entry,
|
|
58
56
|
},
|
|
59
57
|
};
|
|
60
58
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/zapp-react-native-ui-components",
|
|
3
|
-
"version": "16.0.0-rc.
|
|
3
|
+
"version": "16.0.0-rc.73",
|
|
4
4
|
"description": "Applicaster Zapp React Native ui components for the Quick Brick App",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -28,10 +28,10 @@
|
|
|
28
28
|
},
|
|
29
29
|
"homepage": "https://github.com/applicaster/quickbrick#readme",
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@applicaster/applicaster-types": "16.0.0-rc.
|
|
32
|
-
"@applicaster/zapp-react-native-bridge": "16.0.0-rc.
|
|
33
|
-
"@applicaster/zapp-react-native-redux": "16.0.0-rc.
|
|
34
|
-
"@applicaster/zapp-react-native-utils": "16.0.0-rc.
|
|
31
|
+
"@applicaster/applicaster-types": "16.0.0-rc.73",
|
|
32
|
+
"@applicaster/zapp-react-native-bridge": "16.0.0-rc.73",
|
|
33
|
+
"@applicaster/zapp-react-native-redux": "16.0.0-rc.73",
|
|
34
|
+
"@applicaster/zapp-react-native-utils": "16.0.0-rc.73",
|
|
35
35
|
"fast-json-stable-stringify": "^2.1.0",
|
|
36
36
|
"promise": "^8.3.0",
|
|
37
37
|
"react-native-sortables": "1.7.1",
|