@applicaster/zapp-react-native-ui-components 12.0.0-rc.99 → 12.0.2-alpha.1558929238

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.
Files changed (65) hide show
  1. package/Components/Cell/Cell.tsx +29 -10
  2. package/Components/Focusable/__tests__/index.android.test.tsx +72 -0
  3. package/Components/Focusable/index.android.tsx +15 -9
  4. package/Components/FocusableList/FocusableChild.ts +2 -2
  5. package/Components/FocusableList/index.tsx +11 -12
  6. package/Components/FocusableScrollView/index.tsx +1 -1
  7. package/Components/FreezeWithCallback/Freeze.tsx +31 -0
  8. package/Components/FreezeWithCallback/LICENSE +21 -0
  9. package/Components/FreezeWithCallback/Suspender.tsx +17 -0
  10. package/Components/FreezeWithCallback/__tests__/index.test.tsx +170 -0
  11. package/Components/FreezeWithCallback/index.tsx +3 -0
  12. package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +67 -7
  13. package/Components/GeneralContentScreen/utils/useCurationAPI.ts +53 -9
  14. package/Components/Image/Image.android.tsx +1 -1
  15. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +11 -7
  16. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +1 -7
  17. package/Components/MasterCell/DefaultComponents/CellBadge/index.tsx +6 -1
  18. package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +12 -0
  19. package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +6 -5
  20. package/Components/MasterCell/DefaultComponents/Image/Image.web.tsx +2 -2
  21. package/Components/MasterCell/DefaultComponents/ImageBorderContainer/index.ts +71 -0
  22. package/Components/MasterCell/DefaultComponents/__tests__/image.test.js +1 -1
  23. package/Components/MasterCell/DefaultComponents/index.ts +2 -0
  24. package/Components/MasterCell/SharedUI/ProgressBar/hooks/useFillInPercent.ts +7 -1
  25. package/Components/MasterCell/dataAdapter.ts +21 -7
  26. package/Components/MasterCell/index.tsx +5 -3
  27. package/Components/MasterCell/utils/index.ts +3 -4
  28. package/Components/PlayerContainer/PlayerContainer.tsx +14 -8
  29. package/Components/Screen/TV/hooks/index.ts +0 -2
  30. package/Components/Screen/TV/index.web.tsx +32 -19
  31. package/Components/Screen/__tests__/Screen.test.tsx +0 -49
  32. package/Components/Screen/index.tsx +2 -20
  33. package/Components/Screen/navigationHandler.ts +1 -3
  34. package/Components/Screen/orientationHandler.ts +18 -7
  35. package/Components/Screen/utils.ts +16 -0
  36. package/Components/ScreenResolver/index.tsx +6 -11
  37. package/Components/Transitioner/Scene.tsx +9 -2
  38. package/Components/Transitioner/Transitioner.tsx +38 -23
  39. package/Components/Transitioner/__tests__/Scene.test.js +7 -0
  40. package/Components/Transitioner/__tests__/__snapshots__/transitioner.test.js.snap +1 -0
  41. package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +5 -0
  42. package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +3 -9
  43. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +87 -41
  44. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +20 -9
  45. package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +41 -22
  46. package/Components/VideoModal/PlayerDetails.tsx +16 -2
  47. package/Components/VideoModal/PlayerWrapper.tsx +84 -66
  48. package/Components/VideoModal/VideoModal.tsx +33 -27
  49. package/Components/VideoModal/__tests__/PlayerDetails.test.tsx +7 -0
  50. package/Components/VideoModal/__tests__/__snapshots__/PlayerDetails.test.tsx.snap +2 -0
  51. package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +72 -10
  52. package/Components/ZappFrameworkComponents/BarView/BarView.tsx +8 -3
  53. package/Components/ZappFrameworkComponents/BarView/Buttons/BackButton.tsx +7 -2
  54. package/Components/ZappFrameworkComponents/BarView/Buttons/CloseButton.tsx +7 -2
  55. package/Components/ZappFrameworkComponents/BarView/__tests__/BarView.test.tsx +87 -0
  56. package/Components/ZappFrameworkComponents/Buttons/ActionButton.tsx +9 -4
  57. package/Components/default-cell-renderer/__tests__/__snapshots__/defaultCellRenderer.test.tsx.snap +4 -4
  58. package/Contexts/CachedDimensionsContext/index.ts +13 -9
  59. package/Contexts/ConfirmDialogState/index.ts +1 -1
  60. package/Contexts/FocusableGroupContext/withFocusableContext.tsx +18 -11
  61. package/Contexts/ScreenContext/index.tsx +6 -1
  62. package/const.ts +6 -0
  63. package/index.js +3 -0
  64. package/package.json +5 -5
  65. package/Components/Screen/TV/hooks/useRestoreFocus.ts +0 -22
