@internetarchive/ia-book-actions 0.1.9-alpha2 → 0.1.9-alpha4
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 +1 -1
- package/package.json +2 -1
- package/src/assets/data/lending-get-status-service.js +1 -1
- package/src/components/collapsible-action-group.js +10 -4
- package/src/components/timer-countdown.js +2 -1
- package/src/core/services/loan-renew-helper.js +1 -1
- package/src/ia-book-actions.js +110 -91
- package/test/core/services/loan-renew-helper.test.js +1 -1
- package/test/ia-book-actions.test.js +4 -2
package/demo/index.html
CHANGED
|
@@ -131,7 +131,7 @@
|
|
|
131
131
|
iaBookActions.returnUrl = '';
|
|
132
132
|
iaBookActions.localCache = localCache;
|
|
133
133
|
|
|
134
|
-
let loanTotalTime2Min = { loanTotalTime:
|
|
134
|
+
let loanTotalTime2Min = { loanTotalTime: 65, loanRenewAtLast: 60, pageChangedInLast: 15 };
|
|
135
135
|
let loanTotalTime5Min = { loanTotalTime: 300, loanRenewAtLast: 180, pageChangedInLast: 15 };
|
|
136
136
|
let loanTotalTime1Hour = { loanTotalTime: 3600, loanRenewAtLast: 660, pageChangedInLast: 900 };
|
|
137
137
|
|
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.9-alpha4",
|
|
4
4
|
"description": "Bookreader actions for borrowable book",
|
|
5
5
|
"author": "Internet Archive",
|
|
6
6
|
"license": "AGPL-3.0-only",
|
|
@@ -18,6 +18,7 @@
|
|
|
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
24
|
"@internetarchive/modal-manager": "^0.2.12",
|
|
@@ -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:
|
|
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
|
|
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 =
|
|
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
|
|
@@ -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
|
|
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
|
*
|
package/src/ia-book-actions.js
CHANGED
|
@@ -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,11 +24,17 @@ 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
|
+
*/
|
|
26
31
|
export const modalButtonStyle = {
|
|
27
32
|
iaButton:
|
|
28
|
-
'min-height:
|
|
29
|
-
renew: 'background:#194880;
|
|
30
|
-
return: 'background:#d9534f;
|
|
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;',
|
|
31
38
|
refresh:
|
|
32
39
|
'background:none;font-size:inherit;border:0;padding:0;color:#0000ee;cursor:pointer;text-decoration:underline',
|
|
33
40
|
};
|
|
@@ -71,7 +78,6 @@ export default class IABookActions extends LitElement {
|
|
|
71
78
|
this.sharedObserver = undefined;
|
|
72
79
|
this.disableActionGroup = false;
|
|
73
80
|
this.modal = undefined;
|
|
74
|
-
this.toast = undefined;
|
|
75
81
|
this.tokenDelay = 120; // in seconds
|
|
76
82
|
|
|
77
83
|
// private props
|
|
@@ -81,10 +87,19 @@ export default class IABookActions extends LitElement {
|
|
|
81
87
|
this.secondaryActions = [];
|
|
82
88
|
this.lendingOptions = {};
|
|
83
89
|
this.borrowType = ''; // (browsed|borrowed)
|
|
84
|
-
this.
|
|
90
|
+
this.browseTimer = undefined;
|
|
91
|
+
|
|
92
|
+
/** @deprecated */
|
|
93
|
+
this.toast = undefined;
|
|
94
|
+
/** @deprecated */
|
|
85
95
|
this.suppressToast = false;
|
|
96
|
+
/** @deprecated */
|
|
86
97
|
this.suppressAutoRenew = false;
|
|
87
|
-
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* when user click on [return the book] button on warning modal
|
|
101
|
+
*/
|
|
102
|
+
this.returnNow = false;
|
|
88
103
|
|
|
89
104
|
/**
|
|
90
105
|
* contains one hour auto-loan-renew time configuration
|
|
@@ -258,19 +273,13 @@ export default class IABookActions extends LitElement {
|
|
|
258
273
|
* Bind 1 hour loan auto renew event,
|
|
259
274
|
* There are two events we want to use,
|
|
260
275
|
* 1. BookReader:userAction - dispatched from bookreader side
|
|
261
|
-
* 2.
|
|
262
|
-
* 3. IABookActions:loanRenew - dispatched from timer-countdown component
|
|
276
|
+
* 2. IABookActions:loanRenew - dispatched from timer-countdown component
|
|
263
277
|
*/
|
|
264
278
|
bindLoanRenewEvents() {
|
|
265
279
|
/**
|
|
266
280
|
* dispatched this event from bookreader page changed
|
|
267
281
|
*/
|
|
268
282
|
window.addEventListener('BookReader:userAction', () => {
|
|
269
|
-
console.log('** BR user Action', {
|
|
270
|
-
suppressAutoRenew: this.suppressAutoRenew,
|
|
271
|
-
borrowType: this.borrowType,
|
|
272
|
-
});
|
|
273
|
-
|
|
274
283
|
this.suppressToast = true;
|
|
275
284
|
this.closeToastManager();
|
|
276
285
|
|
|
@@ -283,32 +292,13 @@ export default class IABookActions extends LitElement {
|
|
|
283
292
|
}
|
|
284
293
|
});
|
|
285
294
|
|
|
286
|
-
/**
|
|
287
|
-
* detect click-event on document to close toast-template
|
|
288
|
-
*/
|
|
289
|
-
document.addEventListener('click', e => {
|
|
290
|
-
console.log('*** top level click handler to remove toast on click');
|
|
291
|
-
if (this.loanRenewHelper && this.loanRenewResult.secondsLeft > 0) {
|
|
292
|
-
this.suppressToast = true;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
// reload when user click on book-page and book is EXPIRED
|
|
296
|
-
const hasExpired =
|
|
297
|
-
'browsingExpired' in this.lendingStatus &&
|
|
298
|
-
this.lendingStatus?.browsingExpired;
|
|
299
|
-
if (hasExpired && e.target.nodeName !== 'IA-BOOK-ACTIONS') {
|
|
300
|
-
// window.location?.reload();
|
|
301
|
-
}
|
|
302
|
-
});
|
|
303
|
-
|
|
304
295
|
/**
|
|
305
296
|
* this event dispatched from timer-countdown component for:
|
|
306
|
-
* 1. user turned book page after showing auto-returned warning
|
|
307
|
-
* 2. show warning
|
|
297
|
+
* 1. user turned book page after showing auto-returned warning modal
|
|
298
|
+
* 2. show warning modal having remaining time
|
|
308
299
|
* @see TimerCountdown::timerCountdown
|
|
309
300
|
*/
|
|
310
301
|
this.addEventListener('IABookActions:loanRenew', async event => {
|
|
311
|
-
console.log('** IABookActions:loanRenew', event.detail);
|
|
312
302
|
await this.autoLoanRenewChecker(false);
|
|
313
303
|
|
|
314
304
|
/**
|
|
@@ -324,18 +314,16 @@ export default class IABookActions extends LitElement {
|
|
|
324
314
|
this.browseHasExpired();
|
|
325
315
|
}
|
|
326
316
|
|
|
327
|
-
// show warning
|
|
317
|
+
// show warning modal with remaining time to auto returned it.
|
|
328
318
|
if (this.loanRenewResult.renewNow === false) {
|
|
329
319
|
/**
|
|
330
320
|
* so compensate for the 50 second buffer to handle above race conditions
|
|
331
321
|
* let's reduce 1 min from warning texts and early return the book when 1 min left.
|
|
332
322
|
*/
|
|
333
323
|
secondsLeft -= 60;
|
|
334
|
-
|
|
335
324
|
this.loanRenewResult.secondsLeft = secondsLeft;
|
|
336
325
|
|
|
337
|
-
|
|
338
|
-
this.showModalManager();
|
|
326
|
+
this.showWarningModal();
|
|
339
327
|
}
|
|
340
328
|
});
|
|
341
329
|
}
|
|
@@ -360,7 +348,6 @@ export default class IABookActions extends LitElement {
|
|
|
360
348
|
|
|
361
349
|
/**
|
|
362
350
|
* create / select the modal-manager component on DOM
|
|
363
|
-
*
|
|
364
351
|
* @memberof IABookActions
|
|
365
352
|
*/
|
|
366
353
|
async useModalManager() {
|
|
@@ -374,9 +361,10 @@ export default class IABookActions extends LitElement {
|
|
|
374
361
|
await document.body.appendChild(this.modal);
|
|
375
362
|
}
|
|
376
363
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
364
|
+
/**
|
|
365
|
+
* Show the waring modal to ask user if they are still reading
|
|
366
|
+
*/
|
|
367
|
+
async showWarningModal() {
|
|
380
368
|
await this.useModalManager();
|
|
381
369
|
|
|
382
370
|
// if secondsLeft < 60, consider it 1 minute
|
|
@@ -387,57 +375,89 @@ export default class IABookActions extends LitElement {
|
|
|
387
375
|
secondsLeft = secondsLeft > 60 ? secondsLeft : 60;
|
|
388
376
|
}
|
|
389
377
|
|
|
390
|
-
let customContent = '';
|
|
391
|
-
|
|
392
378
|
const config = new ModalConfig();
|
|
393
|
-
|
|
394
379
|
config.headline = 'Are you still here?';
|
|
395
380
|
config.showCloseButton = false;
|
|
396
381
|
config.closeOnBackdropClick = false;
|
|
382
|
+
config.message = this.loanRenewHelper?.getMessageTexts(
|
|
383
|
+
this.loanRenewResult.texts,
|
|
384
|
+
secondsLeft
|
|
385
|
+
);
|
|
397
386
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
@click=${() => {
|
|
421
|
-
this.autoLoanRenewChecker(true);
|
|
422
|
-
}}
|
|
423
|
-
>
|
|
424
|
-
Keep reading
|
|
425
|
-
</button>
|
|
426
|
-
<button
|
|
427
|
-
style="${modalButtonStyle.iaButton} ${modalButtonStyle.return}"
|
|
428
|
-
@click=${() => {
|
|
429
|
-
this.dispatchEvent(new CustomEvent('returnNow', {}));
|
|
430
|
-
// this.browseHasExpired();
|
|
431
|
-
}}
|
|
432
|
-
>
|
|
433
|
-
Return the book
|
|
434
|
-
</button>
|
|
435
|
-
</div> `;
|
|
436
|
-
}
|
|
387
|
+
const customContent = html`<br />
|
|
388
|
+
<div id="custom-buttons" style="display:flex;justify-content:center;">
|
|
389
|
+
<button
|
|
390
|
+
style="${modalButtonStyle.iaButton} ${modalButtonStyle.renew}"
|
|
391
|
+
@click=${async event => {
|
|
392
|
+
this.changeModalState(event);
|
|
393
|
+
this.autoLoanRenewChecker(true);
|
|
394
|
+
}}
|
|
395
|
+
>
|
|
396
|
+
Keep reading
|
|
397
|
+
</button>
|
|
398
|
+
<button
|
|
399
|
+
style="${modalButtonStyle.iaButton} ${modalButtonStyle.return}"
|
|
400
|
+
@click=${async event => {
|
|
401
|
+
this.changeModalState(event);
|
|
402
|
+
document.querySelector('ia-book-actions').disableActionGroup = true;
|
|
403
|
+
this.returnNow = true;
|
|
404
|
+
}}
|
|
405
|
+
>
|
|
406
|
+
Return the book
|
|
407
|
+
</button>
|
|
408
|
+
</div> `;
|
|
437
409
|
|
|
438
410
|
this.modal.showModal({ config, customModalContent: customContent });
|
|
439
411
|
}
|
|
440
412
|
|
|
413
|
+
/**
|
|
414
|
+
* helper function to change button click states on modal-manager
|
|
415
|
+
* - add loader on button click
|
|
416
|
+
* - change button appearance
|
|
417
|
+
*
|
|
418
|
+
* @param {Event} event
|
|
419
|
+
*/
|
|
420
|
+
changeModalState(event) {
|
|
421
|
+
if (event.target === undefined) return;
|
|
422
|
+
|
|
423
|
+
const button = event.target;
|
|
424
|
+
button.innerHTML = `<ia-activity-indicator
|
|
425
|
+
mode="processing"
|
|
426
|
+
style=${modalButtonStyle.loaderIcon}
|
|
427
|
+
></ia-activity-indicator>`;
|
|
428
|
+
|
|
429
|
+
const parentElement = event.target.parentNode;
|
|
430
|
+
parentElement.style.pointerEvents = 'none';
|
|
431
|
+
parentElement.style.opacity = 0.8;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Show modal when book is auto returned
|
|
436
|
+
*/
|
|
437
|
+
async showExpiredModal() {
|
|
438
|
+
await this.useModalManager();
|
|
439
|
+
|
|
440
|
+
const config = new ModalConfig();
|
|
441
|
+
config.headline = '';
|
|
442
|
+
config.showCloseButton = false;
|
|
443
|
+
config.closeOnBackdropClick = false;
|
|
444
|
+
config.message = 'This book has been returned due to inactivity.';
|
|
445
|
+
|
|
446
|
+
const customContent = html`<br />
|
|
447
|
+
<div style="text-align: center">
|
|
448
|
+
<button
|
|
449
|
+
style="${modalButtonStyle.iaButton} ${modalButtonStyle.renew}"
|
|
450
|
+
@click=${() => {
|
|
451
|
+
window.location?.reload();
|
|
452
|
+
}}
|
|
453
|
+
>
|
|
454
|
+
Okay
|
|
455
|
+
</button>
|
|
456
|
+
</div> `;
|
|
457
|
+
|
|
458
|
+
this.modal?.showModal({ config, customModalContent: customContent });
|
|
459
|
+
}
|
|
460
|
+
|
|
441
461
|
/**
|
|
442
462
|
* close/hide modal-manager
|
|
443
463
|
*/
|
|
@@ -560,11 +580,9 @@ export default class IABookActions extends LitElement {
|
|
|
560
580
|
// show message after browsed book is expired.
|
|
561
581
|
this.loanRenewResult.renewNow = false;
|
|
562
582
|
this.loanRenewResult.texts =
|
|
563
|
-
'This book has been
|
|
583
|
+
'This book has been returned due to inactivity.';
|
|
564
584
|
|
|
565
|
-
|
|
566
|
-
// this.showToastMessage();
|
|
567
|
-
this.showModalManager('expired');
|
|
585
|
+
this.showExpiredModal();
|
|
568
586
|
|
|
569
587
|
window?.Sentry?.captureMessage(sentryLogs.browseHasExpired);
|
|
570
588
|
}
|
|
@@ -642,6 +660,7 @@ export default class IABookActions extends LitElement {
|
|
|
642
660
|
?disabled=${this.disableActionGroup}
|
|
643
661
|
?autoRenew=${this.loanRenewResult.renewNow}
|
|
644
662
|
?autoReturn=${this.lendingStatus.browsingExpired}
|
|
663
|
+
?returnNow=${this.returnNow}
|
|
645
664
|
@loanAutoRenewed=${this.handleLoanAutoRenewed}
|
|
646
665
|
@lendingActionError=${this.handleLendingActionError}
|
|
647
666
|
@toggleActionGroup=${this.handleToggleActionGroup}
|
|
@@ -665,11 +684,11 @@ export default class IABookActions extends LitElement {
|
|
|
665
684
|
|
|
666
685
|
this.suppressToast = false;
|
|
667
686
|
await this.browseHasRenew();
|
|
668
|
-
// await this.showToastMessage();
|
|
669
687
|
await this.resetTimerCountState();
|
|
670
688
|
|
|
671
689
|
// close the modal
|
|
672
|
-
|
|
690
|
+
this.modal?.remove();
|
|
691
|
+
this.modal?.showModal({ config: {}, customModalContent: `` });
|
|
673
692
|
|
|
674
693
|
window?.Sentry?.captureMessage(sentryLogs.bookHasRenewed);
|
|
675
694
|
|
|
@@ -723,7 +742,7 @@ export default class IABookActions extends LitElement {
|
|
|
723
742
|
this.disableActionGroup = !this.disableActionGroup;
|
|
724
743
|
}
|
|
725
744
|
|
|
726
|
-
|
|
745
|
+
/**
|
|
727
746
|
* handle lending errors occure during different operation like
|
|
728
747
|
* browse book, borrow book, borrowed book token etc...
|
|
729
748
|
*
|
|
@@ -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
|
|
279
|
+
'This book has been returned due to inactivity.'
|
|
278
280
|
);
|
|
279
281
|
});
|
|
280
282
|
|