@applicaster/zapp-react-native-ui-components 16.0.0-rc.9 → 16.0.0-rc.91

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 (146) hide show
  1. package/Components/AudioPlayer/tv/__tests__/audioPlayer.test.js +19 -0
  2. package/Components/AudioPlayer/tv/__tests__/index.test.tsx +120 -0
  3. package/Components/AudioPlayer/tv/index.tsx +5 -1
  4. package/Components/Cell/Cell.tsx +3 -6
  5. package/Components/Cell/TvOSCellComponent.tsx +0 -1
  6. package/Components/CellRendererResolver/index.ts +2 -2
  7. package/Components/ComponentResolver/__tests__/componentResolver.test.js +1 -1
  8. package/Components/FocusableGroup/FocusableTvOS.tsx +11 -7
  9. package/Components/GeneralContentScreen/GeneralContentScreenHookAdapter.tsx +54 -0
  10. package/Components/GeneralContentScreen/__tests__/GeneralContentScreenHookAdapter.test.tsx +64 -0
  11. package/Components/GeneralContentScreen/__tests__/HookContentFocusGroup.web.test.tsx +91 -0
  12. package/Components/GeneralContentScreen/hookAdapter/__tests__/networkService.test.ts +74 -0
  13. package/Components/GeneralContentScreen/hookAdapter/__tests__/runInBackground.test.ts +139 -0
  14. package/Components/GeneralContentScreen/hookAdapter/__tests__/skipExpression.test.ts +294 -0
  15. package/Components/GeneralContentScreen/hookAdapter/__tests__/validationHelper.test.ts +188 -0
  16. package/Components/GeneralContentScreen/hookAdapter/logger.ts +6 -0
  17. package/Components/GeneralContentScreen/hookAdapter/networkService.ts +53 -0
  18. package/Components/GeneralContentScreen/hookAdapter/runInBackground.ts +48 -0
  19. package/Components/GeneralContentScreen/hookAdapter/skipExpression.ts +252 -0
  20. package/Components/GeneralContentScreen/hookAdapter/validationHelper.ts +90 -0
  21. package/Components/GeneralContentScreen/hookFocus/index.tsx +13 -0
  22. package/Components/GeneralContentScreen/hookFocus/index.web.tsx +69 -0
  23. package/Components/GeneralContentScreen/index.ts +2 -0
  24. package/Components/Layout/FullWidthRow.tsx +38 -0
  25. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +21 -6
  26. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/placement.test.ts +47 -21
  27. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/ActionButtonController.tsx +19 -4
  28. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/__tests__/ActionButtonController.test.tsx +43 -1
  29. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/placement.ts +34 -6
  30. package/Components/MasterCell/DefaultComponents/Button.tsx +1 -0
  31. package/Components/MasterCell/DefaultComponents/Image/hoc/utils/__tests__/withDimensions.test.ts +420 -125
  32. package/Components/MasterCell/DefaultComponents/Image/hoc/utils/index.ts +114 -19
  33. package/Components/MasterCell/DefaultComponents/Image/hoc/withDimensions.tsx +19 -8
  34. package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +5 -3
  35. package/Components/MasterCell/DefaultComponents/PressableView.tsx +29 -9
  36. package/Components/MasterCell/DefaultComponents/Text/hooks/useText.ts +4 -0
  37. package/Components/MasterCell/DefaultComponents/Toggle.tsx +36 -14
  38. package/Components/MasterCell/DefaultComponents/__tests__/Toggle.test.tsx +167 -0
  39. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/ActionButton.tsx +154 -100
  40. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/AssetComponent.tsx +15 -4
  41. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Button.ts +56 -22
  42. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Spacer.ts +6 -4
  43. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabelsContainer.ts +3 -1
  44. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/PressableView.test.tsx +32 -21
  45. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/index.test.ts +15 -4
  46. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/index.ts +23 -16
  47. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/__tests__/insertButtons.test.ts +8 -3
  48. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/index.ts +2 -2
  49. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/__tests__/insertButtonsBetweenLabels.test.ts +45 -13
  50. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/index.ts +2 -2
  51. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/__tests__/factory.test.ts +32 -0
  52. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/index.ts +1 -2
  53. package/Components/MasterCell/contexts/PressedStateContext.ts +3 -0
  54. package/Components/MasterCell/dataAdapter.ts +15 -5
  55. package/Components/MasterCell/hooks/index.ts +2 -0
  56. package/Components/MasterCell/hooks/usePressedState.ts +4 -0
  57. package/Components/MasterCell/index.tsx +30 -5
  58. package/Components/MasterCell/utils/__tests__/resolveColor.test.js +169 -132
  59. package/Components/MasterCell/utils/__tests__/resolveColorForProp.test.js +92 -0
  60. package/Components/MasterCell/utils/index.ts +85 -46
  61. package/Components/ModalComponent/ActionsBottomSheet/ActionButton.tsx +56 -0
  62. package/Components/ModalComponent/ActionsBottomSheet/__tests__/openActionsBottomSheet.test.ts +38 -0
  63. package/Components/ModalComponent/ActionsBottomSheet/boundActionButton.tsx +24 -0
  64. package/Components/ModalComponent/ActionsBottomSheet/index.ts +9 -0
  65. package/Components/ModalComponent/ActionsBottomSheet/openActionsBottomSheet.ts +61 -0
  66. package/Components/ModalComponent/AudioPlayer/Components/Button.tsx +392 -0
  67. package/Components/ModalComponent/AudioPlayer/Components/Header.tsx +728 -0
  68. package/Components/ModalComponent/AudioPlayer/Components/Input.tsx +628 -0
  69. package/Components/ModalComponent/AudioPlayer/Components/Item.tsx +982 -0
  70. package/Components/ModalComponent/AudioPlayer/Components/NowPlayingHeaderSection.tsx +94 -0
  71. package/Components/ModalComponent/AudioPlayer/Components/__tests__/Item.test.tsx +113 -0
  72. package/Components/ModalComponent/AudioPlayer/Components/index.ts +9 -0
  73. package/Components/ModalComponent/AudioPlayer/utils/__tests__/mergeStyles.test.ts +248 -0
  74. package/Components/ModalComponent/AudioPlayer/utils/mergeStyles.ts +386 -0
  75. package/Components/ModalComponent/BottomSheetDragArea.tsx +33 -0
  76. package/Components/ModalComponent/BottomSheetModalContent.tsx +419 -50
  77. package/Components/ModalComponent/Button/index.tsx +5 -3
  78. package/Components/ModalComponent/Header/CloseButton.tsx +12 -0
  79. package/Components/ModalComponent/Header/__tests__/CloseButton.test.tsx +72 -0
  80. package/Components/ModalComponent/Header/index.tsx +20 -7
  81. package/Components/ModalComponent/Header/utils.ts +1 -1
  82. package/Components/ModalComponent/ModalBlocksStyleContext.tsx +10 -0
  83. package/Components/ModalComponent/SortableList.tsx +157 -0
  84. package/Components/ModalComponent/SortableList.web.tsx +34 -0
  85. package/Components/ModalComponent/TextInputContent.tsx +112 -0
  86. package/Components/ModalComponent/__tests__/BottomSheetDragArea.test.tsx +74 -0
  87. package/Components/ModalComponent/__tests__/BottomSheetModalContent.test.tsx +270 -0
  88. package/Components/ModalComponent/__tests__/SortableList.web.test.tsx +16 -0
  89. package/Components/ModalComponent/__tests__/TextInputContent.test.tsx +156 -0
  90. package/Components/ModalComponent/__tests__/showTextInput.test.ts +95 -0
  91. package/Components/ModalComponent/index.tsx +18 -9
  92. package/Components/ModalComponent/presets/__tests__/getCell.test.ts +90 -0
  93. package/Components/ModalComponent/presets/dynamicCollectionCell.tsx +78 -0
  94. package/Components/ModalComponent/presets/index.ts +27 -0
  95. package/Components/ModalComponent/presets/selectableCell.tsx +28 -0
  96. package/Components/ModalComponent/presets/standardCell.tsx +41 -0
  97. package/Components/ModalComponent/presets/types.ts +35 -0
  98. package/Components/ModalComponent/showTextInput.ts +43 -0
  99. package/Components/ModalComponent/utils.ts +82 -21
  100. package/Components/OfflineHandler/__tests__/__snapshots__/index.test.tsx.snap +4 -85
  101. package/Components/OfflineHandler/__tests__/index.test.tsx +78 -18
  102. package/Components/OfflineHandler/hooks.ts +111 -0
  103. package/Components/OfflineHandler/index.tsx +51 -73
  104. package/Components/OfflineHandler/utils/index.ts +1 -13
  105. package/Components/PlayerContainer/PlayerContainer.tsx +25 -17
  106. package/Components/PlayerContainer/ProgramInfo/index.tsx +3 -4
  107. package/Components/PlayerContainer/__tests__/PlayerContainer.test.tsx +337 -0
  108. package/Components/PreloaderWrapper/index.tsx +1 -1
  109. package/Components/River/ComponentsMap/ComponentsMap.tsx +87 -61
  110. package/Components/River/River.tsx +4 -0
  111. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +140 -89
  112. package/Components/River/__tests__/componentsMap.test.js +8 -3
  113. package/Components/River/__tests__/resultCallback.test.tsx +47 -0
  114. package/Components/Screen/index.tsx +8 -4
  115. package/Components/Screen/navigationHandler.ts +8 -8
  116. package/Components/ScreenFeedLoader/ScreenFeedLoader.tsx +5 -3
  117. package/Components/ScreenResolverFeedProvider/ScreenResolverFeedProvider.tsx +1 -1
  118. package/Components/ScreenRevealManager/ScreenRevealManager.ts +18 -2
  119. package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +36 -0
  120. package/Components/ScreenRevealManager/__tests__/withScreenRevealManager.test.tsx +56 -0
  121. package/Components/ScreenRevealManager/withScreenRevealManager.tsx +21 -4
  122. package/Components/TopCutoffOverlay/index.tsx +2 -2
  123. package/Components/Transitioner/Transitioner.tsx +28 -17
  124. package/Components/VideoLive/LiveImageManager.ts +1 -1
  125. package/Components/VideoModal/utils.ts +6 -1
  126. package/Components/ZappFrameworkComponents/BarView/BarView.tsx +12 -5
  127. package/Components/ZappFrameworkComponents/BarView/__tests__/BarView.test.tsx +2 -2
  128. package/Contexts/CachedDimensionsContext/__tests__/index.test.ts +154 -0
  129. package/Contexts/CachedDimensionsContext/index.ts +17 -2
  130. package/Contexts/ModalNavigationContext/__tests__/index.test.tsx +15 -0
  131. package/Contexts/ModalNavigationContext/index.tsx +2 -2
  132. package/Helpers/ComponentCellSelectionHelper/index.js +0 -6
  133. package/Helpers/index.js +0 -39
  134. package/Hooks/useEntryActionsExpander.ts +63 -0
  135. package/package.json +6 -5
  136. package/Components/OfflineHandler/NotificationView/NotificationView.lg.tsx +0 -112
  137. package/Components/OfflineHandler/NotificationView/NotificationView.samsung.tsx +0 -107
  138. package/Components/OfflineHandler/NotificationView/NotificationView.tsx +0 -153
  139. package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +0 -66
  140. package/Components/OfflineHandler/NotificationView/utils.ts +0 -34
  141. package/Components/Screen/utils.ts +0 -16
  142. package/Helpers/Analytics/index.js +0 -95
  143. /package/Components/BackgroundImage/{BackgroundImage.tv.android.tsx → BackgroundImage.android.tv.tsx} +0 -0
  144. /package/Components/BackgroundImage/{BackgroundImage.tv.ios.tsx → BackgroundImage.ios.tv.tsx} +0 -0
  145. /package/Components/MasterCell/DefaultComponents/ButtonContainerView/{index.tv.android.tsx → index.android.tv.tsx} +0 -0
  146. /package/Decorators/ZappPipesDataConnector/__tests__/{Hero.js → Hero.tsx} +0 -0
