@vouchfor/embeds 0.0.0-experiment.93ea548 → 0.0.0-experiment.a2cd27b

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vouchfor/embeds",
3
- "version": "0.0.0-experiment.93ea548",
3
+ "version": "0.0.0-experiment.a2cd27b",
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.93ea548",
39
+ "@vouchfor/media-player": "0.0.0-experiment.a2cd27b",
40
40
  "uuid": "^9.0.1"
41
41
  },
42
42
  "peerDependencies": {
@@ -51,7 +51,7 @@ class FetcherController {
51
51
  // so to ensure that the cache stays up to date, whenever we detect a cache hit we trigger another
52
52
  // API call with the `Cache-Control` header which will re-fill the cache
53
53
  const resCacheCheck = res?.headers?.get('X-Cache-Check');
54
- if (resCacheCheck && resCacheCheck !== cacheCheck) {
54
+ if (resCacheCheck !== cacheCheck) {
55
55
  fetch(`${embedApiUrl}/vouches/${vouchId}`, {
56
56
  method: 'GET',
57
57
  headers: [
@@ -81,7 +81,7 @@ class FetcherController {
81
81
  // so to ensure that the cache stays up to date, whenever we detect a cache hit we trigger another
82
82
  // API call with the `Cache-Control` header which will re-fill the cache
83
83
  const resCacheCheck = res?.headers?.get('X-Cache-Check');
84
- if (resCacheCheck && resCacheCheck !== cacheCheck) {
84
+ if (resCacheCheck !== cacheCheck) {
85
85
  fetch(`${embedApiUrl}/templates/${templateId}`, {
86
86
  method: 'GET',
87
87
  headers: [
@@ -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
  }