@internetarchive/ia-book-actions 0.1.5 → 0.1.6

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 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 = 'https://openlibrary.org';
131
+ iaBookActions.returnUrl = '';
132
132
  iaBookActions.localCache = localCache;
133
133
 
134
- let loanTotalTime2Min = { loanTotalTime: 20, loanRenewAtLast: 15, pageChangedInLast: 5 };
135
- let loanTotalTime5Min = { loanTotalTime: 300, loanRenewAtLast: 290, pageChangedInLast: 15 };
134
+ let loanTotalTime2Min = { loanTotalTime: 10, loanRenewAtLast: 5, pageChangedInLast: 5 };
135
+ let loanTotalTime5Min = { loanTotalTime: 300, loanRenewAtLast: 180, pageChangedInLast: 15 };
136
136
  let loanTotalTime1Hour = { loanTotalTime: 3600, loanRenewAtLast: 600, pageChangedInLast: 900 };
137
137
 
138
- iaBookActions.loanRenewTimeConfig = loanTotalTime5Min;
138
+ iaBookActions.loanRenewTimeConfig = loanTotalTime2Min;
139
139
 
140
140
  let lendingStatus1 = iaBookActions.lendingStatus;
141
141
  let secondsLeftOnLoan = iaBookActions.loanRenewTimeConfig.loanTotalTime;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@internetarchive/ia-book-actions",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "Bookreader actions for borrowable book",
5
5
  "author": "Internet Archive",
6
6
  "license": "AGPL-3.0-only",
@@ -32,7 +32,8 @@ export class CollapsibleActionGroup extends ActionsHandler {
32
32
  dropdownArrow: { type: String },
33
33
  disabled: { type: Boolean },
34
34
  returnUrl: { type: String },
35
- renewNow: { type: Boolean },
35
+ autoRenew: { type: Boolean },
36
+ autoReturn: { type: Boolean },
36
37
  };
37
38
  }
38
39
 
@@ -52,7 +53,8 @@ export class CollapsibleActionGroup extends ActionsHandler {
52
53
  this.loaderIcon = 'https://archive.org/upload/images/tree/loading.gif';
53
54
  this.disabled = false;
54
55
  this.returnUrl = '';
55
- this.renewNow = false;
56
+ this.autoRenew = false;
57
+ this.autoReturn = false;
56
58
  }
57
59
 
