@vouchfor/embeds 0.0.0-experiment.039a74b → 0.0.0-experiment.1b80037

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,208 @@
1
+ import { html, LitElement } from 'lit';
2
+ import { customElement, property } from 'lit/decorators.js';
3
+ import { ifDefined } from 'lit/directives/if-defined.js';
4
+ import { createRef, ref } from 'lit/directives/ref.js';
5
+
6
+ import type { Scene, TemplateInstance } from '@vouchfor/canvas-video';
7
+ import type { MediaPlayer, MediaPlayerProps } from '@vouchfor/media-player';
8
+ import type { Ref } from 'lit/directives/ref.js';
9
+ import type { Environment } from '~/utils/env';
10
+
11
+ import { EventForwardController } from './controllers/event-forwarder';
12
+ import { FetcherController } from './controllers/fetcher';
13
+ import { TrackingController } from './controllers/tracking';
14
+
15
+ import '@vouchfor/media-player';
16
+
17
+ type EmbedProps = Pick<
18
+ MediaPlayerProps,
19
+ 'data' | 'resolution' | 'aspectRatio' | 'preload' | 'autoplay' | 'controls'
20
+ > & {
21
+ env: Environment;
22
+ apiKey: string;
23
+ vouchId?: string;
24
+ templateId?: string;
25
+ };
26
+
27
+ @customElement('vouch-embed')
28
+ class Embed extends LitElement {
29
+ private _mediaPlayerRef: Ref<MediaPlayer> = createRef();
30
+
31
+ @property({ type: Object, attribute: 'data' }) data: EmbedProps['data'];
32
+ @property({ type: String }) vouchId: EmbedProps['vouchId'];
33
+ @property({ type: String }) templateId: EmbedProps['templateId'];
34
+
35
+ @property({ type: String }) env: EmbedProps['env'] = 'prod';
36
+ @property({ type: String }) apiKey: EmbedProps['apiKey'] = '';
37
+
38
+ @property({ type: String }) preload: EmbedProps['preload'] = 'auto';
39
+ @property({ type: Boolean }) autoplay: EmbedProps['autoplay'] = false;
40
+
41
+ @property({ type: Number }) resolution: EmbedProps['resolution'] = 1080;
42
+ @property({ type: Number }) aspectRatio: EmbedProps['aspectRatio'] = 0;
43
+ @property({ type: Array }) controls: EmbedProps['controls'];
44
+
45
+ private eventController = new EventForwardController(this, [
46
+ 'durationchange',
47
+ 'ended',
48
+ 'error',
49
+ 'loadeddata',
50
+ 'pause',
51
+ 'stalled',
52
+ 'play',
53
+ 'playing',
54
+ 'ratechange',
55
+ 'scenechange',
56
+ 'seeking',
57
+ 'seeked',
58
+ 'timeupdate',
59
+ 'volumechange',
60
+ 'waiting',
61
+
62
+ 'video:loadeddata',
63
+ 'video:seeked',
64
+ 'video:play',
65
+ 'video:playing',
66
+ 'video:pause',
67
+ 'video:stalled',
68
+ 'video:timeupdate',
69
+ 'video:ended',
70
+ 'video:error'
71
+ ]);
72
+ private _fetcherController = new FetcherController(this);
73
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
74
+ // @ts-ignore
75
+ private _trackingController = new TrackingController(this);
76
+
77
+ get vouch() {
78
+ return this._fetcherController.vouch;
79
+ }
80
+
81
+ get template(): TemplateInstance | null {
82
+ return this._fetcherController.template;
83
+ }
84
+
85
+ get fetching() {
86
+ return this._fetcherController.fetching;
87
+ }
88
+
89
+ get waiting() {
90
+ return this._mediaPlayerRef.value?.waiting;
91
+ }
92
+
93
+ get seeking() {
94
+ return this._mediaPlayerRef.value?.seeking;
95
+ }
96
+
97
+ get paused() {
98
+ return this._mediaPlayerRef.value?.paused;
99
+ }
100
+
101
+ get captions() {
102
+ return this._mediaPlayerRef.value?.captions;
103
+ }
104
+
105
+ get fullscreen() {
106
+ return this._mediaPlayerRef.value?.fullscreen;
107
+ }
108
+
109
+ get duration() {
110
+ return this._mediaPlayerRef.value?.duration;
111
+ }
112
+
113
+ set currentTime(value: number) {
114
+ if (this._mediaPlayerRef.value) {
115
+ this._mediaPlayerRef.value.currentTime = value;
116
+ }
117
+ }
118
+ get currentTime() {
119
+ return this._mediaPlayerRef.value?.currentTime ?? 0;
120
+ }
121
+
122
+ set playbackRate(value: number) {
123
+ if (this._mediaPlayerRef.value) {
124
+ this._mediaPlayerRef.value.playbackRate = value;
125
+ }
126
+ }
127
+ get playbackRate() {
128
+ return this._mediaPlayerRef.value?.playbackRate ?? 1;
129
+ }
130
+
131
+ set volume(value: number) {
132
+ if (this._mediaPlayerRef.value) {
133
+ this._mediaPlayerRef.value.volume = value;
134
+ }
135
+ }
136
+ get volume() {
137
+ return this._mediaPlayerRef.value?.volume ?? 1;
138
+ }
139
+
140
+ set muted(value: boolean) {
141
+ if (this._mediaPlayerRef.value) {
142
+ this._mediaPlayerRef.value.muted = value;
143
+ }
144
+ }
145
+ get muted() {
146
+ return this._mediaPlayerRef.value?.muted ?? false;
147
+ }
148
+
149
+ get scene(): Scene | null {
150
+ return this._mediaPlayerRef.value?.scene ?? null;
151
+ }
152
+
153
+ get scenes(): Scene[] {
154
+ return this._mediaPlayerRef.value?.scenes ?? [];
155
+ }
156
+
157
+ get videoState() {
158
+ return this._mediaPlayerRef.value?.videoState;
159
+ }
160
+
161
+ get mediaPlayer() {
162
+ return this._mediaPlayerRef.value;
163
+ }
164
+
165
+ play() {
166
+ this._mediaPlayerRef.value?.play();
167
+ }
168
+
169
+ pause() {
170
+ this._mediaPlayerRef.value?.pause();
171
+ }
172
+
173
+ setScene(index: number) {
174
+ this._mediaPlayerRef.value?.setScene(index);
175
+ }
176
+
177
+ render() {
178
+ return html`
179
+ <vmp-media-player
180
+ ${ref(this._mediaPlayerRef)}
181
+ ${this.eventController.register()}
182
+ ?autoplay=${this.autoplay}
183
+ ?loading=${this.fetching}
184
+ .data=${this.vouch}
185
+ .template=${this.template}
186
+ resolution=${ifDefined(this.resolution)}
187
+ aspectRatio=${ifDefined(this.aspectRatio)}
188
+ preload=${ifDefined(this.preload)}
189
+ .controls=${this.controls}
190
+ ></vmp-media-player>
191
+ `;
192
+ }
193
+ }
194
+
195
+ declare global {
196
+ interface HTMLElementTagNameMap {
197
+ 'vouch-embed': Embed;
198
+ }
199
+
200
+ namespace JSX {
201
+ interface IntrinsicElements {
202
+ 'vouch-embed': Embed;
203
+ }
204
+ }
205
+ }
206
+
207
+ export { Embed };
208
+ export type { EmbedProps };
package/src/index.ts CHANGED
@@ -1 +1 @@
1
- export { InlineEmbed } from '~/components/InlineEmbed';
1
+ export { Embed } from '~/components/Embed';
@@ -0,0 +1,78 @@
1
+ type Environment = 'dev' | 'staging' | 'prod';
2
+
3
+ type GetEnvUrlsReturn = {
4
+ marketingUrl: string;
5
+ videoUrl: string;
6
+ publicApiUrl: string;
7
+ embedApiUrl: string;
8
+ publicRecorderUrl: string;
9
+ };
10
+
11
+ const marketingUrl = 'https://vouchfor.com';
12
+
13
+ const devVideoUrl = 'https://d2rxhdlm2q91uk.cloudfront.net';
14
+ const stagingVideoUrl = 'https://d1ix11aj5kfygl.cloudfront.net';
15
+ const prodVideoUrl = 'https://d157jlwnudd93d.cloudfront.net';
16
+
17
+ const devPublicApiUrl = 'https://bshyfw4h5a.execute-api.ap-southeast-2.amazonaws.com/dev';
18
+ const stagingPublicApiUrl = 'https://gyzw7rpbq3.execute-api.ap-southeast-2.amazonaws.com/staging';
19
+ const prodPublicApiUrl = 'https://vfcjuim1l3.execute-api.ap-southeast-2.amazonaws.com/prod';
20
+
21
+ const devEmbedApiUrl = 'http://localhost:6060/v1';
22
+ const stagingEmbedApiUrl = 'https://embed-staging.vouchfor.com/v1';
23
+ const prodEmbedApiUrl = 'https://embed.vouchfor.com/v1';
24
+
25
+ const devPublicRecorderUrl = 'https://dev.vouchfor.com';
26
+ const stagingPublicRecorderUrl = 'https://staging.vouchfor.com';
27
+ const prodPublicRecorderUrl = 'https://app.vouchfor.com';
28
+
29
+ // We are handling the case where env is an unknown string so the ts error is a lie
30
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
31
+ // @ts-ignore
32
+ function getEnvUrls(env: Environment): GetEnvUrlsReturn {
33
+ if (!['dev', 'staging', 'prod'].includes(env)) {
34
+ throw new Error(`Unknown environment: ${env}`);
35
+ }
36
+
37
+ if (env === 'dev') {
38
+ return {
39
+ marketingUrl,
40
+ videoUrl: devVideoUrl,
41
+ publicApiUrl: devPublicApiUrl,
42
+ embedApiUrl: devEmbedApiUrl,
43
+ publicRecorderUrl: devPublicRecorderUrl
44
+ };
45
+ }
46
+
47
+ if (env === 'staging') {
48
+ return {
49
+ marketingUrl,
50
+ videoUrl: stagingVideoUrl,
51
+ publicApiUrl: stagingPublicApiUrl,
52
+ embedApiUrl: stagingEmbedApiUrl,
53
+ publicRecorderUrl: stagingPublicRecorderUrl
54
+ };
55
+ }
56
+
57
+ if (env === 'prod') {
58
+ return {
59
+ marketingUrl,
60
+ videoUrl: prodVideoUrl,
61
+ publicApiUrl: prodPublicApiUrl,
62
+ embedApiUrl: prodEmbedApiUrl,
63
+ publicRecorderUrl: prodPublicRecorderUrl
64
+ };
65
+ }
66
+ }
67
+
68
+ export {
69
+ marketingUrl,
70
+ devEmbedApiUrl,
71
+ stagingEmbedApiUrl,
72
+ prodEmbedApiUrl,
73
+ devPublicRecorderUrl,
74
+ stagingPublicRecorderUrl,
75
+ prodPublicRecorderUrl,
76
+ getEnvUrls
77
+ };
78
+ export type { Environment };
@@ -0,0 +1,13 @@
1
+ function forwardEvent(type: string, fromElement: HTMLElement, toElement: HTMLElement) {
2
+ function forwarder(event: Event) {
3
+ toElement.dispatchEvent(new CustomEvent(event.type, event));
4
+ }
5
+
6
+ fromElement.addEventListener(type, forwarder);
7
+
8
+ return () => {
9
+ fromElement.removeEventListener(type, forwarder);
10
+ };
11
+ }
12
+
13
+ export { forwardEvent };
@@ -1,17 +0,0 @@
1
- import { MediaPlayer } from '@vouchfor/media-player';
2
- import type { MediaPlayerProps } from '@vouchfor/media-player';
3
- type InlineEmbedProps = MediaPlayerProps;
4
- declare class InlineEmbed extends MediaPlayer {
5
- }
6
- declare global {
7
- interface HTMLElementTagNameMap {
8
- 'vembed-inline': InlineEmbed;
9
- }
10
- namespace JSX {
11
- interface IntrinsicElements {
12
- 'vembed-inline': InlineEmbed;
13
- }
14
- }
15
- }
16
- export { InlineEmbed };
17
- export type { InlineEmbedProps };
@@ -1,2 +0,0 @@
1
- export { InlineEmbed } from './InlineEmbed';
2
- export type { InlineEmbedProps } from './InlineEmbed';
@@ -1,119 +0,0 @@
1
- import { html } from 'lit';
2
- import { ifDefined } from 'lit/directives/if-defined.js';
3
-
4
- import type { InlineEmbedProps } from './';
5
- import type { Meta, StoryObj } from '@storybook/web-components';
6
-
7
- import './';
8
-
9
- type InlineEmbedArgs = InlineEmbedProps & {
10
- showVouch?: boolean;
11
- };
12
-
13
- const _InlineEmbed = ({
14
- vouchHashId,
15
- preload,
16
- autoplay,
17
- env,
18
- apiKey,
19
- controls,
20
- enableTracking,
21
- trackingSource,
22
-
23
- // Template properties
24
- type,
25
- format,
26
- resolution,
27
- aspectRatio,
28
- headerShortName,
29
- headerSubtitle,
30
- headerBrandLogo
31
- }: InlineEmbedArgs) => {
32
- return html`
33
- <div style="height: 100vh">
34
- <vembed-inline
35
- env=${ifDefined(env)}
36
- apiKey=${ifDefined(apiKey)}
37
- ?autoplay=${autoplay}
38
- ?enableTracking=${enableTracking}
39
- .vouchHashId=${vouchHashId}
40
- resolution=${ifDefined(resolution)}
41
- aspectRatio=${ifDefined(aspectRatio)}
42
- preload=${ifDefined(preload)}
43
- type=${ifDefined(type)}
44
- .controls=${controls}
45
- trackingSource=${ifDefined(trackingSource)}
46
- format=${ifDefined(format)}
47
- ?headerShortName=${headerShortName}
48
- ?headerBrandLogo=${headerBrandLogo}
49
- headerSubtitle=${ifDefined(headerSubtitle)}
50
- ></vembed-inline>
51
- </div>
52
- `;
53
- };
54
-
55
- // More on how to set up stories at: https://storybook.js.org/docs/web-components/writing-stories/introduction
56
- const meta = {
57
- title: 'Embeds/Inline',
58
- tags: ['autodocs'],
59
- render: (args) => _InlineEmbed(args),
60
- component: 'vembed-inline'
61
- } satisfies Meta<InlineEmbedProps>;
62
-
63
- type Story = StoryObj<InlineEmbedArgs>;
64
-
65
- const InlineEmbed: Story = {
66
- args: {
67
- env: 'dev',
68
- apiKey: 'RtW1R7JYej-pymoGNEUCblZz0NTLZzsuC9xKxcsewIYpf3UEIePcGvtkRnd4K',
69
- vouchHashId: 'X526IKhw90',
70
- resolution: 1080,
71
- aspectRatio: 0,
72
- preload: 'none',
73
- autoplay: false,
74
- enableTracking: true,
75
- trackingSource: 'media_player_storybook',
76
-
77
- type: 'BASIC',
78
- format: 'letterbox',
79
- headerShortName: true,
80
- headerSubtitle: 'client',
81
- headerBrandLogo: false,
82
-
83
- showVouch: true
84
- },
85
- argTypes: {
86
- type: {
87
- control: 'radio',
88
- options: ['BARE', 'BASIC', 'INLINE', 'JUMBO', 'FOUNDIT', 'CISCO']
89
- },
90
- headerSubtitle: {
91
- control: 'radio',
92
- options: ['role', 'client']
93
- },
94
- headerShortName: {
95
- control: 'boolean'
96
- },
97
- headerBrandLogo: {
98
- control: 'boolean'
99
- },
100
- preload: {
101
- control: 'radio',
102
- options: ['auto', 'none']
103
- },
104
- format: {
105
- control: 'radio',
106
- options: ['letterbox', 'letterbox-video', 'crop', 'crop-top', 'squarebox']
107
- },
108
- env: {
109
- control: 'radio',
110
- options: ['prod', 'staging', 'dev']
111
- }
112
- },
113
- parameters: {
114
- layout: 'fullscreen'
115
- }
116
- };
117
-
118
- export default meta;
119
- export { InlineEmbed };
@@ -1,24 +0,0 @@
1
- import { MediaPlayer } from '@vouchfor/media-player';
2
- import { customElement } from 'lit/decorators.js';
3
-
4
- import type { MediaPlayerProps } from '@vouchfor/media-player';
5
-
6
- type InlineEmbedProps = MediaPlayerProps;
7
-
8
- @customElement('vembed-inline')
9
- class InlineEmbed extends MediaPlayer {}
10
-
11
- declare global {
12
- interface HTMLElementTagNameMap {
13
- 'vembed-inline': InlineEmbed;
14
- }
15
-
16
- namespace JSX {
17
- interface IntrinsicElements {
18
- 'vembed-inline': InlineEmbed;
19
- }
20
- }
21
- }
22
-
23
- export { InlineEmbed };
24
- export type { InlineEmbedProps };
@@ -1,2 +0,0 @@
1
- export { InlineEmbed } from './InlineEmbed';
2
- export type { InlineEmbedProps } from './InlineEmbed';