@@ -0,0 +1,111 @@
1
+ import React, { useMemo } from "react";
2
+
3
+ import {
4
+ isWeb,
5
+ platformSelect,
6
+ } from "@applicaster/zapp-react-native-utils/reactUtils";
7
+ import { sessionStorage } from "@applicaster/zapp-react-native-bridge/ZappStorage/SessionStorage";
8
+
9
+ import { usePlugins } from "@applicaster/zapp-react-native-redux";
10
+ import { textTransform } from "@applicaster/zapp-react-native-utils/cellUtils";
11
+ import { findPluginByIdentifier } from "@applicaster/zapp-react-native-utils/pluginUtils";
12
+
13
+ import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
14
+
15
+ const OFFLINE_EXPERIENCE = "offline-experience";
16
+
17
+ const NETWORK_STATUS_LOCALIZATIONS_KEY = "network_status_localizations";
18
+
19
+ const THEME_STORAGE_NAMESPACE = "quick-brick-theme";
20
+
21
+ export function useNetworkStatusLocalizations() {
22
+ const [storedLocalizations, setStoredLocalizations] = React.useState<Record<
23
+ string,
24
+ string
25
+ > | null>(null);
26
+
27
+ React.useEffect(() => {
28
+ async function loadStoredLocalizations() {
29
+ try {
30
+ const stored = await sessionStorage.getItem(
31
+ NETWORK_STATUS_LOCALIZATIONS_KEY,
32
+ THEME_STORAGE_NAMESPACE
33
+ );
34
+
35
+ if (stored) {
36
+ setStoredLocalizations(stored);
37
+ }
38
+ } catch (error) {
39
+ console.error("Error loading network status localizations", error);
40
+ }
41
+ }
42
+
43
+ loadStoredLocalizations();
44
+ }, []);
45
+
46
+ return storedLocalizations;
47
+ }
48
+
49
+ export function useOfflineHandlerProps(online: boolean) {
50
+ const theme = useTheme<BaseThemePropertiesTV>();
51
+ const storedLocalizations = useNetworkStatusLocalizations();
52
+ const plugins = usePlugins();
53
+ const offlinePlugin = findPluginByIdentifier(OFFLINE_EXPERIENCE, plugins);
54
+ const { useOfflineExperienceConfiguration } = offlinePlugin?.module ?? {};
55
+
56
+ const {
57
+ configurationFields,
58
+ localizations,
59
+ }: {
60
+ configurationFields: OfflineExperiencePluginConfiguration;
61
+ localizations: OfflineExperienceLocalizations;
62
+ } = useOfflineExperienceConfiguration?.() ?? {};
63
+
64
+ let title, subtitle, style;
65
+
66
+ if (isWeb()) {
67
+ title = online
68
+ ? (theme?.online_notification_title ??
69
+ storedLocalizations?.online_notification_title)
70
+ : (theme?.offline_notification_title ??
71
+ storedLocalizations?.offline_notification_title);
72
+
73
+ subtitle = online
74
+ ? (theme?.online_notification_subtitle ??
75
+ storedLocalizations?.online_notification_subtitle)
76
+ : (theme?.offline_notification_subtitle ??
77
+ storedLocalizations?.offline_notification_subtitle);
78
+ } else {
79
+ title = textTransform(
80
+ configurationFields.offline_toast_message_text_transform,
81
+ online
82
+ ? localizations.online_toast_message
83
+ : localizations.offline_toast_message
84
+ );
85
+
86
+ style = {
87
+ color: online
88
+ ? configurationFields.offline_toast_message_online_font_color
89
+ : configurationFields.offline_toast_message_font_color,
90
+ backgroundColor: online
91
+ ? configurationFields.offline_toast_online_background_color
92
+ : configurationFields.offline_toast_offline_background_color,
93
+ fontFamily: platformSelect({
94
+ ios: configurationFields.offline_toast_message_ios_font_family,
95
+ android: configurationFields.offline_toast_message_android_font_family,
96
+ }),
97
+ fontSize: configurationFields.offline_toast_message_font_size,
98
+ lineHeight: configurationFields.offline_toast_message_line_height,
99
+ letterSpacing: platformSelect({
100
+ ios: configurationFields.offline_toast_message_letter_spacing_ios,
101
+ android:
102
+ configurationFields.offline_toast_message_letter_spacing_android,
103
+ }),
104
+ };
105
+ }
106
+
107
+ return useMemo(
108
+ () => ({ message: title, extraMessage: subtitle, style }),
109
+ [title, subtitle, style]
110
+ );
111
+ }
@@ -1,105 +1,83 @@
1
- import React, { useCallback, useEffect, useRef, useState } from "react";
2
- import { View, StyleSheet } from "react-native";
3
- import { NotificationView as NotificationViewNative } from "./NotificationView/NotificationView";
4
- import { NotificationView as NotificationViewSamsung } from "./NotificationView/NotificationView.samsung";
5
- import { NotificationView as NotificationViewLG } from "./NotificationView/NotificationView.lg";
6
-
1
+ import React, { useCallback, useEffect } from "react";
7
2
  import { useConnectionInfo } from "@applicaster/zapp-react-native-utils/reactHooks/connection";
