@vouchfor/embeds 0.0.0-experiment.ab40ec5 → 0.0.0-experiment.ace6cf6

Sign up to get free protection for your applications and to get access to all the features.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vouchfor/embeds",
3
- "version": "0.0.0-experiment.ab40ec5",
3
+ "version": "0.0.0-experiment.ace6cf6",
4
4
  "license": "MIT",
5
5
  "author": "Aaron Williams",
6
6
  "main": "dist/es/embeds.js",
@@ -36,7 +36,7 @@
36
36
  },
37
37
  "dependencies": {
38
38
  "@lit/task": "^1.0.0",
39
- "@vouchfor/media-player": "0.0.0-experiment.ab40ec5",
39
+ "@vouchfor/media-player": "0.0.0-experiment.ace6cf6",
40
40
  "uuid": "^9.0.1"
41
41
  },
42
42
  "peerDependencies": {
@@ -1,4 +1,3 @@
1
- /* eslint-disable max-lines */
2
1
  import { v4 as uuidv4 } from 'uuid';
3
2
 
4
3
  import type { Embed } from '..';
@@ -7,13 +6,13 @@ import type { ReactiveController, ReactiveControllerHost } from 'lit';
7
6
 
8
7
  import { getEnvUrls } from '~/utils/env';
9
8
 
10
- const STREAMED_THROTTLE = 10000;
9
+ // In seconds due to checking against node.currentTime
10
+ const STREAMED_THROTTLE = 10;
11
11
 
12
12
  type EmbedHost = ReactiveControllerHost & Embed;
13
13
 
14
14
  type TrackingEvent = 'VOUCH_LOADED' | 'VOUCH_RESPONSE_VIEWED' | 'VIDEO_PLAYED' | 'VIDEO_STREAMED';
15
15
  type TrackingPayload = {
16
- vouchId: string;
17
16
  answerId?: string;
18
17
  streamStart?: number;
19
18
  streamEnd?: number;
@@ -37,9 +36,9 @@ class TrackingController implements ReactiveController {
37
36
  private _hasPlayed = false;
38
37
  private _hasLoaded: BooleanMap = {};
39
38
  private _answersViewed: BooleanMap = {};
40
- private _streamedTime: TimeMap = {};
39
+ private _streamStartTime: TimeMap = {};
41
40
  private _streamLatestTime: TimeMap = {};
42
- private _streamedPrevTimestamp: TimeMap = {};
41
+ private _currentlyPlayingVideo: VideoEventDetail | null = null;
43
42
 
44
43
  constructor(host: EmbedHost) {
45
44
  this.host = host;
@@ -130,14 +129,16 @@ class TrackingController implements ReactiveController {
130
129
  };
131
130
  };
132
131
 
133
- private _sendTrackingEvent = (event: TrackingEvent, payload: TrackingPayload) => {
134
- const { publicApiUrl } = getEnvUrls(this.host.env);
135
- const { client, tab, request, visitor } = this._getUids();
132
+ private _sendTrackingEvent = (event: TrackingEvent, payload?: TrackingPayload) => {
133
+ const vouchId = this._findVouchId();
136
134
 
137
- if (this.host.disableTracking) {
135
+ if (!vouchId || this.host.disableTracking) {
138
136
  return;
139
137
  }
140
138
 
139
+ const { publicApiUrl } = getEnvUrls(this.host.env);
140
+ const { client, tab, request, visitor } = this._getUids();
141
+
141
142
  navigator.sendBeacon(
142
143
  `${publicApiUrl}/api/events`,
143
144
  JSON.stringify({
@@ -161,24 +162,15 @@ class TrackingController implements ReactiveController {
161
162
 
162
163
  // Only send loaded event once per session
163
164
  if (!this._hasLoaded[vouchId]) {
164
- this._sendTrackingEvent('VOUCH_LOADED', {
165
- vouchId
166
- });
165
+ this._sendTrackingEvent('VOUCH_LOADED');
167
166
  this._hasLoaded[vouchId] = true;
168
167
  }
169
168
  };
170
169
 
171
170
  private _handlePlay = () => {
172
- const vouchId = this._findVouchId();
173
-
174
- if (!vouchId) {
175
- return;
176
- }
177
-
178
171
  // Only send the video played event once per session
179
172
  if (!this._hasPlayed) {
180
173
  this._sendTrackingEvent('VIDEO_PLAYED', {
181
- vouchId,
182
174
  streamStart: this.host.currentTime
183
175
  });
184
176
  this._hasPlayed = true;
@@ -186,100 +178,58 @@ class TrackingController implements ReactiveController {
186
178
  };
187
179
 
188
180
  private _handleVideoPlay = ({ detail: { id, key, node } }: CustomEvent<VideoEventDetail>) => {
189
- const vouchId = this._findVouchId();
190
-
191
- if (!vouchId) {
192
- return;
193
- }
194
-
195
181
  // Only increment play count once per session
196
182
  if (!this._answersViewed[key]) {
197
183
  this._sendTrackingEvent('VOUCH_RESPONSE_VIEWED', {
198
- vouchId,
199
184
  answerId: id
200
185
  });
201
186
  this._answersViewed[key] = true;
202
187
  }
203
188
 
204
- this._streamedTime[key] = node.currentTime;
189
+ this._streamStartTime[key] = node.currentTime;
205
190
  this._streamLatestTime[key] = node.currentTime;
206
- this._streamedPrevTimestamp[key] = Date.now();
207
- };
208
-
209
- private _handleVideoSeeking = ({ detail: { id, key } }: CustomEvent<VideoEventDetail>) => {
210
- const vouchId = this._findVouchId();
211
-
212
- if (!vouchId) {
213
- return;
214
- }
215
-
216
- if (this._streamLatestTime[key]) {
217
- this._sendTrackingEvent('VIDEO_STREAMED', {
218
- vouchId,
219
- answerId: id,
220
- streamStart: this._streamedTime[key],
221
- streamEnd: this._streamLatestTime[key]
222
- });
223
- }
224
-
225
- delete this._streamedTime[key];
226
- delete this._streamLatestTime[key];
227
- delete this._streamedPrevTimestamp[key];
228
191
  };
229
192
 
230
193
  private _handleVideoTimeUpdate = ({ detail: { id, key, node } }: CustomEvent<VideoEventDetail>) => {
231
- const vouchId = this._findVouchId();
232
-
233
- if (!vouchId) {
234
- return;
194
+ // We only want to count any time that the video is actually playing
195
+ if (!this.host.paused) {
196
+ this._currentlyPlayingVideo = { id, key, node };
197
+ this._streamLatestTime[key] = node.currentTime;
235
198
  }
236
199
 
237
- const currentTimestamp = Date.now();
238
200
  if (
239
- node.currentTime &&
240
201
  !node.paused &&
241
202
  !this.host.paused &&
242
203
  // Only fire the video seeked event when this video is the active one
243
204
  id === this.host.scene?.video?.id &&
244
205
  // Throttle the frequency that we send streamed events while playing
245
- currentTimestamp - this._streamedPrevTimestamp[key] > STREAMED_THROTTLE
206
+ this._streamLatestTime[key] - this._streamStartTime[key] > STREAMED_THROTTLE
246
207
  ) {
247
208
  this._sendTrackingEvent('VIDEO_STREAMED', {
248
- vouchId,
249
209
  answerId: id,
250
- streamStart: this._streamedTime[key],
251
- streamEnd: node.currentTime
210
+ streamStart: this._streamStartTime[key],
211
+ streamEnd: this._streamLatestTime[key]
252
212
  });
253
- this._streamedTime[key] = node.currentTime;
254
- this._streamedPrevTimestamp[key] = currentTimestamp;
255
- }
256
213
 
257
- this._streamLatestTime[key] = node.currentTime;
258
- };
259
-
260
- private _handleVideoPause = ({ detail: { id, key, node } }: CustomEvent<VideoEventDetail>) => {
261
- const vouchId = this._findVouchId();
262
-
263
- if (!vouchId) {
264
- return;
214
+ this._streamStartTime[key] = node.currentTime;
265
215
  }
216
+ };
266
217
 
267
- // Don't send a tracking event if the video pauses when seeking backwards
268
- if (node.currentTime > this._streamedTime[key]) {
218
+ private _handleVideoPause = ({ detail: { id, key } }: CustomEvent<VideoEventDetail>) => {
219
+ // Don't send a tracking event when seeking backwards
220
+ if (this._streamLatestTime[key] > this._streamStartTime[key]) {
269
221
  // Send a video streamed event any time the video pauses then reset the streamed state
270
222
  // We do this to capture the last bit of time that the video was played between the previous
271
223
  // stream event and the video being paused manually or stopping because it ended
272
224
  this._sendTrackingEvent('VIDEO_STREAMED', {
273
- vouchId,
274
225
  answerId: id,
275
- streamStart: this._streamedTime[key],
276
- streamEnd: node.currentTime
226
+ streamStart: this._streamStartTime[key],
227
+ streamEnd: this._streamLatestTime[key]
277
228
  });
278
229
  }
279
-
280
- delete this._streamedTime[key];
230
+ this._currentlyPlayingVideo = null;
231
+ delete this._streamStartTime[key];
281
232
  delete this._streamLatestTime[key];
282
- delete this._streamedPrevTimestamp[key];
283
233
  };
284
234
 
285
235
  hostConnected() {
@@ -287,17 +237,29 @@ class TrackingController implements ReactiveController {
287
237
  this.host.addEventListener('vouch:loaded', this._handleVouchLoaded);
288
238
  this.host.mediaPlayer?.addEventListener('play', this._handlePlay);
289
239
  this.host.mediaPlayer?.addEventListener('video:play', this._handleVideoPlay);
290
- this.host.mediaPlayer?.addEventListener('video:seeking', this._handleVideoSeeking);
291
240
  this.host.mediaPlayer?.addEventListener('video:pause', this._handleVideoPause);
292
241
  this.host.mediaPlayer?.addEventListener('video:timeupdate', this._handleVideoTimeUpdate);
293
242
  });
294
243
  }
295
244
 
296
245
  hostDisconnected() {
246
+ if (this._currentlyPlayingVideo) {
247
+ const { id, key } = this._currentlyPlayingVideo;
248
+ if (this._streamLatestTime[key] > this._streamStartTime[key]) {
249
+ // Send a video streamed event any time the video pauses then reset the streamed state
250
+ // We do this to capture the last bit of time that the video was played between the previous
251
+ // stream event and the video being paused manually or stopping because it ended
252
+ this._sendTrackingEvent('VIDEO_STREAMED', {
253
+ answerId: id,
254
+ streamStart: this._streamStartTime[key],
255
+ streamEnd: this._streamLatestTime[key]
256
+ });
257
+ }
258
+ }
259
+
297
260
  this.host.removeEventListener('vouch:loaded', this._handleVouchLoaded);
298
261
  this.host.mediaPlayer?.removeEventListener('play', this._handlePlay);
299
262
  this.host.mediaPlayer?.removeEventListener('video:play', this._handleVideoPlay);
300
- this.host.mediaPlayer?.removeEventListener('video:seeking', this._handleVideoSeeking);
301
263
  this.host.mediaPlayer?.removeEventListener('video:pause', this._handleVideoPause);
302
264
  this.host.mediaPlayer?.removeEventListener('video:timeupdate', this._handleVideoTimeUpdate);
303
265
  }