@applicaster/zapp-react-native-ui-components 12.0.0-alpha.5114122134 → 12.0.0-alpha.5251355865
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Components/AudioPlayer/AudioPlayer.tsx +9 -1
- package/Components/AudioPlayer/__tests__/__snapshots__/audioPlayer.test.js.snap +5 -0
- package/Components/AudioPlayer/helpers.tsx +1 -0
- package/Components/GeneralContentScreen/GeneralContentScreen.tsx +5 -5
- package/Components/HandlePlayable/HandlePlayable.tsx +1 -1
- package/Components/HookRenderer/index.tsx +1 -6
- package/Components/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +1 -41
- package/Components/Layout/TV/__tests__/index.test.tsx +1 -34
- package/Components/Layout/TV/index.tsx +20 -36
- package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +0 -1
- package/Components/MasterCell/DefaultComponents/CellBadge/index.tsx +21 -8
- package/Components/MasterCell/SharedUI/CollapsibleTextContainer/index.ts +8 -10
- package/Components/ModalComponent/BottomSheetModalContent.tsx +121 -0
- package/Components/ModalComponent/Button/Item.tsx +82 -0
- package/Components/ModalComponent/Button/ItemIcon.tsx +60 -0
- package/Components/ModalComponent/Button/ItemLabel.tsx +101 -0
- package/Components/ModalComponent/Button/assets.ts +4 -0
- package/Components/ModalComponent/Button/index.tsx +111 -0
- package/Components/ModalComponent/Header/CloseButton.tsx +59 -0
- package/Components/ModalComponent/Header/Title.tsx +70 -0
- package/Components/ModalComponent/Header/assets.ts +4 -0
- package/Components/ModalComponent/Header/index.tsx +71 -0
- package/Components/ModalComponent/Header/utils.ts +79 -0
- package/Components/ModalComponent/index.tsx +38 -0
- package/Components/ModalComponent/utils.ts +126 -0
- package/Components/River/ComponentsMap/ComponentsMap.tsx +25 -0
- package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +1 -1
- package/Components/RouteManager/__tests__/__snapshots__/routeManager.test.js.snap +1 -0
- package/Components/RouteManager/__tests__/routeManager.test.js +9 -2
- package/Components/RouteManager/index.tsx +4 -2
- package/Components/Screen/__tests__/Screen.test.tsx +83 -73
- package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +10 -6
- package/Components/Screen/__tests__/navigationHandler.test.ts +1 -178
- package/Components/Screen/index.tsx +84 -44
- package/Components/Screen/navigationHandler.ts +0 -82
- package/Components/ScreenResolver/index.tsx +1 -1
- package/Components/TrackedView/index.tsx +6 -3
- package/Components/VideoLive/LiveImageManager.ts +37 -10
- package/Components/VideoLive/PlayerLiveImageComponent.tsx +79 -73
- package/Components/VideoModal/PlayerDetails.tsx +3 -0
- package/Components/VideoModal/PlayerWrapper.tsx +1 -1
- package/Components/VideoModal/__tests__/PlayerDetails.test.tsx +0 -2
- package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +1 -2
- package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +211 -163
- package/Components/Viewport/ViewportAware/__tests__/viewportAware.test.js +26 -21
- package/Components/Viewport/ViewportTracker/__tests__/viewportTracker.test.js +4 -5
- package/Components/index.js +2 -0
- package/Contexts/ScreenContext/index.tsx +1 -1
- package/package.json +5 -5
|
@@ -1,36 +1,26 @@
|
|
|
1
1
|
/// <reference types="@applicaster/applicaster-types" />
|
|
2
|
-
import React
|
|
2
|
+
import React from "react";
|
|
3
3
|
import * as R from "ramda";
|
|
4
4
|
import { View } from "react-native";
|
|
5
|
-
import { RouteManager } from "../RouteManager";
|
|
6
|
-
import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
|
|
7
|
-
import { useScreenOrientationHandler } from "./orientationHandler";
|
|
8
|
-
import { useScreenConfiguration } from "../River/useScreenConfiguration";
|
|
9
|
-
import { useCurrentScreenData } from "@applicaster/zapp-react-native-utils/reactHooks/screen";
|
|
10
5
|
import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
|
|
11
|
-
import { ScreenContextProvider } from "../../Contexts/ScreenContext";
|
|
12
6
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
isRTL: boolean;
|
|
31
|
-
category: string;
|
|
32
|
-
} & QuickBrickAppNavigator;
|
|
33
|
-
};
|
|
7
|
+
import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
|
|
8
|
+
import { getComponentModule } from "@applicaster/zapp-react-native-utils/pluginUtils";
|
|
9
|
+
import { getNavigationPropsV2 } from "@applicaster/zapp-react-native-utils/navigationUtils";
|
|
10
|
+
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
11
|
+
import {
|
|
12
|
+
useDimensions,
|
|
13
|
+
useRoute,
|
|
14
|
+
useCurrentScreenData,
|
|
15
|
+
useNavbarState,
|
|
16
|
+
useScreenData,
|
|
17
|
+
} from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
18
|
+
import { getNavigationPluginModule } from "@applicaster/zapp-react-native-app/App/Layout/layoutHelpers";
|
|
19
|
+
|
|
20
|
+
import { ScreenContextProvider } from "../../Contexts/ScreenContext";
|
|
21
|
+
import { RouteManager } from "../RouteManager";
|
|
22
|
+
import { useScreenConfiguration } from "../River/useScreenConfiguration";
|
|
23
|
+
import { useScreenOrientationHandler } from "./orientationHandler";
|
|
34
24
|
|
|
35
25
|
function colorIsNotTransparent(color) {
|
|
36
26
|
const layers = R.compose(
|
|
@@ -49,25 +39,68 @@ function isValidColor(string) {
|
|
|
49
39
|
return string && string !== "transparent" && colorIsNotTransparent(string);
|
|
50
40
|
}
|
|
51
41
|
|
|
52
|
-
|
|
53
|
-
|
|
42
|
+
type Props = {
|
|
43
|
+
pathname: string;
|
|
44
|
+
screenData: unknown;
|
|
45
|
+
setToggleMenu: () => void;
|
|
46
|
+
setCloseMenu: () => void;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export function Screen({ setToggleMenu, setCloseMenu }: Props) {
|
|
50
|
+
const theme = useTheme<BaseThemePropertiesMobile>();
|
|
54
51
|
const { plugins } = usePickFromState(["plugins"]);
|
|
55
52
|
|
|
56
|
-
|
|
53
|
+
// Gets the data for the current screen configuration
|
|
54
|
+
const currentScreenData = useCurrentScreenData();
|
|
55
|
+
const { backgroundColor } = useScreenConfiguration(currentScreenData.id);
|
|
56
|
+
const insets = useSafeAreaInsets();
|
|
57
|
+
|
|
58
|
+
const { width: screenWidth } = useDimensions("screen", {
|
|
59
|
+
fullDimensions: true,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const routeData = useRoute();
|
|
63
|
+
|
|
64
|
+
const currentRiver = useScreenData(
|
|
65
|
+
(routeData.screenData as ZappRiver)?.targetScreen
|
|
66
|
+
? (routeData.screenData as ZappRiver)?.targetScreen.id
|
|
67
|
+
: (routeData.screenData as ZappRiver)?.screen_id
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
const { title } = useNavbarState();
|
|
71
|
+
|
|
72
|
+
const navBarProps = React.useMemo(
|
|
73
|
+
() => ({
|
|
74
|
+
...getNavigationPropsV2({
|
|
75
|
+
currentRiver,
|
|
76
|
+
title,
|
|
77
|
+
category: "nav_bar",
|
|
78
|
+
}),
|
|
79
|
+
setToggleMenu,
|
|
80
|
+
setCloseMenu,
|
|
81
|
+
}),
|
|
82
|
+
[currentRiver, setCloseMenu, setToggleMenu]
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
const NavBar = React.useMemo(
|
|
57
86
|
() =>
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
87
|
+
getNavigationPluginModule(
|
|
88
|
+
"nav_bar",
|
|
89
|
+
currentRiver,
|
|
90
|
+
plugins
|
|
91
|
+
) as React.ComponentType<MobileNavBarPluginProps>,
|
|
92
|
+
[]
|
|
63
93
|
);
|
|
64
94
|
|
|
65
|
-
const {
|
|
95
|
+
const { screenData, pathname } = routeData;
|
|
66
96
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
97
|
+
const offlinePlugin = getComponentModule({
|
|
98
|
+
plugins,
|
|
99
|
+
components: {},
|
|
100
|
+
componentType: "offline-experience",
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const { OfflineFallbackScreen } = offlinePlugin;
|
|
71
104
|
|
|
72
105
|
const style = React.useMemo(
|
|
73
106
|
() => ({
|
|
@@ -75,21 +108,28 @@ export function Screen(props: Props) {
|
|
|
75
108
|
backgroundColor: isValidColor(backgroundColor)
|
|
76
109
|
? backgroundColor
|
|
77
110
|
: theme.app_background_color,
|
|
111
|
+
paddingTop: 0,
|
|
112
|
+
// paddingBottom: insets.bottom, // Should be applied directly by UI plugin
|
|
113
|
+
// paddingLeft: insets.left, // Should be applied directly by UI plugin
|
|
114
|
+
// paddingRight: insets.right, // Should be applied directly by UI plugin
|
|
78
115
|
}),
|
|
79
|
-
[theme.app_background_color, backgroundColor]
|
|
116
|
+
[insets, screenWidth, theme.app_background_color, backgroundColor]
|
|
80
117
|
);
|
|
81
118
|
|
|
82
119
|
const targetScreenData =
|
|
83
|
-
currentScreenData || screenData?.targetScreen || screenData;
|
|
120
|
+
currentScreenData || (screenData as any)?.targetScreen || screenData;
|
|
84
121
|
|
|
85
122
|
useScreenOrientationHandler({ screenData: targetScreenData });
|
|
86
123
|
|
|
87
124
|
return (
|
|
88
125
|
<ScreenContextProvider pathname={pathname}>
|
|
89
126
|
<View style={style}>
|
|
90
|
-
<NavBar
|
|
127
|
+
<NavBar
|
|
128
|
+
{...{ ...(navBarProps as MobileNavBarPluginProps), id: pathname }}
|
|
129
|
+
/>
|
|
91
130
|
|
|
92
131
|
<OfflineFallbackScreen screenData={screenData}>
|
|
132
|
+
{/* RouteManager don't need these props, can it be removed ? */}
|
|
93
133
|
<RouteManager pathname={pathname} screenData={screenData} />
|
|
94
134
|
</OfflineFallbackScreen>
|
|
95
135
|
</View>
|
|
@@ -1,85 +1,3 @@
|
|
|
1
|
-
import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* This function helps to decide wether the navbar should be presented on the screen
|
|
5
|
-
* based on route and / or screen Data
|
|
6
|
-
*
|
|
7
|
-
* Currently the scenarios include
|
|
8
|
-
* - players => false
|
|
9
|
-
* - search screen plugin => false
|
|
10
|
-
* - hooks with `showNavBar: true` or `presentFullScreen` not set to true, defaults to no navbar for hook screens
|
|
11
|
-
* - screens with `allow_screen_plugin_presentation` in general property => false
|
|
12
|
-
*
|
|
13
|
-
* @param {String} route current route of the screen
|
|
14
|
-
* @param {Object} screenData payload associated with the currently presented screen
|
|
15
|
-
* @returns {Boolean}
|
|
16
|
-
*/
|
|
17
|
-
export function isNavBarVisible(
|
|
18
|
-
route,
|
|
19
|
-
screenData,
|
|
20
|
-
showNavBar,
|
|
21
|
-
canGoBack,
|
|
22
|
-
videoModalState: QuickBrickVideoModalState | null = null
|
|
23
|
-
) {
|
|
24
|
-
/* screenData is not actual navigator.data
|
|
25
|
-
* or navigator.screenData,
|
|
26
|
-
* but navigator.data.screen */
|
|
27
|
-
if (route.includes("playable")) {
|
|
28
|
-
return false;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
if (
|
|
32
|
-
videoModalState?.mode === "FULLSCREEN" &&
|
|
33
|
-
videoModalState?.visible === true
|
|
34
|
-
) {
|
|
35
|
-
return false;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (screenData?.type === "qb_search_screen" && !isTV()) {
|
|
39
|
-
return false;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/* Match if screen is Hook */
|
|
43
|
-
if (route.startsWith("/hooks/")) {
|
|
44
|
-
const module = screenData?.module;
|
|
45
|
-
|
|
46
|
-
if (module?.presentFullScreen) {
|
|
47
|
-
return false;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return module?.showNavBar ?? false;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
if (screenData?.hookPlugin) {
|
|
54
|
-
const hookPlugin = screenData?.hookPlugin?.module;
|
|
55
|
-
|
|
56
|
-
return (
|
|
57
|
-
hookPlugin?.showNavBar || hookPlugin?.presentFullScreen !== true || false
|
|
58
|
-
);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (
|
|
62
|
-
screenData?.general?.allow_screen_plugin_presentation ||
|
|
63
|
-
screenData?.general?.hide_app_nav_bar
|
|
64
|
-
) {
|
|
65
|
-
return false;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const hideSecondaryLevel = screenData?.navigations?.some(
|
|
69
|
-
(navigation) => navigation.styles?.hide_secondary_level_menu
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
if (canGoBack && hideSecondaryLevel) {
|
|
73
|
-
return false;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
if (typeof showNavBar === "boolean" && !showNavBar) {
|
|
77
|
-
return false;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
return true;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
1
|
/**
|
|
84
2
|
* This function helps to decide wether the menu should be presented on the screen
|
|
85
3
|
* based on route and / or screen Data
|
|
@@ -17,9 +17,12 @@ type TrackedViewProps = {
|
|
|
17
17
|
export const TrackedView = memo(function TrackedView(props: TrackedViewProps) {
|
|
18
18
|
const children = props.children;
|
|
19
19
|
|
|
20
|
-
const onPositionUpdated = useCallback(
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
const onPositionUpdated = useCallback(
|
|
21
|
+
(event: TrackedViewNativeProps) => {
|
|
22
|
+
props.onPositionUpdated(event.nativeEvent);
|
|
23
|
+
},
|
|
24
|
+
[props.onPositionUpdated]
|
|
25
|
+
);
|
|
23
26
|
|
|
24
27
|
if (isTV()) {
|
|
25
28
|
return <View {...(props as any)}>{children}</View>;
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
import { setUserCellPlayerMutedPreference } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/userCellPlayerMutedPreference";
|
|
7
7
|
import { loggerLiveImageManager } from "./loggerHelper";
|
|
8
8
|
import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
9
|
+
import { Component } from "react";
|
|
9
10
|
|
|
10
11
|
const { log_debug, log_info, log_error } = loggerLiveImageManager;
|
|
11
12
|
|
|
@@ -36,7 +37,8 @@ type Position = {
|
|
|
36
37
|
type LiveImageProps = {
|
|
37
38
|
player: Player;
|
|
38
39
|
playerId: string;
|
|
39
|
-
setMode
|
|
40
|
+
setMode?: (type: LiveImageType) => void;
|
|
41
|
+
component: Component;
|
|
40
42
|
// TODO: ...primary, powerCell, tvGallery, etc.
|
|
41
43
|
// type: string;
|
|
42
44
|
};
|
|
@@ -164,7 +166,7 @@ export class LiveImageManager implements PlayerLifecycleListener {
|
|
|
164
166
|
|
|
165
167
|
// Fake close event, because we unmount native view
|
|
166
168
|
item.player?.onPlayerClose();
|
|
167
|
-
item.setMode(LiveImageType.Image);
|
|
169
|
+
item.setMode?.(LiveImageType.Image);
|
|
168
170
|
|
|
169
171
|
if (item === this.currentlyPlaying) {
|
|
170
172
|
this.currentlyPlaying = null;
|
|
@@ -191,8 +193,7 @@ export class LiveImageManager implements PlayerLifecycleListener {
|
|
|
191
193
|
}
|
|
192
194
|
|
|
193
195
|
this.currentlyPlaying = item;
|
|
194
|
-
|
|
195
|
-
item.setMode(LiveImageType.Video);
|
|
196
|
+
item.setMode?.(LiveImageType.Video);
|
|
196
197
|
|
|
197
198
|
if (item.player.playerState.isReadyToPlay) {
|
|
198
199
|
item.player.play();
|
|
@@ -209,6 +210,11 @@ export class LiveImageManager implements PlayerLifecycleListener {
|
|
|
209
210
|
this.pauseItem(item);
|
|
210
211
|
};
|
|
211
212
|
|
|
213
|
+
public onLiveImageCompleted = (_item: LiveImage) => {
|
|
214
|
+
// TODO: Notify listeners that player has died
|
|
215
|
+
// Do not try look for new playable, otherwise you will restart video finish to play
|
|
216
|
+
};
|
|
217
|
+
|
|
212
218
|
public muteAll = () => {
|
|
213
219
|
log_debug("muteAll");
|
|
214
220
|
|
|
@@ -235,6 +241,14 @@ export class LiveImageManager implements PlayerLifecycleListener {
|
|
|
235
241
|
const playerItem = this.findNextPlayableItem();
|
|
236
242
|
|
|
237
243
|
if (playerItem) {
|
|
244
|
+
if (!playerItem.isFullyVisible) {
|
|
245
|
+
log_error(
|
|
246
|
+
`checkPlayerPosition: trying to start playback currently invisible item: ${playerInfo(
|
|
247
|
+
playerItem.player
|
|
248
|
+
)}`
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
|
|
238
252
|
// Will also check if it's item that already playing
|
|
239
253
|
this.playLiveImage(playerItem);
|
|
240
254
|
}
|
|
@@ -242,7 +256,6 @@ export class LiveImageManager implements PlayerLifecycleListener {
|
|
|
242
256
|
|
|
243
257
|
onPrimaryPlayerClosed = () => {
|
|
244
258
|
const playerItem = this.findNextPlayableItem();
|
|
245
|
-
|
|
246
259
|
log_info("onPrimaryPlayerClosed: starting to play visible live-image");
|
|
247
260
|
|
|
248
261
|
this.currentlyPlaying = null;
|
|
@@ -356,12 +369,12 @@ export class LiveImageManager implements PlayerLifecycleListener {
|
|
|
356
369
|
// TODO: This branch was used when we had play button to toggle replay.
|
|
357
370
|
// Now we mute button instead but we keep it just in case.
|
|
358
371
|
if (IS_ALLOWED_REPLAY && !isTV() && isCurrentItemEnded) {
|
|
359
|
-
this.currentlyPlaying
|
|
360
|
-
this.currentlyPlaying
|
|
372
|
+
this.currentlyPlaying?.player.seekTo(0);
|
|
373
|
+
this.currentlyPlaying?.player.pause();
|
|
361
374
|
|
|
362
375
|
// TODO: ...Maybe player some other item
|
|
363
376
|
} else {
|
|
364
|
-
item.setMode(LiveImageType.Image);
|
|
377
|
+
item.setMode?.(LiveImageType.Image);
|
|
365
378
|
}
|
|
366
379
|
|
|
367
380
|
// Prevent receiving onEnd event from both `close` and `end` player events
|
|
@@ -377,12 +390,25 @@ export class LiveImageManager implements PlayerLifecycleListener {
|
|
|
377
390
|
};
|
|
378
391
|
|
|
379
392
|
onLiveImageError = (item: LiveImage, error: Error) => {
|
|
380
|
-
item.setMode(LiveImageType.Image);
|
|
393
|
+
item.setMode?.(LiveImageType.Image);
|
|
381
394
|
|
|
382
395
|
const currentItem = this.currentlyPlaying;
|
|
383
396
|
|
|
397
|
+
log_debug(
|
|
398
|
+
`onLiveImageError: error: ${error.message}, live-image ${playerInfo(
|
|
399
|
+
item.player
|
|
400
|
+
)}, currentPlayingId: ${
|
|
401
|
+
this.currentlyPlaying?.playerId
|
|
402
|
+
}, primaryPlayerId: ${this.primaryPlayer?.playerId}`
|
|
403
|
+
);
|
|
404
|
+
|
|
384
405
|
if (currentItem === item) {
|
|
385
406
|
this.currentlyPlaying = null;
|
|
407
|
+
|
|
408
|
+
log_debug(
|
|
409
|
+
`onLiveImageError: currentitem: ${currentItem.playerId} was removed`
|
|
410
|
+
);
|
|
411
|
+
|
|
386
412
|
// TODO: ...Maybe player some other item
|
|
387
413
|
}
|
|
388
414
|
|
|
@@ -448,12 +474,13 @@ export class LiveImage implements QuickBrickPlayer.SharedPlayerCallBacks {
|
|
|
448
474
|
};
|
|
449
475
|
|
|
450
476
|
readonly playerId: string;
|
|
477
|
+
readonly component: Component;
|
|
451
478
|
|
|
452
479
|
constructor(props: LiveImageProps) {
|
|
453
480
|
this.player = props.player;
|
|
454
481
|
this.setMode = props.setMode;
|
|
455
482
|
this.playerId = this.player.playerId;
|
|
456
|
-
|
|
483
|
+
this.component = props.component;
|
|
457
484
|
this.player.addListener({ id: "live-image", listener: this });
|
|
458
485
|
}
|
|
459
486
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// /// <reference types="@applicaster/applicaster-types" />
|
|
2
2
|
//
|
|
3
3
|
import * as React from "react";
|
|
4
|
-
import * as R from "ramda";
|
|
5
4
|
|
|
6
5
|
import { AppState, AppStateStatus } from "react-native";
|
|
7
6
|
import { TrackedView } from "../TrackedView";
|
|
@@ -9,7 +8,6 @@ import { useDimensions } from "@applicaster/zapp-react-native-utils/reactHooks/l
|
|
|
9
8
|
|
|
10
9
|
import { playerFactory } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/playerFactory";
|
|
11
10
|
|
|
12
|
-
import { BufferAnimation } from "@applicaster/zapp-react-native-ui-components/Components/PlayerContainer/BufferAnimation";
|
|
13
11
|
import {
|
|
14
12
|
isApplePlatform,
|
|
15
13
|
isTV,
|
|
@@ -25,12 +23,13 @@ import { loggerLiveImageComponent } from "./loggerHelper";
|
|
|
25
23
|
import { usePlayer } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayer";
|
|
26
24
|
import { PlayerRole } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/conts";
|
|
27
25
|
import { getAutoplaySettings } from "./utils";
|
|
26
|
+
import { isString } from "@applicaster/zapp-react-native-utils/stringUtils";
|
|
27
|
+
import { BufferAnimation } from "../PlayerContainer/BufferAnimation";
|
|
28
28
|
|
|
29
29
|
const { log_error } = loggerLiveImageComponent;
|
|
30
30
|
|
|
31
|
-
const isMeasurement = (item) =>
|
|
32
|
-
|
|
33
|
-
};
|
|
31
|
+
const isMeasurement = (item: ZappEntry) =>
|
|
32
|
+
isString(item.id) && item.id.startsWith("pre-measurement-");
|
|
34
33
|
|
|
35
34
|
type Props = {
|
|
36
35
|
item: ZappEntry;
|
|
@@ -55,69 +54,66 @@ const PlayerLiveImageComponent = (props: Props) => {
|
|
|
55
54
|
isTV() ? false : getUserCellPlayerMutedPreference(props.audioMutedByDefault)
|
|
56
55
|
);
|
|
57
56
|
|
|
58
|
-
const [PlayerViewComponent, setPlayerViewComponent] = React.useState(null);
|
|
59
|
-
const Player: any = PlayerViewComponent || BufferAnimation;
|
|
60
|
-
|
|
61
57
|
const ref = React.useRef(null);
|
|
62
58
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
if (type ===
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
timeoutRef.current = setTimeout(() => {
|
|
78
|
-
setMode(type);
|
|
79
|
-
}, 1000) as unknown as NodeJS.Timeout;
|
|
80
|
-
} else {
|
|
81
|
-
if (timeoutRef.current) {
|
|
82
|
-
clearTimeout(timeoutRef.current);
|
|
83
|
-
}
|
|
59
|
+
/**
|
|
60
|
+
* Debounced setMode function.
|
|
61
|
+
*
|
|
62
|
+
* Sets to video with a 1sec delay. Set to image happens immediately and cancels the timeout.
|
|
63
|
+
*
|
|
64
|
+
* @param type video or image
|
|
65
|
+
*/
|
|
66
|
+
const setModeDebounced = React.useCallback((type: LiveImageType) => {
|
|
67
|
+
if (type === LiveImageType.Video) {
|
|
68
|
+
if (type === mode) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
84
71
|
|
|
72
|
+
timeoutRef.current = setTimeout(() => {
|
|
85
73
|
setMode(type);
|
|
74
|
+
}, 1000) as unknown as NodeJS.Timeout;
|
|
75
|
+
} else {
|
|
76
|
+
if (timeoutRef.current) {
|
|
77
|
+
clearTimeout(timeoutRef.current);
|
|
78
|
+
timeoutRef.current = null;
|
|
86
79
|
}
|
|
87
|
-
};
|
|
88
80
|
|
|
81
|
+
setMode(type);
|
|
82
|
+
}
|
|
83
|
+
}, [mode]);
|
|
84
|
+
|
|
85
|
+
const { screenConfig, playerPluginId } = props;
|
|
86
|
+
|
|
87
|
+
const liveImageItem: LiveImage = React.useMemo(() => {
|
|
89
88
|
const playerFactoryItem = playerFactory({
|
|
90
89
|
player: ref,
|
|
91
|
-
listener: props as Record<string, any>,
|
|
92
90
|
playerId,
|
|
93
91
|
autoplay: false,
|
|
94
92
|
entry: item,
|
|
95
|
-
muted,
|
|
96
|
-
playerPluginId:
|
|
97
|
-
screenConfig:
|
|
93
|
+
muted, // Initial muted state, not needed in dependencies
|
|
94
|
+
playerPluginId: playerPluginId,
|
|
95
|
+
screenConfig: screenConfig,
|
|
98
96
|
playerRole: PlayerRole.Cell,
|
|
99
97
|
});
|
|
100
98
|
|
|
101
|
-
const Component = playerFactoryItem?.Component;
|
|
102
|
-
|
|
103
|
-
if (!R.isNil(Component) && Component !== PlayerViewComponent) {
|
|
104
|
-
setPlayerViewComponent(Component);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
99
|
if (playerFactoryItem) {
|
|
108
100
|
return new LiveImage({
|
|
109
101
|
playerId,
|
|
110
102
|
player: playerFactoryItem.controller,
|
|
111
|
-
|
|
103
|
+
component: playerFactoryItem?.Component
|
|
112
104
|
});
|
|
113
105
|
} else {
|
|
114
106
|
throw new Error("Player factory item is null");
|
|
115
107
|
}
|
|
116
|
-
}, [playerId, item
|
|
108
|
+
}, [playerId, item, playerPluginId, screenConfig]);
|
|
109
|
+
|
|
110
|
+
React.useEffect(() => {
|
|
111
|
+
liveImageItem.setMode = setModeDebounced;
|
|
112
|
+
}, [setModeDebounced, liveImageItem]);
|
|
117
113
|
|
|
118
114
|
const { start, end } = React.useMemo(
|
|
119
115
|
() => getAutoplaySettings(item),
|
|
120
|
-
[item
|
|
116
|
+
[item]
|
|
121
117
|
);
|
|
122
118
|
|
|
123
119
|
const controller = liveImageItem.getPlayer();
|
|
@@ -127,6 +123,10 @@ const PlayerLiveImageComponent = (props: Props) => {
|
|
|
127
123
|
ref.current = component;
|
|
128
124
|
};
|
|
129
125
|
|
|
126
|
+
const onLiveImageCompleted = React.useCallback(() => {
|
|
127
|
+
LiveImageManager.instance.onLiveImageCompleted(liveImageItem);
|
|
128
|
+
}, [liveImageItem]);
|
|
129
|
+
|
|
130
130
|
const onStop = React.useCallback(() => {
|
|
131
131
|
LiveImageManager.instance.pauseLiveImage(liveImageItem);
|
|
132
132
|
}, [liveImageItem]);
|
|
@@ -140,13 +140,14 @@ const PlayerLiveImageComponent = (props: Props) => {
|
|
|
140
140
|
const unregister = LiveImageManager.instance.register(liveImageItem);
|
|
141
141
|
|
|
142
142
|
return () => {
|
|
143
|
-
unregister();
|
|
144
|
-
|
|
145
143
|
if (timeoutRef.current) {
|
|
146
144
|
clearTimeout(timeoutRef.current);
|
|
145
|
+
timeoutRef.current = null;
|
|
147
146
|
}
|
|
147
|
+
|
|
148
|
+
unregister();
|
|
148
149
|
};
|
|
149
|
-
}, [
|
|
150
|
+
}, [item, liveImageItem]);
|
|
150
151
|
|
|
151
152
|
React.useEffect(() => {
|
|
152
153
|
if (!player) {
|
|
@@ -178,11 +179,12 @@ const PlayerLiveImageComponent = (props: Props) => {
|
|
|
178
179
|
setMuted(false);
|
|
179
180
|
},
|
|
180
181
|
onPlayerClose: () => {
|
|
182
|
+
onLiveImageCompleted();
|
|
181
183
|
setIsReady(false);
|
|
182
184
|
},
|
|
183
185
|
},
|
|
184
186
|
});
|
|
185
|
-
}, [player, onStop]);
|
|
187
|
+
}, [player, onStop, onLiveImageCompleted, start, end]);
|
|
186
188
|
|
|
187
189
|
React.useEffect(() => {
|
|
188
190
|
const subscription = AppState.addEventListener(
|
|
@@ -233,40 +235,43 @@ const PlayerLiveImageComponent = (props: Props) => {
|
|
|
233
235
|
};
|
|
234
236
|
}
|
|
235
237
|
}
|
|
236
|
-
}, [liveImageItem]);
|
|
238
|
+
}, [liveImageItem, playerId, controller]);
|
|
237
239
|
|
|
238
|
-
const onPositionUpdated = (
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
240
|
+
const onPositionUpdated = React.useCallback(
|
|
241
|
+
(data) => {
|
|
242
|
+
const normalizedRect = (rect: {left: number, right: number, top: number, bottom: number }) => {
|
|
243
|
+
if (!rect) {
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
243
246
|
|
|
244
|
-
|
|
247
|
+
const { left, right, top, bottom } = rect;
|
|
245
248
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
249
|
+
return {
|
|
250
|
+
left: left / dimensions.width,
|
|
251
|
+
right: right / dimensions.width,
|
|
252
|
+
top: top / dimensions.height,
|
|
253
|
+
bottom: bottom / dimensions.height,
|
|
254
|
+
centerX: (left + right) / 2 / dimensions.width,
|
|
255
|
+
centerY: (top + bottom) / 2 / dimensions.height,
|
|
256
|
+
};
|
|
253
257
|
};
|
|
254
|
-
};
|
|
255
258
|
|
|
256
|
-
|
|
259
|
+
liveImageItem.position = normalizedRect(data.rect);
|
|
257
260
|
|
|
258
|
-
|
|
261
|
+
const newIsFullyVisible = !!data.rect;
|
|
259
262
|
|
|
260
|
-
|
|
261
|
-
|
|
263
|
+
if (liveImageItem.isFullyVisible !== newIsFullyVisible) {
|
|
264
|
+
liveImageItem.isFullyVisible = newIsFullyVisible;
|
|
262
265
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
266
|
+
liveImageItem.isFullyVisible
|
|
267
|
+
? LiveImageManager.instance.onViewportEnter(liveImageItem)
|
|
268
|
+
: LiveImageManager.instance.onViewportLeave(liveImageItem);
|
|
269
|
+
} else {
|
|
270
|
+
LiveImageManager.instance.checkPlayerPosition(liveImageItem);
|
|
271
|
+
}
|
|
272
|
+
},
|
|
273
|
+
[liveImageItem, dimensions]
|
|
274
|
+
);
|
|
270
275
|
|
|
271
276
|
const platformSpecificProps = isApplePlatform()
|
|
272
277
|
? { hideNativeControls: true, controls: false }
|
|
@@ -276,6 +281,7 @@ const PlayerLiveImageComponent = (props: Props) => {
|
|
|
276
281
|
const isVideoMode = mode === LiveImageType.Video;
|
|
277
282
|
const isBackgroundImageVisible = isImageMode || !isReadyToPlay;
|
|
278
283
|
const videoStyles = { ...style, position: "absolute", zIndex: -100 };
|
|
284
|
+
const Player: any = liveImageItem.component || BufferAnimation;
|
|
279
285
|
|
|
280
286
|
return (
|
|
281
287
|
<TrackedView
|
|
@@ -23,6 +23,8 @@ const containerStyle = ({
|
|
|
23
23
|
backgroundColor: modal_background_color,
|
|
24
24
|
});
|
|
25
25
|
|
|
26
|
+
// const audioMarginStyle = { marginTop: 0, marginBottom: 0 };
|
|
27
|
+
|
|
26
28
|
export const PlayerDetails = ({
|
|
27
29
|
entry,
|
|
28
30
|
style,
|
|
@@ -75,6 +77,7 @@ export const PlayerDetails = ({
|
|
|
75
77
|
transform: [{ translateY }],
|
|
76
78
|
opacity,
|
|
77
79
|
},
|
|
80
|
+
// isAudioPlayer && audioMarginStyle,
|
|
78
81
|
]}
|
|
79
82
|
>
|
|
80
83
|
{screenData && (
|