Ver código fonte

feat(analytics): local tracks duration event.

master
Hristo Terezov 6 anos atrás
pai
commit
70921bb6ef

+ 22
- 0
react/features/analytics/AnalyticsEvents.js Ver arquivo

@@ -372,6 +372,28 @@ export function createLiveStreamingDialogEvent(dialogName, buttonName) {
372 372
     };
373 373
 }
374 374
 
375
+/**
376
+ * Creates an event with the local tracks duration.
377
+ *
378
+ * @param {Object} duration - The object with the duration of the local tracks.
379
+ * @returns {Object} The event in a format suitable for sending via
380
+ * sendAnalytics.
381
+ */
382
+export function createLocalTracksDurationEvent(duration) {
383
+    const { audio, video, conference } = duration;
384
+    const { camera, desktop } = video;
385
+
386
+    return {
387
+        action: 'local.tracks.durations',
388
+        attributes: {
389
+            audio: audio.value,
390
+            camera: camera.value,
391
+            conference: conference.value,
392
+            desktop: desktop.value
393
+        }
394
+    };
395
+}
396
+
375 397
 /**
376 398
  * Creates an event which indicates that an action related to recording has
377 399
  * occured.

+ 11
- 0
react/features/analytics/actionTypes.js Ver arquivo

@@ -0,0 +1,11 @@
1
+// @flow
2
+
3
+/**
4
+ * The type of (redux) action which signals that local media duration has changed.
5
+ *
6
+ * {
7
+ *     type: UPDATE_LOCAL_TRACKS_DURATION,
8
+ *     localTracksDuration: Object
9
+ * }
10
+ */
11
+export const UPDATE_LOCAL_TRACKS_DURATION = 'UPDATE_LOCAL_TRACKS_DURATION';

+ 1
- 0
react/features/analytics/index.js Ver arquivo

@@ -2,3 +2,4 @@ export * from './AnalyticsEvents';
2 2
 export * from './functions';
3 3
 
4 4
 import './middleware';
5
+import './reducer';

+ 130
- 8
react/features/analytics/middleware.js Ver arquivo

@@ -1,8 +1,74 @@
1
-import { SET_ROOM } from '../base/conference';
1
+// @flow
2
+
3
+import {
4
+    CONFERENCE_JOINED,
5
+    CONFERENCE_WILL_LEAVE,
6
+    SET_ROOM
7
+} from '../base/conference';
2 8
 import { SET_CONFIG } from '../base/config';
3 9
 import { MiddlewareRegistry } from '../base/redux';
10
+import {
11
+    getLocalAudioTrack,
12
+    getLocalVideoTrack,
13
+    TRACK_ADDED,
14
+    TRACK_REMOVED,
15
+    TRACK_UPDATED
16
+} from '../base/tracks';
17
+
18
+import { UPDATE_LOCAL_TRACKS_DURATION } from './actionTypes';
19
+import { createLocalTracksDurationEvent } from './AnalyticsEvents';
20
+import { initAnalytics, resetAnalytics, sendAnalytics } from './functions';
21
+
22
+/**
23
+ * Calculates the duration of the local tracks.
24
+ *
25
+ * @param {Object} state - The redux state.
26
+ * @returns {Object} - The local tracks duration.
27
+ */
28
+function calculateLocalTrackDuration(state) {
29
+    const now = Date.now();
30
+    const { localTracksDuration } = state['features/analytics'];
31
+    const { conference } = state['features/base/conference'];
32
+    const { audio, video } = localTracksDuration;
33
+    const { camera, desktop } = video;
34
+    const tracks = state['features/base/tracks'];
35
+    const audioTrack = getLocalAudioTrack(tracks);
36
+    const videoTrack = getLocalVideoTrack(tracks);
37
+    const newDuration = { ...localTracksDuration };
38
+
39
+    if (!audioTrack || audioTrack.muted || !conference) {
40
+        newDuration.audio = {
41
+            startedTime: -1,
42
+            value: audio.value + (audio.startedTime === -1 ? 0 : now - audio.startedTime)
43
+        };
44
+    } else if (audio.startedTime === -1) {
45
+        newDuration.audio.startedTime = now;
46
+    }
47
+
48
+    if (!videoTrack || videoTrack.muted || !conference) {
49
+        newDuration.video = {
50
+            camera: {
51
+                startedTime: -1,
52
+                value: camera.value + (camera.startedTime === -1 ? 0 : now - camera.startedTime)
53
+            },
54
+            desktop: {
55
+                startedTime: -1,
56
+                value: desktop.value + (desktop.startedTime === -1 ? 0 : now - desktop.startedTime)
57
+            }
58
+        };
59
+    } else {
60
+        const { videoType } = videoTrack;
4 61
 
5
-import { initAnalytics, resetAnalytics } from './functions';
62
+        if (video[videoType].startedTime === -1) {
63
+            newDuration.video[videoType].startedTime = now;
64
+        }
65
+    }
66
+
67
+    return {
68
+        ...localTracksDuration,
69
+        ...newDuration
70
+    };
71
+}
6 72
 
