@applicaster/zapp-react-native-ui-components 12.0.0-alpha.5035843303 → 12.0.0-alpha.5786305091
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/CellWithFocusable.tsx +14 -26
- package/Components/Cell/TvOSCellComponent.tsx +9 -4
- package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +1 -4
- package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +0 -4
- package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.android.tsx +1 -4
- package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.tsx +2 -10
- package/Components/MasterCell/utils/__tests__/insertBetween.test.js +0 -9
- package/Components/MeasurmentsPortal/MeasurementsPortal.tsx +249 -78
- package/Components/VideoLive/LiveImageManager.ts +1 -45
- package/package.json +5 -5
|
@@ -3,8 +3,6 @@ import * as React from "react";
|
|
|
3
3
|
import { focusManager } from "@applicaster/zapp-react-native-utils/appUtils/focusManager";
|
|
4
4
|
import * as FOCUS_EVENTS from "@applicaster/zapp-react-native-utils/appUtils/focusManager/events";
|
|
5
5
|
import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
|
|
6
|
-
import { toBooleanWithDefaultFalse } from "@applicaster/zapp-react-native-utils/booleanUtils";
|
|
7
|
-
|
|
8
6
|
import { isAppleTV } from "../../Helpers/Platform";
|
|
9
7
|
import { useCellState } from "../MasterCell/utils";
|
|
10
8
|
|
|
@@ -37,19 +35,18 @@ const useCellFocusedState = (groupId: string, id: string) => {
|
|
|
37
35
|
};
|
|
38
36
|
|
|
39
37
|
type Props = {
|
|
40
|
-
item: ZappEntry
|
|
41
|
-
CellRenderer: React.ComponentType<{ item: Object
|
|
42
|
-
id: string
|
|
43
|
-
groupId: string
|
|
44
|
-
onFocus: Function
|
|
45
|
-
index: number
|
|
46
|
-
scrollTo: Function
|
|
47
|
-
preferredFocus?: boolean
|
|
48
|
-
skipFocusManagerRegistration?: boolean
|
|
49
|
-
isFocusable?: boolean
|
|
50
|
-
behavior: Behavior
|
|
51
|
-
selected?: boolean
|
|
52
|
-
focused?: boolean;
|
|
38
|
+
item: ZappEntry,
|
|
39
|
+
CellRenderer: React.ComponentType<{ item: Object, state: string }>,
|
|
40
|
+
id: string,
|
|
41
|
+
groupId: string,
|
|
42
|
+
onFocus: Function,
|
|
43
|
+
index: number,
|
|
44
|
+
scrollTo: Function,
|
|
45
|
+
preferredFocus?: boolean,
|
|
46
|
+
skipFocusManagerRegistration?: boolean,
|
|
47
|
+
isFocusable?: boolean,
|
|
48
|
+
behavior: Behavior,
|
|
49
|
+
selected?: boolean,
|
|
53
50
|
};
|
|
54
51
|
|
|
55
52
|
export function CellWithFocusable(props: Props) {
|
|
@@ -65,19 +62,11 @@ export function CellWithFocusable(props: Props) {
|
|
|
65
62
|
skipFocusManagerRegistration,
|
|
66
63
|
isFocusable,
|
|
67
64
|
behavior,
|
|
68
|
-
selected
|
|
69
|
-
focused,
|
|
65
|
+
selected
|
|
70
66
|
} = props;
|
|
71
67
|
|
|
72
68
|
const isFocused = useCellFocusedState(groupId, id);
|
|
73
|
-
|
|
74
|
-
const state = useCellState({
|
|
75
|
-
id: item.id,
|
|
76
|
-
behavior,
|
|
77
|
-
focused: isFocused || toBooleanWithDefaultFalse(focused),
|
|
78
|
-
selected,
|
|
79
|
-
});
|
|
80
|
-
|
|
69
|
+
const state = useCellState({ id: item.id, behavior, focused: isFocused, selected });
|
|
81
70
|
const [focusedButtonId, setFocusedButtonId] = React.useState(undefined);
|
|
82
71
|
|
|
83
72
|
// for horizontal scrolling
|
|
@@ -106,7 +95,6 @@ export function CellWithFocusable(props: Props) {
|
|
|
106
95
|
preferredFocus={preferredFocus}
|
|
107
96
|
skipFocusManagerRegistration={skipFocusManagerRegistration}
|
|
108
97
|
isFocusable={isFocusable}
|
|
109
|
-
focused={focused}
|
|
110
98
|
/>
|
|
111
99
|
);
|
|
112
100
|
}
|
|
@@ -226,7 +226,13 @@ export class TvOSCellComponent extends React.Component<Props, State> {
|
|
|
226
226
|
|
|
227
227
|
hasReceivedFocus() {}
|
|
228
228
|
|
|
229
|
-
scrollTo(index) {
|
|
229
|
+
scrollTo(index) {
|
|
230
|
+
// Workaround for HL autoscroll/autoloop feature, we scroll manually on tvOS.
|
|
231
|
+
// Should be removed after refactoring autoscroll/autoloop feature
|
|
232
|
+
if (this.props.component?.component_type !== "horizontal_list_qb") {
|
|
233
|
+
this.props?.scrollToIndex?.(index);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
230
236
|
|
|
231
237
|
render() {
|
|
232
238
|
const {
|
|
@@ -243,7 +249,7 @@ export class TvOSCellComponent extends React.Component<Props, State> {
|
|
|
243
249
|
hasReceivedFocus,
|
|
244
250
|
groupId,
|
|
245
251
|
isFocusable,
|
|
246
|
-
behavior
|
|
252
|
+
behavior
|
|
247
253
|
} = this.props;
|
|
248
254
|
|
|
249
255
|
const { id } = item;
|
|
@@ -273,7 +279,6 @@ export class TvOSCellComponent extends React.Component<Props, State> {
|
|
|
273
279
|
index={index}
|
|
274
280
|
scrollTo={this.scrollTo}
|
|
275
281
|
preferredFocus={preferredFocus}
|
|
276
|
-
focused={this.props.focused}
|
|
277
282
|
behavior={behavior}
|
|
278
283
|
/>
|
|
279
284
|
</View>
|
|
@@ -305,7 +310,7 @@ export class TvOSCellComponent extends React.Component<Props, State> {
|
|
|
305
310
|
item,
|
|
306
311
|
focused: this.props.focused || selectedCell || focused,
|
|
307
312
|
scrollTo: this.scrollTo,
|
|
308
|
-
behavior
|
|
313
|
+
behavior
|
|
309
314
|
}}
|
|
310
315
|
/>
|
|
311
316
|
);
|
|
@@ -4,7 +4,6 @@ import { Focusable } from "@applicaster/zapp-react-native-ui-components/Componen
|
|
|
4
4
|
import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
|
|
5
5
|
import * as R from "ramda";
|
|
6
6
|
import { getXray } from "@applicaster/zapp-react-native-utils/logger";
|
|
7
|
-
import { toBooleanWithDefaultFalse } from "@applicaster/zapp-react-native-utils/booleanUtils";
|
|
8
7
|
|
|
9
8
|
const { Logger } = getXray();
|
|
10
9
|
|
|
@@ -36,9 +35,7 @@ export function FocusableView({ style, children, item, ...otherProps }: Props) {
|
|
|
36
35
|
|
|
37
36
|
const focusableId = getFocusableId(prefixId, suffixId);
|
|
38
37
|
|
|
39
|
-
const focused =
|
|
40
|
-
focusableId === focusedButtonId ||
|
|
41
|
-
toBooleanWithDefaultFalse(otherProps.focused);
|
|
38
|
+
const focused = focusableId === focusedButtonId;
|
|
42
39
|
|
|
43
40
|
const additionalStyles = focused ? focusedStyles : normalStyles;
|
|
44
41
|
|
|
@@ -31,10 +31,6 @@ function Image({
|
|
|
31
31
|
|
|
32
32
|
const source = useImageSource({ uri, entry, otherProps });
|
|
33
33
|
|
|
34
|
-
// const source = {
|
|
35
|
-
// uri: `https://placehold.co/392x221/blue/FFFFFF.png?text=${otherProps?.item?.index}`,
|
|
36
|
-
// };
|
|
37
|
-
|
|
38
34
|
React.useEffect(() => {
|
|
39
35
|
// reset error state on URI change as the error is referencing previous uri
|
|
40
36
|
setErrorState(null);
|
|
@@ -61,10 +61,7 @@ export function ButtonContainerView({
|
|
|
61
61
|
<View style={applyRTLStylesIfNeeded(style, isRTL)}>
|
|
62
62
|
{insertBetween(
|
|
63
63
|
(index) => (
|
|
64
|
-
<HorizontalSeparator
|
|
65
|
-
key={`separator_${index}`}
|
|
66
|
-
width={horizontalGutter}
|
|
67
|
-
/>
|
|
64
|
+
<HorizontalSeparator key={index} width={horizontalGutter} />
|
|
68
65
|
),
|
|
69
66
|
cloneElementsWithIds(buttonIds, filteredChildren)
|
|
70
67
|
)}
|
|
@@ -7,10 +7,6 @@ import type {
|
|
|
7
7
|
ContainerProps,
|
|
8
8
|
ContainerChildren,
|
|
9
9
|
} from "./types";
|
|
10
|
-
import {
|
|
11
|
-
useIsRTL,
|
|
12
|
-
applyRTLStylesIfNeeded,
|
|
13
|
-
} from "@applicaster/zapp-react-native-utils/localizationUtils";
|
|
14
10
|
|
|
15
11
|
const HorizontalSeparator = ({ width }: HorizontalSeparatorProps) => (
|
|
16
12
|
<View style={{ width }} />
|
|
@@ -21,7 +17,6 @@ export function ButtonContainerView({
|
|
|
21
17
|
children,
|
|
22
18
|
...otherProps
|
|
23
19
|
}: ContainerProps) {
|
|
24
|
-
const isRTL = useIsRTL();
|
|
25
20
|
const horizontalGutter = R.pathOr(0, ["horizontalGutter"], otherProps);
|
|
26
21
|
|
|
27
22
|
const filteredChildren = useFilterChildren<ContainerChildren>(children);
|
|
@@ -31,13 +26,10 @@ export function ButtonContainerView({
|
|
|
31
26
|
}
|
|
32
27
|
|
|
33
28
|
return (
|
|
34
|
-
<View style={
|
|
29
|
+
<View style={style}>
|
|
35
30
|
{insertBetween(
|
|
36
31
|
(index) => (
|
|
37
|
-
<HorizontalSeparator
|
|
38
|
-
key={`separator_${index}`}
|
|
39
|
-
width={horizontalGutter}
|
|
40
|
-
/>
|
|
32
|
+
<HorizontalSeparator key={index} width={horizontalGutter} />
|
|
41
33
|
),
|
|
42
34
|
filteredChildren
|
|
43
35
|
)}
|
|
@@ -9,13 +9,4 @@ describe("insertBetween", () => {
|
|
|
9
9
|
|
|
10
10
|
expect(result).toEqual([1, "|", 2, "|", 3]);
|
|
11
11
|
});
|
|
12
|
-
|
|
13
|
-
it("insertBetween", () => {
|
|
14
|
-
const separator = (index) => `separator:${index}`;
|
|
15
|
-
const items = [1, 2, 3];
|
|
16
|
-
|
|
17
|
-
const result = insertBetween(separator, items);
|
|
18
|
-
|
|
19
|
-
expect(result).toEqual([1, "separator:0", 2, "separator:1", 3]);
|
|
20
|
-
});
|
|
21
12
|
});
|
|
@@ -1,17 +1,43 @@
|
|
|
1
|
-
|
|
1
|
+
import React, { useEffect, useCallback, ReactNode, useReducer } from "react";
|
|
2
|
+
import { View, StyleSheet, LayoutChangeEvent } from "react-native";
|
|
3
|
+
import { createLogger } from "@applicaster/zapp-react-native-utils/logger";
|
|
2
4
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
const { log_debug } = createLogger({
|
|
6
|
+
subsystem: "MeasurementsPortal",
|
|
7
|
+
category: "UIComponents",
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
type LayoutDimensions = {
|
|
11
|
+
width: number;
|
|
12
|
+
height: number;
|
|
13
|
+
};
|
|
7
14
|
|
|
8
|
-
type
|
|
15
|
+
type MeasurementResult = {
|
|
16
|
+
width: number;
|
|
17
|
+
height: number;
|
|
18
|
+
index?: number;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
type MeasurementsPortalProps = {
|
|
9
22
|
Component: React.ElementType;
|
|
10
|
-
onLayout: (LayoutChangeEvent) => void;
|
|
11
|
-
componentProps?: Record<
|
|
23
|
+
onLayout: (event: LayoutChangeEvent) => void;
|
|
24
|
+
componentProps?: Record<string, any>;
|
|
25
|
+
onLoadFinished?: () => void;
|
|
12
26
|
};
|
|
13
27
|
|
|
14
|
-
|
|
28
|
+
type MeasurementPortalContextValue = {
|
|
29
|
+
measureComponent: (
|
|
30
|
+
comp: React.ElementType,
|
|
31
|
+
props: Record<string, any>
|
|
32
|
+
) => Promise<MeasurementResult>;
|
|
33
|
+
measuringInProgress: boolean;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
type MeasurementsPortalContextProviderProps = {
|
|
37
|
+
children: ReactNode;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const isError = (possibleError: any): boolean => possibleError instanceof Error;
|
|
15
41
|
|
|
16
42
|
const styles = StyleSheet.create({
|
|
17
43
|
container: {
|
|
@@ -25,100 +51,245 @@ const styles = StyleSheet.create({
|
|
|
25
51
|
wrapper: {},
|
|
26
52
|
});
|
|
27
53
|
|
|
28
|
-
|
|
54
|
+
type State = {
|
|
55
|
+
component: React.ElementType;
|
|
56
|
+
componentProps: Record<string, any> | null;
|
|
57
|
+
measureComponentCallback: ((result: MeasurementResult) => void) | null;
|
|
58
|
+
onLoadFinishedIsCalled: boolean;
|
|
59
|
+
measuringInProgress: boolean;
|
|
60
|
+
layout: LayoutDimensions;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
type Action =
|
|
64
|
+
| { type: "SET_COMPONENT"; payload: React.ElementType }
|
|
65
|
+
| { type: "SET_COMPONENT_PROPS"; payload: Record<string, any> | null }
|
|
66
|
+
| {
|
|
67
|
+
type: "SET_MEASURE_CALLBACK";
|
|
68
|
+
payload: ((result: MeasurementResult) => void) | null;
|
|
69
|
+
}
|
|
70
|
+
| { type: "SET_ON_LOAD_FINISHED"; payload: boolean }
|
|
71
|
+
| { type: "SET_MEASURING_PROGRESS"; payload: boolean }
|
|
72
|
+
| { type: "SET_LAYOUT"; payload: LayoutDimensions };
|
|
73
|
+
|
|
74
|
+
const MEASUREMENT_ACTIONS = {
|
|
75
|
+
setComponent: "SET_COMPONENT",
|
|
76
|
+
setComponentProps: "SET_COMPONENT_PROPS",
|
|
77
|
+
setMeasureCallback: "SET_MEASURE_CALLBACK",
|
|
78
|
+
setOnLoadFinished: "SET_ON_LOAD_FINISHED",
|
|
79
|
+
setMeasuringProgress: "SET_MEASURING_PROGRESS",
|
|
80
|
+
setLayout: "SET_LAYOUT",
|
|
81
|
+
} as const;
|
|
82
|
+
|
|
83
|
+
const reducer = (state: State, action: Action): State => {
|
|
84
|
+
switch (action.type) {
|
|
85
|
+
case MEASUREMENT_ACTIONS.setComponent:
|
|
86
|
+
return { ...state, component: action.payload };
|
|
87
|
+
case MEASUREMENT_ACTIONS.setComponentProps:
|
|
88
|
+
return { ...state, componentProps: action.payload };
|
|
89
|
+
case MEASUREMENT_ACTIONS.setMeasureCallback:
|
|
90
|
+
return { ...state, measureComponentCallback: action.payload };
|
|
91
|
+
case MEASUREMENT_ACTIONS.setOnLoadFinished:
|
|
92
|
+
return { ...state, onLoadFinishedIsCalled: action.payload };
|
|
93
|
+
case MEASUREMENT_ACTIONS.setMeasuringProgress:
|
|
94
|
+
return { ...state, measuringInProgress: action.payload };
|
|
95
|
+
case MEASUREMENT_ACTIONS.setLayout:
|
|
96
|
+
return { ...state, layout: action.payload };
|
|
97
|
+
default:
|
|
98
|
+
return state;
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const initialState: State = {
|
|
103
|
+
component: null,
|
|
104
|
+
componentProps: null,
|
|
105
|
+
measureComponentCallback: null,
|
|
106
|
+
onLoadFinishedIsCalled: false,
|
|
107
|
+
measuringInProgress: false,
|
|
108
|
+
layout: { width: 0, height: 0 },
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Handles layout changes for the MeasurementsPortal component
|
|
113
|
+
*/
|
|
114
|
+
const onLayout =
|
|
115
|
+
(dispatch) =>
|
|
116
|
+
({ nativeEvent: { layout: newLayout } }: LayoutChangeEvent) => {
|
|
117
|
+
log_debug("Rendered measurement layout", { newLayout });
|
|
118
|
+
dispatch({ type: MEASUREMENT_ACTIONS.setLayout, payload: newLayout });
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* MeasurementsPortal is a component that is used to measure the size of a component
|
|
123
|
+
* It is not rendered to the main tree, but it actually uses a component and its props to measure the size of the component.
|
|
124
|
+
* The <Component> comes from the measureComponent method of the MeasurementPortalContext.
|
|
125
|
+
*/
|
|
126
|
+
const MeasurementsPortal = ({
|
|
127
|
+
Component,
|
|
128
|
+
onLayout,
|
|
129
|
+
componentProps,
|
|
130
|
+
}: MeasurementsPortalProps) => {
|
|
131
|
+
if (!Component) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
|
|
29
135
|
return (
|
|
30
136
|
<View style={styles.container} testID="MeasurementsPortal">
|
|
31
|
-
<View style={styles.wrapper} onLayout={onLayout}
|
|
137
|
+
<View style={styles.wrapper} onLayout={onLayout}>
|
|
32
138
|
<Component {...componentProps} />
|
|
33
139
|
</View>
|
|
34
140
|
</View>
|
|
35
141
|
);
|
|
36
142
|
};
|
|
37
143
|
|
|
38
|
-
const MeasurementPortalContext =
|
|
144
|
+
const MeasurementPortalContext =
|
|
145
|
+
React.createContext<MeasurementPortalContextValue | null>(null);
|
|
39
146
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
147
|
+
/**
|
|
148
|
+
* The MeasurementsPortalContextProvider provides a measureComponent method, and a measuringInProgress flag
|
|
149
|
+
* With these we can pre-render a component and measure its size before rendering it to the main tree to avoid layout shifts
|
|
150
|
+
*/
|
|
151
|
+
const MeasurementsPortalContextProvider = ({
|
|
152
|
+
children,
|
|
153
|
+
}: MeasurementsPortalContextProviderProps) => {
|
|
154
|
+
const [state, dispatch] = useReducer(reducer, initialState);
|
|
43
155
|
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
const onLoadFinishedIsCalled = React.useRef(null);
|
|
156
|
+
const onLoadFinishedRef =
|
|
157
|
+
React.useRef<MeasurementsPortalProps["onLoadFinished"]>(null);
|
|
47
158
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
159
|
+
/**
|
|
160
|
+
* Finalize resolves the promise created by measureComponent and finalizes the measurement process
|
|
161
|
+
* It resolves with the measurement result (width/height/index) to the measureComponentCallback
|
|
162
|
+
*/
|
|
163
|
+
const finalize = useCallback(() => {
|
|
164
|
+
if (state.measureComponentCallback) {
|
|
165
|
+
log_debug("Finalize measurement process", {
|
|
166
|
+
layout: state.layout,
|
|
167
|
+
componentProps: state.componentProps,
|
|
168
|
+
});
|
|
51
169
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
170
|
+
state.measureComponentCallback({
|
|
171
|
+
width: state.layout.width,
|
|
172
|
+
height: state.layout.height,
|
|
173
|
+
index: state.componentProps?.index,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
55
176
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
177
|
+
dispatch({ type: MEASUREMENT_ACTIONS.setMeasureCallback, payload: null });
|
|
178
|
+
|
|
179
|
+
dispatch({
|
|
180
|
+
type: MEASUREMENT_ACTIONS.setMeasuringProgress,
|
|
181
|
+
payload: false,
|
|
61
182
|
});
|
|
62
183
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
onLoadFinishedIsCalled.current = false;
|
|
66
|
-
};
|
|
184
|
+
dispatch({ type: MEASUREMENT_ACTIONS.setOnLoadFinished, payload: false });
|
|
185
|
+
}, [state.layout, state.componentProps, state.measureComponentCallback]);
|
|
67
186
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
} else {
|
|
75
|
-
onLoadFinishedIsCalled.current = true;
|
|
76
|
-
props.onLoadFinished(...args);
|
|
77
|
-
}
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
componentProps.current = {
|
|
81
|
-
...props,
|
|
82
|
-
onLoadFinished: handleOnLoadFinish,
|
|
83
|
-
};
|
|
84
|
-
};
|
|
187
|
+
useEffect(() => {
|
|
188
|
+
log_debug("Measurement portal updated", {
|
|
189
|
+
measureComponentCallback: state.measureComponentCallback,
|
|
190
|
+
onLoadFinishedIsCalled: state.onLoadFinishedIsCalled,
|
|
191
|
+
layout: state.layout,
|
|
192
|
+
});
|
|
85
193
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
194
|
+
if (
|
|
195
|
+
state.measureComponentCallback &&
|
|
196
|
+
state.layout.width > 0 &&
|
|
197
|
+
state.layout.height > 0 &&
|
|
198
|
+
onLoadFinishedRef.current
|
|
199
|
+
) {
|
|
200
|
+
finalize();
|
|
201
|
+
}
|
|
202
|
+
}, [state.measureComponentCallback, state.layout, finalize]);
|
|
90
203
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
204
|
+
/**
|
|
205
|
+
* Utility function that takes, a component and its props, measures the rendered size of the component
|
|
206
|
+
* and creates a promise that does not get resolved with the measurement result (width/height/index)
|
|
207
|
+
* until the onLoadFinished callback is called
|
|
208
|
+
*/
|
|
209
|
+
const measureComponent = useCallback(
|
|
210
|
+
(
|
|
211
|
+
comp: React.ElementType,
|
|
212
|
+
props: { onLoadFinished: () => void; [key: string]: any }
|
|
213
|
+
): Promise<MeasurementResult> => {
|
|
214
|
+
return new Promise((resolve) => {
|
|
215
|
+
log_debug("Measuring process started", { comp, props });
|
|
95
216
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
217
|
+
dispatch({
|
|
218
|
+
type: MEASUREMENT_ACTIONS.setMeasureCallback,
|
|
219
|
+
payload: resolve,
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
dispatch({
|
|
223
|
+
type: MEASUREMENT_ACTIONS.setMeasuringProgress,
|
|
224
|
+
payload: true,
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
dispatch({
|
|
228
|
+
type: MEASUREMENT_ACTIONS.setOnLoadFinished,
|
|
229
|
+
payload: false,
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
dispatch({
|
|
233
|
+
type: MEASUREMENT_ACTIONS.setLayout,
|
|
234
|
+
payload: { width: 0, height: 0 },
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
onLoadFinishedRef.current =
|
|
238
|
+
// eslint-disable-next-line react/prop-types
|
|
239
|
+
props.onLoadFinished as MeasurementsPortalProps["onLoadFinished"];
|
|
240
|
+
|
|
241
|
+
const handleOnLoadFinish = (...args: any[]) => {
|
|
242
|
+
if (args?.[0]?.error && isError(args[0].error)) {
|
|
243
|
+
log_debug(
|
|
244
|
+
"Component called onLoadFinish with an error, completing measurement process",
|
|
245
|
+
{ args }
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
finalize();
|
|
249
|
+
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
log_debug(
|
|
254
|
+
"Component called onLoadFinish, completing measurement process",
|
|
255
|
+
{ args }
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
dispatch({
|
|
259
|
+
type: MEASUREMENT_ACTIONS.setOnLoadFinished,
|
|
260
|
+
payload: true,
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
onLoadFinishedRef.current?.apply(null, args);
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
dispatch({
|
|
267
|
+
type: MEASUREMENT_ACTIONS.setComponentProps,
|
|
268
|
+
payload: { ...props, onLoadFinished: handleOnLoadFinish },
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
dispatch({ type: MEASUREMENT_ACTIONS.setComponent, payload: comp });
|
|
105
272
|
});
|
|
106
|
-
}
|
|
107
|
-
|
|
273
|
+
},
|
|
274
|
+
[]
|
|
275
|
+
);
|
|
276
|
+
|
|
277
|
+
const contextValue = React.useMemo(
|
|
278
|
+
() => ({
|
|
279
|
+
measureComponent,
|
|
280
|
+
measuringInProgress: state.measuringInProgress,
|
|
281
|
+
}),
|
|
282
|
+
[measureComponent, state.measuringInProgress]
|
|
283
|
+
);
|
|
108
284
|
|
|
109
285
|
return (
|
|
110
286
|
<>
|
|
111
287
|
<MeasurementsPortal
|
|
112
|
-
Component={
|
|
113
|
-
componentProps={componentProps
|
|
114
|
-
onLayout={onLayout}
|
|
288
|
+
Component={state.component}
|
|
289
|
+
componentProps={state.componentProps}
|
|
290
|
+
onLayout={onLayout(dispatch)}
|
|
115
291
|
/>
|
|
116
|
-
<MeasurementPortalContext.Provider
|
|
117
|
-
value={{
|
|
118
|
-
measureComponent,
|
|
119
|
-
measuringInProgress,
|
|
120
|
-
}}
|
|
121
|
-
>
|
|
292
|
+
<MeasurementPortalContext.Provider value={contextValue}>
|
|
122
293
|
{children}
|
|
123
294
|
</MeasurementPortalContext.Provider>
|
|
124
295
|
</>
|
|
@@ -9,12 +9,7 @@ import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
|
9
9
|
|
|
10
10
|
const { log_debug, log_info, log_error } = loggerLiveImageManager;
|
|
11
11
|
|
|
12
|
-
export type LiveImageManagerEvent =
|
|
13
|
-
| "onEnd"
|
|
14
|
-
| "onStart"
|
|
15
|
-
| "onError"
|
|
16
|
-
| "onDestroy"
|
|
17
|
-
| "onPlayerClose";
|
|
12
|
+
export type LiveImageManagerEvent = "onEnd" | "onStart" | "onError";
|
|
18
13
|
|
|
19
14
|
export type LiveImageManagerListenerData = {
|
|
20
15
|
id: string;
|
|
@@ -105,15 +100,6 @@ export class LiveImageManager implements PlayerLifecycleListener {
|
|
|
105
100
|
}
|
|
106
101
|
|
|
107
102
|
this.items = this.items.filter((i) => i !== item);
|
|
108
|
-
|
|
109
|
-
this.invokeListenersUpdate({
|
|
110
|
-
callbackName: "onDestroy",
|
|
111
|
-
event: {
|
|
112
|
-
item,
|
|
113
|
-
playerId: this.currentlyPlaying?.playerId,
|
|
114
|
-
primaryPlayerId: this.primaryPlayer?.playerId,
|
|
115
|
-
},
|
|
116
|
-
});
|
|
117
103
|
};
|
|
118
104
|
|
|
119
105
|
public onViewportEnter = (item: LiveImage) => {
|
|
@@ -390,25 +376,6 @@ export class LiveImageManager implements PlayerLifecycleListener {
|
|
|
390
376
|
});
|
|
391
377
|
};
|
|
392
378
|
|
|
393
|
-
onLiveImagePlayerClose = (item: LiveImage) => {
|
|
394
|
-
log_debug(
|
|
395
|
-
`onLiveImagePlayerClose: live-image ${playerInfo(
|
|
396
|
-
item.player
|
|
397
|
-
)}, currentPlayingId: ${
|
|
398
|
-
this.currentlyPlaying?.playerId
|
|
399
|
-
}, primaryPlayerId: ${this.primaryPlayer?.playerId}`
|
|
400
|
-
);
|
|
401
|
-
|
|
402
|
-
this.invokeListenersUpdate({
|
|
403
|
-
callbackName: "onPlayerClose",
|
|
404
|
-
event: {
|
|
405
|
-
item,
|
|
406
|
-
playerId: this.currentlyPlaying?.playerId,
|
|
407
|
-
primaryPlayerId: this.primaryPlayer?.playerId,
|
|
408
|
-
},
|
|
409
|
-
});
|
|
410
|
-
};
|
|
411
|
-
|
|
412
379
|
onLiveImageError = (item: LiveImage, error: Error) => {
|
|
413
380
|
item.setMode(LiveImageType.Image);
|
|
414
381
|
|
|
@@ -512,16 +479,6 @@ export class LiveImage implements QuickBrickPlayer.SharedPlayerCallBacks {
|
|
|
512
479
|
LiveImageManager.instance.onLiveImageError(this, error);
|
|
513
480
|
};
|
|
514
481
|
|
|
515
|
-
onVideoError = (error: Error) => {
|
|
516
|
-
log_error(
|
|
517
|
-
`onVideoError: live-image, fail to play, switching back to image. error: ${
|
|
518
|
-
error?.message
|
|
519
|
-
}, ${playerInfo(this.player)}`
|
|
520
|
-
);
|
|
521
|
-
|
|
522
|
-
LiveImageManager.instance.onLiveImageError(this, error);
|
|
523
|
-
};
|
|
524
|
-
|
|
525
482
|
onVideoEnd = (_event) => {
|
|
526
483
|
log_info(
|
|
527
484
|
`onVideoEnd: live-image, video completed, switching back to image, ${playerInfo(
|
|
@@ -540,7 +497,6 @@ export class LiveImage implements QuickBrickPlayer.SharedPlayerCallBacks {
|
|
|
540
497
|
);
|
|
541
498
|
|
|
542
499
|
// TODO: we maybe want to add onPlayerClose to separate completion behavior
|
|
543
|
-
LiveImageManager.instance.onLiveImagePlayerClose(this);
|
|
544
500
|
LiveImageManager.instance.onLiveImageEnded(this);
|
|
545
501
|
};
|
|
546
502
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/zapp-react-native-ui-components",
|
|
3
|
-
"version": "12.0.0-alpha.
|
|
3
|
+
"version": "12.0.0-alpha.5786305091",
|
|
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",
|
|
@@ -35,10 +35,10 @@
|
|
|
35
35
|
"redux-mock-store": "^1.5.3"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@applicaster/applicaster-types": "12.0.0-alpha.
|
|
39
|
-
"@applicaster/zapp-react-native-bridge": "12.0.0-alpha.
|
|
40
|
-
"@applicaster/zapp-react-native-redux": "12.0.0-alpha.
|
|
41
|
-
"@applicaster/zapp-react-native-utils": "12.0.0-alpha.
|
|
38
|
+
"@applicaster/applicaster-types": "12.0.0-alpha.5786305091",
|
|
39
|
+
"@applicaster/zapp-react-native-bridge": "12.0.0-alpha.5786305091",
|
|
40
|
+
"@applicaster/zapp-react-native-redux": "12.0.0-alpha.5786305091",
|
|
41
|
+
"@applicaster/zapp-react-native-utils": "12.0.0-alpha.5786305091",
|
|
42
42
|
"promise": "^8.3.0",
|
|
43
43
|
"react-router-native": "^5.1.2",
|
|
44
44
|
"url": "^0.11.0",
|