@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.
- package/demo/index.html +32 -32
- package/package.json +12 -6
- package/src/assets/data/lending-get-status-service.js +1 -1
- package/src/assets/styles/collapsible-action-group.js +2 -1
- package/src/components/collapsible-action-group.js +25 -12
- package/src/components/info-icon.js +0 -3
- package/src/components/text-group.js +1 -1
- package/src/components/timer-countdown.js +55 -68
- package/src/core/config/analytics-event-and-category.js +1 -0
- package/src/core/services/actions-handler/actions-handler.js +37 -31
- package/src/core/services/get-lending-actions.js +6 -11
- package/src/core/services/loan-renew-helper.js +159 -0
- package/src/ia-book-actions.js +197 -127
- package/test/ia-book-actions.test.js +1 -5
- package/src/assets/styles/timer-countdown.js +0 -58
- package/src/components/toast-message.js +0 -60
- package/src/core/services/one-hour-auto-renew.js +0 -148
|
@@ -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: '
|
|
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: '
|
|
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: '
|
|
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 =
|
|
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
|
+
}
|
package/src/ia-book-actions.js
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
49
|
-
|
|
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
|
-
|
|
72
|
+
|
|
73
|
+
// loan auto renew
|
|
83
74
|
this.browserTimer = undefined;
|
|
84
|
-
this.
|
|
75
|
+
this.loanRenewConfig = {};
|
|
85
76
|
|
|
86
|
-
|
|
87
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
294
|
-
.autoCheckAt=${this.
|
|
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
|
-
.
|
|
323
|
-
.
|
|
383
|
+
.localCache=${this.localCache}
|
|
384
|
+
.loanRenewConfig=${this.loanRenewConfig}
|
|
324
385
|
?hasAdminAccess=${this.hasAdminAccess}
|
|
325
386
|
?disabled=${this.disableActionGroup}
|
|
326
|
-
?
|
|
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}
|
|
394
|
+
${this.timerCountdownTemplate}
|
|
333
395
|
`;
|
|
334
396
|
}
|
|
335
397
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
);
|
|
345
|
-
|
|
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
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
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-
|
|
514
|
-
--
|
|
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
|
});
|