7 73
 /**
8 74
  * Middleware which intercepts config actions to handle evaluating analytics
@@ -12,25 +78,81 @@ import { initAnalytics, resetAnalytics } from './functions';
12 78
  * @returns {Function}
13 79
  */
14 80
 MiddlewareRegistry.register(store => next => action => {
15
-    switch (action.type) {
16
-    case SET_CONFIG: {
81
+    if (action.type === SET_CONFIG) {
17 82
         if (navigator.product === 'ReactNative') {
18 83
             // Reseting the analytics is currently not needed for web because
19 84
             // the user will be redirected to another page and new instance of
20 85
             // Analytics will be created and initialized.
21 86
             resetAnalytics();
22 87
         }
88
+    }
23 89
 
90
+    const result = next(action);
91
+
92
+    switch (action.type) {
93
+    case CONFERENCE_JOINED: {
94
+        const { dispatch, getState } = store;
95
+        const state = getState();
96
+
97
+        dispatch({
98
+            type: UPDATE_LOCAL_TRACKS_DURATION,
99
+            localTracksDuration: {
100
+                ...calculateLocalTrackDuration(state),
101
+                conference: {
102
+                    startedTime: Date.now(),
103
+                    value: 0
104
+                }
105
+            }
106
+        });
24 107
         break;
25 108
     }
26
-    case SET_ROOM: {
27
-        const result = next(action);
28 109
 
110
+    case CONFERENCE_WILL_LEAVE: {
111
+        const { dispatch, getState } = store;
112
+        const state = getState();
113
+        const { localTracksDuration } = state['features/analytics'];
114
+        const newLocalTracksDuration = {
115
+            ...calculateLocalTrackDuration(state),
116
+            conference: {
117
+                startedTime: -1,
118
+                value: Date.now() - localTracksDuration.conference.startedTime
119
+            }
120
+        };
121
+
122
+        sendAnalytics(createLocalTracksDurationEvent(newLocalTracksDuration));
123
+
124
+        dispatch({
125
+            type: UPDATE_LOCAL_TRACKS_DURATION,
126
+            localTracksDuration: newLocalTracksDuration
127
+        });
128
+        break;
129
+    }
130
+    case SET_ROOM: {
29 131
         initAnalytics(store);
132
+        break;
133
+    }
134
+    case TRACK_ADDED:
135
+    case TRACK_REMOVED:
136
+    case TRACK_UPDATED: {
137
+        const { dispatch, getState } = store;
138
+        const state = getState();
139
+        const { localTracksDuration } = state['features/analytics'];
30 140
 
31
-        return result;
141
+        if (localTracksDuration.conference.startedTime === -1) {
142
+            // We don't want to track the media duration if the conference is not joined yet because otherwise we won't
143
+            // be able to compare them with the conference duration (from conference join to conference will leave).
144
+            break;
145
+        }
146
+        dispatch({
147
+            type: UPDATE_LOCAL_TRACKS_DURATION,
148
+            localTracksDuration: {
149
+                ...localTracksDuration,
150
+                ...calculateLocalTrackDuration(state)
151
+            }
152
+        });
153
+        break;
32 154
     }
33 155
     }
34 156
 
35
-    return next(action);
157
+    return result;
36 158
 });

+ 51
- 0
react/features/analytics/reducer.js Ver arquivo

@@ -0,0 +1,51 @@
1
+// @flow
2
+
3
+import { ReducerRegistry } from '../base/redux';
4
+
5
+import { UPDATE_LOCAL_TRACKS_DURATION } from './actionTypes';
6
+
7
+/**
8
+ * Initial state.
9
+ */
10
+const DEFAULT_STATE = {
11
+    localTracksDuration: {
12
+        audio: {
13
+            startedTime: -1,
14
+            value: 0
15
+        },
16
+        video: {
17
+            camera: {
18
+                startedTime: -1,
19
+                value: 0
20
+            },
21
+            desktop: {
22
+                startedTime: -1,
23
+                value: 0
24
+            }
25
+        },
26
+        conference: {
27
+            startedTime: -1,
28
+            value: 0
29
+        }
30
+    }
31
+};
32
+
33
+/**
34
+ * Listen for actions which changes the state of the analytics feature.
35
+ *
36
+ * @param {Object} state - The Redux state of the feature features/analytics.
37
+ * @param {Object} action - Action object.
38
+ * @param {string} action.type - Type of action.
39
+ * @returns {Object}
40
+ */
41
+ReducerRegistry.register('features/analytics', (state = DEFAULT_STATE, action) => {
42
+    switch (action.type) {
43
+    case UPDATE_LOCAL_TRACKS_DURATION:
44
+        return {
45
+            ...state,
46
+            localTracksDuration: action.localTracksDuration
47
+        };
48
+    default:
49
+        return state;
50
+    }
51
+});

Carregando…
Cancelar
Salvar