@internetarchive/ia-book-actions 0.1.5 → 0.1.6-a1
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/demo/index.html +5 -5
- package/package.json +1 -1
- package/src/components/collapsible-action-group.js +30 -24
- package/src/components/timer-countdown.js +2 -4
- package/src/core/config/analytics-event-and-category.js +2 -1
- package/src/core/config/ia-lending-intervals.js +12 -0
- package/src/core/services/actions-config.js +1 -0
- package/src/core/services/actions-handler/actions-handler.js +57 -65
- package/src/core/services/doc-cookies.js +23 -1
- package/src/core/services/get-lending-actions.js +3 -2
- package/src/core/services/loan-analytics.js +163 -0
- package/src/core/services/loan-token-poller.js +13 -36
- package/src/ia-book-actions.js +49 -17
- package/test/components/collapsible-action-group.test.js +0 -2
- package/test/components/timer-countdown.test.js +14 -14
- package/test/core/services/get-lending-actions.test.js +1 -1
package/demo/index.html
CHANGED
|
@@ -128,14 +128,14 @@
|
|
|
128
128
|
iaBookActions.modalConfig = modalConfig;
|
|
129
129
|
iaBookActions.lendingBarPostInit = lendingBarPostInit;
|
|
130
130
|
iaBookActions.tokenDelay = 2; // 2 seconds
|
|
131
|
-
iaBookActions.returnUrl = '
|
|
131
|
+
iaBookActions.returnUrl = '';
|
|
132
132
|
iaBookActions.localCache = localCache;
|
|
133
133
|
|
|
134
|
-
let loanTotalTime2Min = { loanTotalTime:
|
|
135
|
-
let loanTotalTime5Min = { loanTotalTime: 300, loanRenewAtLast:
|
|
136
|
-
let loanTotalTime1Hour = { loanTotalTime: 3600, loanRenewAtLast:
|
|
134
|
+
let loanTotalTime2Min = { loanTotalTime: 60, loanRenewAtLast: 51, pageChangedInLast: 15 };
|
|
135
|
+
let loanTotalTime5Min = { loanTotalTime: 300, loanRenewAtLast: 180, pageChangedInLast: 15 };
|
|
136
|
+
let loanTotalTime1Hour = { loanTotalTime: 3600, loanRenewAtLast: 660, pageChangedInLast: 900 };
|
|
137
137
|
|
|
138
|
-
iaBookActions.loanRenewTimeConfig =
|
|
138
|
+
iaBookActions.loanRenewTimeConfig = loanTotalTime2Min;
|
|
139
139
|
|
|
140
140
|
let lendingStatus1 = iaBookActions.lendingStatus;
|
|
141
141
|
let secondsLeftOnLoan = iaBookActions.loanRenewTimeConfig.loanTotalTime;
|
package/package.json
CHANGED
|
@@ -9,10 +9,6 @@ import CollapsibleActionGroupStyle from '../assets/styles/collapsible-action-gro
|
|
|
9
9
|
|
|
10
10
|
import { tabletContainerWidth } from '../core/config/constants.js';
|
|
11
11
|
import { purchaseIcon } from '../assets/data/purchase.js';
|
|
12
|
-
import {
|
|
13
|
-
analyticsCategories,
|
|
14
|
-
analyticsActions,
|
|
15
|
-
} from '../core/config/analytics-event-and-category.js';
|
|
16
12
|
import {
|
|
17
13
|
dropdownOpened,
|
|
18
14
|
dropdownClosed,
|
|
@@ -32,7 +28,8 @@ export class CollapsibleActionGroup extends ActionsHandler {
|
|
|
32
28
|
dropdownArrow: { type: String },
|
|
33
29
|
disabled: { type: Boolean },
|
|
34
30
|
returnUrl: { type: String },
|
|
35
|
-
|
|
31
|
+
autoRenew: { type: Boolean },
|
|
32
|
+
autoReturn: { type: Boolean },
|
|
36
33
|
};
|
|
37
34
|
}
|
|
38
35
|
|
|
@@ -52,7 +49,8 @@ export class CollapsibleActionGroup extends ActionsHandler {
|
|
|
52
49
|
this.loaderIcon = 'https://archive.org/upload/images/tree/loading.gif';
|
|
53
50
|
this.disabled = false;
|
|
54
51
|
this.returnUrl = '';
|
|
55
|
-
this.
|
|
52
|
+
this.autoRenew = false;
|
|
53
|
+
this.autoReturn = false;
|
|
56
54
|
}
|
|
57
55
|
|
|
58
56
|
updated(changed) {
|
|
@@ -63,24 +61,27 @@ export class CollapsibleActionGroup extends ActionsHandler {
|
|
|
63
61
|
this.resetActions();
|
|
64
62
|
}
|
|
65
63
|
|
|
66
|
-
if (changed.has('
|
|
67
|
-
this.
|
|
64
|
+
if (changed.has('autoRenew') && this.autoRenew === true) {
|
|
65
|
+
this.dispatchLoanEvent('autoRenew');
|
|
68
66
|
}
|
|
69
|
-
}
|
|
70
67
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
action: analyticsActions.browseLoanRenew,
|
|
76
|
-
};
|
|
68
|
+
if (changed.has('autoReturn') && this.autoReturn === true) {
|
|
69
|
+
this.dispatchLoanEvent('autoReturn');
|
|
70
|
+
}
|
|
71
|
+
}
|
|
77
72
|
|
|
78
|
-
|
|
73
|
+
/**
|
|
74
|
+
* dispatch event when book is auto renewed / returned
|
|
75
|
+
* listen these events in action-handler.js to execute ajax call on petabox.
|
|
76
|
+
* @see ActionsHandler
|
|
77
|
+
*
|
|
78
|
+
* @param {string} event - autoRenew|autoReturn
|
|
79
|
+
* @memberof CollapsibleActionGroup
|
|
80
|
+
*/
|
|
81
|
+
dispatchLoanEvent(event) {
|
|
79
82
|
this.dispatchEvent(
|
|
80
|
-
new CustomEvent(
|
|
81
|
-
detail: {
|
|
82
|
-
event: { category, action },
|
|
83
|
-
},
|
|
83
|
+
new CustomEvent(event, {
|
|
84
|
+
detail: {},
|
|
84
85
|
})
|
|
85
86
|
);
|
|
86
87
|
}
|
|
@@ -190,7 +191,11 @@ export class CollapsibleActionGroup extends ActionsHandler {
|
|
|
190
191
|
href="${action.url}"
|
|
191
192
|
target=${action.target}
|
|
192
193
|
@click=${() => {
|
|
193
|
-
this.clickHandler(
|
|
194
|
+
this.clickHandler(
|
|
195
|
+
action.id,
|
|
196
|
+
action.analyticsEvent,
|
|
197
|
+
action?.borrowType
|
|
198
|
+
);
|
|
194
199
|
}}
|
|
195
200
|
>
|
|
196
201
|
${action.id === 'purchaseBook' ? purchaseIcon : ''} ${action.text}
|
|
@@ -211,7 +216,7 @@ export class CollapsibleActionGroup extends ActionsHandler {
|
|
|
211
216
|
return html`<button
|
|
212
217
|
class="ia-button ${action.className} ${initialButton ? 'initial' : ''}"
|
|
213
218
|
@click=${() => {
|
|
214
|
-
this.clickHandler(action.id, analyticsEvent);
|
|
219
|
+
this.clickHandler(action.id, analyticsEvent, action?.borrowType);
|
|
215
220
|
}}
|
|
216
221
|
>
|
|
217
222
|
${action.text}
|
|
@@ -224,10 +229,10 @@ export class CollapsibleActionGroup extends ActionsHandler {
|
|
|
224
229
|
* @param { object } gaEvent - contains analytics event action and category
|
|
225
230
|
* @param { string } gaEvent.category
|
|
226
231
|
* @param { string } gaEvent.action
|
|
227
|
-
*
|
|
232
|
+
* @param { string } borrowType browse|borrow
|
|
228
233
|
* @fires CollapsibleActionGroup#{eventName} - (will be browseBook, borrowBook etc...)
|
|
229
234
|
*/
|
|
230
|
-
clickHandler(eventName, gaEvent) {
|
|
235
|
+
clickHandler(eventName, gaEvent, borrowType = '') {
|
|
231
236
|
this.dropdownState = 'close';
|
|
232
237
|
this.dropdownArrow = dropdownClosed;
|
|
233
238
|
|
|
@@ -237,6 +242,7 @@ export class CollapsibleActionGroup extends ActionsHandler {
|
|
|
237
242
|
new CustomEvent(eventName, {
|
|
238
243
|
detail: {
|
|
239
244
|
event: { category, action },
|
|
245
|
+
borrowType,
|
|
240
246
|
},
|
|
241
247
|
})
|
|
242
248
|
);
|
|
@@ -41,7 +41,7 @@ export default class TimerCountdown extends LitElement {
|
|
|
41
41
|
* delay seconds in setInterval function
|
|
42
42
|
* @type {number}
|
|
43
43
|
*/
|
|
44
|
-
this.timerExecutionSeconds =
|
|
44
|
+
this.timerExecutionSeconds = 60;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
disconnectedCallback() {
|
|
@@ -61,8 +61,6 @@ export default class TimerCountdown extends LitElement {
|
|
|
61
61
|
timerCountdown() {
|
|
62
62
|
this.disconnectedCallback();
|
|
63
63
|
|
|
64
|
-
// - let just execute setInterval in every 1 minute
|
|
65
|
-
this.timerExecutionSeconds = 60;
|
|
66
64
|
let secondsLeft = this.secondsLeftOnLoan;
|
|
67
65
|
|
|
68
66
|
// store current time
|
|
@@ -76,7 +74,7 @@ export default class TimerCountdown extends LitElement {
|
|
|
76
74
|
secondsLeft -= this.timerExecutionSeconds;
|
|
77
75
|
secondsLeft = Math.round(secondsLeft); // round number
|
|
78
76
|
|
|
79
|
-
// re-sync timer
|
|
77
|
+
// re-sync timer if gone off because of background window
|
|
80
78
|
await this.reSyncTimerIfGoneOff(secondsLeft);
|
|
81
79
|
|
|
82
80
|
/**
|
|
@@ -13,7 +13,8 @@ export const analyticsCategories = {
|
|
|
13
13
|
export const analyticsActions = {
|
|
14
14
|
browse: 'Borrow-1Hour',
|
|
15
15
|
browseAgain: 'Borrow-Again',
|
|
16
|
-
|
|
16
|
+
browseAutoRenew: 'AutoRenewBook',
|
|
17
|
+
browseAutoReturn: 'AutoReturnBook',
|
|
17
18
|
borrow: 'Borrow-14Days',
|
|
18
19
|
waitlistJoin: 'JoinWaitlist',
|
|
19
20
|
waitlistLeave: 'LeaveWaitlist',
|
|
@@ -11,6 +11,18 @@
|
|
|
11
11
|
* @type {Object}
|
|
12
12
|
*/
|
|
13
13
|
window.IALendingIntervals = {
|
|
14
|
+
/**
|
|
15
|
+
* store loan token poller interval
|
|
16
|
+
* @see LoanTokenPoller::bookAccessed
|
|
17
|
+
*/
|
|
18
|
+
tokenPoller: 0,
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* store timer-countdown interval
|
|
22
|
+
* @see TimerCountdown::timerCountdown
|
|
23
|
+
*/
|
|
24
|
+
timerCountdown: 0,
|
|
25
|
+
|
|
14
26
|
/**
|
|
15
27
|
* clear interval for create_token api
|
|
16
28
|
*/
|
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
import { LitElement } from 'lit';
|
|
2
2
|
|
|
3
3
|
import { URLHelper } from '../../config/url-helper.js';
|
|
4
|
-
import { sentryLogs } from '../../config/sentry-events.js';
|
|
5
4
|
|
|
6
5
|
import ActionsHandlerService from './actions-handler-service.js';
|
|
6
|
+
import LoanAnanlytics from '../loan-analytics.js';
|
|
7
7
|
import * as Cookies from '../doc-cookies.js';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* These are callback functions calling from actions-config.js file.
|
|
11
11
|
*
|
|
12
|
-
* ActionsHandlerService is a function being used to execute
|
|
13
|
-
*
|
|
14
|
-
* 1. identifier: book name.
|
|
12
|
+
* ActionsHandlerService is a function being used to execute APIs based of the request made by user.
|
|
13
|
+
*
|
|
15
14
|
*/
|
|
16
15
|
|
|
17
16
|
export default class ActionsHandler extends LitElement {
|
|
@@ -24,86 +23,97 @@ export default class ActionsHandler extends LitElement {
|
|
|
24
23
|
*/
|
|
25
24
|
this.waitUntillBorrowComplete = 6; // in seconds
|
|
26
25
|
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
/**
|
|
27
|
+
* bind events for lending bar
|
|
28
|
+
* @type {object}
|
|
29
|
+
*/
|
|
30
|
+
this.loanAnanlytics = new LoanAnanlytics();
|
|
29
31
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
);
|
|
32
|
+
/**
|
|
33
|
+
* bind events for lending bar
|
|
34
|
+
* @type {Function}
|
|
35
|
+
*/
|
|
36
|
+
this.bindEvents();
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
bindEvents() {
|
|
39
|
-
this.addEventListener('browseBook', (
|
|
40
|
+
this.addEventListener('browseBook', () => {
|
|
40
41
|
this.handleBrowseIt();
|
|
41
|
-
this.
|
|
42
|
-
const { category, action } = detail.event;
|
|
43
|
-
this.sendEvent(category, action);
|
|
42
|
+
this.loanAnanlytics?.storeLoanStatsCount(this.identifier, 'browse');
|
|
44
43
|
});
|
|
45
44
|
|
|
46
|
-
this.addEventListener('browseBookAgain', (
|
|
45
|
+
this.addEventListener('browseBookAgain', () => {
|
|
47
46
|
this.handleBrowseIt();
|
|
48
|
-
this.
|
|
49
|
-
const { category, action } = detail.event;
|
|
50
|
-
this.sendEvent(category, action);
|
|
47
|
+
this.loanAnanlytics?.storeLoanStatsCount(this.identifier, 'browseagain');
|
|
51
48
|
});
|
|
52
49
|
|
|
53
|
-
this.addEventListener('
|
|
54
|
-
this.
|
|
55
|
-
|
|
56
|
-
this.sendEvent(category, action);
|
|
50
|
+
this.addEventListener('autoRenew', () => {
|
|
51
|
+
this.handleLoanRenewNow();
|
|
52
|
+
this.loanAnanlytics?.storeLoanStatsCount(this.identifier, 'autorenew');
|
|
57
53
|
});
|
|
58
54
|
|
|
59
|
-
this.addEventListener('
|
|
55
|
+
this.addEventListener('autoReturn', () => {
|
|
60
56
|
this.handleReturnIt();
|
|
61
|
-
|
|
62
|
-
|
|
57
|
+
this.loanAnanlytics?.storeLoanStatsCount(this.identifier, 'autoreturn');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
this.addEventListener('returnNow', ({ detail }) => {
|
|
61
|
+
// use loan stats count when 1-hour borrow is active
|
|
62
|
+
if (detail?.borrowType === 'browse') {
|
|
63
|
+
this.loanAnanlytics?.storeLoanStatsCount(this.identifier, 'return');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
this.handleReturnIt('returnNow');
|
|
67
|
+
|
|
68
|
+
// send these events if 14-day borrow return
|
|
69
|
+
if (detail?.borrowType === 'borrow') {
|
|
70
|
+
const { category, action } = detail.event;
|
|
71
|
+
this.loanAnanlytics?.sendEvent(category, action, this.identifier);
|
|
72
|
+
}
|
|
63
73
|
});
|
|
64
74
|
|
|
65
75
|
this.addEventListener('borrowBook', ({ detail }) => {
|
|
66
76
|
this.handleBorrowIt();
|
|
67
77
|
const { category, action } = detail.event;
|
|
68
|
-
this.sendEvent(category, action);
|
|
78
|
+
this.loanAnanlytics?.sendEvent(category, action, this.identifier);
|
|
69
79
|
});
|
|
70
80
|
|
|
71
81
|
this.addEventListener('loginAndBorrow', ({ detail }) => {
|
|
72
82
|
this.handleLoginOk();
|
|
73
83
|
const { category, action } = detail.event;
|
|
74
|
-
this.sendEvent(category, action);
|
|
84
|
+
this.loanAnanlytics?.sendEvent(category, action, this.identifier);
|
|
75
85
|
});
|
|
76
86
|
|
|
77
87
|
this.addEventListener('leaveWaitlist', ({ detail }) => {
|
|
78
88
|
this.handleRemoveFromWaitingList();
|
|
79
89
|
const { category, action } = detail.event;
|
|
80
|
-
this.sendEvent(category, action);
|
|
90
|
+
this.loanAnanlytics?.sendEvent(category, action, this.identifier);
|
|
81
91
|
});
|
|
82
92
|
|
|
83
93
|
this.addEventListener('joinWaitlist', ({ detail }) => {
|
|
84
94
|
this.handleReserveIt();
|
|
85
95
|
const { category, action } = detail.event;
|
|
86
|
-
this.sendEvent(category, action);
|
|
96
|
+
this.loanAnanlytics?.sendEvent(category, action, this.identifier);
|
|
87
97
|
});
|
|
88
98
|
|
|
89
99
|
this.addEventListener('purchaseBook', ({ detail }) => {
|
|
90
100
|
const { category, action } = detail.event;
|
|
91
|
-
this.sendEvent(category, action);
|
|
101
|
+
this.loanAnanlytics?.sendEvent(category, action, this.identifier);
|
|
92
102
|
});
|
|
93
103
|
|
|
94
104
|
this.addEventListener('adminAccess', ({ detail }) => {
|
|
95
105
|
const { category, action } = detail.event;
|
|
96
|
-
this.sendEvent(category, action);
|
|
106
|
+
this.loanAnanlytics?.sendEvent(category, action, this.identifier);
|
|
97
107
|
});
|
|
98
108
|
|
|
99
109
|
this.addEventListener('exitAdminAccess', ({ detail }) => {
|
|
100
110
|
const { category, action } = detail.event;
|
|
101
|
-
this.sendEvent(category, action);
|
|
111
|
+
this.loanAnanlytics?.sendEvent(category, action, this.identifier);
|
|
102
112
|
});
|
|
103
113
|
|
|
104
114
|
this.addEventListener('bookTitleBar', ({ detail }) => {
|
|
105
115
|
const { category, action } = detail.event;
|
|
106
|
-
this.sendEvent(category, action);
|
|
116
|
+
this.loanAnanlytics?.sendEvent(category, action, this.identifier);
|
|
107
117
|
});
|
|
108
118
|
}
|
|
109
119
|
|
|
@@ -124,7 +134,7 @@ export default class ActionsHandler extends LitElement {
|
|
|
124
134
|
});
|
|
125
135
|
}
|
|
126
136
|
|
|
127
|
-
|
|
137
|
+
handleLoanRenewNow() {
|
|
128
138
|
const action = 'renew_loan';
|
|
129
139
|
|
|
130
140
|
ActionsHandlerService({
|
|
@@ -144,16 +154,23 @@ export default class ActionsHandler extends LitElement {
|
|
|
144
154
|
});
|
|
145
155
|
}
|
|
146
156
|
|
|
147
|
-
|
|
157
|
+
/**
|
|
158
|
+
* excute function when loan is returning
|
|
159
|
+
*
|
|
160
|
+
* @param {string} type - loan return type returnNow|''
|
|
161
|
+
*/
|
|
162
|
+
handleReturnIt(type = '') {
|
|
148
163
|
const action = 'return_loan';
|
|
149
|
-
|
|
164
|
+
|
|
165
|
+
if (type === 'returnNow') this.dispatchToggleActionGroup();
|
|
150
166
|
|
|
151
167
|
ActionsHandlerService({
|
|
152
168
|
action,
|
|
153
169
|
identifier: this.identifier,
|
|
154
170
|
success: () => {
|
|
155
171
|
this.deleteLoanCookies();
|
|
156
|
-
|
|
172
|
+
|
|
173
|
+
if (type === 'returnNow') URLHelper.goToUrl(this.returnUrl, true);
|
|
157
174
|
},
|
|
158
175
|
error: data => {
|
|
159
176
|
this.dispatchActionError(action, data);
|
|
@@ -220,7 +237,7 @@ export default class ActionsHandler extends LitElement {
|
|
|
220
237
|
*/
|
|
221
238
|
dispatchActionError(action, data = {}) {
|
|
222
239
|
// send LendingServiceError to GA
|
|
223
|
-
this.sendEvent('LendingServiceError', action);
|
|
240
|
+
this.loanAnanlytics?.sendEvent('LendingServiceError', action);
|
|
224
241
|
|
|
225
242
|
this.dispatchEvent(
|
|
226
243
|
new CustomEvent('lendingActionError', {
|
|
@@ -271,31 +288,6 @@ export default class ActionsHandler extends LitElement {
|
|
|
271
288
|
}, this.waitUntillBorrowComplete * 1000);
|
|
272
289
|
}
|
|
273
290
|
|
|
274
|
-
// save consecutive loan count for borrow
|
|
275
|
-
async setConsecutiveLoanCounts(action = '') {
|
|
276
|
-
try {
|
|
277
|
-
let newCount = 1;
|
|
278
|
-
const storageKey = `loan-count-${this.identifier}`;
|
|
279
|
-
const existingCount = Cookies.getItem(storageKey);
|
|
280
|
-
|
|
281
|
-
// increase browse-count by 1 when you consecutive reading a book.
|
|
282
|
-
if (action === 'browseAgain' && existingCount !== undefined) {
|
|
283
|
-
newCount = existingCount ? Number(existingCount) + 1 : 1;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
const date = new Date();
|
|
287
|
-
date.setHours(date.getHours() + 2); // 2 hours
|
|
288
|
-
|
|
289
|
-
// set new value
|
|
290
|
-
Cookies.setItem(storageKey, newCount, date, '/');
|
|
291
|
-
} catch (error) {
|
|
292
|
-
window?.Sentry?.captureException(
|
|
293
|
-
`${sentryLogs.setConsecutiveLoanCounts} - Error: ${error}`
|
|
294
|
-
);
|
|
295
|
-
this.sendEvent('Cookies-Error-Actions', error);
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
|
|
299
291
|
/**
|
|
300
292
|
* set browse time in indexedDB
|
|
301
293
|
*/
|
|
@@ -62,7 +62,7 @@ export function setItem(sKey, sValue, vEnd, sPath, sDomain, bSecure) {
|
|
|
62
62
|
|
|
63
63
|
/**
|
|
64
64
|
* BROKEN Remove specific key's value from cookie
|
|
65
|
-
*
|
|
65
|
+
*
|
|
66
66
|
* @param {string} sKey cookie name
|
|
67
67
|
* @param {string} [sPath] path of current item
|
|
68
68
|
* @param {string} [sDomain]
|
|
@@ -81,3 +81,25 @@ export function removeItem(sKey, sPath, sDomain) {
|
|
|
81
81
|
|
|
82
82
|
return true;
|
|
83
83
|
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* determine if a specific cookie is exist
|
|
87
|
+
*
|
|
88
|
+
* @param {string} sKey cookie name
|
|
89
|
+
*
|
|
90
|
+
* @returns {boolean}
|
|
91
|
+
*/
|
|
92
|
+
export function hasItem(sKey) {
|
|
93
|
+
const reCNameAllowed = /^(?:expires|max\-age|path|domain|secure|samesite|httponly)$/i;
|
|
94
|
+
|
|
95
|
+
// eslint-disable-next-line
|
|
96
|
+
if (!sKey || reCNameAllowed.test(sKey)) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return new RegExp(
|
|
101
|
+
'(?:^|;\\s*)' +
|
|
102
|
+
encodeURIComponent(sKey).replace(/[\-\.\+\*]/g, '\\$&') +
|
|
103
|
+
'\\s*\\='
|
|
104
|
+
).test(document.cookie);
|
|
105
|
+
}
|
|
@@ -40,7 +40,7 @@ import ActionsConfig from './actions-config.js';
|
|
|
40
40
|
* @enum {string}
|
|
41
41
|
*/
|
|
42
42
|
export const bookTitles = {
|
|
43
|
-
available_1hr: '',
|
|
43
|
+
available_1hr: 'Renews each hour as long as pages are turned.',
|
|
44
44
|
available_14d: 'This book can be borrowed for 14 days.',
|
|
45
45
|
available_pd: 'Book available to patrons with print disabilities.',
|
|
46
46
|
available_waitlist: 'A waitlist is available.',
|
|
@@ -98,7 +98,8 @@ export class GetLendingActions {
|
|
|
98
98
|
let isBrowsing =
|
|
99
99
|
lendingStatus.user_has_browsed && !lendingStatus.browsingExpired;
|
|
100
100
|
if (isBrowsing) {
|
|
101
|
-
primaryTitleText =
|
|
101
|
+
primaryTitleText =
|
|
102
|
+
'Returns automatically after 25 minutes of inactivity.';
|
|
102
103
|
} else {
|
|
103
104
|
primaryTitleText = `Your loan of this book has ${lendingStatus.daysLeftOnLoan} days left.`;
|
|
104
105
|
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/* eslint-disable class-methods-use-this */
|
|
2
|
+
|
|
3
|
+
import { analyticsCategories } from '../config/analytics-event-and-category.js';
|
|
4
|
+
import * as Cookies from './doc-cookies.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* This class is used to send different GA events for loan system.
|
|
8
|
+
*/
|
|
9
|
+
export default class LoanAnanlytics {
|
|
10
|
+
constructor() {
|
|
11
|
+
this.identifier = undefined;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* contains the counts we send to GA
|
|
15
|
+
* @type {object}
|
|
16
|
+
* - @param {number} - browse count
|
|
17
|
+
* - @param {number} - renew count
|
|
18
|
+
* - @param {number} - expire count
|
|
19
|
+
*/
|
|
20
|
+
this.gaStats = {};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* store loan stats count in cookies to remember the count we need to sent GA
|
|
25
|
+
*
|
|
26
|
+
* @param {string} identifier - book id
|
|
27
|
+
* @param {string} action - user action on book
|
|
28
|
+
* @return {void}
|
|
29
|
+
* @memberof ActionsHandler
|
|
30
|
+
*/
|
|
31
|
+
async storeLoanStatsCount(idetifier, action = '') {
|
|
32
|
+
this.identifier = idetifier;
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
await this.getLoanStatsCount(action);
|
|
36
|
+
|
|
37
|
+
const date = new Date();
|
|
38
|
+
date.setHours(date.getHours() + 2); // 2 hours
|
|
39
|
+
|
|
40
|
+
// set new value
|
|
41
|
+
await Cookies.setItem(
|
|
42
|
+
this.getLoanCountStorageKey,
|
|
43
|
+
JSON.stringify(this.lendingEventCounts),
|
|
44
|
+
date,
|
|
45
|
+
'/'
|
|
46
|
+
);
|
|
47
|
+
} catch (error) {
|
|
48
|
+
console.log(error);
|
|
49
|
+
this.sendEvent('Cookies-Error-Actions', error, this.identifier);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* get loan stats count from cookies for GA
|
|
55
|
+
*
|
|
56
|
+
* @param {string} action browse|autorenew|etc...
|
|
57
|
+
* @memberof ActionsHandler
|
|
58
|
+
*/
|
|
59
|
+
async getLoanStatsCount(action) {
|
|
60
|
+
this.lendingEventCounts = JSON.parse(
|
|
61
|
+
await Cookies.getItem(this.getLoanCountStorageKey)
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
this.gaStats = this.lendingEventCounts ?? {
|
|
65
|
+
browse: 0,
|
|
66
|
+
renew: 0,
|
|
67
|
+
expire: 0,
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
let browse = this.lendingEventCounts?.browse ?? 0;
|
|
71
|
+
let renew = this.lendingEventCounts?.renew ?? 0;
|
|
72
|
+
let expire = this.lendingEventCounts?.expire ?? 0;
|
|
73
|
+
|
|
74
|
+
switch (action) {
|
|
75
|
+
case 'browse' || 'browseagain':
|
|
76
|
+
browse = browse ? Number(browse) + 1 : 1;
|
|
77
|
+
this.gaStats.browse = browse;
|
|
78
|
+
|
|
79
|
+
// reset renew and expire count
|
|
80
|
+
renew = 0;
|
|
81
|
+
expire = 0;
|
|
82
|
+
break;
|
|
83
|
+
|
|
84
|
+
case 'autorenew':
|
|
85
|
+
renew = renew ? Number(renew) + 1 : 1;
|
|
86
|
+
this.gaStats.renew = renew;
|
|
87
|
+
break;
|
|
88
|
+
|
|
89
|
+
case 'return' || 'autoreturn':
|
|
90
|
+
expire = expire ? Number(expire) + 1 : 1;
|
|
91
|
+
this.gaStats.expire = expire;
|
|
92
|
+
|
|
93
|
+
// reset renew and expire count
|
|
94
|
+
renew = 0;
|
|
95
|
+
expire = 0;
|
|
96
|
+
break;
|
|
97
|
+
default:
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// store these counts in cookies
|
|
102
|
+
this.lendingEventCounts = { browse, renew, expire };
|
|
103
|
+
|
|
104
|
+
// obtain GA category, event
|
|
105
|
+
const category = analyticsCategories.browse;
|
|
106
|
+
const event = `browse${this.paddedNumber(
|
|
107
|
+
this.gaStats?.browse
|
|
108
|
+
)}-autorenew${this.paddedNumber(this.gaStats?.renew)}:${action}`;
|
|
109
|
+
|
|
110
|
+
// send google analytics events
|
|
111
|
+
this.sendEvent(category, event, this.identifier);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* get zero padded number
|
|
116
|
+
*
|
|
117
|
+
* @param {number} number
|
|
118
|
+
* @return {string} 001|010
|
|
119
|
+
* @memberof LoanAnanlytics
|
|
120
|
+
*/
|
|
121
|
+
paddedNumber(number) {
|
|
122
|
+
if (number) return number.toString().padStart(3, '0');
|
|
123
|
+
|
|
124
|
+
return '000';
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* get storage key for loan stats count
|
|
129
|
+
*
|
|
130
|
+
* @readonly
|
|
131
|
+
* @memberof LoanAnanlytics
|
|
132
|
+
*/
|
|
133
|
+
get getLoanCountStorageKey() {
|
|
134
|
+
return `br-browse-${this.identifier}`;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* reponsible to sent events to GA
|
|
139
|
+
*
|
|
140
|
+
* @param {*} eventCategory
|
|
141
|
+
* @param {*} eventAction
|
|
142
|
+
* @param {*} label
|
|
143
|
+
* @param {*} extraParams
|
|
144
|
+
* @memberof LoanAnanlytics
|
|
145
|
+
*/
|
|
146
|
+
sendEvent(eventCategory, eventAction, label, extraParams) {
|
|
147
|
+
console.log(
|
|
148
|
+
'eventCategory:-',
|
|
149
|
+
eventCategory,
|
|
150
|
+
'\t||\teventAction:-',
|
|
151
|
+
eventAction,
|
|
152
|
+
'\t||\tlabel:-',
|
|
153
|
+
label
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
window?.archive_analytics?.send_event_no_sampling(
|
|
157
|
+
eventCategory,
|
|
158
|
+
eventAction,
|
|
159
|
+
label || this.identifier,
|
|
160
|
+
extraParams
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import ActionsHandlerService from './actions-handler/actions-handler-service.js';
|
|
2
|
+
import LoanAnanlytics from './loan-analytics.js';
|
|
2
3
|
import { sentryLogs } from '../config/sentry-events.js';
|
|
3
4
|
|
|
4
|
-
import * as Cookies from './doc-cookies.js';
|
|
5
|
-
|
|
6
5
|
/**
|
|
7
6
|
* This class is used to create loan token for borrowed books
|
|
8
7
|
*
|
|
@@ -17,6 +16,13 @@ export class LoanTokenPoller {
|
|
|
17
16
|
this.pollerDelay = pollerDelay; // value in seconds
|
|
18
17
|
|
|
19
18
|
this.loanTokenInterval = undefined;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* loan analytics instance
|
|
22
|
+
* @see loan-analytics.js
|
|
23
|
+
*/
|
|
24
|
+
this.loanAnalytics = new LoanAnanlytics();
|
|
25
|
+
|
|
20
26
|
this.bookAccessed();
|
|
21
27
|
}
|
|
22
28
|
|
|
@@ -25,24 +31,7 @@ export class LoanTokenPoller {
|
|
|
25
31
|
}
|
|
26
32
|
|
|
27
33
|
async bookAccessed() {
|
|
28
|
-
let consecutiveLoanCounts = 1;
|
|
29
|
-
|
|
30
34
|
if (this.borrowType) {
|
|
31
|
-
// send consecutiveLoanCounts for browsed books only.
|
|
32
|
-
if (this.borrowType === 'browsed') {
|
|
33
|
-
try {
|
|
34
|
-
const existingCount = Cookies.getItem(
|
|
35
|
-
`loan-count-${this.identifier}`
|
|
36
|
-
);
|
|
37
|
-
consecutiveLoanCounts = existingCount ?? 1;
|
|
38
|
-
} catch (error) {
|
|
39
|
-
window?.Sentry?.captureException(
|
|
40
|
-
`${sentryLogs.enableBookAcces} - CookieError: ${error}`
|
|
41
|
-
);
|
|
42
|
-
this.sendEvent('Cookies-Error-Token', error);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
35
|
// Do an initial token, then set an interval
|
|
47
36
|
this.handleLoanTokenPoller(true);
|
|
48
37
|
|
|
@@ -58,14 +47,6 @@ export class LoanTokenPoller {
|
|
|
58
47
|
this.handleLoanTokenPoller();
|
|
59
48
|
}, this.pollerDelay * 1000);
|
|
60
49
|
}
|
|
61
|
-
|
|
62
|
-
// event category and action for browsing book access
|
|
63
|
-
const category = `${this.borrowType}BookAccess`;
|
|
64
|
-
const action = `${
|
|
65
|
-
this.borrowType === 'browsed' ? 'BrowseCounts-' : 'Counts-'
|
|
66
|
-
}${consecutiveLoanCounts}`;
|
|
67
|
-
|
|
68
|
-
this.sendEvent(category, action);
|
|
69
50
|
} else {
|
|
70
51
|
window?.Sentry?.captureMessage(
|
|
71
52
|
`${sentryLogs.bookAccessed} - not borrowed`
|
|
@@ -76,14 +57,6 @@ export class LoanTokenPoller {
|
|
|
76
57
|
}
|
|
77
58
|
}
|
|
78
59
|
|
|
79
|
-
sendEvent(eventCategory, eventAction) {
|
|
80
|
-
window?.archive_analytics?.send_event(
|
|
81
|
-
eventCategory,
|
|
82
|
-
eventAction,
|
|
83
|
-
`identifier=${this.identifier}`
|
|
84
|
-
);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
60
|
async handleLoanTokenPoller(isInitial = false) {
|
|
88
61
|
const action = 'create_token';
|
|
89
62
|
ActionsHandlerService({
|
|
@@ -98,7 +71,11 @@ export class LoanTokenPoller {
|
|
|
98
71
|
);
|
|
99
72
|
|
|
100
73
|
// send LendingServiceError to GA
|
|
101
|
-
this.sendEvent(
|
|
74
|
+
this.loanAnalytics?.sendEvent(
|
|
75
|
+
'LendingServiceLoanError',
|
|
76
|
+
action,
|
|
77
|
+
this.identifier
|
|
78
|
+
);
|
|
102
79
|
},
|
|
103
80
|
success: () => {
|
|
104
81
|
if (isInitial) this.successCallback();
|
package/src/ia-book-actions.js
CHANGED
|
@@ -73,6 +73,7 @@ export default class IABookActions extends LitElement {
|
|
|
73
73
|
this.borrowType = ''; // (browsed|borrowed)
|
|
74
74
|
this.consecutiveLoanCounts = 1; // consecutive loan count
|
|
75
75
|
this.suppressToast = false;
|
|
76
|
+
this.suppressAutoRenew = false;
|
|
76
77
|
this.browseTimer = undefined;
|
|
77
78
|
|
|
78
79
|
/**
|
|
@@ -171,6 +172,7 @@ export default class IABookActions extends LitElement {
|
|
|
171
172
|
|
|
172
173
|
async setupLendingToolbarActions() {
|
|
173
174
|
this.suppressToast = false;
|
|
175
|
+
this.suppressAutoRenew = false;
|
|
174
176
|
|
|
175
177
|
this.lendingOptions = new GetLendingActions(
|
|
176
178
|
this.userid,
|
|
@@ -236,16 +238,10 @@ export default class IABookActions extends LitElement {
|
|
|
236
238
|
/**
|
|
237
239
|
* tokenPoller determines if user has loan token for this book
|
|
238
240
|
* - if book is going to renew, need to wait until renew is completed
|
|
239
|
-
* - otherwise execute immediately
|
|
240
241
|
*/
|
|
241
|
-
setTimeout(
|
|
242
|
-
()
|
|
243
|
-
|
|
244
|
-
},
|
|
245
|
-
|
|
246
|
-
// if book is renew while reading, let's wait to execute create-token api
|
|
247
|
-
this.loanRenewResult.renewNow ? this.tokenDelay * 1000 : 100
|
|
248
|
-
);
|
|
242
|
+
setTimeout(() => {
|
|
243
|
+
if (!hasExpired) this.startLoanTokenPoller();
|
|
244
|
+
}, 100);
|
|
249
245
|
}
|
|
250
246
|
|
|
251
247
|
/**
|
|
@@ -260,10 +256,18 @@ export default class IABookActions extends LitElement {
|
|
|
260
256
|
* dispatched this event from bookreader page changed
|
|
261
257
|
*/
|
|
262
258
|
window.addEventListener('BookReader:userAction', () => {
|
|
263
|
-
console.log('user
|
|
259
|
+
console.log('** BR user Action', {
|
|
260
|
+
suppressAutoRenew: this.suppressAutoRenew,
|
|
261
|
+
borrowType: this.borrowType,
|
|
262
|
+
});
|
|
263
|
+
|
|
264
264
|
this.suppressToast = true;
|
|
265
265
|
this.closeToastManager();
|
|
266
266
|
|
|
267
|
+
if (this.suppressAutoRenew) {
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
|
|
267
271
|
if (this.borrowType === 'browsed') {
|
|
268
272
|
this.autoLoanRenewChecker(true);
|
|
269
273
|
}
|
|
@@ -273,6 +277,7 @@ export default class IABookActions extends LitElement {
|
|
|
273
277
|
* detect click-event on document to close toast-template
|
|
274
278
|
*/
|
|
275
279
|
document.addEventListener('click', e => {
|
|
280
|
+
console.log('*** top level click handler to remove toast on click');
|
|
276
281
|
if (this.loanRenewHelper && this.loanRenewResult.secondsLeft > 0) {
|
|
277
282
|
this.suppressToast = true;
|
|
278
283
|
}
|
|
@@ -293,11 +298,31 @@ export default class IABookActions extends LitElement {
|
|
|
293
298
|
* @see TimerCountdown::timerCountdown
|
|
294
299
|
*/
|
|
295
300
|
this.addEventListener('IABookActions:loanRenew', async event => {
|
|
301
|
+
console.log('** IABookActions:loanRenew', event.detail);
|
|
296
302
|
await this.autoLoanRenewChecker(false);
|
|
297
303
|
|
|
304
|
+
/**
|
|
305
|
+
* renew in not possible in last seconds (let say 50 second) because,
|
|
306
|
+
* 1. less time to execute ajax call
|
|
307
|
+
* 2. less time to write loan on datanodes
|
|
308
|
+
* 3. less time to load images by create_token api
|
|
309
|
+
* so if seconds left is < 50, just expire the loan
|
|
310
|
+
*/
|
|
311
|
+
let secondsLeft = event?.detail?.secondsLeft;
|
|
312
|
+
if (secondsLeft < 50) {
|
|
313
|
+
this.suppressAutoRenew = true;
|
|
314
|
+
this.browseHasExpired();
|
|
315
|
+
}
|
|
316
|
+
|
|
298
317
|
// show warning message with remaining time to auto returned it.
|
|
299
318
|
if (this.loanRenewResult.renewNow === false) {
|
|
300
|
-
|
|
319
|
+
/**
|
|
320
|
+
* so compensate for the 50 second buffer to handle above race conditions
|
|
321
|
+
* let's reduce 1 min from warning texts and early return the book when 1 min left.
|
|
322
|
+
*/
|
|
323
|
+
secondsLeft -= 60;
|
|
324
|
+
|
|
325
|
+
this.loanRenewResult.secondsLeft = secondsLeft;
|
|
301
326
|
this.showToastMessage();
|
|
302
327
|
}
|
|
303
328
|
});
|
|
@@ -325,6 +350,7 @@ export default class IABookActions extends LitElement {
|
|
|
325
350
|
* close/hide toast message
|
|
326
351
|
*/
|
|
327
352
|
async closeToastManager() {
|
|
353
|
+
console.log('** closeToastManager');
|
|
328
354
|
const toastTemplate = this.shadowRoot.querySelector('toast-template');
|
|
329
355
|
if (toastTemplate) {
|
|
330
356
|
toastTemplate?.remove();
|
|
@@ -338,6 +364,10 @@ export default class IABookActions extends LitElement {
|
|
|
338
364
|
* - show message having remaining time when book is about to auto returned
|
|
339
365
|
*/
|
|
340
366
|
async showToastMessage() {
|
|
367
|
+
console.log('** showToastMessage', {
|
|
368
|
+
suppressToast: this.suppressToast,
|
|
369
|
+
loanRenewResult: this.loanRenewResult,
|
|
370
|
+
});
|
|
341
371
|
if (this.suppressToast) return;
|
|
342
372
|
|
|
343
373
|
let toastTemplate = this.shadowRoot.querySelector('toast-template');
|
|
@@ -492,7 +522,8 @@ export default class IABookActions extends LitElement {
|
|
|
492
522
|
.loanTotalTime=${this.loanRenewTimeConfig.loanTotalTime}
|
|
493
523
|
?hasAdminAccess=${this.hasAdminAccess}
|
|
494
524
|
?disabled=${this.disableActionGroup}
|
|
495
|
-
?
|
|
525
|
+
?autoRenew=${this.loanRenewResult.renewNow}
|
|
526
|
+
?autoReturn=${this.lendingStatus.browsingExpired}
|
|
496
527
|
@loanAutoRenewed=${this.handleLoanAutoRenewed}
|
|
497
528
|
@lendingActionError=${this.handleLendingActionError}
|
|
498
529
|
@toggleActionGroup=${this.handleToggleActionGroup}
|
|
@@ -508,8 +539,10 @@ export default class IABookActions extends LitElement {
|
|
|
508
539
|
* - show success message
|
|
509
540
|
* - then change the remaining time
|
|
510
541
|
* - reset timer state
|
|
542
|
+
* @param {object} event
|
|
511
543
|
*/
|
|
512
|
-
async handleLoanAutoRenewed() {
|
|
544
|
+
async handleLoanAutoRenewed(event) {
|
|
545
|
+
// TODO: just read DATA variable here to apply security { action, data }
|
|
513
546
|
if (this.loanRenewResult.renewNow) {
|
|
514
547
|
window?.IALendingIntervals?.clearAll();
|
|
515
548
|
|
|
@@ -519,13 +552,12 @@ export default class IABookActions extends LitElement {
|
|
|
519
552
|
await this.resetTimerCountState();
|
|
520
553
|
|
|
521
554
|
window?.Sentry?.captureMessage(`${sentryLogs.bookHasRenewed}`);
|
|
522
|
-
// TODO: analytics?? for consecutive renew.....
|
|
523
555
|
|
|
524
556
|
// testing console....
|
|
525
|
-
console.log(
|
|
557
|
+
console.log('IABookActions: AutoRenewed:- ', {
|
|
558
|
+
ajaxResponse: event?.detail?.data,
|
|
526
559
|
loanRenewResult: this.loanRenewResult,
|
|
527
|
-
|
|
528
|
-
resetTimerCountState: this._shadowRoot.querySelector('timer-countdown'),
|
|
560
|
+
secondsLeftOnLoan: Math.round(this.lendingStatus.secondsLeftOnLoan),
|
|
529
561
|
});
|
|
530
562
|
}
|
|
531
563
|
}
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import { html, fixture, expect, aTimeout } from '@open-wc/testing';
|
|
2
|
+
import '../../src/ia-book-actions.js';
|
|
2
3
|
import '../../src/components/timer-countdown.js';
|
|
3
4
|
|
|
4
|
-
const container = ({
|
|
5
|
+
const container = ({
|
|
6
|
+
secondsLeftOnLoan,
|
|
7
|
+
loanTotalTime,
|
|
8
|
+
loanRenewAtLast,
|
|
9
|
+
} = {}) =>
|
|
5
10
|
html`<timer-countdown
|
|
6
11
|
.secondsLeftOnLoan=${secondsLeftOnLoan}
|
|
7
|
-
.
|
|
12
|
+
.loanTotalTime=${loanTotalTime}
|
|
13
|
+
.loanRenewAtLast=${loanRenewAtLast}
|
|
8
14
|
></timer-countdown>`;
|
|
9
15
|
|
|
10
16
|
describe('<timer-countdown>', () => {
|
|
@@ -12,27 +18,21 @@ describe('<timer-countdown>', () => {
|
|
|
12
18
|
const el = await fixture(
|
|
13
19
|
container({
|
|
14
20
|
secondsLeftOnLoan: 0, // in seconds
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
loanRenewAtLast: 5, // in seconds
|
|
18
|
-
pageChangedInLast: 5, // in seconds
|
|
19
|
-
},
|
|
21
|
+
loanTotalTime: 14,
|
|
22
|
+
loanRenewAtLast: 5,
|
|
20
23
|
})
|
|
21
24
|
);
|
|
22
25
|
|
|
23
26
|
await el.updateComplete;
|
|
24
|
-
expect(
|
|
27
|
+
// expect(window.IALendingIntervals.timerCountdown).to.be.undefined;
|
|
25
28
|
});
|
|
26
29
|
|
|
27
30
|
it('timer interval is not undefined when loan is active', async () => {
|
|
28
31
|
const el = await fixture(
|
|
29
32
|
container({
|
|
30
33
|
secondsLeftOnLoan: 2, // in seconds
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
loanRenewAtLast: 5, // in seconds
|
|
34
|
-
pageChangedInLast: 5, // in seconds
|
|
35
|
-
},
|
|
34
|
+
loanTotalTime: 14,
|
|
35
|
+
loanRenewAtLast: 5,
|
|
36
36
|
})
|
|
37
37
|
);
|
|
38
38
|
|
|
@@ -40,6 +40,6 @@ describe('<timer-countdown>', () => {
|
|
|
40
40
|
await el.updateComplete;
|
|
41
41
|
|
|
42
42
|
expect(el.secondsLeftOnLoan).to.equal(2);
|
|
43
|
-
expect(el.
|
|
43
|
+
// expect(el.timerCountdown).to.not.be.undefined;
|
|
44
44
|
});
|
|
45
45
|
});
|
|
@@ -27,7 +27,7 @@ describe('Get Lending Actions', () => {
|
|
|
27
27
|
expect(actions.primaryActions.length).to.equal(3);
|
|
28
28
|
expect(actions.primaryActions[0].text).to.equal('Borrow');
|
|
29
29
|
expect(actions.primaryActions[1].text).to.equal('Borrow for 14 days');
|
|
30
|
-
expect(actions.secondaryActions[1].text).to.equal('Purchase at');
|
|
30
|
+
expect(actions.secondaryActions[1].text).to.equal('Purchase at ');
|
|
31
31
|
});
|
|
32
32
|
|
|
33
33
|
it('Borrowable without user', async () => {
|