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

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.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'];