8
3
  import { usePrevious } from "@applicaster/zapp-react-native-utils/reactHooks/utils";
9
- import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
4
+ import { useSubscriberFor } from "@applicaster/zapp-react-native-utils/reactHooks/useSubscriberFor";
10
5
 
6
+ import { useOfflineHandlerProps } from "./hooks";
11
7
  import { componentsLogger } from "../../Helpers/logger";
12
8
  import { parseConnectionInfo } from "./utils";
13
9
 
14
10
  const logger = componentsLogger.addSubsystem("OfflineHandler");
15
11
 
12
+ // mirrors NOTIFICATION_TOAST_EVENTS.SHOW_TOAST in
13
+ // @applicaster/quick-brick-core/App/NotificationToastRenderer/NotificationToastManager
14
+ // (kept as a literal here since this package must not depend on quick-brick-core)
15
+ const SHOW_TOAST_EVENT = "showToast";
16
+ const OFFLINE_STATUS_TOAST_ID = "offline-status";
17
+
16
18
  type Props = {
17
19
  children?: React.ReactElement<any>;
18
20
  };
19
21
 
20
- const styles = StyleSheet.create({
21
- container: {
22
- flex: 1,
23
- position: "relative",
24
- },
25
- });
26
-
27
22
  const timers = {
28
23
  goingOffline: 5000,
29
24
  goingOnline: 2000,
30
25
  };
