@internetarchive/ia-book-actions 0.0.29 → 0.1.0
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 +55 -10
- package/package.json +5 -4
- package/src/assets/styles/collapsible-action-group.js +2 -1
- package/src/components/collapsible-action-group.js +27 -0
- package/src/components/info-icon.js +0 -9
- package/src/components/text-group.js +1 -1
- package/src/components/timer-countdown.js +178 -0
- package/src/core/config/analytics-event-and-category.js +1 -0
- package/src/core/config/sentry-events.js +20 -0
- package/src/core/services/actions-config.js +2 -2
- package/src/core/services/actions-handler/actions-handler-service.js +5 -2
- package/src/core/services/actions-handler/actions-handler.js +63 -5
- package/src/core/services/get-lending-actions.js +6 -11
- package/src/core/services/loan-renew-helper.js +161 -0
- package/src/core/services/loan-token-poller.js +19 -10
- package/src/ia-book-actions.js +339 -41
- package/test/components/collapsible-action-group.test.js +2 -2
- package/test/components/timer-countdown.test.js +45 -0
- package/test/core/services/get-lending-actions.test.js +4 -4
- package/test/core/services/loan-renew-helper.test.js +86 -0
- package/test/ia-book-actions.test.js +75 -12
package/src/ia-book-actions.js
CHANGED
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
/* eslint-disable camelcase */
|
|
2
|
-
import { html, css, LitElement } from 'lit';
|
|
2
|
+
import { html, css, LitElement, nothing } from 'lit';
|
|
3
3
|
|
|
4
4
|
import { SharedResizeObserver } from '@internetarchive/shared-resize-observer';
|
|
5
5
|
import { ModalConfig } from '@internetarchive/modal-manager';
|
|
6
|
+
import { ToastConfig } from '@internetarchive/toast-manager';
|
|
7
|
+
import { LocalCache } from '@internetarchive/local-cache';
|
|
6
8
|
|
|
7
9
|
import './components/collapsible-action-group.js';
|
|
8
10
|
import './components/book-title-bar.js';
|
|
9
11
|
import './components/text-group.js';
|
|
10
12
|
import './components/info-icon.js';
|
|
13
|
+
import './components/timer-countdown.js';
|
|
11
14
|
|
|
12
15
|
import { GetLendingActions } from './core/services/get-lending-actions.js';
|
|
13
16
|
import { mobileContainerWidth } from './core/config/constants.js';
|
|
17
|
+
import { sentryLogs } from './core/config/sentry-events.js';
|
|
14
18
|
import { LoanTokenPoller } from './core/services/loan-token-poller.js';
|
|
19
|
+
import { LoanRenewHelper } from './core/services/loan-renew-helper.js';
|
|
15
20
|
|
|
16
21
|
export const events = {
|
|
17
22
|
browseExpired: 'IABookReader:BrowsingHasExpired',
|
|
@@ -36,6 +41,9 @@ export default class IABookActions extends LitElement {
|
|
|
36
41
|
disableActionGroup: { type: Boolean },
|
|
37
42
|
modal: { Object },
|
|
38
43
|
tokenDelay: { type: Number },
|
|
44
|
+
localCache: { type: Object },
|
|
45
|
+
loanRenewTimeConfig: { type: Object },
|
|
46
|
+
loanRenewResult: { type: Object },
|
|
39
47
|
};
|
|
40
48
|
}
|
|
41
49
|
|
|
@@ -53,7 +61,7 @@ export default class IABookActions extends LitElement {
|
|
|
53
61
|
this.sharedObserver = undefined;
|
|
54
62
|
this.disableActionGroup = false;
|
|
55
63
|
this.modal = undefined;
|
|
56
|
-
this.tokenDelay =
|
|
64
|
+
this.tokenDelay = 120; // in seconds
|
|
57
65
|
|
|
58
66
|
// private props
|
|
59
67
|
this.primaryActions = [];
|
|
@@ -63,15 +71,51 @@ export default class IABookActions extends LitElement {
|
|
|
63
71
|
this.lendingOptions = {};
|
|
64
72
|
this.borrowType = ''; // (browsed|borrowed)
|
|
65
73
|
this.consecutiveLoanCounts = 1; // consecutive loan count
|
|
74
|
+
this.suppressToast = false;
|
|
75
|
+
this.browseTimer = undefined;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* contains one hour auto-loan-renew time configuration
|
|
79
|
+
*
|
|
80
|
+
* @type {object} loanRenewTimeConfig
|
|
81
|
+
* @property {number} loanTotalTime - total seconds a loan does have
|
|
82
|
+
* @property {number} loanRenewAtLast - check for loan renew at last
|
|
83
|
+
* @property {number} pageChangedInLast - consider loan renew eligible if viewed new page
|
|
84
|
+
*/
|
|
85
|
+
this.loanRenewTimeConfig = {};
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* contains one hour auto-loan-renew response
|
|
89
|
+
*
|
|
90
|
+
* @type {object} loanRenewResult
|
|
91
|
+
* @property {string} texts - texts messages need to show in toast message
|
|
92
|
+
* @property {boolean} renewNow - key to determine if need to renew now
|
|
93
|
+
* @property {number} secondsLeft - seconds left in active loan
|
|
94
|
+
*/
|
|
95
|
+
this.loanRenewResult = { texts: '', renewNow: false, secondsLeft: 0 };
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* need showdowRoot opened for resetTimerCountState
|
|
99
|
+
* @see IABookActions::resetTimerCountState
|
|
100
|
+
*/
|
|
101
|
+
this._shadowRoot = this.attachShadow({ mode: 'open' });
|
|
66
102
|
}
|
|
67
103
|
|
|
68
104
|
disconnectedCallback() {
|
|
69
105
|
this.tokenPoller?.disconnectedInterval();
|
|
70
|
-
window?.Sentry?.captureMessage(
|
|
106
|
+
window?.Sentry?.captureMessage(sentryLogs.disconnectedCallback);
|
|
71
107
|
this.disconnectResizeObserver();
|
|
72
108
|
}
|
|
73
109
|
|
|
74
110
|
firstUpdated() {
|
|
111
|
+
// bind auto loan renew events
|
|
112
|
+
this.bindLoanRenewEvents();
|
|
113
|
+
|
|
114
|
+
// localCache used for auto-loan-renew
|
|
115
|
+
this.localCache = new LocalCache({
|
|
116
|
+
namespace: 'loanRenew',
|
|
117
|
+
});
|
|
118
|
+
|
|
75
119
|
if (!this.sharedObserver) {
|
|
76
120
|
this.sharedObserver = new SharedResizeObserver();
|
|
77
121
|
this.setupResizeObserver();
|
|
@@ -90,27 +134,6 @@ export default class IABookActions extends LitElement {
|
|
|
90
134
|
}
|
|
91
135
|
}
|
|
92
136
|
|
|
93
|
-
browseHasExpired() {
|
|
94
|
-
const currStatus = { ...this.lendingStatus, browsingExpired: true };
|
|
95
|
-
this.lendingStatus = currStatus;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
startBrowseTimer() {
|
|
99
|
-
const {
|
|
100
|
-
browsingExpired,
|
|
101
|
-
user_has_browsed,
|
|
102
|
-
secondsLeftOnLoan,
|
|
103
|
-
} = this.lendingStatus;
|
|
104
|
-
if (!user_has_browsed || browsingExpired) {
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const timeLeft = secondsLeftOnLoan * 1000;
|
|
109
|
-
setTimeout(() => {
|
|
110
|
-
this.browseHasExpired();
|
|
111
|
-
}, timeLeft);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
137
|
/** SharedObserver resize handler */
|
|
115
138
|
handleResize(entry) {
|
|
116
139
|
// if you are observing multiple targets,
|
|
@@ -144,6 +167,8 @@ export default class IABookActions extends LitElement {
|
|
|
144
167
|
/** End SharedObserver resize handler */
|
|
145
168
|
|
|
146
169
|
async setupLendingToolbarActions() {
|
|
170
|
+
this.suppressToast = false;
|
|
171
|
+
|
|
147
172
|
this.lendingOptions = new GetLendingActions(
|
|
148
173
|
this.userid,
|
|
149
174
|
this.identifier,
|
|
@@ -169,10 +194,8 @@ export default class IABookActions extends LitElement {
|
|
|
169
194
|
'browsingExpired' in this.lendingStatus &&
|
|
170
195
|
this.lendingStatus?.browsingExpired;
|
|
171
196
|
if (hasExpired) {
|
|
172
|
-
if (!this.tokenPoller
|
|
173
|
-
window?.Sentry?.captureMessage(
|
|
174
|
-
'setupLendingToolbarActions hasExpired - no tokenPoller'
|
|
175
|
-
);
|
|
197
|
+
if (!this.tokenPoller) {
|
|
198
|
+
window?.Sentry?.captureMessage(sentryLogs.bookWasExpired);
|
|
176
199
|
}
|
|
177
200
|
this.tokenPoller?.disconnectedInterval();
|
|
178
201
|
/** Global event - always fire */
|
|
@@ -183,12 +206,13 @@ export default class IABookActions extends LitElement {
|
|
|
183
206
|
composed: true,
|
|
184
207
|
})
|
|
185
208
|
);
|
|
209
|
+
|
|
210
|
+
// early return if book is already expired
|
|
186
211
|
return;
|
|
187
212
|
}
|
|
188
213
|
|
|
189
214
|
if (this.borrowType === 'browsed') {
|
|
190
215
|
// start timer for browsed.
|
|
191
|
-
// when browse is completed, we shows browse-again button
|
|
192
216
|
this.startBrowseTimer();
|
|
193
217
|
}
|
|
194
218
|
|
|
@@ -198,7 +222,214 @@ export default class IABookActions extends LitElement {
|
|
|
198
222
|
return;
|
|
199
223
|
}
|
|
200
224
|
|
|
201
|
-
|
|
225
|
+
// initial timer state for 1-hour loan
|
|
226
|
+
if (this.borrowType === 'browsed' && !this.loanRenewResult.renewNow) {
|
|
227
|
+
setTimeout(() => {
|
|
228
|
+
this.resetTimerCountState();
|
|
229
|
+
}, 100);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* tokenPoller determines if user has loan token for this book
|
|
234
|
+
* - if book is going to renew, need to wait until renew is completed
|
|
235
|
+
* - otherwise execute immediately
|
|
236
|
+
*/
|
|
237
|
+
setTimeout(
|
|
238
|
+
() => {
|
|
239
|
+
this.startLoanTokenPoller();
|
|
240
|
+
},
|
|
241
|
+
|
|
242
|
+
// if book is renew while reading, let's wait to execute create-token api
|
|
243
|
+
this.loanRenewResult.renewNow ? this.tokenDelay * 1000 : 100
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Bind 1 hour loan auto renew event,
|
|
249
|
+
* There are two events we want to use,
|
|
250
|
+
* 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
|
|
253
|
+
*/
|
|
254
|
+
bindLoanRenewEvents() {
|
|
255
|
+
/**
|
|
256
|
+
* dispatched this event from bookreader page changed
|
|
257
|
+
*/
|
|
258
|
+
window.addEventListener('BookReader:userAction', () => {
|
|
259
|
+
if (this.borrowType === 'browsed') {
|
|
260
|
+
this.suppressToast = false;
|
|
261
|
+
this.autoLoanRenewChecker(true);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return nothing;
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* detect click-event on document to close toast-template
|
|
269
|
+
*/
|
|
270
|
+
document.addEventListener('click', e => {
|
|
271
|
+
if (this.loanRenewHelper && this.loanRenewResult.secondsLeft > 0) {
|
|
272
|
+
this.suppressToast = true;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// reload when user click on book-page and book is EXPIRED
|
|
276
|
+
const hasExpired =
|
|
277
|
+
'browsingExpired' in this.lendingStatus &&
|
|
278
|
+
this.lendingStatus?.browsingExpired;
|
|
279
|
+
if (hasExpired && e.target.nodeName !== 'IA-BOOK-ACTIONS') {
|
|
280
|
+
window.location?.reload();
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* this event dispatched from timer-countdown component for:
|
|
286
|
+
* 1. user turned book page after showing auto-returned warning message
|
|
287
|
+
* 2. show warning message having remaining time
|
|
288
|
+
* @see TimerCountdown::timerCountdown
|
|
289
|
+
*/
|
|
290
|
+
this.addEventListener('IABookActions:loanRenew', async event => {
|
|
291
|
+
await this.autoLoanRenewChecker(false);
|
|
292
|
+
|
|
293
|
+
// show warning message with remaining time to auto returned it.
|
|
294
|
+
if (this.loanRenewResult.renewNow === false) {
|
|
295
|
+
this.loanRenewResult.secondsLeft = event.detail.secondsLeft;
|
|
296
|
+
this.showToastMessage();
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* To determine if need to be renewed browsed book
|
|
303
|
+
* @see LoanRenewHelper
|
|
304
|
+
*
|
|
305
|
+
* @param {Boolean} hasPageChanged
|
|
306
|
+
*/
|
|
307
|
+
async autoLoanRenewChecker(hasPageChanged = false) {
|
|
308
|
+
this.loanRenewHelper = new LoanRenewHelper(
|
|
309
|
+
hasPageChanged,
|
|
310
|
+
this.identifier,
|
|
311
|
+
this.localCache,
|
|
312
|
+
this.loanRenewTimeConfig
|
|
313
|
+
);
|
|
314
|
+
|
|
315
|
+
await this.loanRenewHelper.handleLoanRenew();
|
|
316
|
+
this.loanRenewResult = this.loanRenewHelper.result;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Show toast messages on some specific loan renew features. e.g.
|
|
321
|
+
* - show message when book is auto renewed
|
|
322
|
+
* - show message when book is expired
|
|
323
|
+
* - show message having remaining time when book is about to auto returned
|
|
324
|
+
*/
|
|
325
|
+
async showToastMessage() {
|
|
326
|
+
if (this.suppressToast) return;
|
|
327
|
+
|
|
328
|
+
const iaBookActions = document.querySelector('ia-book-actions')?.shadowRoot;
|
|
329
|
+
if (!iaBookActions) return;
|
|
330
|
+
|
|
331
|
+
let toastTemplate = iaBookActions.querySelector('toast-template');
|
|
332
|
+
if (!toastTemplate) {
|
|
333
|
+
toastTemplate = document.createElement('toast-template');
|
|
334
|
+
}
|
|
335
|
+
await iaBookActions.appendChild(toastTemplate);
|
|
336
|
+
|
|
337
|
+
// if secondsLeft < 60, consider it 1 minute
|
|
338
|
+
let { secondsLeft } = this.loanRenewResult;
|
|
339
|
+
secondsLeft = secondsLeft > 60 ? secondsLeft : 60;
|
|
340
|
+
|
|
341
|
+
const config = new ToastConfig();
|
|
342
|
+
config.dismisOnClick = true;
|
|
343
|
+
config.texts = this.loanRenewHelper?.getMessageTexts(
|
|
344
|
+
this.loanRenewResult.texts,
|
|
345
|
+
secondsLeft
|
|
346
|
+
);
|
|
347
|
+
|
|
348
|
+
toastTemplate.showToast({
|
|
349
|
+
config,
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
async browseHasRenew() {
|
|
354
|
+
const loanTime = await this.localCache.get(`${this.identifier}-loanTime`);
|
|
355
|
+
const secondsLeft = (loanTime - new Date()) / 1000; // different in seconds
|
|
356
|
+
|
|
357
|
+
const currStatus = {
|
|
358
|
+
...this.lendingStatus,
|
|
359
|
+
user_has_browsed: true,
|
|
360
|
+
browsingExpired: false,
|
|
361
|
+
secondsLeftOnLoan: secondsLeft,
|
|
362
|
+
};
|
|
363
|
+
this.lendingStatus = currStatus;
|
|
364
|
+
|
|
365
|
+
this.startBrowseTimer();
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
startBrowseTimer() {
|
|
369
|
+
clearTimeout(this.browseTimer);
|
|
370
|
+
const {
|
|
371
|
+
browsingExpired,
|
|
372
|
+
user_has_browsed,
|
|
373
|
+
secondsLeftOnLoan,
|
|
374
|
+
} = this.lendingStatus;
|
|
375
|
+
if (!user_has_browsed || browsingExpired) {
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
this.browseTimer = setTimeout(() => {
|
|
380
|
+
this.browseHasExpired();
|
|
381
|
+
}, secondsLeftOnLoan * 1000);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Execute when loan is expired
|
|
386
|
+
*/
|
|
387
|
+
async browseHasExpired() {
|
|
388
|
+
this.tokenPoller?.disconnectedInterval();
|
|
389
|
+
|
|
390
|
+
const currStatus = { ...this.lendingStatus, browsingExpired: true };
|
|
391
|
+
this.lendingStatus = currStatus;
|
|
392
|
+
|
|
393
|
+
// remove respected key:value for loan-renew
|
|
394
|
+
await this.localCache.delete(`${this.identifier}-loanTime`);
|
|
395
|
+
await this.localCache.delete(`${this.identifier}-pageChangedTime`);
|
|
396
|
+
|
|
397
|
+
// show message after browsed book is expired.
|
|
398
|
+
this.loanRenewResult.renewNow = false;
|
|
399
|
+
this.loanRenewResult.texts =
|
|
400
|
+
'This book has been automatically returned due to inactivity.';
|
|
401
|
+
|
|
402
|
+
this.suppressToast = false;
|
|
403
|
+
this.showToastMessage();
|
|
404
|
+
|
|
405
|
+
window?.Sentry?.captureMessage(sentryLogs.browseHasExpired);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Reset timer animation state after book renewed
|
|
410
|
+
*/
|
|
411
|
+
async resetTimerCountState() {
|
|
412
|
+
const timerCountdown = this._shadowRoot.querySelector('timer-countdown');
|
|
413
|
+
if (!timerCountdown) return;
|
|
414
|
+
|
|
415
|
+
const secondsLeft = Number(this.lendingStatus.secondsLeftOnLoan);
|
|
416
|
+
const strokeDashOffset =
|
|
417
|
+
(secondsLeft / this.loanRenewTimeConfig.loanTotalTime) * 315;
|
|
418
|
+
|
|
419
|
+
// set seconds left in loan expire
|
|
420
|
+
timerCountdown.style?.setProperty('--secondsLeftOnLoan', `${secondsLeft}s`);
|
|
421
|
+
|
|
422
|
+
// set circle stroke offset left in loan expire
|
|
423
|
+
timerCountdown.style?.setProperty(
|
|
424
|
+
'--strokeLeftOnLoan',
|
|
425
|
+
`${Number(strokeDashOffset)}` // the perimeter of the circle = (π * 2 * radius)
|
|
426
|
+
);
|
|
427
|
+
|
|
428
|
+
const animationCircle = timerCountdown.shadowRoot.querySelector('.circle');
|
|
429
|
+
animationCircle.style.animationName = 'none';
|
|
430
|
+
setTimeout(() => {
|
|
431
|
+
animationCircle.style.animationName = 'circletimer';
|
|
432
|
+
}, 100);
|
|
202
433
|
}
|
|
203
434
|
|
|
204
435
|
render() {
|
|
@@ -220,6 +451,16 @@ export default class IABookActions extends LitElement {
|
|
|
220
451
|
></book-title-bar>`;
|
|
221
452
|
}
|
|
222
453
|
|
|
454
|
+
get timerCountdownTemplate() {
|
|
455
|
+
return this.borrowType === 'browsed'
|
|
456
|
+
? html`<timer-countdown
|
|
457
|
+
.secondsLeftOnLoan=${Number(this.lendingStatus.secondsLeftOnLoan)}
|
|
458
|
+
.loanTotalTime=${this.loanRenewTimeConfig.loanTotalTime}
|
|
459
|
+
.loanRenewAtLast=${this.loanRenewTimeConfig.loanRenewAtLast}
|
|
460
|
+
></timer-countdown>`
|
|
461
|
+
: nothing;
|
|
462
|
+
}
|
|
463
|
+
|
|
223
464
|
get bookActionBar() {
|
|
224
465
|
return html`
|
|
225
466
|
<collapsible-action-group
|
|
@@ -231,23 +472,56 @@ export default class IABookActions extends LitElement {
|
|
|
231
472
|
.width=${this.width}
|
|
232
473
|
.borrowType=${this.borrowType}
|
|
233
474
|
.returnUrl=${this.returnUrl}
|
|
475
|
+
.localCache=${this.localCache}
|
|
476
|
+
.loanTotalTime=${this.loanRenewTimeConfig.loanTotalTime}
|
|
234
477
|
?hasAdminAccess=${this.hasAdminAccess}
|
|
235
478
|
?disabled=${this.disableActionGroup}
|
|
479
|
+
?renewNow=${this.loanRenewResult.renewNow}
|
|
480
|
+
@loanAutoRenewed=${this.handleLoanAutoRenewed}
|
|
236
481
|
@lendingActionError=${this.handleLendingActionError}
|
|
237
482
|
@toggleActionGroup=${this.handleToggleActionGroup}
|
|
238
483
|
>
|
|
239
484
|
</collapsible-action-group>
|
|
240
485
|
${this.textGroupTemplate} ${this.infoIconTemplate}
|
|
486
|
+
${this.timerCountdownTemplate}
|
|
241
487
|
`;
|
|
242
488
|
}
|
|
243
489
|
|
|
490
|
+
/**
|
|
491
|
+
* Execute after auto loan renewed is completed
|
|
492
|
+
* - show success message
|
|
493
|
+
* - then change the remaining time
|
|
494
|
+
* - reset timer state
|
|
495
|
+
*/
|
|
496
|
+
async handleLoanAutoRenewed() {
|
|
497
|
+
if (this.loanRenewResult.renewNow) {
|
|
498
|
+
this.tokenPoller?.disconnectedInterval();
|
|
499
|
+
|
|
500
|
+
this.suppressToast = false;
|
|
501
|
+
await this.showToastMessage();
|
|
502
|
+
await this.browseHasRenew();
|
|
503
|
+
await this.resetTimerCountState();
|
|
504
|
+
|
|
505
|
+
window?.Sentry?.captureMessage(`${sentryLogs.bookHasRenewed}`);
|
|
506
|
+
// TODO: analytics?? for consecutive renew.....
|
|
507
|
+
|
|
508
|
+
// testing console....
|
|
509
|
+
console.log(sentryLogs.bookHasRenewed, {
|
|
510
|
+
loanRenewResult: this.loanRenewResult,
|
|
511
|
+
browseHasRenew: this.lendingStatus.secondsLeftOnLoan,
|
|
512
|
+
resetTimerCountState: this._shadowRoot.querySelector('timer-countdown'),
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
244
517
|
/**
|
|
245
518
|
* enable access of borrowed/browsed books
|
|
519
|
+
* @see LoanTokenPoller
|
|
246
520
|
*/
|
|
247
521
|
startLoanTokenPoller() {
|
|
248
522
|
if (this.tokenPoller) {
|
|
249
|
-
window?.Sentry?.captureMessage(
|
|
250
|
-
this.tokenPoller
|
|
523
|
+
window?.Sentry?.captureMessage(sentryLogs.clearTokenPoller);
|
|
524
|
+
this.tokenPoller?.disconnectedInterval();
|
|
251
525
|
}
|
|
252
526
|
const successCallback = () => {
|
|
253
527
|
this.lendingBarPostInit();
|
|
@@ -272,7 +546,7 @@ export default class IABookActions extends LitElement {
|
|
|
272
546
|
this.borrowType,
|
|
273
547
|
successCallback,
|
|
274
548
|
errorCallback,
|
|
275
|
-
this.tokenDelay //
|
|
549
|
+
this.tokenDelay // in seconds
|
|
276
550
|
);
|
|
277
551
|
}
|
|
278
552
|
|
|
@@ -303,6 +577,20 @@ export default class IABookActions extends LitElement {
|
|
|
303
577
|
|
|
304
578
|
if (errorMsg) this.showErrorModal(errorMsg, action);
|
|
305
579
|
|
|
580
|
+
// if error related to loan token
|
|
581
|
+
// - clear tokenPoller interval
|
|
582
|
+
// - set user_has_browsed to `false`
|
|
583
|
+
if (action === 'create_token') {
|
|
584
|
+
this.tokenPoller?.disconnectedInterval();
|
|
585
|
+
|
|
586
|
+
const currStatus = {
|
|
587
|
+
...this.lendingStatus,
|
|
588
|
+
user_has_browsed: false,
|
|
589
|
+
available_to_browse: true,
|
|
590
|
+
};
|
|
591
|
+
this.lendingStatus = currStatus;
|
|
592
|
+
}
|
|
593
|
+
|
|
306
594
|
// update action bar state if book is not available to browse or borrow.
|
|
307
595
|
if (errorMsg && errorMsg.match(/not available to borrow/gm)) {
|
|
308
596
|
let currStatus = this.lendingStatus;
|
|
@@ -358,7 +646,8 @@ export default class IABookActions extends LitElement {
|
|
|
358
646
|
href="mailto:info@archive.org?subject=Help: cannot access my borrowed book: ${this
|
|
359
647
|
.identifier}"
|
|
360
648
|
>info@archive.org</a
|
|
361
|
-
|
|
649
|
+
><br /><br />
|
|
650
|
+
<code>errorLog: ${errorMsg}</code>`;
|
|
362
651
|
}
|
|
363
652
|
|
|
364
653
|
this.modal.showModal({
|
|
@@ -379,11 +668,13 @@ export default class IABookActions extends LitElement {
|
|
|
379
668
|
}
|
|
380
669
|
|
|
381
670
|
get textGroupTemplate() {
|
|
382
|
-
return
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
671
|
+
return this.primaryTitle
|
|
672
|
+
? html`<text-group
|
|
673
|
+
textClass=${this.textClass}
|
|
674
|
+
.texts=${this.primaryTitle}
|
|
675
|
+
>
|
|
676
|
+
</text-group>`
|
|
677
|
+
: nothing;
|
|
387
678
|
}
|
|
388
679
|
|
|
389
680
|
get hasAdminAccess() {
|
|
@@ -394,17 +685,24 @@ export default class IABookActions extends LitElement {
|
|
|
394
685
|
return css`
|
|
395
686
|
:host {
|
|
396
687
|
display: block;
|
|
397
|
-
background: var(--primaryBGColor, #000);
|
|
398
|
-
color: var(--primaryTextColor, #fff);
|
|
399
688
|
}
|
|
689
|
+
|
|
400
690
|
.lending-wrapper {
|
|
401
691
|
width: 100%;
|
|
402
692
|
margin: 0 auto;
|
|
693
|
+
background: var(--primaryBGColor, #000);
|
|
694
|
+
color: var(--primaryTextColor, #fff);
|
|
403
695
|
display: inline-flex;
|
|
404
696
|
align-items: center;
|
|
405
697
|
justify-content: center;
|
|
406
698
|
flex-wrap: wrap;
|
|
407
699
|
}
|
|
700
|
+
|
|
701
|
+
toast-template {
|
|
702
|
+
--toastTopMargin: 80px;
|
|
703
|
+
--toastBGColor: #333;
|
|
704
|
+
--toastFontColor: #fff;
|
|
705
|
+
}
|
|
408
706
|
`;
|
|
409
707
|
}
|
|
410
708
|
}
|
|
@@ -6,7 +6,7 @@ const container = ({
|
|
|
6
6
|
{
|
|
7
7
|
analyticsEvent: { action: 'Browse', category: 'Lending' },
|
|
8
8
|
className: 'ia-button primary',
|
|
9
|
-
text: 'Borrow
|
|
9
|
+
text: 'Borrow',
|
|
10
10
|
id: 'browseBook',
|
|
11
11
|
},
|
|
12
12
|
{
|
|
@@ -40,7 +40,7 @@ describe('<collapsible-action-group>', () => {
|
|
|
40
40
|
.true;
|
|
41
41
|
|
|
42
42
|
const primaryButton = primaryActionContainer.querySelector('.ia-button');
|
|
43
|
-
expect(primaryButton.innerText).to.equal('Borrow
|
|
43
|
+
expect(primaryButton.innerText).to.equal('Borrow');
|
|
44
44
|
});
|
|
45
45
|
|
|
46
46
|
it('check if loader is active and action-group is disabled', async () => {
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { html, fixture, expect, aTimeout } from '@open-wc/testing';
|
|
2
|
+
import '../../src/components/timer-countdown.js';
|
|
3
|
+
|
|
4
|
+
const container = ({ secondsLeftOnLoan, loanRenewTimeConfig } = {}) =>
|
|
5
|
+
html`<timer-countdown
|
|
6
|
+
.secondsLeftOnLoan=${secondsLeftOnLoan}
|
|
7
|
+
.loanRenewTimeConfig=${loanRenewTimeConfig}
|
|
8
|
+
></timer-countdown>`;
|
|
9
|
+
|
|
10
|
+
describe('<timer-countdown>', () => {
|
|
11
|
+
it('timer interval is undefined when loan is expired', async () => {
|
|
12
|
+
const el = await fixture(
|
|
13
|
+
container({
|
|
14
|
+
secondsLeftOnLoan: 0, // in seconds
|
|
15
|
+
loanRenewTimeConfig: {
|
|
16
|
+
loanTotalTime: 14, // in seconds
|
|
17
|
+
loanRenewAtLast: 5, // in seconds
|
|
18
|
+
pageChangedInLast: 5, // in seconds
|
|
19
|
+
},
|
|
20
|
+
})
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
await el.updateComplete;
|
|
24
|
+
expect(el.timerInterval).to.be.undefined;
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('timer interval is not undefined when loan is active', async () => {
|
|
28
|
+
const el = await fixture(
|
|
29
|
+
container({
|
|
30
|
+
secondsLeftOnLoan: 2, // in seconds
|
|
31
|
+
loanRenewTimeConfig: {
|
|
32
|
+
loanTotalTime: 14, // in seconds
|
|
33
|
+
loanRenewAtLast: 5, // in seconds
|
|
34
|
+
pageChangedInLast: 5, // in seconds
|
|
35
|
+
},
|
|
36
|
+
})
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
await aTimeout(1900);
|
|
40
|
+
await el.updateComplete;
|
|
41
|
+
|
|
42
|
+
expect(el.secondsLeftOnLoan).to.equal(2);
|
|
43
|
+
expect(el.timerInterval).to.not.be.undefined;
|
|
44
|
+
});
|
|
45
|
+
});
|
|
@@ -25,7 +25,7 @@ describe('Get Lending Actions', () => {
|
|
|
25
25
|
|
|
26
26
|
expect(actions.primaryTitle).to.equal(bookTitles.available_1hr);
|
|
27
27
|
expect(actions.primaryActions.length).to.equal(3);
|
|
28
|
-
expect(actions.primaryActions[0].text).to.equal('Borrow
|
|
28
|
+
expect(actions.primaryActions[0].text).to.equal('Borrow');
|
|
29
29
|
expect(actions.primaryActions[1].text).to.equal('Borrow for 14 days');
|
|
30
30
|
expect(actions.secondaryActions[1].text).to.equal('Purchase at');
|
|
31
31
|
});
|
|
@@ -111,7 +111,7 @@ describe('Get Lending Actions', () => {
|
|
|
111
111
|
expect(actions.primaryTitle).to.equal(bookTitles.claim_waitlist);
|
|
112
112
|
expect(actions.primaryActions.length).to.equal(3);
|
|
113
113
|
expect(actions.primaryActions[0].text).to.equal('Borrow for 14 days');
|
|
114
|
-
expect(actions.primaryActions[1].text).to.equal('Borrow
|
|
114
|
+
expect(actions.primaryActions[1].text).to.equal('Borrow');
|
|
115
115
|
expect(actions.primaryActions[2].text).to.equal('Leave Waitlist');
|
|
116
116
|
expect(spy.called).to.be.true;
|
|
117
117
|
});
|
|
@@ -138,7 +138,7 @@ describe('Get Lending Actions', () => {
|
|
|
138
138
|
expect(actions.primaryTitle).to.equal(bookTitles.on_waitlist);
|
|
139
139
|
expect(actions.primaryActions.length).to.equal(2);
|
|
140
140
|
expect(actions.primaryActions[0].text).to.equal('Leave Waitlist');
|
|
141
|
-
expect(actions.primaryActions[1].text).to.equal('Borrow
|
|
141
|
+
expect(actions.primaryActions[1].text).to.equal('Borrow');
|
|
142
142
|
expect(claimSpy.called).to.be.false;
|
|
143
143
|
expect(waitlistSpy.called).to.be.true;
|
|
144
144
|
});
|
|
@@ -162,7 +162,7 @@ describe('Get Lending Actions', () => {
|
|
|
162
162
|
expect(actions.primaryTitle).to.equal(bookTitles.on_waitlist);
|
|
163
163
|
expect(actions.primaryActions.length).to.equal(2);
|
|
164
164
|
expect(actions.primaryActions[0].text).to.equal('Leave Waitlist');
|
|
165
|
-
expect(actions.primaryActions[1].text).to.equal('Borrow
|
|
165
|
+
expect(actions.primaryActions[1].text).to.equal('Borrow');
|
|
166
166
|
expect(waitlistSpy.called).to.be.true;
|
|
167
167
|
});
|
|
168
168
|
});
|