@@ -9,6 +9,18 @@ import { loadPipesData } from "@applicaster/zapp-react-native-redux/ZappPipes";
9
9
  import { isEmptyOrNil } from "@applicaster/zapp-react-native-utils/cellUtils";
10
10
  import { Categories } from "./logger";
11
11
  import { createLogger } from "@applicaster/zapp-react-native-utils/logger";
12
+ import { useRoute } from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
13
+ import {
14
+ ZappPipesEntryContext,
15
+ ZappPipesScreenContext,
16
+ ZappPipesSearchContext,
17
+ } from "@applicaster/zapp-react-native-ui-components/Contexts";
18
+
19
+ import {
20
+ getInflatedDataSourceUrl,
21
+ getSearchContext,
22
+ } from "@applicaster/zapp-react-native-utils/reactHooks/feed/useInflatedUrl";
23
+
12
24
  import { produce } from "immer";
13
25
  // types reference
14
26
 
@@ -20,6 +32,7 @@ type LayoutPresets = PresetsMapping["presets_mappings"];
20
32
 
21
33
  const SMART_COMPONENT_TYPE = "quick-brick-smart-component";
22
34
  const SOURCE_PATH = ["data", "source"];
35
+ const MAPPING_PATH = ["data", "mapping"];
23
36
 
24
37
  const isSmartComponent = (component) =>
25
38
  component.component_type === SMART_COMPONENT_TYPE;
