@vouchfor/embeds 0.0.0-experiment.853d689 → 0.0.0-experiment.896f0e4
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/dist/es/components/Embed/controllers/tracking.d.ts +2 -3
- package/dist/es/embeds.js +357 -367
- package/dist/es/embeds.js.map +1 -1
- package/dist/iife/embeds.iife.js +93 -89
- package/dist/iife/embeds.iife.js.map +1 -1
- package/package.json +2 -2
- package/src/components/Embed/controllers/fetcher.ts +2 -2
- package/src/components/Embed/controllers/tracking.ts +53 -90
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@vouchfor/embeds",
|
3
|
-
"version": "0.0.0-experiment.
|
3
|
+
"version": "0.0.0-experiment.896f0e4",
|
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.
|
39
|
+
"@vouchfor/media-player": "0.0.0-experiment.896f0e4",
|
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
|
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
|
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
|
-
|
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
|
39
|
+
private _streamStartTime: TimeMap = {};
|
41
40
|
private _streamLatestTime: TimeMap = {};
|
42
|
-
private
|
41
|
+
private _currentlyPlayingVideo: VideoEventDetail | null = null;
|
43
42
|
|
44
43
|
constructor(host: EmbedHost) {
|
45
44
|
this.host = host;
|
@@ -48,11 +47,9 @@ class TrackingController implements ReactiveController {
|
|
48
47
|
|
49
48
|
private _findVouchId() {
|
50
49
|
if (this.host.vouch) {
|
51
|
-
if ('uuid' in this.host.vouch) {
|
52
|
-
return this.host.vouch.uuid;
|
53
|
-
}
|
54
50
|
return this.host.vouch.id;
|
55
51
|
}
|
52
|
+
return null;
|
56
53
|
}
|
57
54
|
|
58
55
|
private _createVisitor = (visitorId: string) => {
|
@@ -130,19 +127,24 @@ class TrackingController implements ReactiveController {
|
|
130
127
|
};
|
131
128
|
};
|
132
129
|
|
133
|
-
private _sendTrackingEvent = (event: TrackingEvent, payload
|
134
|
-
const
|
135
|
-
const { client, tab, request, visitor } = this._getUids();
|
130
|
+
private _sendTrackingEvent = (event: TrackingEvent, payload?: TrackingPayload) => {
|
131
|
+
const vouchId = this._findVouchId();
|
136
132
|
|
137
|
-
if (this.host.disableTracking) {
|
133
|
+
if (!vouchId || this.host.disableTracking) {
|
138
134
|
return;
|
139
135
|
}
|
140
136
|
|
137
|
+
const { publicApiUrl } = getEnvUrls(this.host.env);
|
138
|
+
const { client, tab, request, visitor } = this._getUids();
|
139
|
+
|
141
140
|
navigator.sendBeacon(
|
142
141
|
`${publicApiUrl}/api/events`,
|
143
142
|
JSON.stringify({
|
144
143
|
event,
|
145
|
-
payload
|
144
|
+
payload: {
|
145
|
+
vouchId,
|
146
|
+
...payload
|
147
|
+
},
|
146
148
|
context: {
|
147
149
|
'x-uid-client': client,
|
148
150
|
'x-uid-tab': tab,
|
@@ -161,125 +163,74 @@ class TrackingController implements ReactiveController {
|
|
161
163
|
|
162
164
|
// Only send loaded event once per session
|
163
165
|
if (!this._hasLoaded[vouchId]) {
|
164
|
-
this._sendTrackingEvent('VOUCH_LOADED'
|
165
|
-
vouchId
|
166
|
-
});
|
166
|
+
this._sendTrackingEvent('VOUCH_LOADED');
|
167
167
|
this._hasLoaded[vouchId] = true;
|
168
168
|
}
|
169
169
|
};
|
170
170
|
|
171
171
|
private _handlePlay = () => {
|
172
|
-
const vouchId = this._findVouchId();
|
173
|
-
|
174
|
-
if (!vouchId) {
|
175
|
-
return;
|
176
|
-
}
|
177
|
-
|
178
172
|
// Only send the video played event once per session
|
179
173
|
if (!this._hasPlayed) {
|
180
174
|
this._sendTrackingEvent('VIDEO_PLAYED', {
|
181
|
-
vouchId,
|
182
175
|
streamStart: this.host.currentTime
|
183
176
|
});
|
184
177
|
this._hasPlayed = true;
|
185
178
|
}
|
186
179
|
};
|
187
180
|
|
188
|
-
private _handleVideoPlay = ({ detail: { id, node } }: CustomEvent<VideoEventDetail>) => {
|
189
|
-
const vouchId = this._findVouchId();
|
190
|
-
|
191
|
-
if (!vouchId) {
|
192
|
-
return;
|
193
|
-
}
|
194
|
-
|
181
|
+
private _handleVideoPlay = ({ detail: { id, key, node } }: CustomEvent<VideoEventDetail>) => {
|
195
182
|
// Only increment play count once per session
|
196
|
-
if (!this._answersViewed[
|
183
|
+
if (!this._answersViewed[key]) {
|
197
184
|
this._sendTrackingEvent('VOUCH_RESPONSE_VIEWED', {
|
198
|
-
vouchId,
|
199
185
|
answerId: id
|
200
186
|
});
|
201
|
-
this._answersViewed[
|
187
|
+
this._answersViewed[key] = true;
|
202
188
|
}
|
203
189
|
|
204
|
-
this.
|
205
|
-
this._streamLatestTime[
|
206
|
-
this._streamedPrevTimestamp[id] = Date.now();
|
190
|
+
this._streamStartTime[key] = node.currentTime;
|
191
|
+
this._streamLatestTime[key] = node.currentTime;
|
207
192
|
};
|
208
193
|
|
209
|
-
private
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
194
|
+
private _handleVideoTimeUpdate = ({ detail: { id, key, node } }: CustomEvent<VideoEventDetail>) => {
|
195
|
+
// We only want to count any time that the video is actually playing
|
196
|
+
if (!this.host.paused) {
|
197
|
+
this._currentlyPlayingVideo = { id, key, node };
|
198
|
+
this._streamLatestTime[key] = node.currentTime;
|
214
199
|
}
|
215
200
|
|
216
|
-
if (this._streamLatestTime[id]) {
|
217
|
-
this._sendTrackingEvent('VIDEO_STREAMED', {
|
218
|
-
vouchId,
|
219
|
-
answerId: id,
|
220
|
-
streamStart: this._streamedTime[id],
|
221
|
-
streamEnd: this._streamLatestTime[id]
|
222
|
-
});
|
223
|
-
}
|
224
|
-
|
225
|
-
delete this._streamedTime[id];
|
226
|
-
delete this._streamLatestTime[id];
|
227
|
-
delete this._streamedPrevTimestamp[id];
|
228
|
-
};
|
229
|
-
|
230
|
-
private _handleVideoTimeUpdate = ({ detail: { id, node } }: CustomEvent<VideoEventDetail>) => {
|
231
|
-
const vouchId = this._findVouchId();
|
232
|
-
|
233
|
-
if (!vouchId) {
|
234
|
-
return;
|
235
|
-
}
|
236
|
-
|
237
|
-
const currentTimestamp = Date.now();
|
238
201
|
if (
|
239
|
-
node.currentTime &&
|
240
202
|
!node.paused &&
|
241
203
|
!this.host.paused &&
|
242
204
|
// Only fire the video seeked event when this video is the active one
|
243
205
|
id === this.host.scene?.video?.id &&
|
244
206
|
// Throttle the frequency that we send streamed events while playing
|
245
|
-
|
207
|
+
this._streamLatestTime[key] - this._streamStartTime[key] > STREAMED_THROTTLE
|
246
208
|
) {
|
247
209
|
this._sendTrackingEvent('VIDEO_STREAMED', {
|
248
|
-
vouchId,
|
249
210
|
answerId: id,
|
250
|
-
streamStart: this.
|
251
|
-
streamEnd:
|
211
|
+
streamStart: this._streamStartTime[key],
|
212
|
+
streamEnd: this._streamLatestTime[key]
|
252
213
|
});
|
253
|
-
this._streamedTime[id] = node.currentTime;
|
254
|
-
this._streamedPrevTimestamp[id] = currentTimestamp;
|
255
|
-
}
|
256
|
-
|
257
|
-
this._streamLatestTime[id] = node.currentTime;
|
258
|
-
};
|
259
214
|
|
260
|
-
|
261
|
-
const vouchId = this._findVouchId();
|
262
|
-
|
263
|
-
if (!vouchId) {
|
264
|
-
return;
|
215
|
+
this._streamStartTime[key] = node.currentTime;
|
265
216
|
}
|
217
|
+
};
|
266
218
|
|
267
|
-
|
268
|
-
|
219
|
+
private _handleVideoPause = ({ detail: { id, key } }: CustomEvent<VideoEventDetail>) => {
|
220
|
+
// Don't send a tracking event when seeking backwards
|
221
|
+
if (this._streamLatestTime[key] > this._streamStartTime[key]) {
|
269
222
|
// Send a video streamed event any time the video pauses then reset the streamed state
|
270
223
|
// We do this to capture the last bit of time that the video was played between the previous
|
271
224
|
// stream event and the video being paused manually or stopping because it ended
|
272
225
|
this._sendTrackingEvent('VIDEO_STREAMED', {
|
273
|
-
vouchId,
|
274
226
|
answerId: id,
|
275
|
-
streamStart: this.
|
276
|
-
streamEnd:
|
227
|
+
streamStart: this._streamStartTime[key],
|
228
|
+
streamEnd: this._streamLatestTime[key]
|
277
229
|
});
|
278
230
|
}
|
279
|
-
|
280
|
-
delete this.
|
281
|
-
delete this._streamLatestTime[
|
282
|
-
delete this._streamedPrevTimestamp[id];
|
231
|
+
this._currentlyPlayingVideo = null;
|
232
|
+
delete this._streamStartTime[key];
|
233
|
+
delete this._streamLatestTime[key];
|
283
234
|
};
|
284
235
|
|
285
236
|
hostConnected() {
|
@@ -287,17 +238,29 @@ class TrackingController implements ReactiveController {
|
|
287
238
|
this.host.addEventListener('vouch:loaded', this._handleVouchLoaded);
|
288
239
|
this.host.mediaPlayer?.addEventListener('play', this._handlePlay);
|
289
240
|
this.host.mediaPlayer?.addEventListener('video:play', this._handleVideoPlay);
|
290
|
-
this.host.mediaPlayer?.addEventListener('video:seeking', this._handleVideoSeeking);
|
291
241
|
this.host.mediaPlayer?.addEventListener('video:pause', this._handleVideoPause);
|
292
242
|
this.host.mediaPlayer?.addEventListener('video:timeupdate', this._handleVideoTimeUpdate);
|
293
243
|
});
|
294
244
|
}
|
295
245
|
|
296
246
|
hostDisconnected() {
|
247
|
+
if (this._currentlyPlayingVideo) {
|
248
|
+
const { id, key } = this._currentlyPlayingVideo;
|
249
|
+
if (this._streamLatestTime[key] > this._streamStartTime[key]) {
|
250
|
+
// Send a video streamed event any time the video pauses then reset the streamed state
|
251
|
+
// We do this to capture the last bit of time that the video was played between the previous
|
252
|
+
// stream event and the video being paused manually or stopping because it ended
|
253
|
+
this._sendTrackingEvent('VIDEO_STREAMED', {
|
254
|
+
answerId: id,
|
255
|
+
streamStart: this._streamStartTime[key],
|
256
|
+
streamEnd: this._streamLatestTime[key]
|
257
|
+
});
|
258
|
+
}
|
259
|
+
}
|
260
|
+
|
297
261
|
this.host.removeEventListener('vouch:loaded', this._handleVouchLoaded);
|
298
262
|
this.host.mediaPlayer?.removeEventListener('play', this._handlePlay);
|
299
263
|
this.host.mediaPlayer?.removeEventListener('video:play', this._handleVideoPlay);
|
300
|
-
this.host.mediaPlayer?.removeEventListener('video:seeking', this._handleVideoSeeking);
|
301
264
|
this.host.mediaPlayer?.removeEventListener('video:pause', this._handleVideoPause);
|
302
265
|
this.host.mediaPlayer?.removeEventListener('video:timeupdate', this._handleVideoTimeUpdate);
|
303
266
|
}
|