@internetarchive/ia-book-actions 0.0.29 → 0.1.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.
@@ -0,0 +1,156 @@
1
+ /* eslint-disable class-methods-use-this */
2
+
3
+ import { nothing } from 'lit';
4
+
5
+ /**
6
+ * This class is used to determine if use is eligible for auto renew loan.
7
+ */
8
+ export class LoanRenewHelper {
9
+ constructor(hasPageChanged, identifier, localCache, loanRenewTimeConfig) {
10
+ this.hasPageChanged = hasPageChanged;
11
+ this.identifier = identifier;
12
+ this.localCache = localCache;
13
+ this.loanRenewTimeConfig = loanRenewTimeConfig;
14
+
15
+ // messages for auto return machenism
16
+ this.loanRenewMessage = 'This book has been renewed for 1 hour.';
17
+ this.loanReturnWarning =
18
+ 'This book will be automatically returned in #time #unitsOfTime unless you turn a page.';
19
+
20
+ // private props
21
+ this.result = {
22
+ texts: null,
23
+ renewNow: false,
24
+ };
25
+ }
26
+
27
+ handleLoanRenew() {
28
+ try {
29
+ if (this.hasPageChanged) {
30
+ return this.pageChanged(); // user clicked on page
31
+ }
32
+ return this.autoChecker(); // auto checker at last 10th minute
33
+ } catch (error) {
34
+ console.log(error);
35
+ }
36
+
37
+ return nothing;
38
+ }
39
+
40
+ /**
41
+ * Trigger this function when user has browsed a book and change the post
42
+ * - every time user change the page, we set current time in indexedDB
43
+ * - also check if need to auto renew current loan
44
+ *
45
+ * @returns {this.result}
46
+ */
47
+ async pageChanged() {
48
+ const {
49
+ loanRenewAtLast, // 50
50
+ } = this.loanRenewTimeConfig;
51
+ const currentTime = new Date();
52
+ const loanTime = await this.localCache.get(`${this.identifier}-loanTime`);
53
+
54
+ const lastTimeFrame = this.changeTime(loanTime, loanRenewAtLast, 'sub');
55
+
56
+ // if user viewed new page in last 10 minutes, renew immediately
57
+ if (lastTimeFrame !== null && currentTime >= lastTimeFrame) {
58
+ this.result.texts = this.loanRenewMessage;
59
+ this.result.renewNow = true;
60
+ }
61
+
62
+ this.setPageChangedTime();
63
+ return this.result;
64
+ }
65
+
66
+ /**
67
+ * Trigger this function when countdown hits at last 10th minute
68
+ * - if user is active, just renew the loan at last 10th minute
69
+ *
70
+ * @returns {this.result}
71
+ */
72
+ async autoChecker() {
73
+ const { pageChangedInLast } = this.loanRenewTimeConfig;
74
+ const pageChangedTime = await this.localCache.get(
75
+ `${this.identifier}-pageChangedTime`
76
+ );
77
+
78
+ // in last 15 min if user make any activity.
79
+ const pageChangeTimeFrame = this.changeTime(
80
+ new Date(),
81
+ pageChangedInLast,
82
+ 'sub'
83
+ ); // 15 seconds
84
+
85
+ if (
86
+ pageChangedTime === undefined ||
87
+ pageChangedTime <= pageChangeTimeFrame
88
+ ) {
89
+ this.result.texts = this.loanReturnWarning;
90
+ this.result.renewNow = false; // not viewed
91
+ } else if (pageChangedTime >= pageChangeTimeFrame) {
92
+ this.result.texts = '';
93
+ this.result.renewNow = true; // viewed in last time frame
94
+ }
95
+
96
+ return this.result;
97
+ }
98
+
99
+ /**
100
+ * set current time in indexedDB when user viewed a new page
101
+ */
102
+ async setPageChangedTime() {
103
+ await this.localCache.set({
104
+ key: `${this.identifier}-pageChangedTime`,
105
+ value: new Date(), // current time
106
+ ttl: Number(this.loanRenewTimeConfig.loanTotalTime),
107
+ });
108
+ }
109
+
110
+ /**
111
+ * Texts we want to show in toast template including remaining time
112
+ * - eg. 1 minute, 2 minutes
113
+ *
114
+ * @param {String} texts
115
+ * @param {Number} secondsLeft
116
+ *
117
+ * @returns {String} // texts will be appear in toast template
118
+ */
119
+ getMessageTexts(texts, secondsLeft) {
120
+ const unitOfTime = 'minute';
121
+
122
+ let toastTexts = texts;
123
+ let timeLeft = secondsLeft;
124
+
125
+ // convert time from second to minute
126
+ timeLeft = Math.ceil(timeLeft / 60);
127
+
128
+ // replace #time variable with remaining time
129
+ toastTexts = toastTexts?.replace(/#time/, timeLeft);
130
+
131
+ // replace #unitsOfTime variable with minute/minutes
132
+ return toastTexts?.replace(
133
+ /#unitsOfTime/,
134
+ timeLeft !== 1 ? `${unitOfTime}s` : unitOfTime
135
+ );
136
+ }
137
+
138
+ /**
139
+ * Helper function to get time difference
140
+ *
141
+ * @param {Date} date
142
+ * @param {Number} seconds
143
+ * @param {String} op - like sub, add, mul, sub
144
+ *
145
+ * @returns {Object} Date
146
+ */
147
+ changeTime(date, seconds, op) {
148
+ if (date === undefined) return null;
149
+
150
+ if (op === 'sub') {
151
+ return new Date(date.getTime() - seconds * 1000);
152
+ }
153
+
154
+ return new Date(date.getTime() + seconds * 1000);
155
+ }
156
+ }
@@ -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
  },