@vouchfor/embeds 0.0.0-experiment.dce2e9b → 0.0.0-experiment.de7febc

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.dce2e9b",
3
+ "version": "0.0.0-experiment.de7febc",
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.dce2e9b",
39
+ "@vouchfor/media-player": "0.0.0-experiment.de7febc",
40
40
  "uuid": "^9.0.1"
41
41
  },
42
42
  "peerDependencies": {
@@ -1,4 +1,5 @@
1
1
  import { Task } from '@lit/task';
2
+ import { v4 as uuidv4 } from 'uuid';
2
3
 
3
4
  import type { Embed, EmbedProps } from '..';
4
5
  import type { ReactiveControllerHost } from 'lit';
@@ -34,42 +35,61 @@ class FetcherController {
34
35
  private getVouch = async (env: Environment, apiKey: string, vouchId: string) => {
35
36
  const { embedApiUrl } = getEnvUrls(env);
36
37
 
37
- const vouch = await fetch(`${embedApiUrl}/vouches/${vouchId}`, {
38
- method: 'GET',
39
- headers: [['X-Api-Key', apiKey]]
40
- }).then((response) => {
41
- this.host.dispatchEvent(new CustomEvent('vouch:loaded', { detail: vouchId }));
42
- return response.json();
43
- });
44
-
45
- // HACK: trigger another fetch after we received the data to update the cache in the background
46
- fetch(`${embedApiUrl}/vouches/${vouchId}`, {
38
+ const cacheCheck = uuidv4();
39
+ const res = await fetch(`${embedApiUrl}/vouches/${vouchId}`, {
47
40
  method: 'GET',
48
41
  headers: [
49
42
  ['X-Api-Key', apiKey],
50
- ['Cache-Control', 'max-age=0']
43
+ ['X-Cache-Check', cacheCheck]
51
44
  ]
52
45
  });
53
46
 
47
+ const vouch = await res.json();
48
+ this.host.dispatchEvent(new CustomEvent('vouch:loaded', { detail: vouchId }));
49
+
50
+ // HACK: we're currently using API Gateway caching on the embed API without any invalidation logic,
51
+ // so to ensure that the cache stays up to date, whenever we detect a cache hit we trigger another
52
+ // API call with the `Cache-Control` header which will re-fill the cache
53
+ const resCacheCheck = res?.headers?.get('X-Cache-Check');
54
+ if (resCacheCheck && resCacheCheck !== cacheCheck) {
55
+ fetch(`${embedApiUrl}/vouches/${vouchId}`, {
56
+ method: 'GET',
57
+ headers: [
58
+ ['X-Api-Key', apiKey],
59
+ ['Cache-Control', 'max-age=0']
60
+ ]
61
+ });
62
+ }
63
+
54
64
  return vouch;
55
65
  };
56
66
 
57
67
  private getTemplate = async (env: Environment, apiKey: string, templateId: string) => {
58
68
  const { embedApiUrl } = getEnvUrls(env);
59
69
 
60
- const template = await fetch(`${embedApiUrl}/templates/${templateId}`, {
61
- method: 'GET',
62
- headers: [['X-Api-Key', apiKey]]
63
- }).then((response) => response.json());
64
-
65
- // HACK: trigger another fetch after we received the data to update the cache in the background
66
- fetch(`${embedApiUrl}/templates/${templateId}`, {
70
+ const cacheCheck = uuidv4();
71
+ const res = await fetch(`${embedApiUrl}/templates/${templateId}`, {
67
72
  method: 'GET',
68
73
  headers: [
69
74
  ['X-Api-Key', apiKey],
70
- ['Cache-Control', 'max-age=0']
75
+ ['X-Cache-Check', cacheCheck]
71
76
  ]
72
77
  });
78
+ const template = await res.json();
79
+
80
+ // HACK: we're currently using API Gateway caching on the embed API without any invalidation logic,
81
+ // so to ensure that the cache stays up to date, whenever we detect a cache hit we trigger another
82
+ // API call with the `Cache-Control` header which will re-fill the cache
83
+ const resCacheCheck = res?.headers?.get('X-Cache-Check');
84
+ if (resCacheCheck && resCacheCheck !== cacheCheck) {
85
+ fetch(`${embedApiUrl}/templates/${templateId}`, {
86
+ method: 'GET',
87
+ headers: [
88
+ ['X-Api-Key', apiKey],
89
+ ['Cache-Control', 'max-age=0']
90
+ ]
91
+ });
92
+ }
73
93
 
74
94
  return template;
75
95
  };
@@ -6,7 +6,7 @@ import type { ReactiveController, ReactiveControllerHost } from 'lit';
6
6
 
7
7
  import { getEnvUrls } from '~/utils/env';
8
8
 
9
- const STREAMED_THROTTLE = 2000;
9
+ const STREAMED_THROTTLE = 10000;
10
10
 
11
11
  type EmbedHost = ReactiveControllerHost & Embed;
12
12
 
@@ -45,11 +45,11 @@ class TrackingController implements ReactiveController {
45
45
  }
46
46
 
47
47
  private _findVouchId() {
48
- if (this.host.data) {
49
- if ('uuid' in this.host.data) {
50
- return this.host.data.uuid;
48
+ if (this.host.vouch) {
49
+ if ('uuid' in this.host.vouch) {
50
+ return this.host.vouch.uuid;
51
51
  }
52
- return this.host.data.id;
52
+ return this.host.vouch.id;
53
53
  }
54
54
  }
55
55
 
@@ -132,21 +132,22 @@ class TrackingController implements ReactiveController {
132
132
  const { publicApiUrl } = getEnvUrls(this.host.env);
133
133
  const { client, tab, request, visitor } = this._getUids();
134
134
 
135
- // Don't send tracking if we don't have a source
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
- );
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
+ }
150
151
  };
151
152
 
152
153
  private _handleVouchLoaded = ({ detail: vouchId }: CustomEvent<string>) => {
@@ -182,6 +183,7 @@ class TrackingController implements ReactiveController {
182
183
 
183
184
  private _handleVideoPlay = ({ detail: { id, node } }: CustomEvent<VideoEventDetail>) => {
184
185
  const vouchId = this._findVouchId();
186
+
185
187
  if (!vouchId) {
186
188
  return;
187
189
  }
@@ -199,11 +201,14 @@ class TrackingController implements ReactiveController {
199
201
 
200
202
  private _handleVideoTimeUpdate = ({ detail: { id, node } }: CustomEvent<VideoEventDetail>) => {
201
203
  const vouchId = this._findVouchId();
204
+
202
205
  if (!vouchId) {
203
206
  return;
204
207
  }
205
208
  const currentTimestamp = Date.now();
206
209
  if (
210
+ node.currentTime &&
211
+ !node.paused &&
207
212
  !this.host.paused &&
208
213
  // Only fire the video seeked event when this video is the active one
209
214
  id === this.host.scene?.video?.id &&
@@ -223,18 +228,23 @@ class TrackingController implements ReactiveController {
223
228
 
224
229
  private _handleVideoPause = ({ detail: { id, node } }: CustomEvent<VideoEventDetail>) => {
225
230
  const vouchId = this._findVouchId();
231
+
226
232
  if (!vouchId) {
227
233
  return;
228
234
  }
229
- // Send a video streamed event any time the video pauses then reset the streamed state
230
- // We do this to capture the last bit of time that the video was played between the previous
231
- // stream event and the video being paused manually or stopping because it ended
232
- this._sendTrackingEvent('VIDEO_STREAMED', {
233
- vouchId,
234
- answerId: id,
235
- streamStart: this._streamedTime[id],
236
- streamEnd: node.currentTime
237
- });
235
+
236
+ // Don't send a tracking event if the video pauses when seeking backwards
237
+ if (node.currentTime > this._streamedTime[id]) {
238
+ // Send a video streamed event any time the video pauses then reset the streamed state
239
+ // We do this to capture the last bit of time that the video was played between the previous
240
+ // stream event and the video being paused manually or stopping because it ended
241
+ this._sendTrackingEvent('VIDEO_STREAMED', {
242
+ vouchId,
243
+ answerId: id,
244
+ streamStart: this._streamedTime[id],
245
+ streamEnd: node.currentTime
246
+ });
247
+ }
238
248
  delete this._streamedTime[id];
239
249
  delete this._streamedPrevTimestamp[id];
240
250
  };
@@ -17,6 +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
21
  trackingSource?: string;
21
22
  vouchId?: string;
22
23
  templateId?: string;
@@ -32,6 +33,7 @@ class Embed extends LitElement {
32
33
 
33
34
  @property({ type: String }) env: EmbedProps['env'] = 'prod';
34
35
  @property({ type: String }) apiKey: EmbedProps['apiKey'] = '';
36
+ @property({ type: Boolean }) enableTracking: EmbedProps['enableTracking'] = true;
35
37
  @property({ type: String }) trackingSource: EmbedProps['trackingSource'] = 'embed';
36
38
 
37
39
  @property({ type: Array }) controls: EmbedProps['controls'];