@applicaster/zapp-react-native-utils 12.0.0-alpha.5606245605 → 12.0.0-alpha.5624518702
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/analyticsUtils/AnalyticPlayerListener.ts +155 -0
- package/analyticsUtils/AnalyticsEvents/index.ts +6 -0
- package/analyticsUtils/AnalyticsEvents/searchEvents.ts +21 -0
- package/analyticsUtils/PlayerAnalyticsManager.ts +90 -0
- package/analyticsUtils/analyticsPlugins.ts +25 -0
- package/analyticsUtils/events.ts +6 -0
- package/analyticsUtils/index.tsx +6 -0
- package/analyticsUtils/{analyticsTracker.ts → playerAnalyticsTracker.ts} +19 -10
- package/appUtils/HooksManager/index.js +3 -1
- package/appUtils/playerManager/DelayedSeekPlayerWrapper.tsx +239 -0
- package/appUtils/playerManager/conts.ts +5 -0
- package/appUtils/playerManager/index.ts +9 -27
- package/appUtils/playerManager/player.ts +46 -4
- package/appUtils/playerManager/playerFactory.ts +12 -10
- package/appUtils/playerManager/playerNative.ts +11 -2
- package/appUtils/playerManager/usePlayerControllerSetup.tsx +23 -4
- package/cellUtils/__tests__/cellUtils.test.ts +9 -20
- package/cellUtils/index.ts +10 -14
- package/dateUtils/index.ts +11 -0
- package/headersUtils/index.ts +1 -0
- package/inflatedUrlUtils/index.ts +4 -4
- package/localizationUtils/__tests__/localizationUtils.test.ts +72 -53
- package/localizationUtils/index.ts +13 -22
- package/manifestUtils/defaultManifestConfigurations/generalContent.js +6 -0
- package/manifestUtils/defaultManifestConfigurations/player.js +2 -1
- package/manifestUtils/index.js +5 -1
- package/manifestUtils/secondaryImage/index.js +18 -15
- package/navigationUtils/index.ts +8 -4
- package/numberUtils/index.ts +4 -0
- package/package.json +2 -2
- package/playerUtils/configurationGenerator.ts +3 -1
- package/reactHooks/autoscrolling/__tests__/useTrackCurrentAutoScrollingElement.test.ts +2 -0
- package/reactHooks/autoscrolling/__tests__/{useTrackedView.test.ts → useTrackedView.test.tsx} +18 -1
- package/reactHooks/cell-click/__tests__/index.test.js +5 -0
- package/reactHooks/cell-click/index.ts +2 -2
- package/reactHooks/feed/__tests__/useInflatedUrl.test.ts +18 -3
- package/reactHooks/feed/useInflatedUrl.ts +44 -15
- package/reactHooks/layout/__tests__/{index.test.js → index.test.tsx} +44 -29
- package/reactHooks/layout/getDeviceInfo/index.ts +4 -2
- package/reactHooks/layout/types/index.ts +2 -0
- package/reactHooks/layout/useDimensions/useDimensions.ts +13 -7
- package/reactHooks/navigation/__mocks__/index.ts +15 -0
- package/reactHooks/navigation/__mocks__/useNavigation.ts +7 -0
- package/reactHooks/navigation/__tests__/index.test.tsx +26 -8
- package/reactHooks/navigation/index.ts +21 -19
- package/reactHooks/player/__tests__/useAutoSeek.test.tsx +8 -1
- package/reactHooks/player/useAutoSeek.tsx +23 -7
- package/reactHooks/player/useTapSeek.tsx +13 -8
- package/reactHooks/resolvers/__tests__/useCellResolver.test.tsx +3 -1
- package/reactHooks/screen/__mocks__/useScreenContext.ts +3 -1
- package/reactHooks/screen/useScreenContext.ts +5 -4
- package/reactHooks/screen/useTargetScreenData.ts +7 -0
- package/reactHooks/useListenEventBusEvent.ts +1 -4
- package/screenState/index.ts +0 -8
- package/stringUtils/index.ts +5 -0
- package/testUtils/getByTestId.ts +14 -0
- package/testUtils/index.tsx +38 -23
- package/types.ts +4 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { PlayerAnalyticsTracker } from "./playerAnalyticsTracker";
|
|
2
|
+
import { Player } from "../appUtils/playerManager/player";
|
|
3
|
+
import { AD_EVENT, GENERAL_EVENT, PLAYBACK_EVENT, QOS_EVENT } from "./events";
|
|
4
|
+
|
|
5
|
+
type Props = {
|
|
6
|
+
analyticsTracker: any;
|
|
7
|
+
player: any;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
/* eslint brace-style: 0 */ // --> OFF
|
|
11
|
+
export class AnalyticPlayerListener
|
|
12
|
+
implements QuickBrickPlayer.SharedPlayerCallBacks
|
|
13
|
+
{
|
|
14
|
+
static analyticsListenerID = "analytics-listener";
|
|
15
|
+
private analyticsTracker: PlayerAnalyticsTracker;
|
|
16
|
+
private player: Player;
|
|
17
|
+
|
|
18
|
+
private onVideoLoadCalled; // android only
|
|
19
|
+
|
|
20
|
+
constructor(props: Props) {
|
|
21
|
+
this.analyticsTracker = props.analyticsTracker;
|
|
22
|
+
this.player = props.player;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
handleAnalyticEvent = (event, data = {}) => {
|
|
26
|
+
if (this.analyticsTracker?.handleAnalyticEvent) {
|
|
27
|
+
this.analyticsTracker?.handleAnalyticEvent?.(event, {
|
|
28
|
+
...data,
|
|
29
|
+
player_type: this.player.playerPluginId,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
onVideoEnd = () => {
|
|
35
|
+
this.handleAnalyticEvent(PLAYBACK_EVENT.complete);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
onError = (err: Error) => {
|
|
39
|
+
this.handleAnalyticEvent(PLAYBACK_EVENT.error, err); // TODO: Check error format
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// HACK: This is a hack to ignore the next resume event after a delayed seek
|
|
43
|
+
private ignoreNextResume = false;
|
|
44
|
+
onLoad = () => {
|
|
45
|
+
this.ignoreNextResume = false;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
onPlayerPause = (event) => {
|
|
49
|
+
if (!this.player.isInDelayedSeek()) {
|
|
50
|
+
this.handleAnalyticEvent(PLAYBACK_EVENT.pause, event);
|
|
51
|
+
} else {
|
|
52
|
+
this.ignoreNextResume = true;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
onPlayerResume = (event) => {
|
|
57
|
+
if (!this.player.isInDelayedSeek() && !this.ignoreNextResume) {
|
|
58
|
+
this.handleAnalyticEvent(PLAYBACK_EVENT.play, event);
|
|
59
|
+
} else {
|
|
60
|
+
this.ignoreNextResume = false;
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
onPlayerSeekComplete = (event) => {
|
|
65
|
+
this.handleAnalyticEvent(PLAYBACK_EVENT.seek_complete, event);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
onPlayerSeekStart = (event) => {
|
|
69
|
+
this.handleAnalyticEvent(PLAYBACK_EVENT.seek_start, event);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
onBufferStart = (event) => {
|
|
73
|
+
this.handleAnalyticEvent(PLAYBACK_EVENT.buffer_start, event);
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
onBufferComplete = (event) => {
|
|
77
|
+
this.handleAnalyticEvent(PLAYBACK_EVENT.buffer_complete, event);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// Not implemented
|
|
81
|
+
onPlayerStop = (event) => {
|
|
82
|
+
this.handleAnalyticEvent(PLAYBACK_EVENT.stop, event);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// TODO: Implement method on native
|
|
86
|
+
onVideoBandwidthUpdate = (event) => {
|
|
87
|
+
this.handleAnalyticEvent(QOS_EVENT.bitrate_changed, event);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
onAdBegin = (event) => {
|
|
91
|
+
this.handleAnalyticEvent(AD_EVENT.ad_start, event);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
onAdBreakBegin = (event) => {
|
|
95
|
+
this.handleAnalyticEvent(AD_EVENT.ad_break_start, event);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
onAdBreakEnd = (event) => {
|
|
99
|
+
this.handleAnalyticEvent(AD_EVENT.ad_break_completed, event);
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
onAdEnd = (event) => {
|
|
103
|
+
this.handleAnalyticEvent(AD_EVENT.ad_complete, event);
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
onAdError = (event) => {
|
|
107
|
+
this.handleAnalyticEvent(AD_EVENT.ad_error, event);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
onAdRequest = (event) => {
|
|
111
|
+
this.handleAnalyticEvent(AD_EVENT.ad_request, event);
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
onAdClicked = (event) => {
|
|
115
|
+
this.handleAnalyticEvent(AD_EVENT.ad_clicked, event);
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
onAdTapped = (event) => {
|
|
119
|
+
this.handleAnalyticEvent(AD_EVENT.ad_tapped, event);
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
onVideoLoad = () => {
|
|
123
|
+
// if (!this.state.isAd && !this.onVideoLoadCalled) {
|
|
124
|
+
if (!this.player.isAd() && !this.onVideoLoadCalled) {
|
|
125
|
+
this.onVideoLoadCalled = true;
|
|
126
|
+
|
|
127
|
+
this.handleAnalyticEvent(PLAYBACK_EVENT.load_video, {
|
|
128
|
+
player_type: this.player.playerPluginId,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
onPlayerSessionEnd = () => {
|
|
134
|
+
// TODO: Implement method on native android, ios exists
|
|
135
|
+
this.handleAnalyticEvent(GENERAL_EVENT.session_end);
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
onPlayerSessionStart = () => {
|
|
139
|
+
// TODO: Implement method on native android, ios exists
|
|
140
|
+
|
|
141
|
+
this.handleAnalyticEvent(GENERAL_EVENT.session_start);
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
onPlayerNext = ({ nativeEvent }) => {
|
|
145
|
+
// TODO: Implement method on native
|
|
146
|
+
|
|
147
|
+
this.handleAnalyticEvent(PLAYBACK_EVENT.next, nativeEvent);
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
onPlayerPrevious = ({ nativeEvent }) => {
|
|
151
|
+
// TODO: Implement method on native
|
|
152
|
+
|
|
153
|
+
this.handleAnalyticEvent(PLAYBACK_EVENT.previous, nativeEvent);
|
|
154
|
+
};
|
|
155
|
+
}
|
|
@@ -7,6 +7,10 @@ import { sendHeaderClickEvent } from "./sendHeaderClickEvent";
|
|
|
7
7
|
import { sendHardwareBackButtonClickEvent } from "./sendHardwareBackButtonClickEvent";
|
|
8
8
|
import { sendDownloadsEvent } from "./sendDownloadsEvent";
|
|
9
9
|
import { sendScreenEvent } from "./sendScreenEvent";
|
|
10
|
+
import {
|
|
11
|
+
sendSearchCanceledEvent,
|
|
12
|
+
sendSearchExecutedEvent,
|
|
13
|
+
} from "./searchEvents";
|
|
10
14
|
|
|
11
15
|
export {
|
|
12
16
|
sendScreenEvent,
|
|
@@ -18,4 +22,6 @@ export {
|
|
|
18
22
|
sendHeaderClickEvent,
|
|
19
23
|
sendHardwareBackButtonClickEvent,
|
|
20
24
|
sendDownloadsEvent,
|
|
25
|
+
sendSearchCanceledEvent,
|
|
26
|
+
sendSearchExecutedEvent,
|
|
21
27
|
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { log_debug } from "../logger";
|
|
2
|
+
import { postAnalyticEvent } from "../manager";
|
|
3
|
+
import { ANALYTICS_CORE_EVENTS } from "../events";
|
|
4
|
+
|
|
5
|
+
export const sendSearchCanceledEvent = (analyticsProps) => {
|
|
6
|
+
const eventName = ANALYTICS_CORE_EVENTS.SEARCH_CANCELED;
|
|
7
|
+
postAnalyticEvent(eventName, analyticsProps);
|
|
8
|
+
|
|
9
|
+
log_debug(`sendSearchCanceledEvent: send event: ${eventName}`, {
|
|
10
|
+
analyticsProps,
|
|
11
|
+
});
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const sendSearchExecutedEvent = (analyticsProps) => {
|
|
15
|
+
const eventName = ANALYTICS_CORE_EVENTS.SEARCH_EXECUTED;
|
|
16
|
+
postAnalyticEvent(eventName, analyticsProps);
|
|
17
|
+
|
|
18
|
+
log_debug(`sendSearchExecutedEvent: send event: ${eventName}`, {
|
|
19
|
+
analyticsProps,
|
|
20
|
+
});
|
|
21
|
+
};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PlayerAnalyticsTracker,
|
|
3
|
+
PlayerAnalyticsTrackerI,
|
|
4
|
+
} from "./playerAnalyticsTracker";
|
|
5
|
+
import {
|
|
6
|
+
PlayerLifecycleListener,
|
|
7
|
+
PlayerManager,
|
|
8
|
+
} from "../appUtils/playerManager";
|
|
9
|
+
import { Player } from "../appUtils/playerManager/player";
|
|
10
|
+
import { AnalyticPlayerListener } from "./AnalyticPlayerListener";
|
|
11
|
+
import { PlayerRole } from "../appUtils/playerManager/conts";
|
|
12
|
+
|
|
13
|
+
enum PlayerAnalyticsTrackerType {
|
|
14
|
+
PlayerAnalyticsTracker = "PlayerAnalyticsTracker",
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
class PlayerAnalyticsTrackerFactory {
|
|
18
|
+
createTracker(type: PlayerAnalyticsTrackerType) {
|
|
19
|
+
switch (type) {
|
|
20
|
+
case PlayerAnalyticsTrackerType.PlayerAnalyticsTracker:
|
|
21
|
+
return new PlayerAnalyticsTracker();
|
|
22
|
+
default:
|
|
23
|
+
return new PlayerAnalyticsTracker();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export class PlayerAnalyticsManager implements PlayerLifecycleListener {
|
|
29
|
+
static instance: PlayerAnalyticsManager;
|
|
30
|
+
private trackers: Record<string, PlayerAnalyticsTrackerI> = {};
|
|
31
|
+
private trackerFactory: PlayerAnalyticsTrackerFactory;
|
|
32
|
+
|
|
33
|
+
public getTracker(
|
|
34
|
+
id: string,
|
|
35
|
+
type: PlayerAnalyticsTrackerType = PlayerAnalyticsTrackerType.PlayerAnalyticsTracker
|
|
36
|
+
): PlayerAnalyticsTrackerI {
|
|
37
|
+
if (!this.trackers[id]) {
|
|
38
|
+
this.trackers[id] = this.trackerFactory.createTracker(type);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return this.trackers[id];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
constructor(trackerFactory: PlayerAnalyticsTrackerFactory) {
|
|
45
|
+
this.trackerFactory = trackerFactory;
|
|
46
|
+
this.startListenPlayerManager();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
static getInstance(
|
|
50
|
+
trackerFactory: PlayerAnalyticsTrackerFactory = new PlayerAnalyticsTrackerFactory()
|
|
51
|
+
): PlayerAnalyticsManager {
|
|
52
|
+
if (!PlayerAnalyticsManager.instance) {
|
|
53
|
+
PlayerAnalyticsManager.instance = new PlayerAnalyticsManager(
|
|
54
|
+
trackerFactory
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return PlayerAnalyticsManager.instance;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
private startListenPlayerManager() {
|
|
62
|
+
PlayerManager.getInstance().addLifecycleListener(this);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// PlayerLifecycleListener
|
|
66
|
+
|
|
67
|
+
onRegistered = (player: Player) => {
|
|
68
|
+
if (player.playerRole !== PlayerRole.Primary) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const tracker = this.getTracker(player.playerId);
|
|
73
|
+
tracker.setPlayer(player);
|
|
74
|
+
|
|
75
|
+
const analyticsListener = new AnalyticPlayerListener({
|
|
76
|
+
analyticsTracker: tracker,
|
|
77
|
+
player,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
player.addListener({
|
|
81
|
+
id: AnalyticPlayerListener.analyticsListenerID,
|
|
82
|
+
listener: analyticsListener,
|
|
83
|
+
});
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
onUnRegistered = (player: Player) => {
|
|
87
|
+
player.removeListener(AnalyticPlayerListener.analyticsListenerID);
|
|
88
|
+
delete this.trackers[player.playerId];
|
|
89
|
+
};
|
|
90
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as R from "ramda";
|
|
2
|
+
import { appStore } from "@applicaster/zapp-react-native-redux/AppStore";
|
|
3
|
+
|
|
4
|
+
export const getVideojsAnalyticPluginsConfig = async (entry: ZappEntry) => {
|
|
5
|
+
let videojsConfiguration = {};
|
|
6
|
+
|
|
7
|
+
const plugins = appStore.get("plugins");
|
|
8
|
+
const analyticPlugins = R.filter(R.propEq("type", "analytics"), plugins);
|
|
9
|
+
|
|
10
|
+
const configurations = await Promise.all(
|
|
11
|
+
analyticPlugins.map(async (plugin) => {
|
|
12
|
+
const { configuration, module } = plugin;
|
|
13
|
+
|
|
14
|
+
return module?.getPlayerConfiguration?.(configuration, entry);
|
|
15
|
+
})
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
configurations.forEach((analyticConfig) => {
|
|
19
|
+
if (typeof analyticConfig === "object" && analyticConfig !== null) {
|
|
20
|
+
videojsConfiguration = { ...videojsConfiguration, ...analyticConfig };
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
return videojsConfiguration;
|
|
25
|
+
};
|
package/analyticsUtils/events.ts
CHANGED
|
@@ -23,10 +23,16 @@ export const TAPPING_EVENTS = {
|
|
|
23
23
|
TAP_NAVBAR_BACK_BUTTON: "tap_navbar_back_button",
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
+
export const SEARCH_EVENTS = {
|
|
27
|
+
SEARCH_EXECUTED: "search_executed",
|
|
28
|
+
SEARCH_CANCELED: "search_canceled",
|
|
29
|
+
};
|
|
30
|
+
|
|
26
31
|
export const ANALYTICS_CORE_EVENTS = {
|
|
27
32
|
...LEGACY_EVENTS,
|
|
28
33
|
...SCREEN_VIEW_EVENTS,
|
|
29
34
|
...TAPPING_EVENTS,
|
|
35
|
+
...SEARCH_EVENTS,
|
|
30
36
|
};
|
|
31
37
|
|
|
32
38
|
export const WEBVIEW_EVENTS = {
|
package/analyticsUtils/index.tsx
CHANGED
|
@@ -18,6 +18,8 @@ import {
|
|
|
18
18
|
sendHardwareBackButtonClickEvent,
|
|
19
19
|
sendDownloadsEvent,
|
|
20
20
|
sendScreenEvent,
|
|
21
|
+
sendSearchCanceledEvent,
|
|
22
|
+
sendSearchExecutedEvent,
|
|
21
23
|
} from "./AnalyticsEvents";
|
|
22
24
|
|
|
23
25
|
import { postAnalyticEvent } from "./manager";
|
|
@@ -66,6 +68,8 @@ type AnalyticsFunctions = {
|
|
|
66
68
|
item: ZappEntry;
|
|
67
69
|
state: OfflineItemState;
|
|
68
70
|
}) => void;
|
|
71
|
+
sendSearchCanceledEvent: (props: AnalyticsDefaultProperties) => void;
|
|
72
|
+
sendSearchExecutedEvent: (props: AnalyticsDefaultProperties) => void;
|
|
69
73
|
};
|
|
70
74
|
|
|
71
75
|
type GetAnalyticsFunctions = (
|
|
@@ -102,6 +106,8 @@ export function getAnalyticsFunctions({
|
|
|
102
106
|
sendHardwareBackButtonClickEvent,
|
|
103
107
|
sendScreenEvent,
|
|
104
108
|
sendDownloadsEvent,
|
|
109
|
+
sendSearchCanceledEvent,
|
|
110
|
+
sendSearchExecutedEvent,
|
|
105
111
|
};
|
|
106
112
|
}
|
|
107
113
|
|
|
@@ -5,16 +5,25 @@ import { postAnalyticEvent } from "./manager";
|
|
|
5
5
|
import { isLive } from "../playerUtils";
|
|
6
6
|
import { extensionsEvents } from "./AnalyticsEvents/helper";
|
|
7
7
|
import { AD_EVENT, EVENT_TYPES, PLAYER_DISPLAY_STATES } from "./events";
|
|
8
|
+
import { Player } from "../appUtils/playerManager/player";
|
|
8
9
|
|
|
9
|
-
export
|
|
10
|
+
export interface PlayerAnalyticsTrackerI {
|
|
11
|
+
handleAnalyticEvent(
|
|
12
|
+
event: QuickBrickPlayer.Events,
|
|
13
|
+
eventData?: QuickBrickPlayer.EventData
|
|
14
|
+
): void;
|
|
15
|
+
setPlayer(player: Player): void;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class PlayerAnalyticsTracker implements PlayerAnalyticsTrackerI {
|
|
10
19
|
adTime: number | string;
|
|
11
20
|
mediaTime: number | string;
|
|
12
|
-
entry: ZappEntry;
|
|
13
21
|
eventType: string;
|
|
14
22
|
playerState: QuickBrickPlayer.NativePlayerState;
|
|
23
|
+
private player: Player;
|
|
15
24
|
|
|
16
|
-
|
|
17
|
-
this.
|
|
25
|
+
setPlayer(player: Player) {
|
|
26
|
+
this.player = player;
|
|
18
27
|
}
|
|
19
28
|
|
|
20
29
|
isAdBreak(event: string): boolean {
|
|
@@ -30,6 +39,10 @@ export class AnalyticsTracker {
|
|
|
30
39
|
return !!isAdBreak;
|
|
31
40
|
}
|
|
32
41
|
|
|
42
|
+
get entry(): ZappEntry {
|
|
43
|
+
return this.player?.getEntry();
|
|
44
|
+
}
|
|
45
|
+
|
|
33
46
|
getDuration(
|
|
34
47
|
event: QuickBrickPlayer.Events,
|
|
35
48
|
eventData: QuickBrickPlayer.EventData
|
|
@@ -215,13 +228,9 @@ export class AnalyticsTracker {
|
|
|
215
228
|
|
|
216
229
|
handleAnalyticEvent(
|
|
217
230
|
event: QuickBrickPlayer.Events,
|
|
218
|
-
eventData: QuickBrickPlayer.EventData = {}
|
|
219
|
-
entry: ZappEntry,
|
|
220
|
-
playerState = null
|
|
231
|
+
eventData: QuickBrickPlayer.EventData = {}
|
|
221
232
|
) {
|
|
222
|
-
this.
|
|
223
|
-
this.playerState = playerState;
|
|
224
|
-
|
|
233
|
+
this.playerState = this.player?.getState();
|
|
225
234
|
postAnalyticEvent(event, this.prepareAnalyticPayload(event, eventData));
|
|
226
235
|
}
|
|
227
236
|
}
|
|
@@ -270,7 +270,9 @@ export function HooksManager({
|
|
|
270
270
|
});
|
|
271
271
|
|
|
272
272
|
const isHookInHomescreen = payload?.home;
|
|
273
|
-
|
|
273
|
+
|
|
274
|
+
const isHookFlowBlocker =
|
|
275
|
+
hookPlugin?.module?.isFlowBlocker?.() ?? false;
|
|
274
276
|
|
|
275
277
|
// TODO: We technically need to also support the case where there is more than one hook in the stack,
|
|
276
278
|
// if needed we could add differentiation explicit user cancellation (when we should go to background) and other types of cancellation
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { Player } from "./player";
|
|
2
|
+
import { BehaviorSubject } from "rxjs";
|
|
3
|
+
const DELAYED_SEEK_DELAY = 2000;
|
|
4
|
+
|
|
5
|
+
export class DelayedSeekPlayerWrapper extends Player {
|
|
6
|
+
private wrappedPlayer: Player;
|
|
7
|
+
private delayedSeek: boolean;
|
|
8
|
+
private wasPlaying: boolean;
|
|
9
|
+
private delayedSeekTimer: NodeJS.Timeout;
|
|
10
|
+
private resumeTimer: NodeJS.Timeout;
|
|
11
|
+
|
|
12
|
+
constructor(props, player: Player) {
|
|
13
|
+
super(props);
|
|
14
|
+
// this.playerState = this.wrappedPlayer.playerState;
|
|
15
|
+
this.wrappedPlayer = player;
|
|
16
|
+
|
|
17
|
+
this.wrappedPlayer.addListener({
|
|
18
|
+
id: "Master-Wrapper-Listener",
|
|
19
|
+
listener: this,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
getOverlayObservable = () => {
|
|
24
|
+
return this.wrappedPlayer.getOverlayObservable();
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
getEntryObservable = (): BehaviorSubject<ZappEntry | null> => {
|
|
28
|
+
return this.wrappedPlayer.getEntryObservable();
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
getEntry = (): ZappEntry | null => this.wrappedPlayer.getEntry();
|
|
32
|
+
|
|
33
|
+
getListener = (): QuickBrickPlayer.SharedPlayerCallBacks => {
|
|
34
|
+
return this.wrappedPlayer.getListener();
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
togglePlayPause = () => {
|
|
38
|
+
this.wrappedPlayer.togglePlayPause();
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
selectTrack = (_track) => this.wrappedPlayer.selectTrack(_track);
|
|
42
|
+
|
|
43
|
+
// Implement all default methods by delegating them to wrapped player
|
|
44
|
+
public seekTo = (position: number) => {
|
|
45
|
+
this.wrappedPlayer.seekTo(position);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
public isPlaying = () => {
|
|
49
|
+
return this.wrappedPlayer.isPlaying();
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
public isPaused = () => {
|
|
53
|
+
return this.wrappedPlayer.isPaused();
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
public play = () => {
|
|
57
|
+
this.wrappedPlayer.play();
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
public pause = () => {
|
|
61
|
+
this.wrappedPlayer.pause();
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
getState = () => {
|
|
65
|
+
const currentState = this.wrappedPlayer.getState();
|
|
66
|
+
|
|
67
|
+
if (!this.delayedSeek) {
|
|
68
|
+
return currentState;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
...currentState,
|
|
73
|
+
contentPosition: this.playerState.seekPosition,
|
|
74
|
+
isPaused: false,
|
|
75
|
+
seekPosition: this.playerState.seekPosition,
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
forward = (deltaTime: number) => {
|
|
80
|
+
if (this.delayedSeek || this.delayedSeekTimer) {
|
|
81
|
+
if (this.playerState.seekPosition == null) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
this.playerState.seekPosition += deltaTime;
|
|
86
|
+
|
|
87
|
+
this.onVideoProgress({
|
|
88
|
+
currentTime: this.playerState.seekPosition,
|
|
89
|
+
duration: this.getDuration(),
|
|
90
|
+
seekableDuration: this.playerState.seekableDuration,
|
|
91
|
+
isLive: this.playerState.isLive,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
this.scheduleResumePlayback();
|
|
95
|
+
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (this.playerState.seekPosition == null) {
|
|
100
|
+
this.playerState.seekPosition = this.playerState.contentPosition;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
this.playerState.seekPosition += deltaTime;
|
|
104
|
+
|
|
105
|
+
this.delayedSeekTimer && clearTimeout(this.delayedSeekTimer);
|
|
106
|
+
|
|
107
|
+
this.delayedSeekTimer = setTimeout(() => {
|
|
108
|
+
this.delayedSeekTimer = null;
|
|
109
|
+
|
|
110
|
+
if (this.playerState.seekPosition == null) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
this.wrappedPlayer.seekTo(this.playerState.seekPosition);
|
|
115
|
+
|
|
116
|
+
this.logState("forward", { deltaTime });
|
|
117
|
+
}, DELAYED_SEEK_DELAY);
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
rewind = (deltaTime: number) => {
|
|
121
|
+
if (this.delayedSeek || this.delayedSeekTimer) {
|
|
122
|
+
if (this.playerState.seekPosition == null) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
this.playerState.seekPosition -= deltaTime;
|
|
127
|
+
|
|
128
|
+
if (this.playerState.seekPosition < 0) {
|
|
129
|
+
this.playerState.seekPosition = 0;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
this.onVideoProgress({
|
|
133
|
+
currentTime: this.playerState.seekPosition,
|
|
134
|
+
duration: this.getDuration(),
|
|
135
|
+
seekableDuration: this.playerState.seekableDuration,
|
|
136
|
+
isLive: this.playerState.isLive,
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
this.scheduleResumePlayback();
|
|
140
|
+
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
this.delayedSeekTimer && clearTimeout(this.delayedSeekTimer);
|
|
145
|
+
|
|
146
|
+
if (this.playerState.seekPosition == null) {
|
|
147
|
+
this.playerState.seekPosition = this.playerState.contentPosition;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
this.playerState.seekPosition -= deltaTime;
|
|
151
|
+
|
|
152
|
+
if (this.playerState.seekPosition < 0) {
|
|
153
|
+
this.playerState.seekPosition = 0;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
this.delayedSeekTimer = setTimeout(() => {
|
|
157
|
+
this.delayedSeekTimer = null;
|
|
158
|
+
|
|
159
|
+
if (this.playerState.seekPosition == null) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
this.wrappedPlayer.seekTo(this.playerState.seekPosition);
|
|
164
|
+
|
|
165
|
+
// this.currentPlayerComponent()?.["rewind"]?.(deltaTime);
|
|
166
|
+
this.logState("rewind", { deltaTime });
|
|
167
|
+
}, DELAYED_SEEK_DELAY);
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
public getConfig = () => this.wrappedPlayer.getConfig();
|
|
171
|
+
|
|
172
|
+
// TODO: replace with enum of supported orientations
|
|
173
|
+
isFullScreenSupported(): boolean {
|
|
174
|
+
return this.wrappedPlayer.isFullScreenSupported();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
public supportsNativeControls = (): boolean =>
|
|
178
|
+
this.wrappedPlayer.supportsNativeControls();
|
|
179
|
+
|
|
180
|
+
public supportNativeCast = (): boolean =>
|
|
181
|
+
this.wrappedPlayer.supportNativeCast();
|
|
182
|
+
|
|
183
|
+
public isInDelayedSeek = () => this.delayedSeek;
|
|
184
|
+
|
|
185
|
+
public enterDelayedSeek() {
|
|
186
|
+
this.delayedSeekTimer && clearTimeout(this.delayedSeekTimer);
|
|
187
|
+
this.delayedSeekTimer = null;
|
|
188
|
+
|
|
189
|
+
if (this.delayedSeek) {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
this.delayedSeek = true;
|
|
194
|
+
|
|
195
|
+
this.playerState.seekPosition = this.playerState.contentPosition;
|
|
196
|
+
|
|
197
|
+
if (!this.isPaused()) {
|
|
198
|
+
this.wasPlaying = true;
|
|
199
|
+
this.wrappedPlayer.pause();
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
public exitDelayedSeek() {
|
|
204
|
+
if (!this.delayedSeek) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (this.delayedSeekTimer) {
|
|
209
|
+
this.scheduleResumePlayback();
|
|
210
|
+
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
this.delayedSeek = false;
|
|
215
|
+
|
|
216
|
+
if (this.playerState.seekPosition != null) {
|
|
217
|
+
this.wrappedPlayer.seekTo(this.playerState.seekPosition);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
this.scheduleResumePlayback();
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
public scheduleResumePlayback(timeout: number = DELAYED_SEEK_DELAY) {
|
|
224
|
+
if (this.delayedSeek) {
|
|
225
|
+
this.delayedSeek = false;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (!this.wasPlaying) {
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
clearTimeout(this.resumeTimer);
|
|
233
|
+
|
|
234
|
+
this.resumeTimer = setTimeout(() => {
|
|
235
|
+
this.resumeTimer = null;
|
|
236
|
+
this.play();
|
|
237
|
+
}, timeout);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
@@ -20,6 +20,11 @@ export enum SharedPlayerCallBacksKeys {
|
|
|
20
20
|
OnPlayerMute = "onPlayerMute",
|
|
21
21
|
OnPlayerUnmute = "onPlayerUnmute",
|
|
22
22
|
|
|
23
|
+
onVideoFullscreenPlayerDidDismiss = "onVideoFullscreenPlayerDidDismiss",
|
|
24
|
+
onVideoFullscreenPlayerDidPresent = "onVideoFullscreenPlayerDidPresent",
|
|
25
|
+
onVideoFullscreenPlayerWillDismiss = "onVideoFullscreenPlayerWillDismiss",
|
|
26
|
+
onVideoFullscreenPlayerWillPresent = "onVideoFullscreenPlayerWillPresent",
|
|
27
|
+
|
|
23
28
|
OnAdBegin = "onAdBegin",
|
|
24
29
|
OnAdBreakBegin = "onAdBreakBegin",
|
|
25
30
|
OnAdBreakEnd = "onAdBreakEnd",
|