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

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.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',