@applicaster/zapp-react-native-ui-components 12.0.0-alpha.7528906949 → 12.0.0-alpha.7531504237

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 (36) hide show
  1. package/Components/Cell/Cell.tsx +26 -5
  2. package/Components/Layout/TV/ScreenContainer.tsx +79 -5
  3. package/Components/Layout/TV/__tests__/ScreenContainer.test.tsx +38 -28
  4. package/Components/Layout/TV/__tests__/__snapshots__/ScreenContainer.test.tsx.snap +25 -13
  5. package/Components/Layout/TV/__tests__/index.test.tsx +2 -1
  6. package/Components/Layout/TV/index.tsx +11 -65
  7. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +2 -1
  8. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +1 -7
  9. package/Components/MasterCell/DefaultComponents/ImageBorderContainer/index.ts +57 -0
  10. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +20 -0
  11. package/Components/MasterCell/DefaultComponents/index.ts +2 -0
  12. package/Components/MasterCell/SharedUI/ProgressBar/hooks/useFillInPercent.ts +7 -1
  13. package/Components/MasterCell/dataAdapter.ts +21 -7
  14. package/Components/MasterCell/index.tsx +5 -3
  15. package/Components/Navigator/StackNavigator.tsx +4 -1
  16. package/Components/PlayerContainer/PlayerContainer.tsx +2 -5
  17. package/Components/Screen/TV/index.web.tsx +9 -30
  18. package/Components/Screen/index.tsx +27 -58
  19. package/Components/Screen/navigationHandler.ts +5 -0
  20. package/Components/ScreenResolver/index.tsx +6 -11
  21. package/Components/Transitioner/Scene.tsx +12 -11
  22. package/Components/Transitioner/Transitioner.tsx +17 -15
  23. package/Components/Transitioner/__tests__/__snapshots__/Scene.test.js.snap +8 -4
  24. package/Components/VideoLive/PlayerLiveImageComponent.tsx +52 -24
  25. package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +26 -24
  26. package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +2 -14
  27. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +31 -7
  28. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +9 -2
  29. package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +23 -5
  30. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +28 -8
  31. package/Components/VideoModal/ModalAnimation/utils.ts +1 -0
  32. package/Components/VideoModal/PlayerWrapper.tsx +1 -1
  33. package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +48 -2
  34. package/Components/VideoModal/hooks/useModalSize.ts +10 -12
  35. package/Components/default-cell-renderer/__tests__/__snapshots__/defaultCellRenderer.test.tsx.snap +4 -4
  36. package/package.json +5 -5
@@ -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);
@@ -9,6 +9,7 @@ import { PathnameContext } from "@applicaster/zapp-react-native-ui-components/Co
9
9
 
10
10
  import { Screen } from "../Screen/TV/index.web";
11
11
  import { isLast } from "@applicaster/zapp-react-native-utils/arrayUtils";
12
+ import { ScreenContextProvider } from "../../Contexts/ScreenContext";
12
13
 
