@internetarchive/ia-book-actions 0.1.9 → 0.1.10

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@internetarchive/ia-book-actions",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "Bookreader actions for borrowable book",
5
5
  "author": "Internet Archive",
6
6
  "license": "AGPL-3.0-only",
@@ -18,9 +18,10 @@
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",
21
22
  "@internetarchive/icon-info": "^1.2.6",
22
23
  "@internetarchive/local-cache": "^0.2.1",
23
- "@internetarchive/modal-manager": "^0.2.8",
24
+ "@internetarchive/modal-manager": "^0.2.12",
24
25
  "@internetarchive/shared-resize-observer": "^0.2.0",
25
26
  "@internetarchive/toast-manager": "0.1.0",
26
27
  "lit": "^2.5.0"
@@ -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,
@@ -118,7 +118,7 @@ export const canBrowseAndBorrow = {
118
118
  loanStartDate: '2021-07-30 09:57:40',
119
119
  loansUrl: '/details/@neeraj-archive?tab=loans#loans-on-loan',
120
120
  maxLoans: 10,
121
- secondsLeftOnLoan: 3591,
121
+ secondsLeftOnLoan: 155,
122
122
  shouldProtectImages: true,
123
123
  totalWaitlistCount: 0,
124
124
  userHasBorrowed: false,
@@ -30,6 +30,7 @@ export class CollapsibleActionGroup extends ActionsHandler {
30
30
  returnUrl: { type: String },
31
31
  autoRenew: { type: Boolean },
32
32
  autoReturn: { type: Boolean },
33
+ returnNow: { type: Boolean },
33
34
  };
34
35
  }
35
36
 
@@ -51,6 +52,7 @@ export class CollapsibleActionGroup extends ActionsHandler {
51
52
  this.returnUrl = '';
52
53
  this.autoRenew = false;
53
54
  this.autoReturn = false;
55
+ this.returnNow = false;
54
56
  }
55
57
 
