@applicaster/zapp-react-native-ui-components 12.0.27-alpha.7708038842 → 12.0.27-rc.1
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/Cell/Cell.tsx +15 -11
- package/Components/Focusable/Focusable.tsx +2 -1
- package/Components/River/ComponentsMap/ComponentsMap.tsx +65 -39
- package/Components/River/ComponentsMap/hooks/useLoadingState.ts +49 -92
- package/Components/River/RiverFooter.tsx +9 -39
- package/Components/River/RiverItem.tsx +3 -42
- package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +31 -100
- package/Components/River/__tests__/componentsMap.test.js +4 -3
- package/Components/TextInputTv/index.tsx +11 -0
- package/package.json +5 -5
package/Components/Cell/Cell.tsx
CHANGED
|
@@ -14,10 +14,7 @@ import { BaseFocusable } from "../BaseFocusable";
|
|
|
14
14
|
import { AccessibilityManager } from "@applicaster/zapp-react-native-utils/appUtils/accessibilityManager";
|
|
15
15
|
|
|
16
16
|
type Props = {
|
|
17
|
-
item:
|
|
18
|
-
id: string;
|
|
19
|
-
title: string;
|
|
20
|
-
};
|
|
17
|
+
item: ZappEntry;
|
|
21
18
|
index: number;
|
|
22
19
|
shouldScrollHorizontally: (arg1: [any]) => boolean | null | undefined;
|
|
23
20
|
shouldScrollVertically: (...args: any) => boolean | null | undefined;
|
|
@@ -83,7 +80,9 @@ const touchableStyles = {
|
|
|
83
80
|
} as const;
|
|
84
81
|
|
|
85
82
|
export class CellComponent extends React.Component<Props, State> {
|
|
86
|
-
|
|
83
|
+
accessibilityManager: AccessibilityManager;
|
|
84
|
+
|
|
85
|
+
constructor(props: Props) {
|
|
87
86
|
super(props);
|
|
88
87
|
this.onPress = this.onPress.bind(this);
|
|
89
88
|
this.onFocus = this.onFocus.bind(this);
|
|
@@ -94,11 +93,15 @@ export class CellComponent extends React.Component<Props, State> {
|
|
|
94
93
|
this.scrollToIndex = this.scrollToIndex.bind(this);
|
|
95
94
|
|
|
96
95
|
this.state = {
|
|
97
|
-
hasFocusableInside: props.CellRenderer.hasFocusableInside?.(
|
|
96
|
+
hasFocusableInside: props.CellRenderer.hasFocusableInside?.(
|
|
97
|
+
props.item as Record<string, any>
|
|
98
|
+
),
|
|
98
99
|
};
|
|
100
|
+
|
|
101
|
+
this.accessibilityManager = AccessibilityManager.getInstance();
|
|
99
102
|
}
|
|
100
103
|
|
|
101
|
-
setScreenLayout(componentAnchorPointY, screenLayout) {
|
|
104
|
+
setScreenLayout(componentAnchorPointY: number, screenLayout: any) {
|
|
102
105
|
if (componentAnchorPointY && screenLayout) {
|
|
103
106
|
const updateLayout =
|
|
104
107
|
componentAnchorPointY !== screenLayout?.componentAnchorPointY;
|
|
@@ -200,7 +203,7 @@ export class CellComponent extends React.Component<Props, State> {
|
|
|
200
203
|
if (prevProps.item !== this.props.item) {
|
|
201
204
|
this.setState({
|
|
202
205
|
hasFocusableInside: this.props.CellRenderer.hasFocusableInside?.(
|
|
203
|
-
this.props.item
|
|
206
|
+
this.props.item as Record<string, any>
|
|
204
207
|
),
|
|
205
208
|
});
|
|
206
209
|
}
|
|
@@ -270,20 +273,21 @@ export class CellComponent extends React.Component<Props, State> {
|
|
|
270
273
|
style={baseCellStyles}
|
|
271
274
|
isFocusable={isFocusable}
|
|
272
275
|
skipFocusManagerRegistration={skipFocusManagerRegistration}
|
|
276
|
+
{...this.accessibilityManager.getButtonAccessibilityProps(
|
|
277
|
+
item?.extensions?.accessibility?.label || item?.title
|
|
278
|
+
)}
|
|
273
279
|
>
|
|
274
280
|
{(focused, event) => {
|
|
275
281
|
const isFocused = this.isCellFocused(focused);
|
|
276
282
|
|
|
277
283
|
if (isFocused) {
|
|
278
|
-
const accessibilityManager = AccessibilityManager.getInstance();
|
|
279
|
-
|
|
280
284
|
const accessibilityTitle =
|
|
281
285
|
item?.extensions?.accessibility?.label || item?.title || "";
|
|
282
286
|
|
|
283
287
|
const accessibilityHint =
|
|
284
288
|
item?.extensions?.accessibility?.hint || "";
|
|
285
289
|
|
|
286
|
-
accessibilityManager.readText({
|
|
290
|
+
this.accessibilityManager.readText({
|
|
287
291
|
text: `${accessibilityTitle} ${accessibilityHint}`,
|
|
288
292
|
});
|
|
289
293
|
}
|
|
@@ -121,7 +121,7 @@ class Focusable extends BaseFocusable<Props> {
|
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
render() {
|
|
124
|
-
const { children, style } = this.props;
|
|
124
|
+
const { children, style, ...otherProps } = this.props;
|
|
125
125
|
const { focused } = this.state;
|
|
126
126
|
|
|
127
127
|
const id = this.getId();
|
|
@@ -139,6 +139,7 @@ class Focusable extends BaseFocusable<Props> {
|
|
|
139
139
|
data-testid={focusableId}
|
|
140
140
|
focused-teststate={focused ? "focused" : "default"}
|
|
141
141
|
style={style}
|
|
142
|
+
{...otherProps}
|
|
142
143
|
>
|
|
143
144
|
{children(focused, { mouse: this.mouse })}
|
|
144
145
|
</div>
|
|
@@ -75,6 +75,21 @@ function ComponentsMapComponent(props: Props) {
|
|
|
75
75
|
|
|
76
76
|
const [flatListHeight, setFlatListHeight] = React.useState(null);
|
|
77
77
|
|
|
78
|
+
const {
|
|
79
|
+
isAnyLoading,
|
|
80
|
+
isAllLoaded,
|
|
81
|
+
waitForAllComponents,
|
|
82
|
+
onLoadFinished,
|
|
83
|
+
onLoadFailed,
|
|
84
|
+
shouldShowLoadingError,
|
|
85
|
+
isAnyLoaded,
|
|
86
|
+
arePreviousComponentsLoaded,
|
|
87
|
+
} = useLoadingState(feed?.entry?.length || components.length);
|
|
88
|
+
|
|
89
|
+
const theme = useTheme();
|
|
90
|
+
|
|
91
|
+
const logTimestamp = useProfilerLogging();
|
|
92
|
+
|
|
78
93
|
const riverComponents = React.useMemo(() => {
|
|
79
94
|
if (feed?.entry?.length < components.length) {
|
|
80
95
|
return R.slice(0, feed?.entry?.length, components);
|
|
@@ -83,42 +98,55 @@ function ComponentsMapComponent(props: Props) {
|
|
|
83
98
|
return components;
|
|
84
99
|
}, [components, feed]);
|
|
85
100
|
|
|
86
|
-
const logTimestamp = useProfilerLogging();
|
|
87
|
-
|
|
88
|
-
const onLoadDone = React.useCallback(() => {
|
|
89
|
-
logTimestamp(riverId?.toString());
|
|
90
|
-
}, [logTimestamp]);
|
|
91
|
-
|
|
92
|
-
const { loadingState, onLoadFinished, onLoadFailed, shouldShowLoadingError } =
|
|
93
|
-
useLoadingState(riverComponents.length, onLoadDone);
|
|
94
|
-
|
|
95
|
-
const theme = useTheme();
|
|
96
|
-
|
|
97
101
|
const renderRiverItem = React.useCallback(
|
|
98
102
|
({ item, index }) => {
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
isScreenWrappedInContainer,
|
|
104
|
-
feed,
|
|
105
|
-
groupId,
|
|
106
|
-
onLoadFailed,
|
|
107
|
-
onLoadFinished,
|
|
108
|
-
getStaticComponentFeed,
|
|
109
|
-
isLast: isLast(index, riverComponents.length),
|
|
110
|
-
loadingState,
|
|
103
|
+
const readyToBeDisplayed = arePreviousComponentsLoaded(index);
|
|
104
|
+
|
|
105
|
+
const styles = {
|
|
106
|
+
display: !readyToBeDisplayed ? "none" : ("flex" as "none" | "flex"),
|
|
111
107
|
};
|
|
112
108
|
|
|
113
109
|
return (
|
|
114
|
-
<
|
|
115
|
-
<
|
|
116
|
-
|
|
110
|
+
<View style={styles}>
|
|
111
|
+
<ScreenLoadingMeasurementsListItemWrapper index={index}>
|
|
112
|
+
<RiverItem
|
|
113
|
+
{...{
|
|
114
|
+
readyToBeDisplayed,
|
|
115
|
+
riverId,
|
|
116
|
+
item,
|
|
117
|
+
index,
|
|
118
|
+
isScreenWrappedInContainer,
|
|
119
|
+
feed,
|
|
120
|
+
groupId,
|
|
121
|
+
onLoadFailed,
|
|
122
|
+
onLoadFinished,
|
|
123
|
+
getStaticComponentFeed,
|
|
124
|
+
isLast: isLast(index, riverComponents.length),
|
|
125
|
+
}}
|
|
126
|
+
/>
|
|
127
|
+
</ScreenLoadingMeasurementsListItemWrapper>
|
|
128
|
+
</View>
|
|
117
129
|
);
|
|
118
130
|
},
|
|
119
|
-
[
|
|
131
|
+
[
|
|
132
|
+
feed,
|
|
133
|
+
getStaticComponentFeed,
|
|
134
|
+
arePreviousComponentsLoaded,
|
|
135
|
+
onLoadFailed,
|
|
136
|
+
onLoadFinished,
|
|
137
|
+
]
|
|
120
138
|
);
|
|
121
139
|
|
|
140
|
+
const renderFooter = React.useCallback(() => {
|
|
141
|
+
return (
|
|
142
|
+
<RiverFooter
|
|
143
|
+
visible={isAnyLoading}
|
|
144
|
+
flatListHeight={flatListHeight}
|
|
145
|
+
isAnyLoaded={isAnyLoaded || waitForAllComponents}
|
|
146
|
+
/>
|
|
147
|
+
);
|
|
148
|
+
}, [flatListHeight, isAnyLoading, isAnyLoaded]);
|
|
149
|
+
|
|
122
150
|
const screenStyle = React.useMemo(
|
|
123
151
|
() => ({
|
|
124
152
|
paddingTop: ifEmptyUseFallback(
|
|
@@ -156,6 +184,12 @@ function ComponentsMapComponent(props: Props) {
|
|
|
156
184
|
|
|
157
185
|
usePipesCacheReset(riverId, riverComponents);
|
|
158
186
|
|
|
187
|
+
React.useEffect(() => {
|
|
188
|
+
if (isAllLoaded) {
|
|
189
|
+
logTimestamp(riverId?.toString());
|
|
190
|
+
}
|
|
191
|
+
}, [isAllLoaded]);
|
|
192
|
+
|
|
159
193
|
const refreshControl = React.useMemo(
|
|
160
194
|
() =>
|
|
161
195
|
pullToRefreshEnabled ? (
|
|
@@ -251,11 +285,6 @@ function ComponentsMapComponent(props: Props) {
|
|
|
251
285
|
}
|
|
252
286
|
}, []);
|
|
253
287
|
|
|
254
|
-
const contentContainerStyle = React.useMemo(
|
|
255
|
-
() => (isScreenWrappedInContainer ? {} : screenStyle),
|
|
256
|
-
[isScreenWrappedInContainer, screenStyle]
|
|
257
|
-
);
|
|
258
|
-
|
|
259
288
|
if (shouldShowLoadingError) {
|
|
260
289
|
return <RiverError />;
|
|
261
290
|
}
|
|
@@ -267,7 +296,7 @@ function ComponentsMapComponent(props: Props) {
|
|
|
267
296
|
<View style={styles.container}>
|
|
268
297
|
<ScreenLoadingMeasurements
|
|
269
298
|
riverId={riverId}
|
|
270
|
-
numberOfComponents={
|
|
299
|
+
numberOfComponents={feed?.entry?.length || components.length}
|
|
271
300
|
>
|
|
272
301
|
<ViewportTracker>
|
|
273
302
|
<FlatList
|
|
@@ -289,13 +318,10 @@ function ComponentsMapComponent(props: Props) {
|
|
|
289
318
|
keyExtractor={keyExtractor}
|
|
290
319
|
renderItem={renderRiverItem}
|
|
291
320
|
data={riverComponents}
|
|
292
|
-
contentContainerStyle={
|
|
293
|
-
|
|
294
|
-
<RiverFooter
|
|
295
|
-
flatListHeight={flatListHeight}
|
|
296
|
-
loadingState={loadingState}
|
|
297
|
-
/>
|
|
321
|
+
contentContainerStyle={
|
|
322
|
+
isScreenWrappedInContainer ? {} : screenStyle
|
|
298
323
|
}
|
|
324
|
+
ListFooterComponent={renderFooter}
|
|
299
325
|
refreshControl={refreshControl}
|
|
300
326
|
onScrollBeginDrag={onScrollBeginDrag}
|
|
301
327
|
onScroll={onScroll}
|
|
@@ -1,99 +1,47 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import * as R from "ramda";
|
|
3
|
+
|
|
4
|
+
const allTrue = R.all(R.equals(true));
|
|
5
|
+
const anyFalse = R.any(R.equals(false));
|
|
6
|
+
const anyTrue = R.any(R.equals(true));
|
|
5
7
|
|
|
6
8
|
const reducer = (state, { payload }) => {
|
|
7
|
-
if (!isNil(payload) && !state[payload]) {
|
|
8
|
-
return set(lensIndex(payload), true)(state);
|
|
9
|
+
if (!R.isNil(payload) && !state[payload]) {
|
|
10
|
+
return R.set(R.lensIndex(payload), true)(state);
|
|
9
11
|
}
|
|
10
12
|
|
|
11
13
|
return state;
|
|
12
14
|
};
|
|
13
15
|
|
|
14
|
-
type LoadingState = {
|
|
15
|
-
index: number;
|
|
16
|
-
done: boolean;
|
|
17
|
-
waitForAllComponents: boolean;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
16
|
type Return = {
|
|
21
|
-
|
|
17
|
+
isAnyLoading: boolean;
|
|
18
|
+
isAllLoaded: boolean;
|
|
19
|
+
waitForAllComponents: boolean;
|
|
22
20
|
onLoadFinished: (index: number) => void;
|
|
23
21
|
onLoadFailed: ({ error, index }: { error: Error; index: number }) => void;
|
|
24
22
|
shouldShowLoadingError: boolean;
|
|
23
|
+
isAnyLoaded: boolean;
|
|
25
24
|
arePreviousComponentsLoaded: (index: number) => boolean;
|
|
26
25
|
};
|
|
27
26
|
|
|
28
|
-
|
|
29
|
-
const SHOULD_FAIL_ON_COMPONENT_LOADING = false;
|
|
30
|
-
|
|
31
|
-
const createLoadingStateObservable = () =>
|
|
32
|
-
new BehaviorSubject<LoadingState>({
|
|
33
|
-
index: -1,
|
|
34
|
-
done: false,
|
|
35
|
-
waitForAllComponents: SHOULD_FAIL_ON_COMPONENT_LOADING,
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
export const useLoadingState = (
|
|
39
|
-
count: number,
|
|
40
|
-
onLoadDone: () => void
|
|
41
|
-
): Return => {
|
|
42
|
-
const componentStateRef = React.useRef(new Array(count).fill(false));
|
|
43
|
-
const [loadingError, setLoadingError] = React.useState(null);
|
|
44
|
-
|
|
45
|
-
const loadingState = useRefWithInitialValue<BehaviorSubject<LoadingState>>(
|
|
46
|
-
createLoadingStateObservable
|
|
47
|
-
);
|
|
48
|
-
|
|
49
|
-
const arePreviousComponentsLoaded = React.useCallback((index) => {
|
|
50
|
-
if (index === 0) {
|
|
51
|
-
return true;
|
|
52
|
-
}
|
|
27
|
+
type Action = { payload: { index: number } };
|
|
53
28
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
return componentsBefore.every(T);
|
|
57
|
-
}, []);
|
|
29
|
+
type Loaded = true;
|
|
30
|
+
type Loading = false;
|
|
58
31
|
|
|
59
|
-
|
|
60
|
-
const newState = reducer(componentStateRef.current, { payload });
|
|
61
|
-
componentStateRef.current = newState;
|
|
62
|
-
const isDone = arePreviousComponentsLoaded(count - 1);
|
|
32
|
+
type LoadingState = Array<Loaded | Loading>;
|
|
63
33
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
// Fix: Only advance index sequentially, don't jump to highest loaded component
|
|
67
|
-
let newIndex = state.index;
|
|
68
|
-
|
|
69
|
-
if (payload === state.index + 1) {
|
|
70
|
-
// Only advance if this is the next sequential component
|
|
71
|
-
newIndex = payload;
|
|
72
|
-
} else if (payload < state.index) {
|
|
73
|
-
// If a lower index loads after higher ones, don't change the index
|
|
74
|
-
// This prevents the race condition that causes unmounting
|
|
75
|
-
newIndex = state.index;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const newLoadingState = {
|
|
79
|
-
...state,
|
|
80
|
-
index: newIndex,
|
|
81
|
-
done: isDone,
|
|
82
|
-
};
|
|
34
|
+
// TODO: Take this value from Zapp configuration, when feature is added to GeneralScreen
|
|
35
|
+
const SHOULD_FAIL_ON_COMPONENT_LOADING = false;
|
|
83
36
|
|
|
84
|
-
|
|
37
|
+
export const useLoadingState = (count: number): Return => {
|
|
38
|
+
const [loadingError, setLoadingError] = React.useState(null);
|
|
85
39
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}, []);
|
|
40
|
+
const [componentsState, dispatch] = React.useReducer<
|
|
41
|
+
React.Reducer<LoadingState, Action>
|
|
42
|
+
>(reducer, new Array(count).fill(false));
|
|
90
43
|
|
|
91
44
|
const handleComponentLoaded = React.useCallback((index) => {
|
|
92
|
-
// Prevent duplicate loading events - if component is already loaded, ignore
|
|
93
|
-
if (componentStateRef.current[index]) {
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
45
|
dispatch({ payload: index });
|
|
98
46
|
}, []);
|
|
99
47
|
|
|
@@ -107,26 +55,35 @@ export const useLoadingState = (
|
|
|
107
55
|
);
|
|
108
56
|
|
|
109
57
|
const handleComponentLoadErrorWhenNoNeedToFail = React.useCallback(
|
|
110
|
-
({ index }) =>
|
|
58
|
+
({ index }) => {
|
|
59
|
+
handleComponentLoaded(index);
|
|
60
|
+
},
|
|
111
61
|
[]
|
|
112
62
|
);
|
|
113
63
|
|
|
114
|
-
|
|
115
|
-
() =>
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
}
|
|
124
|
-
[
|
|
125
|
-
loadingError,
|
|
126
|
-
handleComponentLoaded,
|
|
127
|
-
handleComponentLoadErrorWhenNeedToFail,
|
|
128
|
-
handleComponentLoadErrorWhenNoNeedToFail,
|
|
129
|
-
arePreviousComponentsLoaded,
|
|
130
|
-
]
|
|
64
|
+
const arePreviousComponentsLoaded = React.useCallback(
|
|
65
|
+
(index) => {
|
|
66
|
+
if (index === 0) {
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const componentsBefore = R.slice(0, index, componentsState);
|
|
71
|
+
|
|
72
|
+
return allTrue(componentsBefore);
|
|
73
|
+
},
|
|
74
|
+
[componentsState]
|
|
131
75
|
);
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
isAnyLoading: anyFalse(componentsState),
|
|
79
|
+
isAllLoaded: allTrue(componentsState),
|
|
80
|
+
onLoadFinished: handleComponentLoaded,
|
|
81
|
+
onLoadFailed: SHOULD_FAIL_ON_COMPONENT_LOADING
|
|
82
|
+
? handleComponentLoadErrorWhenNeedToFail
|
|
83
|
+
: handleComponentLoadErrorWhenNoNeedToFail,
|
|
84
|
+
shouldShowLoadingError: SHOULD_FAIL_ON_COMPONENT_LOADING && loadingError,
|
|
85
|
+
isAnyLoaded: anyTrue(componentsState),
|
|
86
|
+
waitForAllComponents: SHOULD_FAIL_ON_COMPONENT_LOADING,
|
|
87
|
+
arePreviousComponentsLoaded,
|
|
88
|
+
};
|
|
132
89
|
};
|
|
@@ -1,23 +1,11 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import React from "react";
|
|
2
2
|
import { StyleSheet, View } from "react-native";
|
|
3
3
|
import { Spinner } from "@applicaster/zapp-react-native-ui-components/Components/Spinner";
|
|
4
|
-
import type { Subject } from "rxjs";
|
|
5
|
-
import { useStateFromSubscribe } from "@applicaster/zapp-react-native-utils/reactHooks/state/useStateFromSubscribe";
|
|
6
|
-
|
|
7
|
-
type LoadingState = {
|
|
8
|
-
waitForAllComponents: boolean;
|
|
9
|
-
index: number;
|
|
10
|
-
done: boolean;
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
type State = {
|
|
14
|
-
visible: boolean;
|
|
15
|
-
isAnyLoaded: boolean;
|
|
16
|
-
};
|
|
17
4
|
|
|
18
5
|
type Props = {
|
|
6
|
+
visible?: boolean;
|
|
19
7
|
flatListHeight?: number;
|
|
20
|
-
|
|
8
|
+
isAnyLoaded: boolean;
|
|
21
9
|
};
|
|
22
10
|
|
|
23
11
|
const FOOTER_COMPONENT_HEIGHT = 200;
|
|
@@ -31,35 +19,17 @@ const footerStyles = StyleSheet.create({
|
|
|
31
19
|
});
|
|
32
20
|
|
|
33
21
|
function RiverFooterComponent(props: Props) {
|
|
34
|
-
const { flatListHeight,
|
|
35
|
-
|
|
36
|
-
const { visible, isAnyLoaded } = useStateFromSubscribe<LoadingState, State>(
|
|
37
|
-
loadingState,
|
|
38
|
-
useCallback(
|
|
39
|
-
({ index, done, waitForAllComponents }, setState) =>
|
|
40
|
-
setState(() => ({
|
|
41
|
-
visible: !done,
|
|
42
|
-
isAnyLoaded: index >= 0 || waitForAllComponents,
|
|
43
|
-
})),
|
|
44
|
-
[]
|
|
45
|
-
),
|
|
46
|
-
{
|
|
47
|
-
visible: true,
|
|
48
|
-
isAnyLoaded: false,
|
|
49
|
-
}
|
|
50
|
-
);
|
|
22
|
+
const { visible = true, flatListHeight, isAnyLoaded } = props;
|
|
51
23
|
|
|
52
24
|
if (!visible) return null;
|
|
53
25
|
|
|
54
26
|
return (
|
|
55
27
|
<View
|
|
56
|
-
renderToHardwareTextureAndroid
|
|
57
|
-
style={
|
|
58
|
-
footerStyles.container,
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
},
|
|
62
|
-
]}
|
|
28
|
+
renderToHardwareTextureAndroid={true}
|
|
29
|
+
style={{
|
|
30
|
+
...footerStyles.container,
|
|
31
|
+
height: isAnyLoaded ? FOOTER_COMPONENT_HEIGHT : flatListHeight,
|
|
32
|
+
}}
|
|
63
33
|
>
|
|
64
34
|
<Spinner size={isAnyLoaded ? "small" : "large"} />
|
|
65
35
|
</View>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import React from "react";
|
|
2
2
|
import * as R from "ramda";
|
|
3
3
|
|
|
4
4
|
import { applyDecorators } from "../../Decorators";
|
|
@@ -11,7 +11,6 @@ import {
|
|
|
11
11
|
import { riverLogger } from "./logger";
|
|
12
12
|
import { tvPluginsWithCellRenderer } from "../../const";
|
|
13
13
|
import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
14
|
-
import type { BehaviorSubject } from "rxjs";
|
|
15
14
|
|
|
16
15
|
export type RiverItemType = {
|
|
17
16
|
item: ZappUIComponent;
|
|
@@ -25,7 +24,6 @@ export type RiverItemType = {
|
|
|
25
24
|
getStaticComponentFeed: GeneralContentScreenProps["getStaticComponentFeed"];
|
|
26
25
|
readyToBeDisplayed?: boolean;
|
|
27
26
|
isLast: boolean;
|
|
28
|
-
loadingState: BehaviorSubject<{ index: number }>;
|
|
29
27
|
};
|
|
30
28
|
|
|
31
29
|
function getFeedUrl(feed: ZappFeed, index: number) {
|
|
@@ -38,37 +36,6 @@ function getFeedUrl(feed: ZappFeed, index: number) {
|
|
|
38
36
|
}
|
|
39
37
|
}
|
|
40
38
|
|
|
41
|
-
const isNextIndex = (index, readyIndex) => {
|
|
42
|
-
return readyIndex + 1 >= index;
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* useLoadingState for RiverItemComponent
|
|
47
|
-
* takes currentIndex and loadingState as arguments
|
|
48
|
-
**/
|
|
49
|
-
const useLoadingState = (
|
|
50
|
-
currentIndex: number,
|
|
51
|
-
loadingState: RiverItemType["loadingState"]
|
|
52
|
-
) => {
|
|
53
|
-
const [readyToBeDisplayed, setReadyToBeDisplayed] = React.useState(
|
|
54
|
-
isNextIndex(currentIndex, loadingState.getValue().index)
|
|
55
|
-
);
|
|
56
|
-
|
|
57
|
-
useEffect(() => {
|
|
58
|
-
const subscription = loadingState.subscribe(({ index }) => {
|
|
59
|
-
if (isNextIndex(currentIndex, index)) {
|
|
60
|
-
setReadyToBeDisplayed(true);
|
|
61
|
-
}
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
return () => {
|
|
65
|
-
subscription.unsubscribe();
|
|
66
|
-
};
|
|
67
|
-
}, [currentIndex]);
|
|
68
|
-
|
|
69
|
-
return readyToBeDisplayed;
|
|
70
|
-
};
|
|
71
|
-
|
|
72
39
|
function RiverItemComponent(props: RiverItemType) {
|
|
73
40
|
const {
|
|
74
41
|
item,
|
|
@@ -80,12 +47,10 @@ function RiverItemComponent(props: RiverItemType) {
|
|
|
80
47
|
onLoadFinished,
|
|
81
48
|
onLoadFailed,
|
|
82
49
|
getStaticComponentFeed,
|
|
50
|
+
readyToBeDisplayed,
|
|
83
51
|
isLast,
|
|
84
|
-
loadingState,
|
|
85
52
|
} = props;
|
|
86
53
|
|
|
87
|
-
const readyToBeDisplayed = useLoadingState(index, loadingState);
|
|
88
|
-
|
|
89
54
|
const feedUrl = getFeedUrl(feed, index);
|
|
90
55
|
|
|
91
56
|
const Component = useComponentResolver(
|
|
@@ -125,10 +90,6 @@ function RiverItemComponent(props: RiverItemType) {
|
|
|
125
90
|
}
|
|
126
91
|
}, []);
|
|
127
92
|
|
|
128
|
-
if (!readyToBeDisplayed) {
|
|
129
|
-
return null;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
93
|
if (Component === null || typeof Component === "undefined") {
|
|
133
94
|
riverLogger.warning({
|
|
134
95
|
message: `Component ${item.component_type} is null - skipping rendering`,
|
|
@@ -150,7 +111,7 @@ function RiverItemComponent(props: RiverItemType) {
|
|
|
150
111
|
component={item}
|
|
151
112
|
componentIndex={index}
|
|
152
113
|
onLoadFailed={onLoadFailed}
|
|
153
|
-
onLoadFinished={() => onLoadFinished(index)}
|
|
114
|
+
onLoadFinished={() => onLoadFinished(index)} // Keeping it here to don't break the plugins.
|
|
154
115
|
groupId={groupId}
|
|
155
116
|
feedUrl={feedUrl}
|
|
156
117
|
isLast={isLast}
|
|
@@ -17,90 +17,7 @@ exports[`componentsMap renders renders components map correctly 1`] = `
|
|
|
17
17
|
}
|
|
18
18
|
>
|
|
19
19
|
<RCTScrollView
|
|
20
|
-
ListFooterComponent={
|
|
21
|
-
<Memo(RiverFooterComponent)
|
|
22
|
-
flatListHeight={null}
|
|
23
|
-
loadingState={
|
|
24
|
-
BehaviorSubject {
|
|
25
|
-
"_value": Object {
|
|
26
|
-
"done": false,
|
|
27
|
-
"index": -1,
|
|
28
|
-
"waitForAllComponents": false,
|
|
29
|
-
},
|
|
30
|
-
"closed": false,
|
|
31
|
-
"currentObservers": null,
|
|
32
|
-
"hasError": false,
|
|
33
|
-
"isStopped": false,
|
|
34
|
-
"observers": Array [
|
|
35
|
-
SafeSubscriber {
|
|
36
|
-
"_finalizers": Array [
|
|
37
|
-
Subscription {
|
|
38
|
-
"_finalizers": null,
|
|
39
|
-
"_parentage": [Circular],
|
|
40
|
-
"closed": false,
|
|
41
|
-
"initialTeardown": [Function],
|
|
42
|
-
},
|
|
43
|
-
],
|
|
44
|
-
"_parentage": null,
|
|
45
|
-
"closed": false,
|
|
46
|
-
"destination": ConsumerObserver {
|
|
47
|
-
"partialObserver": Object {
|
|
48
|
-
"complete": undefined,
|
|
49
|
-
"error": undefined,
|
|
50
|
-
"next": [Function],
|
|
51
|
-
},
|
|
52
|
-
},
|
|
53
|
-
"initialTeardown": undefined,
|
|
54
|
-
"isStopped": false,
|
|
55
|
-
},
|
|
56
|
-
SafeSubscriber {
|
|
57
|
-
"_finalizers": Array [
|
|
58
|
-
Subscription {
|
|
59
|
-
"_finalizers": null,
|
|
60
|
-
"_parentage": [Circular],
|
|
61
|
-
"closed": false,
|
|
62
|
-
"initialTeardown": [Function],
|
|
63
|
-
},
|
|
64
|
-
],
|
|
65
|
-
"_parentage": null,
|
|
66
|
-
"closed": false,
|
|
67
|
-
"destination": ConsumerObserver {
|
|
68
|
-
"partialObserver": Object {
|
|
69
|
-
"complete": undefined,
|
|
70
|
-
"error": undefined,
|
|
71
|
-
"next": [Function],
|
|
72
|
-
},
|
|
73
|
-
},
|
|
74
|
-
"initialTeardown": undefined,
|
|
75
|
-
"isStopped": false,
|
|
76
|
-
},
|
|
77
|
-
SafeSubscriber {
|
|
78
|
-
"_finalizers": Array [
|
|
79
|
-
Subscription {
|
|
80
|
-
"_finalizers": null,
|
|
81
|
-
"_parentage": [Circular],
|
|
82
|
-
"closed": false,
|
|
83
|
-
"initialTeardown": [Function],
|
|
84
|
-
},
|
|
85
|
-
],
|
|
86
|
-
"_parentage": null,
|
|
87
|
-
"closed": false,
|
|
88
|
-
"destination": ConsumerObserver {
|
|
89
|
-
"partialObserver": Object {
|
|
90
|
-
"complete": undefined,
|
|
91
|
-
"error": undefined,
|
|
92
|
-
"next": [Function],
|
|
93
|
-
},
|
|
94
|
-
},
|
|
95
|
-
"initialTeardown": undefined,
|
|
96
|
-
"isStopped": false,
|
|
97
|
-
},
|
|
98
|
-
],
|
|
99
|
-
"thrownError": null,
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
/>
|
|
103
|
-
}
|
|
20
|
+
ListFooterComponent={[Function]}
|
|
104
21
|
contentContainerStyle={
|
|
105
22
|
Object {
|
|
106
23
|
"paddingBottom": undefined,
|
|
@@ -163,14 +80,22 @@ exports[`componentsMap renders renders components map correctly 1`] = `
|
|
|
163
80
|
style={null}
|
|
164
81
|
>
|
|
165
82
|
<View
|
|
166
|
-
onLayout={[Function]}
|
|
167
83
|
style={
|
|
168
84
|
Object {
|
|
169
|
-
"
|
|
85
|
+
"display": "flex",
|
|
170
86
|
}
|
|
171
87
|
}
|
|
172
88
|
>
|
|
173
|
-
<View
|
|
89
|
+
<View
|
|
90
|
+
onLayout={[Function]}
|
|
91
|
+
style={
|
|
92
|
+
Object {
|
|
93
|
+
"flex": 1,
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
>
|
|
97
|
+
<View />
|
|
98
|
+
</View>
|
|
174
99
|
</View>
|
|
175
100
|
</View>
|
|
176
101
|
<View
|
|
@@ -178,13 +103,23 @@ exports[`componentsMap renders renders components map correctly 1`] = `
|
|
|
178
103
|
style={null}
|
|
179
104
|
>
|
|
180
105
|
<View
|
|
181
|
-
onLayout={[Function]}
|
|
182
106
|
style={
|
|
183
107
|
Object {
|
|
184
|
-
"
|
|
108
|
+
"display": "none",
|
|
185
109
|
}
|
|
186
110
|
}
|
|
187
|
-
|
|
111
|
+
>
|
|
112
|
+
<View
|
|
113
|
+
onLayout={[Function]}
|
|
114
|
+
style={
|
|
115
|
+
Object {
|
|
116
|
+
"flex": 1,
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
>
|
|
120
|
+
<View />
|
|
121
|
+
</View>
|
|
122
|
+
</View>
|
|
188
123
|
</View>
|
|
189
124
|
<View
|
|
190
125
|
onLayout={[Function]}
|
|
@@ -192,16 +127,12 @@ exports[`componentsMap renders renders components map correctly 1`] = `
|
|
|
192
127
|
<View
|
|
193
128
|
renderToHardwareTextureAndroid={true}
|
|
194
129
|
style={
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
Object {
|
|
202
|
-
"height": null,
|
|
203
|
-
},
|
|
204
|
-
]
|
|
130
|
+
Object {
|
|
131
|
+
"alignItems": "center",
|
|
132
|
+
"height": null,
|
|
133
|
+
"justifyContent": "center",
|
|
134
|
+
"width": "100%",
|
|
135
|
+
}
|
|
205
136
|
}
|
|
206
137
|
>
|
|
207
138
|
<ActivityIndicator
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import
|
|
2
|
+
import TestRenderer from "react-test-renderer";
|
|
3
|
+
import { cleanup } from "@testing-library/react-native";
|
|
3
4
|
import { Provider } from "react-redux";
|
|
4
5
|
import configureStore from "redux-mock-store";
|
|
5
6
|
|
|
@@ -141,12 +142,12 @@ describe("componentsMap", () => {
|
|
|
141
142
|
.spyOn(themeUtils, "useTheme")
|
|
142
143
|
.mockImplementation(() => () => theme);
|
|
143
144
|
|
|
144
|
-
const
|
|
145
|
+
const wrapper = TestRenderer.create(
|
|
145
146
|
<Provider store={store}>
|
|
146
147
|
<ComponentsMap {...props} />
|
|
147
148
|
</Provider>
|
|
148
149
|
);
|
|
149
150
|
|
|
150
|
-
expect(toJSON()).toMatchSnapshot();
|
|
151
|
+
expect(wrapper.toJSON()).toMatchSnapshot();
|
|
151
152
|
});
|
|
152
153
|
});
|
|
@@ -4,6 +4,7 @@ import { Appearance, Platform, StyleSheet, TextInput } from "react-native";
|
|
|
4
4
|
import { isFunction } from "@applicaster/zapp-react-native-utils/functionUtils";
|
|
5
5
|
import { isWeb } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
6
6
|
import { useIsRTL } from "@applicaster/zapp-react-native-utils/localizationUtils";
|
|
7
|
+
import { useAccessibilityManager } from "@applicaster/zapp-react-native-utils/appUtils/accessibilityManager/hooks";
|
|
7
8
|
|
|
8
9
|
type Props = Partial<{
|
|
9
10
|
style: any;
|
|
@@ -42,6 +43,8 @@ function TextInputTV(props: Props, ref) {
|
|
|
42
43
|
const [colorScheme, setColorScheme] = useState(getInitialColorScheme());
|
|
43
44
|
const isRTL = useIsRTL();
|
|
44
45
|
|
|
46
|
+
const accessibilityManager = useAccessibilityManager({});
|
|
47
|
+
|
|
45
48
|
const onColorChange = useCallback(
|
|
46
49
|
({ colorScheme: color }) => {
|
|
47
50
|
if (color !== colorScheme) {
|
|
@@ -153,6 +156,13 @@ function TextInputTV(props: Props, ref) {
|
|
|
153
156
|
])
|
|
154
157
|
)(props);
|
|
155
158
|
|
|
159
|
+
const getAccessibilityProps = () => {
|
|
160
|
+
return {
|
|
161
|
+
accessibilityProps:
|
|
162
|
+
accessibilityManager.getInputAccessibilityProps("Search"),
|
|
163
|
+
};
|
|
164
|
+
};
|
|
165
|
+
|
|
156
166
|
const inputProps = {
|
|
157
167
|
...getProps(),
|
|
158
168
|
...getStyle(),
|
|
@@ -161,6 +171,7 @@ function TextInputTV(props: Props, ref) {
|
|
|
161
171
|
...getSecureTextEntry(),
|
|
162
172
|
...getOnEndEditing(),
|
|
163
173
|
...getOnPress(),
|
|
174
|
+
...getAccessibilityProps(),
|
|
164
175
|
};
|
|
165
176
|
|
|
166
177
|
if (
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/zapp-react-native-ui-components",
|
|
3
|
-
"version": "12.0.27-
|
|
3
|
+
"version": "12.0.27-rc.1",
|
|
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",
|
|
@@ -34,10 +34,10 @@
|
|
|
34
34
|
"redux-mock-store": "^1.5.3"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@applicaster/applicaster-types": "12.0.27-
|
|
38
|
-
"@applicaster/zapp-react-native-bridge": "12.0.27-
|
|
39
|
-
"@applicaster/zapp-react-native-redux": "12.0.27-
|
|
40
|
-
"@applicaster/zapp-react-native-utils": "12.0.27-
|
|
37
|
+
"@applicaster/applicaster-types": "12.0.27-rc.1",
|
|
38
|
+
"@applicaster/zapp-react-native-bridge": "12.0.27-rc.1",
|
|
39
|
+
"@applicaster/zapp-react-native-redux": "12.0.27-rc.1",
|
|
40
|
+
"@applicaster/zapp-react-native-utils": "12.0.27-rc.1",
|
|
41
41
|
"promise": "^8.3.0",
|
|
42
42
|
"react-router-native": "^5.1.2",
|
|
43
43
|
"url": "^0.11.0",
|