@internetarchive/ia-book-actions 0.0.29-alpha4 → 0.0.29-alpha40

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.
@@ -1,7 +1,8 @@
1
1
  import { LitElement } from 'lit';
2
2
 
3
- import { LocalCache } from '@internetarchive/local-cache';
4
3
  import { URLHelper } from '../../config/url-helper.js';
4
+ import { sentryLogs } from '../../config/sentry-events.js';
5
+
5
6
  import ActionsHandlerService from './actions-handler-service.js';
6
7
  import * as Cookies from '../doc-cookies.js';
7
8
 
@@ -16,14 +17,12 @@ import * as Cookies from '../doc-cookies.js';
16
17
  export default class ActionsHandler extends LitElement {
17
18
  constructor() {
18
19
  super();
19
- this.identifier = '';
20
- this.ajaxTimeout = 6000;
21
- this.returnUrl = '';
22
- this.autoRenewConfig = {};
23
20
 
24
- this.localCache = new LocalCache({
25
- namespace: '1HourLoanStorage',
26
- });
21
+ /**
22
+ * wait untill borrow is complete, then refresh the page
23
+ * @type {number}
24
+ */
25
+ this.waitUntillBorrowComplete = 6; // in seconds
27
26
 
28
27
  this.bindEvents();
29
28
  }
@@ -36,7 +35,7 @@ export default class ActionsHandler extends LitElement {
36
35
  );
37
36
  }
38
37
 