31
26
 
32
- export const NotificationView = platformSelect({
33
- samsung_tv: NotificationViewSamsung,
34
- lg_tv: NotificationViewLG,
35
- default: NotificationViewNative,
36
- });
37
-
38
27
  export const OfflineHandler = ({ children }: Props) => {
39
28
  const connectionInfo = useConnectionInfo(false);
40
- const { online, deviceOnline } = parseConnectionInfo(connectionInfo);
29
+ const { online } = parseConnectionInfo(connectionInfo);
30
+ const prevOnline = usePrevious(online);
41
31
 
42
- const [hidden, setHidden] = useState<boolean>(true);
43
- const previousOnline: boolean = usePrevious(online);
44
- const timer = useRef<NodeJS.Timeout | null>(null);
45
- const initialRender = useRef(false);
32
+ const emitShowToast = useSubscriberFor(SHOW_TOAST_EVENT);
46
33
 
47
- const dismiss = useCallback(() => {
48
- setHidden(true);
34
+ const { message, extraMessage, style } = useOfflineHandlerProps(online);
49
35
 
50
- if (timer.current) {
51
- clearTimeout(timer.current);
52
- timer.current = null;
53
- }
54
- }, []);
36
+ const showOnlineToast = useCallback(() => {
37
+ logger.log("Device is back online, emit toast");
55
38
 
56
- const setTimer = useCallback((cb, duration) => {
57
- if (timer.current) {
58
- clearTimeout(timer.current);
59
- }
39
+ emitShowToast({
40
+ id: OFFLINE_STATUS_TOAST_ID,
41
+ message,
42
+ extraMessage,
43
+ style,
44
+ timeout: timers.goingOnline,
45
+ });
46
+ }, [emitShowToast, message, extraMessage, style]);
47
+
48
+ const showOfflineToast = useCallback(() => {
49
+ logger.log("Device went offline, emit toast");
60
50
 
61
- timer.current = setTimeout(cb, duration);
62
- }, []);
51
+ emitShowToast({
52
+ id: OFFLINE_STATUS_TOAST_ID,
53
+ message,
54
+ extraMessage,
55
+ style,
56
+ timeout: timers.goingOffline,
57
+ });
58
+ }, [emitShowToast, message, extraMessage, style]);
63
59
 
64
60
  useEffect(() => {
65
- if (initialRender.current) {
66
- const wentOffline = previousOnline && online === false;
67
- const wentOnline = !previousOnline && online;
68
- const renderedOffline = !online && previousOnline === null;
69
-
70
- if (!deviceOnline || wentOffline || renderedOffline) {
71
- logger.log("Device went offline");
72
- setTimer(() => setHidden(true), timers.goingOffline);
73
- setHidden(false);
61
+ // On mount there is no previous state yet: only surface the offline toast
62
+ // for a device that starts offline. Don't announce "back online" for a
63
+ // device that was online to begin with.
64
+ if (prevOnline === undefined) {
65
+ if (!online) {
66
+ showOfflineToast();
74
67
  }
75
68
 
76
- if (wentOnline) {
77
- logger.log("Device is back online");
78
- setHidden(false);
79
- setTimer(() => setHidden(true), timers.goingOnline);
80
- }
81
- } else {
82
- initialRender.current = true;
69
+ return;
83
70
  }
84
71
 
85
- return () => {
86
- if (timer.current) {
87
- clearTimeout(timer.current);
88
- timer.current = null;
72
+ // On subsequent renders, only emit a toast when the online/offline state changes.
73
+ if (prevOnline !== online) {
74
+ if (!online) {
75
+ showOfflineToast();
76
+ } else {
77
+ showOnlineToast();
89
78
  }
90
- };
91
- }, [connectionInfo]);
92
-
93
- return (
94
- <View style={styles.container}>
95
- <NotificationView
96
- hidden={hidden}
97
- online={online}
98
- previousOnline={previousOnline}
99
- dismiss={dismiss}
100
- >
101
- {children}
102
- </NotificationView>
103
- </View>
104
- );
79
+ }
80
+ }, [online, prevOnline, showOfflineToast, showOnlineToast]);
81
+
82
+ return children;
105
83
  };
@@ -1,9 +1,8 @@
1
1
  import * as R from "ramda";
2
-
3
2
  import { NetInfoState } from "@react-native-community/netinfo";
4
3
  import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
5
4
  import { isOnline as isOnlineUtils } from "@applicaster/quick-brick-core/App/NetworkStatusProvider/utils";
6
- import { useSafeAreaInsets } from "react-native-safe-area-context";
5
+
7
6
  import { Platform } from "react-native";
8
7
 
9
8
  type ConnectionInfo = NetInfoState & { deviceStatus: NetStatus };
@@ -51,14 +50,3 @@ export const parseConnectionInfo: typeof parseConnectionInfoForDefault =
51
50
  samsung_tv: parseConnectionInfoForSamsung,
52
51
  default: parseConnectionInfoForDefault,
53
52
  });