@@ -58,13 +71,14 @@ export const enrichComponent = (
58
71
  comp: ZappUIComponent,
59
72
  index: number,
60
73
  feeds: Feeds,
61
- layoutPresets: LayoutPresets
74
+ layoutPresets: LayoutPresets,
75
+ urlsMap: { [key: string]: string }
62
76
  ): ZappUIComponent[] | null => {
63
77
  if (!isSmartComponent(comp)) {
64
78
  return [comp];
65
79
  }
66
80
 
67
- const presets = prop<string[]>(path(SOURCE_PATH, comp), feeds);
81
+ const presets = prop<string[]>(urlsMap[comp.id], feeds);
68
82
 
69
83
  if (presets && presets.data && presets.data.entry) {
70
84
  return presets.data.entry
@@ -113,11 +127,35 @@ export const useCurationAPI = (
113
127
  [components]
114
128
  );
115
129
 
116
- const urls = useMemo<string[]>(
117
- () =>
118
- smartComponents?.map?.((component) => path(SOURCE_PATH, component)) ?? [],
119
- [smartComponents]
120
- );
130
+ const { pathname } = useRoute();
131
+ const [entryContext] = ZappPipesEntryContext.useZappPipesContext(pathname);
132
+ const [searchContext] = ZappPipesSearchContext.useZappPipesContext();
133
+ const [screenContext] = ZappPipesScreenContext.useZappPipesContext();
134
+
135
+ const urlsMap = useMemo<{ [key: string]: string }>(() => {
136
+ const map = {};
137
+
138
+ smartComponents?.forEach?.((component) => {
139
+ const url = path(SOURCE_PATH, component);
140
+ const mapping = path(MAPPING_PATH, component);
141
+
142
+ map[component.id] = mapping
143
+ ? getInflatedDataSourceUrl({
144
+ source: url,
145
+ contexts: {
146
+ entry: entryContext,
147
+ screen: screenContext,
148
+ search: getSearchContext(searchContext, mapping),
149
+ },
150
+ mapping,
151
+ })
152
+ : url;
153
+ });
154
+
155
+ return map;
156
+ }, [smartComponents, entryContext, screenContext, searchContext]);
157
+
158
+ const urls = useMemo<string[]>(() => Object.values(urlsMap), [urlsMap]);
121
159
 
122
160
  useEffect(() => {
123
161
  urls.forEach((url, index) => {
@@ -138,11 +176,17 @@ export const useCurationAPI = (
138
176
  if (!components) return [];
139
177
 
140
178
  return components.reduce((acc, comp, index) => {
141
- const enrichedComp = enrichComponent(comp, index, feeds, layoutPresets);
179
+ const enrichedComp = enrichComponent(
180
+ comp,
181
+ index,
182
+ feeds,
183
+ layoutPresets,
184
+ urlsMap
185
+ );
142
186
 
143
187
  return enrichedComp ? [...acc, ...enrichedComp] : acc;
144
188
  }, [] as Array<ZappUIComponent>);
145
- }, [components, feeds, layoutPresets]);
189
+ }, [components, feeds, layoutPresets, urlsMap]);
146
190
 
147
191
  return getFinalComponents(
148
192
  enrichedComponents,
@@ -8,5 +8,5 @@ import { useImageSource } from "./utils";
8
8
  export const QBImage = (props: FastImageProps) => {
9
9
  const source = useImageSource(props.source);
10
10
 
11
- return <FastImage {...props} source={source} />;
11
+ return <FastImage useLastImageAsDefaultSource {...props} source={source} />;
12
12
  };
@@ -66,20 +66,24 @@ export function ActionButton(props: Props) {
66
66
  );
67
67
 
68
68
  useEffect(() => {
69
- if (actionDisabled || !actionContext) return;
70
-
71
- setActionState(actionContext.initialEntryState(item));
72
- }, [actionDisabled, item?.id, actionContext]);
69
+ if (!((actionDisabled || !actionContext) && actionState !== null)) {
70
+ setActionState(actionContext.initialEntryState(item));
71
+ }
72
+ }, [actionDisabled, item?.id, actionContext, action]);
73
73
 
74
74
  const onPress = useCallback(() => {
75
75
  actionContext.invokeAction(item, {
76
- updateState: setActionState,
76
+ updateState: (state) => {
77
+ setActionState(state);
78
+ },
77
79
  });
78
- }, [actionState, actionContext?.state, setActionState, item?.id]);
80
+ }, [actionState, actionContext?.state, item?.id]);
79
81
 
80
82
  useEffect(() => {
81
83
  if (typeof actionContext?.addListener === "function") {
82
- return actionContext?.addListener(item.id as string, setActionState);
84
+ return actionContext?.addListener(String(item.id), (state) => {
85
+ setActionState(state);
86
+ });
83
87
  }
84
88
  }, [item?.id]);
85
89
 
@@ -117,9 +117,6 @@ export const BorderContainerView = (props: Props) => {
117
117
  children,
118
118
  } = props;
119
119
 
120
- const borderStyleForMissingBorder =
121
- style?.borderWidth === 0 || !style?.borderColor ? { borderRadius: 0 } : {};
122
-
123
120
  const padding =
124
121
  borderPosition === "outside"
