@applicaster/zapp-react-native-ui-components 11.1.0 → 12.0.0-alpha.5035843303
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 +26 -14
- package/Components/Cell/TvOSCellComponent.tsx +4 -9
- package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +4 -1
- package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +4 -0
- package/Components/MasterCell/DefaultComponents/Toggle.tsx +5 -3
- package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.android.tsx +4 -1
- package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.tsx +10 -2
- package/Components/MasterCell/utils/__tests__/insertBetween.test.js +9 -0
- package/Components/VideoLive/LiveImageManager.ts +45 -1
- package/package.json +5 -5
|
@@ -3,6 +3,8 @@ 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
|
+
|
|
6
8
|
import { isAppleTV } from "../../Helpers/Platform";
|
|
7
9
|
import { useCellState } from "../MasterCell/utils";
|
|
8
10
|
|
|
@@ -35,18 +37,19 @@ const useCellFocusedState = (groupId: string, id: string) => {
|
|
|
35
37
|
};
|
|
36
38
|
|
|
37
39
|
type Props = {
|
|
38
|
-
item: ZappEntry
|
|
39
|
-
CellRenderer: React.ComponentType<{ item: Object
|
|
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
|
|
40
|
+
item: ZappEntry;
|
|
41
|
+
CellRenderer: React.ComponentType<{ item: Object; state: string }>;
|
|
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;
|
|
50
53
|
};
|
|
51
54
|
|
|
52
55
|
export function CellWithFocusable(props: Props) {
|
|
@@ -62,11 +65,19 @@ export function CellWithFocusable(props: Props) {
|
|
|
62
65
|
skipFocusManagerRegistration,
|
|
63
66
|
isFocusable,
|
|
64
67
|
behavior,
|
|
65
|
-
selected
|
|
68
|
+
selected,
|
|
69
|
+
focused,
|
|
66
70
|
} = props;
|
|
67
71
|
|
|
68
72
|
const isFocused = useCellFocusedState(groupId, id);
|
|
69
|
-
|
|
73
|
+
|
|
74
|
+
const state = useCellState({
|
|
75
|
+
id: item.id,
|
|
76
|
+
behavior,
|
|
77
|
+
focused: isFocused || toBooleanWithDefaultFalse(focused),
|
|
78
|
+
selected,
|
|
79
|
+
});
|
|
80
|
+
|
|
70
81
|
const [focusedButtonId, setFocusedButtonId] = React.useState(undefined);
|
|
71
82
|
|
|
72
83
|
// for horizontal scrolling
|
|
@@ -95,6 +106,7 @@ export function CellWithFocusable(props: Props) {
|
|
|
95
106
|
preferredFocus={preferredFocus}
|
|
96
107
|
skipFocusManagerRegistration={skipFocusManagerRegistration}
|
|
97
108
|
isFocusable={isFocusable}
|
|
109
|
+
focused={focused}
|
|
98
110
|
/>
|
|
99
111
|
);
|
|
100
112
|
}
|
|
@@ -226,13 +226,7 @@ export class TvOSCellComponent extends React.Component<Props, State> {
|
|
|
226
226
|
|
|
227
227
|
hasReceivedFocus() {}
|
|
228
228
|
|
|
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
|
-
}
|
|
229
|
+
scrollTo(index) {}
|
|
236
230
|
|
|
237
231
|
render() {
|
|
238
232
|
const {
|
|
@@ -249,7 +243,7 @@ export class TvOSCellComponent extends React.Component<Props, State> {
|
|
|
249
243
|
hasReceivedFocus,
|
|
250
244
|
groupId,
|
|
251
245
|
isFocusable,
|
|
252
|
-
behavior
|
|
246
|
+
behavior,
|
|
253
247
|
} = this.props;
|
|
254
248
|
|
|
255
249
|
const { id } = item;
|
|
@@ -279,6 +273,7 @@ export class TvOSCellComponent extends React.Component<Props, State> {
|
|
|
279
273
|
index={index}
|
|
280
274
|
scrollTo={this.scrollTo}
|
|
281
275
|
preferredFocus={preferredFocus}
|
|
276
|
+
focused={this.props.focused}
|
|
282
277
|
behavior={behavior}
|
|
283
278
|
/>
|
|
284
279
|
</View>
|
|
@@ -310,7 +305,7 @@ export class TvOSCellComponent extends React.Component<Props, State> {
|
|
|
310
305
|
item,
|
|
311
306
|
focused: this.props.focused || selectedCell || focused,
|
|
312
307
|
scrollTo: this.scrollTo,
|
|
313
|
-
behavior
|
|
308
|
+
behavior,
|
|
314
309
|
}}
|
|
315
310
|
/>
|
|
316
311
|
);
|
|
@@ -4,6 +4,7 @@ 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";
|
|
7
8
|
|
|
8
9
|
const { Logger } = getXray();
|
|
9
10
|
|
|
@@ -35,7 +36,9 @@ export function FocusableView({ style, children, item, ...otherProps }: Props) {
|
|
|
35
36
|
|
|
36
37
|
const focusableId = getFocusableId(prefixId, suffixId);
|
|
37
38
|
|
|
38
|
-
const focused =
|
|
39
|
+
const focused =
|
|
40
|
+
focusableId === focusedButtonId ||
|
|
41
|
+
toBooleanWithDefaultFalse(otherProps.focused);
|
|
39
42
|
|
|
40
43
|
const additionalStyles = focused ? focusedStyles : normalStyles;
|
|
41
44
|
|
|
@@ -31,6 +31,10 @@ 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
|
+
|
|
34
38
|
React.useEffect(() => {
|
|
35
39
|
// reset error state on URI change as the error is referencing previous uri
|
|
36
40
|
setErrorState(null);
|
|
@@ -9,10 +9,11 @@ type Props = {
|
|
|
9
9
|
type?: string;
|
|
10
10
|
};
|
|
11
11
|
style: Styles;
|
|
12
|
+
cellState: CellState;
|
|
12
13
|
};
|
|
13
14
|
|
|
14
15
|
export const Toggle = (props: Props) => {
|
|
15
|
-
const { style, item, action } = props;
|
|
16
|
+
const { style, item, action, cellState } = props;
|
|
16
17
|
|
|
17
18
|
const {
|
|
18
19
|
trackColor,
|
|
@@ -22,6 +23,7 @@ export const Toggle = (props: Props) => {
|
|
|
22
23
|
iosBackgroundColor,
|
|
23
24
|
} = style;
|
|
24
25
|
|
|
26
|
+
const isSelected = React.useMemo(() => (cellState === "selected"), [item?.id, cellState]);
|
|
25
27
|
const actionContext = useActions(action?.identifier);
|
|
26
28
|
|
|
27
29
|
const [actionState, setActionState] = useState(
|
|
@@ -37,10 +39,10 @@ export const Toggle = (props: Props) => {
|
|
|
37
39
|
return (
|
|
38
40
|
<Switch
|
|
39
41
|
trackColor={{ false: trackColor, true: trackColorActive }}
|
|
40
|
-
thumbColor={
|
|
42
|
+
thumbColor={isSelected ? thumbColorActive : thumbColor}
|
|
41
43
|
ios_backgroundColor={iosBackgroundColor}
|
|
42
44
|
onValueChange={onPress}
|
|
43
|
-
value={
|
|
45
|
+
value={isSelected}
|
|
44
46
|
/>
|
|
45
47
|
);
|
|
46
48
|
};
|
|
@@ -61,7 +61,10 @@ export function ButtonContainerView({
|
|
|
61
61
|
<View style={applyRTLStylesIfNeeded(style, isRTL)}>
|
|
62
62
|
{insertBetween(
|
|
63
63
|
(index) => (
|
|
64
|
-
<HorizontalSeparator
|
|
64
|
+
<HorizontalSeparator
|
|
65
|
+
key={`separator_${index}`}
|
|
66
|
+
width={horizontalGutter}
|
|
67
|
+
/>
|
|
65
68
|
),
|
|
66
69
|
cloneElementsWithIds(buttonIds, filteredChildren)
|
|
67
70
|
)}
|
|
@@ -7,6 +7,10 @@ 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";
|
|
10
14
|
|
|
11
15
|
const HorizontalSeparator = ({ width }: HorizontalSeparatorProps) => (
|
|
12
16
|
<View style={{ width }} />
|
|
@@ -17,6 +21,7 @@ export function ButtonContainerView({
|
|
|
17
21
|
children,
|
|
18
22
|
...otherProps
|
|
19
23
|
}: ContainerProps) {
|
|
24
|
+
const isRTL = useIsRTL();
|
|
20
25
|
const horizontalGutter = R.pathOr(0, ["horizontalGutter"], otherProps);
|
|
21
26
|
|
|
22
27
|
const filteredChildren = useFilterChildren<ContainerChildren>(children);
|
|
@@ -26,10 +31,13 @@ export function ButtonContainerView({
|
|
|
26
31
|
}
|
|
27
32
|
|
|
28
33
|
return (
|
|
29
|
-
<View style={style}>
|
|
34
|
+
<View style={applyRTLStylesIfNeeded(style, isRTL)}>
|
|
30
35
|
{insertBetween(
|
|
31
36
|
(index) => (
|
|
32
|
-
<HorizontalSeparator
|
|
37
|
+
<HorizontalSeparator
|
|
38
|
+
key={`separator_${index}`}
|
|
39
|
+
width={horizontalGutter}
|
|
40
|
+
/>
|
|
33
41
|
),
|
|
34
42
|
filteredChildren
|
|
35
43
|
)}
|
|
@@ -9,4 +9,13 @@ 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
|
+
});
|
|
12
21
|
});
|
|
@@ -9,7 +9,12 @@ 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 =
|
|
12
|
+
export type LiveImageManagerEvent =
|
|
13
|
+
| "onEnd"
|
|
14
|
+
| "onStart"
|
|
15
|
+
| "onError"
|
|
16
|
+
| "onDestroy"
|
|
17
|
+
| "onPlayerClose";
|
|
13
18
|
|
|
14
19
|
export type LiveImageManagerListenerData = {
|
|
15
20
|
id: string;
|
|
@@ -100,6 +105,15 @@ export class LiveImageManager implements PlayerLifecycleListener {
|
|
|
100
105
|
}
|
|
101
106
|
|
|
102
107
|
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
|
+
});
|
|
103
117
|
};
|
|
104
118
|
|
|
105
119
|
public onViewportEnter = (item: LiveImage) => {
|
|
@@ -376,6 +390,25 @@ export class LiveImageManager implements PlayerLifecycleListener {
|
|
|
376
390
|
});
|
|
377
391
|
};
|
|
378
392
|
|
|
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
|
+
|
|
379
412
|
onLiveImageError = (item: LiveImage, error: Error) => {
|
|
380
413
|
item.setMode(LiveImageType.Image);
|
|
381
414
|
|
|
@@ -479,6 +512,16 @@ export class LiveImage implements QuickBrickPlayer.SharedPlayerCallBacks {
|
|
|
479
512
|
LiveImageManager.instance.onLiveImageError(this, error);
|
|
480
513
|
};
|
|
481
514
|
|
|
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
|
+
|
|
482
525
|
onVideoEnd = (_event) => {
|
|
483
526
|
log_info(
|
|
484
527
|
`onVideoEnd: live-image, video completed, switching back to image, ${playerInfo(
|
|
@@ -497,6 +540,7 @@ export class LiveImage implements QuickBrickPlayer.SharedPlayerCallBacks {
|
|
|
497
540
|
);
|
|
498
541
|
|
|
499
542
|
// TODO: we maybe want to add onPlayerClose to separate completion behavior
|
|
543
|
+
LiveImageManager.instance.onLiveImagePlayerClose(this);
|
|
500
544
|
LiveImageManager.instance.onLiveImageEnded(this);
|
|
501
545
|
};
|
|
502
546
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/zapp-react-native-ui-components",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "12.0.0-alpha.5035843303",
|
|
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": "
|
|
39
|
-
"@applicaster/zapp-react-native-bridge": "
|
|
40
|
-
"@applicaster/zapp-react-native-redux": "
|
|
41
|
-
"@applicaster/zapp-react-native-utils": "
|
|
38
|
+
"@applicaster/applicaster-types": "12.0.0-alpha.5035843303",
|
|
39
|
+
"@applicaster/zapp-react-native-bridge": "12.0.0-alpha.5035843303",
|
|
40
|
+
"@applicaster/zapp-react-native-redux": "12.0.0-alpha.5035843303",
|
|
41
|
+
"@applicaster/zapp-react-native-utils": "12.0.0-alpha.5035843303",
|
|
42
42
|
"promise": "^8.3.0",
|
|
43
43
|
"react-router-native": "^5.1.2",
|
|
44
44
|
"url": "^0.11.0",
|