@streamscloud/streams-analytics-collector 2.0.10 → 3.0.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/dist/analytics-collector.d.ts +24 -0
- package/dist/analytics-collector.js +51 -0
- package/dist/{analytics/app-events-tracker.d.ts → app-events-tracker.d.ts} +37 -0
- package/dist/appearance-events-tracker.d.ts +14 -0
- package/dist/appearance-events-tracker.js +26 -0
- package/dist/contained-entity-measurements-tracker.d.ts +15 -0
- package/dist/contained-entity-measurements-tracker.js +78 -0
- package/dist/events-reporter.d.ts +14 -0
- package/dist/events-reporter.js +27 -0
- package/dist/gql-data-sender.d.ts +16 -0
- package/dist/gql-data-sender.js +38 -0
- package/dist/graphql/report-event.graphql.js +3 -0
- package/dist/graphql/report-external-event.graphql.js +3 -0
- package/dist/graphql/report-external-measurement.graphql.js +3 -0
- package/dist/graphql/report-measurement.graphql.js +3 -0
- package/dist/index.d.ts +6 -2
- package/dist/index.js +6 -4
- package/dist/measurements-batcher.d.ts +23 -0
- package/dist/measurements-batcher.js +90 -0
- package/dist/measurements-reporter.d.ts +15 -0
- package/dist/measurements-reporter.js +28 -0
- package/dist/report-event.graphql.js +3 -0
- package/dist/report-external-event.graphql.js +3 -0
- package/dist/report-external-measurement.graphql.js +3 -0
- package/dist/report-measurement.graphql.js +3 -0
- package/dist/reporting/events-reporter.d.ts +14 -0
- package/dist/reporting/events-reporter.js +30 -0
- package/dist/reporting/gql-data-sender.d.ts +12 -0
- package/dist/reporting/gql-data-sender.js +35 -0
- package/dist/reporting/index.d.ts +4 -0
- package/dist/reporting/measurements-reporter.d.ts +15 -0
- package/dist/reporting/measurements-reporter.js +31 -0
- package/dist/reporting/report-event.graphql.js +3 -0
- package/dist/reporting/report-external-event.graphql.js +3 -0
- package/dist/reporting/report-external-measurement.graphql.js +3 -0
- package/dist/reporting/report-measurement.graphql.js +3 -0
- package/dist/reporting/types.d.ts +54 -0
- package/dist/reporting/types.js +35 -0
- package/dist/schemas.d.ts +5 -0
- package/dist/schemas.js +17 -0
- package/dist/types.d.ts +0 -4
- package/dist/types.js +38 -0
- package/dist/viewport-visibility-tracker.d.ts +13 -0
- package/dist/viewport-visibility-tracker.js +104 -0
- package/package.json +5 -6
- package/dist/adcampaignsquery.graphql.js +0 -3
- package/dist/analytics/ViewportVisibilityTracker.d.ts +0 -46
- package/dist/analytics/ViewportVisibilityTracker.js +0 -117
- package/dist/analytics/app-events-tracker.js +0 -430
- package/dist/analytics/index.d.ts +0 -3
- package/dist/analytics/index.js +0 -3
- package/dist/analytics/types.d.ts +0 -37
- package/dist/analytics/types.js +0 -33
- package/dist/analytics.graphql.js +0 -3
- package/dist/articlequery.graphql.js +0 -3
- package/dist/components/index.d.ts +0 -2
- package/dist/components/index.js +0 -1
- package/dist/components/types.d.ts +0 -19
- package/dist/components/types.js +0 -12
- package/dist/components-data/component-data-provider.service.d.ts +0 -3
- package/dist/components-data/component-data-provider.service.js +0 -35
- package/dist/components-data/components-data-fetcher.service.d.ts +0 -5
- package/dist/components-data/components-data-fetcher.service.js +0 -35
- package/dist/components-data/components-data-parameters-reader.service.d.ts +0 -2
- package/dist/components-data/components-data-parameters-reader.service.js +0 -20
- package/dist/components-data/index.d.ts +0 -4
- package/dist/components-data/index.js +0 -3
- package/dist/components-data/types.d.ts +0 -24
- package/dist/components-data/types.js +0 -6
- package/dist/contentlistsquery.graphql.js +0 -3
- package/dist/data-loaders/index.d.ts +0 -2
- package/dist/data-loaders/index.js +0 -2
- package/dist/data-loaders/short-videos-data-loader/index.d.ts +0 -2
- package/dist/data-loaders/short-videos-data-loader/loader.d.ts +0 -4
- package/dist/data-loaders/short-videos-data-loader/loader.js +0 -14
- package/dist/data-loaders/short-videos-data-loader/mapper.d.ts +0 -3
- package/dist/data-loaders/short-videos-data-loader/mapper.js +0 -27
- package/dist/data-loaders/short-videos-data-loader/types.d.ts +0 -14
- package/dist/data-loaders/streams-data-loader/index.d.ts +0 -2
- package/dist/data-loaders/streams-data-loader/loader.d.ts +0 -4
- package/dist/data-loaders/streams-data-loader/loader.js +0 -14
- package/dist/data-loaders/streams-data-loader/mapper.d.ts +0 -3
- package/dist/data-loaders/streams-data-loader/mapper.js +0 -11
- package/dist/data-loaders/streams-data-loader/types.d.ts +0 -4
- package/dist/data-loaders/types.d.ts +0 -3
- package/dist/external-analytics.graphql.js +0 -3
- package/dist/internal-analytics.graphql.js +0 -3
- package/dist/postsquery.graphql.js +0 -3
- package/dist/services/embed-content-api-client.d.ts +0 -8
- package/dist/services/embed-content-api-client.js +0 -48
- package/dist/services/embed-content-client.service.d.ts +0 -2
- package/dist/services/embed-content-client.service.js +0 -5
- package/dist/services/index.d.ts +0 -2
- package/dist/services/index.js +0 -2
- package/dist/services/operations/posts-query.graphql.js +0 -3
- package/dist/services/operations/streams-query.graphql.js +0 -3
- package/dist/services/types.d.ts +0 -303
- package/dist/streams-api-client-model.d.ts +0 -5
- package/dist/streams-content-api-client.d.ts +0 -14
- package/dist/streams-content-api-client.js +0 -89
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
class GqlDataSender {
|
|
2
|
+
gqlEndpoint = null;
|
|
3
|
+
client = null;
|
|
4
|
+
constructor(options) {
|
|
5
|
+
if ('client' in options) {
|
|
6
|
+
this.client = options.client;
|
|
7
|
+
this.gqlEndpoint = null;
|
|
8
|
+
}
|
|
9
|
+
else {
|
|
10
|
+
this.gqlEndpoint = options.endpoint;
|
|
11
|
+
this.client = null;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
async send(query, variables) {
|
|
15
|
+
if (this.gqlEndpoint) {
|
|
16
|
+
await sendGqlBeacon(this.gqlEndpoint, query, variables);
|
|
17
|
+
}
|
|
18
|
+
else if (this.client) {
|
|
19
|
+
await this.client.mutation(query, variables).toPromise();
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
console.error('GqlDataSender not properly initialized.');
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
async function sendGqlBeacon(endpoint, query, variables) {
|
|
27
|
+
const body = JSON.stringify({ query, variables });
|
|
28
|
+
const blob = new Blob([body], { type: 'application/json' });
|
|
29
|
+
const accepted = navigator.sendBeacon(endpoint, blob);
|
|
30
|
+
if (!accepted) {
|
|
31
|
+
throw new Error('Failed to queue analytics beacon.');
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export { GqlDataSender };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { type ReportedEvent, EventsReporter } from './events-reporter';
|
|
2
|
+
export { type GqlDataSenderOptions, GqlDataSender } from './gql-data-sender';
|
|
3
|
+
export { type ReportedMeasurement, MeasurementsReporter } from './measurements-reporter';
|
|
4
|
+
export { EntityRelationType, EntityType, EventType, MeasurementType, type AnalyticsMetadata, type AnalyticsSchema, type Entity, type EntityRelation, } from './types';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { AnalyticsSchema, AnalyticsMetadata, MeasurementType, EntityRelation, Entity } from './types';
|
|
2
|
+
import type { GqlDataSender } from './gql-data-sender';
|
|
3
|
+
export type ReportedMeasurement = {
|
|
4
|
+
entity: Entity;
|
|
5
|
+
measurementType: MeasurementType;
|
|
6
|
+
value: number;
|
|
7
|
+
relatedEntities?: EntityRelation[] | null;
|
|
8
|
+
};
|
|
9
|
+
export declare class MeasurementsReporter {
|
|
10
|
+
private gqlDataSender;
|
|
11
|
+
private analyticsSchema;
|
|
12
|
+
private analyticsMetadata?;
|
|
13
|
+
constructor(gqlDataSender: GqlDataSender, analyticsSchema: AnalyticsSchema, analyticsMetadata?: AnalyticsMetadata | null);
|
|
14
|
+
reportMeasurement({ entity, measurementType, value, relatedEntities }: ReportedMeasurement): Promise<void>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
class MeasurementsReporter {
|
|
2
|
+
gqlDataSender;
|
|
3
|
+
analyticsSchema;
|
|
4
|
+
analyticsMetadata;
|
|
5
|
+
constructor(gqlDataSender, analyticsSchema, analyticsMetadata) {
|
|
6
|
+
this.gqlDataSender = gqlDataSender;
|
|
7
|
+
this.analyticsSchema = analyticsSchema;
|
|
8
|
+
this.analyticsMetadata = analyticsMetadata;
|
|
9
|
+
}
|
|
10
|
+
async reportMeasurement({ entity, measurementType, value, relatedEntities }) {
|
|
11
|
+
const input = {
|
|
12
|
+
entityType: entity.type,
|
|
13
|
+
entityId: entity.id,
|
|
14
|
+
measurementType,
|
|
15
|
+
value,
|
|
16
|
+
relatedEntities: relatedEntities
|
|
17
|
+
?.map(({ relationType, entity }) => ({
|
|
18
|
+
relationType,
|
|
19
|
+
entityType: entity.type,
|
|
20
|
+
entityId: entity.id,
|
|
21
|
+
})) ?? null,
|
|
22
|
+
installation: this.analyticsMetadata?.installationId
|
|
23
|
+
? {
|
|
24
|
+
id: this.analyticsMetadata.installationId,
|
|
25
|
+
} : null,
|
|
26
|
+
};
|
|
27
|
+
await this.gqlDataSender.send(this.analyticsSchema.reportMeasurement, { input });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export { MeasurementsReporter };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export declare enum EntityType {
|
|
2
|
+
Article = "ARTICLE",
|
|
3
|
+
Audio = "AUDIO",
|
|
4
|
+
Event = "EVENT",
|
|
5
|
+
Media = "MEDIA",
|
|
6
|
+
Moment = "MOMENT",
|
|
7
|
+
Link = "LINK",
|
|
8
|
+
ShortVideo = "SHORT_VIDEO",
|
|
9
|
+
Text = "TEXT",
|
|
10
|
+
Video = "VIDEO",
|
|
11
|
+
Product = "PRODUCT",
|
|
12
|
+
Stream = "STREAM",
|
|
13
|
+
StreamPage = "STREAM_PAGE",
|
|
14
|
+
Ad = "AD",
|
|
15
|
+
MediaPage = "MEDIA_PAGE"
|
|
16
|
+
}
|
|
17
|
+
export declare enum EventType {
|
|
18
|
+
Appeared = "APPEARED",
|
|
19
|
+
Viewed = "VIEWED",
|
|
20
|
+
Clicked = "CLICKED"
|
|
21
|
+
}
|
|
22
|
+
export declare enum MeasurementType {
|
|
23
|
+
ScrollDepth = "SCROLL_DEPTH",
|
|
24
|
+
EngagementTime = "ENGAGEMENT_TIME",
|
|
25
|
+
Progress = "PROGRESS"
|
|
26
|
+
}
|
|
27
|
+
export declare enum EntityRelationType {
|
|
28
|
+
PlacedIn = "PLACED_IN"
|
|
29
|
+
}
|
|
30
|
+
export type AnalyticsSchema = {
|
|
31
|
+
reportEvent: string;
|
|
32
|
+
reportMeasurement: string;
|
|
33
|
+
};
|
|
34
|
+
export type AnalyticsMetadata = {
|
|
35
|
+
installationId?: string | null;
|
|
36
|
+
};
|
|
37
|
+
export type Entity = {
|
|
38
|
+
type: EntityType;
|
|
39
|
+
id: string;
|
|
40
|
+
};
|
|
41
|
+
export type EntityRelation = {
|
|
42
|
+
relationType: EntityRelationType;
|
|
43
|
+
entity: Entity;
|
|
44
|
+
};
|
|
45
|
+
export type EntityRelationInput = {
|
|
46
|
+
relationType: EntityRelationType;
|
|
47
|
+
entityType: EntityType;
|
|
48
|
+
entityId: string;
|
|
49
|
+
};
|
|
50
|
+
export type InstallationInput = {
|
|
51
|
+
id: string;
|
|
52
|
+
device?: string | null;
|
|
53
|
+
browser?: string | null;
|
|
54
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
var EntityType;
|
|
2
|
+
(function (EntityType) {
|
|
3
|
+
EntityType["Article"] = "ARTICLE";
|
|
4
|
+
EntityType["Audio"] = "AUDIO";
|
|
5
|
+
EntityType["Event"] = "EVENT";
|
|
6
|
+
EntityType["Media"] = "MEDIA";
|
|
7
|
+
EntityType["Moment"] = "MOMENT";
|
|
8
|
+
EntityType["Link"] = "LINK";
|
|
9
|
+
EntityType["ShortVideo"] = "SHORT_VIDEO";
|
|
10
|
+
EntityType["Text"] = "TEXT";
|
|
11
|
+
EntityType["Video"] = "VIDEO";
|
|
12
|
+
EntityType["Product"] = "PRODUCT";
|
|
13
|
+
EntityType["Stream"] = "STREAM";
|
|
14
|
+
EntityType["StreamPage"] = "STREAM_PAGE";
|
|
15
|
+
EntityType["Ad"] = "AD";
|
|
16
|
+
EntityType["MediaPage"] = "MEDIA_PAGE";
|
|
17
|
+
})(EntityType || (EntityType = {}));
|
|
18
|
+
var EventType;
|
|
19
|
+
(function (EventType) {
|
|
20
|
+
EventType["Appeared"] = "APPEARED";
|
|
21
|
+
EventType["Viewed"] = "VIEWED";
|
|
22
|
+
EventType["Clicked"] = "CLICKED";
|
|
23
|
+
})(EventType || (EventType = {}));
|
|
24
|
+
var MeasurementType;
|
|
25
|
+
(function (MeasurementType) {
|
|
26
|
+
MeasurementType["ScrollDepth"] = "SCROLL_DEPTH";
|
|
27
|
+
MeasurementType["EngagementTime"] = "ENGAGEMENT_TIME";
|
|
28
|
+
MeasurementType["Progress"] = "PROGRESS";
|
|
29
|
+
})(MeasurementType || (MeasurementType = {}));
|
|
30
|
+
var EntityRelationType;
|
|
31
|
+
(function (EntityRelationType) {
|
|
32
|
+
EntityRelationType["PlacedIn"] = "PLACED_IN";
|
|
33
|
+
})(EntityRelationType || (EntityRelationType = {}));
|
|
34
|
+
|
|
35
|
+
export { EntityRelationType, EntityType, EventType, MeasurementType };
|
package/dist/schemas.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import ReportEvent from './graphql/report-event.graphql.js';
|
|
2
|
+
import ReportExternalEvent from './graphql/report-external-event.graphql.js';
|
|
3
|
+
import ReportMeasurement from './graphql/report-measurement.graphql.js';
|
|
4
|
+
import ReportExternalMeasurement from './graphql/report-external-measurement.graphql.js';
|
|
5
|
+
|
|
6
|
+
const Schemas = {
|
|
7
|
+
internal: {
|
|
8
|
+
reportEvent: ReportEvent,
|
|
9
|
+
reportMeasurement: ReportMeasurement,
|
|
10
|
+
},
|
|
11
|
+
external: {
|
|
12
|
+
reportEvent: ReportExternalEvent,
|
|
13
|
+
reportMeasurement: ReportExternalMeasurement,
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export { Schemas };
|
package/dist/types.d.ts
CHANGED
package/dist/types.js
CHANGED
|
@@ -1 +1,39 @@
|
|
|
1
|
+
var ApiMode;
|
|
2
|
+
(function (ApiMode) {
|
|
3
|
+
ApiMode["Internal"] = "internal";
|
|
4
|
+
ApiMode["External"] = "external";
|
|
5
|
+
})(ApiMode || (ApiMode = {}));
|
|
6
|
+
var EntityType;
|
|
7
|
+
(function (EntityType) {
|
|
8
|
+
EntityType["Article"] = "ARTICLE";
|
|
9
|
+
EntityType["Audio"] = "AUDIO";
|
|
10
|
+
EntityType["Event"] = "EVENT";
|
|
11
|
+
EntityType["Media"] = "MEDIA";
|
|
12
|
+
EntityType["Moment"] = "MOMENT";
|
|
13
|
+
EntityType["Link"] = "LINK";
|
|
14
|
+
EntityType["ShortVideo"] = "SHORT_VIDEO";
|
|
15
|
+
EntityType["Text"] = "TEXT";
|
|
16
|
+
EntityType["Video"] = "VIDEO";
|
|
17
|
+
EntityType["Product"] = "PRODUCT";
|
|
18
|
+
EntityType["Stream"] = "STREAM";
|
|
19
|
+
EntityType["StreamPage"] = "STREAM_PAGE";
|
|
20
|
+
EntityType["Ad"] = "AD";
|
|
21
|
+
})(EntityType || (EntityType = {}));
|
|
22
|
+
var EventType;
|
|
23
|
+
(function (EventType) {
|
|
24
|
+
EventType["Appeared"] = "APPEARED";
|
|
25
|
+
EventType["Viewed"] = "VIEWED";
|
|
26
|
+
EventType["Clicked"] = "CLICKED";
|
|
27
|
+
})(EventType || (EventType = {}));
|
|
28
|
+
var MeasurementType;
|
|
29
|
+
(function (MeasurementType) {
|
|
30
|
+
MeasurementType["ScrollDepth"] = "SCROLL_DEPTH";
|
|
31
|
+
MeasurementType["EngagementTime"] = "ENGAGEMENT_TIME";
|
|
32
|
+
MeasurementType["Progress"] = "PROGRESS";
|
|
33
|
+
})(MeasurementType || (MeasurementType = {}));
|
|
34
|
+
var EntityRelationType;
|
|
35
|
+
(function (EntityRelationType) {
|
|
36
|
+
EntityRelationType["PlacedIn"] = "PLACED_IN";
|
|
37
|
+
})(EntityRelationType || (EntityRelationType = {}));
|
|
1
38
|
|
|
39
|
+
export { ApiMode, EntityRelationType, EntityType, EventType, MeasurementType };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare class ViewportVisibilityTracker {
|
|
2
|
+
private trackedIds;
|
|
3
|
+
private targetToElements;
|
|
4
|
+
private elementToTargetId;
|
|
5
|
+
private callbacks;
|
|
6
|
+
private observer;
|
|
7
|
+
registerElement(el: HTMLElement, targetId: string, callback: (id: string) => void): void;
|
|
8
|
+
unregisterElement(el: HTMLElement): void;
|
|
9
|
+
unregisterAllElements(targetId: string): void;
|
|
10
|
+
reset(): void;
|
|
11
|
+
private getObserver;
|
|
12
|
+
private handleIntersections;
|
|
13
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
const VISIBILITY_THRESHOLD = 0.5;
|
|
2
|
+
class ViewportVisibilityTracker {
|
|
3
|
+
trackedIds = new Set();
|
|
4
|
+
targetToElements = new Map();
|
|
5
|
+
elementToTargetId = new Map();
|
|
6
|
+
callbacks = new Map();
|
|
7
|
+
observer;
|
|
8
|
+
registerElement(el, targetId, callback) {
|
|
9
|
+
if (this.trackedIds.has(targetId)) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
const set = this.targetToElements.get(targetId) ?? new Set();
|
|
13
|
+
set.add(el);
|
|
14
|
+
this.targetToElements.set(targetId, set);
|
|
15
|
+
this.elementToTargetId.set(el, targetId);
|
|
16
|
+
this.callbacks.set(targetId, callback);
|
|
17
|
+
const observer = this.getObserver();
|
|
18
|
+
observer.observe(el);
|
|
19
|
+
}
|
|
20
|
+
unregisterElement(el) {
|
|
21
|
+
const targetId = this.elementToTargetId.get(el);
|
|
22
|
+
if (!targetId) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
this.getObserver().unobserve(el);
|
|
26
|
+
this.elementToTargetId.delete(el);
|
|
27
|
+
const set = this.targetToElements.get(targetId);
|
|
28
|
+
if (set) {
|
|
29
|
+
set.delete(el);
|
|
30
|
+
if (set.size === 0) {
|
|
31
|
+
this.targetToElements.delete(targetId);
|
|
32
|
+
this.callbacks.delete(targetId);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
unregisterAllElements(targetId) {
|
|
37
|
+
const set = this.targetToElements.get(targetId);
|
|
38
|
+
if (set) {
|
|
39
|
+
for (const el of set) {
|
|
40
|
+
this.getObserver().unobserve(el);
|
|
41
|
+
this.elementToTargetId.delete(el);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
this.targetToElements.delete(targetId);
|
|
45
|
+
this.callbacks.delete(targetId);
|
|
46
|
+
}
|
|
47
|
+
reset() {
|
|
48
|
+
for (const set of this.targetToElements.values()) {
|
|
49
|
+
for (const el of set) {
|
|
50
|
+
this.getObserver().unobserve(el);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
this.trackedIds.clear();
|
|
54
|
+
this.targetToElements.clear();
|
|
55
|
+
this.elementToTargetId.clear();
|
|
56
|
+
this.callbacks.clear();
|
|
57
|
+
if (this.observer) {
|
|
58
|
+
this.observer.disconnect();
|
|
59
|
+
this.observer = undefined;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
getObserver() {
|
|
63
|
+
if (!this.observer) {
|
|
64
|
+
this.observer = new IntersectionObserver(this.handleIntersections.bind(this), {
|
|
65
|
+
threshold: VISIBILITY_THRESHOLD,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
return this.observer;
|
|
69
|
+
}
|
|
70
|
+
handleIntersections(entries) {
|
|
71
|
+
for (const entry of entries) {
|
|
72
|
+
const el = entry.target;
|
|
73
|
+
const targetId = this.elementToTargetId.get(el);
|
|
74
|
+
if (!targetId) {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
if (entry.isIntersecting && entry.intersectionRatio >= VISIBILITY_THRESHOLD) {
|
|
78
|
+
if (!this.trackedIds.has(targetId)) {
|
|
79
|
+
this.trackedIds.add(targetId);
|
|
80
|
+
const cb = this.callbacks.get(targetId);
|
|
81
|
+
if (cb) {
|
|
82
|
+
try {
|
|
83
|
+
cb(targetId);
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
console.error('ViewportVisibilityTracker callback error:', err);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const set = this.targetToElements.get(targetId);
|
|
90
|
+
if (set) {
|
|
91
|
+
for (const node of set) {
|
|
92
|
+
this.getObserver().unobserve(node);
|
|
93
|
+
this.elementToTargetId.delete(node);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
this.targetToElements.delete(targetId);
|
|
97
|
+
this.callbacks.delete(targetId);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export { ViewportVisibilityTracker };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@streamscloud/streams-analytics-collector",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|
|
@@ -8,12 +8,11 @@
|
|
|
8
8
|
"exports": {
|
|
9
9
|
".": {
|
|
10
10
|
"import": "./dist/index.js",
|
|
11
|
-
"types": "./dist/index.d.ts"
|
|
12
|
-
"svelte": "./dist/index.js"
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
13
12
|
},
|
|
14
|
-
"./
|
|
15
|
-
"import": "./dist/
|
|
16
|
-
"types": "./dist/
|
|
13
|
+
"./reporting": {
|
|
14
|
+
"import": "./dist/reporting/index.js",
|
|
15
|
+
"types": "./dist/reporting/index.d.ts"
|
|
17
16
|
}
|
|
18
17
|
},
|
|
19
18
|
"files": [
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
var SiteActiveCampaignsQuery = "query SiteActiveCampaigns($filter: SiteActiveCampaignsFilterInput!, $pageQuery: PaginationOfAdCampaignsOrderByInput!) {\n siteActiveCampaigns(filter: $filter, pagination: $pageQuery) {\n total\n items {\n id\n name\n status\n adGroupIds\n adGroups {\n name\n ads {\n ..._adForGridPayloadFragment\n }\n adPlacement {\n name\n }\n }\n status\n updatedAt\n editorProfile {\n id\n name\n handle\n image\n }\n }\n }\n}\n\nfragment _adForGridPayloadFragment on Ad {\n id\n media {\n type\n url\n thumbnailUrl\n }\n name\n title\n description\n status\n updatedAt\n}\n";
|
|
2
|
-
|
|
3
|
-
export { SiteActiveCampaignsQuery as default };
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ViewportVisibilityTracker
|
|
3
|
-
*
|
|
4
|
-
* Utility for tracking when DOM elements (e.g., stream tiles) become visible in the viewport.
|
|
5
|
-
* Fires impression events only once per unique element (by streamId) per session/visit.
|
|
6
|
-
* Uses IntersectionObserver for efficient observation, with a fallback to manual visibility checks.
|
|
7
|
-
*
|
|
8
|
-
* Usage:
|
|
9
|
-
* ViewportVisibilityTracker.registerTile(element, streamId, callback?);
|
|
10
|
-
*
|
|
11
|
-
* - The element must have a unique streamId (e.g., from data-stream-id attribute).
|
|
12
|
-
* - The impression event is fired only once per streamId per session.
|
|
13
|
-
* - Handles rapid scrolling, re-renders, and element replacement.
|
|
14
|
-
* - You can provide a custom callback to be called when the element becomes visible.
|
|
15
|
-
*/
|
|
16
|
-
export declare class ViewportVisibilityTracker {
|
|
17
|
-
private static trackedStreamIds;
|
|
18
|
-
private static observedElements;
|
|
19
|
-
private static callbacks;
|
|
20
|
-
private static observer;
|
|
21
|
-
/**
|
|
22
|
-
* Register a tile element for visibility tracking.
|
|
23
|
-
* @param el The DOM element to observe
|
|
24
|
-
* @param streamId The unique stream ID for this tile
|
|
25
|
-
* @param callback Optional callback to call when the element becomes visible (defaults to AppEventsTracker.trackStreamTileImpression)
|
|
26
|
-
*/
|
|
27
|
-
static registerTile(el: HTMLElement, streamId: string, callback?: (streamId: string) => void): void;
|
|
28
|
-
/**
|
|
29
|
-
* Internal: Get or create the IntersectionObserver instance
|
|
30
|
-
*/
|
|
31
|
-
private static getObserver;
|
|
32
|
-
/**
|
|
33
|
-
* Internal: IntersectionObserver callback
|
|
34
|
-
*/
|
|
35
|
-
private static handleIntersections;
|
|
36
|
-
/**
|
|
37
|
-
* Fallback: Check if an element is in the viewport (at least 50% visible)
|
|
38
|
-
* @param el The DOM element
|
|
39
|
-
* @returns true if at least 50% of the element is visible
|
|
40
|
-
*/
|
|
41
|
-
static isElementInViewport(el: HTMLElement): boolean;
|
|
42
|
-
/**
|
|
43
|
-
* Reset tracked state (for testing or new session)
|
|
44
|
-
*/
|
|
45
|
-
static reset(): void;
|
|
46
|
-
}
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import { AppEventsTracker } from './app-events-tracker.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* ViewportVisibilityTracker
|
|
5
|
-
*
|
|
6
|
-
* Utility for tracking when DOM elements (e.g., stream tiles) become visible in the viewport.
|
|
7
|
-
* Fires impression events only once per unique element (by streamId) per session/visit.
|
|
8
|
-
* Uses IntersectionObserver for efficient observation, with a fallback to manual visibility checks.
|
|
9
|
-
*
|
|
10
|
-
* Usage:
|
|
11
|
-
* ViewportVisibilityTracker.registerTile(element, streamId, callback?);
|
|
12
|
-
*
|
|
13
|
-
* - The element must have a unique streamId (e.g., from data-stream-id attribute).
|
|
14
|
-
* - The impression event is fired only once per streamId per session.
|
|
15
|
-
* - Handles rapid scrolling, re-renders, and element replacement.
|
|
16
|
-
* - You can provide a custom callback to be called when the element becomes visible.
|
|
17
|
-
*/
|
|
18
|
-
class ViewportVisibilityTracker {
|
|
19
|
-
// Set of streamIds that have already been tracked for impression
|
|
20
|
-
static trackedStreamIds = new Set();
|
|
21
|
-
// Map from streamId to the currently observed element
|
|
22
|
-
static observedElements = new Map();
|
|
23
|
-
// Map from streamId to the callback to call when visible
|
|
24
|
-
static callbacks = new Map();
|
|
25
|
-
// Singleton IntersectionObserver instance
|
|
26
|
-
static observer = null;
|
|
27
|
-
/**
|
|
28
|
-
* Register a tile element for visibility tracking.
|
|
29
|
-
* @param el The DOM element to observe
|
|
30
|
-
* @param streamId The unique stream ID for this tile
|
|
31
|
-
* @param callback Optional callback to call when the element becomes visible (defaults to AppEventsTracker.trackStreamTileImpression)
|
|
32
|
-
*/
|
|
33
|
-
static registerTile(el, streamId, callback = AppEventsTracker.trackStreamTileImpression.bind(AppEventsTracker)) {
|
|
34
|
-
if (!el || !streamId)
|
|
35
|
-
return;
|
|
36
|
-
// If already tracked, do nothing
|
|
37
|
-
if (this.trackedStreamIds.has(streamId))
|
|
38
|
-
return;
|
|
39
|
-
// If an element for this streamId is already being observed, unobserve it (handles re-renders)
|
|
40
|
-
const prevEl = this.observedElements.get(streamId);
|
|
41
|
-
if (prevEl && prevEl !== el) {
|
|
42
|
-
this.getObserver().unobserve(prevEl);
|
|
43
|
-
}
|
|
44
|
-
this.observedElements.set(streamId, el);
|
|
45
|
-
this.callbacks.set(streamId, callback);
|
|
46
|
-
this.getObserver().observe(el);
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* Internal: Get or create the IntersectionObserver instance
|
|
50
|
-
*/
|
|
51
|
-
static getObserver() {
|
|
52
|
-
if (!this.observer) {
|
|
53
|
-
this.observer = new window.IntersectionObserver(this.handleIntersections.bind(this), {
|
|
54
|
-
threshold: 0.5, // At least 50% of the element must be visible
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
return this.observer;
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Internal: IntersectionObserver callback
|
|
61
|
-
*/
|
|
62
|
-
static handleIntersections(entries) {
|
|
63
|
-
for (const entry of entries) {
|
|
64
|
-
const el = entry.target;
|
|
65
|
-
// Try to get streamId from map or from data attribute
|
|
66
|
-
let streamId = Array.from(this.observedElements.entries()).find(([, v]) => v === el)?.[0];
|
|
67
|
-
if (!streamId) {
|
|
68
|
-
streamId = el.getAttribute("data-stream-id") || undefined;
|
|
69
|
-
}
|
|
70
|
-
if (!streamId)
|
|
71
|
-
continue;
|
|
72
|
-
if (entry.isIntersecting && entry.intersectionRatio >= 0.5) {
|
|
73
|
-
// Only fire once per streamId per session
|
|
74
|
-
if (!this.trackedStreamIds.has(streamId)) {
|
|
75
|
-
this.trackedStreamIds.add(streamId);
|
|
76
|
-
const callback = this.callbacks.get(streamId);
|
|
77
|
-
if (callback) {
|
|
78
|
-
callback(streamId);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
// Once tracked, stop observing this element
|
|
82
|
-
this.getObserver().unobserve(el);
|
|
83
|
-
this.observedElements.delete(streamId);
|
|
84
|
-
this.callbacks.delete(streamId);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* Fallback: Check if an element is in the viewport (at least 50% visible)
|
|
90
|
-
* @param el The DOM element
|
|
91
|
-
* @returns true if at least 50% of the element is visible
|
|
92
|
-
*/
|
|
93
|
-
static isElementInViewport(el) {
|
|
94
|
-
if (!el)
|
|
95
|
-
return false;
|
|
96
|
-
const rect = el.getBoundingClientRect();
|
|
97
|
-
const windowHeight = window.innerHeight || document.documentElement.clientHeight;
|
|
98
|
-
const windowWidth = window.innerWidth || document.documentElement.clientWidth;
|
|
99
|
-
const vertInView = rect.top <= windowHeight * 0.5 && rect.top + rect.height * 0.5 >= 0;
|
|
100
|
-
const horInView = rect.left <= windowWidth * 0.5 && rect.left + rect.width * 0.5 >= 0;
|
|
101
|
-
return vertInView && horInView;
|
|
102
|
-
}
|
|
103
|
-
/**
|
|
104
|
-
* Reset tracked state (for testing or new session)
|
|
105
|
-
*/
|
|
106
|
-
static reset() {
|
|
107
|
-
this.trackedStreamIds.clear();
|
|
108
|
-
this.observedElements.clear();
|
|
109
|
-
this.callbacks.clear();
|
|
110
|
-
if (this.observer) {
|
|
111
|
-
this.observer.disconnect();
|
|
112
|
-
this.observer = null;
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
export { ViewportVisibilityTracker };
|