@internetarchive/ia-book-actions 0.0.29-alpha2 → 0.0.29-alpha21

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.
@@ -19,7 +19,7 @@ import ActionsConfig from './actions-config.js';
19
19
  *
20
20
  * Response of this class contain an object as follows:-
21
21
  * {
22
- * primaryTitle: 'Renewable every hour, pending availability.',
22
+ * primaryTitle: '',
23
23
  * primaryActions: [this.actionsConfig.returnBookConfig()],
24
24
  * primaryColor: 'primary',
25
25
  * secondaryActions: [this.actionsConfig.purchaseConfig()],
@@ -40,7 +40,7 @@ import ActionsConfig from './actions-config.js';
40
40
  * @enum {string}
41
41
  */
42
42
  export const bookTitles = {
43
- available_1hr: 'Renewable every hour, pending availability.',
43
+ available_1hr: '',
44
44
  available_14d: 'This book can be borrowed for 14 days.',
45
45
  available_pd: 'Book available to patrons with print disabilities.',
46
46
  available_waitlist: 'A waitlist is available.',
@@ -49,7 +49,7 @@ export const bookTitles = {
49
49
  being_borrowed: 'Another patron is using this book. Please check back later.',
50
50
  eligible_pd: 'You are eligible for print-disabled access.',
51
51
  on_waitlist: 'You are on the waitlist for this book.',
52
- session_expired: 'Your loan has expired.',
52
+ session_expired: '',
53
53
  unavailable: 'This book is not available at this time.',
54
54
  };
55
55
 
@@ -98,7 +98,7 @@ export class GetLendingActions {
98
98
  let isBrowsing =
99
99
  lendingStatus.user_has_browsed && !lendingStatus.browsingExpired;
100
100
  if (isBrowsing) {
101
- primaryTitleText = this.getBrowseCountdownTitle();
101
+ primaryTitleText = '';
102
102
  } else {
103
103
  primaryTitleText = `Your loan of this book has ${lendingStatus.daysLeftOnLoan} days left.`;
104
104
  }
@@ -251,13 +251,6 @@ export class GetLendingActions {
251
251
  borrow1HrAction() {
252
252
  const lendingStatus = this.lendingStatus || {};
253
253
 
254
- const possibleTitles = {
255
- one_hour: 'Renewable every hour, pending availability.',
256
- session_tryagain:
257
- 'Another patron is using this book. Please check back later.',
258
- session_expired: 'Your loan has expired.',
259
- };
260
-
261
254
  // assess borrowable state
262
255
  const browsingHasExpired =
263
256
  !lendingStatus.available_to_browse && lendingStatus.browsingExpired;
@@ -364,6 +357,8 @@ export class GetLendingActions {
364
357
  }
365
358
 
366
359
  /**
360
+ * @deprecated
361
+ *
367
362
  * Builds the countdown toolbar title.
368
363
  *
369
364
  * @return string innerHTML for the dialogOpts title attribute
@@ -0,0 +1,159 @@
1
+ import { nothing } from 'lit';
2
+ import { ToastConfig } from '@internetarchive/toast-manager';
3
+
4
+ /**
5
+ * This class is used to determine if use is eligible for auto renew loan.
6
+ */
7
+ export class LoanRenewHelper {
8
+ constructor(hasPageChanged, identifier, localCache, loanRenewConfig) {
9
+ this.hasPageChanged = hasPageChanged;
10
+ this.identifier = identifier;
11
+ this.localCache = localCache;
12
+ this.loanRenewConfig = loanRenewConfig;
13
+
14
+ // messages for auto return machenism
15
+ this.loanRenewMessage = 'This book has been renewed for 1 hour.';
16
+ this.loanReturnWarning =
17
+ 'This book will be automatically returned in #time minutes unless you turn a page.';
18
+
19
+ // private props
20
+ this.result = {
21
+ texts: null,
22
+ renewNow: false,
23
+ timeLeft: 0,
24
+ };
25
+ }
26
+
27
+ handleLoanRenew() {
28
+ try {
29
+ if (this.hasPageChanged) {
30
+ // when user click/flip on book page
31
+ this.showToastMessage();
32
+ return this.pageChanged(); // user clicked on page
33
+ }
34
+ return this.autoChecker(); // auto checker at 50th minute
35
+ } catch (error) {
36
+ console.log(error);
37
+ }
38
+
39
+ return nothing;
40
+ }
41
+
42
+ /**
43
+ * Trigger this function when user has browsed a book and change the post
44
+ * - every time user change the page, we set current time in indexedDB
45
+ * - also check if need to auto renew current loan
46
+ *
47
+ * @returns {this.result}
48
+ */
49
+ async pageChanged() {
50
+ const {
51
+ totalTime, // 60
52
+ autoCheckAt, // 50
53
+ } = this.loanRenewConfig;
54
+ const currentTime = new Date();
55
+ const loanTime = await this.localCache.get(`${this.identifier}-loanTime`);
56
+
57
+ const lastTimeFrame = this.changeTime(
58
+ loanTime,
59
+ totalTime - autoCheckAt,
60
+ 'add'
61
+ );
62
+
63
+ // if user viewed new page in last 10 minutes, renew immediately
64
+ if (currentTime >= lastTimeFrame) {
65
+ this.result.texts = this.loanRenewMessage;
66
+ this.result.renewNow = true;
67
+ }
68
+
69
+ this.setPageChangedTime();
70
+ return this.result;
71
+ }
72
+
73
+ /**
74
+ * Trigger this function when countdown hits last 10 minutes
75
+ * - if user is open new page, just renew the loan at last 10th minute
76
+ *
77
+ * @returns {this.result}
78
+ */
79
+ async autoChecker() {
80
+ const currentTime = new Date();
81
+ const { pageChangedInLast } = this.loanRenewConfig;
82
+ const lastPageFlipTime = await this.localCache.get(
83
+ `${this.identifier}-pageChangedTime`
84
+ );
85
+
86
+ // last 15 min if used flipped a page.
87
+ const lastFlipTimeFrame = this.changeTime(
88
+ currentTime,
89
+ pageChangedInLast,
90
+ 'sub'
91
+ ); // 15 seconds
92
+
93
+ if (
94
+ lastPageFlipTime === undefined ||
95
+ lastPageFlipTime <= lastFlipTimeFrame
96
+ ) {
97
+ this.result.texts = this.loanReturnWarning;
98
+ this.result.renewNow = false; // not viewed
99
+ this.showToastMessage();
100
+ } else if (lastPageFlipTime >= lastFlipTimeFrame) {
101
+ this.result.texts = '';
102
+ this.result.renewNow = true; // viewed in last time frame
103
+ }
104
+
105
+ return this.result;
106
+ }
107
+
108
+ /**
109
+ * set current time in indexedDB when user viewed a new page
110
+ */
111
+ async setPageChangedTime() {
112
+ await this.localCache.set({
113
+ key: `${this.identifier}-pageChangedTime`,
114
+ value: new Date(), // current time
115
+ ttl: Number(this.loanRenewConfig.totalTime),
116
+ });
117
+ }
118
+
119
+ /**
120
+ * Show toast messages on some specific loan renew features. e.g.
121
+ * - show success msg when book is auto renewed
122
+ * - show success msg when book is auto returned
123
+ * - show warninig msg when book is about to auto returned
124
+ */
125
+ async showToastMessage() {
126
+ const iaBookActions = document.querySelector('ia-book-actions').shadowRoot;
127
+ let toastTemplate = iaBookActions.querySelector('toast-template');
128
+ if (!toastTemplate) {
129
+ toastTemplate = document.createElement('toast-template');
130
+ }
131
+ await iaBookActions.appendChild(toastTemplate);
132
+
133
+ const config = new ToastConfig();
134
+ config.texts = this.result.texts?.replace(/#time/, this.result.timeLeft);
135
+ config.dismisOnClick = true;
136
+ toastTemplate.showToast({
137
+ config,
138
+ });
139
+ }
140
+
141
+ /**
142
+ * Helper function to get time difference
143
+ *
144
+ * @param {Date} date
145
+ * @param {Number} minutes
146
+ * @param {String} op
147
+ *
148
+ * @returns {Object} Date
149
+ */
150
+ changeTime(date, minutes, op) {
151
+ if (date === undefined) return nothing;
152
+
153
+ if (op === 'sub') {
154
+ return new Date(date.getTime() - minutes * 1000);
155
+ }
156
+
157
+ return new Date(date.getTime() + minutes * 1000);
158
+ }
159
+ }
@@ -10,12 +10,11 @@ import './components/book-title-bar.js';
10
10
  import './components/text-group.js';
11
11
  import './components/info-icon.js';
12
12
  import './components/timer-countdown.js';
13
- import './components/toast-message.js';
14
13
 
15
14
  import { GetLendingActions } from './core/services/get-lending-actions.js';
16
15
  import { mobileContainerWidth } from './core/config/constants.js';
17
16
  import { LoanTokenPoller } from './core/services/loan-token-poller.js';
18
- import { OneHourAutoRenew } from './core/services/one-hour-auto-renew.js';
17
+ import { LoanRenewHelper } from './core/services/loan-renew-helper.js';
19
18
 
20
19
  export const events = {
21
20
  browseExpired: 'IABookReader:BrowsingHasExpired',
@@ -36,17 +35,13 @@ export default class IABookActions extends LitElement {
36
35
  attribute: false,
37
36
  },
38
37
  barType: { type: String },
39
- autoRenew: { type: Boolean },
40
38
  sharedObserver: { attribute: false },
41
39
  disableActionGroup: { type: Boolean },
42
40
  modal: { Object },
43
41
  tokenDelay: { type: Number },
44
- toastTexts: { type: String },
45
- remainingTime: { type: Number },
46
- resetTimerCountdown: { type: Boolean },
47
42
  localCache: { type: Object },
48
- autoRenewTimeConfig: { type: Object },
49
- browseAgainNow: { type: Boolean },
43
+ loanRenewConfig: { type: Object },
44
+ loanRenewResult: { type: Object },
50
45
  };
51
46
  }
52
47
 
@@ -61,15 +56,10 @@ export default class IABookActions extends LitElement {
61
56
  this.bwbPurchaseUrl = '';
62
57
  this.lendingBarPostInit = () => {};
63
58
  this.barType = 'action'; // 'title'|'action'
64
- this.autoRenew = false;
65
59
  this.sharedObserver = undefined;
66
60
  this.disableActionGroup = false;
67
61
  this.modal = undefined;
68
62
  this.tokenDelay = 120000; // 2 minutes
69
- this.localCache = new LocalCache({
70
- namespace: '1HourLoanStorage'
71
- }),
72
- this.autoRenewTimeConfig = {},
73
63
 
74
64
  // private props
75
65
  this.primaryActions = [];
@@ -79,21 +69,39 @@ export default class IABookActions extends LitElement {
79
69
  this.lendingOptions = {};
80
70
  this.borrowType = ''; // (browsed|borrowed)
81
71
  this.consecutiveLoanCounts = 1; // consecutive loan count
82
- this.browseAgainNow = false;
72
+
73
+ // loan auto renew
83
74
  this.browserTimer = undefined;
84
- this.hideToast = true;
75
+ this.loanRenewConfig = {};
85
76
 
86
- this.bindAutoRenewEvent();
87
- this.resetTimerCountdown = false;
77
+ /**
78
+ * contains loan auto-renew response
79
+ * - texts:- texts messages need to show in toast message
80
+ * - renewNow:- key to determine if need to renew now
81
+ */
82
+ this.loanRenewResult = {
83
+ texts: null,
84
+ renewNow: false,
85
+ };
86
+
87
+ this._shadowRoot = this.attachShadow({ mode: 'open' });
88
88
  }
89
89
 
90
90
  disconnectedCallback() {
91
- this.tokenPoller?.disconnectedInterval();
91
+ this.tokenPoller.disconnectedInterval();
92
92
  window?.Sentry?.captureMessage('disconnectedCallback');
93
93
  this.disconnectResizeObserver();
94
94
  }
95
95
 
96
96
  firstUpdated() {
97
+ // bind auto loan renew events
98
+ this.bindLoanRenewEvents();
99
+
100
+ // localCache used for auto-loan-renew
101
+ this.localCache = new LocalCache({
102
+ namespace: 'loanRenew',
103
+ });
104
+
97
105
  if (!this.sharedObserver) {
98
106
  this.sharedObserver = new SharedResizeObserver();
99
107
  this.setupResizeObserver();
@@ -106,81 +114,12 @@ export default class IABookActions extends LitElement {
106
114
  this.update();
107
115
  }
108
116
 
109
- if (changed.has('autoRenew')) {
110
- this.resetTimerCountdown = true;
111
- }
112
117
  if (changed.has('sharedObserver')) {
113
118
  this.disconnectResizeObserver();
114
119
  this.setupResizeObserver();
115
120
  }
116
121
  }
117
122
 
118
- bindAutoRenewEvent() {
119
- // dispatched this event from timer-countdown
120
- this.addEventListener('autoRenewAttempt', async event => {
121
- const isPageFlipped = event.detail.bookPageChange;
122
-
123
- const dd = await new OneHourAutoRenew(
124
- isPageFlipped,
125
- this.identifier,
126
- this.localCache,
127
- this.autoRenewTimeConfig,
128
- this.lendingStatus.user_has_browsed && !this.lendingStatus.browsingExpired
129
- );
130
-
131
- this.toastTexts = dd.toastTexts;
132
- this.hideToast = dd.hideToast;
133
- this.browseAgainNow = dd.renewNow;
134
- if (this.browseAgainNow) {
135
- await this.browseHasRenew();
136
- }
137
- // this.handleBookAutoRenew(event);
138
- });
139
- }
140
-
141
- async browseHasExpired() {
142
- const currStatus = { ...this.lendingStatus, browsingExpired: true };
143
- this.lendingStatus = currStatus;
144
-
145
- // remove respected key:value
146
- await this.localCache.delete(`${this.identifier}-loanTime`);
147
- await this.localCache.delete(`${this.identifier}-pageFlipTime`);
148
-
149
- // show message after browsed book is expired.
150
- this.toastTexts = this.autoReturnMessage;
151
- }
152
-
153
- async browseHasRenew() {
154
- const currStatus = {
155
- ...this.lendingStatus,
156
- user_has_browsed: true,
157
- browsingExpired: false,
158
- secondsLeftOnLoan: 61
159
- };
160
- this.lendingStatus = currStatus;
161
-
162
- this.startBrowseTimer();
163
- }
164
-
165
- startBrowseTimer() {
166
- clearTimeout(this.browserTimer);
167
-
168
- const {
169
- browsingExpired,
170
- user_has_browsed,
171
- secondsLeftOnLoan,
172
- } = this.lendingStatus;
173
- if (!user_has_browsed || browsingExpired) {
174
- return;
175
- }
176
-
177
- // expired book and show browse again button
178
- const timeLeft = secondsLeftOnLoan * 1000;
179
- this.browserTimer = setTimeout(() => {
180
- this.browseHasExpired();
181
- }, timeLeft);
182
- }
183
-
184
123
  /** SharedObserver resize handler */
185
124
  handleResize(entry) {
186
125
  // if you are observing multiple targets,
@@ -234,13 +173,16 @@ export default class IABookActions extends LitElement {
234
173
  });
235
174
 
236
175
  this.borrowType = actions.borrowType;
237
- this.remainingTime = this.lendingStatus.secondsLeftOnLoan;
238
176
 
239
177
  const hasExpired =
240
178
  'browsingExpired' in this.lendingStatus &&
241
179
  this.lendingStatus?.browsingExpired;
242
180
  if (hasExpired) {
243
- window?.Sentry?.captureMessage('setupLendingToolbarActions hasExpired');
181
+ if (!this.tokenPoller) {
182
+ window?.Sentry?.captureMessage(
183
+ 'setupLendingToolbarActions hasExpired - no tokenPoller'
184
+ );
185
+ }
244
186
  this.tokenPoller?.disconnectedInterval();
245
187
  /** Global event - always fire */
246
188
  this.dispatchEvent(
@@ -265,7 +207,138 @@ export default class IABookActions extends LitElement {
265
207
  return;
266
208
  }
267
209
 
268
- this.startLoanTokenPoller();
210
+ /**
211
+ * tokenPoller determines if user has loan token for this book
212
+ * - if book is going to renew, need to wait until renew is completed
213
+ * - otherwise execute immediately
214
+ */
215
+ setTimeout(
216
+ () => {
217
+ this.startLoanTokenPoller();
218
+
219
+ if (!this.loanRenewResult.renewNow) {
220
+ this.resetTimerCountState();
221
+ }
222
+ },
223
+ this.loanRenewResult.renewNow ? this.tokenDelay : 100
224
+ );
225
+ }
226
+
227
+ /**
228
+ * Bind 1 hour loan auto renew,
229
+ * There are two events we want to use,
230
+ * 1. BookReader:pageChanged - dispatched from bookreader side
231
+ * 2. IABookActions:loanRenew - dispatched from timer-countdown component
232
+ */
233
+ bindLoanRenewEvents() {
234
+ // dispatched this event from bookreader page changed
235
+ window.addEventListener('BookReader:pageChanged', () => {
236
+ // early return if book is not browsed (1 hour borrow)
237
+ if (this.borrowType === 'browsed') {
238
+ this.autoLoanRenewChecker(true);
239
+ }
240
+
241
+ return nothing;
242
+ });
243
+
244
+ /**
245
+ * this event dispatched from timer-countdown component for:
246
+ * 1. user turned book page after showing auto-returned warning message
247
+ * 2. show warning message having remaining time
248
+ */
249
+ this.addEventListener('IABookActions:loanRenew', async event => {
250
+ const eventDetails = event.detail;
251
+
252
+ await this.autoLoanRenewChecker(false);
253
+
254
+ // show warning message with remaining time to auto returned it.
255
+ if (this.loanRenewHelper.result.renewNow === false) {
256
+ this.loanRenewHelper.result.timeLeft = eventDetails.timeLeft;
257
+ this.loanRenewHelper.showToastMessage();
258
+ }
259
+ });
260
+ }
261
+
262
+ async autoLoanRenewChecker(hasPageChanged = false) {
263
+ this.loanRenewHelper = new LoanRenewHelper(
264
+ hasPageChanged,
265
+ this.identifier,
266
+ this.localCache,
267
+ this.loanRenewConfig
268
+ );
269
+
270
+ await this.loanRenewHelper.handleLoanRenew();
271
+ this.loanRenewResult = this.loanRenewHelper.result;
272
+ }
273
+
274
+ async browseHasRenew() {
275
+ const loanTime = await this.localCache.get(`${this.identifier}-loanTime`);
276
+ const secondsLeft = (loanTime - new Date()) / 1000; // different in seconds
277
+
278
+ const currStatus = {
279
+ ...this.lendingStatus,
280
+ user_has_browsed: true,
281
+ browsingExpired: false,
282
+ secondsLeftOnLoan: secondsLeft,
283
+ };
284
+ this.lendingStatus = currStatus;
285
+
286
+ this.startBrowseTimer();
287
+ }
288
+
289
+ startBrowseTimer() {
290
+ clearTimeout(this.browserTimer);
291
+
292
+ const {
293
+ browsingExpired,
294
+ user_has_browsed,
295
+ secondsLeftOnLoan,
296
+ } = this.lendingStatus;
297
+ if (!user_has_browsed || browsingExpired) {
298
+ return;
299
+ }
300
+
301
+ const timeLeft = secondsLeftOnLoan * 1000;
302
+ this.browserTimer = setTimeout(() => {
303
+ this.browseHasExpired();
304
+ }, timeLeft);
305
+ }
306
+
307
+ /**
308
+ * Execute when loan is expired
309
+ */
310
+ async browseHasExpired() {
311
+ const currStatus = { ...this.lendingStatus, browsingExpired: true };
312
+ this.lendingStatus = currStatus;
313
+
314
+ // remove respected key:value for loan-renew
315
+ await this.localCache.delete(`${this.identifier}-loanTime`);
316
+ await this.localCache.delete(`${this.identifier}-pageChangedTime`);
317
+
318
+ // show message after browsed book is expired.
319
+ this.loanRenewResult.renewNow = false;
320
+ this.loanRenewResult.texts =
321
+ 'This book has been automatically returned due to inactivity.';
322
+
323
+ this.loanRenewHelper.showToastMessage();
324
+ }
325
+
326
+ /**
327
+ * Reset timer animation state after book renewed
328
+ */
329
+ async resetTimerCountState() {
330
+ const timerCountdown = this.shadowRoot.querySelector('timer-countdown');
331
+ const animationCircle = timerCountdown.shadowRoot.querySelector('.circle');
332
+
333
+ timerCountdown.style?.setProperty(
334
+ '--secondsLeft',
335
+ `${Number(this.lendingStatus.secondsLeftOnLoan)}s`
336
+ );
337
+
338
+ animationCircle.style.animationName = 'none';
339
+ setTimeout(() => {
340
+ animationCircle.style.animationName = 'circletimer';
341
+ }, 100);
269
342
  }
270
343
 
271
344
  render() {
@@ -290,26 +363,14 @@ export default class IABookActions extends LitElement {
290
363
  get timerCountdownTemplate() {
291
364
  return this.borrowType === 'browsed'
292
365
  ? html`<timer-countdown
293
- time=${Number(this.remainingTime)}
294
- .autoCheckAt=${this.autoRenewTimeConfig.autoCheckAt}
295
- ?resetTimerCountdown=${this.resetTimerCountdown}
366
+ .time=${Number(this.lendingStatus.secondsLeftOnLoan)}
367
+ .autoCheckAt=${this.loanRenewConfig.autoCheckAt}
296
368
  ></timer-countdown>`
297
369
  : nothing;
298
370
  }
299
371
 
300
- get toastMessageTemplate() {
301
- return html`
302
- <toast-message
303
- .texts=${this.toastTexts}
304
- .dismisOnClick="1"
305
- ?hideToast=${this.hideToast}
306
- ></toast-message>
307
- `;
308
- }
309
-
310
372
  get bookActionBar() {
311
373
  return html`
312
- <button @click=${() => this.pageFlipEvent()}>Page Flip</button>
313
374
  <collapsible-action-group
314
375
  .userid=${this.userid}
315
376
  .identifier=${this.identifier}
@@ -319,30 +380,34 @@ export default class IABookActions extends LitElement {
319
380
  .width=${this.width}
320
381
  .borrowType=${this.borrowType}
321
382
  .returnUrl=${this.returnUrl}
322
- .autoRenew=${this.autoRenew}
323
- .autoRenewTimeConfig=${this.autoRenewTimeConfig}
383
+ .localCache=${this.localCache}
384
+ .loanRenewConfig=${this.loanRenewConfig}
324
385
  ?hasAdminAccess=${this.hasAdminAccess}
325
386
  ?disabled=${this.disableActionGroup}
326
- ?browseAgainNow=${this.browseAgainNow}
387
+ ?renewNow=${this.loanRenewResult.renewNow}
388
+ @loanAutoRenewed=${this.handleLoanAutoRenewed}
327
389
  @lendingActionError=${this.handleLendingActionError}
328
390
  @toggleActionGroup=${this.handleToggleActionGroup}
329
391
  >
330
392
  </collapsible-action-group>
331
393
  ${this.textGroupTemplate} ${this.infoIconTemplate}
332
- ${this.timerCountdownTemplate} ${this.toastMessageTemplate}
394
+ ${this.timerCountdownTemplate}
333
395
  `;
334
396
  }
335
397
 
336
- // will move this function to bookreader...
337
- async pageFlipEvent() {
338
- try {
339
- // dispatch event to say book page has flipped
340
- document.querySelector("ia-book-actions").dispatchEvent(
341
- new CustomEvent("autoRenewAttempt", {
342
- detail: { bookPageChange: true },
343
- })
344
- );
345
- } catch (error) { console.log(error) }
398
+ /**
399
+ * Execute after auto loan renewed is completed
400
+ * - show success message
401
+ * - then change the remaining time
402
+ * - reset timer state
403
+ */
404
+ async handleLoanAutoRenewed() {
405
+ if (this.loanRenewResult.renewNow) {
406
+ this.tokenPoller.disconnectedInterval();
407
+ await this.loanRenewHelper.showToastMessage();
408
+ await this.browseHasRenew();
409
+ await this.resetTimerCountState();
410
+ }
346
411
  }
347
412
 
348
413
  /**
@@ -483,11 +548,13 @@ export default class IABookActions extends LitElement {
483
548
  }
484
549
 
485
550
  get textGroupTemplate() {
486
- return html`<text-group
487
- textClass=${this.textClass}
488
- texts=${this.primaryTitle}
489
- >
490
- </text-group>`;
551
+ return this.primaryTitle
552
+ ? html`<text-group
553
+ textClass=${this.textClass}
554
+ .texts=${this.primaryTitle}
555
+ >
556
+ </text-group>`
557
+ : nothing;
491
558
  }
492
559
 
493
560
  get hasAdminAccess() {
@@ -498,20 +565,23 @@ export default class IABookActions extends LitElement {
498
565
  return css`
499
566
  :host {
500
567
  display: block;
501
- background: var(--primaryBGColor, #000);
502
- color: var(--primaryTextColor, #fff);
503
568
  }
569
+
504
570
  .lending-wrapper {
505
571
  width: 100%;
506
572
  margin: 0 auto;
573
+ background: var(--primaryBGColor, #000);
574
+ color: var(--primaryTextColor, #fff);
507
575
  display: inline-flex;
508
576
  align-items: center;
509
577
  justify-content: center;
510
578
  flex-wrap: wrap;
511
579
  }
512
580
 
513
- toast-message {
514
- --messageHeightFromTop: 18%;
581
+ toast-template {
582
+ --toastTopMargin: 80px;
583
+ --toastBGColor: #333;
584
+ --toastFontColor: #fff;
515
585
  }
516
586
  `;
517
587
  }
@@ -182,9 +182,7 @@ describe('Borrow status actions', () => {
182
182
  },
183
183
  ];
184
184
 
185
- expect(el.primaryTitle).to.equal(
186
- 'Renewable every hour, pending availability.'
187
- );
185
+ expect(el.primaryTitle).to.equal('');
188
186
  expect(el.primaryActions.length).to.equal(2);
189
187
  expect(el.primaryActions[0].text).to.equal('Log In and Borrow');
190
188
  expect(el.primaryActions[0].text).to.equal(expectedPrimaryActions[0].text);
@@ -253,7 +251,6 @@ describe('Browsing expired status', () => {
253
251
  await aTimeout(1500); // wait for 1.5 second
254
252
  await el.updateComplete;
255
253
 
256
- expect(el.primaryTitle).contains('Your loan has expired.');
257
254
  expect(el.primaryActions[0].text).to.equal('Borrow again');
258
255
  });
259
256
 
@@ -289,7 +286,6 @@ describe('Browsing expired status', () => {
289
286
  await aTimeout(1500); // wait for 1.5 sec
290
287
 
291
288
  expect(eventReceived).to.equal(true);
292
- expect(el.primaryTitle).contains('Your loan has expired.');
293
289
  expect(el.primaryActions[0].text).to.equal('Borrow again');
294
290
  expect(el.tokenPoller.loanTokenInterval).to.equal(undefined);
295
291
  });