@streamlayer/feature-advertisement 0.3.2 → 0.4.0
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/lib/advertisement.d.ts
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
import { AbstractFeatureUnit, StreamLayerContext } from '@streamlayer/sdk-web-interfaces';
|
|
2
2
|
import { externalAd, type ExternalAdOptions } from './units/external-ad';
|
|
3
3
|
import { externalPauseAd, type ExternalPauseAdOptions } from './units/external-pause-ad';
|
|
4
|
+
import { type PauseAdListOptions, type PauseAdStore } from './units/pause-ad';
|
|
4
5
|
export type ExternalAdStore = ReturnType<ReturnType<typeof externalAd>>;
|
|
5
6
|
export type ExternalPauseAdStore = ReturnType<ReturnType<typeof externalPauseAd>>;
|
|
6
7
|
export declare class Advertisement extends AbstractFeatureUnit {
|
|
7
8
|
getExternalAd: (adUrl: string, options: ExternalAdOptions) => [ExternalAdStore, string];
|
|
8
9
|
getExternalPauseAd: (adUrl: string, options: ExternalPauseAdOptions) => [ExternalPauseAdStore, string];
|
|
10
|
+
getPauseAdList: (options: PauseAdListOptions) => PauseAdStore;
|
|
9
11
|
private externalAdStores;
|
|
10
12
|
private externalAdEnabled;
|
|
11
13
|
private externalPauseAdStores;
|
|
12
14
|
private externalPauseAdEnabled;
|
|
15
|
+
private pauseAdStore;
|
|
16
|
+
private pauseAdEnabled;
|
|
13
17
|
private log;
|
|
14
18
|
constructor(instance: StreamLayerContext);
|
|
15
19
|
connect: () => void;
|
package/lib/advertisement.js
CHANGED
|
@@ -2,19 +2,42 @@ import { AbstractFeatureUnit, createSingleStore } from '@streamlayer/sdk-web-int
|
|
|
2
2
|
import { createLogger } from '@streamlayer/sdk-web-logger';
|
|
3
3
|
import { externalAd } from './units/external-ad';
|
|
4
4
|
import { externalPauseAd } from './units/external-pause-ad';
|
|
5
|
+
import { pauseAdListStore } from './units/pause-ad';
|
|
5
6
|
export class Advertisement extends AbstractFeatureUnit {
|
|
6
7
|
getExternalAd;
|
|
7
8
|
getExternalPauseAd;
|
|
9
|
+
getPauseAdList;
|
|
8
10
|
externalAdStores = new Map();
|
|
9
11
|
externalAdEnabled = new Map();
|
|
10
12
|
externalPauseAdStores = new Map();
|
|
11
13
|
externalPauseAdEnabled = new Map();
|
|
14
|
+
pauseAdStore = null;
|
|
15
|
+
pauseAdEnabled = null;
|
|
12
16
|
log;
|
|
13
17
|
constructor(instance) {
|
|
14
18
|
super();
|
|
15
19
|
this.log = createLogger('advertisement');
|
|
16
20
|
const externalAdGenerator = externalAd(instance);
|
|
17
21
|
const externalPauseAdGenerator = externalPauseAd(instance);
|
|
22
|
+
const pauseAdGenerator = pauseAdListStore(instance);
|
|
23
|
+
this.getPauseAdList = (options) => {
|
|
24
|
+
if (this.pauseAdStore) {
|
|
25
|
+
const $enabled = this.pauseAdEnabled;
|
|
26
|
+
const wasDisabled = $enabled.get() === false;
|
|
27
|
+
$enabled.set(options.$enabled);
|
|
28
|
+
if (wasDisabled && options.$enabled) {
|
|
29
|
+
window.requestAnimationFrame(() => {
|
|
30
|
+
if (this.pauseAdStore)
|
|
31
|
+
this.pauseAdStore.$list.revalidate();
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
return this.pauseAdStore;
|
|
35
|
+
}
|
|
36
|
+
const $enabled = createSingleStore(options.$enabled);
|
|
37
|
+
this.pauseAdEnabled = $enabled;
|
|
38
|
+
this.pauseAdStore = pauseAdGenerator($enabled, options);
|
|
39
|
+
return this.pauseAdStore;
|
|
40
|
+
};
|
|
18
41
|
this.getExternalAd = (adUrl, options) => {
|
|
19
42
|
if (this.externalAdStores.has(adUrl)) {
|
|
20
43
|
const $enabled = this.externalAdEnabled.get(adUrl);
|
|
@@ -75,5 +98,12 @@ export class Advertisement extends AbstractFeatureUnit {
|
|
|
75
98
|
});
|
|
76
99
|
this.externalPauseAdEnabled.clear();
|
|
77
100
|
this.externalPauseAdStores.clear();
|
|
101
|
+
if (this.pauseAdStore) {
|
|
102
|
+
this.pauseAdStore.$list.off();
|
|
103
|
+
this.pauseAdStore.$index.off();
|
|
104
|
+
this.pauseAdEnabled?.off();
|
|
105
|
+
this.pauseAdStore = null;
|
|
106
|
+
this.pauseAdEnabled = null;
|
|
107
|
+
}
|
|
78
108
|
};
|
|
79
109
|
}
|
package/lib/index.d.ts
CHANGED
|
@@ -7,4 +7,5 @@ declare module '@streamlayer/sdk-web-interfaces' {
|
|
|
7
7
|
export { type ExternalAdStore, type ExternalPauseAdStore } from './advertisement';
|
|
8
8
|
export { type GAMStaticAdCore } from './units/external-ad';
|
|
9
9
|
export { type GAMStaticCore } from './units/external-pause-ad';
|
|
10
|
+
export { type PauseAdCard, type PauseAdStore } from './units/pause-ad';
|
|
10
11
|
export declare const advertisement: (instance: StreamLayerContext, _opts: unknown, done: () => void) => void;
|
|
@@ -36,10 +36,9 @@ export const externalPauseAd = (instance) => {
|
|
|
36
36
|
log.debug(nonlinear, 'nonlinear');
|
|
37
37
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
38
38
|
// @ts-ignore
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const adUrlTemplate = nonlinear.variations?.[0]?.nonlinearClickThroughURLTemplate;
|
|
39
|
+
const variation = nonlinear.variations?.[0];
|
|
40
|
+
const staticResource = variation?.staticResource;
|
|
41
|
+
const adUrlTemplate = variation?.nonlinearClickThroughURLTemplate;
|
|
43
42
|
if (staticResource) {
|
|
44
43
|
await preloadImage(staticResource);
|
|
45
44
|
eventBus.emit('exposedPauseAd', {
|
|
@@ -55,6 +54,9 @@ export const externalPauseAd = (instance) => {
|
|
|
55
54
|
url: adUrl,
|
|
56
55
|
imageSrc: staticResource,
|
|
57
56
|
adUrl: adUrlTemplate,
|
|
57
|
+
ad: validAd,
|
|
58
|
+
creative: nonlinear,
|
|
59
|
+
variation,
|
|
58
60
|
};
|
|
59
61
|
}
|
|
60
62
|
}
|
|
@@ -62,11 +64,12 @@ export const externalPauseAd = (instance) => {
|
|
|
62
64
|
log.debug(companion, 'companion');
|
|
63
65
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
64
66
|
// @ts-ignore
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
const variation = companion?.variations?.[0];
|
|
68
|
+
const staticResource = variation?.staticResources?.[0]?.url;
|
|
69
|
+
const adUrlTemplate = variation?.companionClickThroughURLTemplate;
|
|
70
|
+
// `&& companion` is for TS narrowing only (companion.adId access below);
|
|
71
|
+
// it is runtime-redundant since staticResource derives from companion.
|
|
72
|
+
if (staticResource && companion) {
|
|
70
73
|
const id = companion.adId || companion.id || parentId;
|
|
71
74
|
await preloadImage(staticResource);
|
|
72
75
|
eventBus.emit('exposedPauseAd', {
|
|
@@ -82,6 +85,9 @@ export const externalPauseAd = (instance) => {
|
|
|
82
85
|
url: adUrl,
|
|
83
86
|
imageSrc: staticResource,
|
|
84
87
|
adUrl: adUrlTemplate,
|
|
88
|
+
ad: validAd,
|
|
89
|
+
creative: companion,
|
|
90
|
+
variation,
|
|
85
91
|
};
|
|
86
92
|
}
|
|
87
93
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { createSingleStore, StreamLayerContext } from '@streamlayer/sdk-web-interfaces';
|
|
2
|
+
import '@streamlayer/sdk-web-api';
|
|
3
|
+
import '@streamlayer/sdk-web-core/store';
|
|
4
|
+
export type PauseAdLayout = 'left' | 'center' | 'image';
|
|
5
|
+
export type PauseAdCard = {
|
|
6
|
+
id: string;
|
|
7
|
+
parentId: string;
|
|
8
|
+
backgroundImageSrc: string;
|
|
9
|
+
layout: PauseAdLayout;
|
|
10
|
+
isInteractive: boolean;
|
|
11
|
+
logoSrc?: string;
|
|
12
|
+
title?: string;
|
|
13
|
+
description?: string;
|
|
14
|
+
ctaLabel?: string;
|
|
15
|
+
ctaUrl?: string;
|
|
16
|
+
qrCodeUrl?: string;
|
|
17
|
+
qrCodeLabel?: string;
|
|
18
|
+
};
|
|
19
|
+
export type PauseAdListOptions = {
|
|
20
|
+
refetchInterval: number;
|
|
21
|
+
$enabled: boolean;
|
|
22
|
+
};
|
|
23
|
+
export declare const pauseAdListStore: (instance: StreamLayerContext) => ($enabled: ReturnType<typeof createSingleStore<boolean>>, options: PauseAdListOptions) => {
|
|
24
|
+
$list: import("@nanostores/query").FetcherStore<PauseAdCard[], any>;
|
|
25
|
+
$index: import("nanostores").PreinitializedWritableAtom<number> & object;
|
|
26
|
+
next: () => void;
|
|
27
|
+
reset: () => void;
|
|
28
|
+
getCurrent: () => PauseAdCard | undefined;
|
|
29
|
+
};
|
|
30
|
+
export type PauseAdStore = ReturnType<ReturnType<typeof pauseAdListStore>>;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { createLogger } from '@streamlayer/sdk-web-logger';
|
|
2
|
+
import { atom } from 'nanostores';
|
|
3
|
+
import '@streamlayer/sdk-web-api';
|
|
4
|
+
import '@streamlayer/sdk-web-core/store';
|
|
5
|
+
import { CardFormat, NotificationSponsorLogoMode, PauseAdLayout as PauseAdLayoutProto, PromotionButtonType, PromotionFormat, QRCode, UseGeneralLogo, UserAccountInfo, } from '@streamlayer/sl-eslib/interactive/interactive.common_pb';
|
|
6
|
+
import { Feed } from '@streamlayer/sl-eslib/interactive/feed/interactive.feed_pb';
|
|
7
|
+
const log = createLogger('advertisement:pause-ad');
|
|
8
|
+
const parsePauseAdCard = (question) => {
|
|
9
|
+
const backgroundImageSrc = question.backgroundImage?.landscapeUrl;
|
|
10
|
+
if (!backgroundImageSrc) {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
const promotionOptions = question.options?.options.case === 'promotion' ? question.options.options.value : undefined;
|
|
14
|
+
const pauseAdOptions = promotionOptions?.pauseAdOptions;
|
|
15
|
+
const optionsSponsor = promotionOptions?.sponsor;
|
|
16
|
+
const layoutProto = pauseAdOptions?.layout ?? PauseAdLayoutProto.UNSET;
|
|
17
|
+
const layout = layoutProto === PauseAdLayoutProto.CENTER ? 'center' : layoutProto === PauseAdLayoutProto.IMAGE ? 'image' : 'left';
|
|
18
|
+
const isInteractive = promotionOptions?.buttonType === PromotionButtonType.SEND_TO_USER;
|
|
19
|
+
const hasQr = pauseAdOptions?.qrCode === QRCode.QR_CODE_ENABLED && !!pauseAdOptions?.qrCodeUrl;
|
|
20
|
+
let ctaLabel;
|
|
21
|
+
let ctaUrl;
|
|
22
|
+
if (isInteractive && promotionOptions?.buttonOptions.case === 'sendToUserOptions') {
|
|
23
|
+
const accountInfo = promotionOptions.buttonOptions.value.userAccountInfo;
|
|
24
|
+
const isPhone = accountInfo === UserAccountInfo.PHONE;
|
|
25
|
+
ctaUrl = isPhone ? 'sendToPhone:' : 'sendToEmail:';
|
|
26
|
+
ctaLabel = isPhone ? 'Send info to account phone' : 'Send info to account email';
|
|
27
|
+
}
|
|
28
|
+
const logoSrc = optionsSponsor?.logoPosition === NotificationSponsorLogoMode.NOTIFICATION_SPONSOR_LOGO_NONE
|
|
29
|
+
? undefined
|
|
30
|
+
: optionsSponsor?.useGeneralLogo === UseGeneralLogo.ENABLED
|
|
31
|
+
? question.promotion?.generalTabMetadata?.advertiserTransparentLogo || undefined
|
|
32
|
+
: optionsSponsor?.centerLogo || undefined;
|
|
33
|
+
return {
|
|
34
|
+
id: question.id,
|
|
35
|
+
parentId: question.eventId || question.id,
|
|
36
|
+
backgroundImageSrc,
|
|
37
|
+
layout,
|
|
38
|
+
logoSrc,
|
|
39
|
+
title: promotionOptions?.banner?.title || undefined,
|
|
40
|
+
description: promotionOptions?.banner?.body || undefined,
|
|
41
|
+
isInteractive,
|
|
42
|
+
ctaLabel: ctaLabel || undefined,
|
|
43
|
+
ctaUrl: ctaUrl || undefined,
|
|
44
|
+
qrCodeUrl: hasQr ? (pauseAdOptions?.qrCodeUrl ?? undefined) : undefined,
|
|
45
|
+
qrCodeLabel: hasQr ? (pauseAdOptions?.qrCodeLabel ?? undefined) : undefined,
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
export const pauseAdListStore = (instance) => {
|
|
49
|
+
const $slStreamId = instance.stores.slStreamId.getAtomStore();
|
|
50
|
+
return ($enabled, options) => {
|
|
51
|
+
const { client, queryKey } = instance.transport.createPromiseClient(Feed, {
|
|
52
|
+
method: 'pauseAdList',
|
|
53
|
+
params: [$slStreamId, $enabled],
|
|
54
|
+
});
|
|
55
|
+
const $list = instance.transport.nanoquery.createFetcherStore(queryKey, {
|
|
56
|
+
dedupeTime: 0,
|
|
57
|
+
revalidateInterval: options.refetchInterval > 0 ? options.refetchInterval : 0,
|
|
58
|
+
fetcher: async (_, __, eventId, isEnabled) => {
|
|
59
|
+
if (!eventId || typeof eventId !== 'string' || !isEnabled) {
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
62
|
+
log.debug({ eventId }, 'fetching pause ad list');
|
|
63
|
+
const res = await client.pauseAdList({ eventId });
|
|
64
|
+
const questions = res.data ?? [];
|
|
65
|
+
const cards = questions
|
|
66
|
+
.filter((q) => {
|
|
67
|
+
const opts = q.options?.options.case === 'promotion' ? q.options.options.value : undefined;
|
|
68
|
+
return opts?.format === PromotionFormat.FULL_BLEED || q.format === CardFormat.FULL_BLEED;
|
|
69
|
+
})
|
|
70
|
+
.map((q) => parsePauseAdCard(q))
|
|
71
|
+
.filter((c) => c !== null);
|
|
72
|
+
log.debug({ count: cards.length }, 'parsed %d fullbleed pause ad cards');
|
|
73
|
+
return cards;
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
const $index = atom(0);
|
|
77
|
+
const next = () => {
|
|
78
|
+
const list = $list.get().data;
|
|
79
|
+
if (!list?.length)
|
|
80
|
+
return;
|
|
81
|
+
$index.set(($index.get() + 1) % list.length);
|
|
82
|
+
};
|
|
83
|
+
const reset = () => {
|
|
84
|
+
$index.set(0);
|
|
85
|
+
};
|
|
86
|
+
const getCurrent = () => {
|
|
87
|
+
const list = $list.get().data;
|
|
88
|
+
if (!list?.length)
|
|
89
|
+
return undefined;
|
|
90
|
+
return list[$index.get()];
|
|
91
|
+
};
|
|
92
|
+
return { $list, $index, next, reset, getCurrent };
|
|
93
|
+
};
|
|
94
|
+
};
|
package/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@streamlayer/feature-advertisement",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"peerDependencies": {
|
|
5
5
|
"@dailymotion/vast-client": "^6.4.2",
|
|
6
6
|
"@streamlayer/sl-eslib": "^5.229.1",
|
|
7
7
|
"nanostores": "^1.1.0",
|
|
8
|
-
"@streamlayer/sdk-web-core": "^1.22.
|
|
9
|
-
"@streamlayer/sdk-web-api": "^1.17.
|
|
10
|
-
"@streamlayer/sdk-web-interfaces": "^1.10.
|
|
11
|
-
"@streamlayer/sdk-web-types": "^1.18.
|
|
12
|
-
"@streamlayer/sdk-web-logger": "^1.1.
|
|
8
|
+
"@streamlayer/sdk-web-core": "^1.22.3",
|
|
9
|
+
"@streamlayer/sdk-web-api": "^1.17.3",
|
|
10
|
+
"@streamlayer/sdk-web-interfaces": "^1.10.3",
|
|
11
|
+
"@streamlayer/sdk-web-types": "^1.18.3",
|
|
12
|
+
"@streamlayer/sdk-web-logger": "^1.1.3"
|
|
13
13
|
},
|
|
14
14
|
"devDependencies": {
|
|
15
15
|
"@types/vast-client": "^3.0.4"
|