125
122
  ? {
@@ -131,10 +128,7 @@ export const BorderContainerView = (props: Props) => {
131
128
  : {};
132
129
 
133
130
  return (
134
- <View
135
- {...props}
136
- style={[style, styles.container, borderStyleForMissingBorder]}
137
- >
131
+ <View pointerEvents="box-none" {...props} style={[style, styles.container]}>
138
132
  <Border style={style} padding={padding} borderPosition={borderPosition} />
139
133
  {children}
140
134
  </View>
@@ -3,6 +3,7 @@ import {
3
3
  ifElseFocusedSelected,
4
4
  ifElseSelected,
5
5
  } from "@applicaster/zapp-react-native-utils/cellUtils";
6
+ import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
6
7
 
7
8
  import { getPositionStyles } from "./utils";
8
9
 
@@ -31,7 +32,11 @@ export const CellBadge = ({
31
32
  return {
32
33
  type: View, // Cell badge view
33
34
  style: {
34
- flex: 1,
35
+ flex: platformSelect({
36
+ tvos: 0,
37
+ android_tv: 0,
38
+ default: 1,
39
+ }),
35
40
  position: "absolute",
36
41
  ...getPositionStyles(position, {
37
42
  width: value("cell_badge_width"),
@@ -5,6 +5,7 @@ import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/acti
5
5
  import * as R from "ramda";
6
6
  import { getXray } from "@applicaster/zapp-react-native-utils/logger";
7
7
  import { toBooleanWithDefaultFalse } from "@applicaster/zapp-react-native-utils/booleanUtils";
8
+ import { useAccessibilityManager } from "@applicaster/zapp-react-native-utils/appUtils/accessibilityManager/hooks";
8
9
 
9
10
  const { Logger } = getXray();
10
11
 
@@ -44,6 +45,11 @@ export function FocusableView({ style, children, item, ...otherProps }: Props) {
44
45
 
45
46
  const actionContext = useActions(pluginIdentifier);
46
47
 
48
+ const accessibilityManager = useAccessibilityManager({});
49
+
50
+ const { ttsLabel = "" } =
51
+ actionContext?.initialEntryState(item)?.getAccessibility?.(item) || {};
52
+
47
53
  const onPress = (event) => {
48
54
  event?.preventDefault?.();
49
55
 
@@ -88,6 +94,12 @@ export function FocusableView({ style, children, item, ...otherProps }: Props) {
88
94
  mouse: focusable.mouse,
89
95
  });
90
96
  });
97
+
98
+ if (ttsLabel) {
99
+ accessibilityManager.readText({
100
+ text: ttsLabel,
101
+ });
102
+ }
91
103
  };
92
104
 
93
105
  const handleBlur = (_focusable, _direction) => {
@@ -38,21 +38,22 @@ function Image({
38
38
 
39
39
  const shouldRenderImg = source && !error;
40
40
 
41
+ // Default placeholder image fallback is empty string because it won't rerender
42
+ // in case of undefined or {uri: undefined} source
43
+ // https://github.com/facebook/react-native/issues/9195
41
44
  const defaultPlaceHolderImage = React.useMemo(
42
45
  () => ({ uri: placeholderImage || "" }),
43
46
  [source, error]
44
47
  );
45
48
 
46
- const _source = shouldRenderImg
47
- ? withDimensions(source)
48
- : defaultPlaceHolderImage;
49
-
50
49
  return (
51
50
  // @ts-ignore
52
51
  <MemoizedImage
53
52
  style={style as ImageStyle}
54
53
  onError={() => setErrorState(true)}
55
- source={_source?.uri ? _source : undefined}
54
+ source={
55
+ shouldRenderImg ? withDimensions(source) : defaultPlaceHolderImage
56
+ }
56
57
  {...R.omit(["source"], otherProps)}
57
58
  />
58
59
  );
@@ -31,10 +31,10 @@ function Image({
31
31
 
32
32
  return (
33
33
  <RnImage
34
- defaultSource={placeholderImage || ""}
34
+ defaultSource={placeholderImage || null}
35
35
  style={style}
36
36
  source={updatedSource}
37
- {...R.omit(["source"], otherProps)}
37
+ {...R.omit(["source", "placeholderImage"], otherProps)}
38
38
  />
39
39
  );
40
40
  }
@@ -0,0 +1,71 @@
1
+ import { ImageStyle } from "react-native";
2
+
3
+ import {
4
+ getImageContainerMarginStyles,
5
+ ifElseFocusedSelected,
6
+ } from "@applicaster/zapp-react-native-utils/cellUtils";
7
+ import { toNumberWithDefaultZero } from "@applicaster/zapp-react-native-utils/numberUtils";
8
+
9
+ export const ImageBorderContainer = ({
10
+ value,
11
+ state,
12
+ imageStyles,
13
+ }: {
14
+ value: (key: string) => string;
15
+ state: CellState;
16
+ imageStyles: ImageStyle;
17
+ }) => {
18
+ const imageMargins = getImageContainerMarginStyles({ value });
19
+
20
+ return {
21
+ type: "BorderContainerView",
22
+ style: {
23
+ position: "absolute",
24
+ // Image border now separated from the image, so cell padding affects the border position
25
+ marginTop:
26
+ toNumberWithDefaultZero(imageMargins.marginTop) +
27
+ toNumberWithDefaultZero(value("cell_padding_top")),
28
+ marginLeft:
29
+ toNumberWithDefaultZero(imageMargins.marginLeft) +
30
+ toNumberWithDefaultZero(value("cell_padding_left")),
31
+ marginRight:
32
+ toNumberWithDefaultZero(imageMargins.marginRight) +
33
+ toNumberWithDefaultZero(value("cell_padding_right")),
34
+ marginBottom:
35
+ toNumberWithDefaultZero(imageMargins.marginBottom) +
36
+ toNumberWithDefaultZero(value("cell_padding_bottom")),
37
+ zIndex: 1,
38
+ borderWidth: value("image_border_size"),
39
+ borderColor: ifElseFocusedSelected(
40
+ state,
41
+ value("image_focused_border_color"),
42
+ value("image_selected_border_color"),
43
+ value("image_focused_selected_border_color"),
44
+ value("image_border_color")
45
+ ),
46
+ borderRadius: toNumberWithDefaultZero(value("image_corner_radius")),
47
+ },
48
+ additionalProps: {
49
+ pointerEvents: "none",
50
+ borderPosition: value("image_border_position"),
51
+ borderPaddingTop: toNumberWithDefaultZero(
52
+ value("image_border_padding_top")
53
+ ),
54
+ borderPaddingRight: toNumberWithDefaultZero(
55
+ value("image_border_padding_right")
56
+ ),
57
+ borderPaddingBottom: toNumberWithDefaultZero(
58
+ value("image_border_padding_bottom")
59
+ ),
60
+ borderPaddingLeft: toNumberWithDefaultZero(
61
+ value("image_border_padding_left")
62
+ ),
63
+ },
64
+ elements: [
65
+ {
66
+ type: "View",
67
+ style: imageStyles,
68
+ },
69
+ ],
70
+ };
71
+ };
@@ -39,6 +39,6 @@ describe("image with no source", () => {
39
39
  );
40
40
 
41
41
  const testInstance = testRenderer.root;
42
- expect(testInstance.findByType(Image).props.source).toBeUndefined();
42
+ expect(testInstance.findByType(Image).props.source).toEqual({ uri: "" });
43
43
  });
44
44
  });
@@ -15,6 +15,7 @@ import { BorderContainerView } from "./BorderContainerView";
15
15
  import { CellBadge } from "./CellBadge";
16
16
  import { TvActionButtons } from "./tv/TvActionButtons";
17
17
  import { ButtonContainerView } from "./tv/ButtonContainerView";
18
+ import { ImageBorderContainer } from "./ImageBorderContainer";
18
19
 
19
20
  export const defaultComponents = {
20
21
  View,
@@ -28,6 +29,7 @@ export const defaultComponents = {
28
29
  ProgressBar,
29
30
  Toggle,
30
31
  LiveImage,
32
+ ImageBorderContainer,
31
33
  BorderContainerView,
32
34
  DynamicBadge,
33
35
  SecondaryImage,
@@ -5,6 +5,7 @@ import * as R from "ramda";
5
5
 
6
6
  import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
7
7
  import { normalizeProgressValue } from "./utils";
8
+ import { useIsScreenActive } from "@applicaster/zapp-react-native-utils/reactHooks";
8
9
 
9
10
  const ACTION_NAME = "continue-watching";
10
11
  const DEFAULT_FILL = 0;
@@ -27,6 +28,7 @@ const getProgressValue = (
27
28
 
28
29
  export const useFillInPercent = (entry: ZappEntry): number => {
29
30
  const action = useActions(ACTION_NAME);
31
+ const isActive = useIsScreenActive();
30
32
 
31
33
  const [fillInPercent, setFillInPercent] =
32
34
  React.useState<number>(DEFAULT_FILL);
@@ -39,6 +41,10 @@ export const useFillInPercent = (entry: ZappEntry): number => {
39
41
  }, []);
40
42
 
41
43
  React.useEffect(() => {
44
+ if (!isActive) {
45
+ return;
46
+ }
47
+
42
48
  // refresh progress in UI when it was changed in CW
43
49
  const unsubscribe = action?.addDataSourceListener?.(() => {
44
50
  getProgressValue(action, entry, (progress) => {
@@ -49,7 +55,7 @@ export const useFillInPercent = (entry: ZappEntry): number => {
49
55
  return () => {
50
56
  unsubscribe?.();
51
57
  };
52
- }, []);
58
+ }, [isActive]);
53
59
 
54
60
  return fillInPercent;
55
61
  };
@@ -37,14 +37,28 @@ function retrieveData(entry, func, args) {
37
37
  */
38
38
 
39
39
  export function configInflater(
40
- entry,
41
- { type, style, additionalProps = {}, data = [], elements }
40
+ entry: any,
41
+ {
42
+ type,
43
+ style,
44
+ additionalProps = {},
45
+ data = [],
46
+ elements,
47
+ }: {
48
+ type: any;
49
+ style: any;
50
+ additionalProps?: Record<string, any>;
51
+ data?: Array<{ propName: string; func: Function; args: any[] }>;
52
+ elements?: any[];
53
+ }
42
54
  ) {
43
- const props = R.reduce(
44
- (acc, curr) =>
45
- R.assoc(curr.propName, retrieveData(entry, curr.func, curr.args), acc),
46
- additionalProps,
47
- data
55
+ const props = data.reduce(
56
+ (acc, curr) => {
57
+ acc[curr.propName] = retrieveData(entry, curr.func, curr.args);
58
+
59
+ return acc;
60
+ },
61
+ { ...additionalProps }
48
62
  );
49
63
 
50
64
  let adjustedElements;
@@ -52,6 +52,8 @@ export function masterCellBuilder({
52
52
  const { View } = componentsMap;
53
53
  const elementsBuilder = dataAdapter(cellConfiguration);
54
54
 
55
+ // This is very expensive operation
56
+ // Keep calls to minimum
55
57
  const hasFocusableInside = hasFocusableInsideBuilder(elementsBuilder);
56
58
 
57
59
  /**
@@ -74,8 +76,8 @@ export function masterCellBuilder({
74
76
  const selectedState = screen?.selectedEntry;
75
77
 
76
78
  const entryIsSelected = React.useMemo(
77
- () => selectedState?.id === item?.id,
78
- [selectedState?.id]
79
+ () => !!(selectedState?.id && selectedState?.id === item?.id),
80
+ [selectedState?.id, item?.id]
79
81
  );
80
82
 
81
83
  const elements = React.useMemo(
@@ -84,7 +86,7 @@ export function masterCellBuilder({
84
86
  entry: item,
85
87
  state: getEntryState(state, entryIsSelected),
86
88
  }),
87
- [state, item, entryIsSelected]
89
+ [state, item?.id, entryIsSelected] // Assuming that item won't mutate
88
90
  );
89
91
 
90
92
  const wrapperRef = React.useRef(null);
@@ -53,10 +53,9 @@ function resolveColorForProp(entry, style, colorProp) {
53
53
  const colorFromProp = style[colorProp];
54
54
  const nestedEntryValue = R.path(colorFromProp.split("."), entry);
55
55
 
56
- if (
57
- nestedEntryValue === undefined &&
58
- !validateColor(colorFromProp.replace(".00", "")) // https://github.com/dreamyguy/validate-color/issues/44
59
- ) {
56
+ const color = colorFromProp.replace(".00", "").replace(".0", ""); // https://github.com/dreamyguy/validate-color/issues/44
57
+
58
+ if (nestedEntryValue === undefined && !validateColor(color)) {
60
59
  logWarning(colorFromProp, style, entry, nestedEntryValue);
61
60
  }
62
61
 
@@ -362,13 +362,15 @@ const PlayerContainerComponent = (props: Props) => {
362
362
  const toggleFullscreen = (event) => {
363
363
  const { fullscreen = false } = event || {};
364
364
 
365
- if (fullscreen) {
366
- playerEvent("onFullscreenPlayerWillPresent");
367
- navigator.fullscreenVideoModal();
368
- } else {
369
- playerEvent("onFullscreenPlayerWillDismiss");
370
- navigator.maximiseVideoModal();
371
- }
365
+ setTimeout(() => {
366
+ if (fullscreen) {
367
+ navigator.fullscreenVideoModal();
368
+ playerEvent("onFullscreenPlayerWillPresent");
369
+ } else {
370
+ navigator.maximiseVideoModal();
371
+ playerEvent("onFullscreenPlayerWillDismiss");
372
+ }
373
+ });
372
374
  };
373
375
 
374
376
  const onCellTap = (entry) => {
@@ -463,6 +465,10 @@ const PlayerContainerComponent = (props: Props) => {
463
465
 
464
466
  if (mode === VideoModalMode.FULLSCREEN) {
465
467
  toggleFullscreen({ fullscreen: false });
468
+
469
+ if (!isModal) {
470
+ playerManager.closeNativePlayer();
471
+ }
466
472
  }
467
473
 
468
474
  return !!(
@@ -519,7 +525,7 @@ const PlayerContainerComponent = (props: Props) => {
519
525
  }
520
526
 
521
527
  return () => {
522
- playerEvent("player_did_close");
528
+ playerEvent("player_did_close", { item });
523
529
 
524
530
  if (!isModal) {
525
531
  showNavBar(true);
@@ -1,3 +1 @@
1
1
  export { useInitialFocus } from "./useInitialFocus";
2
-
3
- export { useRestoreFocus } from "./useRestoreFocus";
@@ -1,12 +1,15 @@
1
- import * as React from "react";
1
+ import React from "react";
2
2
  import { last } from "ramda";
3
- import { View, StyleSheet } from "react-native";
3
+ import { StyleSheet, View } from "react-native";
4
4
  import {
5
5
  findMenuPlugin,
6
6
  getNavigationProps,
7
7
  getPathAttributes,
8
8
  } from "@applicaster/zapp-react-native-utils/navigationUtils";
9
- import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks";
9
+ import {
10
+ useIsScreenActive,
11
+ useNavigation,
12
+ } from "@applicaster/zapp-react-native-utils/reactHooks";
10
13
  import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
11
14
  import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
12
15
  import {
@@ -17,8 +20,10 @@ import { isNilOrEmpty } from "@applicaster/zapp-react-native-utils/reactUtils/he
17
20
 
18
21
  import { NavBarContainer } from "../../Layout/TV/NavBarContainer";
19
22
  import { ScreenResolver } from "../../ScreenResolver";
20
- import { useInitialFocus, useRestoreFocus } from "./hooks";
23
+ import { useInitialFocus } from "./hooks";
21
24
  import { isPlayerPlugin } from "@applicaster/zapp-react-native-utils/pluginUtils";
25
+ import { focusManager } from "@applicaster/zapp-react-native-utils/appUtils";
26
+ import { FreezeWithCallback } from "../../FreezeWithCallback";
22
27
 
23
28
  const styles = StyleSheet.create({
24
29
  container: {
@@ -103,7 +108,6 @@ export const Screen = ({ route, Components }: Props) => {
103
108
  const backgroundColor = useScreenBackgroundColor(screenId);
104
109
 
105
110
  useInitialFocus();
106
- useRestoreFocus();
107
111
 
108
112
  const activeRiver = getActiveRiver({
109
113
  screenId,
@@ -145,21 +149,30 @@ export const Screen = ({ route, Components }: Props) => {
145
149
  ? routeState?.state?.entry
146
150
  : routeState?.state?.screen;
147
151
 
152
+ const isScreenActive = useIsScreenActive();
153
+
148
154
  return (
149
- <View style={[styles.container, { backgroundColor }]}>
150
- <NavBarContainer isVisible={isNavBarVisible} onReady={noop}>
151
- <NavBar
152
- isVisible={isNavBarVisible}
153
- menuPlugin={menuPlugin}
154
- navigationProps={navigationProps}
155
+ <FreezeWithCallback
156
+ freeze={!isScreenActive}
157
+ onRelease={() => {
158
+ focusManager.setInitialFocus();
159
+ }}
160
+ >
161
+ <View style={[styles.container, { backgroundColor }]}>
162
+ <NavBarContainer isVisible={isNavBarVisible} onReady={noop}>
163
+ <NavBar
164
+ isVisible={isNavBarVisible}
165
+ menuPlugin={menuPlugin}
166
+ navigationProps={navigationProps}
167
+ />
168
+ </NavBarContainer>
169
+ <ScreenResolver
170
+ screenType={screenType}
171
+ screenId={screenId}
172
+ screenData={screenData}
173
+ groupId={route}
155
174
  />
156
- </NavBarContainer>
157
- <ScreenResolver
158
- screenType={screenType}
159
- screenId={screenId}
160
- screenData={screenData}
161
- groupId={route}
162
- />
163
- </View>
175
+ </View>
176
+ </FreezeWithCallback>
164
177
  );
165
178
  };
@@ -99,14 +99,6 @@ jest.mock("@applicaster/zapp-react-native-redux/hooks", () => ({
99
99
  }),
100
100
  }));
101
101
 
102
- const {
103
- ORIENTATIONS,
104
- } = require("@applicaster/zapp-react-native-utils/appUtils/orientationHelper");
105
-
106
- const {
107
- useCurrentScreenData,
108
- } = require("@applicaster/zapp-react-native-utils/reactHooks/screen");
109
-
110
102
  const {
111
103
  allowedOrientationsForScreen,
112
104
  getOrientation,
@@ -158,45 +150,4 @@ describe("<Screen Component />", () => {
158
150
  expect(wrapper.toJSON()).toMatchSnapshot();
159
151
  });
160
152
  });
161
-
162
- it("invokes the orientation hook when the route changes", () => {
163
- act(() => {
164
- wrapper = create(<Screen {...screenProps} />);
165
- });
166
-
167
- // @ts-ignore
168
- useCurrentScreenData.mockReturnValue({
169
- id: "player-testId",
170
- general: { screen_orientation: ORIENTATIONS.landscapeSensor },
171
- plugin_type: "player",
172
- });
173
-
174
- act(() => {
175
- wrapper.update(
176
- <Screen
177
- {...screenProps}
178
- pathname="/playable"
179
- screenData={{
180
- id: "player-testId",
181
- general: { screen_orientation: ORIENTATIONS.landscapeSensor },
182
- plugin_type: "player",
183
- }}
184
- />
185
- );
186
- });
187
-
188
- useCurrentScreenData.mockReturnValue({
189
- id: "player-testId",
190
- general: { screen_orientation: ORIENTATIONS.landscapeSensor },
191
- });
192
-
193
- act(() => {
194
- wrapper.update(<Screen {...screenProps} />);
195
- });
196
-
197
- expect(allowedOrientationsForScreen).toHaveBeenNthCalledWith(
198
- 1,
199
- ORIENTATIONS.landscapeSensor
200
- );
201
- });
202
153
  });