@applicaster/zapp-react-native-utils 12.0.0-alpha.7254207052 → 12.0.0-alpha.7303454980
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 +141 -0
- package/analyticsUtils/AnalyticsEvents/index.ts +13 -0
- package/analyticsUtils/AnalyticsEvents/searchEvents.ts +21 -0
- package/analyticsUtils/AnalyticsEvents/sendShareEvent.ts +21 -0
- package/analyticsUtils/PlayerAnalyticsManager.ts +90 -0
- package/analyticsUtils/analyticsPlugins.ts +25 -0
- package/analyticsUtils/events.ts +12 -0
- package/analyticsUtils/index.tsx +12 -0
- package/analyticsUtils/{analyticsTracker.ts → playerAnalyticsTracker.ts} +19 -10
- package/appUtils/accessibilityManager/const.ts +50 -0
- package/appUtils/accessibilityManager/hooks.ts +40 -0
- package/appUtils/accessibilityManager/index.ts +166 -0
- package/appUtils/accessibilityManager/logger.ts +5 -0
- package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +2 -3
- package/appUtils/focusManager/__tests__/focusManager.test.js +4 -24
- package/appUtils/focusManager/__tests__/index.test.ts +2 -3
- package/appUtils/focusManager/index.ts +76 -57
- package/appUtils/focusManager/treeDataStructure/Utils/index.js +75 -44
- package/appUtils/focusManager/utils/index.ts +100 -0
- package/appUtils/focusManagerAux/index.ts +76 -0
- package/appUtils/focusManagerAux/utils/index.ts +100 -0
- package/appUtils/index.js +2 -0
- package/appUtils/keyCodes/keys.web.js +8 -0
- package/appUtils/keyCodes/keysByPlatform.js +11 -1
- package/appUtils/platform/const.ts +25 -0
- package/appUtils/platform/index.ts +3 -0
- package/appUtils/platform/platformUtils.ts +217 -0
- package/appUtils/playerManager/conts.ts +1 -0
- package/appUtils/playerManager/index.ts +20 -32
- package/appUtils/playerManager/nativePlayerManager.ts +12 -0
- package/appUtils/playerManager/player.ts +10 -3
- package/appUtils/playerManager/playerHooks/usePlayerCurrentTime.tsx +57 -0
- package/appUtils/playerManager/playerNative.ts +11 -15
- package/appUtils/playerManager/usePlayerControllerSetup.tsx +8 -2
- package/appUtils/playerManager/usePlayerState.tsx +19 -34
- package/arrayUtils/__tests__/isFilledArray.test.ts +63 -0
- package/arrayUtils/index.ts +5 -0
- package/booleanUtils/index.ts +4 -4
- package/cellUtils/__tests__/cellUtils.test.ts +9 -20
- package/cellUtils/index.ts +44 -17
- package/dateUtils/index.ts +11 -0
- package/functionUtils/index.ts +38 -0
- package/headersUtils/index.ts +1 -0
- package/index.d.ts +15 -0
- package/inflatedUrlUtils/index.ts +4 -4
- package/keyboardState/index.ts +0 -0
- 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 +121 -0
- package/manifestUtils/keys.js +2 -1
- package/navigationUtils/index.ts +48 -0
- package/numberUtils/__tests__/isLikeNumber.test.ts +18 -0
- package/numberUtils/__tests__/toNumber.test.ts +2 -2
- package/numberUtils/index.ts +25 -17
- package/package.json +2 -2
- package/playerUtils/configurationGenerator.ts +120 -0
- package/playerUtils/imaScriptDom.ts +59 -0
- package/pluginUtils/index.ts +9 -0
- package/reactHooks/autoscrolling/__tests__/{useTrackedView.test.ts → useTrackedView.test.tsx} +16 -1
- package/reactHooks/cell-click/__tests__/index.test.js +10 -1
- package/reactHooks/cell-click/index.ts +12 -12
- package/reactHooks/debugging/__tests__/index.test.js +81 -11
- package/reactHooks/feed/__tests__/useBuildPipesUrl.test.tsx +30 -70
- package/reactHooks/feed/__tests__/useFeedRefresh.test.tsx +6 -2
- package/reactHooks/feed/__tests__/useInflatedUrl.test.ts +18 -3
- package/reactHooks/feed/useBuildPipesUrl.ts +7 -10
- package/reactHooks/feed/useFeedRefresh.tsx +9 -7
- package/reactHooks/feed/useInflatedUrl.ts +44 -15
- package/reactHooks/layout/__tests__/{index.test.js → index.test.tsx} +43 -29
- package/reactHooks/layout/getDeviceInfo/index.ts +4 -2
- package/reactHooks/layout/types/index.ts +1 -0
- package/reactHooks/layout/useDimensions/useDimensions.ts +7 -2
- package/reactHooks/navigation/__mocks__/index.ts +13 -0
- package/reactHooks/navigation/__mocks__/useNavigation.ts +3 -0
- package/reactHooks/navigation/__tests__/index.test.tsx +11 -8
- package/reactHooks/navigation/index.ts +61 -63
- package/reactHooks/player/useAutoSeek.tsx +9 -7
- package/reactHooks/resolvers/useCellResolver.ts +5 -1
- package/reactHooks/screen/__tests__/useCurrentScreenData.test.tsx +21 -23
- package/reactHooks/screen/index.ts +2 -0
- package/reactHooks/screen/useScreenBackgroundColor.ts +23 -0
- package/reactHooks/screen/useScreenContext.ts +3 -3
- package/reactHooks/useConfiguration.ts +2 -2
- package/reactUtils/index.ts +104 -0
- package/screenState/index.ts +0 -8
- package/stringUtils/index.ts +5 -0
- package/testUtils/getByTestId.ts +14 -0
- package/testUtils/index.tsx +24 -22
- package/types.ts +4 -0
- package/appUtils/focusManager/FocusableEvents.ts +0 -8
- package/appUtils/focusManager/treeDataStructure/Utils/__tests__/Utils_android.test.js +0 -1094
- package/appUtils/focusManager/treeDataStructure/Utils/index.android.js +0 -261
|
@@ -0,0 +1,141 @@
|
|
|
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
|
+
onPlayerPause = (event) => {
|
|
43
|
+
this.handleAnalyticEvent(PLAYBACK_EVENT.pause, event);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
onPlayerResume = (event) => {
|
|
47
|
+
this.handleAnalyticEvent(PLAYBACK_EVENT.play, event);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
onPlayerSeekComplete = (event) => {
|
|
51
|
+
this.handleAnalyticEvent(PLAYBACK_EVENT.seek_complete, event);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
onPlayerSeekStart = (event) => {
|
|
55
|
+
this.handleAnalyticEvent(PLAYBACK_EVENT.seek_start, event);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
onBufferStart = (event) => {
|
|
59
|
+
this.handleAnalyticEvent(PLAYBACK_EVENT.buffer_start, event);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
onBufferComplete = (event) => {
|
|
63
|
+
this.handleAnalyticEvent(PLAYBACK_EVENT.buffer_complete, event);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// Not implemented
|
|
67
|
+
onPlayerStop = (event) => {
|
|
68
|
+
this.handleAnalyticEvent(PLAYBACK_EVENT.stop, event);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// TODO: Implement method on native
|
|
72
|
+
onVideoBandwidthUpdate = (event) => {
|
|
73
|
+
this.handleAnalyticEvent(QOS_EVENT.bitrate_changed, event);
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
onAdBegin = (event) => {
|
|
77
|
+
this.handleAnalyticEvent(AD_EVENT.ad_start, event);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
onAdBreakBegin = (event) => {
|
|
81
|
+
this.handleAnalyticEvent(AD_EVENT.ad_break_start, event);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
onAdBreakEnd = (event) => {
|
|
85
|
+
this.handleAnalyticEvent(AD_EVENT.ad_break_completed, event);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
onAdEnd = (event) => {
|
|
89
|
+
this.handleAnalyticEvent(AD_EVENT.ad_complete, event);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
onAdError = (event) => {
|
|
93
|
+
this.handleAnalyticEvent(AD_EVENT.ad_error, event);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
onAdRequest = (event) => {
|
|
97
|
+
this.handleAnalyticEvent(AD_EVENT.ad_request, event);
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
onAdClicked = (event) => {
|
|
101
|
+
this.handleAnalyticEvent(AD_EVENT.ad_clicked, event);
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
onAdTapped = (event) => {
|
|
105
|
+
this.handleAnalyticEvent(AD_EVENT.ad_tapped, event);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
onVideoLoad = () => {
|
|
109
|
+
// if (!this.state.isAd && !this.onVideoLoadCalled) {
|
|
110
|
+
if (!this.player.isAd() && !this.onVideoLoadCalled) {
|
|
111
|
+
this.onVideoLoadCalled = true;
|
|
112
|
+
|
|
113
|
+
this.handleAnalyticEvent(PLAYBACK_EVENT.load_video, {
|
|
114
|
+
player_type: this.player.playerPluginId,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
onPlayerSessionEnd = () => {
|
|
120
|
+
// TODO: Implement method on native android, ios exists
|
|
121
|
+
this.handleAnalyticEvent(GENERAL_EVENT.session_end);
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
onPlayerSessionStart = () => {
|
|
125
|
+
// TODO: Implement method on native android, ios exists
|
|
126
|
+
|
|
127
|
+
this.handleAnalyticEvent(GENERAL_EVENT.session_start);
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
onPlayerNext = ({ nativeEvent }) => {
|
|
131
|
+
// TODO: Implement method on native
|
|
132
|
+
|
|
133
|
+
this.handleAnalyticEvent(PLAYBACK_EVENT.next, nativeEvent);
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
onPlayerPrevious = ({ nativeEvent }) => {
|
|
137
|
+
// TODO: Implement method on native
|
|
138
|
+
|
|
139
|
+
this.handleAnalyticEvent(PLAYBACK_EVENT.previous, nativeEvent);
|
|
140
|
+
};
|
|
141
|
+
}
|
|
@@ -7,6 +7,15 @@ 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";
|
|
14
|
+
|
|
15
|
+
import {
|
|
16
|
+
sendShareInitiatedEvent,
|
|
17
|
+
sendShareSuccessEvent,
|
|
18
|
+
} from "./sendShareEvent";
|
|
10
19
|
|
|
11
20
|
export {
|
|
12
21
|
sendScreenEvent,
|
|
@@ -18,4 +27,8 @@ export {
|
|
|
18
27
|
sendHeaderClickEvent,
|
|
19
28
|
sendHardwareBackButtonClickEvent,
|
|
20
29
|
sendDownloadsEvent,
|
|
30
|
+
sendSearchCanceledEvent,
|
|
31
|
+
sendSearchExecutedEvent,
|
|
32
|
+
sendShareInitiatedEvent,
|
|
33
|
+
sendShareSuccessEvent,
|
|
21
34
|
};
|
|
@@ -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,21 @@
|
|
|
1
|
+
import { log_debug } from "../logger";
|
|
2
|
+
import { postAnalyticEvent } from "../manager";
|
|
3
|
+
import { ANALYTICS_CORE_EVENTS } from "../events";
|
|
4
|
+
|
|
5
|
+
export const sendShareInitiatedEvent = (analyticsProps) => {
|
|
6
|
+
const eventName = ANALYTICS_CORE_EVENTS.SHARE_INITIATED;
|
|
7
|
+
postAnalyticEvent(eventName, analyticsProps);
|
|
8
|
+
|
|
9
|
+
log_debug(`sendShareInitiatedEvent: send event: ${eventName}`, {
|
|
10
|
+
analyticsProps,
|
|
11
|
+
});
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const sendShareSuccessEvent = (analyticsProps) => {
|
|
15
|
+
const eventName = ANALYTICS_CORE_EVENTS.SHARE_SUCCESS;
|
|
16
|
+
postAnalyticEvent(eventName, analyticsProps);
|
|
17
|
+
|
|
18
|
+
log_debug(`sendShareSuccessEvent: 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,22 @@ 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
|
+
|
|
31
|
+
export const SHARE_EVENTS = {
|
|
32
|
+
SHARE_INITIATED: "share_initiated",
|
|
33
|
+
SHARE_SUCCESS: "share_success",
|
|
34
|
+
};
|
|
35
|
+
|
|
26
36
|
export const ANALYTICS_CORE_EVENTS = {
|
|
27
37
|
...LEGACY_EVENTS,
|
|
28
38
|
...SCREEN_VIEW_EVENTS,
|
|
29
39
|
...TAPPING_EVENTS,
|
|
40
|
+
...SEARCH_EVENTS,
|
|
41
|
+
...SHARE_EVENTS,
|
|
30
42
|
};
|
|
31
43
|
|
|
32
44
|
export const WEBVIEW_EVENTS = {
|
package/analyticsUtils/index.tsx
CHANGED
|
@@ -18,6 +18,10 @@ import {
|
|
|
18
18
|
sendHardwareBackButtonClickEvent,
|
|
19
19
|
sendDownloadsEvent,
|
|
20
20
|
sendScreenEvent,
|
|
21
|
+
sendSearchCanceledEvent,
|
|
22
|
+
sendSearchExecutedEvent,
|
|
23
|
+
sendShareInitiatedEvent,
|
|
24
|
+
sendShareSuccessEvent,
|
|
21
25
|
} from "./AnalyticsEvents";
|
|
22
26
|
|
|
23
27
|
import { postAnalyticEvent } from "./manager";
|
|
@@ -66,6 +70,10 @@ type AnalyticsFunctions = {
|
|
|
66
70
|
item: ZappEntry;
|
|
67
71
|
state: OfflineItemState;
|
|
68
72
|
}) => void;
|
|
73
|
+
sendSearchCanceledEvent: (props: AnalyticsDefaultProperties) => void;
|
|
74
|
+
sendSearchExecutedEvent: (props: AnalyticsDefaultProperties) => void;
|
|
75
|
+
sendShareInitiatedEvent: (props: AnalyticsDefaultProperties) => void;
|
|
76
|
+
sendShareSuccessEvent: (props: AnalyticsDefaultProperties) => void;
|
|
69
77
|
};
|
|
70
78
|
|
|
71
79
|
type GetAnalyticsFunctions = (
|
|
@@ -102,6 +110,10 @@ export function getAnalyticsFunctions({
|
|
|
102
110
|
sendHardwareBackButtonClickEvent,
|
|
103
111
|
sendScreenEvent,
|
|
104
112
|
sendDownloadsEvent,
|
|
113
|
+
sendSearchCanceledEvent,
|
|
114
|
+
sendSearchExecutedEvent,
|
|
115
|
+
sendShareInitiatedEvent,
|
|
116
|
+
sendShareSuccessEvent,
|
|
105
117
|
};
|
|
106
118
|
}
|
|
107
119
|
|
|
@@ -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
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export const BUTTON_ACCESSIBILITY_KEYS = {
|
|
2
|
+
play: {
|
|
3
|
+
label: "accessibility_play_label",
|
|
4
|
+
hint: "accessibility_play_hint",
|
|
5
|
+
mini: {
|
|
6
|
+
label: "accessibility_play_mini_label",
|
|
7
|
+
hint: "accessibility_play_mini_hint",
|
|
8
|
+
},
|
|
9
|
+
},
|
|
10
|
+
pause: {
|
|
11
|
+
label: "accessibility_pause_label",
|
|
12
|
+
hint: "accessibility_pause_hint",
|
|
13
|
+
mini: {
|
|
14
|
+
label: "accessibility_pause_mini_label",
|
|
15
|
+
hint: "accessibility_pause_mini_hint",
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
rewind: {
|
|
19
|
+
label: "accessibility_rewind_label",
|
|
20
|
+
hint: "accessibility_rewind_hint",
|
|
21
|
+
},
|
|
22
|
+
forward: {
|
|
23
|
+
label: "accessibility_forward_label",
|
|
24
|
+
hint: "accessibility_forward_hint",
|
|
25
|
+
},
|
|
26
|
+
close: {
|
|
27
|
+
label: "accessibility_close_label",
|
|
28
|
+
hint: "accessibility_close_hint",
|
|
29
|
+
mini: {
|
|
30
|
+
label: "accessibility_close_mini_label",
|
|
31
|
+
hint: "accessibility_close_mini_hint",
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
maximize: {
|
|
35
|
+
label: "accessibility_maximize_label",
|
|
36
|
+
hint: "accessibility_maximize_hint",
|
|
37
|
+
},
|
|
38
|
+
subtitles: {
|
|
39
|
+
label: "accessibility_subtitle_label",
|
|
40
|
+
hint: "accessibility_subtitle_hint",
|
|
41
|
+
},
|
|
42
|
+
back: {
|
|
43
|
+
label: "accessibility_back_label",
|
|
44
|
+
hint: "accessibility_back_hint",
|
|
45
|
+
},
|
|
46
|
+
fullscreen: {
|
|
47
|
+
label: "accessibility_fullscreen_label",
|
|
48
|
+
hint: "accessibility_fullscreen_hint",
|
|
49
|
+
},
|
|
50
|
+
} as const;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { useEffect, useMemo } from "react";
|
|
2
|
+
import { AccessibilityManager } from "./index";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* This hook is used to instantiate the accessibility manager and update localizations used by the class.
|
|
6
|
+
* If the plugin config is not provided or it does not contain any localizations, the accessibility manager will not be updated.
|
|
7
|
+
* We use the plugin configuration because it offers a flattened structure where we can easily access the accessibility labels and hints
|
|
8
|
+
* i.e. pluginConfiguration = {
|
|
9
|
+
* accessibility_close_label: "Close",
|
|
10
|
+
* accessibility_close_hint: "Press here to close",
|
|
11
|
+
* }
|
|
12
|
+
*/
|
|
13
|
+
export const useAccessibilityManager = (
|
|
14
|
+
pluginConfiguration: Record<string, any> = {}
|
|
15
|
+
) => {
|
|
16
|
+
const accessibilityManager = useMemo(() => {
|
|
17
|
+
return AccessibilityManager.getInstance();
|
|
18
|
+
}, []);
|
|
19
|
+
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (pluginConfiguration) {
|
|
22
|
+
accessibilityManager.updateLocalizations(pluginConfiguration);
|
|
23
|
+
}
|
|
24
|
+
}, [pluginConfiguration, accessibilityManager]);
|
|
25
|
+
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
const subscription = accessibilityManager.getStateAsObservable().subscribe({
|
|
28
|
+
next: () => {
|
|
29
|
+
// TODO: handle accessibility states
|
|
30
|
+
// screenReaderEnabled: false
|
|
31
|
+
// reduceMotionEnabled: false
|
|
32
|
+
// boldTextEnabled: false
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
return () => subscription.unsubscribe();
|
|
37
|
+
}, [accessibilityManager]);
|
|
38
|
+
|
|
39
|
+
return accessibilityManager;
|
|
40
|
+
};
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { BehaviorSubject } from "rxjs";
|
|
2
|
+
import { accessibilityManagerLogger as logger } from "./logger";
|
|
3
|
+
import { TTSManager } from "../platform/platformUtils";
|
|
4
|
+
import { BUTTON_ACCESSIBILITY_KEYS } from "./const";
|
|
5
|
+
import { AccessibilityRole } from "react-native";
|
|
6
|
+
|
|
7
|
+
export class AccessibilityManager {
|
|
8
|
+
private static _instance: AccessibilityManager | null = null;
|
|
9
|
+
private state$ = new BehaviorSubject<AccessibilityState>({
|
|
10
|
+
screenReaderEnabled: false,
|
|
11
|
+
reduceMotionEnabled: false,
|
|
12
|
+
boldTextEnabled: false,
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
private announcements$ = new BehaviorSubject<{
|
|
16
|
+
message: string;
|
|
17
|
+
localizedMessage?: string | undefined;
|
|
18
|
+
timestamp: number;
|
|
19
|
+
} | null>(null);
|
|
20
|
+
|
|
21
|
+
private ttsManager = TTSManager.getInstance();
|
|
22
|
+
private localizations: { [key: string]: string } = {};
|
|
23
|
+
|
|
24
|
+
private constructor() {}
|
|
25
|
+
|
|
26
|
+
public static getInstance(): AccessibilityManager {
|
|
27
|
+
if (!AccessibilityManager._instance) {
|
|
28
|
+
AccessibilityManager._instance = new AccessibilityManager();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return AccessibilityManager._instance;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The method now accepts any object with localizations using a flattened structure
|
|
36
|
+
*
|
|
37
|
+
* i.e. { accessibility_close_label: "Close", accessibility_close_hint: "Press here to close" }
|
|
38
|
+
*
|
|
39
|
+
* No longer accepts:
|
|
40
|
+
*
|
|
41
|
+
* i.e. localizations: [{ en: { accessibility_close_label: "Close", accessibility_close_hint: "Press here to close" } }]
|
|
42
|
+
*/
|
|
43
|
+
public updateLocalizations(localizations: { [key: string]: string }) {
|
|
44
|
+
this.localizations = localizations;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
public getState(): AccessibilityState {
|
|
48
|
+
return this.state$.getValue();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
public getStateAsObservable() {
|
|
52
|
+
return this.state$.asObservable();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* text you want to be read, if you want to use localized text pass keyOfLocalizedText instead
|
|
57
|
+
* keyOfLocalizedText is the key to the localized text
|
|
58
|
+
*/
|
|
59
|
+
public readText({
|
|
60
|
+
text,
|
|
61
|
+
keyOfLocalizedText,
|
|
62
|
+
}: {
|
|
63
|
+
text: string;
|
|
64
|
+
keyOfLocalizedText?: string;
|
|
65
|
+
}) {
|
|
66
|
+
let textToRead = text;
|
|
67
|
+
|
|
68
|
+
if (keyOfLocalizedText) {
|
|
69
|
+
if (!this.localizations) {
|
|
70
|
+
logger.error(
|
|
71
|
+
"Attempting to use localized key without initialized localizations"
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const localizedMessage = this.getLocalizedMessage(keyOfLocalizedText);
|
|
78
|
+
|
|
79
|
+
if (!localizedMessage) {
|
|
80
|
+
logger.warn(`No localization found for key: ${keyOfLocalizedText}`);
|
|
81
|
+
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
textToRead = localizedMessage;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (this.ttsManager) {
|
|
89
|
+
logger.debug({
|
|
90
|
+
message: `Reading text: ${textToRead}`,
|
|
91
|
+
data: {
|
|
92
|
+
textToRead,
|
|
93
|
+
text,
|
|
94
|
+
keyOfLocalizedText,
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
this.ttsManager.readText(textToRead);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
this.announcements$.next({
|
|
102
|
+
message: textToRead,
|
|
103
|
+
localizedMessage: keyOfLocalizedText ? textToRead : undefined,
|
|
104
|
+
timestamp: Date.now(),
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
public getButtonAccessibilityProps(buttonName: string): AccessibilityProps {
|
|
109
|
+
const buttonConfig = BUTTON_ACCESSIBILITY_KEYS[buttonName];
|
|
110
|
+
|
|
111
|
+
if (!buttonConfig) {
|
|
112
|
+
return {
|
|
113
|
+
accessibilityLabel: buttonName,
|
|
114
|
+
accessibilityHint: `Press button to perform action on ${buttonName}`,
|
|
115
|
+
"aria-label": buttonName,
|
|
116
|
+
"aria-description": `Press button to perform action on ${buttonName}`,
|
|
117
|
+
accessibilityRole: "button" as AccessibilityRole,
|
|
118
|
+
"aria-role": "button",
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const labelKey = buttonConfig.label;
|
|
123
|
+
const hintKey = buttonConfig.hint;
|
|
124
|
+
|
|
125
|
+
const label = this.getLocalizedMessage(labelKey) || buttonName;
|
|
126
|
+
|
|
127
|
+
const hint =
|
|
128
|
+
this.getLocalizedMessage(hintKey) ||
|
|
129
|
+
`Press button to perform action on ${buttonName}`;
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
accessibilityLabel: label,
|
|
133
|
+
accessibilityHint: hint,
|
|
134
|
+
"aria-label": label,
|
|
135
|
+
"aria-description": hint,
|
|
136
|
+
accessibilityRole: "button" as AccessibilityRole,
|
|
137
|
+
"aria-role": "button",
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
public getInputAccessibilityProps(inputName: string): AccessibilityProps {
|
|
142
|
+
return {
|
|
143
|
+
accessibilityLabel: inputName,
|
|
144
|
+
accessibilityHint: `Enter text into ${inputName}`,
|
|
145
|
+
"aria-label": inputName,
|
|
146
|
+
"aria-description": `Enter text into ${inputName}`,
|
|
147
|
+
accessibilityRole: "textbox" as AccessibilityRole,
|
|
148
|
+
"aria-role": "textbox",
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
public getLocalizedMessage(key: string): string | void {
|
|
153
|
+
if (!key) return;
|
|
154
|
+
|
|
155
|
+
if (!this.localizations) {
|
|
156
|
+
logger.error(
|
|
157
|
+
"Attempting to use localized key without initialized localizations",
|
|
158
|
+
{ key }
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return this.localizations[key];
|
|
165
|
+
}
|
|
166
|
+
}
|