@applicaster/zapp-react-native-ui-components 16.0.0-alpha.3081324238 → 16.0.0-alpha.3202041045
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/MasterCell/DefaultComponents/ActionButton.tsx +13 -0
- package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +12 -3
- package/Components/MasterCell/DefaultComponents/PressableView.tsx +3 -0
- package/Components/MasterCell/DefaultComponents/__tests__/ActionButton.test.tsx +288 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/ActionButton.tsx +2 -1
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/PressableView.test.tsx +4 -2
- package/Components/MasterCell/__tests__/masterCell.test.js +60 -0
- package/Components/MasterCell/index.tsx +28 -4
- package/Components/MasterCell/utils/__tests__/withoutFocusedState.test.js +20 -0
- package/Components/MasterCell/utils/index.ts +18 -0
- package/Components/ModalComponent/AudioPlayer/utils/__tests__/mergeStyles.test.ts +40 -0
- package/Components/ModalComponent/AudioPlayer/utils/mergeStyles.ts +55 -15
- package/Components/ModalComponent/BottomSheetModalContent.tsx +5 -1
- package/Components/ModalComponent/__tests__/showTextInput.test.ts +25 -0
- package/Components/ModalComponent/showTextInput.ts +2 -0
- package/Components/PlayerContainer/PlayerContainer.tsx +22 -5
- package/Components/PlayerContainer/__tests__/PlayerContainer.test.tsx +51 -0
- package/Components/River/ComponentsMap/ComponentsMap.tsx +4 -1
- package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +2 -0
- package/Components/VideoLive/LiveImageManager.ts +126 -12
- package/Components/VideoLive/MuteUnmuteController.ts +135 -0
- package/Components/VideoLive/PlayerLiveImageComponent.tsx +15 -19
- package/Components/VideoLive/__tests__/LiveImageMuteState.test.ts +311 -0
- package/Components/VideoLive/__tests__/MuteUnmuteController.test.ts +135 -0
- package/Components/VideoLive/__tests__/PlayerLiveImageComponent.test.tsx +63 -1
- 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/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/package.json +5 -5
|
@@ -5,7 +5,9 @@ import { useRoute } from "@applicaster/zapp-react-native-utils/reactHooks/naviga
|
|
|
5
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
|
+
import { useUIComponentContext } from "@applicaster/zapp-react-native-ui-components/Contexts/UIComponentContext";
|
|
8
9
|
import { ResolverSelector } from "./ResolverSelector";
|
|
10
|
+
import { useScreenToolboxRecord } from "./screenToolbox";
|
|
9
11
|
import { ComponentDataSourceContext } from "./types";
|
|
10
12
|
|
|
11
13
|
type Props = {
|
|
@@ -22,7 +24,7 @@ export function zappPipesDataConnector(
|
|
|
22
24
|
Component: React.FC<any> | React.ComponentClass<any>
|
|
23
25
|
) {
|
|
24
26
|
return function WrappedWithZappPipesData(props: Props) {
|
|
25
|
-
const { screenData } = useRoute();
|
|
27
|
+
const { screenData, pathname } = useRoute();
|
|
26
28
|
const plugins = usePlugins();
|
|
27
29
|
const screenContextData = useScreenContext();
|
|
28
30
|
|
|
@@ -53,6 +55,16 @@ export function zappPipesDataConnector(
|
|
|
53
55
|
|
|
54
56
|
const [searchContext] = ZappPipesSearchContext.useZappPipesContext();
|
|
55
57
|
|
|
58
|
+
const parent = useUIComponentContext() as { id?: string } | null;
|
|
59
|
+
|
|
60
|
+
const toolboxRecord = useScreenToolboxRecord(
|
|
61
|
+
component,
|
|
62
|
+
riverId,
|
|
63
|
+
componentIndex,
|
|
64
|
+
parent?.id,
|
|
65
|
+
pathname
|
|
66
|
+
);
|
|
67
|
+
|
|
56
68
|
// Prepare context for resolvers
|
|
57
69
|
const resolverProps: ComponentDataSourceContext = {
|
|
58
70
|
component,
|
|
@@ -71,9 +83,12 @@ export function zappPipesDataConnector(
|
|
|
71
83
|
|
|
72
84
|
return (
|
|
73
85
|
<ResolverSelector {...resolverProps}>
|
|
74
|
-
{(zappPipesDataProps) =>
|
|
75
|
-
|
|
76
|
-
|
|
86
|
+
{(zappPipesDataProps) => {
|
|
87
|
+
// A plain field on the toolbox record, read only when the agent asks: no render.
|
|
88
|
+
toolboxRecord.feed = zappPipesDataProps.zappPipesData;
|
|
89
|
+
|
|
90
|
+
return <Component {...props} {...zappPipesDataProps} />;
|
|
91
|
+
}}
|
|
77
92
|
</ResolverSelector>
|
|
78
93
|
);
|
|
79
94
|
};
|
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
import { useEffect, useMemo } from "react";
|
|
2
|
+
import { Platform } from "react-native";
|
|
3
|
+
import * as R from "ramda";
|
|
4
|
+
import { appStore } from "@applicaster/zapp-react-native-redux/AppStore";
|
|
5
|
+
import {
|
|
6
|
+
registerXrayToolbox,
|
|
7
|
+
XrayToolSpec,
|
|
8
|
+
} from "@applicaster/zapp-react-native-utils/logger/xrayToolbox";
|
|
9
|
+
import { modalStore } from "@applicaster/zapp-react-native-utils/modalState";
|
|
10
|
+
import { ROUTE_TYPES } from "@applicaster/zapp-react-native-utils/navigationUtils/routeTypes";
|
|
11
|
+
import { getXrayNavigator } from "@applicaster/zapp-react-native-utils/navigationUtils/xrayNavigator";
|
|
12
|
+
import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
13
|
+
import { zappHookModalStore } from "@applicaster/zapp-react-native-ui-components/Contexts/ZappHookModalContext";
|
|
14
|
+
|
|
15
|
+
import { ZappPipesData } from "./types";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* X-Ray `screen.*` toolbox: what the current screen shows — its river components in order, the
|
|
19
|
+
* feed each one loaded and the entries it displays. Every river component, on every platform,
|
|
20
|
+
* goes through the zappPipes decorator, which writes a record here on mount and drops it on
|
|
21
|
+
* unmount: a plain Set, nothing subscribes to it, so it never renders anything. Records are
|
|
22
|
+
* dropped for good once X-Ray turns out to have no toolbox bridge (release builds).
|
|
23
|
+
*
|
|
24
|
+
* Screens that are not rivers (screen plugins, hooks, the player) report what renders them,
|
|
25
|
+
* read from navigation and the modal stores; a plugin with state of its own registers its own
|
|
26
|
+
* toolbox, as the player does.
|
|
27
|
+
*
|
|
28
|
+
* Mounted is not visible (lists are virtualised): which rows and cells are on screen comes from
|
|
29
|
+
* the native `ui.inspect`. River rows carry testID `component-<componentId>` (mobile, tvOS,
|
|
30
|
+
* Android TV) and cells carry their entry id in their testID.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
type ComponentRecord = {
|
|
34
|
+
component: ZappUIComponent;
|
|
35
|
+
riverId: string;
|
|
36
|
+
index?: number;
|
|
37
|
+
parentId?: string;
|
|
38
|
+
pathname: string;
|
|
39
|
+
/** Written on each render: the data the component was given, after item filter and limit. */
|
|
40
|
+
feed?: ZappPipesData;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/** What a route shows; `screenType` is how ScreenResolver renders it. */
|
|
44
|
+
type RouteState = {
|
|
45
|
+
screenType?: string;
|
|
46
|
+
screen?: any;
|
|
47
|
+
entry?: Partial<ZappEntry>;
|
|
48
|
+
nested?: { screen?: ZappRiver };
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/** A screen presented over the navigation stack. `covers` is false when it is out of the way. */
|
|
52
|
+
type Layer = RouteState & { pathname: string; covers: boolean };
|
|
53
|
+
|
|
54
|
+
const records = new Set<ComponentRecord>();
|
|
55
|
+
|
|
56
|
+
/** Set once registration failed: X-Ray has no toolbox bridge, so nothing will ever read records. */
|
|
57
|
+
let unavailable = false;
|
|
58
|
+
|
|
59
|
+
const DEFAULT_ENTRIES_LIMIT = 10;
|
|
60
|
+
|
|
61
|
+
const toLimit = (value: unknown) =>
|
|
62
|
+
typeof value === "number" && value >= 0 ? value : DEFAULT_ENTRIES_LIMIT;
|
|
63
|
+
|
|
64
|
+
/** The hooks modal, with the pathname ZappHooksModal gives it; hidden while it runs in the background. */
|
|
65
|
+
const hooksModalLayer = (): Layer | undefined => {
|
|
66
|
+
const { state, isHooksExecutionInProgress, isRunningInBackground } =
|
|
67
|
+
zappHookModalStore.getState();
|
|
68
|
+
|
|
69
|
+
if (!isHooksExecutionInProgress || !state.path) return undefined;
|
|
70
|
+
|
|
71
|
+
const hookPlugin = state.screenData?.hookPlugin;
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
pathname: `${ROUTE_TYPES.HOOKS_MODAL}/${hookPlugin ? hookPlugin.name : ""}`,
|
|
75
|
+
covers:
|
|
76
|
+
!isRunningInBackground && hookPlugin?.module?.presentFullScreen !== false,
|
|
77
|
+
screenType: "hooks",
|
|
78
|
+
screen: hookPlugin,
|
|
79
|
+
entry: state.screenData?.payload as ZappEntry,
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/** The modal screen or bottom sheet, with the pathname and props ModalProvider gives it. */
|
|
84
|
+
const modalLayer = (): Layer | undefined => {
|
|
85
|
+
const { visible, screen, props } = modalStore.getState().modalState;
|
|
86
|
+
|
|
87
|
+
if (!visible || !screen) return undefined;
|
|
88
|
+
|
|
89
|
+
const { screenId, screenType, entry, screenData } = {
|
|
90
|
+
screenId: screen.id,
|
|
91
|
+
screenType: "river",
|
|
92
|
+
...props,
|
|
93
|
+
} as Record<string, any>;
|
|
94
|
+
|
|
95
|
+
// A modal hook (and the ad interstitial) presents a { hookPlugin, payload } wrapper.
|
|
96
|
+
const isHook = screenType === "hooks";
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
pathname: `${ROUTE_TYPES.MODAL}/${screenId}`,
|
|
100
|
+
covers: true,
|
|
101
|
+
screenType,
|
|
102
|
+
screen: isHook ? (screen.hookPlugin ?? screenData?.hookPlugin) : screen,
|
|
103
|
+
entry: isHook ? screen.payload : entry,
|
|
104
|
+
};
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const videoModalLayer = (): Layer | undefined => {
|
|
108
|
+
const videoModal = getXrayNavigator()?.videoModalState;
|
|
109
|
+
|
|
110
|
+
if (!videoModal?.visible) return undefined;
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
pathname: `${ROUTE_TYPES.VIDEO_MODAL}/${videoModal.item?.id}`,
|
|
114
|
+
covers: videoModal.mode !== "MINIMIZED",
|
|
115
|
+
screenType: "playable",
|
|
116
|
+
entry: videoModal.item,
|
|
117
|
+
};
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Topmost first: the hooks modal and modals are native RN Modals, drawn over the video modal.
|
|
122
|
+
* Only layers this platform renders: the mobile layout renders the hooks and video modals, and
|
|
123
|
+
* ModalProvider renders nothing on web.
|
|
124
|
+
*/
|
|
125
|
+
const presentedLayers = () => {
|
|
126
|
+
const web = Platform.OS === "web";
|
|
127
|
+
const mobile = !web && !isTV();
|
|
128
|
+
|
|
129
|
+
return [
|
|
130
|
+
mobile && hooksModalLayer(),
|
|
131
|
+
!web && modalLayer(),
|
|
132
|
+
mobile && videoModalLayer(),
|
|
133
|
+
].filter((layer): layer is Layer => Boolean(layer));
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
/** The pathname of what is on top: a covering presented layer, else the stack top. */
|
|
137
|
+
export const getTopPathname = (): string | undefined =>
|
|
138
|
+
presentedLayers().find((layer) => layer.covers)?.pathname ??
|
|
139
|
+
getXrayNavigator()?.getPathname();
|
|
140
|
+
|
|
141
|
+
/** What the route at `pathname` shows. Presented layers are not on the stack: their stores hold them. */
|
|
142
|
+
const routeState = (pathname: string): RouteState => {
|
|
143
|
+
const layer = presentedLayers().find((item) => item.pathname === pathname);
|
|
144
|
+
|
|
145
|
+
if (layer) return layer;
|
|
146
|
+
|
|
147
|
+
// Not presented any more (a stale modal pathname): nothing to describe.
|
|
148
|
+
if (!pathname.startsWith("/")) return {};
|
|
149
|
+
|
|
150
|
+
const state = getXrayNavigator()?.getStackForPathname(pathname)?.state;
|
|
151
|
+
|
|
152
|
+
// A stack route ends in <screenType>/<screenId>.
|
|
153
|
+
const screenType = pathname.split("/").slice(-2)[0];
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
screenType,
|
|
157
|
+
...state,
|
|
158
|
+
// A hook screen's entry is the { payload } wrapper it was pushed with.
|
|
159
|
+
entry:
|
|
160
|
+
screenType === "hooks"
|
|
161
|
+
? ((state?.entry as any)?.payload ?? state?.entry)
|
|
162
|
+
: state?.entry,
|
|
163
|
+
};
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
/** Screen types ScreenResolver renders itself rather than through a screen plugin. */
|
|
167
|
+
const BUILT_IN_KINDS = {
|
|
168
|
+
playable: "player",
|
|
169
|
+
link: "link",
|
|
170
|
+
favorites: "favorites",
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* What renders the screen, as ScreenResolver picks it: a river of components, a built-in screen,
|
|
175
|
+
* a hook plugin, or a screen plugin (a river whose type is a plugin identifier, or a route whose
|
|
176
|
+
* screen type is one).
|
|
177
|
+
*/
|
|
178
|
+
const describeKind = ({
|
|
179
|
+
screenType,
|
|
180
|
+
screen,
|
|
181
|
+
}: RouteState): { kind: string; identifier?: string } => {
|
|
182
|
+
if (!screenType) return { kind: "none" };
|
|
183
|
+
|
|
184
|
+
if (screen?.modalBottomSheetContentProps) return { kind: "bottomSheet" };
|
|
185
|
+
|
|
186
|
+
if (screenType === "hooks") {
|
|
187
|
+
return { kind: "hook", identifier: screen?.identifier };
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (BUILT_IN_KINDS[screenType]) return { kind: BUILT_IN_KINDS[screenType] };
|
|
191
|
+
|
|
192
|
+
if (screenType !== "river") return { kind: "plugin", identifier: screenType };
|
|
193
|
+
|
|
194
|
+
return screen?.type === "general_content"
|
|
195
|
+
? { kind: "river" }
|
|
196
|
+
: { kind: "plugin", identifier: screen?.type };
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
const describePlugin = (identifier?: string) => {
|
|
200
|
+
if (!identifier) return null;
|
|
201
|
+
|
|
202
|
+
const plugins = (appStore.get("plugins") ?? []) as QuickBrickPlugin[];
|
|
203
|
+
|
|
204
|
+
return {
|
|
205
|
+
identifier,
|
|
206
|
+
name: plugins.find((plugin) => plugin.identifier === identifier)?.name,
|
|
207
|
+
};
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
const describeFeed = (feed?: ZappPipesData) => {
|
|
211
|
+
if (!feed?.url) return null;
|
|
212
|
+
|
|
213
|
+
const error = feed.error as { message?: string } | undefined;
|
|
214
|
+
|
|
215
|
+
return {
|
|
216
|
+
url: feed.url,
|
|
217
|
+
state: error ? "error" : feed.loading ? "loading" : "loaded",
|
|
218
|
+
...(error && { error: error.message ?? String(error) }),
|
|
219
|
+
count: feed.data?.entry?.length ?? 0,
|
|
220
|
+
};
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
const byIndex = (a: ComponentRecord, b: ComponentRecord) =>
|
|
224
|
+
(a.index ?? 0) - (b.index ?? 0);
|
|
225
|
+
|
|
226
|
+
/** Components in order, a group's children nested under it (they share the river and its indexes). */
|
|
227
|
+
const describeComponents = (
|
|
228
|
+
mounted: ComponentRecord[],
|
|
229
|
+
belongs: (record: ComponentRecord) => boolean,
|
|
230
|
+
limit: number
|
|
231
|
+
) =>
|
|
232
|
+
mounted
|
|
233
|
+
.filter(belongs)
|
|
234
|
+
.sort(byIndex)
|
|
235
|
+
.map((record) => {
|
|
236
|
+
const children = describeComponents(
|
|
237
|
+
mounted,
|
|
238
|
+
(child) => child.parentId === record.component.id,
|
|
239
|
+
limit
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
return {
|
|
243
|
+
index: record.index,
|
|
244
|
+
id: record.component.id,
|
|
245
|
+
type: record.component.component_type,
|
|
246
|
+
name: record.component.name,
|
|
247
|
+
feed: describeFeed(record.feed),
|
|
248
|
+
entries: (record.feed?.data?.entry ?? [])
|
|
249
|
+
.slice(0, limit)
|
|
250
|
+
.map((entry, index) => ({ index, id: entry.id, title: entry.title })),
|
|
251
|
+
...(children.length > 0 && { children }),
|
|
252
|
+
};
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
const describeRiverComponents = (mounted: ComponentRecord[], limit: number) => {
|
|
256
|
+
const ids = new Set(mounted.map((record) => record.component.id));
|
|
257
|
+
|
|
258
|
+
return describeComponents(
|
|
259
|
+
mounted,
|
|
260
|
+
(record) => !record.parentId || !ids.has(record.parentId),
|
|
261
|
+
limit
|
|
262
|
+
);
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
const describeScreen = (pathname: string, limit: number) => {
|
|
266
|
+
const state = routeState(pathname);
|
|
267
|
+
const { screen, entry, nested } = state;
|
|
268
|
+
const { kind, identifier } = describeKind(state);
|
|
269
|
+
const plugin = describePlugin(identifier);
|
|
270
|
+
|
|
271
|
+
const mounted = Array.from(records).filter(
|
|
272
|
+
(record) => record.pathname === pathname
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
const riverIds = R.uniq(mounted.map((record) => record.riverId));
|
|
276
|
+
const rivers = appStore.get("rivers") as Record<string, ZappRiver>;
|
|
277
|
+
|
|
278
|
+
return {
|
|
279
|
+
pathname,
|
|
280
|
+
screen: {
|
|
281
|
+
kind,
|
|
282
|
+
...(screen && { id: screen.id, name: screen.name, type: screen.type }),
|
|
283
|
+
...(plugin && { plugin }),
|
|
284
|
+
},
|
|
285
|
+
...(nested?.screen && {
|
|
286
|
+
nested: {
|
|
287
|
+
id: nested.screen.id,
|
|
288
|
+
name: nested.screen.name,
|
|
289
|
+
type: nested.screen.type,
|
|
290
|
+
},
|
|
291
|
+
}),
|
|
292
|
+
entry: entry ? { id: entry.id, title: entry.title } : null,
|
|
293
|
+
rivers: riverIds.map((riverId) => ({
|
|
294
|
+
id: riverId,
|
|
295
|
+
name: rivers?.[riverId]?.name,
|
|
296
|
+
type: rivers?.[riverId]?.type,
|
|
297
|
+
components: describeRiverComponents(
|
|
298
|
+
mounted.filter((record) => record.riverId === riverId),
|
|
299
|
+
limit
|
|
300
|
+
),
|
|
301
|
+
})),
|
|
302
|
+
};
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
const tools: XrayToolSpec[] = [
|
|
306
|
+
{
|
|
307
|
+
name: "current",
|
|
308
|
+
description:
|
|
309
|
+
"The screen on top (hooks modal, modal or bottom sheet, video modal unless docked, else stack top) " +
|
|
310
|
+
"or the one at `pathname`: what renders it (kind river, plugin, hook, player, link, favorites, " +
|
|
311
|
+
"bottomSheet, and the plugin), its river components in order (a group's children nested) with " +
|
|
312
|
+
"their feed (url, state, count) and first entries. " +
|
|
313
|
+
"Lists mount ahead of the viewport, so a component here may be off screen: use the native " +
|
|
314
|
+
"ui.inspect for what is visible (row testID component-<id>; cell testIDs contain the entry id).",
|
|
315
|
+
parameters: [
|
|
316
|
+
{
|
|
317
|
+
name: "pathname",
|
|
318
|
+
type: "string",
|
|
319
|
+
description:
|
|
320
|
+
"A mounted screen's route (see mounted); defaults to the top one.",
|
|
321
|
+
},
|
|
322
|
+
{
|
|
323
|
+
name: "limit",
|
|
324
|
+
type: "number",
|
|
325
|
+
description: `Entries per component, default ${DEFAULT_ENTRIES_LIMIT}.`,
|
|
326
|
+
},
|
|
327
|
+
],
|
|
328
|
+
handler: ({ pathname, limit }) => {
|
|
329
|
+
const target =
|
|
330
|
+
typeof pathname === "string" && pathname ? pathname : getTopPathname();
|
|
331
|
+
|
|
332
|
+
if (!target) throw new Error("Navigation is not available yet");
|
|
333
|
+
|
|
334
|
+
return {
|
|
335
|
+
...describeScreen(target, toLimit(limit)),
|
|
336
|
+
mounted: R.uniq(Array.from(records, (record) => record.pathname)),
|
|
337
|
+
};
|
|
338
|
+
},
|
|
339
|
+
},
|
|
340
|
+
];
|
|
341
|
+
|
|
342
|
+
const registerScreenToolbox = () =>
|
|
343
|
+
registerXrayToolbox({
|
|
344
|
+
name: "screen",
|
|
345
|
+
description:
|
|
346
|
+
"What the current screen shows: what renders it, river components, their feeds and entries.",
|
|
347
|
+
tools,
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Keeps a record of a mounted river component for the toolbox. Returns the record so the caller
|
|
352
|
+
* can attach the feed data it renders with.
|
|
353
|
+
*/
|
|
354
|
+
export const useScreenToolboxRecord = (
|
|
355
|
+
component: ZappUIComponent,
|
|
356
|
+
riverId: string,
|
|
357
|
+
index: number | undefined,
|
|
358
|
+
parentId: string | undefined,
|
|
359
|
+
pathname: string
|
|
360
|
+
): ComponentRecord => {
|
|
361
|
+
const record = useMemo(
|
|
362
|
+
() => ({ component, riverId, index, parentId, pathname }),
|
|
363
|
+
[component, riverId, index, parentId, pathname]
|
|
364
|
+
);
|
|
365
|
+
|
|
366
|
+
useEffect(() => {
|
|
367
|
+
if (unavailable) return;
|
|
368
|
+
|
|
369
|
+
registerScreenToolbox().then((registered) => {
|
|
370
|
+
if (!registered) {
|
|
371
|
+
unavailable = true;
|
|
372
|
+
records.clear();
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
records.add(record);
|
|
377
|
+
|
|
378
|
+
return () => {
|
|
379
|
+
records.delete(record);
|
|
380
|
+
};
|
|
381
|
+
}, [record]);
|
|
382
|
+
|
|
383
|
+
return record;
|
|
384
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/zapp-react-native-ui-components",
|
|
3
|
-
"version": "16.0.0-alpha.
|
|
3
|
+
"version": "16.0.0-alpha.3202041045",
|
|
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-alpha.
|
|
32
|
-
"@applicaster/zapp-react-native-bridge": "16.0.0-alpha.
|
|
33
|
-
"@applicaster/zapp-react-native-redux": "16.0.0-alpha.
|
|
34
|
-
"@applicaster/zapp-react-native-utils": "16.0.0-alpha.
|
|
31
|
+
"@applicaster/applicaster-types": "16.0.0-alpha.3202041045",
|
|
32
|
+
"@applicaster/zapp-react-native-bridge": "16.0.0-alpha.3202041045",
|
|
33
|
+
"@applicaster/zapp-react-native-redux": "16.0.0-alpha.3202041045",
|
|
34
|
+
"@applicaster/zapp-react-native-utils": "16.0.0-alpha.3202041045",
|
|
35
35
|
"fast-json-stable-stringify": "^2.1.0",
|
|
36
36
|
"promise": "^8.3.0",
|
|
37
37
|
"react-native-sortables": "1.7.1",
|