58
60
  updated(changed) {
@@ -63,21 +65,33 @@ export class CollapsibleActionGroup extends ActionsHandler {
63
65
  this.resetActions();
64
66
  }
65
67
 
66
- if (changed.has('renewNow') && this.renewNow === true) {
67
- this.dispatchLoanRenewEvent();
68
+ if (changed.has('autoRenew') && this.autoRenew === true) {
69
+ this.dispatchLoanEvent('autoRenew');
70
+ }
71
+
72
+ if (changed.has('autoReturn') && this.autoReturn === true) {
73
+ this.dispatchLoanEvent('autoReturn');
68
74
  }
69
75
  }
70
76
 
71
- // dispatch this event to loan renew handler
72
- dispatchLoanRenewEvent() {
73
- const { category, action } = {
74
- category: analyticsCategories.browse,
75
- action: analyticsActions.browseLoanRenew,
76
- };
77
+ /**
78
+ * dispatch event when book is auto renewed / returned
79
+ * listen these events in action-handler.js
80
+ * @see ActionsHandler
81
+ *
82
+ * @param {string} event - autoRenew|autoReturn
83
+ * @memberof CollapsibleActionGroup
84
+ */
85
+ dispatchLoanEvent(event) {
86
+ const category = analyticsCategories.browse;
87
+ const action =
88
+ event === 'autoRenew'
89
+ ? analyticsActions.browseAutoRenew
90
+ : analyticsActions.browseAutoReturn;
77
91
 
78
92
  // listen in action-handler.js to execute ajax call on petabox.
79
93
  this.dispatchEvent(
80
- new CustomEvent('loanRenew', {
94
+ new CustomEvent(event, {
81
95
  detail: {
82
96
  event: { category, action },
83
97
  },
@@ -190,7 +204,11 @@ export class CollapsibleActionGroup extends ActionsHandler {
190
204
  href="${action.url}"
191
205
  target=${action.target}
192
206
  @click=${() => {
193
- this.clickHandler(action.id, action.analyticsEvent);
207
+ this.clickHandler(
208
+ action.id,
209
+ action.analyticsEvent,
210
+ action?.borrowType
211
+ );
194
212
  }}
195
213
  >
196
214
  ${action.id === 'purchaseBook' ? purchaseIcon : ''} ${action.text}
@@ -211,7 +229,7 @@ export class CollapsibleActionGroup extends ActionsHandler {
211
229
  return html`<button
212
230
  class="ia-button ${action.className} ${initialButton ? 'initial' : ''}"
213
231
  @click=${() => {
214
- this.clickHandler(action.id, analyticsEvent);
232
+ this.clickHandler(action.id, analyticsEvent, action?.borrowType);
215
233
  }}
216
234
  >
217
235
  ${action.text}
@@ -224,10 +242,10 @@ export class CollapsibleActionGroup extends ActionsHandler {
224
242
  * @param { object } gaEvent - contains analytics event action and category
225
243
  * @param { string } gaEvent.category
226
244
  * @param { string } gaEvent.action
227
- *
245
+ * @param { string } borrowType browse|borrow
228
246
  * @fires CollapsibleActionGroup#{eventName} - (will be browseBook, borrowBook etc...)
229
247
  */
230
- clickHandler(eventName, gaEvent) {
248
+ clickHandler(eventName, gaEvent, borrowType = '') {
231
249
  this.dropdownState = 'close';
232
250
  this.dropdownArrow = dropdownClosed;
233
251
 
@@ -237,6 +255,7 @@ export class CollapsibleActionGroup extends ActionsHandler {
237
255
  new CustomEvent(eventName, {
238
256
  detail: {
239
257
  event: { category, action },
258
+ borrowType,
240
259
  },
241
260
  })
242
261
  );
@@ -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 = 0;
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
@@ -13,7 +13,8 @@ export const analyticsCategories = {
13
13
  export const analyticsActions = {
14
14
  browse: 'Borrow-1Hour',
15
15
  browseAgain: 'Borrow-Again',
16
- browseLoanRenew: 'Borrow-Loan-Renew',
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
  */
@@ -70,6 +70,7 @@ export default class ActionsConfig {
70
70
  category: eventCategory,
71
71
  action: this.analyticsActions.doneBorrowing,
72
72
  },
73
+ borrowType: this.lendingStatus.user_has_browsed ? 'browse' : 'borrow',
73
74
  };
74
75
  }
75
76
 
@@ -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
- * APIs based of the request made by user. It consist some parameters are as follows:-
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
- this.bindEvents();
28
- }
26
+ /**
27
+ * bind events for lending bar
28
+ * @type {object}
29
+ */
30
+ this.loanAnanlytics = new LoanAnanlytics();
29
31
 
30
- sendEvent(eventCategory, eventAction) {
31
- window?.archive_analytics?.send_event(
32
- eventCategory,
33
- eventAction,
34
- `identifier=${this.identifier}`
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', ({ detail }) => {
40
+ this.addEventListener('browseBook', () => {
40
41
  this.handleBrowseIt();
41
- this.setConsecutiveLoanCounts();
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', ({ detail }) => {
45
+ this.addEventListener('browseBookAgain', () => {
47
46
  this.handleBrowseIt();
48
- this.setConsecutiveLoanCounts('browseAgain');
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('loanRenew', ({ detail }) => {
54
- this.handleLoanRenew();
55
- const { category, action } = detail.event;
56
- this.sendEvent(category, action);
50
+ this.addEventListener('autoRenew', () => {
51
+ this.handleLoanRenewNow();
52
+ this.loanAnanlytics?.storeLoanStatsCount(this.identifier, 'autorenew');
53
+ });
54
+
55
+ this.addEventListener('autoReturn', () => {
56
+ this.deleteLoanCookies();
57
+ this.loanAnanlytics?.storeLoanStatsCount(this.identifier, 'autoreturn');
57
58
  });
58
59
 
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
+
60
66
  this.handleReturnIt();
61
- const { category, action } = detail.event;
62
- this.sendEvent(category, action);
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
- handleLoanRenew() {
137
+ handleLoanRenewNow() {
128
138
  const action = 'renew_loan';
129
139
 
130
140
  ActionsHandlerService({
@@ -220,7 +230,7 @@ export default class ActionsHandler extends LitElement {
220
230
  */
221
231
  dispatchActionError(action, data = {}) {
222
232
  // send LendingServiceError to GA
223
- this.sendEvent('LendingServiceError', action);
233
+ this.loanAnanlytics?.sendEvent('LendingServiceError', action);
224
234
 
225
235
  this.dispatchEvent(
226
236
  new CustomEvent('lendingActionError', {
@@ -271,31 +281,6 @@ export default class ActionsHandler extends LitElement {
271
281
  }, this.waitUntillBorrowComplete * 1000);
272
282
  }
273
283
 
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
284
  /**
300
285
  * set browse time in indexedDB
301
286
  */
@@ -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
- * @fixme hasItem isn't even implemented! This will always error.
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
+ }
@@ -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('LendingServiceLoanError', action);
74
+ this.loanAnalytics?.sendEvent(
75
+ 'LendingServiceLoanError',
76
+ action,
77
+ this.identifier
78
+ );
102
79
  },
103
80
  success: () => {
104
81
  if (isInitial) this.successCallback();
@@ -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
- if (!hasExpired) this.startLoanTokenPoller();
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 action');
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,8 +298,16 @@ 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
+ // renew in last seconds (let say 50 second) not possible because,
305
+ // - very short to renew on datanodes by hitting petabox API
306
+ // - loading images by create loan token
307
+ if (event?.detail?.secondsLeft < 50) {
308
+ this.suppressAutoRenew = true;
309
+ }
310
+
298
311
  // show warning message with remaining time to auto returned it.
299
312
  if (this.loanRenewResult.renewNow === false) {
300
313
  this.loanRenewResult.secondsLeft = event.detail.secondsLeft;
@@ -325,6 +338,7 @@ export default class IABookActions extends LitElement {
325
338
  * close/hide toast message
326
339
  */
327
340
  async closeToastManager() {
341
+ console.log('** closeToastManager');
328
342
  const toastTemplate = this.shadowRoot.querySelector('toast-template');
329
343
  if (toastTemplate) {
330
344
  toastTemplate?.remove();
@@ -338,6 +352,10 @@ export default class IABookActions extends LitElement {
338
352
  * - show message having remaining time when book is about to auto returned
339
353
  */
340
354
  async showToastMessage() {
355
+ console.log('** showToastMessage', {
356
+ suppressToast: this.suppressToast,
357
+ loanRenewResult: this.loanRenewResult,
358
+ });
341
359
  if (this.suppressToast) return;
342
360
 
343
361
  let toastTemplate = this.shadowRoot.querySelector('toast-template');
@@ -492,7 +510,8 @@ export default class IABookActions extends LitElement {
492
510
  .loanTotalTime=${this.loanRenewTimeConfig.loanTotalTime}
493
511
  ?hasAdminAccess=${this.hasAdminAccess}
494
512
  ?disabled=${this.disableActionGroup}
495
- ?renewNow=${this.loanRenewResult.renewNow}
513
+ ?autoRenew=${this.loanRenewResult.renewNow}
514
+ ?autoReturn=${this.lendingStatus.browsingExpired}
496
515
  @loanAutoRenewed=${this.handleLoanAutoRenewed}
497
516
  @lendingActionError=${this.handleLendingActionError}
498
517
  @toggleActionGroup=${this.handleToggleActionGroup}
@@ -508,8 +527,10 @@ export default class IABookActions extends LitElement {
508
527
  * - show success message
509
528
  * - then change the remaining time
510
529
  * - reset timer state
530
+ * @param {object} event
511
531
  */
512
- async handleLoanAutoRenewed() {
532
+ async handleLoanAutoRenewed(event) {
533
+ // TODO: just read DATA variable here to apply security { action, data }
513
534
  if (this.loanRenewResult.renewNow) {
514
535
  window?.IALendingIntervals?.clearAll();
515
536
 
@@ -519,13 +540,12 @@ export default class IABookActions extends LitElement {
519
540
  await this.resetTimerCountState();
520
541
 
521
542
  window?.Sentry?.captureMessage(`${sentryLogs.bookHasRenewed}`);
522
- // TODO: analytics?? for consecutive renew.....
523
543
 
524
544
  // testing console....
525
- console.log(sentryLogs.bookHasRenewed, {
545
+ console.log('IABookActions: AutoRenewed:- ', {
546
+ ajaxResponse: event?.detail?.data,
526
547
  loanRenewResult: this.loanRenewResult,
527
- browseHasRenew: this.lendingStatus.secondsLeftOnLoan,
528
- resetTimerCountState: this._shadowRoot.querySelector('timer-countdown'),
548
+ secondsLeftOnLoan: Math.round(this.lendingStatus.secondsLeftOnLoan),
529
549
  });
530
550
  }
531
551
  }
@@ -64,8 +64,6 @@ describe('<collapsible-action-group>', () => {
64
64
  expect(primaryActionContainer.classList.contains('action-buttons')).to.be
65
65
  .true;
66
66
 
67
- expect(el.sendEvent).to.exist;
68
-
69
67
  let eName;
70
68
  let eAnalytics;
71
69
  const manualClickStub = (eventName, gaEvent) => {
@@ -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 = ({ secondsLeftOnLoan, loanRenewTimeConfig } = {}) =>
5
+ const container = ({
6
+ secondsLeftOnLoan,
7
+ loanTotalTime,
8
+ loanRenewAtLast,
9
+ } = {}) =>
5
10
  html`<timer-countdown
6
11
  .secondsLeftOnLoan=${secondsLeftOnLoan}
7
- .loanRenewTimeConfig=${loanRenewTimeConfig}
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
- loanRenewTimeConfig: {
16
- loanTotalTime: 14, // in seconds
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(el.timerInterval).to.be.undefined;
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
- loanRenewTimeConfig: {
32
- loanTotalTime: 14, // in seconds
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.timerInterval).to.not.be.undefined;
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 () => {