@vouchfor/embeds 0.0.0-experiment.70f6f8d → 0.0.0-experiment.74a768c

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.70f6f8d",
3
+ "version": "0.0.0-experiment.74a768c",
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.70f6f8d",
39
+ "@vouchfor/media-player": "0.0.0-experiment.74a768c",
40
40
  "uuid": "^9.0.1"
41
41
  },
42
42
  "peerDependencies": {
@@ -6,13 +6,13 @@ import type { ReactiveController, ReactiveControllerHost } from 'lit';
6
6
 
7
7
  import { getEnvUrls } from '~/utils/env';
8
8
 
9
- const STREAMED_THROTTLE = 10000;
9
+ // In seconds due to checking against node.currentTime
10
+ const STREAMED_THROTTLE = 10;
10
11
 
11
12
  type EmbedHost = ReactiveControllerHost & Embed;
12
13
 
13
14
  type TrackingEvent = 'VOUCH_LOADED' | 'VOUCH_RESPONSE_VIEWED' | 'VIDEO_PLAYED' | 'VIDEO_STREAMED';
14
15
  type TrackingPayload = {
15
- vouchId: string;
16
16
  answerId?: string;
17
17
  streamStart?: number;
18
18
  streamEnd?: number;
@@ -36,8 +36,9 @@ class TrackingController implements ReactiveController {
36
36
  private _hasPlayed = false;
37
37
  private _hasLoaded: BooleanMap = {};
38
38
  private _answersViewed: BooleanMap = {};
39
- private _streamedTime: TimeMap = {};
40
- private _streamedPrevTimestamp: TimeMap = {};
39
+ private _streamStartTime: TimeMap = {};
40
+ private _streamLatestTime: TimeMap = {};
41
+ private _currentlyPlayingVideo: VideoEventDetail | null = null;
41
42
 
42
43
  constructor(host: EmbedHost) {
43
44
  this.host = host;
@@ -128,26 +129,30 @@ class TrackingController implements ReactiveController {
128
129
  };
129
130
  };
130
131
 
131
- private _sendTrackingEvent = (event: TrackingEvent, payload: TrackingPayload) => {
132
+ private _sendTrackingEvent = (event: TrackingEvent, payload?: TrackingPayload) => {
133
+ const vouchId = this._findVouchId();
134
+
135
+ if (!vouchId || this.host.disableTracking) {
136
+ return;
137
+ }
138
+
132
139
  const { publicApiUrl } = getEnvUrls(this.host.env);
133
140
  const { client, tab, request, visitor } = this._getUids();
134
141
 
135
- if (this.host.enableTracking) {
136
- navigator.sendBeacon(
137
- `${publicApiUrl}/api/events`,
138
- JSON.stringify({
139
- event,
140
- payload,
141
- context: {
142
- 'x-uid-client': client,
143
- 'x-uid-tab': tab,
144
- 'x-uid-request': request,
145
- 'x-uid-visitor': visitor,
146
- 'x-reporting-metadata': this._getReportingMetadata()
147
- }
148
- })
149
- );
150
- }
142
+ navigator.sendBeacon(
143
+ `${publicApiUrl}/api/events`,
144
+ JSON.stringify({
145
+ event,
146
+ payload,
147
+ context: {
148
+ 'x-uid-client': client,
149
+ 'x-uid-tab': tab,
150
+ 'x-uid-request': request,
151
+ 'x-uid-visitor': visitor,
152
+ 'x-reporting-metadata': this._getReportingMetadata()
153
+ }
154
+ })
155
+ );
151
156
  };
152
157
 
153
158
  private _handleVouchLoaded = ({ detail: vouchId }: CustomEvent<string>) => {
@@ -157,96 +162,74 @@ class TrackingController implements ReactiveController {
157
162
 
158
163
  // Only send loaded event once per session
159
164
  if (!this._hasLoaded[vouchId]) {
160
- this._sendTrackingEvent('VOUCH_LOADED', {
161
- vouchId
162
- });
165
+ this._sendTrackingEvent('VOUCH_LOADED');
163
166
  this._hasLoaded[vouchId] = true;
164
167
  }
165
168
  };
166
169
 
167
170
  private _handlePlay = () => {
168
- const vouchId = this._findVouchId();
169
-
170
- if (!vouchId) {
171
- return;
172
- }
173
-
174
171
  // Only send the video played event once per session
175
172
  if (!this._hasPlayed) {
176
173
  this._sendTrackingEvent('VIDEO_PLAYED', {
177
- vouchId,
178
174
  streamStart: this.host.currentTime
179
175
  });
180
176
  this._hasPlayed = true;
181
177
  }
182
178
  };
183
179
 
184
- private _handleVideoPlay = ({ detail: { id, node } }: CustomEvent<VideoEventDetail>) => {
185
- const vouchId = this._findVouchId();
186
-
187
- if (!vouchId) {
188
- return;
189
- }
180
+ private _handleVideoPlay = ({ detail: { id, key, node } }: CustomEvent<VideoEventDetail>) => {
190
181
  // Only increment play count once per session
191
- if (!this._answersViewed[id]) {
182
+ if (!this._answersViewed[key]) {
192
183
  this._sendTrackingEvent('VOUCH_RESPONSE_VIEWED', {
193
- vouchId,
194
184
  answerId: id
195
185
  });
196
- this._answersViewed[id] = true;
186
+ this._answersViewed[key] = true;
197
187
  }
198
- this._streamedTime[id] = node.currentTime;
199
- this._streamedPrevTimestamp[id] = Date.now();
200
- };
201
188
 
202
- private _handleVideoTimeUpdate = ({ detail: { id, node } }: CustomEvent<VideoEventDetail>) => {
203
- const vouchId = this._findVouchId();
189
+ this._streamStartTime[key] = node.currentTime;
190
+ this._streamLatestTime[key] = node.currentTime;
191
+ };
204
192
 
205
- if (!vouchId) {
206
- return;
193
+ private _handleVideoTimeUpdate = ({ detail: { id, key, node } }: CustomEvent<VideoEventDetail>) => {
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;
207
198
  }
208
- const currentTimestamp = Date.now();
199
+
209
200
  if (
210
- node.currentTime &&
211
201
  !node.paused &&
212
202
  !this.host.paused &&
213
203
  // Only fire the video seeked event when this video is the active one
214
204
  id === this.host.scene?.video?.id &&
215
205
  // Throttle the frequency that we send streamed events while playing
216
- currentTimestamp - this._streamedPrevTimestamp[id] > STREAMED_THROTTLE
206
+ this._streamLatestTime[key] - this._streamStartTime[key] > STREAMED_THROTTLE
217
207
  ) {
218
208
  this._sendTrackingEvent('VIDEO_STREAMED', {
219
- vouchId,
220
209
  answerId: id,
221
- streamStart: this._streamedTime[id],
222
- streamEnd: node.currentTime
210
+ streamStart: this._streamStartTime[key],
211
+ streamEnd: this._streamLatestTime[key]
223
212
  });
224
- this._streamedTime[id] = node.currentTime;
225
- this._streamedPrevTimestamp[id] = currentTimestamp;
226
- }
227
- };
228
213
 
229
- private _handleVideoPause = ({ detail: { id, node } }: CustomEvent<VideoEventDetail>) => {
230
- const vouchId = this._findVouchId();
231
-
232
- if (!vouchId) {
233
- return;
214
+ this._streamStartTime[key] = node.currentTime;
234
215
  }
216
+ };
235
217
 
236
- // Don't send a tracking event if the video pauses when seeking backwards
237
- if (node.currentTime > this._streamedTime[id]) {
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]) {
238
221
  // Send a video streamed event any time the video pauses then reset the streamed state
239
222
  // We do this to capture the last bit of time that the video was played between the previous
240
223
  // stream event and the video being paused manually or stopping because it ended
241
224
  this._sendTrackingEvent('VIDEO_STREAMED', {
242
- vouchId,
243
225
  answerId: id,
244
- streamStart: this._streamedTime[id],
245
- streamEnd: node.currentTime
226
+ streamStart: this._streamStartTime[key],
227
+ streamEnd: this._streamLatestTime[key]
246
228
  });
247
229
  }
248
- delete this._streamedTime[id];
249
- delete this._streamedPrevTimestamp[id];
230
+ this._currentlyPlayingVideo = null;
231
+ delete this._streamStartTime[key];
232
+ delete this._streamLatestTime[key];
250
233
  };
251
234
 
252
235
  hostConnected() {
@@ -260,6 +243,20 @@ class TrackingController implements ReactiveController {
260
243
  }
261
244
 
262
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
+
263
260
  this.host.removeEventListener('vouch:loaded', this._handleVouchLoaded);
264
261
  this.host.mediaPlayer?.removeEventListener('play', this._handlePlay);
265
262
  this.host.mediaPlayer?.removeEventListener('video:play', this._handleVideoPlay);
@@ -17,7 +17,7 @@ import '@vouchfor/media-player';
17
17
  type EmbedProps = Pick<MediaPlayerProps, 'data' | 'aspectRatio' | 'preload' | 'autoplay' | 'controls'> & {
18
18
  env: Environment;
19
19
  apiKey: string;
20
- enableTracking?: boolean;
20
+ disableTracking?: boolean;
21
21
  trackingSource?: string;
22
22
  vouchId?: string;
23
23
  templateId?: string;
@@ -33,7 +33,7 @@ class Embed extends LitElement {
33
33
 
34
34
  @property({ type: String }) env: EmbedProps['env'] = 'prod';
35
35
  @property({ type: String }) apiKey: EmbedProps['apiKey'] = '';
36
- @property({ type: Boolean }) enableTracking: EmbedProps['enableTracking'] = true;
36
+ @property({ type: Boolean }) disableTracking: EmbedProps['disableTracking'] = false;
37
37
  @property({ type: String }) trackingSource: EmbedProps['trackingSource'] = 'embed';
38
38
 
39
39
  @property({ type: Array }) controls: EmbedProps['controls'];
@@ -59,6 +59,7 @@ class Embed extends LitElement {
59
59
  'waiting',
60
60
 
61
61
  'video:loadeddata',
62
+ 'video:seeking',
62
63
  'video:seeked',
63
64
  'video:play',
64
65
  'video:playing',