@internetarchive/ia-book-actions 0.0.29-alpha37 → 0.0.29-alpha39
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
package/src/ia-book-actions.js
CHANGED
|
@@ -72,15 +72,22 @@ export default class IABookActions extends LitElement {
|
|
|
72
72
|
this.borrowType = ''; // (browsed|borrowed)
|
|
73
73
|
this.consecutiveLoanCounts = 1; // consecutive loan count
|
|
74
74
|
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
/**
|
|
76
|
+
* contains loan renew time config
|
|
77
|
+
* - totalTime:- number of seconds a loan should last
|
|
78
|
+
* - autoCheckAt:- check for loan renew at last
|
|
79
|
+
* - pageChangedInLast: consider loan renew eligible in last
|
|
80
|
+
* - isDevBox:- it just for testing purpose
|
|
81
|
+
*/
|
|
77
82
|
this.loanRenewConfig = {};
|
|
78
83
|
this.suppressToast = false;
|
|
84
|
+
this.browseTimer = undefined;
|
|
79
85
|
|
|
80
86
|
/**
|
|
81
87
|
* contains loan auto-renew response
|
|
82
88
|
* - texts:- texts messages need to show in toast message
|
|
83
89
|
* - renewNow:- key to determine if need to renew now
|
|
90
|
+
* - timeLeft:- number of seconds left in active loan
|
|
84
91
|
*/
|
|
85
92
|
this.loanRenewResult = {
|
|
86
93
|
texts: null,
|
|
@@ -92,7 +99,7 @@ export default class IABookActions extends LitElement {
|
|
|
92
99
|
}
|
|
93
100
|
|
|
94
101
|
disconnectedCallback() {
|
|
95
|
-
this.tokenPoller
|
|
102
|
+
this.tokenPoller?.disconnectedInterval();
|
|
96
103
|
window?.Sentry?.captureMessage(sentryLogs.disconnectedCallback);
|
|
97
104
|
this.disconnectResizeObserver();
|
|
98
105
|
}
|
|
@@ -213,6 +220,13 @@ export default class IABookActions extends LitElement {
|
|
|
213
220
|
return;
|
|
214
221
|
}
|
|
215
222
|
|
|
223
|
+
// initial timer state for 1-hour loan
|
|
224
|
+
if (this.borrowType === 'browsed' && !this.loanRenewResult.renewNow) {
|
|
225
|
+
setTimeout(() => {
|
|
226
|
+
this.resetTimerCountState();
|
|
227
|
+
}, 100);
|
|
228
|
+
}
|
|
229
|
+
|
|
216
230
|
/**
|
|
217
231
|
* tokenPoller determines if user has loan token for this book
|
|
218
232
|
* - if book is going to renew, need to wait until renew is completed
|
|
@@ -221,17 +235,13 @@ export default class IABookActions extends LitElement {
|
|
|
221
235
|
setTimeout(
|
|
222
236
|
() => {
|
|
223
237
|
this.startLoanTokenPoller();
|
|
224
|
-
|
|
225
|
-
if (this.borrowType === 'browsed' && !this.loanRenewResult.renewNow) {
|
|
226
|
-
this.resetTimerCountState();
|
|
227
|
-
}
|
|
228
238
|
},
|
|
229
239
|
this.loanRenewResult.renewNow ? this.tokenDelay * 1000 : 100
|
|
230
240
|
);
|
|
231
241
|
}
|
|
232
242
|
|
|
233
243
|
/**
|
|
234
|
-
* Bind 1 hour loan auto renew,
|
|
244
|
+
* Bind 1 hour loan auto renew event,
|
|
235
245
|
* There are two events we want to use,
|
|
236
246
|
* 1. BookReader:pageChanged - dispatched from bookreader side
|
|
237
247
|
* 2. IABookActions:loanRenew - dispatched from timer-countdown component
|
|
@@ -252,17 +262,17 @@ export default class IABookActions extends LitElement {
|
|
|
252
262
|
/**
|
|
253
263
|
* Detech click-event on document to close toast-template
|
|
254
264
|
*/
|
|
255
|
-
document.addEventListener('click',
|
|
265
|
+
document.addEventListener('click', e => {
|
|
256
266
|
if (this.loanRenewHelper && this.loanRenewResult.timeLeft > 0) {
|
|
257
267
|
console.log('clicked-outside');
|
|
258
268
|
this.suppressToast = true;
|
|
259
269
|
}
|
|
260
270
|
|
|
261
|
-
// reload when user click on page
|
|
271
|
+
// reload when user click on book-page and book is EXPIRED
|
|
262
272
|
const hasExpired =
|
|
263
273
|
'browsingExpired' in this.lendingStatus &&
|
|
264
274
|
this.lendingStatus?.browsingExpired;
|
|
265
|
-
if (hasExpired) {
|
|
275
|
+
if (hasExpired && e.target.nodeName !== 'IA-BOOK-ACTIONS') {
|
|
266
276
|
window.location?.reload();
|
|
267
277
|
}
|
|
268
278
|
});
|
|
@@ -283,6 +293,11 @@ export default class IABookActions extends LitElement {
|
|
|
283
293
|
});
|
|
284
294
|
}
|
|
285
295
|
|
|
296
|
+
/**
|
|
297
|
+
* To determine if need to be renewed browsed book
|
|
298
|
+
*
|
|
299
|
+
* @param {Boolean} hasPageChanged
|
|
300
|
+
*/
|
|
286
301
|
async autoLoanRenewChecker(hasPageChanged = false) {
|
|
287
302
|
this.loanRenewHelper = new LoanRenewHelper(
|
|
288
303
|
hasPageChanged,
|
|
@@ -304,7 +319,9 @@ export default class IABookActions extends LitElement {
|
|
|
304
319
|
async showToastMessage() {
|
|
305
320
|
if (this.suppressToast) return;
|
|
306
321
|
|
|
307
|
-
const iaBookActions = document.querySelector('ia-book-actions')
|
|
322
|
+
const iaBookActions = document.querySelector('ia-book-actions')?.shadowRoot;
|
|
323
|
+
if (!iaBookActions) return;
|
|
324
|
+
|
|
308
325
|
let toastTemplate = iaBookActions.querySelector('toast-template');
|
|
309
326
|
if (!toastTemplate) {
|
|
310
327
|
toastTemplate = document.createElement('toast-template');
|
|
@@ -338,8 +355,7 @@ export default class IABookActions extends LitElement {
|
|
|
338
355
|
}
|
|
339
356
|
|
|
340
357
|
startBrowseTimer() {
|
|
341
|
-
clearTimeout(this.
|
|
342
|
-
|
|
358
|
+
clearTimeout(this.browseTimer);
|
|
343
359
|
const {
|
|
344
360
|
browsingExpired,
|
|
345
361
|
user_has_browsed,
|
|
@@ -349,7 +365,7 @@ export default class IABookActions extends LitElement {
|
|
|
349
365
|
return;
|
|
350
366
|
}
|
|
351
367
|
|
|
352
|
-
this.
|
|
368
|
+
this.browseTimer = setTimeout(() => {
|
|
353
369
|
this.browseHasExpired();
|
|
354
370
|
}, secondsLeftOnLoan * 1000);
|
|
355
371
|
}
|
|
@@ -358,7 +374,7 @@ export default class IABookActions extends LitElement {
|
|
|
358
374
|
* Execute when loan is expired
|
|
359
375
|
*/
|
|
360
376
|
async browseHasExpired() {
|
|
361
|
-
|
|
377
|
+
this.tokenPoller?.disconnectedInterval();
|
|
362
378
|
|
|
363
379
|
const currStatus = { ...this.lendingStatus, browsingExpired: true };
|
|
364
380
|
this.lendingStatus = currStatus;
|
|
@@ -468,7 +484,7 @@ export default class IABookActions extends LitElement {
|
|
|
468
484
|
*/
|
|
469
485
|
async handleLoanAutoRenewed() {
|
|
470
486
|
if (this.loanRenewResult.renewNow) {
|
|
471
|
-
this.tokenPoller
|
|
487
|
+
this.tokenPoller?.disconnectedInterval();
|
|
472
488
|
|
|
473
489
|
this.suppressToast = false;
|
|
474
490
|
await this.showToastMessage();
|
|
@@ -491,7 +507,7 @@ export default class IABookActions extends LitElement {
|
|
|
491
507
|
startLoanTokenPoller() {
|
|
492
508
|
if (this.tokenPoller) {
|
|
493
509
|
window?.Sentry?.captureMessage(sentryLogs.clearTokenPoller);
|
|
494
|
-
this.tokenPoller
|
|
510
|
+
this.tokenPoller?.disconnectedInterval();
|
|
495
511
|
}
|
|
496
512
|
const successCallback = () => {
|
|
497
513
|
this.lendingBarPostInit();
|
|
@@ -551,7 +567,7 @@ export default class IABookActions extends LitElement {
|
|
|
551
567
|
// - clear tokenPoller interval
|
|
552
568
|
// - set user_has_browsed to `false`
|
|
553
569
|
if (action === 'create_token') {
|
|
554
|
-
this.tokenPoller
|
|
570
|
+
this.tokenPoller?.disconnectedInterval();
|
|
555
571
|
|
|
556
572
|
const currStatus = {
|
|
557
573
|
...this.lendingStatus,
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { html, fixture, expect, aTimeout } from '@open-wc/testing';
|
|
2
|
+
import '../../src/components/timer-countdown.js';
|
|
3
|
+
|
|
4
|
+
const container = ({ timeLeftOnLoan, loanRenewConfig } = {}) =>
|
|
5
|
+
html`<timer-countdown
|
|
6
|
+
.timeLeftOnLoan=${timeLeftOnLoan}
|
|
7
|
+
.loanRenewConfig=${loanRenewConfig}
|
|
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
|
+
timeLeftOnLoan: 0, // in seconds
|
|
15
|
+
loanRenewConfig: {
|
|
16
|
+
totalTime: 14, // in seconds
|
|
17
|
+
autoCheckAt: 5, // in seconds
|
|
18
|
+
pageChangedInLast: 5, // in seconds
|
|
19
|
+
isDevBox: true,
|
|
20
|
+
},
|
|
21
|
+
})
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
await el.updateComplete;
|
|
25
|
+
expect(el.timerInterval).to.be.undefined;
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('timer interval is not undefined when loan is active', async () => {
|
|
29
|
+
const el = await fixture(
|
|
30
|
+
container({
|
|
31
|
+
timeLeftOnLoan: 2, // in seconds
|
|
32
|
+
loanRenewConfig: {
|
|
33
|
+
totalTime: 14, // in seconds
|
|
34
|
+
autoCheckAt: 5, // in seconds
|
|
35
|
+
pageChangedInLast: 5, // in seconds
|
|
36
|
+
isDevBox: true,
|
|
37
|
+
},
|
|
38
|
+
})
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
await aTimeout(1900);
|
|
42
|
+
await el.updateComplete;
|
|
43
|
+
|
|
44
|
+
expect(el.timeLeftOnLoan).to.equal(1);
|
|
45
|
+
expect(el.timerInterval).to.not.be.undefined;
|
|
46
|
+
});
|
|
47
|
+
});
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { expect, aTimeout } from '@open-wc/testing';
|
|
2
|
+
|
|
3
|
+
import { LocalCache } from '@internetarchive/local-cache';
|
|
4
|
+
import { LoanRenewHelper } from '../../../src/core/services/loan-renew-helper.js';
|
|
5
|
+
|
|
6
|
+
const identifier = 'booBar';
|
|
7
|
+
const loanRenewConfig = {
|
|
8
|
+
totalTime: 13,
|
|
9
|
+
autoCheckAt: 10,
|
|
10
|
+
pageChangedInLast: 12,
|
|
11
|
+
isDevBox: true,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
// localCache used for auto-loan-renew
|
|
15
|
+
const localCache = new LocalCache({
|
|
16
|
+
namespace: 'loanRenew',
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
beforeEach(async () => {
|
|
20
|
+
await localCache.set({
|
|
21
|
+
key: `${identifier}-loanTime`,
|
|
22
|
+
value: new Date(new Date().getTime() + loanRenewConfig.totalTime * 1000),
|
|
23
|
+
ttl: Number(loanRenewConfig.totalTime),
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
describe('Loan Renew Determine', () => {
|
|
28
|
+
it('flip book page while reading a book', async () => {
|
|
29
|
+
await aTimeout(1500);
|
|
30
|
+
|
|
31
|
+
await localCache.set({
|
|
32
|
+
key: `${identifier}-pageChangedTime`,
|
|
33
|
+
value: new Date(), // current time
|
|
34
|
+
ttl: Number(loanRenewConfig.totalTime),
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const loanRenewHelper = new LoanRenewHelper(
|
|
38
|
+
true, // hasPageChanged
|
|
39
|
+
identifier,
|
|
40
|
+
localCache,
|
|
41
|
+
loanRenewConfig
|
|
42
|
+
);
|
|
43
|
+
await loanRenewHelper.handleLoanRenew();
|
|
44
|
+
|
|
45
|
+
expect(loanRenewHelper.result.renewNow).to.be.false;
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('auto renewed book when user is currently reading', async () => {
|
|
49
|
+
await localCache.set({
|
|
50
|
+
key: `${identifier}-pageChangedTime`,
|
|
51
|
+
value: new Date(), // current time
|
|
52
|
+
ttl: Number(loanRenewConfig.totalTime),
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
await aTimeout(1500);
|
|
56
|
+
|
|
57
|
+
const loanRenewHelper = new LoanRenewHelper(
|
|
58
|
+
false, // timer-countdown event
|
|
59
|
+
identifier,
|
|
60
|
+
localCache,
|
|
61
|
+
loanRenewConfig
|
|
62
|
+
);
|
|
63
|
+
await loanRenewHelper.handleLoanRenew();
|
|
64
|
+
|
|
65
|
+
expect(loanRenewHelper.result.renewNow).to.be.true;
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('get toast message template', async () => {
|
|
69
|
+
const loanRenewHelper = new LoanRenewHelper(
|
|
70
|
+
false,
|
|
71
|
+
identifier,
|
|
72
|
+
localCache,
|
|
73
|
+
loanRenewConfig
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
const toastMsg = loanRenewHelper.getMessageTexts(
|
|
77
|
+
loanRenewHelper.loanReturnWarning,
|
|
78
|
+
'65'
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
expect(toastMsg).to.be.equal(
|
|
82
|
+
'This book will be automatically returned in 1 minute unless you turn a page.'
|
|
83
|
+
);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
@@ -4,11 +4,17 @@ import '../src/ia-book-actions.js';
|
|
|
4
4
|
import Sinon from 'sinon';
|
|
5
5
|
|
|
6
6
|
import { SharedResizeObserver } from '@internetarchive/shared-resize-observer';
|
|
7
|
+
import { LocalCache } from '@internetarchive/local-cache';
|
|
7
8
|
|
|
8
9
|
afterEach(() => {
|
|
9
10
|
Sinon.restore();
|
|
10
11
|
});
|
|
11
12
|
|
|
13
|
+
// localCache used for auto-loan-renew
|
|
14
|
+
const localCache = new LocalCache({
|
|
15
|
+
namespace: 'loanRenew',
|
|
16
|
+
});
|
|
17
|
+
|
|
12
18
|
const container = ({ userid, identifier, lendingStatus = {} } = {}) =>
|
|
13
19
|
html`<ia-book-actions
|
|
14
20
|
.userid=${userid}
|
|
@@ -230,9 +236,14 @@ describe('Browsing expired status', () => {
|
|
|
230
236
|
},
|
|
231
237
|
})
|
|
232
238
|
);
|
|
233
|
-
expect(el.primaryTitle).contains('
|
|
239
|
+
expect(el.primaryTitle).contains('');
|
|
234
240
|
expect(el.primaryActions[0].text).to.equal('Return now');
|
|
235
241
|
expect(el.primaryActions[1].text).to.equal('Print Disability Access');
|
|
242
|
+
|
|
243
|
+
// default params of one-hour loan renew
|
|
244
|
+
expect(el.loanRenewResult.texts).to.equal(null);
|
|
245
|
+
expect(el.loanRenewResult.renewNow).to.equal(false);
|
|
246
|
+
expect(el.loanRenewResult.timeLeft).to.equal(0);
|
|
236
247
|
});
|
|
237
248
|
|
|
238
249
|
it('Book is browsing and going to expire after 1 second', async () => {
|
|
@@ -248,10 +259,23 @@ describe('Browsing expired status', () => {
|
|
|
248
259
|
})
|
|
249
260
|
);
|
|
250
261
|
|
|
262
|
+
// timer-countdown is still active
|
|
263
|
+
expect(el.shadowRoot.querySelector('timer-countdown')).to.not.be.null;
|
|
264
|
+
|
|
251
265
|
await aTimeout(1500); // wait for 1.5 second
|
|
252
266
|
await el.updateComplete;
|
|
253
267
|
|
|
254
268
|
expect(el.primaryActions[0].text).to.equal('Borrow');
|
|
269
|
+
|
|
270
|
+
// timer-countdown is gone now
|
|
271
|
+
expect(el.shadowRoot.querySelector('timer-countdown')).to.be.null;
|
|
272
|
+
|
|
273
|
+
// // book has been expired
|
|
274
|
+
expect(el.loanRenewResult.renewNow).to.equal(false);
|
|
275
|
+
expect(el.loanRenewResult.timeLeft).to.equal(0);
|
|
276
|
+
expect(el.loanRenewResult.texts).to.equal(
|
|
277
|
+
'This book has been automatically returned due to inactivity.'
|
|
278
|
+
);
|
|
255
279
|
});
|
|
256
280
|
|
|
257
281
|
it('Expiring book cancels interval & emits event', async () => {
|
|
@@ -276,9 +300,8 @@ describe('Browsing expired status', () => {
|
|
|
276
300
|
el.lendingStatus = browsingStatus;
|
|
277
301
|
await el.updateComplete;
|
|
278
302
|
|
|
279
|
-
expect(el.primaryTitle).contains('
|
|
303
|
+
expect(el.primaryTitle).contains('');
|
|
280
304
|
expect(el.primaryActions[0].text).to.equal('Return now');
|
|
281
|
-
expect(el.tokenPoller.loanTokenInterval).to.not.equal(undefined);
|
|
282
305
|
|
|
283
306
|
const expiredStatus = { ...browsingStatus, browsingExpired: true };
|
|
284
307
|
el.lendingStatus = expiredStatus;
|
|
@@ -291,6 +314,50 @@ describe('Browsing expired status', () => {
|
|
|
291
314
|
});
|
|
292
315
|
});
|
|
293
316
|
|
|
317
|
+
describe('Auto renew one hour loan', () => {
|
|
318
|
+
it('Book is browsing and renewed it now', async () => {
|
|
319
|
+
const el = await fixture(
|
|
320
|
+
container({
|
|
321
|
+
userid: '@userid',
|
|
322
|
+
identifier: 'Foo',
|
|
323
|
+
lendingStatus: {
|
|
324
|
+
is_lendable: true,
|
|
325
|
+
user_has_browsed: true,
|
|
326
|
+
browseHasExpired: false,
|
|
327
|
+
secondsLeftOnLoan: 12,
|
|
328
|
+
},
|
|
329
|
+
})
|
|
330
|
+
);
|
|
331
|
+
|
|
332
|
+
el.loanRenewResult = {
|
|
333
|
+
texts: 'This book has been renewed for 1 hour.',
|
|
334
|
+
renewNow: true,
|
|
335
|
+
timeLeft: 10,
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
await localCache.set({
|
|
339
|
+
key: `${el.identifier}-loanTime`,
|
|
340
|
+
value: new Date(new Date().getTime() + 10 * 1000),
|
|
341
|
+
ttl: Number(10),
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
const myEvent = new CustomEvent('loanAutoRenewed');
|
|
345
|
+
el.addEventListener('loanAutoRenewed', () => {
|
|
346
|
+
el.handleLoanAutoRenewed();
|
|
347
|
+
});
|
|
348
|
+
el.dispatchEvent(myEvent);
|
|
349
|
+
|
|
350
|
+
await aTimeout(1500); // wait for 1.5 second
|
|
351
|
+
await el.updateComplete;
|
|
352
|
+
|
|
353
|
+
expect(el.loanRenewResult.timeLeft).to.equal(10);
|
|
354
|
+
expect(el.loanRenewResult.renewNow).to.equal(true);
|
|
355
|
+
expect(el.loanRenewResult.texts).to.equal(
|
|
356
|
+
'This book has been renewed for 1 hour.'
|
|
357
|
+
);
|
|
358
|
+
});
|
|
359
|
+
});
|
|
360
|
+
|
|
294
361
|
describe('Shared Resize Observer', () => {
|
|
295
362
|
it('can receive a Shared Resize Observer', async () => {
|
|
296
363
|
const sharedObserverStub = new SharedResizeObserver();
|