@internetarchive/ia-book-actions 0.1.11-b4 → 0.1.11-b40

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
@@ -131,11 +131,11 @@
131
131
  iaBookActions.returnUrl = '';
132
132
  iaBookActions.localCache = localCache;
133
133
 
134
- let loanTotalTime2Min = { loanTotalTime: 65, loanRenewAtLast: 60, pageChangedInLast: 15 };
134
+ let loanTotalTime2Min = { loanTotalTime: 65, loanRenewAtLast: 60, pageChangedInLast: 30 };
135
135
  let loanTotalTime5Min = { loanTotalTime: 300, loanRenewAtLast: 180, pageChangedInLast: 15 };
136
136
  let loanTotalTime1Hour = { loanTotalTime: 3600, loanRenewAtLast: 660, pageChangedInLast: 900 };
137
137
 
138
- iaBookActions.loanRenewTimeConfig = loanTotalTime2Min;
138
+ iaBookActions.loanRenewTimeConfig = loanTotalTime5Min;
139
139
 
140
140
  let lendingStatus1 = iaBookActions.lendingStatus;
141
141
  let secondsLeftOnLoan = iaBookActions.loanRenewTimeConfig.loanTotalTime;
@@ -242,6 +242,31 @@
242
242
  });
243
243
  });
244
244
 