13
14
  type Components = {
14
15
  NavBar: React.ComponentType<any>;
@@ -43,7 +44,9 @@ export const StackNavigator = ({ Components }: Props) => {
43
44
  >
44
45
  <ScreenDataContext.Provider value={state}>
45
46
  <PathnameContext.Provider value={route}>
46
- <Screen route={route} Components={Components} />
47
+ <ScreenContextProvider pathname={route}>
48
+ <Screen route={route} Components={Components} />
49
+ </ScreenContextProvider>
47
50
  </PathnameContext.Provider>
48
51
  </ScreenDataContext.Provider>
49
52
  </FocusableGroup>
@@ -124,6 +124,7 @@ const nativeStyles = {
124
124
  playerScreen: {
125
125
  flex: 1,
126
126
  backgroundColor: "black",
127
+ overflow: "hidden",
127
128
  },
128
129
  playerWrapper: {
129
130
  flex: 1,
@@ -252,11 +253,7 @@ const PlayerContainerComponent = (props: Props) => {
252
253
  }
253
254
 
254
255
  showNavBar(true);
255
-
256
- // Hack to close player screen on mobile only (tv handles that in InteractionManager)
257
- if (!isTV()) {
258
- navigator.goBack();
259
- }
256
+ navigator.goBack();
260
257
  }, [isModal, navigator.goBack, showNavBar]);
261
258
 
262
259
  const playEntry = (entry) => navigator.replaceTop(entry, { mode });
@@ -1,20 +1,24 @@
1
1
  import * as React from "react";
2
- import { last, pathOr } from "ramda";
2
+ import { last } from "ramda";
3
3
  import { View, StyleSheet } from "react-native";
4
4
  import {
5
+ findMenuPlugin,
5
6
  getNavigationProps,
6
7
  getPathAttributes,
7
- resolveNavigationPlugin,
8
8
  } from "@applicaster/zapp-react-native-utils/navigationUtils";
9
9
  import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks";
10
10
  import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
11
11
  import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
12
- import { useScreenBackgroundColor } from "@applicaster/zapp-react-native-utils/reactHooks/screen";
12
+ import {
13
+ useNavbarState,
14
+ useScreenBackgroundColor,
15
+ } from "@applicaster/zapp-react-native-utils/reactHooks/screen";
13
16
  import { isNilOrEmpty } from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
14
17
 
15
18
  import { NavBarContainer } from "../../Layout/TV/NavBarContainer";
16
19
  import { ScreenResolver } from "../../ScreenResolver";
17
20
  import { useInitialFocus, useRestoreFocus } from "./hooks";
21
+ import { isPlayerPlugin } from "@applicaster/zapp-react-native-utils/pluginUtils";
18
22
 
19
23
  const styles = StyleSheet.create({
20
24
  container: {
@@ -36,25 +40,6 @@ type Props = {
36
40
  Components: Components;
37
41
  };
38
42
 
39
- const getPluginType = pathOr("unknown_plugin_type", [
40
- "state",
41
- "screen",
42
- "plugin_type",
43
- ]);
44
-
45
- export const isPresentFullScreen = pathOr(false, [
46
- "state",
47
- "screen",
48
- "module",
49
- "presentFullScreen",
50
- ]);
51
-
52
- const isPlayerPlugin = (routeState): boolean =>
53
- getPluginType(routeState) === "player";
54
-
55
- const isLoginPlugin = (routeState): boolean =>
56
- getPluginType(routeState) === "login";
57
-
58
43
  const getActiveRiver = ({
59
44
  screenId,
60
45
  screenType,
@@ -146,20 +131,14 @@ export const Screen = ({ route, Components }: Props) => {
146
131
  fallbackNavigations: navigator.activeRiver.navigations,
147
132
  });
148
133
 
149
- const menuPlugin = resolveNavigationPlugin({
150
- category: "menu",
151
- navigations,
152
- plugins,
153
- }) as QuickBrickPlugin;
134
+ const menuPlugin = findMenuPlugin(navigations, plugins);
154
135
 
155
136
  const NavBar = (Components.NavBar ||
156
137
  menuPlugin.module) as React.ComponentType<any>;
157
138
 
158
139
  const isPlayer = isPlayerPlugin(routeState);
159
- const isLogin = isLoginPlugin(routeState);
160
- const isFullScreen = isPresentFullScreen(routeState);
161
140
 
162
- const isNavBarVisible = !isPlayer && !isLogin && !isFullScreen;
141
+ const { visible: isNavBarVisible } = useNavbarState();
163
142
 
164
143
  const screenData =
165
144
  screenType === "hooks" || isPlayer
@@ -11,10 +11,10 @@ import {
11
11
  import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
12
12
  import { getComponentModule } from "@applicaster/zapp-react-native-utils/pluginUtils";
13
13
  import {
14
- getNavigationPropsV2,
15
- resolveNavigationPlugin,
14
+ shouldNavBarDisplayMenu,
15
+ getNavBarProps,
16
+ getScreenId,
16
17
  } from "@applicaster/zapp-react-native-utils/navigationUtils";
17
- import { useSafeAreaInsets } from "react-native-safe-area-context";
18
18
  import {
19
19
  useDimensions,
20
20
  useRoute,
@@ -25,20 +25,16 @@ import {
25
25
  } from "@applicaster/zapp-react-native-utils/reactHooks";
26
26
  import { getNavigationPluginModule } from "@applicaster/zapp-react-native-app/App/Layout/layoutHelpers";
27
27
 
28
- import { ScreenContextProvider } from "../../Contexts/ScreenContext";
29
28
  import { RouteManager } from "../RouteManager";
30
29
  import { useScreenConfiguration } from "../River/useScreenConfiguration";
31
30
  import { useScreenOrientationHandler } from "./orientationHandler";
32
31
 
33
- const getScreenId = (
34
- screenData: LegacyNavigationScreenData,
35
- fallbackRiver: ZappRiver
36
- ) => {
37
- return screenData?.targetScreen
38
- ? screenData?.targetScreen?.id ?? fallbackRiver.id
39
- : "screen_id" in screenData
40
- ? screenData.screen_id
41
- : fallbackRiver.id;
32
+ const screenStyles = {
33
+ flex: 1,
34
+ paddingTop: 0,
35
+ // paddingBottom: insets.bottom, // Should be applied directly by UI plugin
36
+ // paddingLeft: insets.left, // Should be applied directly by UI plugin
37
+ // paddingRight: insets.right, // Should be applied directly by UI plugin
42
38
  };
43
39
 
44
40
  function colorIsNotTransparent(color) {
@@ -49,9 +45,7 @@ function colorIsNotTransparent(color) {
49
45
  R.replace("rgba(", "")
50
46
  )(color);
51
47
 
52
- const alpha = Number(layers?.[3]);
53
-
54
- return alpha > 0;
48
+ return Number(layers?.[3]) > 0;
55
49
  }
56
50
 
57
51
  function isValidColor(string) {
@@ -71,7 +65,6 @@ export function Screen(_props: Props) {
71
65
  // Gets the data for the current screen configuration
72
66
  const currentScreenData = useCurrentScreenData();
73
67
  const { backgroundColor } = useScreenConfiguration(currentScreenData.id);
74
- const insets = useSafeAreaInsets();
75
68
 
76
69
  const {
77
70
  width: screenWidth,
@@ -96,30 +89,12 @@ export function Screen(_props: Props) {
96
89
 
97
90
  const layoutData = { isTablet, isTabletPortrait, width: screenWidth, height };
98
91
 
99
- const hasMenu =
100
- resolveNavigationPlugin({
101
- category: "menu",
102
- navigations: currentRiver.navigations,
103
- plugins,
104
- })?.identifier === "quick_brick_side_menu";
105
-
106
- const navBarProps = React.useMemo<MobileNavBarPluginProps | null>(() => {
107
- const props = getNavigationPropsV2({
108
- currentRiver,
109
- title,
110
- category: "nav_bar",
111
- });
112
-
113
- if (props) {
114
- return {
115
- ...props,
116
- id: pathname,
117
- pathname: pathname,
118
- };
119
- }
120
-
121
- return null;
122
- }, [currentRiver, pathname]);
92
+ const hasMenu = shouldNavBarDisplayMenu(currentRiver, plugins);
93
+
94
+ const navBarProps = React.useMemo<MobileNavBarPluginProps | null>(
95
+ getNavBarProps(currentRiver, pathname, title),
96
+ [currentRiver, pathname]
97
+ );
123
98
 
124
99
  const NavBar = React.useMemo(
125
100
  () =>
@@ -141,16 +116,12 @@ export function Screen(_props: Props) {
141
116
 
142
117
  const style = React.useMemo(
143
118
  () => ({
144
- flex: 1,
119
+ ...screenStyles,
145
120
  backgroundColor: isValidColor(backgroundColor)
146
121
  ? backgroundColor
147
122
  : theme.app_background_color,
148
- paddingTop: 0,
149
- // paddingBottom: insets.bottom, // Should be applied directly by UI plugin
150
- // paddingLeft: insets.left, // Should be applied directly by UI plugin
151
- // paddingRight: insets.right, // Should be applied directly by UI plugin
152
123
  }),
153
- [insets, screenWidth, theme.app_background_color, backgroundColor]
124
+ [theme.app_background_color, backgroundColor]
154
125
  );
155
126
 
156
127
  const targetScreenData =
@@ -168,19 +139,17 @@ export function Screen(_props: Props) {
168
139
  layoutData,
169
140
  })
170
141
  ) {
171
- return null;
142
+ return <View style={style} />;
172
143
  }
173
144
 
174
145
  return (
175
- <ScreenContextProvider pathname={pathname}>
176
- <View style={style}>
177
- {navBarProps && <NavBar {...navBarProps} hasMenu={hasMenu} />}
178
-
179
- <OfflineFallbackScreen screenData={targetScreenData}>
180
- {/* @TODO RouteManager doesn't use props, can they be removed ? */}
181
- <RouteManager pathname={pathname} screenData={screenData} />
182
- </OfflineFallbackScreen>
183
- </View>
184
- </ScreenContextProvider>
146
+ <View style={style}>
147
+ {navBarProps && <NavBar {...navBarProps} hasMenu={hasMenu} />}
148
+
149
+ <OfflineFallbackScreen screenData={targetScreenData}>
150
+ {/* @TODO RouteManager doesn't use props, can they be removed ? */}
151
+ <RouteManager pathname={pathname} screenData={screenData} />
152
+ </OfflineFallbackScreen>
153
+ </View>
185
154
  );
186
155
  }
@@ -5,6 +5,11 @@
5
5
  * is similar to the navbar, except that it covers scenarios where only the navbar (and not)
6
6
  * the menu, will be hidden
7
7
  *
8
+ * - playable screens
9
+ * - qb_search_screen
10
+ * - screen hooks that specify showNavBar or presentFullScreen
11
+ * - screens or nested screens that have allow_screen_plugin_presentation set to true
12
+ *
8
13
  * @param {String} route current route of the screen
9
14
  * @param {Object} screenData payload associated with the currently presented screen
10
15
  * @returns {Boolean}
@@ -1,6 +1,5 @@
1
1
  import * as React from "react";
2
- import * as R from "ramda";
3
- import { useDispatch } from "react-redux";
2
+ import { path, prop } from "ramda";
4
3
  import {
5
4
  findPluginByType,
6
5
  findPluginByIdentifier,
@@ -15,7 +14,6 @@ import { componentsLogger } from "../../Helpers/logger";
15
14
  import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
16
15
  import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks";
17
16
  import { useScreenAnalytics } from "@applicaster/zapp-react-native-utils/analyticsUtils/helpers/hooks";
18
- import { setAppStateHasFinishedTransitioning } from "@applicaster/zapp-react-native-redux/appState";
19
17
 
20
18
  const logger = componentsLogger.addSubsystem("ScreenResolver");
21
19
 
@@ -54,11 +52,8 @@ export function ScreenResolverComponent(props: Props) {
54
52
 
55
53
  const [, setScreenContext] = ZappPipesScreenContext.useZappPipesContext();
56
54
 
57
- const dispatch = useDispatch();
58
-
59
55
  React.useEffect(() => {
60
56
  setScreenContext(rivers[screenId]);
61
- dispatch(setAppStateHasFinishedTransitioning());
62
57
  }, [screenId]);
63
58
 
64
59
  const ScreenPlugin =
@@ -100,14 +95,14 @@ export function ScreenResolverComponent(props: Props) {
100
95
  }
101
96
 
102
97
  const ScreenComponent =
103
- R.path(["module", "Component"], ScreenPlugin) ||
104
- R.prop("module", ScreenPlugin) ||
105
- R.prop("Component", ScreenPlugin) ||
98
+ path(["module", "Component"], ScreenPlugin) ||
99
+ prop("module", ScreenPlugin) ||
100
+ prop("Component", ScreenPlugin) ||
106
101
  ScreenPlugin;
107
102
 
108
103
  const configuration =
109
- R.prop("configuration", ScreenPlugin) ||
110
- R.prop("__plugin_configuration", ScreenComponent);
104
+ prop("configuration", ScreenPlugin) ||
105
+ prop("__plugin_configuration", ScreenComponent);
111
106
 
112
107
  if (ScreenComponent) {
113
108
  return (
@@ -1,13 +1,9 @@
1
1
  import React from "react";
2
- import {
3
- Animated,
4
- useWindowDimensions,
5
- ViewProps,
6
- ViewStyle,
7
- } from "react-native";
2
+ import { Animated, ViewProps, ViewStyle } from "react-native";
8
3
  import { useSafeAreaFrame } from "react-native-safe-area-context";
9
4
  import { PathnameContext } from "../../Contexts/PathnameContext";
10
5
  import { ScreenDataContext } from "../../Contexts/ScreenDataContext";
6
+ import { ScreenContextProvider } from "../../Contexts/ScreenContext";
11
7
 
12
8
  type Props = {
13
9
  children: any;
@@ -53,12 +49,15 @@ export function Scene({
53
49
  }, [screenData, isActive]);
54
50
 
55
51
  const frame = useSafeAreaFrame();
56
- const { height, width } = useWindowDimensions();
57
52
 
58
- // Safe frame takes too long to update,
59
- // so we can not rely on it there, and use height and width instead.
60
53
  const isAnimating = animating && overlayStyle;
61
- const dimensions = isAnimating ? { height, width } : frame;
54
+
55
+ const dimensions = isAnimating
56
+ ? {
57
+ height: "100%",
58
+ width: "100%",
59
+ }
60
+ : frame;
62
61
 
63
62
  return (
64
63
  <Animated.View
@@ -67,7 +66,9 @@ export function Scene({
67
66
  >
68
67
  <ScreenDataContext.Provider value={initialScreenData}>
69
68
  <PathnameContext.Provider value={pathname}>
70
- {children}
69
+ <ScreenContextProvider pathname={pathname}>
70
+ {children}
71
+ </ScreenContextProvider>
71
72
  </PathnameContext.Provider>
72
73
  </ScreenDataContext.Provider>
73
74
  {isAnimating && <Animated.View style={overlayStyle} />}
@@ -166,21 +166,23 @@ export class TransitionerComponent extends React.PureComponent<Props, State> {
166
166
 
167
167
  transitionWithoutAnimation(resetState) {
168
168
  const { scenes } = this.state;
169
-
170
- const newState = resetState
171
- ? {
172
- scenes: [prepareScene(this.props.children)],
173
- to: {
174
- index: 0,
175
- },
176
- }
177
- : {
178
- scenes: R.update(
179
- scenes.length - 1,
180
- prepareScene(this.props.children),
181
- scenes
182
- ),
183
- };
169
+ const newScene = prepareScene(this.props.children);
170
+ const isLastSceneHook = scenes[scenes.length - 1].key.startsWith("/hook");
171
+
172
+ let newState;
173
+
174
+ if (resetState) {
175
+ newState = {
176
+ scenes: [newScene],
177
+ to: { index: 0 },
178
+ };
179
+ } else if (isLastSceneHook || scenes.length === 1) {
180
+ newState = { scenes: R.update(scenes.length - 1, newScene, scenes) };
181
+ } else {
182
+ newState = {
183
+ scenes: R.compose(R.tail, R.append(newScene))(scenes),
184
+ };
185
+ }
184
186
 
185
187
  this.setState(newState);
186
188
  }
@@ -22,7 +22,9 @@ exports[`<Scene /> renders correctly 1`] = `
22
22
  }
23
23
  >
24
24
  <ContextProvider>
25
- <ContextProvider />
25
+ <ContextProvider>
26
+ <ScreenContextProvider />
27
+ </ContextProvider>
26
28
  </ContextProvider>
27
29
  </ForwardRef(AnimatedComponentWrapper)>
28
30
  `;
@@ -39,14 +41,16 @@ exports[`<Scene /> renders the overlay while animating 1`] = `
39
41
  "paddingBottom": 49,
40
42
  },
41
43
  Object {
42
- "height": 1334,
43
- "width": 750,
44
+ "height": "100%",
45
+ "width": "100%",
44
46
  },
45
47
  ]
46
48
  }
47
49
  >
48
50
  <ContextProvider>
49
- <ContextProvider />
51
+ <ContextProvider>
52
+ <ScreenContextProvider />
53
+ </ContextProvider>
50
54
  </ContextProvider>
51
55
  <ForwardRef(AnimatedComponentWrapper)
52
56
  style={
@@ -2,7 +2,7 @@
2
2
  //
3
3
  import * as React from "react";
4
4
 
5
- import { AppState, AppStateStatus } from "react-native";
5
+ import { AppState, AppStateStatus, View } from "react-native";
6
6
  import { TrackedView } from "../TrackedView";
7
7
  import { useDimensions } from "@applicaster/zapp-react-native-utils/reactHooks/layout";
8
8
 
@@ -25,8 +25,9 @@ import { PlayerRole } from "@applicaster/zapp-react-native-utils/appUtils/player
25
25
  import { getAutoplaySettings } from "./utils";
26
26
  import { isString } from "@applicaster/zapp-react-native-utils/stringUtils";
27
27
  import { BufferAnimation } from "../PlayerContainer/BufferAnimation";
28
+ import { useEffect } from "react";
28
29
 
29
- const { log_error } = loggerLiveImageComponent;
30
+ const { log_error, log_debug } = loggerLiveImageComponent;
30
31
 
31
32
  const isMeasurement = (item: ZappEntry) =>
32
33
  isString(item.id) && item.id.startsWith("pre-measurement-");
@@ -56,6 +57,7 @@ const PlayerLiveImageComponent = (props: Props) => {
56
57
  );
57
58
 
58
59
  const ref = React.useRef(null);
60
+ const trackViewRef = React.useRef(null);
59
61
 
60
62
  /**
61
63
  * Debounced setMode function.
@@ -86,6 +88,30 @@ const PlayerLiveImageComponent = (props: Props) => {
86
88
  [mode]
87
89
  );
88
90
 
91
+ const entryToViewIdMapping = React.useRef({});
92
+
93
+ useEffect(() => {
94
+ const nativeTag = trackViewRef.current?._nativeTag?.toString();
95
+
96
+ if (!nativeTag) {
97
+ return;
98
+ }
99
+
100
+ const prevValue = entryToViewIdMapping.current[nativeTag];
101
+
102
+ if (prevValue && prevValue.itemId !== item.id) {
103
+ if (mode === LiveImageType.Video) {
104
+ log_debug(
105
+ "Entry to view association changed, stopping current live image"
106
+ );
107
+
108
+ setMode(LiveImageType.Image);
109
+ }
110
+ }
111
+
112
+ entryToViewIdMapping.current[nativeTag] = { itemId: item.id };
113
+ }, [trackViewRef.current, item.id, mode]);
114
+
89
115
  const { screenConfig, playerPluginId } = props;
90
116
 
91
117
  const liveImageItem: LiveImage = React.useMemo(() => {
@@ -295,28 +321,30 @@ const PlayerLiveImageComponent = (props: Props) => {
295
321
  testId={`tracked-view-${playerId}-${item.title}`}
296
322
  onPositionUpdated={onPositionUpdated}
297
323
  >
298
- {isVideoMode && (
299
- <Player
300
- autoplay={false}
301
- ref={_assignRoot}
302
- entry={item} // Must be passed first in list
303
- style={videoStyles}
304
- playerId={playerId}
305
- muted={muted}
306
- listener={controller?.getListener()}
307
- resizeMode={"cover"}
308
- {...platformSpecificProps}
309
- />
310
- )}
311
- {
312
- <AnimatedInOut
313
- visible={isBackgroundImageVisible}
314
- animationConfig={overlayFadeIn}
315
- staticStyles={style}
316
- >
317
- <Image {...props} resizeMode="cover" style={style} applyMaxSize />
318
- </AnimatedInOut>
319
- }
324
+ <View ref={trackViewRef}>
325
+ {isVideoMode && (
326
+ <Player
327
+ autoplay={false}
328
+ ref={_assignRoot}
329
+ entry={item} // Must be passed first in list
330
+ style={videoStyles}
331
+ playerId={playerId}
332
+ muted={muted}
333
+ listener={controller?.getListener()}
334
+ resizeMode={"cover"}
335
+ {...platformSpecificProps}
336
+ />
337
+ )}
338
+ {
339
+ <AnimatedInOut
340
+ visible={isBackgroundImageVisible}
341
+ animationConfig={overlayFadeIn}
342
+ staticStyles={style}
343
+ >
344
+ <Image {...props} resizeMode="cover" style={style} applyMaxSize />
345
+ </AnimatedInOut>
346
+ }
347
+ </View>
320
348
  </TrackedView>
321
349
  );
322
350
  };
@@ -5,33 +5,35 @@ exports[`PlayerLiveImageComponent should render correctly with default props 1`]
5
5
  onPositionUpdated={[Function]}
6
6
  testId="tracked-view-player1-Test"
7
7
  >
8
- <View
9
- collapsable={false}
10
- style={
11
- Object {
12
- "opacity": 1,
13
- }
14
- }
15
- >
16
- <Image
17
- applyMaxSize={true}
18
- audioMutedByDefault={false}
19
- imageKey="image1"
20
- item={
8
+ <View>
9
+ <View
10
+ collapsable={false}
11
+ style={
21
12
  Object {
22
- "id": "1",
23
- "title": "Test",
24
- "type": Object {
25
- "value": "",
26
- },
13
+ "opacity": 1,
27
14
  }
28
15
  }
29
- onError={[Function]}
30
- playerId="player1"
31
- resizeMode="cover"
32
- screenConfig={Object {}}
33
- style={Object {}}
34
- />
16
+ >
17
+ <Image
18
+ applyMaxSize={true}
19
+ audioMutedByDefault={false}
20
+ imageKey="image1"
21
+ item={
22
+ Object {
23
+ "id": "1",
24
+ "title": "Test",
25
+ "type": Object {
26
+ "value": "",
27
+ },
28
+ }
29
+ }
30
+ onError={[Function]}
31
+ playerId="player1"
32
+ resizeMode="cover"
33
+ screenConfig={Object {}}
34
+ style={Object {}}
35
+ />
36
+ </View>
35
37
  </View>
36
38
  </TrackedView>
37
39
  `;