@internetarchive/ia-book-actions 0.0.29-alpha1 → 0.0.29-alpha10

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
@@ -127,32 +127,15 @@
127
127
  iaBookActions.lendingBarPostInit = lendingBarPostInit;
128
128
  iaBookActions.tokenDelay = 2000; // 2 seconds
129
129
  iaBookActions.returnUrl = 'https://openlibrary.org';
130
- // iaBookActions.localCache = localCache;
131
- iaBookActions.autoRenewTimeConfig = {
130
+ iaBookActions.localCache = localCache;
131
+ iaBookActions.autoRenewConfig = {
132
132
  totalTime: 60, // 60 second = 1 min
133
- autoRenewCheckerAtLast: 10, // totalTime - 10th minute
134
- autoRenewVisitedInLast: 15, // within last 15 second/minute
133
+ autoCheckAt: 55, // totalTime - 10th minute
134
+ FlippedInLast: 15, // within last 15 second/minute
135
135
  };
136
136
 
137
137
  let lendingStatus1 = iaBookActions.lendingStatus;
138
- let secondsLeftOnLoan = 60;
139
-
140
- document.querySelector('#flip-page').addEventListener('click', async (e) => {
141
- try {
142
- // set pageFlipTime time in localStorage
143
- await localCache.set({
144
- key: `${identifier}-pageFlipTime`,
145
- value: new Date(), // current time
146
- })
147
-
148
- // dispatch event to say book page has flipped
149
- document.querySelector("ia-book-actions").dispatchEvent(
150
- new CustomEvent("bookPageFlip", {
151
- detail: { bookPageChange: true },
152
- })
153
- );
154
- } catch (error) {}
155
- });
138
+ let secondsLeftOnLoan = iaBookActions.autoRenewConfig.totalTime;
156
139
 
157
140
  document.querySelectorAll('.titleBar input[type=checkbox]').forEach((item) => {
158
141
  item.addEventListener('click', (e) => {
@@ -164,21 +147,6 @@
164
147
  });
165
148
  });
166
149
 
167
- document.querySelectorAll('.autoRenew input[type=checkbox]').forEach((item) => {
168
- item.addEventListener('click', (e) => {
169
- if (e.target.checked) {
170
- iaBookActions.autoRenew = true;
171
- iaBookActions.resetTimerCountdown = true;
172
- lendingStatus1.secondsLeftOnLoan = secondsLeftOnLoan;
173
- } else {
174
- iaBookActions.autoRenew = false;
175
- }
176
-
177
- let lending = {...defaultLendingStatus, ...lendingStatus1};
178
- iaBookActions.lendingStatus = lending;
179
- });
180
- });
181
-
182
150
  document.querySelectorAll('.errorEnable input[type=checkbox]').forEach((item) => {
183
151
  if (window.location.href.indexOf('?error=true') !== -1) {
184
152
  item.checked = true;
@@ -244,6 +212,7 @@
244
212
  await localCache.set({
245
213
  key: `${identifier}-loanTime`,
246
214
  value: new Date(), // current time
215
+ ttl: Number(iaBookActions.autoRenewConfig.totalTime),
247
216
  })
248
217
 
249
218
  await localCache.delete(`${identifier}-pageFlipTime`);
@@ -328,10 +297,6 @@
328
297
  <table class="initial">
329
298
  <tr colspan='5'>
330
299
  <td class="errorEnable"><label><input type="checkbox" name="errorEnable" value="errorEnable" />Do you want to see error response?</label></td>
331
- <td class="autoRenew"><label><input type="checkbox" name="autoRenew" value="autoRenew" />Do you want to auto-renew 1 hour</label></td>
332
- </tr>
333
- <tr>
334
- <td><button id="flip-page">Flip page</button></td>
335
300
  </tr>
336
301
  <tr>
337
302
  <td class="userState">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@internetarchive/ia-book-actions",
3
- "version": "0.0.29-alpha1",
3
+ "version": "0.0.29-alpha10",
4
4
  "description": "Bookreader actions for borrowable book",
5
5
  "author": "Internet Archive",
6
6
  "license": "AGPL-3.0-only",
@@ -11,8 +11,8 @@
11
11
  },
12
12
  "scripts": {
13
13
  "start": "web-dev-server",
14
- "lint": "eslint --ext .js",
15
- "format": "eslint --ext .js,",
14
+ "lint": "eslint --ext .js,.html . --ignore-path .gitignore && prettier \"**/*.js\" --check --ignore-path .gitignore",
15
+ "format": "eslint --ext .js,.html . --fix --ignore-path .gitignore && prettier \"**/*.js\" --write --ignore-path .gitignore",
16
16
  "test": "yarn run lint && yarn test:fast",
17
17
  "test:fast": "web-test-runner --coverage --playwright --browsers chromium firefox webkit",
18
18
  "test:watch": "web-test-runner --watch --playwright --browsers chromium firefox webkit"
@@ -43,6 +43,12 @@
43
43
  "sinon": "^13.0.2"
44
44
  },
45
45
  "eslintConfig": {
46
+ "rules": {
47
+ "no-console": "off",
48
+ "no-unused-expressions": "off",
49
+ "class-methods-use-this": "off",
50
+ "lit-a11y/click-events-have-key-events": "off"
51
+ },
46
52
  "extends": [
47
53
  "@open-wc/eslint-config",
48
54
  "eslint-config-prettier"
@@ -100,7 +100,7 @@ export const canBrowseAndBorrow = {
100
100
  // userid: "",
101
101
  bookUrl: '/details/practicalorganic00plim',
102
102
  daysLeftOnLoan: 0,
103
- isAdmin: true,
103
+ isAdmin: false,
104
104
  isArchiveOrgLending: true,
105
105
  isAvailable: false,
106
106
  isAvailableForBrowsing: false,
@@ -59,17 +59,22 @@ export class CollapsibleActionGroup extends ActionsHandler {
59
59
  this.resetActions();
60
60
  }
61
61
 
62
- if (this.browseAgainNow === true) {
63
- this.dispatchEvent(
64
- new CustomEvent('autoBrowseBook', {
65
- detail: {
66
- event: { 'autoRenewed1Hour': 'browseBook' },
67
- },
68
- })
69
- );
62
+ if (changed.has('browseAgainNow') && this.browseAgainNow === true) {
63
+ this.dispatchAutoRenewBookEvent();
70
64
  }
71
65
  }
72
66
 
67
+ // dispatch this event to loan renew handler
68
+ dispatchAutoRenewBookEvent() {
69
+ this.dispatchEvent(
70
+ new CustomEvent('autoRenewLoan', {
71
+ detail: {
72
+ event: { autoRenewed1Hour: 'browseBook' },
73
+ },
74
+ })
75
+ );
76
+ }
77
+
73
78
  /**
74
79
  * merge primaryActions and secondaryActions into dropdown
75
80
  */
@@ -4,44 +4,40 @@ export default class TimerCountdown extends LitElement {
4
4
  static get properties() {
5
5
  return {
6
6
  time: { type: Number },
7
- autoRenewCheckerAtLast: { type: Number },
8
- resetTimerCountdown: { type: Boolean },
7
+ autoCheckAt: { type: Number },
9
8
  };
10
9
  }
11
10
 
12
11
  constructor() {
13
12
  super();
14
- this.time = 60;
15
- this.autoRenewCheckerAtLast = 0;
13
+ this.time = 0;
14
+ this.autoCheckAt = 0;
15
+
16
+ // private props
16
17
  this.timerInterval = undefined;
17
- this.resetTimerCountdown = false;
18
18
  }
19
19
 
20
20
  disconnectedCallback() {
21
- clearInterval(this.timerInterval)
22
- }
23
-
24
- firstUpdated() {
25
- this.timerCountdown();
21
+ clearInterval(this.timerInterval);
26
22
  }
27
23
 
28
24
  updated(changed) {
29
- if (changed.has('resetTimerCountdown')) {
25
+ if (changed.has('time') && this.time > 0) {
30
26
  clearInterval(this.timerInterval);
31
- this.timerCountdown()
27
+ this.timerCountdown();
32
28
  }
33
29
  }
34
-
30
+
35
31
  timerCountdown() {
36
32
  this.timerInterval = setInterval(() => {
37
33
  this.time -= 1;
38
34
 
39
35
  // execute 50th minute check
40
- if (this.time === this.autoRenewCheckerAtLast) {
36
+ if (Math.round(this.time) === this.autoCheckAt) {
41
37
  this.dispatchEvent(
42
- new CustomEvent('autoReturnWarning', {
38
+ new CustomEvent('autoRenewAttempt', {
43
39
  detail: {
44
- remainingT: this.time
40
+ bookPageChange: false,
45
41
  },
46
42
  bubbles: true,
47
43
  composed: true,
@@ -50,18 +46,16 @@ export default class TimerCountdown extends LitElement {
50
46
  }
51
47
 
52
48
  if (this.time === 0) clearInterval(this.timerInterval);
53
-
54
49
  }, 1000);
55
50
  }
56
51
 
57
-
58
52
  render() {
59
53
  return html`<div id="countdown">
60
- <div id="countdown-number">${this.time}</div>
61
- <svg>
62
- <circle r="18" cx="20" cy="20"></circle>
63
- </svg>
64
- </div>`
54
+ <div id="countdown-number">${Math.round(this.time)}</div>
55
+ <svg>
56
+ <circle r="18" cx="20" cy="20"></circle>
57
+ </svg>
58
+ </div>`;
65
59
  }
66
60
 
67
61
  static get styles() {
@@ -71,22 +65,14 @@ export default class TimerCountdown extends LitElement {
71
65
  font-size: 14px;
72
66
  position: absolute;
73
67
  }
74
- #countdown1 {
75
- position: relative;
76
- margin: auto;
77
- margin-top: 100px;
78
- height: 40px;
79
- width: 40px;
80
- text-align: center;
81
- }
82
-
68
+
83
69
  #countdown-number {
84
70
  color: white;
85
71
  display: inline-block;
86
72
  line-height: 40px;
87
73
  margin-right: 12px;
88
74
  }
89
-
75
+
90
76
  svg {
91
77
  position: absolute;
92
78
  top: 0;
@@ -95,7 +81,7 @@ export default class TimerCountdown extends LitElement {
95
81
  height: 40px;
96
82
  transform: rotateY(-180deg) rotateZ(-90deg);
97
83
  }
98
-
84
+
99
85
  svg circle {
100
86
  stroke-dasharray: 113px;
101
87
  stroke-dashoffset: 0px;
@@ -105,7 +91,7 @@ export default class TimerCountdown extends LitElement {
105
91
  fill: none;
106
92
  animation: countdown 60s linear infinite forwards;
107
93
  }
108
-
94
+
109
95
  @keyframes countdown {
110
96
  from {
111
97
  stroke-dashoffset: 0px;
@@ -114,6 +100,7 @@ export default class TimerCountdown extends LitElement {
114
100
  stroke-dashoffset: 113px;
115
101
  }
116
102
  }
103
+
117
104
  .hidden {
118
105
  display: none;
119
106
  }
@@ -4,8 +4,9 @@ export class ToastMessage extends LitElement {
4
4
  static get properties() {
5
5
  return {
6
6
  texts: { type: String },
7
- dismisOnClick: { type: Function },
7
+ dismisOnClick: { type: Boolean },
8
8
  hideToast: { type: Boolean },
9
+ clostAfter: { type: Number },
9
10
  };
10
11
  }
11
12
 
@@ -14,12 +15,20 @@ export class ToastMessage extends LitElement {
14
15
  this.texts = '';
15
16
  this.textClass = '';
16
17
  this.hideToast = false;
18
+ this.clostAfter = 0;
17
19
  }
18
20
 
19
21
  render() {
20
22
  return !this.hideToast
21
23
  ? html`
22
- <span class="toast-message ${this.textClass}">${this.texts}</span>
24
+ <span
25
+ class="toast-message fade-in ${this.textClass}"
26
+ title="Click/tap to close"
27
+ @click=${() => {
28
+ this.dismisOnClick ? (this.hideToast = true) : nothing;
29
+ }}
30
+ >${this.texts}</span
31
+ >
23
32
  `
24
33
  : nothing;
25
34
  }
@@ -36,9 +45,9 @@ export class ToastMessage extends LitElement {
36
45
  position: absolute;
37
46
  top: ${heightFromTop};
38
47
  font-size: 1.4rem;
48
+ font-family: 'Helvetica Neue';
39
49
  color: #fff;
40
50
  left: 50%;
41
- right: 50%;
42
51
  background: #333;
43
52
  border: 1px solid #fff;
44
53
  border-radius: 5px;
@@ -46,7 +55,18 @@ export class ToastMessage extends LitElement {
46
55
  padding: 10px;
47
56
  width: fit-content;
48
57
  transform: translate(-50%, -50%);
58
+ cursor: pointer;
59
+ animation: fadeIn 5s;
60
+ @keyframes fadeIn {
61
+ 0% {
62
+ opacity: 0;
63
+ }
64
+ 100% {
65
+ opacity: 1;
66
+ }
67
+ }
49
68
  }
69
+
50
70
  .hidden {
51
71
  display: none;
52
72
  }
@@ -1,6 +1,5 @@
1
1
  import { LitElement } from 'lit';
2
2
 
3
- import { LocalCache } from '@internetarchive/local-cache';
4
3
  import { URLHelper } from '../../config/url-helper.js';
5
4
  import ActionsHandlerService from './actions-handler-service.js';
6
5
  import * as Cookies from '../doc-cookies.js';
@@ -16,14 +15,7 @@ import * as Cookies from '../doc-cookies.js';
16
15
  export default class ActionsHandler extends LitElement {
17
16
  constructor() {
18
17
  super();
19
- this.identifier = '';
20
18
  this.ajaxTimeout = 6000;
21
- this.returnUrl = '';
22
-
23
- this.localCache = new LocalCache({
24
- namespace: '1HourLoanStorage',
25
- defaultTTL: 1 * 30, // 0.5 minute
26
- });
27
19
 
28
20
  this.bindEvents();
29
21
  }
@@ -36,9 +28,7 @@ export default class ActionsHandler extends LitElement {
36
28
  );
37
29
  }
38
30
 
39
- async bindEvents() {
40
- // console.log(await this.localCache.get('loanTime'));
41
-
31
+ bindEvents() {
42
32
  this.addEventListener('browseBook', ({ detail }) => {
43
33
  this.handleBrowseIt();
44
34
  this.setConsecutiveLoanCounts();
@@ -53,10 +43,8 @@ export default class ActionsHandler extends LitElement {
53
43
  this.sendEvent(category, action);
54
44
  });
55
45
 
56
- this.addEventListener('autoBrowseBook', ({ detail }) => {
57
- console.log('book has be renewed for you. thank you!!!');
58
- this.handleAutoBrowseIt();
59
- // this.setConsecutiveLoanCounts();
46
+ this.addEventListener('autoRenewLoan', ({ detail }) => {
47
+ this.handleAutoRenewLoan();
60
48
  const { category, action } = detail.event;
61
49
  this.sendEvent(category, action);
62
50
  });
@@ -115,7 +103,7 @@ export default class ActionsHandler extends LitElement {
115
103
  handleBrowseIt() {
116
104
  const action = 'browse_book';
117
105
  this.dispatchToggleActionGroup();
118
- this.setBrowseTimeSession()
106
+ this.setBrowseTimeSession();
119
107
 
120
108
  ActionsHandlerService({
121
109
  action,
@@ -129,9 +117,9 @@ export default class ActionsHandler extends LitElement {
129
117
  });
130
118
  }
131
119
 
132
- handleAutoBrowseIt() {
133
- const action = 'auto_browse';
134
- this.setBrowseTimeSession()
120
+ handleAutoRenewLoan() {
121
+ const action = 'renew_loan';
122
+ this.setBrowseTimeSession();
135
123
 
136
124
  ActionsHandlerService({
137
125
  action,
@@ -143,7 +131,6 @@ export default class ActionsHandler extends LitElement {
143
131
  });
144
132
  }
145
133
 
146
-
147
134
  handleReturnIt() {
148
135
  const action = 'return_loan';
149
136
  this.dispatchToggleActionGroup();
@@ -294,6 +281,24 @@ export default class ActionsHandler extends LitElement {
294
281
  }
295
282
  }
296
283
 
284
+ /**
285
+ * set browse time in indexedDB
286
+ */
287
+ async setBrowseTimeSession() {
288
+ try {
289
+ await this.localCache.set({
290
+ key: `${this.identifier}-loanTime`,
291
+ value: new Date(), // current time
292
+ ttl: Number(this.autoRenewConfig.totalTime),
293
+ });
294
+
295
+ // delete flip time
296
+ await this.localCache.delete(`${this.identifier}-pageFlipTime`);
297
+ } catch (error) {
298
+ console.log(error);
299
+ }
300
+ }
301
+
297
302
  deleteLoanCookies() {
298
303
  const date = new Date();
299
304
  date.setTime(date.getTime() - 24 * 60 * 60 * 1000); // one day ago
@@ -313,16 +318,4 @@ export default class ActionsHandler extends LitElement {
313
318
  '.archive.org'
314
319
  );
315
320
  }
316
-
317
- async setBrowseTimeSession() {
318
- try {
319
- // set a value
320
- await this.localCache.set({
321
- key: `${this.identifier}-loanTime`,
322
- value: new Date(), // current time
323
- })
324
-
325
- await this.localCache.delete(`${this.identifier}-pageFlipTime`);
326
- } catch (error) {}
327
- }
328
321
  }
@@ -1,134 +1,157 @@
1
1
  import { nothing } from 'lit';
2
2
 
3
- import ActionsHandlerService from './actions-handler/actions-handler-service.js';
4
- import * as Cookies from './doc-cookies.js';
5
-
6
3
  /**
7
- * This class is used to create loan token for borrowed books
4
+ * This class is used to determine if use is eligible for auto renew loan.
8
5
  *
9
- * ActionsHandlerService is a function being used to execute
10
6
  */
11
7
  export class OneHourAutoRenew {
12
- constructor(pageClick, identifier, localCache, autoRenewTimeConfig) {
13
- this.pageClick = pageClick;
8
+ constructor(
9
+ isPageClick,
10
+ identifier,
11
+ localCache,
12
+ autoRenewConfig
13
+ ) {
14
+ this.isPageClick = isPageClick;
14
15
  this.identifier = identifier;
15
16
  this.localCache = localCache;
16
- this.autoRenewTimeConfig = autoRenewTimeConfig; // callback function to be called after loan token is created
17
+ this.autoRenewConfig = autoRenewConfig;
17
18
 
18
- this.autoReturnMessage = 'This book will be automatically returned in 15 seconds unless you turn a page.';
19
+ // messages for auto return machenism
19
20
  this.autoRenewMessage = 'This book has been renewed for another hour.';
21
+ this.autoReturnMessage =
22
+ 'This book has been auto-returned due to inactivity.';
23
+ this.autoReturnWarning =
24
+ 'This book will be auto-returned in 10 minutes unless you turn a page.';
20
25
 
21
-
22
- this.resultRes = {
26
+ // private props
27
+ this.autoRenewResult = {
23
28
  toastTexts: '',
24
29
  hideToast: true,
30
+ browseAgainNow: false,
25
31
  };
26
32
 
27
- return this.dd();
33
+ return this.handleBookAutoRenew();
28
34
  }
29
35
 
30
- async dd() {
36
+ handleBookAutoRenew() {
31
37
  try {
32
- const currentDate = new Date();
38
+ // when user click/flip on book page
39
+ if (this.isPageClick) {
40
+ console.log('page flipped!!');
41
+ return this.userFlippedBookPage(); // user clicked on page
42
+ }
33
43
 
34
- const {
35
- totalTime, // 60
36
- autoRenewCheckerAtLast, // 50
37
- autoRenewVisitedInLast, // 15
38
- } = this.autoRenewTimeConfig;
44
+ return this.autoCheckerTimer(); // auto checker at 50th minute
45
+ } catch (error) {
46
+ console.log(error);
47
+ }
39
48
 
40
- const loanTime = await this.localCache.get(`${this.identifier}-loanTime`);
41
- const lastPageFlipTime = await this.localCache.get(`${this.identifier}-pageFlipTime`);
49
+ return nothing;
50
+ }
42
51
 
43
- const lastFlipTimeFrame = await this.changeTime(
44
- currentDate,
45
- autoRenewVisitedInLast,
46
- 'sub'
47
- ); // 15 seconds
52
+ // set new pageFlipTime in indexedDB
53
+ async setPageFlippedTime() {
54
+ await this.localCache.set({
55
+ key: `${this.identifier}-pageFlipTime`,
56
+ value: new Date(), // current time
57
+ ttl: Number(this.autoRenewConfig.totalTime),
58
+ });
59
+ }
48
60
 
49
- console.log(lastPageFlipTime, lastFlipTimeFrame)
50
-
51
- // if page is not flipped in last 15 minutes,
52
- // instantaly renew it if user viewed new page in last 10 minutes
53
- if (this.pageClick === true) {
54
- const lastTimeFrame = await this.changeTime(
55
- loanTime,
56
- totalTime - autoRenewCheckerAtLast,
57
- 'add'
58
- ); // 50 seconds
59
- console.log('page flipped!')
60
-
61
- if (currentDate >= lastTimeFrame) {
62
- console.log('1 YES NOW BORROW IT AGAIN NOW!');
63
- this.toastTexts = this.autoRenewMessage;
64
- this.hideToast = false;
65
- }
66
-
67
- return nothing // early return
68
- }
61
+ async userFlippedBookPage() {
62
+ const {
63
+ totalTime, // 60
64
+ autoCheckAt, // 50
65
+ } = this.autoRenewConfig;
66
+ const currentTime = new Date();
67
+ const loanTime = await this.localCache.get(`${this.identifier}-loanTime`);
68
+
69
+ const lastTimeFrame = this.changeTime(
70
+ loanTime,
71
+ totalTime - autoCheckAt,
72
+ 'add'
73
+ ); // 50 seconds
74
+
75
+ // if user viewed new page in last 10 minutes, renew immediately
76
+ if (currentTime >= lastTimeFrame) {
77
+ console.log('1 YES NOW BORROW IT AGAIN NOW!');
78
+ this.autoRenewResult.toastTexts = this.autoRenewMessage;
79
+ this.autoRenewResult.hideToast = false;
80
+ this.autoRenewResult.browseAgainNow = true;
81
+
82
+ this.setNewBrowseTime();
83
+ }
69
84
 
70
- console.log(lastPageFlipTime, lastFlipTimeFrame)
71
- if (lastPageFlipTime === undefined) { // not viewed
72
- console.log('2 DON\'T BORROW IT!');
73
- this.resultRes.toastTexts = this.autoReturnMessage;
74
- this.resultRes.hideToast = false;
75
- } else if (lastPageFlipTime >= lastFlipTimeFrame) { // viewed in last time frame
76
- console.log('3 YES BORROW IT AGAIN!');
77
- this.resultRes.toastTexts = this.autoRenewMessage;
78
- this.resultRes.hideToast = false;
79
- this.toastTexts = this.autoRenewMessage;
80
- // this.browseAgainNow = true;
81
- this.hideToast = false;
82
- // await this.browseHasRenew();
83
- } else if (lastPageFlipTime <= lastFlipTimeFrame) { // viewed but not in last time frame
84
- console.log('4 DON\'T BORROW IT!');
85
- this.resultRes.toastTexts = this.autoReturnMessage;
86
- this.resultRes.hideToast = false;
87
- }
85
+ this.setPageFlippedTime();
86
+ return this.autoRenewResult;
87
+ }
88
88
 
89
- // console.log(this.resultRes)
90
- return this.resultRes;
89
+ async autoCheckerTimer() {
90
+ const currentTime = new Date();
91
+ const { FlippedInLast } = this.autoRenewConfig;
92
+ const lastPageFlipTime = await this.localCache.get(
93
+ `${this.identifier}-pageFlipTime`
94
+ );
91
95
 
96
+ // last 15 min if used flipped a page.
97
+ const lastFlipTimeFrame = this.changeTime(
98
+ currentTime,
99
+ FlippedInLast,
100
+ 'sub'
101
+ ); // 15 seconds
102
+
103
+ if (
104
+ lastPageFlipTime === undefined ||
105
+ lastPageFlipTime <= lastFlipTimeFrame
106
+ ) {
107
+ // not viewed
108
+ console.log('Not viewed, warning message!');
109
+ this.autoRenewResult.toastTexts = this.autoReturnWarning;
110
+ this.autoRenewResult.browseAgainNow = false;
111
+ this.autoRenewResult.hideToast = false;
112
+ } else if (lastPageFlipTime >= lastFlipTimeFrame) {
113
+ // viewed in last time frame
114
+ console.log('Viewed, auto renew and message!');
115
+ this.autoRenewResult.toastTexts = this.autoRenewMessage;
116
+ this.autoRenewResult.browseAgainNow = true;
117
+ this.autoRenewResult.hideToast = false;
118
+
119
+ this.setNewBrowseTime();
120
+ }
121
+
122
+ return this.autoRenewResult;
123
+ }
124
+
125
+ async setNewBrowseTime() {
126
+ try {
127
+ // last 15 min if used flipped a page.
128
+ const expireDate = this.changeTime(
129
+ new Date(),
130
+ this.autoRenewConfig.totalTime,
131
+ 'add'
132
+ ); // 15 seconds
133
+
134
+ // set a value
135
+ await this.localCache.set({
136
+ key: `${this.identifier}-loanTime`,
137
+ value: expireDate, // expireDate time
138
+ ttl: Number(this.autoRenewConfig.totalTime),
139
+ });
140
+
141
+ // delete pageFlipTime when book is auto renew at nth minute
142
+ await this.localCache.delete(`${this.identifier}-pageFlipTime`);
92
143
  } catch (error) {
93
- console.log(error)
144
+ console.log(error);
94
145
  }
95
146
  }
96
147
 
97
- async changeTime(date, minutes, op) {
98
- if (date === undefined) return undefined;
99
-
148
+ changeTime(date, minutes, op) {
149
+ if (date === undefined) return nothing;
150
+
100
151
  if (op === 'sub') {
101
152
  return new Date(date.getTime() - minutes * 1000);
102
153
  }
103
154
 
104
155
  return new Date(date.getTime() + minutes * 1000);
105
156
  }
106
-
107
- sendEvent(eventCategory, eventAction) {
108
- window?.archive_analytics?.send_event(
109
- eventCategory,
110
- eventAction,
111
- `identifier=${this.identifier}`
112
- );
113
- }
114
-
115
- async handleAutoRenew() {
116
- const action = 'renew_loan';
117
- ActionsHandlerService({
118
- identifier: this.identifier,
119
- action,
120
- error: data => {
121
- window?.Sentry?.captureMessage(
122
- 'handleAutoRenew error',
123
- JSON.stringify(data)
124
- );
125
- // this.errorCallback({ detail: { action, data } });
126
- // send LendingServiceError to GA
127
- this.sendEvent('handleAutoRenew', action);
128
- },
129
- success: () => {
130
- return 'success';
131
- },
132
- });
133
- }
134
157
  }
@@ -12,8 +12,8 @@ import './components/info-icon.js';
12
12
  import './components/timer-countdown.js';
13
13
  import './components/toast-message.js';
14
14
 
15
- import { GetLendingActions } from './core/services/get-lending-actions.js';
16
15
  import { mobileContainerWidth } from './core/config/constants.js';
16
+ import { GetLendingActions } from './core/services/get-lending-actions.js';
17
17
  import { LoanTokenPoller } from './core/services/loan-token-poller.js';
18
18
  import { OneHourAutoRenew } from './core/services/one-hour-auto-renew.js';
19
19
 
@@ -36,17 +36,13 @@ export default class IABookActions extends LitElement {
36
36
  attribute: false,
37
37
  },
38
38
  barType: { type: String },
39
- autoRenew: { type: Boolean },
40
39
  sharedObserver: { attribute: false },
41
40
  disableActionGroup: { type: Boolean },
42
41
  modal: { Object },
43
42
  tokenDelay: { type: Number },
44
- toastTexts: { type: String },
45
- remainingTime: { type: Number },
46
- resetTimerCountdown: { type: Boolean },
47
43
  localCache: { type: Object },
48
- autoRenewTimeConfig: { type: Object },
49
- browseAgainNow: { type: Boolean },
44
+ autoRenewConfig: { type: Object },
45
+ autoRenewResult: { type: Object },
50
46
  };
51
47
  }
52
48
 
@@ -61,16 +57,21 @@ export default class IABookActions extends LitElement {
61
57
  this.bwbPurchaseUrl = '';
62
58
  this.lendingBarPostInit = () => {};
63
59
  this.barType = 'action'; // 'title'|'action'
64
- this.autoRenew = false;
65
60
  this.sharedObserver = undefined;
66
61
  this.disableActionGroup = false;
67
62
  this.modal = undefined;
68
63
  this.tokenDelay = 120000; // 2 minutes
69
64
  this.localCache = new LocalCache({
70
65
  namespace: '1HourLoanStorage',
71
- defaultTTL: 1 * 60, // 1 minute
72
- }),
73
- this.autoRenewTimeConfig = {},
66
+ });
67
+
68
+ // loan auto renew
69
+ this.autoRenewConfig = {};
70
+ this.autoRenewResult = {
71
+ toastTexts: '',
72
+ hideToast: true,
73
+ browseAgainNow: false,
74
+ };
74
75
 
75
76
  // private props
76
77
  this.primaryActions = [];
@@ -80,44 +81,9 @@ export default class IABookActions extends LitElement {
80
81
  this.lendingOptions = {};
81
82
  this.borrowType = ''; // (browsed|borrowed)
82
83
  this.consecutiveLoanCounts = 1; // consecutive loan count
83
- this.browseAgainNow = false;
84
84
  this.browserTimer = undefined;
85
85
 
86
- // show toast-message for auto return message
87
- this.hideToast = true;
88
- this.autoReturnMessage = 'This book will be automatically returned in 15 seconds unless you turn a page.';
89
- this.autoRenewMessage = 'This book has been renewed for another hour.';
90
-
91
- // dispatched this event from timer-countdown
92
- this.addEventListener('autoReturnWarning', event => {
93
- // const dd = this.autoRenew1 = new OneHourAutoRenew(
94
- // event.detail.bookPageChange,
95
- // this.identifier,
96
- // this.localCache,
97
- // this.autoRenewTimeConfig,
98
- // );
99
- // this.toastTexts = dd.resultRes.toastTexts;
100
- // this.hideToast = dd.resultRes.hideToast;
101
- this.handleBookAutoRenew(event);
102
- });
103
-
104
- // dispatched this event from bookreader side
105
- this.addEventListener('bookPageFlip', event => {
106
- // console.log(e)
107
- // const dd = this.autoRenew1 = new OneHourAutoRenew(
108
- // event.detail.bookPageChange,
109
- // this.identifier,
110
- // this.localCache,
111
- // this.autoRenewTimeConfig,
112
- // );
113
- // this.toastTexts = dd.resultRes.toastTexts;
114
- // this.hideToast = dd.resultRes.hideToast;
115
- // console.log(dd.resultRes)
116
- this.handleBookAutoRenew(event);
117
- // this.browseAgainNow = true;
118
- });
119
-
120
- this.resetTimerCountdown = false;
86
+ this.bindAutoRenewEvent();
121
87
  }
122
88
 
123
89
  disconnectedCallback() {
@@ -139,33 +105,62 @@ export default class IABookActions extends LitElement {
139
105
  this.update();
140
106
  }
141
107
 
142
- if (changed.has('autoRenew')) {
143
- this.resetTimerCountdown = true;
144
- }
145
108
  if (changed.has('sharedObserver')) {
146
109
  this.disconnectResizeObserver();
147
110
  this.setupResizeObserver();
148
111
  }
149
112
  }
150
113
 
114
+ bindAutoRenewEvent() {
115
+ // dispatched this event from timer-countdown
116
+ this.addEventListener('autoRenewAttempt', async event => {
117
+ const isPageClick = event.detail.bookPageChange;
118
+
119
+ // early return if user is not browsed book
120
+ if (isPageClick && this.borrowType !== 'browsed') return nothing;
121
+
122
+ this.autoRenewResult = await new OneHourAutoRenew(
123
+ isPageClick,
124
+ this.identifier,
125
+ this.localCache,
126
+ this.autoRenewConfig
127
+ );
128
+
129
+ this.toastTexts = this.autoRenewResult.toastTexts;
130
+ this.hideToast = this.autoRenewResult.hideToast;
131
+ this.browseAgainNow = this.autoRenewResult.browseAgainNow;
132
+
133
+ if (this.autoRenewResult.browseAgainNow) {
134
+ this.tokenPoller.disconnectedInterval();
135
+ await this.browseHasRenew();
136
+ }
137
+
138
+ return nothing;
139
+ });
140
+ }
141
+
151
142
  async browseHasExpired() {
152
143
  const currStatus = { ...this.lendingStatus, browsingExpired: true };
153
144
  this.lendingStatus = currStatus;
145
+ this.autoRenewResult.browseAgainNow = false;
154
146
 
155
147
  // remove respected key:value
156
148
  await this.localCache.delete(`${this.identifier}-loanTime`);
157
149
  await this.localCache.delete(`${this.identifier}-pageFlipTime`);
158
150
 
159
151
  // show message after browsed book is expired.
160
- this.toastTexts = 'This book has been automatically returned due to inactivity.';
152
+ this.autoRenewResult.toastTexts = 'This book has been auto-returned due to inactivity.';
161
153
  }
162
154
 
163
155
  async browseHasRenew() {
164
- const currStatus = {
156
+ const loanTime = await this.localCache.get(`${this.identifier}-loanTime`);
157
+ const secondsLeft = (loanTime - new Date()) / 1000; // different in seconds
158
+
159
+ const currStatus = {
165
160
  ...this.lendingStatus,
166
161
  user_has_browsed: true,
167
162
  browsingExpired: false,
168
- secondsLeftOnLoan: 61
163
+ secondsLeftOnLoan: secondsLeft,
169
164
  };
170
165
  this.lendingStatus = currStatus;
171
166
 
@@ -174,101 +169,22 @@ export default class IABookActions extends LitElement {
174
169
 
175
170
  startBrowseTimer() {
176
171
  clearTimeout(this.browserTimer);
172
+
177
173
  const {
178
174
  browsingExpired,
179
175
  user_has_browsed,
180
176
  secondsLeftOnLoan,
181
177
  } = this.lendingStatus;
182
- console.log(this.lendingStatus);
183
178
  if (!user_has_browsed || browsingExpired) {
184
- console.log('attempted renew');
185
179
  return;
186
180
  }
187
181
 
188
- // expired book and show browse again button
189
182
  const timeLeft = secondsLeftOnLoan * 1000;
190
183
  this.browserTimer = setTimeout(() => {
191
184
  this.browseHasExpired();
192
185
  }, timeLeft);
193
186
  }
194
187
 
195
- async handleBookAutoRenew(event) {
196
- try {
197
- const bookPageChange = event.detail.bookPageChange;
198
- const currentDate = new Date();
199
-
200
- const {
201
- totalTime, // 60
202
- autoRenewCheckerAtLast, // 50
203
- autoRenewVisitedInLast, // 15
204
- } = this.autoRenewTimeConfig;
205
-
206
- const loanTime = await this.localCache.get(`${this.identifier}-loanTime`);
207
- const lastPageFlipTime = await this.localCache.get(`${this.identifier}-pageFlipTime`);
208
-
209
- const lastFlipTimeFrame = await this.changeTime(
210
- currentDate,
211
- autoRenewVisitedInLast,
212
- 'sub'
213
- ); // 15 seconds
214
-
215
- console.log(lastPageFlipTime, lastFlipTimeFrame)
216
-
217
- // if page is not flipped in last 15 minutes,
218
- // instantaly renew it if user viewed new page in last 10 minutes
219
- if (bookPageChange === true) {
220
- const lastTimeFrame = await this.changeTime(
221
- loanTime,
222
- totalTime - autoRenewCheckerAtLast,
223
- 'add'
224
- ); // 50 seconds
225
- console.log('page flipped!')
226
-
227
- if (currentDate >= lastTimeFrame) {
228
- console.log('1 YES NOW BORROW IT AGAIN NOW!');
229
- this.toastTexts = this.autoRenewMessage;
230
- this.browseAgainNow = true;
231
- this.hideToast = false;
232
- await this.browseHasRenew();
233
- }
234
-
235
- return nothing // early return
236
- }
237
-
238
- console.log(lastPageFlipTime, lastFlipTimeFrame)
239
- if (lastPageFlipTime === undefined) { // not viewed
240
- console.log('2 DON\'T BORROW IT!');
241
- this.toastTexts = this.autoReturnMessage;
242
- this.hideToast = false;
243
- } else if (lastPageFlipTime >= lastFlipTimeFrame) { // viewed in last time frame
244
- console.log('3 YES BORROW IT AGAIN!');
245
- this.toastTexts = this.autoRenewMessage;
246
- this.browseAgainNow = true;
247
- this.hideToast = false;
248
- await this.browseHasRenew();
249
- } else if (lastPageFlipTime <= lastFlipTimeFrame) { // viewed but not in last time frame
250
- console.log('4 DON\'T BORROW IT!');
251
- this.toastTexts = this.autoReturnMessage;
252
- this.hideToast = false;
253
- } else {
254
- this.browseAgainNow = true;
255
- console.log('5 YES NOW BORROW IT AGAIN NOW!');
256
- }
257
- } catch (error) {
258
- console.log(error)
259
- }
260
- }
261
-
262
- async changeTime(date, minutes, op) {
263
- if (date === undefined) return undefined;
264
-
265
- if (op === 'sub') {
266
- return new Date(date.getTime() - minutes * 1000);
267
- }
268
-
269
- return new Date(date.getTime() + minutes * 1000);
270
- }
271
-
272
188
  /** SharedObserver resize handler */
273
189
  handleResize(entry) {
274
190
  // if you are observing multiple targets,
@@ -322,13 +238,16 @@ export default class IABookActions extends LitElement {
322
238
  });
323
239
 
324
240
  this.borrowType = actions.borrowType;
325
- this.remainingTime = this.lendingStatus.secondsLeftOnLoan;
326
241
 
327
242
  const hasExpired =
328
243
  'browsingExpired' in this.lendingStatus &&
329
244
  this.lendingStatus?.browsingExpired;
330
245
  if (hasExpired) {
331
- window?.Sentry?.captureMessage('setupLendingToolbarActions hasExpired');
246
+ if (!this.tokenPoller.disconnectedInterval) {
247
+ window?.Sentry?.captureMessage(
248
+ 'setupLendingToolbarActions hasExpired - no tokenPoller'
249
+ );
250
+ }
332
251
  this.tokenPoller?.disconnectedInterval();
333
252
  /** Global event - always fire */
334
253
  this.dispatchEvent(
@@ -353,7 +272,13 @@ export default class IABookActions extends LitElement {
353
272
  return;
354
273
  }
355
274
 
356
- this.startLoanTokenPoller();
275
+ // if (book is about to renew)
276
+ // - start token to poller around this.tokenDelay time until renew is completed
277
+ // else
278
+ // - start token poller immediately
279
+ setTimeout(() => {
280
+ this.startLoanTokenPoller();
281
+ }, this.autoRenewResult.browseAgainNow ? this.tokenDelay : 100);
357
282
  }
358
283
 
359
284
  render() {
@@ -378,9 +303,8 @@ export default class IABookActions extends LitElement {
378
303
  get timerCountdownTemplate() {
379
304
  return this.borrowType === 'browsed'
380
305
  ? html`<timer-countdown
381
- time=${Number(this.remainingTime)}
382
- .autoRenewCheckerAtLast=${this.autoRenewTimeConfig.autoRenewCheckerAtLast}
383
- ?resetTimerCountdown=${this.resetTimerCountdown}
306
+ time=${Number(this.lendingStatus.secondsLeftOnLoan)}
307
+ .autoCheckAt=${this.autoRenewConfig.autoCheckAt}
384
308
  ></timer-countdown>`
385
309
  : nothing;
386
310
  }
@@ -388,15 +312,16 @@ export default class IABookActions extends LitElement {
388
312
  get toastMessageTemplate() {
389
313
  return html`
390
314
  <toast-message
391
- .texts=${this.toastTexts}
392
- .dismisOnClick="1"
393
- ?hideToast=${this.hideToast}
315
+ .texts=${this.autoRenewResult.toastTexts}
316
+ ?dismisOnClick=${Boolean(true)}
317
+ ?hideToast=${this.autoRenewResult.hideToast}
394
318
  ></toast-message>
395
319
  `;
396
320
  }
397
321
 
398
322
  get bookActionBar() {
399
323
  return html`
324
+ <button @click=${() => this.pageFlipEvent()}>Page Flip</button>
400
325
  <collapsible-action-group
401
326
  .userid=${this.userid}
402
327
  .identifier=${this.identifier}
@@ -406,10 +331,11 @@ export default class IABookActions extends LitElement {
406
331
  .width=${this.width}
407
332
  .borrowType=${this.borrowType}
408
333
  .returnUrl=${this.returnUrl}
409
- .autoRenew=${this.autoRenew}
334
+ .localCache=${this.localCache}
335
+ .autoRenewConfig=${this.autoRenewConfig}
410
336
  ?hasAdminAccess=${this.hasAdminAccess}
411
337
  ?disabled=${this.disableActionGroup}
412
- ?browseAgainNow=${this.browseAgainNow}
338
+ ?browseAgainNow=${this.autoRenewResult.browseAgainNow}
413
339
  @lendingActionError=${this.handleLendingActionError}
414
340
  @toggleActionGroup=${this.handleToggleActionGroup}
415
341
  >
@@ -419,6 +345,18 @@ export default class IABookActions extends LitElement {
419
345
  `;
420
346
  }
421
347
 
348
+ // will move this function to bookreader...
349
+ pageFlipEvent() {
350
+ // dispatch event to say book page has flipped
351
+ document.querySelector('ia-book-actions').dispatchEvent(
352
+ new CustomEvent('autoRenewAttempt', {
353
+ detail: { bookPageChange: true },
354
+ bubbles: true,
355
+ composed: true,
356
+ })
357
+ );
358
+ }
359
+
422
360
  /**
423
361
  * enable access of borrowed/browsed books
424
362
  */
@@ -585,7 +523,7 @@ export default class IABookActions extends LitElement {
585
523
  }
586
524
 
587
525
  toast-message {
588
- --messageHeightFromTop: 18%;
526
+ --messageHeightFromTop: 17%;
589
527
  }
590
528
  `;
591
529
  }
@@ -1,58 +0,0 @@
1
- import { css } from 'lit';
2
-
3
- export default timerCountDownStyle = css`
4
- :host {
5
- right: 5px;
6
- font-size: 14px;
7
- position: absolute;
8
- }
9
- #countdown1 {
10
- position: relative;
11
- margin: auto;
12
- margin-top: 100px;
13
- height: 40px;
14
- width: 40px;
15
- text-align: center;
16
- }
17
-
18
- #countdown-number {
19
- color: white;
20
- display: inline-block;
21
- line-height: 40px;
22
- margin-right: 12px;
23
- }
24
-
25
- svg {
26
- position: absolute;
27
- top: 0;
28
- right: 0;
29
- width: 40px;
30
- height: 40px;
31
- transform: rotateY(-180deg) rotateZ(-90deg);
32
- }
33
-
34
- svg circle {
35
- stroke-dasharray: 113px;
36
- stroke-dashoffset: 0px;
37
- stroke-linecap: round;
38
- stroke-width: 3px;
39
- stroke: white;
40
- fill: none;
41
- animation: countdown 60s linear infinite forwards;
42
- }
43
-
44
- @keyframes countdown {
45
- from {
46
- stroke-dashoffset: 0px;
47
- }
48
- to {
49
- stroke-dashoffset: 113px;
50
- }
51
- }
52
- .hidden {
53
- display: none;
54
- }
55
- .visible {
56
- display: inline-block;
57
- }
58
- `;