@streamscloud/embeddable 2.1.6 → 2.2.1

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.
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Retrieves the profile ID from localStorage or generates a new one if it doesn't exist
3
+ * @returns The profile ID to use for analytics tracking
4
+ */
5
+ export declare const getOrCreateProfileId: () => string;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Key used for storing the profile ID in local storage
3
+ */
4
+ const PROFILE_ID_STORAGE_KEY = 'streamscloud_profile_id';
5
+ /**
6
+ * Retrieves the profile ID from localStorage or generates a new one if it doesn't exist
7
+ * @returns The profile ID to use for analytics tracking
8
+ */
9
+ export const getOrCreateProfileId = () => {
10
+ const storedProfileId = localStorage.getItem(PROFILE_ID_STORAGE_KEY);
11
+ if (!storedProfileId) {
12
+ const newProfileId = crypto.randomUUID();
13
+ localStorage.setItem(PROFILE_ID_STORAGE_KEY, newProfileId);
14
+ return newProfileId;
15
+ }
16
+ return storedProfileId;
17
+ };
@@ -0,0 +1 @@
1
+ export declare const handleEsc: (event: KeyboardEvent, callback: () => void) => void;
@@ -0,0 +1,5 @@
1
+ export const handleEsc = (event, callback) => {
2
+ if (event.key === 'Escape' || event.key === 'Esc') {
3
+ callback();
4
+ }
5
+ };
@@ -1 +1,2 @@
1
- export declare const createLocalGQLClient: (graphqlUrl: string, customFetch?: typeof fetch) => import("@urql/core").Client;
1
+ export declare const createLocalGQLClient: (graphqlUrl?: string, customFetch?: typeof fetch) => import("@urql/core").Client;
2
+ export declare const resolveGraphQLUrl: (url?: string) => string;
@@ -1,6 +1,6 @@
1
1
  import { createClient, fetchExchange } from '@urql/core';