245
+ document.querySelector('#show_warning_modal').addEventListener('click', async () => {
246
+ if (!iaBookActions.lendingStatus.user_has_browsed) {
247
+ const newLendingStatus = { ...iaBookActions.lendingStatus, user_has_browsed: true };
248
+ iaBookActions.lendingStatus = newLendingStatus;
249
+ await iaBookActions.updateComplete;
250
+ }
251
+
252
+ iaBookActions.showWarningModal();
253
+
254
+ if (iaBookActions.lendingStatus.user_has_browsed) {
255
+ iaBookActions.showWarningModal();
256
+ }
257
+ });
258
+
259
+ document.querySelector('#show_expired_modal').addEventListener('click', async () => {
260
+ if (!iaBookActions.lendingStatus.user_has_browsed) {
261
+ const newLendingStatus = { ...iaBookActions.lendingStatus, user_has_browsed: true };
262
+ iaBookActions.lendingStatus = newLendingStatus;
263
+ await iaBookActions.updateComplete;
264
+ }
265
+ await new Promise(resolve => setTimeout(resolve, 5000));
266
+
267
+ iaBookActions.browseHasExpired();
268
+ });
269
+
245
270
  document.querySelectorAll('.availableToBorrow input[type=radio]').forEach((item) => {
246
271
  item.addEventListener('click', (e) => {
247
272
  if (e.target.value === 'available_to_borrow') {
@@ -334,6 +359,13 @@
334
359
  <label><input type="radio" name="browse" value="available_to_browse" />available_to_browse</label>
335
360
  <label><input type="radio" name="browse" value="user_has_browsed" />user_has_browsed</label>
336
361
  <label><input type="radio" name="browse" value="browsingExpired" />browsingExpired</label>
362
+ <div>
363
+ <button id="show_warning_modal">When Browsing (`user_has_browsed`), show Warning Modal</button>
364
+ </div>
365
+ <div>
366
+ <button id="show_expired_modal">While Browsing (`user_has_browsed`), Expire book & show its modal</button>
367
+ how_expired_modal
368
+ </div>
337
369
  </td>
338
370
  <td class="availableToBorrow">
339
371
  <label><input type="radio" name="borrow" value="available_to_borrow" />available_to_borrow</label>
@@ -356,5 +388,6 @@
356
388
  </table>
357
389
  </fieldset>
358
390
  </div>
391
+ <modal-manager></modal-manager>
359
392
  </body>
360
393
  </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@internetarchive/ia-book-actions",
3
- "version": "0.1.11-b4",
3
+ "version": "0.1.11-b40",
4
4
  "description": "Bookreader actions for borrowable book",
5
5
  "author": "Internet Archive",
6
6
  "license": "AGPL-3.0-only",
@@ -18,7 +18,6 @@
18
18
  "test:watch": "web-test-runner --watch --playwright --browsers chromium firefox webkit"
19
19
  },
20
20
  "dependencies": {
21
- "@internetarchive/ia-activity-indicator": "^0.0.4",
22
21
  "@internetarchive/icon-info": "^1.2.6",
23
22
  "@internetarchive/local-cache": "^0.2.1",
24
23
  "@internetarchive/modal-manager": "^0.2.12",
@@ -3,32 +3,43 @@ import { html, css, LitElement } from 'lit';
3
3
  export default class TimerCountdown extends LitElement {
4
4
  static get properties() {
5
5
  return {
6
- secondsLeftOnLoan: { type: Number }, // in seconds
6
+ secondsLeftOnLoan: { type: Number },
7
+ displayTime: { type: Boolean },
7
8
  };
8
9
  }
9
10
 
10
11
  constructor() {
11
12
  super();
12
13
 
13
- /**
14
- * seconds left in current loan
15
- * @type {number}
16
- */
17
14
  this.secondsLeftOnLoan = 0;
15
+ this.displayTime = false;
16
+ }
17
+
18
+ get minutesLeftOnLoan() {
19
+ let timeLeft = Math.round(this.secondsLeftOnLoan);
20
+
21
+ // convert time from second to minute
22
+ timeLeft = Math.ceil(timeLeft / 60);
23
+
24
+ if (timeLeft < 10) {
25
+ timeLeft = `0:0${timeLeft}`;
26
+ } else if (timeLeft === 60) {
27
+ timeLeft = `1:00`;
28
+ } else {
29
+ timeLeft = `0:${timeLeft}`;
30
+ }
31
+ return timeLeft;
18
32
  }
19
33
 
20
34
  /**
21
35
  * get remaining time with timeunit
22
36
  *
23
37
  * @memberof TimerCountdown
24
- * @return string
38
+ * @return string - seconds left
25
39
  */
26
40
  get remainingTime() {
27
41
  const unitOfTime = 'minute';
28
- let timeLeft = Math.round(this.secondsLeftOnLoan);
29
-
30
- // convert time from second to minute
31
- timeLeft = Math.ceil(timeLeft / 60);
42
+ const timeLeft = this.minutesLeftOnLoan;
32
43
 
33
44
  return timeLeft !== 1
34
45
  ? `${timeLeft} ${unitOfTime}s`
@@ -36,58 +47,31 @@ export default class TimerCountdown extends LitElement {
36
47
  }
37
48
 
38
49
  render() {
50
+ console.log('~~~~~~~');
51
+ console.log('render -- timer-countdown', this.secondsLeftOnLoan);
52
+ console.log('~~~~~~~');
53
+ const viewClass = this.displayTime ? 'view' : 'hide';
39
54
  return html`
40
- <svg
41
- width="14"
42
- height="14"
43
- viewBox="0 0 100 100"
44
- xmlns="http://www.w3.org/2000/svg"
55
+ <button
56
+ id="timer-counter"
57
+ class=${viewClass}
58
+ @click=${() => {
59
+ this.displayTime = !this.displayTime;
60
+ }}
61
+ role="timer"
45
62
  >
46
- <circle class="circle" cx="50" cy="50" r="50" />
47
- </svg>
48
- <span class="sr-only">${this.remainingTime} left</span>
63
+ <span>${this.minutesLeftOnLoan}</span>
64
+ <span class="sr-only">${this.remainingTime} left</span>
65
+ </button>
49
66
  `;
50
67
  }
51
68
 
52
69
  static get styles() {
53
- const white = css`var(--white, #fff)`;
54
- const timerSecondsLeft = css`var(--secondsLeftOnLoan, 6000s)`; //
55
- const timerStrokeLeft = css`var(--strokeLeftOnLoan, 315)`;
56
-
57
70
  return css`
58
71
  :host {
59
72
  right: 0;
60
73
  margin-right: 10px;
61
74
  position: absolute;
62
- height: 20px;
63
- width: 20px;
64
- }
65
-
66
- @keyframes circletimer {
67
- 0% {
68
- stroke-dashoffset: ${timerStrokeLeft};
69
- stroke-dasharray: 315;
70
- }
71
- 100% {
72
- stroke-dashoffset: 0;
73
- stroke-dasharray: 315;
74
- }
75
- }
76
-
77
- svg {
78
- background-color: ${white};
79
- border-radius: 50px;
80
- border: 2px solid ${white};
81
- transform: rotateZ(-90deg);
82
- }
83
-
84
- svg .circle {
85
- stroke: #000;
86
- stroke-width: 100px;
87
- fill: ${white};
88
- stroke-dashoffset: 315;
89
- stroke-dasharray: 0;
90
- animation: ${timerSecondsLeft} circletimer linear;
91
75
  }
92
76
 
93
77
  .sr-only {
@@ -100,6 +84,18 @@ export default class TimerCountdown extends LitElement {
100
84
  border: none;
101
85
  overflow: hidden;
102
86
  }
87
+
88
+ button#timer-counter {
89
+ cursor: pointer;
90
+ }
91
+
92
+ .hide {
93
+ opacity: 0;
94
+ }
95
+
96
+ .show {
97
+ opacity: 1;
98
+ }
103
99
  `;
104
100
  }
105
101
  }
@@ -21,7 +21,13 @@ export default async function ActionsHandlerService(options) {
21
21
  const tokenError = 'loan token not found. please try again later.';
22
22
  const borrowError =
23
23
  'This book is not available to borrow at this time. Please try again later.';
24
- const erroneousActions = ['browse_book', 'borrow_book', 'create_token'];
24
+ const erroneousActions = [
25
+ 'browse_book',
26
+ 'borrow_book',
27
+ 'create_token',
28
+ 'renew_loan',
29
+ 'return_loan',
30
+ ];
25
31
  const shouldReturnError =
26
32
  location?.href?.indexOf('?error=true') !== -1 &&
27
33
  location?.hostname !== 'archive.org';
@@ -36,7 +42,7 @@ export default async function ActionsHandlerService(options) {
36
42
  method: 'POST',
37
43
  body: formData,
38
44
  })
39
- .then(response => {
45
+ .then(async response => {
40
46
  // intentional error on localhost
41
47
  if (shouldReturnError && erroneousActions.includes(option?.action)) {
42
48
  return {
@@ -47,7 +53,12 @@ export default async function ActionsHandlerService(options) {
47
53
 
48
54
  // return success response for /demo/ server...
49
55
  if (baseHost == '/demo/1' || baseHost == '/demo/') {
50
- if (option?.action == 'renew_loan') {
56
+ if (
57
+ option?.action == 'renew_loan' ||
58
+ option?.action == 'return_loan'
59
+ ) {
60
+ // wait a few seconds so that the user can see the loading state
61
+ await new Promise(resolve => setTimeout(resolve, 5000));
51
62
  return {
52
63
  success: true,
53
64
  loan: { renewal: true },
@@ -2,6 +2,7 @@ import { LitElement } from 'lit';
2
2
 
3
3
  import { URLHelper } from '../../config/url-helper.js';
4
4
  import { sentryLogs } from '../../config/sentry-events.js';
5
+ import log from '../log.js';
5
6
 
6
7
  import ActionsHandlerService from './actions-handler-service.js';
7
8
  import LoanAnanlytics from '../loan-analytics.js';
@@ -142,7 +143,7 @@ export default class ActionsHandler extends LitElement {
142
143
  action,
143
144
  identifier: this.identifier,
144
145
  success: data => {
145
- console.log("RENEW_LOAN --- ", data, action, data.loan, this.identifier);
146
+ log('RENEW_LOAN --- ', data, action, data.loan, this.identifier);
146
147
  if (data?.success === true && data?.loan?.renewal === true) {
147
148
  this.setBrowseTimeSession();
148
149
  this.dispatchEvent(
@@ -316,7 +317,7 @@ export default class ActionsHandler extends LitElement {
316
317
  // delete pageChangedTime when book is auto renew at nth minute
317
318
  await this.localCache.delete(`${this.identifier}-pageChangedTime`);
318
319
  } catch (error) {
319
- console.log(error);
320
+ log(error);
320
321
  }
321
322
  }
322
323
 
@@ -40,7 +40,7 @@ import ActionsConfig from './actions-config.js';
40
40
  * @enum {string}
41
41
  */
42
42
  export const bookTitles = {
43
- available_1hr: 'Renews automatically as long as pages are turned.',
43
+ available_1hr: 'Renews automatically with continued use.',
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: 'Renews automatically as long as pages are turned.',
52
+ session_expired: 'Renews automatically with continued use.',
53
53
  unavailable: 'This book is not available at this time.',
54
54
  };
55
55
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { analyticsCategories } from '../config/analytics-event-and-category.js';
4
4
  import * as Cookies from './doc-cookies.js';
5
+ import log from './log.js';
5
6
 
6
7
  /**
7
8
  * This class is used to send different GA events for loan system.
@@ -45,7 +46,7 @@ export default class LoanAnanlytics {
45
46
  '/'
46
47
  );
47
48
  } catch (error) {
48
- console.log(error);
49
+ log(error);
49
50
  this.sendEvent('Cookies-Error-Actions', error, this.identifier);
50
51
  }
51
52
  }
@@ -144,7 +145,7 @@ export default class LoanAnanlytics {
144
145
  * @memberof LoanAnanlytics
145
146
  */
146
147
  sendEvent(eventCategory, eventAction, label, extraParams) {
147
- // console.log(
148
+ // log(
148
149
  // 'eventCategory:-',
149
150
  // eventCategory,
150
151
  // '\t||\teventAction:-',
@@ -1,6 +1,7 @@
1
1
  /* eslint-disable class-methods-use-this */
2
2
 
3
3
  import { nothing } from 'lit';
4
+ import log from './log.js';
4
5
 
5
6
  /**
6
7
  * This class is used to determine if use is eligible for auto renew loan.
@@ -32,7 +33,7 @@ export class LoanRenewHelper {
32
33
  }
33
34
  return this.autoChecker(); // auto checker at last 10th minute
34
35
  } catch (error) {
35
- console.log(error);
36
+ log(error);
36
37
  }
37
38
 
38
39
  return nothing;
@@ -0,0 +1,16 @@
1
+ /* eslint-disable no-restricted-globals */
2
+ /* eslint-disable semi */
3
+ /**
4
+ * log() utility -- production ignores 'log()' JS calls; dev invokes 'log()'
5
+ */
6
+ const log =
7
+ location.hostname === 'localhost' ||
8
+ location.host.match(/^(www|cat)-[a-z0-9]+\.archive\.org$/) ||
9
+ location.host.match(/\.code\.archive\.org$/) ||
10
+ location.host.match(/\.dev\.archive\.org$/) ||
11
+ location.host.match(/^ia-petabox-/)
12
+ ? // eslint-disable-next-line no-console
13
+ console.log.bind(console) // convenient, no? Stateless function
14
+ : () => {};
15
+
16
+ export { log as default };
@@ -19,6 +19,7 @@ import { mobileContainerWidth } from './core/config/constants.js';
19
19
  import { sentryLogs } from './core/config/sentry-events.js';
20
20
  import { LoanTokenPoller } from './core/services/loan-token-poller.js';
21
21
  import { LoanRenewHelper } from './core/services/loan-renew-helper.js';
22
+ import log from './core/services/log.js';
22
23
 
23
24
  export const events = {
24
25
  browseExpired: 'IABookReader:BrowsingHasExpired',
@@ -77,7 +78,6 @@ export default class IABookActions extends LitElement {
77
78
  this.barType = 'action'; // 'title'|'action'
78
79
  this.sharedObserver = undefined;
79
80
  this.disableActionGroup = false;
80
- this.modal = undefined;
81
81
  this.tokenDelay = 120; // in seconds
82
82
 
83
83
  // private props
@@ -105,7 +105,7 @@ export default class IABookActions extends LitElement {
105
105
  * loanTotalTime: number, - total seconds a loan does have
106
106
  * loanRenewAtLast: number, - check for loan renew at last
107
107
  * pageChangedInLast: number, - consider loan renew eligible if viewed new page
108
- */
108
+ */
109
109
  this.loanRenewTimeConfig = {
110
110
  loanTotalTime: 3600, // 1 hour
111
111
  loanRenewAtLast: 660, // 11 minutes
@@ -121,12 +121,6 @@ export default class IABookActions extends LitElement {
121
121
  * @property {number} secondsLeft - seconds left in active loan
122
122
  */
123
123
  this.loanRenewResult = { texts: '', renewNow: false, secondsLeft: 0 };
124
-
125
- /**
126
- * need showdowRoot opened for resetTimerCountState
127
- * @see IABookActions::resetTimerCountState
128
- */
129
- this._shadowRoot = this.attachShadow({ mode: 'open' });
130
124
  }
131
125
 
132
126
  disconnectedCallback() {
@@ -254,14 +248,6 @@ export default class IABookActions extends LitElement {
254
248
  return;
255
249
  }
256
250
 
257
- // initial timer state for 1-hour loan
258
- if (this.borrowType === 'browsed' && !this.loanRenewResult.renewNow) {
259
- setTimeout(() => {
260
- console.log('IN TIMEOUT OF --- setupLendingToolbarActions, firing resetTimerCountState');
261
- this.resetTimerCountState();
262
- }, 100);
263
- }
264
-
265
251
  /**
266
252
  * tokenPoller determines if user has loan token for this book
267
253
  * - if book is going to renew, need to wait until renew is completed
@@ -282,6 +268,14 @@ export default class IABookActions extends LitElement {
282
268
  * dispatched this event from bookreader page changed
283
269
  */
284
270
  window.addEventListener('BookReader:userAction', () => {
271
+ log('IABookActions:BookReader:userAction');
272
+ if (this.borrowType === 'browsed') {
273
+ this.autoLoanRenewChecker(true);
274
+ }
275
+ });
276
+
277
+ window.focus(() => {
278
+ log('WINDOW FOCUS', this.borrowType);
285
279
  if (this.borrowType === 'browsed') {
286
280
  this.autoLoanRenewChecker(true);
287
281
  }
@@ -306,27 +300,23 @@ export default class IABookActions extends LitElement {
306
300
  this.loanRenewResult = this.loanRenewHelper.result;
307
301
  }
308
302
 
309
- /**
310
- * create / select the modal-manager component on DOM
311
- * @memberof IABookActions
312
- */
313
- async useModalManager() {
314
- if (!this.modal) {
315
- this.modal = document.querySelector('modal-manager');
303
+ /** @returns HTMLElement */
304
+ get modal() {
305
+ const modalOnDom = document.querySelector('modal-manager');
316
306
 
317
- if (!this.modal) this.modal = document.createElement('modal-manager');
318
- }
319
-
320
- this.modal.id = 'action-bar-modal';
321
- await document.body.appendChild(this.modal);
307
+ modalOnDom.id = 'action-bar-modal';
308
+ return modalOnDom;
322
309
  }
323
310
 
324
311
  /**
325
312
  * Show the waring modal to ask user if they are still reading
326
313
  */
327
314
  async showWarningModal() {
328
- console.log('****** showWarningModal ******');
329
- await this.useModalManager();
315
+ log('****** showWarningModal ******');
316
+ // clear modal
317
+ this.modal.customModalContent = nothing;
318
+ this.modal?.closeModal();
319
+ this.loanRenewResult = { texts: '', renewNow: false };
330
320
 
331
321
  // if secondsLeft < 60, consider it 1 minute
332
322
  let { secondsLeft } = this.loanRenewResult;
@@ -336,76 +326,130 @@ export default class IABookActions extends LitElement {
336
326
  secondsLeft = secondsLeft > 60 ? secondsLeft : 60;
337
327
  }
338
328
 
339
- const config = new ModalConfig();
340
- config.headline = 'Are you still here?';
341
- config.showCloseButton = false;
342
- config.closeOnBackdropClick = false;
343
- config.message = this.loanRenewHelper?.getMessageTexts(
344
- this.loanRenewResult.texts,
345
- secondsLeft
346
- );
329
+ const config = new ModalConfig({
330
+ headline: 'Are you still reading?',
331
+ headerColor: '#194880',
332
+ showCloseButton: false,
333
+ closeOnBackdropClick: false,
334
+ message: this.loanRenewHelper?.getMessageTexts(
335
+ this.loanRenewResult.texts,
336
+ secondsLeft
337
+ ),
338
+ });
347
339
 
348
- const customContent = html`<br />
349
- <div id="custom-buttons" style="display:flex;justify-content:center;">
340
+ const customModalContent = html`<br />
341
+ <div
342
+ id="book-action-bar-custom-buttons"
343
+ style="display:flex;justify-content:center;"
344
+ >
350
345
  <button
351
346
  style="${modalButtonStyle.iaButton} ${modalButtonStyle.renew}"
352
- @click=${async event => {
353
- await this.changeModalState(event);
354
- this.loanRenewResult = { texts: '', renewNow: true };
355
- }}
347
+ @click=${() => this.patronWantsToRenewBook()}
356
348
  >
357
349
  Keep reading
358
350
  </button>
359
351
  <button
360
352
  style="${modalButtonStyle.iaButton} ${modalButtonStyle.return}"
361
- @click=${event => {
362
- this.changeModalState(event);
363
- document.querySelector('ia-book-actions').disableActionGroup = true;
364
- this.returnNow = true;
365
- }}
353
+ @click=${() => this.patronWantsToReturnBook()}
366
354
  >
367
355
  Return the book
368
356
  </button>
369
357
  </div> `;
370
358
 
371
- this.modal?.showModal({ config, customModalContent: customContent });
359
+ this.modal.setAttribute('aria-live', 'assertive');
360
+ await this.modal?.showModal({ config, customModalContent });
372
361
  }
373
362
 
374
- /**
375
- * helper function to change button click states on modal-manager
376
- * - add loader on button click
377
- * - change button appearance
378
- *
379
- * @param {Event} event
380
- */
381
- changeModalState(event) {
382
- if (event.target === undefined) return;
363
+ /** @param { 'renewBook' | 'returnBook' } buttonToDisable */
364
+ async showWarningDisabledModal(buttonToDisable = 'renewBook') {
365
+ // if secondsLeft < 60, consider it 1 minute
366
+ let { secondsLeft } = this.loanRenewResult;
367
+ if (secondsLeft === undefined) {
368
+ secondsLeft = this.lendingStatus.secondsLeftOnLoan;
369
+ } else {
370
+ secondsLeft = secondsLeft > 60 ? secondsLeft : 60;
371
+ }
372
+
373
+ const config = new ModalConfig({
374
+ headline: 'Are you still reading?',
375
+ headerColor: '#194880',
376
+ showCloseButton: false,
377
+ closeOnBackdropClick: false,
378
+ message: this.loanRenewHelper?.getMessageTexts(
379
+ this.loanRenewResult.texts,
380
+ secondsLeft
381
+ ),
382
+ });
383
+
384
+ const customModalContent = html`<br />
385
+ <div
386
+ id="disabled-book-action-bar-custom-buttons"
387
+ style="display:flex;justify-content:center; opacity:0.8; pointer-events:none;"
388
+ >
389
+ <button
390
+ disabled
391
+ style="${modalButtonStyle.iaButton} ${modalButtonStyle.renew}"
392
+ >
393
+ ${buttonToDisable === 'renewBook'
394
+ ? html`<ia-activity-indicator
395
+ mode="processing"
396
+ style=${modalButtonStyle.loaderIcon}
397
+ ></ia-activity-indicator>`
398
+ : 'Keep reading'}
399
+ </button>
400
+ <span
401
+ style="position: absolute; visibility: none; height: 1px; width: 1px; overflow: hidden;"
402
+ >Renewing loan, one moment please.</span
403
+ >
404
+ <button
405
+ disabled
406
+ style="${modalButtonStyle.iaButton} ${modalButtonStyle.return}"
407
+ >
408
+ ${buttonToDisable === 'returnBook'
409
+ ? html`<ia-activity-indicator
410
+ mode="processing"
411
+ style=${modalButtonStyle.loaderIcon}
412
+ ></ia-activity-indicator>`
413
+ : 'Return the book'}
414
+ </button>
415
+ </div> `;
416
+
417
+ await this.modal?.showModal({ config, customModalContent });
418
+ }
383
419
 
384
- const button = event.target;
385
- button.innerHTML = `<ia-activity-indicator
386
- mode="processing"
387
- style=${modalButtonStyle.loaderIcon}
388
- ></ia-activity-indicator>`;
420
+ /** Handles Renew action in warning modal */
421
+ async patronWantsToRenewBook() {
422
+ this.showWarningDisabledModal();
423
+ this.loanRenewResult = { texts: '', renewNow: true };
424
+ }
425
+
426
+ async patronWantsToReturnBook() {
427
+ this.showWarningDisabledModal('returnBook');
428
+ document.querySelector('ia-book-actions').disableActionGroup = true;
429
+ this.returnNow = true;
389
430
 
390
- const parentElement = event.target.parentNode;
391
- parentElement.style.pointerEvents = 'none';
392
- parentElement.style.opacity = 0.8;
431
+ this.lendingStatus = {
432
+ ...this.lendingStatus,
433
+ browsingExpired: true,
434
+ secondsLeftOnLoan: 0,
435
+ };
393
436
  }
394
437
 
395
438
  /**
396
439
  * Show modal when book is auto returned
397
440
  */
398
441
  async showExpiredModal() {
399
- console.log('showExpiredModal()');
400
- await this.useModalManager();
401
-
402
- const config = new ModalConfig();
403
- config.headline = '';
404
- config.showCloseButton = false;
405
- config.closeOnBackdropClick = false;
406
- config.message = 'This book has been returned due to inactivity.';
442
+ log('showExpiredModal()');
443
+
444
+ const config = new ModalConfig({
445
+ headline: '',
446
+ showCloseButton: false,
447
+ closeOnBackdropClick: false,
448
+ headerColor: '#194880',
449
+ message: 'This book has been returned due to inactivity.',
450
+ });
407
451
 
408
- const customContent = html`<br />
452
+ const customModalContent = html`<br />
409
453
  <div style="text-align: center">
410
454
  <button
411
455
  style="${modalButtonStyle.iaButton} ${modalButtonStyle.renew}"
@@ -417,15 +461,7 @@ export default class IABookActions extends LitElement {
417
461
  </button>
418
462
  </div> `;
419
463
 
420
- this.modal?.showModal({ config, customModalContent: customContent });
421
- }
422
-
423
- /**
424
- * close/hide modal-manager
425
- */
426
- async closeModalManager() {
427
- this.modal = document.querySelector('modal-manager');
428
- this.modal?.closeModal();
464
+ await this.modal?.showModal({ config, customModalContent });
429
465
  }
430
466
 
431
467
  /**
@@ -489,7 +525,11 @@ export default class IABookActions extends LitElement {
489
525
  async browseHasExpired() {
490
526
  window?.IALendingIntervals?.clearAll();
491
527
 
492
- const currStatus = { ...this.lendingStatus, browsingExpired: true };
528
+ const currStatus = {
529
+ ...this.lendingStatus,
530
+ browsingExpired: true,
531
+ secondsLeftOnLoan: 0,
532
+ };
493
533
  this.lendingStatus = currStatus;
494
534
 
495
535
  // remove respected key:value for loan-renew
@@ -508,53 +548,36 @@ export default class IABookActions extends LitElement {
508
548
 
509
549
  async startBrowseTimer() {
510
550
  clearTimeout(this.browseTimer);
511
- console.log('startBrowseTimerstartBrowseTimer just cleared timer', this.browseTimer, this.lendingStatus);
512
-
513
551
  const {
514
552
  browsingExpired,
515
553
  user_has_browsed,
516
554
  secondsLeftOnLoan,
517
555
  } = this.lendingStatus;
556
+
557
+ log(
558
+ 'startBrowseTimerstartBrowseTimer just cleared timer',
559
+ this.browseTimer,
560
+ { browsingExpired, user_has_browsed, secondsLeftOnLoan }
561
+ );
562
+
518
563
  if (!user_has_browsed || browsingExpired) {
519
- console.log('user not browsed', this.lendingStatus);
564
+ log('user not browsed', {
565
+ user_has_browsed,
566
+ browsingExpired,
567
+ secondsLeftOnLoan,
568
+ });
520
569
  return;
521
570
  }
522
571
 
523
572
  this.browseTimer = setTimeout(() => {
524
- console.log('start `this.browseTimer will fire his.browseHasExpired() - secondsLeftOnLoan', secondsLeftOnLoan);
573
+ log(
574
+ 'start `this.browseTimer will fire his.browseHasExpired() - secondsLeftOnLoan',
575
+ secondsLeftOnLoan
576
+ );
525
577
  this.browseHasExpired();
526
578
  }, secondsLeftOnLoan * 1000);
527
579
  }
528
580
 
529
- /**
530
- * Reset timer animation state after book renewed
531
- */
532
- async resetTimerCountState() {
533
- const timerCountdown = this._shadowRoot.querySelector('timer-countdown');
534
- if (!timerCountdown) return;
535
-
536
- const secondsLeft = Number.parseInt((Number(this.lendingStatus.secondsLeftOnLoan)), 10);
537
- const strokeDashOffset = Number.parseInt(((secondsLeft / this.loanRenewTimeConfig.loanTotalTime) * 315) * 1.5, 10);
538
-
539
-
540
- console.log('resetTimerCountState === ', { secondsLeft, strokeDashOffset });
541
-
542
- // set seconds left in loan expire
543
- timerCountdown.style?.setProperty('--secondsLeftOnLoan', `${secondsLeft}s`);
544
-
545
- // set circle stroke offset left in loan expire
546
- timerCountdown.style?.setProperty(
547
- '--strokeLeftOnLoan',
548
- `${strokeDashOffset}` // the perimeter of the circle = (π * 2 * radius)
549
- );
550
-
551
- const animationCircle = timerCountdown.shadowRoot.querySelector('.circle');
552
- animationCircle.style.animationName = 'none';
553
- setTimeout(() => {
554
- animationCircle.style.animationName = 'circletimer';
555
- }, 100);
556
- }
557
-
558
581
  render() {
559
582
  if (this.barType === 'title') {
560
583
  return html`<section class="lending-wrapper">
@@ -574,6 +597,10 @@ export default class IABookActions extends LitElement {
574
597
  ></book-title-bar>`;
575
598
  }
576
599
 
600
+ get timerCountdownEl() {
601
+ return this.shadowRoot.querySelector('timer-countdown');
602
+ }
603
+
577
604
  get bookActionBar() {
578
605
  return html`
579
606
  <collapsible-action-group
@@ -599,8 +626,9 @@ export default class IABookActions extends LitElement {
599
626
  </collapsible-action-group>
600
627
  ${this.textGroupTemplate} ${this.infoIconTemplate}
601
628
  <timer-countdown
602
- .secondsLeftOnLoan=${Number(this.lendingStatus.secondsLeftOnLoan)}
603
- class=${this.borrowType === 'browsed' ? '' : 'hide'}
629
+ .secondsLeftOnLoan=${Math.round(
630
+ Number(this.lendingStatus.secondsLeftOnLoan)
631
+ )}
604
632
  ></timer-countdown>
605
633
  `;
606
634
  }
@@ -616,18 +644,17 @@ export default class IABookActions extends LitElement {
616
644
  if (this.loanRenewResult.renewNow) {
617
645
  window?.IALendingIntervals?.clearAll();
618
646
 
647
+ // Now, let's reset loan duration & this.lendingStatus
648
+ const loanTime = await this.localCache.get(`${this.identifier}-loanTime`);
649
+ const secondsLeft = Math.round((loanTime - new Date()) / 1000); // different in seconds
650
+
619
651
  // testing console....
620
- console.log('IABookActions: AutoRenewed:- ', {
652
+ log('~~~ IABookActions: handleLoanAutoRenewed:- ', {
621
653
  ajaxResponse: event?.detail?.data,
622
654
  loanRenewResult: this.loanRenewResult,
623
- secondsLeftOnLoan: Math.round(this.lendingStatus.secondsLeftOnLoan), // stale? how to fix
655
+ secondsLeftOnLoan: secondsLeft,
624
656
  });
625
657
 
626
-
627
- // Now, let's reset loan duration & this.lendingStatus
628
- const loanTime = await this.localCache.get(`${this.identifier}-loanTime`);
629
- const secondsLeft = (loanTime - new Date()) / 1000; // different in seconds
630
-
631
658
  const currStatus = {
632
659
  ...this.lendingStatus,
633
660
  user_has_browsed: true,
@@ -636,19 +663,10 @@ export default class IABookActions extends LitElement {
636
663
  };
637
664
  this.lendingStatus = currStatus;
638
665
 
639
- console.log('~~~ handleLoanAutoRenewed - new this.lendingStatus', {
640
- secondsLeftOnLoan: this.lendingStatus.secondsLeftOnLoan
641
- });
642
-
643
- /**
644
- * reset timer countdown states
645
- */
646
- await this.resetTimerCountState();
647
-
648
666
  // close the modal
649
- this.modal?.remove();
650
- this.modal?.showModal({ config: {}, customModalContent: `` });
651
-
667
+ this.modal?.closeModal();
668
+ this.modal.removeAttribute('id');
669
+ this.modal.customModalContent = nothing;
652
670
  window?.Sentry?.captureMessage(sentryLogs.bookHasRenewed);
653
671
  }
654
672
  }
@@ -659,7 +677,6 @@ export default class IABookActions extends LitElement {
659
677
  * @memberof IABookActions
660
678
  */
661
679
  startTimerCountdown() {
662
- console.log('startTimerCountdown()');
663
680
  window?.IALendingIntervals?.clearTimerCountdown();
664
681
 
665
682
  let secondsLeft = Number(this.lendingStatus.secondsLeftOnLoan);
@@ -674,14 +691,18 @@ export default class IABookActions extends LitElement {
674
691
  // if really updated, escape from here
675
692
  const resync = await this.reSyncTimerIfGoneOff(secondsLeft);
676
693
 
677
- console.log("*** startTimerCountdown = reSyncTimerIfGoneOff ***", { secondsLeft, resync });
678
-
679
694
  if (resync) {
680
- console.log('have resyncd, will end startTimerCountdown here - ');
695
+ log('timer has resyncd, abort stale startTimerCountdown - ', {
696
+ secondsLeft,
697
+ });
681
698
  return;
682
699
  }
683
700
 
684
- console.log('startTimerCountdown - NO RESYNC');
701
+ log('startTimerCountdown - countdown still valid. continue...', {
702
+ resync,
703
+ secondsLeft,
704
+ loanRenewAtLast: this.loanRenewTimeConfig.loanRenewAtLast,
705
+ });
685
706
 
686
707
  /**
687
708
  * execute from last 10th minute to 0th minute
@@ -709,15 +730,6 @@ export default class IABookActions extends LitElement {
709
730
  * @memberof TimerCountdown
710
731
  */
711
732
  async reSyncTimerIfGoneOff(timerSecondsLeft) {
712
- // debug info
713
- console.log(
714
- new Date().toLocaleTimeString(),
715
- 'timeLeftInSec',
716
- timerSecondsLeft,
717
- ' ||| timeLeftInMin',
718
- Math.ceil(timerSecondsLeft / 60)
719
- );
720
-
721
733
  const currentTime = new Date();
722
734
 
723
735
  // current time - loan time
@@ -730,19 +742,27 @@ export default class IABookActions extends LitElement {
730
742
  // convert in minutes
731
743
  const whatIsleft = Math.round(timerSecondsLeft);
732
744
  const whatShouldLeft = Math.round(secondsShouldLeft);
745
+ const timerElSeconds = this.timerCountdownEl.secondsLeftOnLoan || 0;
733
746
 
734
- if (whatIsleft !== whatShouldLeft) {
735
- // debug info
736
- console.log(
737
- `timer is gone off, let's re-sync, whatIsleft -> ${whatIsleft}, whatShouldLeft -> ${whatShouldLeft}`
738
- );
747
+ log(`reSyncTimerIfGoneOff?`, {
748
+ whatIsleft,
749
+ whatShouldLeft,
750
+ timerElSeconds,
751
+ timeLeftInMin: Math.ceil(timerSecondsLeft / 60),
752
+ });
739
753
 
740
- // the seconds should left in timer again
754
+ if (timerElSeconds !== whatShouldLeft || whatIsleft !== whatShouldLeft) {
755
+ // set lending status with new time to update decrementor
741
756
  const currStatus = {
742
757
  ...this.lendingStatus,
743
- secondsLeftOnLoan: secondsShouldLeft,
758
+ secondsLeftOnLoan: whatShouldLeft,
744
759
  };
745
760
  this.lendingStatus = currStatus;
761
+ }
762
+
763
+ if (whatIsleft !== whatShouldLeft) {
764
+ log(`timer is gone off, let's re-sync,`);
765
+
746
766
  return true;
747
767
  }
748
768
  return false;
@@ -757,7 +777,7 @@ export default class IABookActions extends LitElement {
757
777
  * @memberof IABookActions
758
778
  */
759
779
  async loanRenewAttempt(secondsLeft) {
760
- console.log('loanRenewAttempt()', secondsLeft, this.loanRenewResult);
780
+ log('loanRenewAttempt()', secondsLeft, this.loanRenewResult);
761
781
  let loanSecondsLeft = secondsLeft;
762
782
  /**
763
783
  * auto-renew is not possible in last seconds (let say 50 second) because,
@@ -767,7 +787,7 @@ export default class IABookActions extends LitElement {
767
787
  * so if seconds left is < 50, just expire the loan
768
788
  */
769
789
  if (loanSecondsLeft < 50) {
770
- console.log('if (loanSecondsLeft < 50) {');
790
+ log('if (loanSecondsLeft < 50) {');
771
791
  await this.browseHasExpired();
772
792
  return;
773
793
  }
@@ -882,8 +902,6 @@ export default class IABookActions extends LitElement {
882
902
 
883
903
  /* show error message if something went wrong */
884
904
  async showErrorModal(errorMsg, action) {
885
- await this.useModalManager();
886
-
887
905
  const modalConfig = new ModalConfig({
888
906
  title: 'Lending error',
889
907
  message: errorMsg,
@@ -910,7 +928,7 @@ export default class IABookActions extends LitElement {
910
928
  <code>errorLog: ${errorMsg}</code>`;
911
929
  }
912
930
 
913
- this.modal?.showModal({
931
+ await this.modal?.showModal({
914
932
  config: modalConfig,
915
933
  });
916
934
  }
@@ -189,7 +189,7 @@ describe('Borrow status actions', () => {
189
189
  ];
190
190
 
191
191
  expect(el.primaryTitle).to.equal(
192
- 'Renews automatically as long as pages are turned.'
192
+ 'Renews automatically with continued use.'
193
193
  );
194
194
  expect(el.primaryActions.length).to.equal(2);
195
195
  expect(el.primaryActions[0].text).to.equal('Log In and Borrow');