@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.
@@ -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, 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,
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
- const state = useCellState({ id: item.id, behavior, focused: isFocused, selected });
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 = focusableId === focusedButtonId;
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={actionState?.state ? thumbColorActive : thumbColor}
42
+ thumbColor={isSelected ? thumbColorActive : thumbColor}
41
43
  ios_backgroundColor={iosBackgroundColor}
42
44
  onValueChange={onPress}
43
- value={!!actionState?.state}
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 key={index} width={horizontalGutter} />
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 key={index} width={horizontalGutter} />
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 = "onEnd" | "onStart" | "onError";
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": "11.1.0",
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": "11.1.0",
39
- "@applicaster/zapp-react-native-bridge": "11.1.0",
40
- "@applicaster/zapp-react-native-redux": "11.1.0",
41
- "@applicaster/zapp-react-native-utils": "11.1.0",
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",