Skip to content

Commit 582f0de

Browse files
zhu-xiaoweixiaoweii
andauthored
fix: change user engagement event and engagement time calculate rule (#13)
Co-authored-by: xiaoweii <xiaoweii@amazom.com>
1 parent 1d54f80 commit 582f0de

File tree

14 files changed

+216
-125
lines changed

14 files changed

+216
-125
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ ClickstreamAnalytics.configure({
113113
sendMode: EventMode.Batch,
114114
sendEventsInterval: 5000,
115115
isTrackPageViewEvents: true,
116+
isTrackUserEngagementEvents: true,
116117
isTrackClickEvents: true,
117118
isTrackSearchEvents: true,
118119
isTrackScrollEvents: true,
@@ -132,6 +133,7 @@ Here is an explanation of each property:
132133
- **sendMode**: EventMode.Immediate, EventMode.Batch, default is Immediate mode.
133134
- **sendEventsInterval**: event sending interval millisecond, works only bath send mode, the default value is `5000`
134135
- **isTrackPageViewEvents**: whether auto record page view events in browser, default is `true`
136+
- **isTrackUserEngagementEvents**: whether auto record user engagement events in browser, default is `true`
135137
- **isTrackClickEvents**: whether auto record link click events in browser, default is `true`
136138
- **isTrackSearchEvents**: whether auto record search result page events in browser, default is `true`
137139
- **isTrackScrollEvents**: whether auto record page scroll events in browser, default is `true`
@@ -152,6 +154,7 @@ ClickstreamAnalytics.updateConfigure({
152154
isLogEvents: true,
153155
authCookie: 'your auth cookie',
154156
isTrackPageViewEvents: false,
157+
isTrackUserEngagementEvents: false,
155158
isTrackClickEvents: false,
156159
isTrackScrollEvents: false,
157160
isTrackSearchEvents: false,

src/provider/AnalyticsEventBuilder.ts

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,17 @@ import {
2424
Item,
2525
UserAttribute,
2626
} from '../types';
27-
import { HashUtil } from '../util/HashUtil';
2827
import { StorageUtil } from '../util/StorageUtil';
2928

3029
const sdkVersion = config.sdkVersion;
3130

3231
export class AnalyticsEventBuilder {
33-
static async createEvent(
32+
static createEvent(
3433
context: ClickstreamContext,
3534
event: ClickstreamEvent,
3635
userAttributes: UserAttribute,
3736
session?: Session
38-
): Promise<AnalyticsEvent> {
37+
): AnalyticsEvent {
3938
const { browserInfo, configuration } = context;
4039
const attributes = this.getEventAttributesWithCheck(event.attributes);
4140
if (session !== undefined) {
@@ -56,8 +55,7 @@ export class AnalyticsEventBuilder {
5655
browserInfo.latestReferrerHost;
5756

5857
const items = this.getEventItemsWithCheck(event.items, attributes);
59-
60-
const analyticEvent = {
58+
return {
6159
hashCode: '',
6260
event_type: event.name,
6361
event_id: uuidV4(),
@@ -80,10 +78,6 @@ export class AnalyticsEventBuilder {
8078
user: userAttributes ?? {},
8179
attributes: attributes,
8280
};
83-
analyticEvent.hashCode = await HashUtil.getHashCode(
84-
JSON.stringify(analyticEvent)
85-
);
86-
return analyticEvent;
8781
}
8882

8983
static getEventAttributesWithCheck(

src/provider/ClickstreamProvider.ts

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { PageViewTracker, SessionTracker } from '../tracker';
2222
import { ClickTracker } from '../tracker/ClickTracker';
2323
import { ScrollTracker } from '../tracker/ScrollTracker';
2424
import {
25+
AnalyticsEvent,
2526
AnalyticsProvider,
2627
ClickstreamAttribute,
2728
ClickstreamConfiguration,
@@ -31,6 +32,7 @@ import {
3132
SendMode,
3233
UserAttribute,
3334
} from '../types';
35+
import { HashUtil } from '../util/HashUtil';
3436
import { StorageUtil } from '../util/StorageUtil';
3537

3638
const logger = new Logger('ClickstreamProvider');
@@ -52,6 +54,7 @@ export class ClickstreamProvider implements AnalyticsProvider {
5254
sendMode: SendMode.Immediate,
5355
sendEventsInterval: 5000,
5456
isTrackPageViewEvents: true,
57+
isTrackUserEngagementEvents: true,
5558
isTrackClickEvents: true,
5659
isTrackSearchEvents: true,
5760
isTrackScrollEvents: true,
@@ -118,17 +121,27 @@ export class ClickstreamProvider implements AnalyticsProvider {
118121
logger.error(result.error_message);
119122
return;
120123
}
121-
AnalyticsEventBuilder.createEvent(
124+
const resultEvent = this.createEvent(event);
125+
this.recordEvent(resultEvent, event.isImmediate);
126+
}
127+
128+
createEvent(event: ClickstreamEvent) {
129+
return AnalyticsEventBuilder.createEvent(
122130
this.context,
123131
event,
124132
this.userAttribute,
125133
this.sessionTracker.session
126-
)
127-
.then(resultEvent => {
128-
this.eventRecorder.record(resultEvent, event.isImmediate);
134+
);
135+
}
136+
137+
recordEvent(event: AnalyticsEvent, isImmediate = false) {
138+
HashUtil.getHashCode(JSON.stringify(event))
139+
.then(hashCode => {
140+
event.hashCode = hashCode;
141+
this.eventRecorder.record(event, isImmediate);
129142
})
130143
.catch(error => {
131-
logger.error(`Create event fail with ${error}`);
144+
logger.error(`Create hash code failed with ${error}`);
132145
});
133146
}
134147

src/provider/Event.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ export class Event {
5353
PAGE_REFERRER_TITLE: '_page_referrer_title',
5454
LATEST_REFERRER: '_latest_referrer',
5555
LATEST_REFERRER_HOST: '_latest_referrer_host',
56+
PREVIOUS_TIMESTAMP: '_previous_timestamp',
5657
ENTRANCES: '_entrances',
5758
SESSION_ID: '_session_id',
5859
SESSION_DURATION: '_session_duration',

src/tracker/PageViewTracker.ts

Lines changed: 80 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -26,26 +26,28 @@ export class PageViewTracker extends BaseTracker {
2626
context: ClickstreamContext;
2727
isEntrances = false;
2828
searchKeywords = Event.Constants.KEYWORDS;
29+
lastEngageTime = 0;
30+
lastScreenStartTimestamp = 0;
2931

3032
init() {
3133
const configuredSearchKeywords = this.provider.configuration.searchKeyWords;
3234
Object.assign(this.searchKeywords, configuredSearchKeywords);
33-
this.trackPageView = this.trackPageView.bind(this);
35+
this.onPageChange = this.onPageChange.bind(this);
3436
if (this.context.configuration.pageType === PageType.SPA) {
3537
this.trackPageViewForSPA();
3638
} else {
37-
this.trackPageView();
39+
this.onPageChange();
3840
}
3941
}
4042

4143
trackPageViewForSPA() {
42-
MethodEmbed.add(history, 'pushState', this.trackPageView);
43-
MethodEmbed.add(history, 'replaceState', this.trackPageView);
44-
window.addEventListener('popstate', this.trackPageView);
45-
this.trackPageView();
44+
MethodEmbed.add(history, 'pushState', this.onPageChange);
45+
MethodEmbed.add(history, 'replaceState', this.onPageChange);
46+
window.addEventListener('popstate', this.onPageChange);
47+
this.onPageChange();
4648
}
4749

48-
trackPageView() {
50+
onPageChange() {
4951
if (!window.sessionStorage) {
5052
logger.warn('unsupported web environment for sessionStorage');
5153
return;
@@ -55,41 +57,81 @@ export class PageViewTracker extends BaseTracker {
5557
const previousPageTitle = StorageUtil.getPreviousPageTitle();
5658
const currentPageUrl = BrowserInfo.getCurrentPageUrl();
5759
const currentPageTitle = BrowserInfo.getCurrentPageTitle();
58-
const previousPageStartTime = StorageUtil.getPreviousPageStartTime();
59-
let engagementTime = 0;
60-
this.isEntrances =
61-
this.provider.sessionTracker.session.isNewSession() &&
62-
previousPageUrl === '';
63-
const currentPageStartTime = new Date().getTime();
64-
if (previousPageStartTime > 0) {
65-
engagementTime = currentPageStartTime - previousPageStartTime;
66-
}
67-
if (previousPageUrl !== currentPageUrl) {
60+
if (
61+
previousPageUrl !== currentPageUrl ||
62+
previousPageTitle !== currentPageTitle
63+
) {
6864
this.provider.scrollTracker?.enterNewPage();
69-
const eventAttributes = {
70-
[Event.ReservedAttribute.PAGE_REFERRER]: previousPageUrl,
71-
[Event.ReservedAttribute.PAGE_REFERRER_TITLE]: previousPageTitle,
72-
[Event.ReservedAttribute.ENTRANCES]: this.isEntrances ? 1 : 0,
73-
};
74-
if (!this.isEntrances) {
75-
eventAttributes[Event.ReservedAttribute.ENGAGEMENT_TIMESTAMP] =
76-
engagementTime;
77-
}
78-
this.provider.record({
79-
name: Event.PresetEvent.PAGE_VIEW,
80-
attributes: eventAttributes,
81-
});
65+
this.recordUserEngagement();
66+
this.trackPageView(previousPageUrl, previousPageTitle);
67+
this.trackSearchEvents();
68+
8269
StorageUtil.savePreviousPageUrl(currentPageUrl);
8370
StorageUtil.savePreviousPageTitle(currentPageTitle);
84-
StorageUtil.savePreviousPageStartTime(currentPageStartTime);
85-
if (this.context.configuration.isTrackSearchEvents) {
86-
this.trackSearchEvents();
87-
}
8871
}
8972
}
9073
}
9174

75+
trackPageView(previousPageUrl: string, previousPageTitle: string) {
76+
const previousPageStartTime = StorageUtil.getPreviousPageStartTime();
77+
const analyticsEvent = this.provider.createEvent({
78+
name: Event.PresetEvent.PAGE_VIEW,
79+
});
80+
const currentPageStartTime = analyticsEvent.timestamp;
81+
82+
const eventAttributes = {
83+
[Event.ReservedAttribute.PAGE_REFERRER]: previousPageUrl,
84+
[Event.ReservedAttribute.PAGE_REFERRER_TITLE]: previousPageTitle,
85+
[Event.ReservedAttribute.ENTRANCES]: this.isEntrances ? 1 : 0,
86+
};
87+
if (previousPageStartTime > 0) {
88+
eventAttributes[Event.ReservedAttribute.PREVIOUS_TIMESTAMP] =
89+
previousPageStartTime;
90+
}
91+
if (this.lastEngageTime > 0) {
92+
eventAttributes[Event.ReservedAttribute.ENGAGEMENT_TIMESTAMP] =
93+
this.lastEngageTime;
94+
}
95+
Object.assign(analyticsEvent.attributes, eventAttributes);
96+
this.provider.recordEvent(analyticsEvent);
97+
98+
this.isEntrances = false;
99+
100+
StorageUtil.savePreviousPageStartTime(currentPageStartTime);
101+
this.lastScreenStartTimestamp = currentPageStartTime;
102+
}
103+
104+
setIsEntrances() {
105+
this.isEntrances = true;
106+
}
107+
108+
updateLastScreenStartTimestamp() {
109+
this.lastScreenStartTimestamp = new Date().getTime();
110+
}
111+
112+
recordUserEngagement(isImmediate = false) {
113+
if (this.lastScreenStartTimestamp === 0) return;
114+
this.lastEngageTime = this.getLastEngageTime();
115+
if (
116+
this.provider.configuration.isTrackUserEngagementEvents &&
117+
this.lastEngageTime > Constants.minEngagementTime
118+
) {
119+
this.provider.record({
120+
name: Event.PresetEvent.USER_ENGAGEMENT,
121+
attributes: {
122+
[Event.ReservedAttribute.ENGAGEMENT_TIMESTAMP]: this.lastEngageTime,
123+
},
124+
isImmediate: isImmediate,
125+
});
126+
}
127+
}
128+
129+
getLastEngageTime() {
130+
return new Date().getTime() - this.lastScreenStartTimestamp;
131+
}
132+
92133
trackSearchEvents() {
134+
if (!this.context.configuration.isTrackSearchEvents) return;
93135
const searchStr = window.location.search;
94136
if (!searchStr || searchStr.length === 0) return;
95137
const urlParams = new URLSearchParams(searchStr);
@@ -108,3 +150,7 @@ export class PageViewTracker extends BaseTracker {
108150
}
109151
}
110152
}
153+
154+
enum Constants {
155+
minEngagementTime = 1000,
156+
}

src/tracker/SessionTracker.ts

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ export class SessionTracker extends BaseTracker {
2323
hiddenStr: string;
2424
visibilityChange: string;
2525
session: Session;
26-
startEngageTimestamp: number;
2726
isWindowClosing = false;
2827

2928
init() {
@@ -63,9 +62,11 @@ export class SessionTracker extends BaseTracker {
6362

6463
onPageAppear(isFirstTime = false) {
6564
logger.debug('page appear');
66-
this.updateEngageTimestamp();
65+
const pageViewTracker = this.provider.pageViewTracker;
66+
pageViewTracker.updateLastScreenStartTimestamp();
6767
this.session = Session.getCurrentSession(this.context);
6868
if (this.session.isNewSession()) {
69+
pageViewTracker.setIsEntrances();
6970
this.provider.record({ name: Event.PresetEvent.SESSION_START });
7071
}
7172
this.provider.record({
@@ -86,16 +87,7 @@ export class SessionTracker extends BaseTracker {
8687
}
8788

8889
recordUserEngagement(isImmediate = false) {
89-
const engagementTime = new Date().getTime() - this.startEngageTimestamp;
90-
if (engagementTime > Constants.minEngagementTime) {
91-
this.provider.record({
92-
name: Event.PresetEvent.USER_ENGAGEMENT,
93-
attributes: {
94-
[Event.ReservedAttribute.ENGAGEMENT_TIMESTAMP]: engagementTime,
95-
},
96-
isImmediate: isImmediate,
97-
});
98-
}
90+
this.provider.pageViewTracker.recordUserEngagement(isImmediate);
9991
}
10092

10193
onBeforeUnload() {
@@ -108,10 +100,6 @@ export class SessionTracker extends BaseTracker {
108100
StorageUtil.saveSession(this.session);
109101
}
110102

111-
updateEngageTimestamp() {
112-
this.startEngageTimestamp = new Date().getTime();
113-
}
114-
115103
checkEnv(): boolean {
116104
if (!document || !document.addEventListener) {
117105
logger.debug('not in the supported web environment');
@@ -133,7 +121,3 @@ export class SessionTracker extends BaseTracker {
133121
return true;
134122
}
135123
}
136-
137-
enum Constants {
138-
minEngagementTime = 1000,
139-
}

src/types/Analytics.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export interface Configuration {
2626
isLogEvents?: boolean;
2727
authCookie?: string;
2828
isTrackPageViewEvents?: boolean;
29+
isTrackUserEngagementEvents?: boolean;
2930
isTrackClickEvents?: boolean;
3031
isTrackScrollEvents?: boolean;
3132
isTrackSearchEvents?: boolean;

src/util/StorageUtil.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ export class StorageUtil {
259259
}
260260

261261
static getPreviousPageStartTime(): number {
262-
const startTime = sessionStorage.getItem(
262+
const startTime = localStorage.getItem(
263263
StorageUtil.previousPageStartTimeKey
264264
);
265265
if (startTime === null) {
@@ -270,7 +270,7 @@ export class StorageUtil {
270270
}
271271

272272
static savePreviousPageStartTime(timestamp: number) {
273-
sessionStorage.setItem(
273+
localStorage.setItem(
274274
StorageUtil.previousPageStartTimeKey,
275275
timestamp.toString()
276276
);

0 commit comments

Comments
 (0)