39
- async bindEvents() {
38
+ bindEvents() {
40
39
  this.addEventListener('browseBook', ({ detail }) => {
41
40
  this.handleBrowseIt();
42
41
  this.setConsecutiveLoanCounts();
@@ -51,9 +50,8 @@ export default class ActionsHandler extends LitElement {
51
50
  this.sendEvent(category, action);
52
51
  });
53
52
 
54
- this.addEventListener('autoBrowseBook', ({ detail }) => {
55
- console.log('auto renew handlerExecuted!!');
56
- this.handleAutoBrowseIt();
53
+ this.addEventListener('loanRenew', ({ detail }) => {
54
+ this.handleLoanRenew();
57
55
  const { category, action } = detail.event;
58
56
  this.sendEvent(category, action);
59
57
  });
@@ -112,12 +110,12 @@ export default class ActionsHandler extends LitElement {
112
110
  handleBrowseIt() {
113
111
  const action = 'browse_book';
114
112
  this.dispatchToggleActionGroup();
115
- this.setBrowseTimeSession()
116
113
 
117
114
  ActionsHandlerService({
118
115
  action,
119
116
  identifier: this.identifier,
120
117
  success: () => {
118
+ this.setBrowseTimeSession();
121
119
  this.handleReadItNow();
122
120
  },
123
121
  error: data => {
@@ -126,25 +124,26 @@ export default class ActionsHandler extends LitElement {
126
124
  });
127
125
  }
128
126
 
129
- handleAutoBrowseIt() {
127
+ handleLoanRenew() {
130
128
  const action = 'renew_loan';
131
- this.setBrowseTimeSession()
132
129
 
133
130
  ActionsHandlerService({
134
131
  action,
135
132
  identifier: this.identifier,
136
- success: () => {
137
- console.log('success!');
133
+ success: data => {
134
+ this.setBrowseTimeSession();
135
+ this.dispatchEvent(
136
+ new CustomEvent('loanAutoRenewed', {
137
+ detail: { action, data },
138
+ })
139
+ );
138
140
  },
139
141
  error: data => {
140
- console.log(data);
141
- console.log('error!');
142
142
  this.dispatchActionError(action, data);
143
143
  },
144
144
  });
145
145
  }
146
146
 
147
-
148
147
  handleReturnIt() {
149
148
  const action = 'return_loan';
150
149
  this.dispatchToggleActionGroup();
@@ -269,7 +268,7 @@ export default class ActionsHandler extends LitElement {
269
268
  // redirection on details page after 5 seconds because borrowing book takes time to create item creation.
270
269
  setTimeout(() => {
271
270
  URLHelper.goToUrl(redirectTo, true);
272
- }, this.ajaxTimeout);
271
+ }, this.waitUntillBorrowComplete * 1000);
273
272
  }
274
273
 
275
274
  // save consecutive loan count for borrow
@@ -290,11 +289,36 @@ export default class ActionsHandler extends LitElement {
290
289
  // set new value
291
290
  Cookies.setItem(storageKey, newCount, date, '/');
292
291
  } catch (error) {
293
- window?.Sentry?.captureException(error);
292
+ window?.Sentry?.captureException(
293
+ `${sentryLogs.setConsecutiveLoanCounts} - Error: ${error}`
294
+ );
294
295
  this.sendEvent('Cookies-Error-Actions', error);
295
296
  }
296
297
  }
297
298
 
299
+ /**
300
+ * set browse time in indexedDB
301
+ */
302
+ async setBrowseTimeSession() {
303
+ try {
304
+ const expireDate = new Date(
305
+ new Date().getTime() + this.loanTotalTime * 1000
306
+ );
307
+
308
+ // set a value
309
+ await this.localCache.set({
310
+ key: `${this.identifier}-loanTime`,
311
+ value: expireDate,
312
+ ttl: Number(this.loanTotalTime),
313
+ });
314
+
315
+ // delete pageChangedTime when book is auto renew at nth minute
316
+ await this.localCache.delete(`${this.identifier}-pageChangedTime`);
317
+ } catch (error) {
318
+ console.log(error);
319
+ }
320
+ }
321
+
298
322
  deleteLoanCookies() {
299
323
  const date = new Date();
300
324
  date.setTime(date.getTime() - 24 * 60 * 60 * 1000); // one day ago
@@ -314,17 +338,4 @@ export default class ActionsHandler extends LitElement {
314
338
  '.archive.org'
315
339
  );
316
340
  }
317
-
318
- async setBrowseTimeSession() {
319
- try {
320
- // set a value
321
- await this.localCache.set({
322
- key: `${this.identifier}-loanTime`,
323
- value: new Date(), // current time
324
- ttl: parseInt(this.autoRenewConfig.totalTime),
325
- })
326
-
327
- await this.localCache.delete(`${this.identifier}-pageFlipTime`);
328
- } catch (error) {}
329
- }
330
341
  }
@@ -19,7 +19,7 @@ import ActionsConfig from './actions-config.js';
19
19
  *
20
20
  * Response of this class contain an object as follows:-
21
21
  * {
22
- * primaryTitle: 'Renewable every hour, pending availability.',
22
+ * primaryTitle: '',
23
23
  * primaryActions: [this.actionsConfig.returnBookConfig()],
24
24
  * primaryColor: 'primary',
25
25
  * secondaryActions: [this.actionsConfig.purchaseConfig()],
@@ -40,7 +40,7 @@ import ActionsConfig from './actions-config.js';
40
40
  * @enum {string}
41
41
  */
42
42
  export const bookTitles = {
43
- available_1hr: 'Renewable every hour, pending availability.',
43
+ available_1hr: '',
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.',
@@ -49,7 +49,7 @@ export const bookTitles = {
49
49
  being_borrowed: 'Another patron is using this book. Please check back later.',
50
50
  eligible_pd: 'You are eligible for print-disabled access.',
51
51
  on_waitlist: 'You are on the waitlist for this book.',
52
- session_expired: 'Your loan has expired.',
52
+ session_expired: '',
53
53
  unavailable: 'This book is not available at this time.',
54
54
  };
55
55
 
@@ -98,7 +98,7 @@ export class GetLendingActions {
98
98
  let isBrowsing =
99
99
  lendingStatus.user_has_browsed && !lendingStatus.browsingExpired;
100
100
  if (isBrowsing) {
101
- primaryTitleText = this.getBrowseCountdownTitle();
101
+ primaryTitleText = '';
102
102
  } else {
103
103
  primaryTitleText = `Your loan of this book has ${lendingStatus.daysLeftOnLoan} days left.`;
104
104
  }
@@ -251,13 +251,6 @@ export class GetLendingActions {
251
251
  borrow1HrAction() {
252
252
  const lendingStatus = this.lendingStatus || {};
253
253
 
254
- const possibleTitles = {
255
- one_hour: 'Renewable every hour, pending availability.',
256
- session_tryagain:
257
- 'Another patron is using this book. Please check back later.',
258
- session_expired: 'Your loan has expired.',
259
- };
260
-
261
254
  // assess borrowable state
262
255
  const browsingHasExpired =
263
256
  !lendingStatus.available_to_browse && lendingStatus.browsingExpired;
@@ -364,6 +357,8 @@ export class GetLendingActions {
364
357
  }
365
358
 
366
359
  /**
360
+ * @deprecated
361
+ *
367
362
  * Builds the countdown toolbar title.
368
363
  *
369
364
  * @return string innerHTML for the dialogOpts title attribute
@@ -0,0 +1,163 @@
1
+ import { nothing } from 'lit';
2
+
3
+ /**
4
+ * This class is used to determine if use is eligible for auto renew loan.
5
+ */
6
+ export class LoanRenewHelper {
7
+ constructor(hasPageChanged, identifier, localCache, loanRenewTimeConfig) {
8
+ this.hasPageChanged = hasPageChanged;
9
+ this.identifier = identifier;
10
+ this.localCache = localCache;
11
+ this.loanRenewTimeConfig = loanRenewTimeConfig;
12
+
13
+ // messages for auto return machenism
14
+ this.loanRenewMessage = 'This book has been renewed for 1 hour.';
15
+ this.loanReturnWarning =
16
+ 'This book will be automatically returned in #time #unitsOfTime unless you turn a page.';
17
+
18
+ // private props
19
+ this.result = {
20
+ texts: null,
21
+ renewNow: false,
22
+ };
23
+ }
24
+
25
+ handleLoanRenew() {
26
+ try {
27
+ if (this.hasPageChanged) {
28
+ return this.pageChanged(); // user clicked on page
29
+ }
30
+ return this.autoChecker(); // auto checker at last 10th minute
31
+ } catch (error) {
32
+ console.log(error);
33
+ }
34
+
35
+ return nothing;
36
+ }
37
+
38
+ /**
39
+ * Trigger this function when user has browsed a book and change the post
40
+ * - every time user change the page, we set current time in indexedDB
41
+ * - also check if need to auto renew current loan
42
+ *
43
+ * @returns {this.result}
44
+ */
45
+ async pageChanged() {
46
+ const {
47
+ loanTotalTime, // 60
48
+ loanRenewAtLast, // 50
49
+ } = this.loanRenewTimeConfig;
50
+ const currentTime = new Date();
51
+ const loanTime = await this.localCache.get(`${this.identifier}-loanTime`);
52
+
53
+ const lastTimeFrame = this.changeTime(
54
+ loanTime,
55
+ loanTotalTime - loanRenewAtLast,
56
+ 'add'
57
+ );
58
+
59
+ // if user viewed new page in last 10 minutes, renew immediately
60
+ if (lastTimeFrame !== null && currentTime >= lastTimeFrame) {
61
+ this.result.texts = this.loanRenewMessage;
62
+ this.result.renewNow = true;
63
+ console.log('manually renewed by page click!');
64
+ }
65
+
66
+ this.setPageChangedTime();
67
+ return this.result;
68
+ }
69
+
70
+ /**
71
+ * Trigger this function when countdown hits at last 10th minute
72
+ * - if user is active, just renew the loan at last 10th minute
73
+ *
74
+ * @returns {this.result}
75
+ */
76
+ async autoChecker() {
77
+ const { pageChangedInLast } = this.loanRenewTimeConfig;
78
+ const pageChangedTime = await this.localCache.get(
79
+ `${this.identifier}-pageChangedTime`
80
+ );
81
+
82
+ // in last 15 min if user make any activity.
83
+ const pageChangeTimeFrame = this.changeTime(
84
+ new Date(),
85
+ pageChangedInLast,
86
+ 'sub'
87
+ ); // 15 seconds
88
+
89
+ if (
90
+ pageChangedTime === undefined ||
91
+ pageChangedTime <= pageChangeTimeFrame
92
+ ) {
93
+ this.result.texts = this.loanReturnWarning;
94
+ this.result.renewNow = false; // not viewed
95
+ } else if (pageChangedTime >= pageChangeTimeFrame) {
96
+ this.result.texts = '';
97
+ this.result.renewNow = true; // viewed in last time frame
98
+ console.log('silently renewed!');
99
+ }
100
+
101
+ return this.result;
102
+ }
103
+
104
+ /**
105
+ * set current time in indexedDB when user viewed a new page
106
+ */
107
+ async setPageChangedTime() {
108
+ await this.localCache.set({
109
+ key: `${this.identifier}-pageChangedTime`,
110
+ value: new Date(), // current time
111
+ ttl: Number(this.loanRenewTimeConfig.loanTotalTime),
112
+ });
113
+ }
114
+
115
+ /**
116
+ * Texts we want to show in toast template including remaining time
117
+ * - eg. 1 minute, 50 seconds
118
+ *
119
+ * @param {String} texts
120
+ * @param {Number} time
121
+ *
122
+ * @returns {String} // texts will be appear in toast template
123
+ */
124
+ getMessageTexts(texts, time) {
125
+ let unitOfTime = 'second';
126
+
127
+ let toastTexts = texts;
128
+ let timeLeft = time;
129
+
130
+ if (timeLeft > 60) {
131
+ unitOfTime = 'minute';
132
+ timeLeft = Math.floor(timeLeft / 60);
133
+ }
134
+
135
+ // replace remaining time
136
+ toastTexts = toastTexts?.replace(/#time/, timeLeft);
137
+
138
+ // replace unitsOfTime eg second vs seconds / minute vs /minutes
139
+ return toastTexts?.replace(
140
+ /#unitsOfTime/,
141
+ timeLeft !== 1 ? `${unitOfTime}s` : unitOfTime
142
+ );
143
+ }
144
+
145
+ /**
146
+ * Helper function to get time difference
147
+ *
148
+ * @param {Date} date
149
+ * @param {Number} seconds
150
+ * @param {String} op - like sub, add, mul, sub
151
+ *
152
+ * @returns {Object} Date
153
+ */
154
+ changeTime(date, seconds, op) {
155
+ if (date === undefined) return null;
156
+
157
+ if (op === 'sub') {
158
+ return new Date(date.getTime() - seconds * 1000);
159
+ }
160
+
161
+ return new Date(date.getTime() + seconds * 1000);
162
+ }
163
+ }
@@ -1,4 +1,6 @@
1
1
  import ActionsHandlerService from './actions-handler/actions-handler-service.js';
2
+ import { sentryLogs } from '../config/sentry-events.js';
3
+
2
4
  import * as Cookies from './doc-cookies.js';
3
5
 
4
6
  /**
@@ -12,10 +14,10 @@ export class LoanTokenPoller {
12
14
  this.borrowType = borrowType;
13
15
  this.successCallback = successCallback; // callback function to be called after loan token is created
14
16
  this.errorCallback = errorCallback; // callback function to be called after loan token is created
15
- this.pollerDelay = pollerDelay; // value in ms (1000 ms = 1 sec)
17
+ this.pollerDelay = pollerDelay; // value in seconds
16
18
 
17
19
  this.loanTokenInterval = undefined;
18
- this.enableBookAccess();
20
+ this.bookAccessed();
19
21
  }
20
22
 
21
23
  disconnectedInterval() {
@@ -31,7 +33,7 @@ export class LoanTokenPoller {
31
33
  // }
32
34
  }
33
35
 
34
- async enableBookAccess() {
36
+ async bookAccessed() {
35
37
  let consecutiveLoanCounts = 1;
36
38
 
37
39
  if (this.borrowType) {
@@ -43,7 +45,9 @@ export class LoanTokenPoller {
43
45
  );
44
46
  consecutiveLoanCounts = existingCount ?? 1;
45
47
  } catch (error) {
46
- window?.Sentry?.captureException(error);
48
+ window?.Sentry?.captureException(
49
+ `${sentryLogs.enableBookAcces} - CookieError: ${error}`
50
+ );
47
51
  this.sendEvent('Cookies-Error-Token', error);
48
52
  }
49
53
  }
@@ -57,7 +61,7 @@ export class LoanTokenPoller {
57
61
  if (this.borrowType !== 'adminBorrowed') {
58
62
  this.loanTokenInterval = setInterval(() => {
59
63
  this.handleLoanTokenPoller();
60
- }, this.pollerDelay);
64
+ }, this.pollerDelay * 1000);
61
65
  }
62
66
 
63
67
  // event category and action for browsing book access
@@ -68,7 +72,10 @@ export class LoanTokenPoller {
68
72
 
69
73
  this.sendEvent(category, action);
70
74
  } else {
71
- window?.Sentry?.captureMessage('enableBookAccess error');
75
+ window?.Sentry?.captureMessage(
76
+ `${sentryLogs.bookAccessed} - not borrowed`
77
+ );
78
+
72
79
  // if book is not browsed, just clear token polling interval
73
80
  this.disconnectedInterval(); // stop token fetch api
74
81
  }
@@ -88,12 +95,14 @@ export class LoanTokenPoller {
88
95
  identifier: this.identifier,
89
96
  action,
90
97
  error: data => {
91
- window?.Sentry?.captureMessage(
92
- 'handleLoanTokenPoller error',
93
- JSON.stringify(data)
94
- );
95
98
  this.disconnectedInterval(); // stop token fetch api
96
99
  this.errorCallback({ detail: { action, data } });
100
+
101
+ // send error to Sentry
102
+ window?.Sentry?.captureMessage(
103
+ `${sentryLogs.handleLoanTokenPoller} - Error: ${JSON.stringify(data)}`
104
+ );
105
+
97
106
  // send LendingServiceError to GA
98
107
  this.sendEvent('LendingServiceLoanError', action);
99
108
  },