@vouchfor/embeds 0.0.0-experiment.eb79200 → 0.0.0-experiment.f112aaa
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/browser-f5bec026.js +433 -0
- package/dist/es/browser-f5bec026.js.map +1 -0
- package/dist/es/embeds.js +7 -1202
- package/dist/es/embeds.js.map +1 -1
- package/dist/es/index-6ce8276f.js +9947 -0
- package/dist/es/index-6ce8276f.js.map +1 -0
- package/dist/es/{components → src/components}/Embed/controllers/fetcher.d.ts +6 -0
- package/dist/es/{components → src/components}/Embed/controllers/tracking.d.ts +7 -2
- package/dist/es/{components → src/components}/Embed/index.d.ts +21 -16
- package/dist/es/src/utils/env.d.ts +12 -0
- package/dist/iife/embeds.iife.js +418 -341
- package/dist/iife/embeds.iife.js.map +1 -1
- package/package.json +6 -6
- package/src/components/Embed/Embed.stories.ts +15 -18
- package/src/components/Embed/controllers/fetcher.ts +124 -15
- package/src/components/Embed/controllers/tracking.ts +119 -93
- package/src/components/Embed/index.ts +40 -27
- package/src/utils/env.ts +18 -32
- package/dist/es/components/Embed/controllers/event-forwarder.d.ts +0 -14
- package/dist/es/utils/env.d.ts +0 -18
- /package/dist/es/{index.d.ts → src/index.d.ts} +0 -0
- /package/dist/es/{utils → src/utils}/events.d.ts +0 -0
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.f112aaa",
|
4
4
|
"license": "MIT",
|
5
5
|
"author": "Aaron Williams",
|
6
6
|
"main": "dist/es/embeds.js",
|
@@ -26,7 +26,7 @@
|
|
26
26
|
"lint:staged": "lint-staged",
|
27
27
|
"prepublishOnly": "yarn build",
|
28
28
|
"size": "size-limit",
|
29
|
-
"storybook": "yarn prebuild && storybook dev -p
|
29
|
+
"storybook": "yarn prebuild && storybook dev -p 6007",
|
30
30
|
"prebuild": "yarn build:deps && yarn generate:manifest",
|
31
31
|
"test": "true"
|
32
32
|
},
|
@@ -35,12 +35,12 @@
|
|
35
35
|
"**/*.{md,json,yml}": "prettier --write"
|
36
36
|
},
|
37
37
|
"dependencies": {
|
38
|
-
"@lit/task": "1.0.0",
|
39
|
-
"@vouchfor/media-player": "0.0.0-experiment.
|
38
|
+
"@lit/task": "^1.0.0",
|
39
|
+
"@vouchfor/media-player": "0.0.0-experiment.f112aaa",
|
40
40
|
"uuid": "^9.0.1"
|
41
41
|
},
|
42
42
|
"peerDependencies": {
|
43
|
-
"lit": "^3.0
|
43
|
+
"lit": "^3.1.0"
|
44
44
|
},
|
45
45
|
"devDependencies": {
|
46
46
|
"@esm-bundle/chai": "^4.3.4-fix.0",
|
@@ -62,7 +62,7 @@
|
|
62
62
|
"eslint": "^8.50.0",
|
63
63
|
"eslint-plugin-import": "^2.28.1",
|
64
64
|
"lint-staged": "^14.0.1",
|
65
|
-
"lit": "^
|
65
|
+
"lit": "^3.1.0",
|
66
66
|
"prettier": "^3.0.3",
|
67
67
|
"react": "^18.2.0",
|
68
68
|
"react-dom": "^18.2.0",
|
@@ -11,16 +11,14 @@ type EmbedArgs = EmbedProps & {
|
|
11
11
|
};
|
12
12
|
|
13
13
|
const _Embed = ({
|
14
|
-
|
14
|
+
vouchId,
|
15
|
+
templateId,
|
16
|
+
questions,
|
15
17
|
preload,
|
16
18
|
autoplay,
|
17
19
|
env,
|
18
20
|
apiKey,
|
19
21
|
controls,
|
20
|
-
enableTracking,
|
21
|
-
trackingSource,
|
22
|
-
|
23
|
-
resolution,
|
24
22
|
aspectRatio
|
25
23
|
}: EmbedArgs) => {
|
26
24
|
return html`
|
@@ -28,14 +26,14 @@ const _Embed = ({
|
|
28
26
|
<vouch-embed
|
29
27
|
env=${ifDefined(env)}
|
30
28
|
apiKey=${ifDefined(apiKey)}
|
29
|
+
vouchId=${ifDefined(vouchId)}
|
30
|
+
templateId=${ifDefined(templateId)}
|
31
|
+
.questions=${questions}
|
32
|
+
.controls=${controls}
|
31
33
|
?autoplay=${autoplay}
|
32
|
-
?enableTracking=${enableTracking}
|
33
|
-
vouchHashId=${ifDefined(vouchHashId)}
|
34
|
-
resolution=${ifDefined(resolution)}
|
35
|
-
aspectRatio=${ifDefined(aspectRatio)}
|
36
34
|
preload=${ifDefined(preload)}
|
37
|
-
|
38
|
-
|
35
|
+
aspectRatio=${ifDefined(aspectRatio)}
|
36
|
+
@error=${console.log}
|
39
37
|
></vouch-embed>
|
40
38
|
</div>
|
41
39
|
`;
|
@@ -53,20 +51,19 @@ type Story = StoryObj<EmbedArgs>;
|
|
53
51
|
|
54
52
|
const Embed: Story = {
|
55
53
|
args: {
|
56
|
-
env: '
|
54
|
+
env: 'local',
|
57
55
|
apiKey: 'TVik9uTMgE-PD25UTHIS6gyl0hMBWC7AT4dkpdlLBT4VIfDWZJrQiCk6Ak7m1',
|
58
|
-
|
59
|
-
|
56
|
+
vouchId: '6JQEIPeStt',
|
57
|
+
templateId: '357fc118-e179-4171-9446-ff2b8e9d1b29',
|
58
|
+
questions: [],
|
60
59
|
aspectRatio: 0,
|
61
60
|
preload: 'none',
|
62
|
-
autoplay: false
|
63
|
-
enableTracking: true,
|
64
|
-
trackingSource: 'media_player_storybook'
|
61
|
+
autoplay: false
|
65
62
|
},
|
66
63
|
argTypes: {
|
67
64
|
env: {
|
68
65
|
control: 'radio',
|
69
|
-
options: ['
|
66
|
+
options: ['local', 'dev', 'staging', 'prod']
|
70
67
|
},
|
71
68
|
preload: {
|
72
69
|
control: 'radio',
|
@@ -1,41 +1,150 @@
|
|
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';
|
6
|
+
import type { Environment } from '~/utils/env';
|
5
7
|
|
6
8
|
import { getEnvUrls } from '~/utils/env';
|
7
9
|
|
8
10
|
type EmbedHost = ReactiveControllerHost & Embed;
|
9
11
|
|
10
|
-
type
|
12
|
+
type FetchTaskDeps = [
|
13
|
+
EmbedProps['env'],
|
14
|
+
EmbedProps['apiKey'],
|
15
|
+
EmbedProps['data'],
|
16
|
+
EmbedProps['vouchId'],
|
17
|
+
EmbedProps['templateId']
|
18
|
+
];
|
19
|
+
|
20
|
+
type FilterTaskDeps = [EmbedProps['data'], EmbedProps['questions']];
|
11
21
|
|
12
22
|
class FetcherController {
|
13
23
|
host: EmbedHost;
|
14
24
|
|
25
|
+
private _fetching = false;
|
26
|
+
private _vouch: EmbedProps['data'];
|
27
|
+
|
28
|
+
set fetching(value) {
|
29
|
+
if (this._fetching !== value) {
|
30
|
+
this._fetching = value;
|
31
|
+
this.host.requestUpdate();
|
32
|
+
}
|
33
|
+
}
|
34
|
+
get fetching() {
|
35
|
+
return this._fetching;
|
36
|
+
}
|
37
|
+
|
38
|
+
private getVouch = async (env: Environment, apiKey: string, vouchId: string) => {
|
39
|
+
const { embedApiUrl } = getEnvUrls(env);
|
40
|
+
|
41
|
+
const cacheCheck = uuidv4();
|
42
|
+
const res = await fetch(`${embedApiUrl}/vouches/${vouchId}`, {
|
43
|
+
method: 'GET',
|
44
|
+
headers: [
|
45
|
+
['X-Api-Key', apiKey],
|
46
|
+
['X-Cache-Check', cacheCheck]
|
47
|
+
]
|
48
|
+
});
|
49
|
+
|
50
|
+
const vouch = await res.json();
|
51
|
+
this.host.dispatchEvent(new CustomEvent('vouch:loaded', { detail: vouch?.id }));
|
52
|
+
|
53
|
+
// HACK: we're currently using API Gateway caching on the embed API without any invalidation logic,
|
54
|
+
// so to ensure that the cache stays up to date, whenever we detect a cache hit we trigger another
|
55
|
+
// API call with the `Cache-Control` header which will re-fill the cache
|
56
|
+
const resCacheCheck = res?.headers?.get('X-Cache-Check');
|
57
|
+
if (resCacheCheck !== cacheCheck) {
|
58
|
+
fetch(`${embedApiUrl}/vouches/${vouchId}`, {
|
59
|
+
method: 'GET',
|
60
|
+
headers: [
|
61
|
+
['X-Api-Key', apiKey],
|
62
|
+
['Cache-Control', 'max-age=0']
|
63
|
+
]
|
64
|
+
});
|
65
|
+
}
|
66
|
+
|
67
|
+
return vouch;
|
68
|
+
};
|
69
|
+
|
70
|
+
private getTemplate = async (env: Environment, apiKey: string, templateId: string) => {
|
71
|
+
const { embedApiUrl } = getEnvUrls(env);
|
72
|
+
|
73
|
+
const cacheCheck = uuidv4();
|
74
|
+
const res = await fetch(`${embedApiUrl}/templates/${templateId}`, {
|
75
|
+
method: 'GET',
|
76
|
+
headers: [
|
77
|
+
['X-Api-Key', apiKey],
|
78
|
+
['X-Cache-Check', cacheCheck]
|
79
|
+
]
|
80
|
+
});
|
81
|
+
const template = await res.json();
|
82
|
+
|
83
|
+
// HACK: we're currently using API Gateway caching on the embed API without any invalidation logic,
|
84
|
+
// so to ensure that the cache stays up to date, whenever we detect a cache hit we trigger another
|
85
|
+
// API call with the `Cache-Control` header which will re-fill the cache
|
86
|
+
const resCacheCheck = res?.headers?.get('X-Cache-Check');
|
87
|
+
if (resCacheCheck !== cacheCheck) {
|
88
|
+
fetch(`${embedApiUrl}/templates/${templateId}`, {
|
89
|
+
method: 'GET',
|
90
|
+
headers: [
|
91
|
+
['X-Api-Key', apiKey],
|
92
|
+
['Cache-Control', 'max-age=0']
|
93
|
+
]
|
94
|
+
});
|
95
|
+
}
|
96
|
+
|
97
|
+
return template;
|
98
|
+
};
|
99
|
+
|
15
100
|
constructor(host: EmbedHost) {
|
16
101
|
this.host = host;
|
17
|
-
new Task<
|
102
|
+
new Task<FetchTaskDeps, void>(
|
18
103
|
this.host,
|
19
|
-
async ([env, apiKey,
|
104
|
+
async ([env, apiKey, data, vouchId, templateId]: FetchTaskDeps) => {
|
20
105
|
try {
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
const { embedApiUrl } = getEnvUrls(env);
|
106
|
+
host.vouch = undefined;
|
107
|
+
host.template = undefined;
|
25
108
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
109
|
+
if (data) {
|
110
|
+
let template;
|
111
|
+
if (templateId) {
|
112
|
+
this.fetching = true;
|
113
|
+
template = await this.getTemplate(env, apiKey, templateId);
|
114
|
+
}
|
115
|
+
this._vouch = data;
|
116
|
+
host.template = template ?? data?.settings?.template?.instance;
|
117
|
+
} else if (vouchId) {
|
118
|
+
this.fetching = true;
|
30
119
|
|
31
|
-
|
32
|
-
|
120
|
+
const [vouch, template] = await Promise.all([
|
121
|
+
this.getVouch(env, apiKey, vouchId),
|
122
|
+
templateId ? this.getTemplate(env, apiKey, templateId) : null
|
123
|
+
]);
|
124
|
+
this._vouch = vouch;
|
125
|
+
host.template = template ?? vouch?.settings?.template?.instance;
|
33
126
|
}
|
34
127
|
} finally {
|
35
|
-
|
128
|
+
this.fetching = false;
|
36
129
|
}
|
37
130
|
},
|
38
|
-
() => [host.env, host.apiKey, host.
|
131
|
+
() => [host.env, host.apiKey, host.data, host.vouchId, host.templateId]
|
132
|
+
);
|
133
|
+
|
134
|
+
// This second task is to be able to filter the vouch without fetching it again if only the questions changed
|
135
|
+
new Task<FilterTaskDeps, void>(
|
136
|
+
this.host,
|
137
|
+
([vouch, questions]: FilterTaskDeps) => {
|
138
|
+
host.vouch = vouch
|
139
|
+
? {
|
140
|
+
...vouch,
|
141
|
+
questions: {
|
142
|
+
items: vouch?.questions.items.filter((_, index) => !questions?.length || questions?.includes(index + 1))
|
143
|
+
}
|
144
|
+
}
|
145
|
+
: undefined;
|
146
|
+
},
|
147
|
+
() => [this._vouch, host.questions]
|
39
148
|
);
|
40
149
|
}
|
41
150
|
}
|
@@ -1,18 +1,20 @@
|
|
1
|
+
import { TEMPLATE_VERSION } from '@vouchfor/canvas-video';
|
1
2
|
import { v4 as uuidv4 } from 'uuid';
|
2
3
|
|
3
4
|
import type { Embed } from '..';
|
4
5
|
import type { VideoEventDetail } from '@vouchfor/media-player';
|
5
6
|
import type { ReactiveController, ReactiveControllerHost } from 'lit';
|
6
7
|
|
8
|
+
import packageJson from '../../../../package.json';
|
7
9
|
import { getEnvUrls } from '~/utils/env';
|
8
10
|
|
9
|
-
const
|
11
|
+
const MINIMUM_SEND_THRESHOLD = 1;
|
10
12
|
|
11
13
|
type EmbedHost = ReactiveControllerHost & Embed;
|
12
14
|
|
13
15
|
type TrackingEvent = 'VOUCH_LOADED' | 'VOUCH_RESPONSE_VIEWED' | 'VIDEO_PLAYED' | 'VIDEO_STREAMED';
|
14
16
|
type TrackingPayload = {
|
15
|
-
vouchId
|
17
|
+
vouchId?: string;
|
16
18
|
answerId?: string;
|
17
19
|
streamStart?: number;
|
18
20
|
streamEnd?: number;
|
@@ -36,21 +38,23 @@ class TrackingController implements ReactiveController {
|
|
36
38
|
private _hasPlayed = false;
|
37
39
|
private _hasLoaded: BooleanMap = {};
|
38
40
|
private _answersViewed: BooleanMap = {};
|
39
|
-
private
|
40
|
-
private
|
41
|
+
private _streamStartTime: TimeMap = {};
|
42
|
+
private _streamLatestTime: TimeMap = {};
|
43
|
+
private _currentlyPlayingVideo: VideoEventDetail | null = null;
|
41
44
|
|
42
45
|
constructor(host: EmbedHost) {
|
43
46
|
this.host = host;
|
44
47
|
host.addController(this);
|
45
48
|
}
|
46
49
|
|
47
|
-
private _findVouchId() {
|
48
|
-
if (
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
return this.host.
|
50
|
+
private _findVouchId(payload?: TrackingPayload) {
|
51
|
+
if (payload && 'vouchId' in payload) {
|
52
|
+
return payload.vouchId;
|
53
|
+
}
|
54
|
+
if (this.host.vouch) {
|
55
|
+
return this.host.vouch.id;
|
53
56
|
}
|
57
|
+
return null;
|
54
58
|
}
|
55
59
|
|
56
60
|
private _createVisitor = (visitorId: string) => {
|
@@ -116,7 +120,6 @@ class TrackingController implements ReactiveController {
|
|
116
120
|
});
|
117
121
|
|
118
122
|
return {
|
119
|
-
// Source might be embeds, could be playlink etc.
|
120
123
|
source: this.host.trackingSource,
|
121
124
|
time: new Date(),
|
122
125
|
region,
|
@@ -125,30 +128,58 @@ class TrackingController implements ReactiveController {
|
|
125
128
|
screenWidth: window.screen.width,
|
126
129
|
referrer: document.referrer,
|
127
130
|
currentUrl: location.href,
|
131
|
+
embedVersion: packageJson.version,
|
132
|
+
templateVersion: TEMPLATE_VERSION,
|
128
133
|
...utmParams
|
129
134
|
};
|
130
135
|
};
|
131
136
|
|
132
|
-
private _sendTrackingEvent = (event: TrackingEvent, payload
|
137
|
+
private _sendTrackingEvent = (event: TrackingEvent, payload?: TrackingPayload) => {
|
138
|
+
const vouchId = this._findVouchId(payload);
|
139
|
+
|
140
|
+
if (!vouchId || this.host.disableTracking) {
|
141
|
+
return;
|
142
|
+
}
|
143
|
+
|
133
144
|
const { publicApiUrl } = getEnvUrls(this.host.env);
|
134
145
|
const { client, tab, request, visitor } = this._getUids();
|
135
146
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
}
|
151
|
-
)
|
147
|
+
navigator.sendBeacon(
|
148
|
+
`${publicApiUrl}/api/v2/events`,
|
149
|
+
JSON.stringify({
|
150
|
+
event,
|
151
|
+
payload: {
|
152
|
+
...payload,
|
153
|
+
vouchId
|
154
|
+
},
|
155
|
+
context: {
|
156
|
+
'x-uid-client': client,
|
157
|
+
'x-uid-tab': tab,
|
158
|
+
'x-uid-request': request,
|
159
|
+
'x-uid-visitor': visitor,
|
160
|
+
'x-reporting-metadata': this._getReportingMetadata()
|
161
|
+
}
|
162
|
+
})
|
163
|
+
);
|
164
|
+
};
|
165
|
+
|
166
|
+
private _streamEnded = () => {
|
167
|
+
if (this._currentlyPlayingVideo) {
|
168
|
+
const { id, key } = this._currentlyPlayingVideo;
|
169
|
+
// Don't send a tracking event when seeking backwards
|
170
|
+
if (this._streamLatestTime[key] > this._streamStartTime[key] + MINIMUM_SEND_THRESHOLD) {
|
171
|
+
// Send a video streamed event any time the stream ends to capture the time between starting
|
172
|
+
// the video and the video stopping for any reason (pausing, deleting the embed node or closing the browser)
|
173
|
+
this._sendTrackingEvent('VIDEO_STREAMED', {
|
174
|
+
answerId: id,
|
175
|
+
streamStart: this._streamStartTime[key],
|
176
|
+
streamEnd: this._streamLatestTime[key]
|
177
|
+
});
|
178
|
+
}
|
179
|
+
|
180
|
+
// Make sure these events are only sent once by deleting the start and latest times
|
181
|
+
delete this._streamStartTime[key];
|
182
|
+
delete this._streamLatestTime[key];
|
152
183
|
}
|
153
184
|
};
|
154
185
|
|
@@ -159,109 +190,104 @@ class TrackingController implements ReactiveController {
|
|
159
190
|
|
160
191
|
// Only send loaded event once per session
|
161
192
|
if (!this._hasLoaded[vouchId]) {
|
162
|
-
this._sendTrackingEvent('VOUCH_LOADED', {
|
163
|
-
vouchId
|
164
|
-
});
|
193
|
+
this._sendTrackingEvent('VOUCH_LOADED', { vouchId });
|
165
194
|
this._hasLoaded[vouchId] = true;
|
166
195
|
}
|
167
196
|
};
|
168
197
|
|
169
198
|
private _handlePlay = () => {
|
170
|
-
const vouchId = this._findVouchId();
|
171
|
-
|
172
|
-
if (!vouchId) {
|
173
|
-
return;
|
174
|
-
}
|
175
|
-
|
176
199
|
// Only send the video played event once per session
|
177
200
|
if (!this._hasPlayed) {
|
178
201
|
this._sendTrackingEvent('VIDEO_PLAYED', {
|
179
|
-
vouchId,
|
180
202
|
streamStart: this.host.currentTime
|
181
203
|
});
|
182
204
|
this._hasPlayed = true;
|
183
205
|
}
|
184
206
|
};
|
185
207
|
|
186
|
-
private _handleVideoPlay = ({ detail: { id, node } }: CustomEvent<VideoEventDetail>) => {
|
187
|
-
const vouchId = this._findVouchId();
|
188
|
-
if (!vouchId) {
|
189
|
-
return;
|
190
|
-
}
|
208
|
+
private _handleVideoPlay = ({ detail: { id, key, node } }: CustomEvent<VideoEventDetail>) => {
|
191
209
|
// Only increment play count once per session
|
192
|
-
if (!this._answersViewed[
|
210
|
+
if (!this._answersViewed[key]) {
|
193
211
|
this._sendTrackingEvent('VOUCH_RESPONSE_VIEWED', {
|
194
|
-
vouchId,
|
195
212
|
answerId: id
|
196
213
|
});
|
197
|
-
this._answersViewed[
|
214
|
+
this._answersViewed[key] = true;
|
198
215
|
}
|
199
|
-
this._streamedTime[id] = node.currentTime;
|
200
|
-
this._streamedPrevTimestamp[id] = Date.now();
|
201
|
-
};
|
202
216
|
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
return;
|
217
|
+
if (!this._streamStartTime[key]) {
|
218
|
+
this._streamStartTime[key] = node.currentTime;
|
219
|
+
this._streamLatestTime[key] = node.currentTime;
|
207
220
|
}
|
208
|
-
|
221
|
+
};
|
222
|
+
|
223
|
+
private _handleVideoTimeUpdate = ({ detail: { id, key, node } }: CustomEvent<VideoEventDetail>) => {
|
209
224
|
if (
|
225
|
+
// We only want to count any time that the video is actually playing
|
210
226
|
!this.host.paused &&
|
211
|
-
// Only
|
212
|
-
id === this.host.scene?.video?.id
|
213
|
-
// Throttle the frequency that we send streamed events while playing
|
214
|
-
currentTimestamp - this._streamedPrevTimestamp[id] > STREAMED_THROTTLE
|
227
|
+
// Only update the latest time if this event fires for the currently active video
|
228
|
+
id === this.host.scene?.video?.id
|
215
229
|
) {
|
230
|
+
this._currentlyPlayingVideo = { id, key, node };
|
231
|
+
this._streamLatestTime[key] = node.currentTime;
|
232
|
+
}
|
233
|
+
};
|
234
|
+
|
235
|
+
private _handleVideoPause = ({ detail: { id, key } }: CustomEvent<VideoEventDetail>) => {
|
236
|
+
if (this._streamLatestTime[key] > this._streamStartTime[key] + MINIMUM_SEND_THRESHOLD) {
|
216
237
|
this._sendTrackingEvent('VIDEO_STREAMED', {
|
217
|
-
vouchId,
|
218
238
|
answerId: id,
|
219
|
-
streamStart: this.
|
220
|
-
streamEnd:
|
239
|
+
streamStart: this._streamStartTime[key],
|
240
|
+
streamEnd: this._streamLatestTime[key]
|
221
241
|
});
|
222
|
-
this._streamedTime[id] = node.currentTime;
|
223
|
-
this._streamedPrevTimestamp[id] = currentTimestamp;
|
224
242
|
}
|
243
|
+
delete this._streamStartTime[key];
|
244
|
+
delete this._streamLatestTime[key];
|
225
245
|
};
|
226
246
|
|
227
|
-
private
|
228
|
-
|
229
|
-
|
230
|
-
|
247
|
+
private _pageUnloading = () => {
|
248
|
+
this._streamEnded();
|
249
|
+
// This will try to send the same stream event again so we delete the start and latest
|
250
|
+
// time in stream ended so that there is no times to send and the pause event does nothing
|
251
|
+
this.host.pause();
|
252
|
+
};
|
253
|
+
|
254
|
+
private _handleVisibilityChange = () => {
|
255
|
+
if (document.visibilityState === 'hidden') {
|
256
|
+
this._pageUnloading();
|
231
257
|
}
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
this.
|
236
|
-
vouchId,
|
237
|
-
answerId: id,
|
238
|
-
streamStart: this._streamedTime[id],
|
239
|
-
streamEnd: node.currentTime
|
240
|
-
});
|
241
|
-
delete this._streamedTime[id];
|
242
|
-
delete this._streamedPrevTimestamp[id];
|
258
|
+
};
|
259
|
+
|
260
|
+
private _handlePageHide = () => {
|
261
|
+
this._pageUnloading();
|
243
262
|
};
|
244
263
|
|
245
264
|
hostConnected() {
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
265
|
+
requestAnimationFrame(() => {
|
266
|
+
if ('onvisibilitychange' in document) {
|
267
|
+
document.addEventListener('visibilitychange', this._handleVisibilityChange);
|
268
|
+
} else {
|
269
|
+
window.addEventListener('pagehide', this._handlePageHide);
|
270
|
+
}
|
271
|
+
this.host.addEventListener('vouch:loaded', this._handleVouchLoaded);
|
272
|
+
this.host.mediaPlayer?.addEventListener('play', this._handlePlay);
|
273
|
+
this.host.mediaPlayer?.addEventListener('video:play', this._handleVideoPlay);
|
274
|
+
this.host.mediaPlayer?.addEventListener('video:pause', this._handleVideoPause);
|
275
|
+
this.host.mediaPlayer?.addEventListener('video:timeupdate', this._handleVideoTimeUpdate);
|
276
|
+
});
|
255
277
|
}
|
256
278
|
|
257
279
|
hostDisconnected() {
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
this.host.mediaPlayer?.removeEventListener('video:timeupdate', this._handleVideoTimeUpdate);
|
280
|
+
this._streamEnded();
|
281
|
+
if ('onvisibilitychange' in document) {
|
282
|
+
document.removeEventListener('visibilitychange', this._handleVisibilityChange);
|
283
|
+
} else {
|
284
|
+
window.removeEventListener('pagehide', this._handlePageHide);
|
264
285
|
}
|
286
|
+
this.host.removeEventListener('vouch:loaded', this._handleVouchLoaded);
|
287
|
+
this.host.mediaPlayer?.removeEventListener('play', this._handlePlay);
|
288
|
+
this.host.mediaPlayer?.removeEventListener('video:play', this._handleVideoPlay);
|
289
|
+
this.host.mediaPlayer?.removeEventListener('video:pause', this._handleVideoPause);
|
290
|
+
this.host.mediaPlayer?.removeEventListener('video:timeupdate', this._handleVideoTimeUpdate);
|
265
291
|
}
|
266
292
|
}
|
267
293
|
|