54
-
55
- export const useNotificationHeight = () => {
56
- const insets = useSafeAreaInsets();
57
-
58
- const navBarHeight = platformSelect({ ios: 44, android: 56 });
59
-
60
- const statusHeight = insets.top;
61
- const notificationHeight = statusHeight + navBarHeight;
62
-
63
- return { statusHeight, notificationHeight };
64
- };
@@ -9,6 +9,7 @@ import {
9
9
  platformSelect,
10
10
  isAndroidTVPlatform,
11
11
  isTvOSPlatform,
12
+ isApplePlatform,
12
13
  } from "@applicaster/zapp-react-native-utils/reactUtils";
13
14
  import {
14
15
  isAudioItem,
@@ -60,6 +61,7 @@ import {
60
61
  PlayerNativeSendCommand,
61
62
  } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/playerNativeCommand";
62
63
  import { useAppData } from "@applicaster/zapp-react-native-redux";
64
+ import { PLAYER_EVENTS } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/const";
63
65
 
64
66
  type Props = {
65
67
  Player: React.ComponentType<any>;
@@ -188,12 +190,10 @@ const renderApplePlayer = ({
188
190
  return null;
189
191
  }
190
192
 
191
- const { title, summary } = item || {};
192
-
193
+ // Title/Subtitle are not resolved here: `ProgramInfo` reads them from the
194
+ // player manager's content channel via `usePlayerContent`.
193
195
  return (
194
196
  <ProgramInfo
195
- title={title}
196
- subtitle={summary}
197
197
  plugin_configuration={pluginConfiguration}
198
198
  videoStyle={videoStyle}
199
199
  />
@@ -265,14 +265,12 @@ const PlayerContainerComponent = (props: Props) => {
265
265
  navigator.goBack();
266
266
  }, [isModal, state.playerId, showNavBar, navigator]);
267
267
 
268
- const pluginConfiguration = React.useMemo(() => {
269
- return (
270
- playerManager.getPluginConfiguration() ||
271
- R.prop("__plugin_configuration", Player)
272
- );
273
- }, [playerManager.isRegistered()]);
268
+ const pluginConfiguration = React.useMemo(
269
+ () => player?.getPluginConfiguration(),
270
+ [player]
271
+ );
274
272
 
275
- const playEntry = (entry) => navigator.replaceTop(entry, { mode });
273
+ const playEntry = (entry: ZappEntry) => navigator.replaceTop(entry, { mode });
276
274
 
277
275
  const onPlayNextPerformNextVideoPlay = React.useCallback(() => {
278
276
  if (!playNextOverlayState.entry) {
@@ -284,12 +282,16 @@ const PlayerContainerComponent = (props: Props) => {
284
282
  }
285
283
 
286
284
  log_debug(
287
- `onPlayNextPerformNextVideoPlay: delay completed, replaceTop to item: ${playNextOverlayState.entry.title}, id: ${playNextOverlayState.entry.id}`
285
+ `onPlayNextPerformNextVideoPlay: delay completed, replaceTop to item: ${playNextOverlayState.entry.title}, id: ${playNextOverlayState.entry.id}, outgoing item id: ${item?.id}`
288
286
  );
289
287
 
288
+ playNextOverlayState.hidePlayNext();
289
+
290
+ playerEvent(PLAYER_EVENTS.PlayNextTriggered, { id: item?.id });
291
+
290
292
  setIsLoadingNextVideo(true);
291
293
  playEntry(playNextOverlayState.entry);
292
- }, [playNextOverlayState]);
294
+ }, [playNextOverlayState, item?.id]);
293
295
 
294
296
  const playNextData: Nullable<PlayNextData> = React.useMemo(() => {
295
297
  if (!playNextOverlayState) {
@@ -346,11 +348,15 @@ const PlayerContainerComponent = (props: Props) => {
346
348
  setIsLoadingNextVideo(false);
347
349
 
348
350
  const resumeTime = Number(item?.extensions?.resumeTime);
351
+ const activePlayer = playerManager.getActivePlayer();
349
352
 
350
- // Сhecking that the player itself knows where to start playing, and there is no need to call seekTo after returning from the Сhromecast
353
+ // Сhecking that the player itself knows where to start playing, and there is no need to call seekTo after returning from the Сhromecast.
354
+ // Live streams and a missing/zero resume position are skipped: seeking to 0 on
355
+ // a live stream pulls playback off the live edge into a buffer-starvation seek loop.
351
356
  if (
352
- !playerManager.getActivePlayer()?.hasResumePosition() &&
353
- !isNaN(resumeTime)
357
+ !activePlayer?.hasResumePosition() &&
358
+ resumeTime > 0 &&
359
+ !activePlayer?.isLive()
354
360
  ) {
355
361
  player?.seekTo(resumeTime);
356
362
  }
@@ -468,6 +474,8 @@ const PlayerContainerComponent = (props: Props) => {
468
474
  if (isModal && mode === VideoModalMode.MAXIMIZED) {
469
475
  if (disableMiniPlayer) {
470
476
  navigator.closeVideoModal();
477
+ } else {
478
+ navigator.minimiseVideoModal();
471
479
  }
472
480
  }
473
481
 
@@ -491,7 +499,7 @@ const PlayerContainerComponent = (props: Props) => {
491
499
  const isAudioContent = isAudioItem(item);
492
500
 
493
501
  useEffect(() => {
494
- if (!isAudioContent) {
502
+ if (!isAudioContent && isApplePlatform()) {
495
503
  sendQuickBrickEvent(
496
504
  QUICK_BRICK_EVENTS.IDLE_TIMER_DISABLED as QuickBrickEvent,
497
505
  {
@@ -7,12 +7,11 @@ import * as React from "react";
7
7
  import { Text, TextStyle, View, ViewStyle } from "react-native";
8
8
  import { QBImage as Image } from "@applicaster/zapp-react-native-ui-components/Components/Image";
9
9
 
10
+ import { usePlayerContent } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayerContent";
10
11
  import * as programStyles from "./styles";
11
12
 
12
13
  type Props = {
13
14
  logo?: string;
14
- title: string;
15
- subtitle: string;
16
15
  isAudioContent: boolean;
17
16
  styles: {};
18
17
  plugin_configuration: {
@@ -28,13 +27,13 @@ const defaultColor = "#EFEFEF";
28
27
 
29
28
  function ProgramInfoComponent({
30
29
  logo,
31
- title = "",
32
- subtitle = "",
33
30
  isAudioContent,
34
31
  styles,
35
32
  plugin_configuration = {},
36
33
  videoStyle = {},
37
34
  }: Props) {
35
+ const { title = "", summary: subtitle = "" } = usePlayerContent();
36
+
38
37
  const platformKey = styleKeys.style_namespace;
39
38
 
40
39
  const colorTitle =