@applicaster/zapp-react-native-ui-components 15.0.0-rc.98 → 15.1.0-alpha.1852552014
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/BackgroundImage/BackgroundImage.tv.android.tsx +28 -0
- package/Components/BackgroundImage/BackgroundImage.tv.ios.tsx +24 -0
- package/Components/BackgroundImage/BackgroundImage.tv.web.tsx +42 -0
- package/Components/BackgroundImage/index.ts +8 -0
- package/Components/BaseFocusable/index.ios.ts +2 -12
- package/Components/Cell/FocusableWrapper.tsx +0 -3
- package/Components/Cell/TvOSCellComponent.tsx +0 -5
- package/Components/CellRendererResolver/index.ts +1 -1
- package/Components/ComponentResolver/__tests__/componentResolver.test.js +1 -1
- package/Components/Focusable/Focusable.tsx +2 -4
- package/Components/Focusable/FocusableTvOS.tsx +1 -18
- package/Components/Focusable/__tests__/__snapshots__/FocusableTvOS.test.tsx.snap +0 -1
- package/Components/FocusableGroup/FocusableTvOS.tsx +12 -37
- package/Components/GeneralContentScreen/GeneralContentScreen.tsx +39 -28
- package/Components/GeneralContentScreen/__tests__/GeneralContentScreen.test.tsx +104 -0
- package/Components/GeneralContentScreen/utils/__tests__/getScreenDataSource.test.ts +19 -0
- package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +1 -1
- package/Components/GeneralContentScreen/utils/getScreenDataSource.ts +9 -0
- package/Components/HandlePlayable/HandlePlayable.tsx +24 -42
- package/Components/HandlePlayable/utils.ts +31 -0
- package/Components/HookRenderer/HookRenderer.tsx +40 -10
- package/Components/HookRenderer/__tests__/HookRenderer.test.tsx +60 -0
- package/Components/Layout/FullWidthRow.tsx +38 -0
- package/Components/Layout/TV/ScreenContainer.tsx +11 -2
- package/Components/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +5 -1
- package/Components/Layout/TV/index.tsx +6 -6
- package/Components/Layout/TV/index.web.tsx +6 -6
- package/Components/LinkHandler/LinkHandler.tsx +2 -2
- package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +10 -4
- package/Components/MasterCell/DefaultComponents/Image/Image.android.tsx +1 -5
- package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +3 -11
- package/Components/MasterCell/DefaultComponents/Image/Image.web.tsx +1 -9
- package/Components/MasterCell/DefaultComponents/Image/hooks/useImage.ts +14 -15
- package/Components/MasterCell/DefaultComponents/LiveImage/__tests__/prepareEntry.test.ts +352 -0
- package/Components/MasterCell/DefaultComponents/LiveImage/executePreloadHooks.ts +136 -0
- package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +34 -16
- package/Components/MasterCell/DefaultComponents/SecondaryImage/hooks/__tests__/useGetImageDimensions.test.ts +6 -7
- package/Components/MasterCell/DefaultComponents/Text/index.tsx +2 -6
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/index.ts +2 -6
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/__tests__/getPluginIdentifier.test.ts +11 -233
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/index.ts +15 -19
- package/Components/MasterCell/dataAdapter.ts +15 -5
- package/Components/MasterCell/index.tsx +10 -1
- package/Components/MasterCell/utils/__tests__/resolveColor.test.js +169 -132
- package/Components/MasterCell/utils/__tests__/resolveColorForProp.test.js +92 -0
- package/Components/MasterCell/utils/index.ts +89 -46
- package/Components/Navigator/StackNavigator.tsx +6 -0
- package/Components/OfflineHandler/NotificationView/NotificationView.tsx +2 -2
- package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +18 -17
- package/Components/OfflineHandler/__tests__/index.test.tsx +18 -27
- package/Components/PlayerContainer/PlayerContainer.tsx +20 -34
- package/Components/PlayerContainer/__tests__/PlayerContainer.test.tsx +286 -0
- package/Components/PreloaderWrapper/__tests__/index.test.tsx +26 -0
- package/Components/PreloaderWrapper/index.tsx +15 -0
- package/Components/River/ComponentsMap/ComponentsMap.tsx +15 -0
- package/Components/River/ComponentsMap/hooks/__tests__/useLoadingState.test.ts +1 -1
- package/Components/River/RefreshControl.tsx +9 -3
- package/Components/River/RiverItem.tsx +26 -20
- package/Components/River/TV/River.tsx +14 -31
- package/Components/River/TV/index.tsx +4 -8
- package/Components/River/TV/withTVEventHandler.tsx +36 -0
- package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +1 -15
- package/Components/Screen/TV/hooks/__tests__/useAfterPaint.test.ts +60 -0
- package/Components/Screen/TV/hooks/index.ts +2 -0
- package/Components/Screen/TV/hooks/useAfterPaint.ts +23 -0
- package/Components/Screen/TV/index.web.tsx +18 -11
- package/Components/Screen/__tests__/Screen.test.tsx +43 -65
- package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +44 -68
- package/Components/Screen/hooks.ts +76 -5
- package/Components/Screen/index.tsx +18 -7
- package/Components/Screen/orientationHandler.ts +3 -3
- package/Components/ScreenFeedLoader/ScreenFeedLoader.tsx +46 -0
- package/Components/ScreenFeedLoader/__tests__/ScreenFeedLoader.test.tsx +94 -0
- package/Components/ScreenFeedLoader/index.ts +1 -0
- package/Components/ScreenResolver/__tests__/screenResolver.test.js +24 -0
- package/Components/ScreenResolver/hooks/index.ts +3 -0
- package/Components/ScreenResolver/hooks/useGetComponent.ts +15 -0
- package/Components/ScreenResolver/hooks/useScreenComponentResolver.tsx +90 -0
- package/Components/ScreenResolver/index.tsx +9 -115
- package/Components/ScreenResolver/utils/__tests__/getScreenTypeProps.test.ts +45 -0
- package/Components/ScreenResolver/utils/getScreenTypeProps.ts +43 -0
- package/Components/ScreenResolver/utils/index.ts +1 -0
- package/Components/ScreenResolver/withDefaultScreenContext.tsx +16 -0
- package/Components/ScreenResolverFeedProvider/ScreenResolverFeedProvider.tsx +25 -0
- package/Components/ScreenResolverFeedProvider/__tests__/ScreenResolverFeedProvider.test.tsx +44 -0
- package/Components/ScreenResolverFeedProvider/index.ts +1 -0
- package/Components/ScreenRevealManager/Overlay.tsx +34 -0
- package/Components/ScreenRevealManager/ScreenRevealManager.ts +8 -40
- package/Components/ScreenRevealManager/__tests__/Overlay.test.tsx +88 -0
- package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +69 -86
- package/Components/ScreenRevealManager/withScreenRevealManager.tsx +10 -18
- package/Components/Tabs/TabContent.tsx +4 -7
- package/Components/Transitioner/Scene.tsx +9 -15
- package/Components/Transitioner/index.js +3 -3
- package/Components/VideoLive/LiveImageManager.ts +209 -53
- package/Components/VideoLive/PlayerLiveImageComponent.tsx +32 -32
- package/Components/VideoLive/__tests__/PlayerLiveImageComponent.test.tsx +2 -17
- package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +5 -5
- package/Components/VideoModal/hooks/__tests__/useDelayedPlayerDetails.test.ts +7 -15
- package/Components/VideoModal/utils.ts +16 -13
- package/Components/Viewport/ViewportEvents/__tests__/viewportEvents.test.js +1 -1
- package/Components/ZappFrameworkComponents/BarView/BarView.tsx +12 -5
- package/Components/ZappFrameworkComponents/BarView/__tests__/BarView.test.tsx +2 -2
- package/Components/ZappUIComponent/index.tsx +12 -6
- package/Components/index.js +1 -1
- package/Contexts/CachedDimensionsContext/index.ts +17 -2
- package/Contexts/ScreenContext/__tests__/index.test.tsx +57 -0
- package/Contexts/ScreenContext/index.tsx +64 -26
- package/Contexts/ScreenTrackedViewPositionsContext/__tests__/index.test.tsx +1 -1
- package/Contexts/ZappPipesContext/ZappPipesContextFactory.tsx +18 -7
- package/Decorators/Analytics/index.tsx +5 -6
- package/Decorators/ConfigurationWrapper/__tests__/__snapshots__/withConfigurationProvider.test.tsx.snap +0 -1
- package/Decorators/ConfigurationWrapper/const.ts +0 -1
- package/Decorators/ZappPipesDataConnector/ResolverSelector.tsx +25 -7
- package/Decorators/ZappPipesDataConnector/__tests__/ResolverSelector.test.tsx +212 -5
- package/Decorators/ZappPipesDataConnector/__tests__/zappPipesDataConnector.test.js +1 -1
- package/Decorators/ZappPipesDataConnector/index.tsx +2 -2
- package/Decorators/ZappPipesDataConnector/resolvers/StaticFeedResolver.tsx +1 -1
- package/Helpers/ComponentCellSelectionHelper/index.js +0 -6
- package/Helpers/DataSourceHelper/index.js +19 -0
- package/Helpers/index.js +7 -40
- package/events/index.ts +2 -0
- package/events/scrollEndReached.ts +15 -0
- package/package.json +5 -5
- package/Components/Layout/TV/LayoutBackground.tsx +0 -31
- package/Components/MasterCell/DefaultComponents/Text/utils/__tests__/withAdjustedLineHeight.test.ts +0 -46
- package/Components/MasterCell/DefaultComponents/Text/utils/index.ts +0 -21
- package/Components/PlayerContainer/ErrorDisplay/ErrorDisplay.tsx +0 -57
- package/Components/PlayerContainer/ErrorDisplay/index.ts +0 -9
- package/Components/PlayerContainer/useRestrictMobilePlayback.tsx +0 -101
- package/Components/River/TV/utils/__tests__/toStringOrEmpty.test.ts +0 -30
- package/Components/River/TV/utils/index.ts +0 -4
- package/Components/River/TV/withFocusableGroupForContent.tsx +0 -71
- package/Components/Screen/utils.ts +0 -16
- package/Helpers/Analytics/index.js +0 -95
- package/Helpers/DataSourceHelper/__tests__/itemLimitForData.test.ts +0 -80
- package/Helpers/DataSourceHelper/index.ts +0 -19
- /package/Components/HookRenderer/{index.tsx → index.ts} +0 -0
|
@@ -4,9 +4,14 @@ import {
|
|
|
4
4
|
playerManager,
|
|
5
5
|
} from "@applicaster/zapp-react-native-utils/appUtils/playerManager";
|
|
6
6
|
import { setUserCellPlayerMutedPreference } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/userCellPlayerMutedPreference";
|
|
7
|
+
import { playerFactory } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/playerFactory";
|
|
8
|
+
import { PlayerRole } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/conts";
|
|
7
9
|
import { loggerLiveImageManager } from "./loggerHelper";
|
|
8
10
|
import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
9
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
executePreloadHooks,
|
|
13
|
+
PreloadHookConfig,
|
|
14
|
+
} from "../MasterCell/DefaultComponents/LiveImage/executePreloadHooks";
|
|
10
15
|
|
|
11
16
|
const TIMEOUT_FOR_DELAY_CHECK_PLAYER_POSITION = 500; // ms
|
|
12
17
|
|
|
@@ -40,13 +45,20 @@ type Position = {
|
|
|
40
45
|
right: number;
|
|
41
46
|
};
|
|
42
47
|
|
|
48
|
+
type PlayerFactoryConfig = {
|
|
49
|
+
player: any; // React ref for native view
|
|
50
|
+
playerId: string;
|
|
51
|
+
playerPluginId: string;
|
|
52
|
+
screenConfig: Record<string, any>;
|
|
53
|
+
entry: ZappEntry; // original entry, used as fallback if no hooks
|
|
54
|
+
};
|
|
55
|
+
|
|
43
56
|
type LiveImageProps = {
|
|
44
|
-
player: Player;
|
|
45
57
|
playerId: string;
|
|
46
58
|
setMode?: (type: LiveImageType) => void;
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
59
|
+
preloadHooks?: PreloadHookConfig[];
|
|
60
|
+
factoryConfig: PlayerFactoryConfig;
|
|
61
|
+
tag: string;
|
|
50
62
|
};
|
|
51
63
|
|
|
52
64
|
// Disabled because we have only unmute button but no play/pause state anymore
|
|
@@ -85,7 +97,7 @@ export class LiveImageManager implements PlayerLifecycleListener {
|
|
|
85
97
|
|
|
86
98
|
public register = (item: LiveImage): (() => void) => {
|
|
87
99
|
this.items.push(item);
|
|
88
|
-
log_debug(`register: live image ${
|
|
100
|
+
log_debug(`register: live image ${item.playerId} - ${item.tag}`);
|
|
89
101
|
|
|
90
102
|
// TV only Start playing video once registered
|
|
91
103
|
if (isTV()) {
|
|
@@ -96,15 +108,13 @@ export class LiveImageManager implements PlayerLifecycleListener {
|
|
|
96
108
|
};
|
|
97
109
|
|
|
98
110
|
public unregister = (item: LiveImage) => {
|
|
99
|
-
log_debug(`unregister: live-image ${
|
|
111
|
+
log_debug(`unregister: live-image ${item.playerId} - ${item.tag}`);
|
|
100
112
|
|
|
101
113
|
if (this.currentlyPlaying === item) {
|
|
102
114
|
this.currentlyPlaying = null;
|
|
103
115
|
|
|
104
116
|
log_debug(
|
|
105
|
-
`unregister: currently playing live-image was destroyed, ${
|
|
106
|
-
item.player
|
|
107
|
-
)}`
|
|
117
|
+
`unregister: currently playing live-image was destroyed, ${item.playerId} - ${item.tag}`
|
|
108
118
|
);
|
|
109
119
|
|
|
110
120
|
// TODO: Maybe start another one
|
|
@@ -118,7 +128,7 @@ export class LiveImageManager implements PlayerLifecycleListener {
|
|
|
118
128
|
item,
|
|
119
129
|
playerId: this.currentlyPlaying?.playerId,
|
|
120
130
|
primaryPlayerId: this.primaryPlayer?.playerId,
|
|
121
|
-
entry: item.getPlayer()
|
|
131
|
+
entry: item.getPlayer()?.getEntry(),
|
|
122
132
|
},
|
|
123
133
|
});
|
|
124
134
|
};
|
|
@@ -130,9 +140,7 @@ export class LiveImageManager implements PlayerLifecycleListener {
|
|
|
130
140
|
|
|
131
141
|
public onViewportEnter = (item: LiveImage) => {
|
|
132
142
|
log_debug(
|
|
133
|
-
`onViewportEnter: live-image ${playerInfo(
|
|
134
|
-
item.player
|
|
135
|
-
)}, primary ${playerInfo(this.primaryPlayer)}`
|
|
143
|
+
`onViewportEnter: live-image ${item.playerId} - ${item.tag}, primary ${playerInfo(this.primaryPlayer)} - position:${item.positionToString()}`
|
|
136
144
|
);
|
|
137
145
|
|
|
138
146
|
if (!isTV()) {
|
|
@@ -154,9 +162,7 @@ export class LiveImageManager implements PlayerLifecycleListener {
|
|
|
154
162
|
|
|
155
163
|
public onViewportLeave = (item: LiveImage) => {
|
|
156
164
|
log_debug(
|
|
157
|
-
`onViewportLeave: live-image playerId
|
|
158
|
-
item.player
|
|
159
|
-
)}, primary ${playerInfo(this.primaryPlayer)}`
|
|
165
|
+
`onViewportLeave: live-image ${item.playerId} - ${item.tag}, primary ${playerInfo(this.primaryPlayer)} - position:${item.positionToString()}`
|
|
160
166
|
);
|
|
161
167
|
|
|
162
168
|
this.pauseItem(item);
|
|
@@ -190,20 +196,29 @@ export class LiveImageManager implements PlayerLifecycleListener {
|
|
|
190
196
|
this.items.find((i) => i.playerId === playerId) || null;
|
|
191
197
|
|
|
192
198
|
private pauseItem = (item: LiveImage) => {
|
|
193
|
-
log_debug(`pauseItem: live-image ${
|
|
199
|
+
log_debug(`pauseItem: live-image ${item.playerId} - ${item.tag}`);
|
|
200
|
+
|
|
201
|
+
if (!item.player) {
|
|
202
|
+
// Player not yet created (e.g. hooks still running) — just reset mode
|
|
203
|
+
item.setMode?.(LiveImageType.Image);
|
|
204
|
+
|
|
205
|
+
if (item === this.currentlyPlaying) {
|
|
206
|
+
this.currentlyPlaying = null;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
194
211
|
|
|
195
212
|
if (!item.player.playerState.isReadyToPlay) {
|
|
196
213
|
log_debug(
|
|
197
|
-
`
|
|
198
|
-
item.player
|
|
199
|
-
)}`
|
|
214
|
+
`pauseItem: live-image not ready, ${item.playerId} - ${item.tag}`
|
|
200
215
|
);
|
|
201
216
|
} else {
|
|
202
|
-
item.player
|
|
217
|
+
item.player.pause();
|
|
203
218
|
}
|
|
204
219
|
|
|
205
220
|
// Fake close event, because we unmount native view
|
|
206
|
-
item.player
|
|
221
|
+
item.player.onPlayerClose();
|
|
207
222
|
item.setMode?.(LiveImageType.Image);
|
|
208
223
|
|
|
209
224
|
if (item === this.currentlyPlaying) {
|
|
@@ -213,9 +228,7 @@ export class LiveImageManager implements PlayerLifecycleListener {
|
|
|
213
228
|
|
|
214
229
|
public playLiveImage = (item: LiveImage) => {
|
|
215
230
|
log_debug(
|
|
216
|
-
`playLiveImage: live-image ${playerInfo(
|
|
217
|
-
item.player
|
|
218
|
-
)}, primary ${playerInfo(this.primaryPlayer)}`
|
|
231
|
+
`playLiveImage: live-image ${item.playerId} - ${item.tag}, primary ${playerInfo(this.primaryPlayer)}`
|
|
219
232
|
);
|
|
220
233
|
|
|
221
234
|
if (this.primaryPlayer) {
|
|
@@ -223,7 +236,7 @@ export class LiveImageManager implements PlayerLifecycleListener {
|
|
|
223
236
|
}
|
|
224
237
|
|
|
225
238
|
if (this.currentlyPlaying) {
|
|
226
|
-
if (this.currentlyPlaying
|
|
239
|
+
if (this.currentlyPlaying.playerId === item.playerId) {
|
|
227
240
|
return;
|
|
228
241
|
} else {
|
|
229
242
|
this.pauseItem(this.currentlyPlaying);
|
|
@@ -231,18 +244,42 @@ export class LiveImageManager implements PlayerLifecycleListener {
|
|
|
231
244
|
}
|
|
232
245
|
|
|
233
246
|
this.currentlyPlaying = item;
|
|
234
|
-
item.setMode?.(LiveImageType.Video);
|
|
235
247
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
248
|
+
item
|
|
249
|
+
.prepareForPlayback()
|
|
250
|
+
.then((result) => {
|
|
251
|
+
if (!result) {
|
|
252
|
+
log_error(
|
|
253
|
+
`Failed to prepare live image ${item.playerId} - ${item.tag} for playback: prepareForPlayback returned false`
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
this.currentlyPlaying = null;
|
|
257
|
+
item.setMode?.(LiveImageType.Image);
|
|
258
|
+
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Guard: item might have been replaced while hooks were running
|
|
263
|
+
if (this.currentlyPlaying !== item) return;
|
|
264
|
+
|
|
265
|
+
item.setMode?.(LiveImageType.Video);
|
|
266
|
+
|
|
267
|
+
if (item.player?.playerState.isReadyToPlay) {
|
|
268
|
+
item.player.play();
|
|
269
|
+
}
|
|
270
|
+
})
|
|
271
|
+
.catch((error) => {
|
|
272
|
+
log_error(
|
|
273
|
+
`Failed to prepare live image ${item.playerId} - ${item.tag} for playback: ${error?.message}`
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
this.onLiveImageError(item, error);
|
|
277
|
+
});
|
|
239
278
|
};
|
|
240
279
|
|
|
241
280
|
public pauseLiveImage = (item: LiveImage) => {
|
|
242
281
|
log_debug(
|
|
243
|
-
`pauseLiveImage: live-image playerId
|
|
244
|
-
item.player
|
|
245
|
-
)}, primary ${playerInfo(this.primaryPlayer)}`
|
|
282
|
+
`pauseLiveImage: live-image ${item.playerId} - ${item.tag}, primary ${playerInfo(this.primaryPlayer)}`
|
|
246
283
|
);
|
|
247
284
|
|
|
248
285
|
this.pauseItem(item);
|
|
@@ -258,7 +295,7 @@ export class LiveImageManager implements PlayerLifecycleListener {
|
|
|
258
295
|
|
|
259
296
|
setUserCellPlayerMutedPreference(true);
|
|
260
297
|
|
|
261
|
-
this.items.forEach((liveImage) => liveImage.
|
|
298
|
+
this.items.forEach((liveImage) => liveImage.mute());
|
|
262
299
|
};
|
|
263
300
|
|
|
264
301
|
public unmuteAll = () => {
|
|
@@ -266,16 +303,36 @@ export class LiveImageManager implements PlayerLifecycleListener {
|
|
|
266
303
|
|
|
267
304
|
setUserCellPlayerMutedPreference(false);
|
|
268
305
|
|
|
269
|
-
this.items.forEach((liveImage) => liveImage.
|
|
306
|
+
this.items.forEach((liveImage) => liveImage.unmute());
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
public onViewPositionChanged = (item: LiveImage) => {
|
|
310
|
+
log_debug(
|
|
311
|
+
`onViewPositionChanged: live-image ${item.playerId} - ${item.tag}, primary ${playerInfo(this.primaryPlayer)} - position:${item.positionToString()}`
|
|
312
|
+
);
|
|
313
|
+
|
|
314
|
+
if (!isTV()) {
|
|
315
|
+
// mobile only
|
|
316
|
+
// we have to delay running checkPlayerPosition, because sometimes on fast scrolling we get wrong order onEnter, then onLeave.
|
|
317
|
+
// which could cause select wrong item to play
|
|
318
|
+
|
|
319
|
+
this.cancelCheckPlayerPositionTimeout();
|
|
320
|
+
|
|
321
|
+
this.checkPlayerPositionTimeout = setTimeout(() => {
|
|
322
|
+
this.cancelCheckPlayerPositionTimeout();
|
|
323
|
+
|
|
324
|
+
this.checkPlayerPosition(item);
|
|
325
|
+
}, TIMEOUT_FOR_DELAY_CHECK_PLAYER_POSITION);
|
|
326
|
+
} else {
|
|
327
|
+
this.checkPlayerPosition(item);
|
|
328
|
+
}
|
|
270
329
|
};
|
|
271
330
|
|
|
272
|
-
|
|
331
|
+
private checkPlayerPosition = (item: LiveImage) => {
|
|
273
332
|
this.cancelCheckPlayerPositionTimeout();
|
|
274
333
|
|
|
275
334
|
log_debug(
|
|
276
|
-
`checkPlayerPosition: live-image playerId
|
|
277
|
-
item.player
|
|
278
|
-
)}, primary ${playerInfo(this.primaryPlayer)}`
|
|
335
|
+
`checkPlayerPosition: live-image ${item.playerId} - ${item.tag}, primary ${playerInfo(this.primaryPlayer)}`
|
|
279
336
|
);
|
|
280
337
|
|
|
281
338
|
const playerItem = this.findNextPlayableItem();
|
|
@@ -391,7 +448,7 @@ export class LiveImageManager implements PlayerLifecycleListener {
|
|
|
391
448
|
item,
|
|
392
449
|
playerId: this.currentlyPlaying?.playerId,
|
|
393
450
|
primaryPlayerId: this.primaryPlayer?.playerId,
|
|
394
|
-
entry: item.getPlayer()
|
|
451
|
+
entry: item.getPlayer()?.getEntry(),
|
|
395
452
|
},
|
|
396
453
|
});
|
|
397
454
|
};
|
|
@@ -426,7 +483,7 @@ export class LiveImageManager implements PlayerLifecycleListener {
|
|
|
426
483
|
item,
|
|
427
484
|
playerId: this.currentlyPlaying?.playerId,
|
|
428
485
|
primaryPlayerId: this.primaryPlayer?.playerId,
|
|
429
|
-
entry: item.getPlayer()
|
|
486
|
+
entry: item.getPlayer()?.getEntry(),
|
|
430
487
|
},
|
|
431
488
|
});
|
|
432
489
|
};
|
|
@@ -448,7 +505,7 @@ export class LiveImageManager implements PlayerLifecycleListener {
|
|
|
448
505
|
this.currentlyPlaying = null;
|
|
449
506
|
|
|
450
507
|
log_debug(
|
|
451
|
-
`onLiveImageError:
|
|
508
|
+
`onLiveImageError: currentItem: ${currentItem.playerId} - ${currentItem.tag} was removed`
|
|
452
509
|
);
|
|
453
510
|
|
|
454
511
|
// TODO: ...Maybe player some other item
|
|
@@ -460,8 +517,8 @@ export class LiveImageManager implements PlayerLifecycleListener {
|
|
|
460
517
|
item,
|
|
461
518
|
error,
|
|
462
519
|
playerId: currentItem?.playerId,
|
|
463
|
-
primaryPlayerId:
|
|
464
|
-
entry: item.getPlayer()
|
|
520
|
+
primaryPlayerId: this.primaryPlayer?.playerId,
|
|
521
|
+
entry: item.getPlayer()?.getEntry(),
|
|
465
522
|
},
|
|
466
523
|
});
|
|
467
524
|
};
|
|
@@ -503,10 +560,10 @@ export class LiveImageManager implements PlayerLifecycleListener {
|
|
|
503
560
|
LiveImageManager.instance;
|
|
504
561
|
|
|
505
562
|
export class LiveImage implements QuickBrickPlayer.SharedPlayerCallBacks {
|
|
506
|
-
public player: Player;
|
|
507
|
-
public setMode
|
|
508
|
-
// Will be replaced with rects
|
|
563
|
+
public player: Player | null = null;
|
|
564
|
+
public setMode?: (type: LiveImageType) => void;
|
|
509
565
|
public isFullyVisible: boolean = false;
|
|
566
|
+
public tag: string;
|
|
510
567
|
public position: Position = {
|
|
511
568
|
centerX: 0,
|
|
512
569
|
centerY: 0,
|
|
@@ -516,18 +573,117 @@ export class LiveImage implements QuickBrickPlayer.SharedPlayerCallBacks {
|
|
|
516
573
|
left: 0,
|
|
517
574
|
};
|
|
518
575
|
|
|
576
|
+
// Keeps muted state in sync: updates the initial value for deferred
|
|
577
|
+
// player creation and forwards to the player if it already exists.
|
|
578
|
+
public initiallyMuted: boolean = true;
|
|
579
|
+
|
|
580
|
+
mute = () => {
|
|
581
|
+
this.initiallyMuted = true;
|
|
582
|
+
this.player?.mute();
|
|
583
|
+
};
|
|
584
|
+
|
|
585
|
+
unmute = () => {
|
|
586
|
+
this.initiallyMuted = false;
|
|
587
|
+
this.player?.unmute();
|
|
588
|
+
};
|
|
589
|
+
|
|
590
|
+
positionToString() {
|
|
591
|
+
if (!this.position) {
|
|
592
|
+
return "position not set";
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
const { centerX, centerY, top, bottom, left, right } = this.position;
|
|
596
|
+
|
|
597
|
+
return `centerX: ${centerX.toFixed(2)}, centerY: ${centerY.toFixed(2)}, top: ${top.toFixed(2)}, bottom: ${bottom.toFixed(2)}, left: ${left.toFixed(2)}, right: ${right.toFixed(2)}`;
|
|
598
|
+
}
|
|
599
|
+
|
|
519
600
|
readonly playerId: string;
|
|
520
|
-
|
|
601
|
+
public component: any = null;
|
|
602
|
+
|
|
603
|
+
private factoryConfig: PlayerFactoryConfig;
|
|
604
|
+
public preloadHooks?: PreloadHookConfig[];
|
|
605
|
+
public processedEntry: ZappEntry | null = null;
|
|
606
|
+
private _preparePromise: Promise<boolean> | null = null;
|
|
521
607
|
|
|
522
608
|
constructor(props: LiveImageProps) {
|
|
523
|
-
this.player = props.player;
|
|
524
609
|
this.setMode = props.setMode;
|
|
525
|
-
this.playerId =
|
|
526
|
-
this.
|
|
610
|
+
this.playerId = props.playerId;
|
|
611
|
+
this.preloadHooks = props.preloadHooks;
|
|
612
|
+
this.factoryConfig = props.factoryConfig;
|
|
613
|
+
this.tag = props.tag || "untagged";
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
async prepareForPlayback(): Promise<boolean> {
|
|
617
|
+
// Already prepared — player exists
|
|
618
|
+
if (this.player) {
|
|
619
|
+
return true;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
// Deduplicate: if preparation is already in flight, await the same promise
|
|
623
|
+
if (this._preparePromise) {
|
|
624
|
+
return this._preparePromise;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
this._preparePromise = this.createPreparedPlayer()
|
|
628
|
+
.then((result) => {
|
|
629
|
+
this._preparePromise = null;
|
|
630
|
+
|
|
631
|
+
return result;
|
|
632
|
+
})
|
|
633
|
+
.catch((error) => {
|
|
634
|
+
this._preparePromise = null;
|
|
635
|
+
|
|
636
|
+
log_error(
|
|
637
|
+
`prepareForPlayback: live-image ${this.playerId}, error preparing for playback: ${error?.message}`,
|
|
638
|
+
{ error }
|
|
639
|
+
);
|
|
640
|
+
|
|
641
|
+
throw error;
|
|
642
|
+
});
|
|
643
|
+
|
|
644
|
+
return this._preparePromise;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
private async createPreparedPlayer(): Promise<boolean> {
|
|
648
|
+
let entry = this.factoryConfig.entry;
|
|
649
|
+
|
|
650
|
+
if (this.preloadHooks?.length) {
|
|
651
|
+
const result = await executePreloadHooks({
|
|
652
|
+
preloadHooks: this.preloadHooks,
|
|
653
|
+
entry,
|
|
654
|
+
});
|
|
655
|
+
|
|
656
|
+
if (!result) return false;
|
|
657
|
+
|
|
658
|
+
this.processedEntry = result;
|
|
659
|
+
entry = result;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
const factoryItem = await playerFactory({
|
|
663
|
+
player: this.factoryConfig.player,
|
|
664
|
+
playerId: this.factoryConfig.playerId,
|
|
665
|
+
autoplay: false,
|
|
666
|
+
entry,
|
|
667
|
+
muted: this.initiallyMuted,
|
|
668
|
+
playerPluginId: this.factoryConfig.playerPluginId,
|
|
669
|
+
screenConfig: this.factoryConfig.screenConfig,
|
|
670
|
+
playerRole: PlayerRole.Cell,
|
|
671
|
+
});
|
|
672
|
+
|
|
673
|
+
if (!factoryItem) {
|
|
674
|
+
throw new Error(
|
|
675
|
+
`Player factory returned null (playerId: ${this.factoryConfig.playerId}, playerPluginId: ${this.factoryConfig.playerPluginId})`
|
|
676
|
+
);
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
this.player = factoryItem.controller;
|
|
680
|
+
this.component = factoryItem.Component;
|
|
527
681
|
this.player.addListener({ id: "live-image", listener: this });
|
|
682
|
+
|
|
683
|
+
return true;
|
|
528
684
|
}
|
|
529
685
|
|
|
530
|
-
public getPlayer = (): Player => {
|
|
686
|
+
public getPlayer = (): Player | null => {
|
|
531
687
|
return this.player;
|
|
532
688
|
};
|
|
533
689
|
|
|
@@ -7,8 +7,6 @@ import { AppState, AppStateStatus, View } from "react-native";
|
|
|
7
7
|
import { TrackedView } from "../TrackedView";
|
|
8
8
|
import { useDimensions } from "@applicaster/zapp-react-native-utils/reactHooks/layout";
|
|
9
9
|
|
|
10
|
-
import { playerFactory } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/playerFactory";
|
|
11
|
-
|
|
12
10
|
import {
|
|
13
11
|
isApplePlatform,
|
|
14
12
|
isTV,
|
|
@@ -22,15 +20,15 @@ import { AnimatedInOut } from "@applicaster/zapp-react-native-ui-components/Comp
|
|
|
22
20
|
import { overlayFadeIn } from "./animationUtils";
|
|
23
21
|
import { loggerLiveImageComponent } from "./loggerHelper";
|
|
24
22
|
import { usePlayer } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayer";
|
|
25
|
-
import { PlayerRole } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/conts";
|
|
26
23
|
import { getAutoplaySettings } from "./utils";
|
|
27
24
|
import { isString } from "@applicaster/zapp-react-native-utils/stringUtils";
|
|
28
25
|
import { BufferAnimation } from "../PlayerContainer/BufferAnimation";
|
|
26
|
+
import { PreloadHookConfig } from "../MasterCell/DefaultComponents/LiveImage/executePreloadHooks";
|
|
29
27
|
|
|
30
28
|
const { log_error, log_debug } = loggerLiveImageComponent;
|
|
31
29
|
|
|
32
|
-
const isMeasurement = (
|
|
33
|
-
isString(
|
|
30
|
+
const isMeasurement = (itemId: string | number | null): boolean =>
|
|
31
|
+
isString(itemId) && itemId.startsWith("pre-measurement-");
|
|
34
32
|
|
|
35
33
|
// Pixels by which the view can slightly extend outside the viewport and still be considered fully visible.
|
|
36
34
|
const CLIP_THRESHOLD = 10;
|
|
@@ -44,6 +42,7 @@ type Props = {
|
|
|
44
42
|
screenConfig: Record<string, any>;
|
|
45
43
|
audioMutedByDefault?: boolean;
|
|
46
44
|
uri: string; // cover image url
|
|
45
|
+
preloadHooks?: PreloadHookConfig[];
|
|
47
46
|
};
|
|
48
47
|
|
|
49
48
|
const PlayerLiveImageComponent = (props: Props) => {
|
|
@@ -116,43 +115,38 @@ const PlayerLiveImageComponent = (props: Props) => {
|
|
|
116
115
|
}
|
|
117
116
|
|
|
118
117
|
entryToViewIdMapping.current[nativeTag] = { itemId: item.id };
|
|
119
|
-
}, [
|
|
118
|
+
}, [item.id, mode]);
|
|
120
119
|
|
|
121
|
-
const { screenConfig, playerPluginId } = props;
|
|
120
|
+
const { screenConfig, playerPluginId, preloadHooks } = props;
|
|
122
121
|
|
|
123
122
|
const liveImageItem: LiveImage = React.useMemo(() => {
|
|
124
|
-
|
|
125
|
-
player: ref,
|
|
123
|
+
return new LiveImage({
|
|
126
124
|
playerId,
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
125
|
+
preloadHooks,
|
|
126
|
+
factoryConfig: {
|
|
127
|
+
player: ref,
|
|
128
|
+
playerId,
|
|
129
|
+
playerPluginId: playerPluginId,
|
|
130
|
+
screenConfig: screenConfig,
|
|
131
|
+
entry: item,
|
|
132
|
+
},
|
|
133
|
+
tag: item.title?.toString(),
|
|
133
134
|
});
|
|
135
|
+
}, [playerId, preloadHooks, playerPluginId, screenConfig, item]);
|
|
134
136
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
playerId,
|
|
138
|
-
player: playerFactoryItem.controller,
|
|
139
|
-
component: playerFactoryItem?.Component,
|
|
140
|
-
});
|
|
141
|
-
} else {
|
|
142
|
-
throw new Error("Player factory item is null");
|
|
143
|
-
}
|
|
144
|
-
}, [playerId, item.id, playerPluginId, screenConfig]);
|
|
137
|
+
// Keep the muted state in sync with user preference and player state
|
|
138
|
+
liveImageItem.initiallyMuted = muted;
|
|
145
139
|
|
|
146
140
|
React.useEffect(() => {
|
|
147
141
|
liveImageItem.setMode = setModeDebounced;
|
|
148
142
|
}, [setModeDebounced, liveImageItem]);
|
|
149
143
|
|
|
144
|
+
// todo: no need for it to be react, can be moved into `player.addListener`
|
|
150
145
|
const { start, end } = React.useMemo(
|
|
151
146
|
() => getAutoplaySettings(item),
|
|
152
147
|
[item.id]
|
|
153
148
|
);
|
|
154
149
|
|
|
155
|
-
const controller = liveImageItem.getPlayer();
|
|
156
150
|
const player = usePlayer(playerId);
|
|
157
151
|
|
|
158
152
|
const _assignRoot = (component) => {
|
|
@@ -169,7 +163,7 @@ const PlayerLiveImageComponent = (props: Props) => {
|
|
|
169
163
|
|
|
170
164
|
React.useEffect(() => {
|
|
171
165
|
// FIXME - find a more elegant way to disable live-image on cell for measurement
|
|
172
|
-
if (isMeasurement(item)) {
|
|
166
|
+
if (isMeasurement(item.id)) {
|
|
173
167
|
return;
|
|
174
168
|
}
|
|
175
169
|
|
|
@@ -253,7 +247,13 @@ const PlayerLiveImageComponent = (props: Props) => {
|
|
|
253
247
|
}, [item.id]);
|
|
254
248
|
|
|
255
249
|
React.useEffect(() => {
|
|
256
|
-
if (isMeasurement(item) || !playerManager) {
|
|
250
|
+
if (isMeasurement(item.id) || !playerManager) {
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const controller = liveImageItem.getPlayer();
|
|
255
|
+
|
|
256
|
+
if (!controller) {
|
|
257
257
|
return;
|
|
258
258
|
}
|
|
259
259
|
|
|
@@ -273,7 +273,7 @@ const PlayerLiveImageComponent = (props: Props) => {
|
|
|
273
273
|
playerManager.unregisterPlayer(playerId);
|
|
274
274
|
};
|
|
275
275
|
}
|
|
276
|
-
}, [liveImageItem, playerId,
|
|
276
|
+
}, [liveImageItem, playerId, mode, item.id]);
|
|
277
277
|
|
|
278
278
|
const onPositionUpdated = React.useCallback(
|
|
279
279
|
(data) => {
|
|
@@ -310,7 +310,7 @@ const PlayerLiveImageComponent = (props: Props) => {
|
|
|
310
310
|
? LiveImageManager.instance.onViewportEnter(liveImageItem)
|
|
311
311
|
: LiveImageManager.instance.onViewportLeave(liveImageItem);
|
|
312
312
|
} else {
|
|
313
|
-
LiveImageManager.instance.
|
|
313
|
+
LiveImageManager.instance.onViewPositionChanged(liveImageItem);
|
|
314
314
|
}
|
|
315
315
|
},
|
|
316
316
|
[liveImageItem, dimensions.height, dimensions.width]
|
|
@@ -338,11 +338,11 @@ const PlayerLiveImageComponent = (props: Props) => {
|
|
|
338
338
|
<Player
|
|
339
339
|
autoplay={false}
|
|
340
340
|
ref={_assignRoot}
|
|
341
|
-
entry={item} // Must be passed first in list
|
|
341
|
+
entry={liveImageItem.processedEntry || item} // Must be passed first in list
|
|
342
342
|
style={videoStyles}
|
|
343
343
|
playerId={playerId}
|
|
344
344
|
muted={muted}
|
|
345
|
-
listener={
|
|
345
|
+
listener={liveImageItem.getPlayer()?.getListener()}
|
|
346
346
|
resizeMode={"cover"}
|
|
347
347
|
{...platformSpecificProps}
|
|
348
348
|
/>
|
|
@@ -120,29 +120,14 @@ describe("PlayerLiveImageComponent", () => {
|
|
|
120
120
|
// TODO: implement this test
|
|
121
121
|
});
|
|
122
122
|
|
|
123
|
-
it("should register the player
|
|
123
|
+
it("should not register the player at mount (lazy creation)", () => {
|
|
124
124
|
render(
|
|
125
125
|
<Wrapper>
|
|
126
126
|
<PlayerLiveImage {...defaultProps} />
|
|
127
127
|
</Wrapper>
|
|
128
128
|
);
|
|
129
129
|
|
|
130
|
-
|
|
131
|
-
defaultProps.playerId
|
|
132
|
-
);
|
|
133
|
-
|
|
134
|
-
expect(isPlayerRegistered).toBe(true);
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
it("should unregister the player for normal item", () => {
|
|
138
|
-
const component = render(
|
|
139
|
-
<Wrapper>
|
|
140
|
-
<PlayerLiveImage {...defaultProps} />
|
|
141
|
-
</Wrapper>
|
|
142
|
-
);
|
|
143
|
-
|
|
144
|
-
component.unmount();
|
|
145
|
-
|
|
130
|
+
// Player is not registered until playback starts (lazy creation)
|
|
146
131
|
const isPlayerRegistered = playerManager.isPlayerRegistered(
|
|
147
132
|
defaultProps.playerId
|
|
148
133
|
);
|
|
@@ -4,10 +4,7 @@ import { Animated, Dimensions } from "react-native";
|
|
|
4
4
|
import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks/navigation/useNavigation";
|
|
5
5
|
|
|
6
6
|
import { useConfiguration } from "../utils";
|
|
7
|
-
import {
|
|
8
|
-
useAppData,
|
|
9
|
-
usePlugins,
|
|
10
|
-
} from "@applicaster/zapp-react-native-redux/hooks";
|
|
7
|
+
import { usePlugins } from "@applicaster/zapp-react-native-redux/hooks";
|
|
11
8
|
import { isBottomTabVisible } from "../../Screen/navigationHandler";
|
|
12
9
|
|
|
13
10
|
import {
|
|
@@ -21,6 +18,7 @@ import {
|
|
|
21
18
|
import { getTabBarHeight } from "@applicaster/zapp-react-native-utils/reactHooks/navigation/getTabBarHeight";
|
|
22
19
|
import { PROGRESS_BAR_HEIGHT } from "./utils";
|
|
23
20
|
import { useIsTablet as getIsTablet } from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
21
|
+
import { useAppSelector } from "@applicaster/zapp-react-native-redux";
|
|
24
22
|
|
|
25
23
|
export type ModalAnimationContextT = {
|
|
26
24
|
yTranslate: React.MutableRefObject<Animated.AnimatedInterpolation<number>>;
|
|
@@ -66,7 +64,9 @@ const Provider = ({ children }: { children: React.ReactNode }) => {
|
|
|
66
64
|
screenData,
|
|
67
65
|
} = useNavigation();
|
|
68
66
|
|
|
69
|
-
const
|
|
67
|
+
const isTabletPortrait = useAppSelector(
|
|
68
|
+
(state) => state.appData.isTabletPortrait
|
|
69
|
+
);
|
|
70
70
|
|
|
71
71
|
const plugins = usePlugins();
|
|
72
72
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { renderHook } from "@testing-library/react-hooks";
|
|
2
2
|
import { useDelayedPlayerDetails } from "../useDelayedPlayerDetails";
|
|
3
3
|
import { useIsTablet } from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
4
4
|
|
|
@@ -13,20 +13,16 @@ describe("useDelayedPlayerDetails", () => {
|
|
|
13
13
|
jest.clearAllMocks();
|
|
14
14
|
});
|
|
15
15
|
|
|
16
|
-
it("should return false initially, that changes after 1 second",
|
|
16
|
+
it("should return false initially, that changes after 1 second", () => {
|
|
17
17
|
const { result } = renderHook(() =>
|
|
18
18
|
useDelayedPlayerDetails({ isInline: true, isDocked: false, isPip: false })
|
|
19
19
|
);
|
|
20
20
|
|
|
21
21
|
expect(result.current).toBe(false);
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
jest.advanceTimersByTime(1000);
|
|
25
|
-
});
|
|
23
|
+
jest.advanceTimersByTime(1000);
|
|
26
24
|
|
|
27
|
-
|
|
28
|
-
expect(result.current).toBe(true);
|
|
29
|
-
});
|
|
25
|
+
expect(result.current).toBe(true);
|
|
30
26
|
});
|
|
31
27
|
|
|
32
28
|
it("should return false when isPip is true", () => {
|
|
@@ -45,7 +41,7 @@ describe("useDelayedPlayerDetails", () => {
|
|
|
45
41
|
expect(result.current).toBe(false);
|
|
46
42
|
});
|
|
47
43
|
|
|
48
|
-
it("should return true for tablet regardless of other flags",
|
|
44
|
+
it("should return true for tablet regardless of other flags", () => {
|
|
49
45
|
(useIsTablet as jest.Mock).mockReturnValue(true);
|
|
50
46
|
|
|
51
47
|
const { result } = renderHook(() =>
|
|
@@ -56,12 +52,8 @@ describe("useDelayedPlayerDetails", () => {
|
|
|
56
52
|
})
|
|
57
53
|
);
|
|
58
54
|
|
|
59
|
-
|
|
60
|
-
jest.advanceTimersByTime(1000);
|
|
61
|
-
});
|
|
55
|
+
jest.advanceTimersByTime(1000);
|
|
62
56
|
|
|
63
|
-
|
|
64
|
-
expect(result.current).toBe(true);
|
|
65
|
-
});
|
|
57
|
+
expect(result.current).toBe(true);
|
|
66
58
|
});
|
|
67
59
|
});
|