56
58
  updated(changed) {
@@ -68,18 +70,22 @@ export class CollapsibleActionGroup extends ActionsHandler {
68
70
  if (changed.has('autoReturn') && this.autoReturn === true) {
69
71
  this.dispatchLoanEvent('autoReturn');
70
72
  }
73
+
74
+ if (changed.has('returnNow') && this.returnNow === true) {
75
+ this.dispatchLoanEvent('returnNow', { borrowType: 'browse' });
76
+ }
71
77
  }
72
78
 
73
79
  /**
74
- * dispatch event when book is auto renewed / returned
80
+ * dispatch event when book is auto auto-renewed / auto-returned / returned
75
81
  * listen these events in action-handler.js to execute ajax call on petabox.
76
82
  * @see ActionsHandler
77
83
  *
78
- * @param {string} event - autoRenew|autoReturn
84
+ * @param {string} event - autoRenew|autoReturn|returnNow
79
85
  * @memberof CollapsibleActionGroup
80
86
  */
81
- dispatchLoanEvent(event) {
82
- this.dispatchEvent(new Event(event));
87
+ dispatchLoanEvent(event, detail) {
88
+ this.dispatchEvent(new CustomEvent(event, { detail }));
83
89
  }
84
90
 
85
91
  /**
@@ -41,7 +41,7 @@ export default class TimerCountdown extends LitElement {
41
41
  * delay seconds in setInterval function
42
42
  * @type {number}
43
43
  */
44
- this.timerExecutionSeconds = 60;
44
+ this.timerExecutionSeconds = 30;
45
45
  }
46
46
 
47
47
  disconnectedCallback() {
@@ -66,6 +66,7 @@ export default class TimerCountdown extends LitElement {
66
66
  // store current time
67
67
  this.timeWhenTimerStart = new Date();
68
68
 
69
+ // this.dispatchLoanRenewEvent(secondsLeft);
69
70
  /**
70
71
  * set interval in window object
71
72
  * @see ia-lending-intervals.js
@@ -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 each hour as long as pages are turned.',
43
+ available_1hr: 'Renews automatically as long as pages are turned.',
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: '',
52
+ session_expired: 'Renews automatically as long as pages are turned.',
53
53
  unavailable: 'This book is not available at this time.',
54
54
  };
55
55
 
@@ -98,8 +98,7 @@ export class GetLendingActions {
98
98
  let isBrowsing =
99
99
  lendingStatus.user_has_browsed && !lendingStatus.browsingExpired;
100
100
  if (isBrowsing) {
101
- primaryTitleText =
102
- 'Returns automatically after 25 minutes of inactivity.';
101
+ primaryTitleText = bookTitles.available_1hr;
103
102
  } else {
104
103
  primaryTitleText = `Your loan of this book has ${lendingStatus.daysLeftOnLoan} days left.`;
105
104
  }
@@ -144,14 +144,14 @@ export default class LoanAnanlytics {
144
144
  * @memberof LoanAnanlytics
145
145
  */
146
146
  sendEvent(eventCategory, eventAction, label, extraParams) {
147
- console.log(
148
- 'eventCategory:-',
149
- eventCategory,
150
- '\t||\teventAction:-',
151
- eventAction,
152
- '\t||\tlabel:-',
153
- label
154
- );
147
+ // console.log(
148
+ // 'eventCategory:-',
149
+ // eventCategory,
150
+ // '\t||\teventAction:-',
151
+ // eventAction,
152
+ // '\t||\tlabel:-',
153
+ // label
154
+ // );
155
155
 
156
156
  window?.archive_analytics?.send_event_no_sampling(
157
157
  eventCategory,
@@ -16,7 +16,7 @@ export class LoanRenewHelper {
16
16
  this.loanRenewMessage =
17
17
  'This book has been renewed for #time #unitsOfTime.';
18
18
  this.loanReturnWarning =
19
- 'This book will be automatically returned in #time #unitsOfTime unless you turn a page.';
19
+ 'With no action, this book will be auto-returned in #time #unitsOfTime.';
20
20
 
21
21
  // private props
22
22
  this.result = {
@@ -39,7 +39,7 @@ export class LoanRenewHelper {
39
39
  }
40
40
 
41
41
  /**
42
- * Trigger this function when user has browsed a book and change the post
42
+ * Trigger this function when user has browsed a book and change the page
43
43
  * - every time user change the page, we set current time in indexedDB
44
44
  * - also check if need to auto renew current loan
45
45
  *
@@ -1,4 +1,5 @@
1
1
  /* eslint-disable camelcase */
2
+ /* eslint-disable class-methods-use-this */
2
3
  import { html, css, LitElement, nothing } from 'lit';
3
4
 
4
5
  import { SharedResizeObserver } from '@internetarchive/shared-resize-observer';
@@ -23,6 +24,21 @@ export const events = {
23
24
  browseExpired: 'IABookReader:BrowsingHasExpired',
24
25
  };
25
26
 
27
+ /**
28
+ * custom styling for modal-manager buttons
29
+ * TODO: lets allow modal-manager to know ia-button classes
30
+ */
31
+ export const modalButtonStyle = {
32
+ iaButton:
33
+ 'min-height:3.5rem;cursor:pointer;color:white;border-radius:0.4rem;border:1px solid #c5d1df;padding:4px 8px;width:auto;user-select:none;',
34
+ renew: 'background:#194880;width:110px;',
35
+ return: 'background:#d9534f;width:120px;',
36
+ loaderIcon:
37
+ 'display:inline-block;width:20px;height:20px;margin-top:2px;color:white;--activityIndicatorLoadingRingColor:#fff;--activityIndicatorLoadingDotColor:#fff;',
38
+ refresh:
39
+ 'background:none;font-size:inherit;border:0;padding:0;color:#0000ee;cursor:pointer;text-decoration:underline',
40
+ };
41
+
26
42
  export default class IABookActions extends LitElement {
27
43
  static get properties() {
28
44
  return {
@@ -71,10 +87,19 @@ export default class IABookActions extends LitElement {
71
87
  this.secondaryActions = [];
72
88
  this.lendingOptions = {};
73
89
  this.borrowType = ''; // (browsed|borrowed)
74
- this.consecutiveLoanCounts = 1; // consecutive loan count
90
+ this.browseTimer = undefined;
91
+
92
+ /** @deprecated */
93
+ this.toast = undefined;
94
+ /** @deprecated */
75
95
  this.suppressToast = false;
96
+ /** @deprecated */
76
97
  this.suppressAutoRenew = false;
77
- this.browseTimer = undefined;
98
+
99
+ /**
100
+ * when user click on [return the book] button on warning modal
101
+ */
102
+ this.returnNow = false;
78
103
 
79
104
  /**
80
105
  * contains one hour auto-loan-renew time configuration
@@ -218,8 +243,9 @@ export default class IABookActions extends LitElement {
218
243
  }
219
244
 
220
245
  if (this.borrowType === 'browsed') {
246
+ console.log('this.borrowType', this.borrowType);
221
247
  // start timer for browsed.
222
- this.startBrowseTimer();
248
+ await this.startBrowseTimer();
223
249
  }
224
250
 
225
251
  // early return if not borrowed or no action-bar
@@ -248,57 +274,25 @@ export default class IABookActions extends LitElement {
248
274
  * Bind 1 hour loan auto renew event,
249
275
  * There are two events we want to use,
250
276
  * 1. BookReader:userAction - dispatched from bookreader side
251
- * 2. click event to refresh the page when book is auto expired
252
- * 3. IABookActions:loanRenew - dispatched from timer-countdown component
277
+ * 2. IABookActions:loanRenew - dispatched from timer-countdown component
253
278
  */
254
279
  bindLoanRenewEvents() {
255
280
  /**
256
281
  * dispatched this event from bookreader page changed
257
282
  */
258
283
  window.addEventListener('BookReader:userAction', () => {
259
- console.log('** BR user Action', {
260
- suppressAutoRenew: this.suppressAutoRenew,
261
- borrowType: this.borrowType,
262
- });
263
-
264
- this.suppressToast = true;
265
- this.closeToastManager();
266
-
267
- if (this.suppressAutoRenew) {
268
- return;
269
- }
270
-
271
284
  if (this.borrowType === 'browsed') {
272
285
  this.autoLoanRenewChecker(true);
273
286
  }
274
287
  });
275
288
 
276
- /**
277
- * detect click-event on document to close toast-template
278
- */
279
- document.addEventListener('click', e => {
280
- console.log('*** top level click handler to remove toast on click');
281
- if (this.loanRenewHelper && this.loanRenewResult.secondsLeft > 0) {
282
- this.suppressToast = true;
283
- }
284
-
285
- // reload when user click on book-page and book is EXPIRED
286
- const hasExpired =
287
- 'browsingExpired' in this.lendingStatus &&
288
- this.lendingStatus?.browsingExpired;
289
- if (hasExpired && e.target.nodeName !== 'IA-BOOK-ACTIONS') {
290
- window.location?.reload();
291
- }
292
- });
293
-
294
289
  /**
295
290
  * this event dispatched from timer-countdown component for:
296
- * 1. user turned book page after showing auto-returned warning message
297
- * 2. show warning message having remaining time
291
+ * 1. user turned book page after showing auto-returned warning modal
292
+ * 2. show warning modal having remaining time
298
293
  * @see TimerCountdown::timerCountdown
299
294
  */
300
295
  this.addEventListener('IABookActions:loanRenew', async event => {
301
- console.log('** IABookActions:loanRenew', event.detail);
302
296
  await this.autoLoanRenewChecker(false);
303
297
 
304
298
  /**
@@ -314,16 +308,16 @@ export default class IABookActions extends LitElement {
314
308
  this.browseHasExpired();
315
309
  }
316
310
 
317
- // show warning message with remaining time to auto returned it.
311
+ // show warning modal with remaining time to auto returned it.
318
312
  if (this.loanRenewResult.renewNow === false) {
319
313
  /**
320
314
  * so compensate for the 50 second buffer to handle above race conditions
321
315
  * let's reduce 1 min from warning texts and early return the book when 1 min left.
322
316
  */
323
317
  secondsLeft -= 60;
324
-
325
318
  this.loanRenewResult.secondsLeft = secondsLeft;
326
- this.showToastMessage();
319
+
320
+ this.showWarningModal();
327
321
  }
328
322
  });
329
323
  }
@@ -347,17 +341,153 @@ export default class IABookActions extends LitElement {
347
341
  }
348
342
 
349
343
  /**
344
+ * create / select the modal-manager component on DOM
345
+ * @memberof IABookActions
346
+ */
347
+ async useModalManager() {
348
+ if (!this.modal) {
349
+ this.modal = document.querySelector('modal-manager');
350
+
351
+ if (!this.modal) this.modal = document.createElement('modal-manager');
352
+ }
353
+
354
+ this.modal.id = 'action-bar-modal';
355
+ await document.body.appendChild(this.modal);
356
+ }
357
+
358
+ /**
359
+ * Show the waring modal to ask user if they are still reading
360
+ */
361
+ async showWarningModal() {
362
+ console.log('****** showWarningModal ******');
363
+ await this.useModalManager();
364
+
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
+ config.headline = 'Are you still here?';
375
+ config.showCloseButton = false;
376
+ config.closeOnBackdropClick = false;
377
+ config.message = this.loanRenewHelper?.getMessageTexts(
378
+ this.loanRenewResult.texts,
379
+ secondsLeft
380
+ );
381
+
382
+ const customContent = html`<br />
383
+ <div id="custom-buttons" style="display:flex;justify-content:center;">
384
+ <button
385
+ style="${modalButtonStyle.iaButton} ${modalButtonStyle.renew}"
386
+ @click=${event => {
387
+ this.changeModalState(event);
388
+ this.autoLoanRenewChecker(true);
389
+ }}
390
+ >
391
+ Keep reading
392
+ </button>
393
+ <button
394
+ style="${modalButtonStyle.iaButton} ${modalButtonStyle.return}"
395
+ @click=${event => {
396
+ this.changeModalState(event);
397
+ document.querySelector('ia-book-actions').disableActionGroup = true;
398
+ this.returnNow = true;
399
+ }}
400
+ >
401
+ Return the book
402
+ </button>
403
+ </div> `;
404
+
405
+ this.modal.showModal({ config, customModalContent: customContent });
406
+ }
407
+
408
+ /**
409
+ * helper function to change button click states on modal-manager
410
+ * - add loader on button click
411
+ * - change button appearance
412
+ *
413
+ * @param {Event} event
414
+ */
415
+ changeModalState(event) {
416
+ if (event.target === undefined) return;
417
+
418
+ const button = event.target;
419
+ button.innerHTML = `<ia-activity-indicator
420
+ mode="processing"
421
+ style=${modalButtonStyle.loaderIcon}
422
+ ></ia-activity-indicator>`;
423
+
424
+ const parentElement = event.target.parentNode;
425
+ parentElement.style.pointerEvents = 'none';
426
+ parentElement.style.opacity = 0.8;
427
+ }
428
+
429
+ /**
430
+ * Show modal when book is auto returned
431
+ */
432
+ async showExpiredModal() {
433
+ await this.useModalManager();
434
+
435
+ const config = new ModalConfig();
436
+ config.headline = '';
437
+ config.showCloseButton = false;
438
+ config.closeOnBackdropClick = false;
439
+ config.message = 'This book has been returned due to inactivity.';
440
+
441
+ const customContent = html`<br />
442
+ <div style="text-align: center">
443
+ <button
444
+ style="${modalButtonStyle.iaButton} ${modalButtonStyle.renew}"
445
+ @click=${() => {
446
+ window.location?.reload();
447
+ }}
448
+ >
449
+ Okay
450
+ </button>
451
+ </div> `;
452
+
453
+ this.modal?.showModal({ config, customModalContent: customContent });
454
+ }
455
+
456
+ /**
457
+ * close/hide modal-manager
458
+ */
459
+ async closeModalManager() {
460
+ this.modal = document.querySelector('modal-manager');
461
+ this.modal?.closeModal();
462
+ }
463
+
464
+ /**
465
+ * @deprecated
466
+ * create / select the toast-template component on DOM
467
+ *
468
+ * @memberof IABookActions
469
+ */
470
+ async useToastManager() {
471
+ if (!this.toast) {
472
+ this.toast = this.shadowRoot.querySelector('toast-template');
473
+
474
+ if (!this.toast) this.toast = document.createElement('toast-template');
475
+ }
476
+
477
+ await this.shadowRoot.appendChild(this.toast);
478
+ }
479
+
480
+ /**
481
+ * @deprecated
350
482
  * close/hide toast message
351
483
  */
352
484
  async closeToastManager() {
353
- console.log('** closeToastManager');
354
- const toastTemplate = this.shadowRoot.querySelector('toast-template');
355
- if (toastTemplate) {
356
- toastTemplate?.remove();
357
- }
485
+ this.toast = this.shadowRoot.querySelector('toast-template');
486
+ this.toast?.remove();
358
487
  }
359
488
 
360
489
  /**
490
+ * @deprecated
361
491
  * Show toast messages on some specific loan renew features. e.g.
362
492
  * - show message when book is auto renewed
363
493
  * - show message when book is expired
@@ -370,11 +500,7 @@ export default class IABookActions extends LitElement {
370
500
  });
371
501
  if (this.suppressToast) return;
372
502
 
373
- let toastTemplate = this.shadowRoot.querySelector('toast-template');
374
- if (!toastTemplate) {
375
- toastTemplate = document.createElement('toast-template');
376
- }
377
- await this.shadowRoot.appendChild(toastTemplate);
503
+ await this.useToastManager();
378
504
 
379
505
  // if secondsLeft < 60, consider it 1 minute
380
506
  let { secondsLeft } = this.loanRenewResult;
@@ -391,12 +517,15 @@ export default class IABookActions extends LitElement {
391
517
  secondsLeft
392
518
  );
393
519
 
394
- toastTemplate.showToast({
520
+ this.toast?.showToast({
395
521
  config,
396
522
  });
397
523
  }
398
524
 
399
525
  async browseHasRenew() {
526
+ console.log('****** browseHasRenew ******');
527
+ window?.IALendingIntervals?.clearAll();
528
+
400
529
  const loanTime = await this.localCache.get(`${this.identifier}-loanTime`);
401
530
  const secondsLeft = (loanTime - new Date()) / 1000; // different in seconds
402
531
 
@@ -407,24 +536,6 @@ export default class IABookActions extends LitElement {
407
536
  secondsLeftOnLoan: secondsLeft,
408
537
  };
409
538
  this.lendingStatus = currStatus;
410
-
411
- this.startBrowseTimer();
412
- }
413
-
414
- startBrowseTimer() {
415
- clearTimeout(this.browseTimer);
416
- const {
417
- browsingExpired,
418
- user_has_browsed,
419
- secondsLeftOnLoan,
420
- } = this.lendingStatus;
421
- if (!user_has_browsed || browsingExpired) {
422
- return;
423
- }
424
-
425
- this.browseTimer = setTimeout(() => {
426
- this.browseHasExpired();
427
- }, secondsLeftOnLoan * 1000);
428
539
  }
429
540
 
430
541
  /**
@@ -443,14 +554,33 @@ export default class IABookActions extends LitElement {
443
554
  // show message after browsed book is expired.
444
555
  this.loanRenewResult.renewNow = false;
445
556
  this.loanRenewResult.texts =
446
- 'This book has been automatically returned due to inactivity.';
557
+ 'This book has been returned due to inactivity.';
447
558
 
448
- this.suppressToast = false;
449
- this.showToastMessage();
559
+ this.showExpiredModal();
450
560
 
451
561
  window?.Sentry?.captureMessage(sentryLogs.browseHasExpired);
452
562
  }
453
563
 
564
+ async startBrowseTimer() {
565
+ clearTimeout(this.browseTimer);
566
+
567
+ const {
568
+ browsingExpired,
569
+ user_has_browsed,
570
+ secondsLeftOnLoan,
571
+ } = this.lendingStatus;
572
+ if (!user_has_browsed || browsingExpired) {
573
+ console.log('user not browsed', this.lendingStatus);
574
+ return;
575
+ }
576
+
577
+ console.log('user browsed', this.lendingStatus);
578
+
579
+ this.browseTimer = setTimeout(() => {
580
+ this.browseHasExpired();
581
+ }, secondsLeftOnLoan * 1000);
582
+ }
583
+
454
584
  /**
455
585
  * Reset timer animation state after book renewed
456
586
  */
@@ -524,6 +654,7 @@ export default class IABookActions extends LitElement {
524
654
  ?disabled=${this.disableActionGroup}
525
655
  ?autoRenew=${this.loanRenewResult.renewNow}
526
656
  ?autoReturn=${this.lendingStatus.browsingExpired}
657
+ ?returnNow=${this.returnNow}
527
658
  @loanAutoRenewed=${this.handleLoanAutoRenewed}
528
659
  @lendingActionError=${this.handleLendingActionError}
529
660
  @toggleActionGroup=${this.handleToggleActionGroup}
@@ -545,19 +676,21 @@ export default class IABookActions extends LitElement {
545
676
  if (this.loanRenewResult.renewNow) {
546
677
  window?.IALendingIntervals?.clearAll();
547
678
 
548
- this.suppressToast = false;
549
- await this.browseHasRenew();
550
- await this.showToastMessage();
551
- await this.resetTimerCountState();
552
-
553
- window?.Sentry?.captureMessage(sentryLogs.bookHasRenewed);
554
-
555
679
  // testing console....
556
680
  console.log('IABookActions: AutoRenewed:- ', {
557
681
  ajaxResponse: event?.detail?.data,
558
682
  loanRenewResult: this.loanRenewResult,
559
683
  secondsLeftOnLoan: Math.round(this.lendingStatus.secondsLeftOnLoan),
560
684
  });
685
+
686
+ await this.browseHasRenew();
687
+ await this.resetTimerCountState();
688
+
689
+ // close the modal
690
+ this.modal?.remove();
691
+ this.modal?.showModal({ config: {}, customModalContent: `` });
692
+
693
+ window?.Sentry?.captureMessage(sentryLogs.bookHasRenewed);
561
694
  }
562
695
  }
563
696
 
@@ -602,7 +735,7 @@ export default class IABookActions extends LitElement {
602
735
  this.disableActionGroup = !this.disableActionGroup;
603
736
  }
604
737
 
605
- /*
738
+ /**
606
739
  * handle lending errors occure during different operation like
607
740
  * browse book, borrow book, borrowed book token etc...
608
741
  *
@@ -656,16 +789,7 @@ export default class IABookActions extends LitElement {
656
789
 
657
790
  /* show error message if something went wrong */
658
791
  async showErrorModal(errorMsg, action) {
659
- // check if this.modal passed as prop
660
- if (!this.modal) {
661
- this.modal = document.querySelector('modal-manager');
662
-
663
- // check the DOM if <modal-manager> already there
664
- if (!this.modal) this.modal = document.createElement('modal-manager');
665
- }
666
-
667
- this.modal.id = 'action-bar-modal';
668
- await document.body.appendChild(this.modal);
792
+ await this.useModalManager();
669
793
 
670
794
  const modalConfig = new ModalConfig({
671
795
  title: 'Lending error',
@@ -676,7 +800,7 @@ export default class IABookActions extends LitElement {
676
800
 
677
801
  if (action === 'create_token') {
678
802
  const refreshButton = html`<button
679
- style="background:none;font-size:inherit;border:0;padding:0;color:#0000ee;cursor:pointer;text-decoration:underline"
803
+ style="${modalButtonStyle.refresh}"
680
804
  @click=${() => window.location.reload(true)}
681
805
  >
682
806
  refresh
@@ -80,7 +80,7 @@ describe('Loan Renew Determine', () => {
80
80
  );
81
81
 
82
82
  expect(toastMsg).to.be.equal(
83
- 'This book will be automatically returned in 2 minutes unless you turn a page.'
83
+ 'With no action, this book will be auto-returned in 2 minutes.'
84
84
  );
85
85
  });
86
86
  });
@@ -188,7 +188,9 @@ describe('Borrow status actions', () => {
188
188
  },
189
189
  ];
190
190
 
191
- expect(el.primaryTitle).to.equal('');
191
+ expect(el.primaryTitle).to.equal(
192
+ 'Renews automatically as long as pages are turned.'
193
+ );
192
194
  expect(el.primaryActions.length).to.equal(2);
193
195
  expect(el.primaryActions[0].text).to.equal('Log In and Borrow');
194
196
  expect(el.primaryActions[0].text).to.equal(expectedPrimaryActions[0].text);
@@ -274,7 +276,7 @@ describe('Browsing expired status', () => {
274
276
  expect(el.loanRenewResult.renewNow).to.equal(false);
275
277
  expect(el.loanRenewResult.secondsLeft).to.equal(0);
276
278
  expect(el.loanRenewResult.texts).to.equal(
277
- 'This book has been automatically returned due to inactivity.'
279
+ 'This book has been returned due to inactivity.'
278
280
  );
279
281
  });
280
282
 
@@ -347,7 +349,7 @@ describe('Auto renew one hour loan', () => {
347
349
  });
348
350
  el.dispatchEvent(myEvent);
349
351
 
350
- await aTimeout(1500); // wait for 1.5 second
352
+ await aTimeout(1900); // wait for 1.9 second
351
353
  await el.updateComplete;
352
354
 
353
355
  expect(el.loanRenewResult.texts).to.equal(