2
2
  export const createLocalGQLClient = (graphqlUrl, customFetch) => createClient({
3
- url: graphqlUrl,
3
+ url: resolveGraphQLUrl(graphqlUrl),
4
4
  requestPolicy: 'network-only',
5
5
  fetchOptions: {
6
6
  credentials: 'include'
@@ -8,3 +8,6 @@ export const createLocalGQLClient = (graphqlUrl, customFetch) => createClient({
8
8
  fetch: customFetch || fetch,
9
9
  exchanges: [fetchExchange]
10
10
  });
11
+ export const resolveGraphQLUrl = (url) => {
12
+ return url || 'https://api.streamscloud.com/graphql';
13
+ };
@@ -7,3 +7,4 @@ export { default as ShortVideoDetails } from './cmp.short-video-details.svelte';
7
7
  export type { ShortVideoViewerModel, ShortVideoViewerAdModel, ShortVideoViewerProductModel } from './types';
8
8
  export type { IShortVideoViewerLocalization } from './short-video-viewer-localization.svelte';
9
9
  export type { IShortVideoDetailsLocalization } from './short-video-details-localization.svelte';
10
+ export { mapShortVideoViewerModel } from './mapper';
@@ -4,3 +4,4 @@ export { default as ShortVideoProductViewer } from './cmp.product.svelte';
4
4
  export { default as ShortVideoViewerAttachments } from './cmp.attachments.svelte';
5
5
  export { default as ShortVideoViewerAttachmentsInline } from './cmp.attachments-inline.svelte';
6
6
  export { default as ShortVideoDetails } from './cmp.short-video-details.svelte';
7
+ export { mapShortVideoViewerModel } from './mapper';
@@ -7,31 +7,83 @@
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
- import { ShortVideoViewer } from '../short-video-viewer';
10
+ import { handleEsc } from '../../core/document.event-handlers';
11
+ import { createLocalGQLClient } from '../../core/graphql';
12
+ import { mapShortVideoViewerModel, ShortVideoViewer } from '../short-video-viewer';
13
+ import { GetShortVideosDocument } from './operations.generated';
14
+ import { Loading } from '../../ui/loading';
11
15
  import { PlayerBuffer, PlayerSlider } from '../../ui/player';
12
16
  import { SpotlightLayout } from '../../ui/spotlight-layout';
13
17
  import { default as Controls } from './controls.svelte';
14
18
  import { ShortVideosPlayerLocalization } from './short-videos-player-localization.svelte';
15
19
  import { ShortVideosPlayerUiManager } from './ui-manager.svelte';
16
- import { onDestroy, onMount } from 'svelte';
17
- let { shortVideosProvider, localization: localizationInit, on } = $props();
20
+ import { onMount, untrack } from 'svelte';
21
+ let { input, localization: localizationInit, on } = $props();
18
22
  const localization = $derived(new ShortVideosPlayerLocalization(localizationInit));
19
- const buffer = $derived(new PlayerBuffer(shortVideosProvider));
23
+ let buffer = $state(input.type === 'provider' ? new PlayerBuffer(input.provider) : null);
24
+ $effect(() => {
25
+ if (input.type !== 'ids')
26
+ return;
27
+ untrack(() => {
28
+ initBuffer(input);
29
+ });
30
+ });
31
+ const initBuffer = (input) => __awaiter(void 0, void 0, void 0, function* () {
32
+ var _a, _b;
33
+ try {
34
+ const { graphqlUrl, ids, initialId } = input;
35
+ const graphql = createLocalGQLClient(graphqlUrl);
36
+ const payload = yield graphql
37
+ .query(GetShortVideosDocument, {
38
+ input: {
39
+ filter: {
40
+ includePostIds: ids
41
+ }
42
+ }
43
+ })
44
+ .toPromise();
45
+ const posts = ((_b = (_a = payload.data) === null || _a === void 0 ? void 0 : _a.shortVideos) === null || _b === void 0 ? void 0 : _b.items) || [];
46
+ const idOrder = new Map(ids.map((id, index) => [id, index]));
47
+ posts.sort((a, b) => {
48
+ var _a, _b;
49
+ return ((_a = idOrder.get(a.id)) !== null && _a !== void 0 ? _a : Infinity) - ((_b = idOrder.get(b.id)) !== null && _b !== void 0 ? _b : Infinity);
50
+ });
51
+ const index = initialId ? posts.findIndex((p) => p.id === initialId) : 0;
52
+ const provider = {
53
+ initialData: {
54
+ prefetchedItems: posts.map(mapShortVideoViewerModel),
55
+ startIndex: index
56
+ },
57
+ loadMore: () => __awaiter(void 0, void 0, void 0, function* () {
58
+ // No more items to load, as all are already prefetched
59
+ return [];
60
+ })
61
+ };
62
+ buffer = new PlayerBuffer(provider);
63
+ }
64
+ catch (_c) {
65
+ console.error('Failed to load short videos by IDs:', input.ids);
66
+ buffer = null; // Reset buffer on error
67
+ }
68
+ });
20
69
  const uiManager = new ShortVideosPlayerUiManager();
21
70
  onMount(() => __awaiter(void 0, void 0, void 0, function* () {
22
71
  uiManager.detailsCollapsed = window && window.innerWidth < window.innerHeight;
23
72
  }));
24
- onDestroy(() => {
25
- // end tracking the short video
26
- });
27
73
  const handleDimensionsChanged = (dimensions) => {
28
74
  uiManager.updateDimensions({
29
75
  mainViewColumnWidth: dimensions.mainSceneWidth,
30
76
  viewTotalWidth: dimensions.totalWidth
31
77
  });
32
78
  };
79
+ const onPlayerClose = () => {
80
+ var _a;
81
+ (_a = on === null || on === void 0 ? void 0 : on.closePlayer) === null || _a === void 0 ? void 0 : _a.call(on);
82
+ };
33
83
  </script>
34
84
 
85
+ <svelte:document onkeydown={(e) => handleEsc(e, onPlayerClose)} />
86
+
35
87
  <div class="short-videos-player-container">
36
88
  <div class="short-videos-player" style={uiManager.globalCssVariables}>
37
89
  {#if buffer}
@@ -44,7 +96,9 @@ const handleDimensionsChanged = (dimensions) => {
44
96
  </PlayerSlider>
45
97
  </div>
46
98
  </SpotlightLayout>
47
- <Controls buffer={buffer} uiManager={uiManager} localization={localization} on={{ closePlayer: () => on?.closePlayer?.() }} />
99
+ <Controls buffer={buffer} uiManager={uiManager} localization={localization} on={{ closePlayer: () => onPlayerClose() }} />
100
+ {:else}
101
+ <Loading positionFixedCenter={true} timeout={1000} />
48
102
  {/if}
49
103
  </div>
50
104
  </div>
@@ -1,8 +1,7 @@
1
- import { type ShortVideoViewerModel } from '../short-video-viewer';
2
- import { type PlayerItemsProvider } from '../../ui/player';
1
+ import type { PlayerInput } from './types';
3
2
  import { type IShortVideosPlayerLocalization } from './short-videos-player-localization.svelte';
4
3
  type Props = {
5
- shortVideosProvider: PlayerItemsProvider<ShortVideoViewerModel>;
4
+ input: PlayerInput;
6
5
  localization?: IShortVideosPlayerLocalization;
7
6
  on?: {
8
7
  closePlayer?: () => void;
@@ -1,15 +1,38 @@
1
- import type { ShortVideoViewerModel } from '../short-video-viewer';
2
- import type { PlayerItemsProvider } from '../../ui/player';
1
+ import { type OpenShortVideosPlayerInit } from './types';
3
2
  import type { IShortVideosPlayerLocalization } from './short-videos-player-localization.svelte';
4
3
  export type { IShortVideosPlayerLocalization };
5
4
  /**
6
- * Opens the short videos player modal with the specified provider and optional localization.
5
+ * Opens the short videos player modal with the specified provider or by fetching videos by IDs.
6
+ *
7
+ * You can use either a ready-made provider, or simply pass a list of video IDs and a GraphQL endpoint to fetch them.
7
8
  *
8
9
  * @param init - Configuration options.
9
- * @param {PlayerItemsProvider<ShortVideoViewerModel>} init.shortVideosProvider - The provider instance supplying short video items to the player.
10
- * @param {IShortVideosPlayerLocalization} [init.localization] - Optional localization settings for the player UI.
11
10
  *
12
- * @example
11
+ * @param {PlayerItemsProvider<ShortVideoViewerModel>} [init.shortVideosProvider]
12
+ * The provider instance supplying short video items to the player.
13
+ * **Use this if you already have your own provider implementation.**
14
+ *
15
+ * @param {string[]} [init.ids]
16
+ * List of short video IDs to display in the player.
17
+ * **Use this if you want the player to fetch and show specific videos.**
18
+ *
19
+ * @param {string} [init.graphqlUrl]
20
+ * The GraphQL endpoint to use when fetching videos by IDs.
21
+ * **Required if you use `ids`.**
22
+ *
23
+ * @param {string} [init.initialId]
24
+ * The ID of the video that should be shown first.
25
+ * **Optional. Only used when using `ids`.**
26
+ *
27
+ * @param {IShortVideosPlayerLocalization} [init.localization]
28
+ * Optional localization settings for the player UI.
29
+ *
30
+ * @param {object} [init.on]
31
+ * Optional event handlers.
32
+ * @param {() => void} [init.on.playerClosed]
33
+ * Called when the player is closed.
34
+ *
35
+ * @example <caption>Using a custom provider</caption>
13
36
  * ```ts
14
37
  * import { openShortVideosPlayer } from '@streamscloud/embeddable/short-videos-player';
15
38
  *
@@ -21,11 +44,25 @@ export type { IShortVideosPlayerLocalization };
21
44
  * }
22
45
  * });
23
46
  * ```
47
+ *
48
+ * @example <caption>Using a list of IDs</caption>
49
+ * ```ts
50
+ * import { openShortVideosPlayer } from '@streamscloud/embeddable/short-videos-player';
51
+ *
52
+ * openShortVideosPlayer({
53
+ * ids: ['id1', 'id2', 'id3'],
54
+ * graphqlUrl: 'https://api.example.com/graphql',
55
+ * initialId: 'id2',
56
+ * localization: {
57
+ * next: 'Next',
58
+ * previous: 'Previous'
59
+ * },
60
+ * on: {
61
+ * playerClosed: () => {
62
+ * console.log('Player was closed');
63
+ * }
64
+ * }
65
+ * });
66
+ * ```
24
67
  */
25
- export declare const openShortVideosPlayer: (init: {
26
- shortVideosProvider: PlayerItemsProvider<ShortVideoViewerModel>;
27
- localization?: IShortVideosPlayerLocalization;
28
- on?: {
29
- playerClosed?: () => void;
30
- };
31
- }) => void;
68
+ export declare const openShortVideosPlayer: (init: OpenShortVideosPlayerInit) => Promise<void>;
@@ -1,14 +1,39 @@
1
+ import { isIdsInit, isShortVideosProviderInit } from './types';
1
2
  import { ShadowHost } from '../../ui/shadow-dom';
3
+ import { mount, unmount } from 'svelte';
2
4
  import { default as ShortVideosPlayer } from './cmp.short-videos-player.svelte';
3
- import { mount } from 'svelte';
4
5
  /**
5
- * Opens the short videos player modal with the specified provider and optional localization.
6
+ * Opens the short videos player modal with the specified provider or by fetching videos by IDs.
7
+ *
8
+ * You can use either a ready-made provider, or simply pass a list of video IDs and a GraphQL endpoint to fetch them.
6
9
  *
7
10
  * @param init - Configuration options.
8
- * @param {PlayerItemsProvider<ShortVideoViewerModel>} init.shortVideosProvider - The provider instance supplying short video items to the player.
9
- * @param {IShortVideosPlayerLocalization} [init.localization] - Optional localization settings for the player UI.
10
11
  *
11
- * @example
12
+ * @param {PlayerItemsProvider<ShortVideoViewerModel>} [init.shortVideosProvider]
13
+ * The provider instance supplying short video items to the player.
14
+ * **Use this if you already have your own provider implementation.**
15
+ *
16
+ * @param {string[]} [init.ids]
17
+ * List of short video IDs to display in the player.
18
+ * **Use this if you want the player to fetch and show specific videos.**
19
+ *
20
+ * @param {string} [init.graphqlUrl]
21
+ * The GraphQL endpoint to use when fetching videos by IDs.
22
+ * **Required if you use `ids`.**
23
+ *
24
+ * @param {string} [init.initialId]
25
+ * The ID of the video that should be shown first.
26
+ * **Optional. Only used when using `ids`.**
27
+ *
28
+ * @param {IShortVideosPlayerLocalization} [init.localization]
29
+ * Optional localization settings for the player UI.
30
+ *
31
+ * @param {object} [init.on]
32
+ * Optional event handlers.
33
+ * @param {() => void} [init.on.playerClosed]
34
+ * Called when the player is closed.
35
+ *
36
+ * @example <caption>Using a custom provider</caption>
12
37
  * ```ts
13
38
  * import { openShortVideosPlayer } from '@streamscloud/embeddable/short-videos-player';
14
39
  *
@@ -20,17 +45,61 @@ import { mount } from 'svelte';
20
45
  * }
21
46
  * });
22
47
  * ```
48
+ *
49
+ * @example <caption>Using a list of IDs</caption>
50
+ * ```ts
51
+ * import { openShortVideosPlayer } from '@streamscloud/embeddable/short-videos-player';
52
+ *
53
+ * openShortVideosPlayer({
54
+ * ids: ['id1', 'id2', 'id3'],
55
+ * graphqlUrl: 'https://api.example.com/graphql',
56
+ * initialId: 'id2',
57
+ * localization: {
58
+ * next: 'Next',
59
+ * previous: 'Previous'
60
+ * },
61
+ * on: {
62
+ * playerClosed: () => {
63
+ * console.log('Player was closed');
64
+ * }
65
+ * }
66
+ * });
67
+ * ```
23
68
  */
24
- export const openShortVideosPlayer = (init) => {
25
- const { shortVideosProvider, localization } = init;
26
- const shadowHost = new ShadowHost({ onClosed: () => init.on?.playerClosed?.() });
27
- mount(ShortVideosPlayer, {
69
+ export const openShortVideosPlayer = async (init) => {
70
+ let input = null;
71
+ if (isShortVideosProviderInit(init)) {
72
+ input = {
73
+ type: 'provider',
74
+ provider: init.shortVideosProvider
75
+ };
76
+ }
77
+ else if (isIdsInit(init)) {
78
+ input = {
79
+ type: 'ids',
80
+ ids: init.ids,
81
+ initialId: init.initialId,
82
+ graphqlUrl: init.graphqlUrl
83
+ };
84
+ }
85
+ else {
86
+ input = null;
87
+ }
88
+ if (!input) {
89
+ return;
90
+ }
91
+ const shadowHost = new ShadowHost();
92
+ const mounted = mount(ShortVideosPlayer, {
28
93
  target: shadowHost.shadowRoot,
29
94
  props: {
30
- shortVideosProvider,
31
- localization,
95
+ input,
96
+ localization: init.localization,
32
97
  on: {
33
- closePlayer: () => {
98
+ closePlayer: async () => {
99
+ if (init.on?.playerClosed) {
100
+ init.on.playerClosed();
101
+ }
102
+ await unmount(mounted);
34
103
  shadowHost.remove();
35
104
  }
36
105
  }
@@ -0,0 +1,64 @@
1
+ import type * as SchemaTypes from '../../../gql/types';
2
+ import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
3
+ export type GetShortVideosQueryVariables = SchemaTypes.Exact<{
4
+ input: SchemaTypes.PostsInput;
5
+ image_scale?: SchemaTypes.InputMaybe<SchemaTypes.ImageScale>;
6
+ }>;
7
+ export type GetShortVideosQuery = {
8
+ shortVideos: {
9
+ items: Array<{
10
+ id: string;
11
+ enableSocialInteractions: boolean;
12
+ postHeading: {
13
+ sourceImage: string | null;
14
+ sourceName: string;
15
+ postDisplayDate: any;
16
+ postViewsCount: number;
17
+ };
18
+ allProducts: Array<{
19
+ title: string;
20
+ id: string;
21
+ link: string | null;
22
+ media: Array<{
23
+ url: string;
24
+ thumbnailUrl: string | null;
25
+ type: SchemaTypes.MediaType;
26
+ }>;
27
+ priceAndAvailability: {
28
+ currency: SchemaTypes.Currency;
29
+ price: number;
30
+ productSalePrices: Array<{
31
+ salePrice: number;
32
+ salePriceEffectiveDateFrom: any | null;
33
+ salePriceEffectiveDateTo: any | null;
34
+ }> | null;
35
+ };
36
+ }>;
37
+ ad: {
38
+ id: string;
39
+ title: string;
40
+ description: string | null;
41
+ buttonText: string | null;
42
+ buttonUrl: string | null;
43
+ openLinkInNewWindow: boolean | null;
44
+ type: SchemaTypes.AdType;
45
+ media: Array<{
46
+ url: string;
47
+ thumbnailUrl: string | null;
48
+ type: SchemaTypes.MediaType;
49
+ }>;
50
+ } | null;
51
+ postData: {
52
+ media: Array<{
53
+ url: string;
54
+ thumbnailUrl: string | null;
55
+ type: SchemaTypes.MediaType;
56
+ }>;
57
+ shortVideoData: {
58
+ text: string | null;
59
+ } | null;
60
+ };
61
+ }>;
62
+ };
63
+ };
64
+ export declare const GetShortVideosDocument: DocumentNode<GetShortVideosQuery, GetShortVideosQueryVariables>;
@@ -0,0 +1,193 @@
1
+ export const GetShortVideosDocument = {
2
+ kind: 'Document',
3
+ definitions: [
4
+ {
5
+ kind: 'OperationDefinition',
6
+ operation: 'query',
7
+ name: { kind: 'Name', value: 'GetShortVideos' },
8
+ variableDefinitions: [
9
+ {
10
+ kind: 'VariableDefinition',
11
+ variable: { kind: 'Variable', name: { kind: 'Name', value: 'input' } },
12
+ type: { kind: 'NonNullType', type: { kind: 'NamedType', name: { kind: 'Name', value: 'PostsInput' } } }
13
+ },
14
+ {
15
+ kind: 'VariableDefinition',
16
+ variable: { kind: 'Variable', name: { kind: 'Name', value: 'image_scale' } },
17
+ type: { kind: 'NamedType', name: { kind: 'Name', value: 'ImageScale' } },
18
+ defaultValue: { kind: 'EnumValue', value: 'ORIGINAL_ENCODED' }
19
+ }
20
+ ],
21
+ selectionSet: {
22
+ kind: 'SelectionSet',
23
+ selections: [
24
+ {
25
+ kind: 'Field',
26
+ alias: { kind: 'Name', value: 'shortVideos' },
27
+ name: { kind: 'Name', value: 'posts' },
28
+ arguments: [{ kind: 'Argument', name: { kind: 'Name', value: 'input' }, value: { kind: 'Variable', name: { kind: 'Name', value: 'input' } } }],
29
+ selectionSet: {
30
+ kind: 'SelectionSet',
31
+ selections: [
32
+ {
33
+ kind: 'Field',
34
+ name: { kind: 'Name', value: 'items' },
35
+ selectionSet: {
36
+ kind: 'SelectionSet',
37
+ selections: [{ kind: 'FragmentSpread', name: { kind: 'Name', value: 'ShortVideoViewerPayloadFragment' } }]
38
+ }
39
+ }
40
+ ]
41
+ }
42
+ }
43
+ ]
44
+ }
45
+ },
46
+ {
47
+ kind: 'FragmentDefinition',
48
+ name: { kind: 'Name', value: 'ShortVideoViewerPayloadFragment' },
49
+ typeCondition: { kind: 'NamedType', name: { kind: 'Name', value: 'Post' } },
50
+ selectionSet: {
51
+ kind: 'SelectionSet',
52
+ selections: [
53
+ { kind: 'Field', name: { kind: 'Name', value: 'id' } },
54
+ { kind: 'Field', name: { kind: 'Name', value: 'enableSocialInteractions' } },
55
+ {
56
+ kind: 'Field',
57
+ name: { kind: 'Name', value: 'postHeading' },
58
+ selectionSet: {
59
+ kind: 'SelectionSet',
60
+ selections: [
61
+ { kind: 'Field', name: { kind: 'Name', value: 'sourceImage' } },
62
+ { kind: 'Field', name: { kind: 'Name', value: 'sourceName' } },
63
+ { kind: 'Field', name: { kind: 'Name', value: 'postDisplayDate' } },
64
+ { kind: 'Field', name: { kind: 'Name', value: 'postViewsCount' } }
65
+ ]
66
+ }
67
+ },
68
+ {
69
+ kind: 'Field',
70
+ name: { kind: 'Name', value: 'allProducts' },
71
+ selectionSet: {
72
+ kind: 'SelectionSet',
73
+ selections: [
74
+ {
75
+ kind: 'Field',
76
+ name: { kind: 'Name', value: 'media' },
77
+ selectionSet: {
78
+ kind: 'SelectionSet',
79
+ selections: [
80
+ { kind: 'Field', name: { kind: 'Name', value: 'url' } },
81
+ { kind: 'Field', name: { kind: 'Name', value: 'thumbnailUrl' } },
82
+ { kind: 'Field', name: { kind: 'Name', value: 'type' } }
83
+ ]
84
+ }
85
+ },
86
+ { kind: 'Field', name: { kind: 'Name', value: 'title' } },
87
+ { kind: 'Field', name: { kind: 'Name', value: 'id' } },
88
+ { kind: 'Field', name: { kind: 'Name', value: 'link' } },
89
+ {
90
+ kind: 'Field',
91
+ name: { kind: 'Name', value: 'priceAndAvailability' },
92
+ selectionSet: {
93
+ kind: 'SelectionSet',
94
+ selections: [
95
+ { kind: 'Field', name: { kind: 'Name', value: 'currency' } },
96
+ { kind: 'Field', name: { kind: 'Name', value: 'price' } },
97
+ {
98
+ kind: 'Field',
99
+ name: { kind: 'Name', value: 'productSalePrices' },
100
+ selectionSet: {
101
+ kind: 'SelectionSet',
102
+ selections: [
103
+ { kind: 'Field', name: { kind: 'Name', value: 'salePrice' } },
104
+ { kind: 'Field', name: { kind: 'Name', value: 'salePriceEffectiveDateFrom' } },
105
+ { kind: 'Field', name: { kind: 'Name', value: 'salePriceEffectiveDateTo' } }
106
+ ]
107
+ }
108
+ }
109
+ ]
110
+ }
111
+ }
112
+ ]
113
+ }
114
+ },
115
+ {
116
+ kind: 'Field',
117
+ name: { kind: 'Name', value: 'ad' },
118
+ selectionSet: {
119
+ kind: 'SelectionSet',
120
+ selections: [
121
+ { kind: 'Field', name: { kind: 'Name', value: 'id' } },
122
+ { kind: 'Field', name: { kind: 'Name', value: 'title' } },
123
+ { kind: 'Field', name: { kind: 'Name', value: 'description' } },
124
+ { kind: 'Field', name: { kind: 'Name', value: 'buttonText' } },
125
+ { kind: 'Field', name: { kind: 'Name', value: 'buttonUrl' } },
126
+ { kind: 'Field', name: { kind: 'Name', value: 'openLinkInNewWindow' } },
127
+ { kind: 'Field', name: { kind: 'Name', value: 'type' } },
128
+ {
129
+ kind: 'Field',
130
+ name: { kind: 'Name', value: 'media' },
131
+ selectionSet: {
132
+ kind: 'SelectionSet',
133
+ selections: [
134
+ { kind: 'Field', name: { kind: 'Name', value: 'url' } },
135
+ { kind: 'Field', name: { kind: 'Name', value: 'thumbnailUrl' } },
136
+ { kind: 'Field', name: { kind: 'Name', value: 'type' } }
137
+ ]
138
+ }
139
+ }
140
+ ]
141
+ }
142
+ },
143
+ {
144
+ kind: 'Field',
145
+ name: { kind: 'Name', value: 'postData' },
146
+ selectionSet: {
147
+ kind: 'SelectionSet',
148
+ selections: [
149
+ {
150
+ kind: 'Field',
151
+ name: { kind: 'Name', value: 'media' },
152
+ selectionSet: {
153
+ kind: 'SelectionSet',
154
+ selections: [
155
+ {
156
+ kind: 'Field',
157
+ name: { kind: 'Name', value: 'url' },
158
+ arguments: [
159
+ {
160
+ kind: 'Argument',
161
+ name: { kind: 'Name', value: 'scale' },
162
+ value: { kind: 'Variable', name: { kind: 'Name', value: 'image_scale' } }
163
+ }
164
+ ]
165
+ },
166
+ {
167
+ kind: 'Field',
168
+ name: { kind: 'Name', value: 'thumbnailUrl' },
169
+ arguments: [
170
+ {
171
+ kind: 'Argument',
172
+ name: { kind: 'Name', value: 'scale' },
173
+ value: { kind: 'Variable', name: { kind: 'Name', value: 'image_scale' } }
174
+ }
175
+ ]
176
+ },
177
+ { kind: 'Field', name: { kind: 'Name', value: 'type' } }
178
+ ]
179
+ }
180
+ },
181
+ {
182
+ kind: 'Field',
183
+ name: { kind: 'Name', value: 'shortVideoData' },
184
+ selectionSet: { kind: 'SelectionSet', selections: [{ kind: 'Field', name: { kind: 'Name', value: 'text' } }] }
185
+ }
186
+ ]
187
+ }
188
+ }
189
+ ]
190
+ }
191
+ }
192
+ ]
193
+ };
@@ -0,0 +1,8 @@
1
+ # noinspection GraphQLSchemaValidation
2
+ query GetShortVideos($input: PostsInput!, $image_scale: ImageScale = ORIGINAL_ENCODED) {
3
+ shortVideos: posts(input: $input) {
4
+ items {
5
+ ...ShortVideoViewerPayloadFragment
6
+ }
7
+ }
8
+ }
@@ -0,0 +1,34 @@
1
+ import type { ShortVideoViewerModel } from '../..';
2
+ import type { IShortVideosPlayerLocalization } from './short-videos-player-localization.svelte';
3
+ import type { PlayerItemsProvider } from '../../ui/player';
4
+ export type PlayerInput = ProviderPlayerInput | IdsPlayerInput;
5
+ export type ProviderPlayerInput = {
6
+ type: 'provider';
7
+ provider: PlayerItemsProvider<ShortVideoViewerModel>;
8
+ };
9
+ export type IdsPlayerInput = {
10
+ type: 'ids';
11
+ ids: string[];
12
+ initialId?: string;
13
+ graphqlUrl?: string;
14
+ };
15
+ export type OpenShortVideosPlayerInit = ProviderInit | IdsInit;
16
+ type ProviderInit = {
17
+ shortVideosProvider: PlayerItemsProvider<ShortVideoViewerModel>;
18
+ localization?: IShortVideosPlayerLocalization;
19
+ on?: {
20
+ playerClosed?: () => void;
21
+ };
22
+ };
23
+ type IdsInit = {
24
+ ids: string[];
25
+ graphqlUrl?: string;
26
+ initialId?: string;
27
+ localization?: IShortVideosPlayerLocalization;
28
+ on?: {
29
+ playerClosed?: () => void;
30
+ };
31
+ };
32
+ export declare const isShortVideosProviderInit: (init: unknown) => init is ProviderInit;
33
+ export declare function isIdsInit(init: unknown): init is IdsInit;
34
+ export {};
@@ -0,0 +1,6 @@
1
+ export const isShortVideosProviderInit = (init) => {
2
+ return typeof init === 'object' && init !== null && 'shortVideosProvider' in init;
3
+ };
4
+ export function isIdsInit(init) {
5
+ return typeof init === 'object' && init !== null && 'ids' in init && 'graphqlUrl' in init;
6
+ }
@@ -7,6 +7,9 @@
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
+ import { getOrCreateProfileId } from '../../core/analytics.profile-id';
11
+ import { handleEsc } from '../../core/document.event-handlers';
12
+ import { createLocalGQLClient, resolveGraphQLUrl } from '../../core/graphql';
10
13
  import { toastrWarning } from '../../core/toastr';
11
14
  import { ShortVideoViewer } from '../../short-videos/short-video-viewer';
12
15
  import { mapToShortVideoViewerModel } from '../layout/models';
@@ -23,7 +26,7 @@ import { StreamPlayerLocalization } from './stream-player-localization.svelte';
23
26
  import { StreamPlayerUiManager } from './ui-manager.svelte';
24
27
  import { onMount } from 'svelte';
25
28
  import { AppEventsTracker } from '@streamscloud/streams-analytics-collector';
26
- let { streamId, graphql, localization: localizationInit, on } = $props();
29
+ let { streamId, graphqlUrl, localization: localizationInit, on } = $props();
27
30
  const localization = $derived(new StreamPlayerLocalization(localizationInit));
28
31
  let model = $state(null);
29
32
  let buffer = $state.raw(null);
@@ -51,12 +54,15 @@ onMount(() => __awaiter(void 0, void 0, void 0, function* () {
51
54
  var _a, _b, _c, _d;
52
55
  uiManager.overviewCollapsed = window && window.innerWidth < window.innerHeight;
53
56
  try {
57
+ const graphql = createLocalGQLClient(graphqlUrl);
54
58
  const streamPayload = yield graphql.query(GetStreamDocument, { id: streamId }).toPromise();
55
59
  if (!((_a = streamPayload.data) === null || _a === void 0 ? void 0 : _a.stream)) {
56
60
  toastrWarning(localization.streamNotFound);
57
61
  (_b = on === null || on === void 0 ? void 0 : on.closePlayer) === null || _b === void 0 ? void 0 : _b.call(on);
58
62
  return;
59
63
  }
64
+ AppEventsTracker.setEndpoint(resolveGraphQLUrl(graphqlUrl));
65
+ AppEventsTracker.setProfileId(getOrCreateProfileId());
60
66
  (_c = on === null || on === void 0 ? void 0 : on.streamActivated) === null || _c === void 0 ? void 0 : _c.call(on, {
61
67
  ownerId: streamPayload.data.stream.ownerProfile.id,
62
68
  title: streamPayload.data.stream.title,
@@ -146,6 +152,8 @@ const onProgress = (pageId, videoId, progress) => {
146
152
  };
147
153
  </script>
148
154
 
155
+ <svelte:document onkeydown={(e) => handleEsc(e, onPlayerClose)} />
156
+
149
157
  <div class="stream-player-container">
150
158
  {#if loading}
151
159
  <Loading positionAbsoluteCenter={true} timeout={600} />
@@ -165,8 +173,8 @@ const onProgress = (pageId, videoId, progress) => {
165
173
  <StreamPageViewer
166
174
  page={item}
167
175
  on={{
168
- progress: (videoId: string, progress: number) => onProgress(item.id, videoId, progress),
169
- productClick: (productId: string) => onProductCardClick(productId)
176
+ progress: (videoId: String, progress: Number) => onProgress(item.id, videoId, progress),
177
+ productClick: (productId: String) => onProductCardClick(productId)
170
178
  }} />
171
179
  {:else if item.type === 'short-video'}
172
180
  <ShortVideoViewer
@@ -196,7 +204,7 @@ const onProgress = (pageId, videoId, progress) => {
196
204
  localization={localization}
197
205
  on={{
198
206
  closePlayer: () => onPlayerClose(),
199
- productClick: (productId: string) => onProductCardClick(productId)
207
+ productClick: (productId: String) => onProductCardClick(productId)
200
208
  }} />
201
209
  {/if}
202
210
  </div>
@@ -1,9 +1,8 @@
1
1
  import { type IStreamPlayerLocalization } from './stream-player-localization.svelte';
2
- import type { Client } from '@urql/core';
3
2
  type Props = {
4
3
  streamId: string;
5
4
  localization?: IStreamPlayerLocalization;
6
- graphql: Client;
5
+ graphqlUrl?: string;
7
6
  on?: {
8
7
  closePlayer?: () => void;
9
8
  streamActivated?: (data: {
@@ -25,7 +25,7 @@ export type { IStreamPlayerLocalization };
25
25
  */
26
26
  export declare const openStreamPlayer: (init: {
27
27
  streamId: string;
28
- graphqlUrl: string;
28
+ graphqlUrl?: string;
29
29
  localization?: IStreamPlayerLocalization;
30
30
  on?: {
31
31
  streamActivated?: (data: {
@@ -1,25 +1,7 @@
1
- import { createLocalGQLClient } from '../../core/graphql';
2
1
  import { ShadowHost } from '../../ui/shadow-dom';
3
2
  import { default as StreamPlayer } from './cmp.stream-player.svelte';
4
3
  import { AppEventsTracker } from '@streamscloud/streams-analytics-collector';
5
- import { mount } from 'svelte';
6
- /**
7
- * Key used for storing the profile ID in local storage
8
- */
9
- const PROFILE_ID_STORAGE_KEY = 'streamscloud_profile_id';
10
- /**
11
- * Retrieves the profile ID from localStorage or generates a new one if it doesn't exist
12
- * @returns The profile ID to use for analytics tracking
13
- */
14
- function getOrCreateProfileId() {
15
- const storedProfileId = localStorage.getItem(PROFILE_ID_STORAGE_KEY);
16
- if (!storedProfileId) {
17
- const newProfileId = crypto.randomUUID();
18
- localStorage.setItem(PROFILE_ID_STORAGE_KEY, newProfileId);
19
- return newProfileId;
20
- }
21
- return storedProfileId;
22
- }
4
+ import { mount, unmount } from 'svelte';
23
5
  /**
24
6
  * Opens the stream player modal with the specified stream details.
25
7
  *
@@ -45,14 +27,12 @@ function getOrCreateProfileId() {
45
27
  */
46
28
  export const openStreamPlayer = (init) => {
47
29
  const { streamId, graphqlUrl, localization } = init;
48
- const shadowHost = new ShadowHost({ onClosed: () => init.on?.playerClosed?.() });
49
- AppEventsTracker.setEndpoint(graphqlUrl);
50
- AppEventsTracker.setProfileId(getOrCreateProfileId());
51
- mount(StreamPlayer, {
30
+ const shadowHost = new ShadowHost();
31
+ const mounted = mount(StreamPlayer, {
52
32
  target: shadowHost.shadowRoot,
53
33
  props: {
54
34
  streamId,
55
- graphql: createLocalGQLClient(graphqlUrl),
35
+ graphqlUrl,
56
36
  localization,
57
37
  on: {
58
38
  streamActivated: (data) => {
@@ -61,7 +41,11 @@ export const openStreamPlayer = (init) => {
61
41
  init.on.streamActivated({ title: data.title, image: data.image });
62
42
  }
63
43
  },
64
- closePlayer: () => {
44
+ closePlayer: async () => {
45
+ if (init.on?.playerClosed) {
46
+ init.on.playerClosed();
47
+ }
48
+ await unmount(mounted);
65
49
  shadowHost.remove();
66
50
  }
67
51
  }
@@ -1,11 +1,7 @@
1
1
  export declare class ShadowHost {
2
2
  shadowRoot: ShadowRoot;
3
3
  private host;
4
- private callbacks;
5
- constructor(init: {
6
- onClosed: () => void;
7
- });
4
+ constructor();
8
5
  attachToBody(): void;
9
6
  remove(): void;
10
- private handleEsc;
11
7
  }
@@ -3,11 +3,7 @@ import reset from './_reset.scss?raw';
3
3
  export class ShadowHost {
4
4
  shadowRoot;
5
5
  host;
6
- callbacks;
7
- constructor(init) {
8
- this.callbacks = {
9
- onClosed: init.onClosed
10
- };
6
+ constructor() {
11
7
  const host = document.createElement('div');
12
8
  host.style.all = 'unset';
13
9
  host.style.position = 'fixed';
@@ -19,7 +15,6 @@ export class ShadowHost {
19
15
  host.style.margin = '0';
20
16
  host.style.boxSizing = 'border-box';
21
17
  host.style.textAlign = 'initial';
22
- document.addEventListener('keydown', this.handleEsc);
23
18
  this.host = host;
24
19
  this.shadowRoot = host.attachShadow({ mode: 'open' });
25
20
  const styleElement = document.createElement('style');
@@ -33,13 +28,5 @@ export class ShadowHost {
33
28
  remove() {
34
29
  this.host.remove();
35
30
  document.getElementsByTagName('html')[0].style.overflow = '';
36
- document.removeEventListener('keydown', this.handleEsc);
37
- this.callbacks.onClosed();
38
31
  }
39
- handleEsc = (event) => {
40
- if (event.key === 'Escape' || event.key === 'Esc') {
41
- // Remove the modal from the DOM, for example:
42
- this.remove();
43
- }
44
- };
45
32
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@streamscloud/embeddable",
3
- "version": "2.1.6",
3
+ "version": "2.2.1",
4
4
  "author": "StreamsCloud",
5
5
  "repository": "https://github.com/StreamsCloud/streamscloud-frontend-packages.git",
6
6
  "type": "module",