@sesamy/sesamy-js 1.2.0 → 1.3.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.
- package/.env +2 -0
- package/.env.production +2 -0
- package/.eslintignore +4 -0
- package/.eslintrc +18 -0
- package/.github/workflows/release.yml +23 -0
- package/.nvmrc +1 -0
- package/.prettierignore +4 -0
- package/.prettierrc +5 -0
- package/CHANGELOG.md +132 -0
- package/article.html +65 -0
- package/contracts.html +23 -0
- package/dts-bundle-generator.config.ts +11 -0
- package/entitlements.html +23 -0
- package/index.html +24 -0
- package/package.json +14 -10
- package/public/sesamy.png +0 -0
- package/renovate.json +4 -0
- package/src/SesamyContracts.ts +86 -0
- package/src/SesamyEntitlements.ts +85 -0
- package/src/app.ts +101 -0
- package/src/constants.ts +2 -0
- package/src/controllers/index.ts +1 -0
- package/src/controllers/paywall.ts +14 -0
- package/src/entitlementsStyle.css +147 -0
- package/src/events/ready.ts +12 -0
- package/src/index.ts +36 -0
- package/src/javascript-api.ts +84 -0
- package/src/services/analytics/element-tracker.ts +117 -0
- package/src/services/analytics/index.ts +234 -0
- package/src/services/analytics/listeners/index.ts +2 -0
- package/src/services/analytics/listeners/route.ts +9 -0
- package/src/services/analytics/listeners/scroll.ts +62 -0
- package/src/services/analytics/session-id.ts +11 -0
- package/src/services/analytics/types/analytics-activity-utils.d.ts +54 -0
- package/src/services/analytics/types/analytics-router-utils.d.ts +7 -0
- package/src/services/analytics/types/analytics-scroll-utils.d.ts +4 -0
- package/src/services/analytics/types/track-event.ts +70 -0
- package/src/services/auth/index.ts +74 -0
- package/src/services/sesamy/index.ts +160 -0
- package/src/state.ts +3 -0
- package/src/style.css +99 -0
- package/src/types/Bills.ts +12 -0
- package/src/types/Config.ts +11 -0
- package/src/types/Contracts.ts +12 -0
- package/src/types/Entitlement.ts +9 -0
- package/src/types/Events.ts +6 -0
- package/src/types/Tag.ts +16 -0
- package/src/vite-env.d.ts +1 -0
- package/tsconfig.json +23 -0
- package/vite.config.ts +43 -0
- package/vite.dev.config.ts +14 -0
- package/dist/sesamy-js.es.js +0 -3169
- package/dist/sesamy-js.umd.js +0 -41
package/src/app.ts
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { User } from '@auth0/auth0-spa-js';
|
|
2
|
+
import './style.css';
|
|
3
|
+
import sesamyLogo from '/sesamy.png';
|
|
4
|
+
import { Events } from './types/Events';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* This class is only used for rendering the UI and not shipped in the bundle
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
class SesamyAppComponent extends HTMLElement {
|
|
11
|
+
private user: User | undefined;
|
|
12
|
+
|
|
13
|
+
constructor() {
|
|
14
|
+
super();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async _handleProfile() {
|
|
18
|
+
this.user = await window.sesamy.auth.getUser();
|
|
19
|
+
this.render();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async connectedCallback() {
|
|
23
|
+
this.setupEventListeners();
|
|
24
|
+
|
|
25
|
+
if (!window.sesamy) {
|
|
26
|
+
window.addEventListener(Events.READY, () => this._handleProfile());
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
this._handleProfile();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
render() {
|
|
34
|
+
this.innerHTML = `
|
|
35
|
+
<div>
|
|
36
|
+
<a href="https://docs.sesamy.com" target="_blank">
|
|
37
|
+
<img src="${sesamyLogo}" class="logo" alt="Vite logo" />
|
|
38
|
+
</a>
|
|
39
|
+
<h1>Sesamy JS</h1>
|
|
40
|
+
<p class="read-the-docs">
|
|
41
|
+
Click on the Sesamy logo to learn more
|
|
42
|
+
</p>
|
|
43
|
+
${this.user ? this.renderUserProfile() : this.renderLoginButton()}
|
|
44
|
+
</div>
|
|
45
|
+
`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
private renderLoginButton(): string {
|
|
49
|
+
return '<button part="login-btn" class="login auth-visible" id="login-btn"> Login </button>';
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
private renderUserProfile(): string {
|
|
53
|
+
return `<div class="user" part="user">
|
|
54
|
+
<a href="/profile" part="user-link">
|
|
55
|
+
${
|
|
56
|
+
this.user?.picture
|
|
57
|
+
? ` <img
|
|
58
|
+
class="big"
|
|
59
|
+
part="user-img img-big"
|
|
60
|
+
src=${this.user.picture}
|
|
61
|
+
alt=${this.user.name || 'User avatar'}
|
|
62
|
+
/>`
|
|
63
|
+
: `<div part="avatar avatar-big" class="avatar big"> Placeholder </div>`
|
|
64
|
+
}
|
|
65
|
+
<h2 class="user-name">${this.user?.name || 'User Name'}</h2>
|
|
66
|
+
</a>
|
|
67
|
+
|
|
68
|
+
<div class="logout-wrap">
|
|
69
|
+
<a href="#" class="logout-btn" id="logout-btn"> Logout </a>
|
|
70
|
+
</div>
|
|
71
|
+
<div>
|
|
72
|
+
<a href="/entitlements.html">
|
|
73
|
+
View Entitlements
|
|
74
|
+
</a>
|
|
75
|
+
</div>
|
|
76
|
+
<div>
|
|
77
|
+
<a href="/contracts.html">
|
|
78
|
+
View Contracts
|
|
79
|
+
</a>
|
|
80
|
+
</div>
|
|
81
|
+
</div>`;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
private setupEventListeners() {
|
|
85
|
+
this.addEventListener('click', event => {
|
|
86
|
+
const clickedElement = event.target as Element;
|
|
87
|
+
const logoutButton = clickedElement.closest('#logout-btn');
|
|
88
|
+
const loginButton = clickedElement.closest('#login-btn');
|
|
89
|
+
|
|
90
|
+
if (logoutButton) {
|
|
91
|
+
window.sesamy.auth.logout();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (loginButton) {
|
|
95
|
+
window.sesamy.auth.loginWithRedirect();
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
customElements.define('sesamy-app', SesamyAppComponent);
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
// Will be stuff in here later
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { triggerEvent } from '../events/ready';
|
|
2
|
+
import { accessArticle as accessArticleService } from '../services/sesamy';
|
|
3
|
+
import { Events } from '../types/Events';
|
|
4
|
+
|
|
5
|
+
export async function accessArticle(articleId: string) {
|
|
6
|
+
// Call the API to access the article
|
|
7
|
+
const response = await accessArticleService({ articleId });
|
|
8
|
+
|
|
9
|
+
if (response.status !== 'ok') {
|
|
10
|
+
triggerEvent(Events.SOFT_PAYWALL, {});
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return response;
|
|
14
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
.container {
|
|
2
|
+
background-color: var(--background, transparent);
|
|
3
|
+
font-family: var(--font-family, 'Helvetica');
|
|
4
|
+
}
|
|
5
|
+
ul {
|
|
6
|
+
list-style-type: none;
|
|
7
|
+
display: flex;
|
|
8
|
+
flex-wrap: wrap;
|
|
9
|
+
gap: var(--items-gap, 16px);
|
|
10
|
+
margin-top: 0;
|
|
11
|
+
padding: 0;
|
|
12
|
+
}
|
|
13
|
+
ul > * {
|
|
14
|
+
flex-grow: 1;
|
|
15
|
+
flex-shrink: 1;
|
|
16
|
+
flex-basis: var(--item-width, 300px);
|
|
17
|
+
}
|
|
18
|
+
button {
|
|
19
|
+
appearance: none;
|
|
20
|
+
display: inline-block;
|
|
21
|
+
text-decoration: none;
|
|
22
|
+
text-align: left;
|
|
23
|
+
background: none;
|
|
24
|
+
border: none;
|
|
25
|
+
width: 100%;
|
|
26
|
+
display: flex;
|
|
27
|
+
align-items: stretch;
|
|
28
|
+
text-decoration: none;
|
|
29
|
+
cursor: pointer;
|
|
30
|
+
}
|
|
31
|
+
button::-moz-focus-inner {
|
|
32
|
+
border: 0;
|
|
33
|
+
padding: 0;
|
|
34
|
+
}
|
|
35
|
+
button > * {
|
|
36
|
+
flex-shrink: 0;
|
|
37
|
+
}
|
|
38
|
+
.image-container {
|
|
39
|
+
padding-bottom: 14px;
|
|
40
|
+
}
|
|
41
|
+
img {
|
|
42
|
+
width: var(--image-size, 95px);
|
|
43
|
+
height: var(--image-size, 95px);
|
|
44
|
+
border-radius: var(--image-border-radius, 12px);
|
|
45
|
+
object-fit: cover;
|
|
46
|
+
overflow: hidden;
|
|
47
|
+
display: -webkit-box;
|
|
48
|
+
-webkit-line-clamp: 2;
|
|
49
|
+
-webkit-box-orient: vertical;
|
|
50
|
+
}
|
|
51
|
+
.details {
|
|
52
|
+
margin-left: var(--details-margin-left, 8px);
|
|
53
|
+
border-bottom-color: var(--divider-color, #70707023);
|
|
54
|
+
border-bottom-width: var(--divider-width, 1px);
|
|
55
|
+
padding-bottom: 14px;
|
|
56
|
+
border-bottom-style: solid;
|
|
57
|
+
display: flex;
|
|
58
|
+
flex-direction: column;
|
|
59
|
+
justify-content: var(--details-justify-content, space-between);
|
|
60
|
+
flex: 1;
|
|
61
|
+
}
|
|
62
|
+
p {
|
|
63
|
+
font-weight: normal;
|
|
64
|
+
margin: 0;
|
|
65
|
+
text-overflow: ellipsis;
|
|
66
|
+
overflow: hidden;
|
|
67
|
+
display: -webkit-box;
|
|
68
|
+
-webkit-box-orient: vertical;
|
|
69
|
+
}
|
|
70
|
+
.title {
|
|
71
|
+
color: var(--title-color, #131313);
|
|
72
|
+
font-size: 15px;
|
|
73
|
+
-webkit-line-clamp: 1;
|
|
74
|
+
}
|
|
75
|
+
.summary {
|
|
76
|
+
color: var(--summary-color, #22222260);
|
|
77
|
+
font-size: 12px;
|
|
78
|
+
-webkit-line-clamp: 2;
|
|
79
|
+
}
|
|
80
|
+
span {
|
|
81
|
+
overflow: hidden;
|
|
82
|
+
text-overflow: ellipsis;
|
|
83
|
+
display: -webkit-box;
|
|
84
|
+
-webkit-line-clamp: 2;
|
|
85
|
+
-webkit-box-orient: vertical;
|
|
86
|
+
}
|
|
87
|
+
span.type {
|
|
88
|
+
color: var(--type-color, #22222270);
|
|
89
|
+
text-transform: uppercase;
|
|
90
|
+
font-size: var(--type-font-size, 11px);
|
|
91
|
+
font-weight: medium;
|
|
92
|
+
margin-top: 4px;
|
|
93
|
+
font-family: var(--type-font-family, var(--font-family, 'Helvetica'));
|
|
94
|
+
}
|
|
95
|
+
span.date {
|
|
96
|
+
color: var(--date-color, #22222260);
|
|
97
|
+
font-size: var(--date-font-size, 12px);
|
|
98
|
+
font-weight: normal;
|
|
99
|
+
font-family: var(--date-font-family, var(--font-family, 'Helvetica'));
|
|
100
|
+
}
|
|
101
|
+
.modal {
|
|
102
|
+
display: none;
|
|
103
|
+
position: fixed;
|
|
104
|
+
z-index: var(--modal-zindex, 100);
|
|
105
|
+
padding-top: 20px;
|
|
106
|
+
left: 0;
|
|
107
|
+
top: 0;
|
|
108
|
+
width: 100%;
|
|
109
|
+
height: 100%;
|
|
110
|
+
overflow: auto;
|
|
111
|
+
}
|
|
112
|
+
.modal-blackout {
|
|
113
|
+
position: fixed;
|
|
114
|
+
top: 0;
|
|
115
|
+
left: 0;
|
|
116
|
+
width: 100%;
|
|
117
|
+
height: 100%;
|
|
118
|
+
background-color: rgba(0, 0, 0, 0.4);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.modal-container {
|
|
122
|
+
position: relative;
|
|
123
|
+
background-color: white;
|
|
124
|
+
margin: auto;
|
|
125
|
+
padding: 0;
|
|
126
|
+
max-width: 500px;
|
|
127
|
+
-webkit-animation-name: animatetop;
|
|
128
|
+
-webkit-animation-duration: 0.4s;
|
|
129
|
+
animation-name: animatetop;
|
|
130
|
+
animation-duration: 0.4s;
|
|
131
|
+
}
|
|
132
|
+
.close-container {
|
|
133
|
+
position: absolute;
|
|
134
|
+
right: 16px;
|
|
135
|
+
top: 16px;
|
|
136
|
+
cursor: pointer;
|
|
137
|
+
z-index: 900;
|
|
138
|
+
width: auto;
|
|
139
|
+
}
|
|
140
|
+
@media only screen and (max-width: 768px) {
|
|
141
|
+
.modal {
|
|
142
|
+
padding: 10px 0;
|
|
143
|
+
}
|
|
144
|
+
.modal-container {
|
|
145
|
+
max-width: 100%;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Events } from '../types/Events';
|
|
2
|
+
|
|
3
|
+
// TODO: Separate the event into a function per event so they can be typed
|
|
4
|
+
export function triggerEvent(eventType: Events, payload: any) {
|
|
5
|
+
const customEvent = new CustomEvent(eventType, {
|
|
6
|
+
detail: payload,
|
|
7
|
+
bubbles: true,
|
|
8
|
+
composed: true,
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
dispatchEvent(customEvent);
|
|
12
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { triggerEvent } from './events/ready';
|
|
2
|
+
import { registerAPI } from './javascript-api';
|
|
3
|
+
import { Config } from './types/Config';
|
|
4
|
+
import { init as initAuth } from './services/auth';
|
|
5
|
+
import { initAnalytics } from './services/analytics';
|
|
6
|
+
import { Events } from './types/Events';
|
|
7
|
+
|
|
8
|
+
export async function init(config: Config) {
|
|
9
|
+
initAnalytics({
|
|
10
|
+
clientId: config.clientId,
|
|
11
|
+
// The default client id can be overridden by the config
|
|
12
|
+
...config.analytics,
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
// Auth needs to be initated after the analytics so that the auth events can be tracked
|
|
16
|
+
await initAuth(config);
|
|
17
|
+
|
|
18
|
+
const api = registerAPI(config.namespace);
|
|
19
|
+
|
|
20
|
+
triggerEvent(Events.READY, {});
|
|
21
|
+
|
|
22
|
+
return api;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// This is for checking if the script is being loaded in the browser
|
|
26
|
+
if (typeof document !== 'undefined') {
|
|
27
|
+
const configElement = document.getElementById('sesamy-js');
|
|
28
|
+
if (configElement?.textContent) {
|
|
29
|
+
try {
|
|
30
|
+
const config = JSON.parse(configElement.textContent);
|
|
31
|
+
init(config);
|
|
32
|
+
} catch (err) {
|
|
33
|
+
console.error('Failed to parse config', err);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { loginWithRedirect, getUser, isAuthenticated, logout } from './services/auth';
|
|
2
|
+
import {
|
|
3
|
+
getEntitlement,
|
|
4
|
+
getEntitlements,
|
|
5
|
+
getContract,
|
|
6
|
+
getContracts,
|
|
7
|
+
getBill,
|
|
8
|
+
getBills,
|
|
9
|
+
getTags,
|
|
10
|
+
setTag,
|
|
11
|
+
pushTag,
|
|
12
|
+
} from './services/sesamy';
|
|
13
|
+
import { accessArticle } from './controllers/paywall';
|
|
14
|
+
import { version } from '../package.json';
|
|
15
|
+
|
|
16
|
+
function getVersion() {
|
|
17
|
+
return version;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface SesamyApi {
|
|
21
|
+
articles: {
|
|
22
|
+
access: typeof accessArticle;
|
|
23
|
+
};
|
|
24
|
+
auth: {
|
|
25
|
+
getUser: typeof getUser;
|
|
26
|
+
isAuthenticated: typeof isAuthenticated;
|
|
27
|
+
loginWithRedirect: typeof loginWithRedirect;
|
|
28
|
+
logout: typeof logout;
|
|
29
|
+
};
|
|
30
|
+
tags: {
|
|
31
|
+
list: typeof getTags;
|
|
32
|
+
set: typeof setTag;
|
|
33
|
+
push: typeof pushTag;
|
|
34
|
+
};
|
|
35
|
+
getEntitlement: typeof getEntitlement;
|
|
36
|
+
getEntitlements: typeof getEntitlements;
|
|
37
|
+
getContract: typeof getContract;
|
|
38
|
+
getContracts: typeof getContracts;
|
|
39
|
+
getBill: typeof getBill;
|
|
40
|
+
getBills: typeof getBills;
|
|
41
|
+
getVersion: typeof getVersion;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface SesamyWindow {
|
|
45
|
+
sesamy: SesamyApi;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Extend the global Window type with your SesamyWindow interface
|
|
49
|
+
declare global {
|
|
50
|
+
interface Window extends SesamyWindow {}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function registerAPI(namespace: string | null = 'sesamy') {
|
|
54
|
+
const api: SesamyApi = {
|
|
55
|
+
auth: {
|
|
56
|
+
getUser,
|
|
57
|
+
isAuthenticated,
|
|
58
|
+
loginWithRedirect,
|
|
59
|
+
logout,
|
|
60
|
+
},
|
|
61
|
+
articles: {
|
|
62
|
+
access: accessArticle,
|
|
63
|
+
},
|
|
64
|
+
tags: {
|
|
65
|
+
list: getTags,
|
|
66
|
+
set: setTag,
|
|
67
|
+
push: pushTag,
|
|
68
|
+
},
|
|
69
|
+
getEntitlement,
|
|
70
|
+
getEntitlements,
|
|
71
|
+
getContract,
|
|
72
|
+
getContracts,
|
|
73
|
+
getBill,
|
|
74
|
+
getBills,
|
|
75
|
+
getVersion,
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
if (namespace !== null) {
|
|
79
|
+
// This makes it possible to use a different namespace
|
|
80
|
+
(window as any)[namespace] = api;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return api;
|
|
84
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/// <reference path="./types/analytics-activity-utils.d.ts" />
|
|
2
|
+
import { onUserActivity } from '@analytics/activity-utils';
|
|
3
|
+
|
|
4
|
+
// IDLE_TIME is in ms
|
|
5
|
+
const timeout = 5000;
|
|
6
|
+
|
|
7
|
+
export interface ElementTrackerOptions {
|
|
8
|
+
element: HTMLElement;
|
|
9
|
+
viewCallback?: () => void;
|
|
10
|
+
activeDurationCallback?: (duration: number) => void;
|
|
11
|
+
idleDurationCallback?: (duration: number) => void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export default class ElementTracker {
|
|
15
|
+
element: HTMLElement;
|
|
16
|
+
|
|
17
|
+
isInViewport = false;
|
|
18
|
+
|
|
19
|
+
isAwake = false;
|
|
20
|
+
|
|
21
|
+
isFlushing?: boolean;
|
|
22
|
+
|
|
23
|
+
observer: IntersectionObserver;
|
|
24
|
+
|
|
25
|
+
lastEventAt = Date.now();
|
|
26
|
+
|
|
27
|
+
registeredView = false;
|
|
28
|
+
|
|
29
|
+
viewCallback?: () => void;
|
|
30
|
+
|
|
31
|
+
activeDurationCallback?: (duration: number, flushing?: boolean) => void;
|
|
32
|
+
|
|
33
|
+
idleDurationCallback?: (duration: number, flushing?: boolean) => void;
|
|
34
|
+
|
|
35
|
+
constructor(options: ElementTrackerOptions) {
|
|
36
|
+
this.element = options.element;
|
|
37
|
+
// We NEED to have callbacks here rather than using events as events will be lost when the browser is unloading
|
|
38
|
+
this.viewCallback = options.viewCallback;
|
|
39
|
+
this.activeDurationCallback = options.activeDurationCallback;
|
|
40
|
+
this.idleDurationCallback = options.idleDurationCallback;
|
|
41
|
+
|
|
42
|
+
// Setup the interaction observer
|
|
43
|
+
this.observer = new IntersectionObserver(
|
|
44
|
+
entries => {
|
|
45
|
+
// I guess there will only be one entry? Verify?
|
|
46
|
+
entries.forEach(entry => {
|
|
47
|
+
this.handleInViewPort(entry.isIntersecting);
|
|
48
|
+
});
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
threshold: 0,
|
|
52
|
+
},
|
|
53
|
+
);
|
|
54
|
+
this.observer.observe(this.element);
|
|
55
|
+
|
|
56
|
+
// Setup the active/idle
|
|
57
|
+
onUserActivity({
|
|
58
|
+
onIdle: (elapsedTime: number) => this.handleAwake(false, elapsedTime),
|
|
59
|
+
onWakeUp: (elapsedTime: number) => this.handleAwake(true, elapsedTime),
|
|
60
|
+
timeout,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Flush the awake timer in case the page is unloading
|
|
66
|
+
*/
|
|
67
|
+
flush() {
|
|
68
|
+
this.isFlushing = true;
|
|
69
|
+
this.handleAwake(!this.isAwake, Math.round((Date.now() - this.lastEventAt) / 1000));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
handleInViewPort(isInViewport: boolean) {
|
|
73
|
+
if (isInViewport) {
|
|
74
|
+
// A view envent only occurs if the element is active.
|
|
75
|
+
this.isAwake = true;
|
|
76
|
+
this.trackInViewport();
|
|
77
|
+
} else {
|
|
78
|
+
// An article outside the viewport is not active
|
|
79
|
+
this.handleAwake(false, Math.round((Date.now() - this.lastEventAt) / 1000));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
this.isInViewport = isInViewport;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
handleAwake(isAwake: boolean, elapsedTime: number) {
|
|
86
|
+
// Store the current awake state
|
|
87
|
+
this.isAwake = isAwake;
|
|
88
|
+
this.lastEventAt = isAwake ? Date.now() - elapsedTime * timeout : Date.now();
|
|
89
|
+
|
|
90
|
+
if (!this.isInViewport) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
this.trackAwake(isAwake, elapsedTime);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
trackAwake(awake: boolean, elapsedTime: number) {
|
|
97
|
+
if (!awake && this.activeDurationCallback) {
|
|
98
|
+
this.activeDurationCallback(elapsedTime, this.isFlushing);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (awake && this.idleDurationCallback) {
|
|
102
|
+
this.idleDurationCallback(elapsedTime, this.isFlushing);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
trackInViewport() {
|
|
107
|
+
if (this.registeredView) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
this.registeredView = true;
|
|
112
|
+
|
|
113
|
+
if (this.viewCallback) {
|
|
114
|
+
this.viewCallback();
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|