@internetarchive/ia-book-actions 0.0.7-7 → 0.0.8
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 +8 -1
- package/package.json +2 -2
- package/src/components/collapsible-action-group.js +0 -50
- package/src/core/services/actions-handler/actions-handler.js +2 -41
- package/src/core/services/loan-token-poller.js +87 -0
- package/src/ia-book-actions.js +61 -9
- package/test/core/services/loan-token-poller.test.js +40 -0
package/demo/index.html
CHANGED
|
@@ -104,14 +104,21 @@
|
|
|
104
104
|
const modalConfig = new ModalConfig();
|
|
105
105
|
modalConfig.headerColor = '#d9534f';
|
|
106
106
|
|
|
107
|
+
let lendingBarPostInit = function lendingBarPostInit() {
|
|
108
|
+
setTimeout(function() {
|
|
109
|
+
console.log('executed', 'br.init()');
|
|
110
|
+
}, 100);
|
|
111
|
+
}
|
|
112
|
+
|
|
107
113
|
// set defaultLendingStatus for unavailable (without borrowables) book
|
|
108
114
|
iaBookActions.userid = userid;
|
|
109
115
|
iaBookActions.identifier = identifier;
|
|
110
116
|
iaBookActions.bookTitle = 'Contemporary Black biography. Volume 39 : profiles from the interContemporary Black biography. Volume 39';
|
|
111
117
|
iaBookActions.lendingStatus = canBrowseAndBorrow;
|
|
112
118
|
iaBookActions.bwbPurchaseUrl = '';
|
|
113
|
-
iaBookActions.barType = '';
|
|
114
119
|
iaBookActions.modalConfig = modalConfig;
|
|
120
|
+
iaBookActions.lendingBarPostInit = lendingBarPostInit;
|
|
121
|
+
iaBookActions.tokenDelay = 2000; // 2 seconds
|
|
115
122
|
|
|
116
123
|
let lendingStatus1 = iaBookActions.lendingStatus;
|
|
117
124
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@internetarchive/ia-book-actions",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.8",
|
|
4
4
|
"description": "Bookreader actions for borrowable book",
|
|
5
5
|
"author": "Internet Archive",
|
|
6
6
|
"license": "AGPL-3.0-only",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"@internetarchive/icon-bookmark": "^1.1.3",
|
|
23
23
|
"@internetarchive/icon-close": "^1.1.3",
|
|
24
24
|
"@internetarchive/icon-info": "^1.2.6",
|
|
25
|
-
"@internetarchive/local-cache": "^0.
|
|
25
|
+
"@internetarchive/local-cache": "^0.2.0",
|
|
26
26
|
"@internetarchive/modal-manager": "^0.1.0",
|
|
27
27
|
"@internetarchive/shared-resize-observer": "^0.0.1",
|
|
28
28
|
"lit-element": "^2.4.0",
|
|
@@ -27,7 +27,6 @@ export class CollapsibleActionGroup extends ActionsHandler {
|
|
|
27
27
|
hasAdminAccess: { type: Boolean },
|
|
28
28
|
dropdownArrow: { type: String },
|
|
29
29
|
disabled: { type: Boolean },
|
|
30
|
-
borrowType: { type: String },
|
|
31
30
|
};
|
|
32
31
|
}
|
|
33
32
|
|
|
@@ -46,15 +45,6 @@ export class CollapsibleActionGroup extends ActionsHandler {
|
|
|
46
45
|
this.title = '';
|
|
47
46
|
this.loaderIcon = 'https://archive.org/upload/images/tree/loading.gif';
|
|
48
47
|
this.disabled = false;
|
|
49
|
-
this.borrowType = ''; // (browsed|borrowed)
|
|
50
|
-
this.consecutiveLoanCounts = 1; // consecutive loan count
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
firstUpdated() {
|
|
54
|
-
// this is execute to fetch loan token
|
|
55
|
-
if (this.borrowType) {
|
|
56
|
-
this.emitEnableBookAccess();
|
|
57
|
-
}
|
|
58
48
|
}
|
|
59
49
|
|
|
60
50
|
updated(changed) {
|
|
@@ -63,46 +53,6 @@ export class CollapsibleActionGroup extends ActionsHandler {
|
|
|
63
53
|
}
|
|
64
54
|
}
|
|
65
55
|
|
|
66
|
-
/**
|
|
67
|
-
* Dispatches event when patron is borrowing book.
|
|
68
|
-
* Notes borrow type, and consecutive borrow counts
|
|
69
|
-
*
|
|
70
|
-
* @fires CollapsibleActionGroup#enableBookAccess
|
|
71
|
-
*/
|
|
72
|
-
async emitEnableBookAccess() {
|
|
73
|
-
// send consecutiveLoanCounts for browsed books only.
|
|
74
|
-
if (this.borrowType === 'browsed') {
|
|
75
|
-
try {
|
|
76
|
-
const existingCount = await this.localCache.get(
|
|
77
|
-
'consecutive-loan-count'
|
|
78
|
-
);
|
|
79
|
-
if (existingCount !== undefined) {
|
|
80
|
-
this.consecutiveLoanCounts = existingCount.value ?? 1;
|
|
81
|
-
}
|
|
82
|
-
} catch (error) {
|
|
83
|
-
console.log(error);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// event category and action for browsing book access
|
|
88
|
-
const eventCategory = `${this.borrowType}BookAccess`;
|
|
89
|
-
const eventAction = `${
|
|
90
|
-
this.borrowType === 'browsed' ? 'BrowseCounts-' : 'Counts-'
|
|
91
|
-
}${this.consecutiveLoanCounts}`;
|
|
92
|
-
|
|
93
|
-
this.dispatchEvent(
|
|
94
|
-
new CustomEvent('enableBookAccess', {
|
|
95
|
-
detail: {
|
|
96
|
-
event: {
|
|
97
|
-
category: eventCategory, // browsedBookAccess | borrowedBookAccess
|
|
98
|
-
action: eventAction, // (BrowseCounts-N|Counts-N)
|
|
99
|
-
},
|
|
100
|
-
borrowType: this.borrowType,
|
|
101
|
-
},
|
|
102
|
-
})
|
|
103
|
-
);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
56
|
/**
|
|
107
57
|
* merge primaryActions and secondaryActions into dropdown
|
|
108
58
|
*/
|
|
@@ -16,18 +16,11 @@ export default class ActionsHandler extends LitElement {
|
|
|
16
16
|
constructor(identifier) {
|
|
17
17
|
super();
|
|
18
18
|
this.identifier = identifier;
|
|
19
|
-
this.ajaxTimeout =
|
|
20
|
-
this.loanTokenPollingDelay =
|
|
21
|
-
window.location.pathname === '/demo/' ? 2000 : 120000; // 120000 ms = 2 min
|
|
22
|
-
this.loanTokenInterval = undefined;
|
|
19
|
+
this.ajaxTimeout = 6000;
|
|
23
20
|
this.localCache = new LocalCache();
|
|
24
21
|
this.bindEvents();
|
|
25
22
|
}
|
|
26
23
|
|
|
27
|
-
disconnectedCallback() {
|
|
28
|
-
this.loanTokenInterval = undefined;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
24
|
sendEvent(eventCategory, eventAction) {
|
|
32
25
|
// eslint-disable-next-line no-console
|
|
33
26
|
console?.log('Book action: ', { eventCategory, eventAction });
|
|
@@ -102,26 +95,6 @@ export default class ActionsHandler extends LitElement {
|
|
|
102
95
|
const { category, action } = detail.event;
|
|
103
96
|
this.sendEvent(category, action);
|
|
104
97
|
});
|
|
105
|
-
|
|
106
|
-
this.addEventListener('enableBookAccess', ({ detail }) => {
|
|
107
|
-
const borrowType = detail?.borrowType;
|
|
108
|
-
|
|
109
|
-
// fetch loan token for browsed/borrowed book and set an interval
|
|
110
|
-
if (borrowType) {
|
|
111
|
-
// Do an initial token, then set an interval
|
|
112
|
-
this.handleLoanTokenPoller();
|
|
113
|
-
|
|
114
|
-
this.loanTokenInterval = setInterval(() => {
|
|
115
|
-
this.handleLoanTokenPoller();
|
|
116
|
-
}, this.loanTokenPollingDelay);
|
|
117
|
-
|
|
118
|
-
const { category, action } = detail?.event;
|
|
119
|
-
this.sendEvent(category, action);
|
|
120
|
-
} else {
|
|
121
|
-
// if book is not browsed, just clear token polling interval
|
|
122
|
-
clearInterval(this.loanTokenInterval);
|
|
123
|
-
}
|
|
124
|
-
});
|
|
125
98
|
}
|
|
126
99
|
|
|
127
100
|
handleBrowseIt() {
|
|
@@ -205,18 +178,6 @@ export default class ActionsHandler extends LitElement {
|
|
|
205
178
|
});
|
|
206
179
|
}
|
|
207
180
|
|
|
208
|
-
handleLoanTokenPoller() {
|
|
209
|
-
const action = 'create_token';
|
|
210
|
-
ActionsHandlerService({
|
|
211
|
-
identifier: this.identifier,
|
|
212
|
-
action,
|
|
213
|
-
error: data => {
|
|
214
|
-
this.dispatchActionError(action, data);
|
|
215
|
-
clearInterval(this.loanTokenInterval); // stop token fetch api
|
|
216
|
-
},
|
|
217
|
-
});
|
|
218
|
-
}
|
|
219
|
-
|
|
220
181
|
/**
|
|
221
182
|
* Dispatches event when an error occured on action
|
|
222
183
|
* Notes:- toggle <collapsible-action-group> visibility (enable/disable).
|
|
@@ -280,7 +241,7 @@ export default class ActionsHandler extends LitElement {
|
|
|
280
241
|
async setConsecutiveLoanCounts(action = '') {
|
|
281
242
|
try {
|
|
282
243
|
let newCount = 1;
|
|
283
|
-
const storageKey = `
|
|
244
|
+
const storageKey = `loan-count-${this.identifier}`;
|
|
284
245
|
const existingCount = await this.localCache.get(storageKey);
|
|
285
246
|
|
|
286
247
|
// increase browse-count by 1 when you consecutive reading a book.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { LocalCache } from '@internetarchive/local-cache';
|
|
2
|
+
import ActionsHandlerService from './actions-handler/actions-handler-service.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* This class is used to create loan token for borrowed books
|
|
6
|
+
*
|
|
7
|
+
* ActionsHandlerService is a function being used to execute
|
|
8
|
+
*/
|
|
9
|
+
export class LoanTokenPoller {
|
|
10
|
+
constructor(id, borrowType, successCallback, errorCallback, pollerDelay) {
|
|
11
|
+
this.identifier = id;
|
|
12
|
+
this.borrowType = borrowType;
|
|
13
|
+
this.successCallback = successCallback; // callback function to be called after loan token is created
|
|
14
|
+
this.errorCallback = errorCallback; // callback function to be called after loan token is created
|
|
15
|
+
this.pollerDelay = pollerDelay; // value in ms (1000 ms = 1 sec)
|
|
16
|
+
|
|
17
|
+
this.loanTokenInterval = undefined;
|
|
18
|
+
this.localCache = new LocalCache();
|
|
19
|
+
this.enableBookAccess();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
disconnectedInterval() {
|
|
23
|
+
clearInterval(this.loanTokenInterval);
|
|
24
|
+
this.loanTokenInterval = undefined;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async enableBookAccess() {
|
|
28
|
+
let consecutiveLoanCounts = 1;
|
|
29
|
+
|
|
30
|
+
if (this.borrowType) {
|
|
31
|
+
// send consecutiveLoanCounts for browsed books only.
|
|
32
|
+
if (this.borrowType === 'browsed') {
|
|
33
|
+
try {
|
|
34
|
+
const existingCount = await this.localCache.get(
|
|
35
|
+
`loan-count-${this.identifier}`
|
|
36
|
+
);
|
|
37
|
+
consecutiveLoanCounts = existingCount ?? 1;
|
|
38
|
+
} catch (error) {
|
|
39
|
+
console.log(error);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Do an initial token, then set an interval
|
|
44
|
+
this.handleLoanTokenPoller(true);
|
|
45
|
+
|
|
46
|
+
this.loanTokenInterval = setInterval(() => {
|
|
47
|
+
this.handleLoanTokenPoller();
|
|
48
|
+
}, this.pollerDelay);
|
|
49
|
+
|
|
50
|
+
// event category and action for browsing book access
|
|
51
|
+
const category = `${this.borrowType}BookAccess`;
|
|
52
|
+
const action = `${
|
|
53
|
+
this.borrowType === 'browsed' ? 'BrowseCounts-' : 'Counts-'
|
|
54
|
+
}${consecutiveLoanCounts}`;
|
|
55
|
+
|
|
56
|
+
this.sendEvent(category, action);
|
|
57
|
+
} else {
|
|
58
|
+
// if book is not browsed, just clear token polling interval
|
|
59
|
+
this.disconnectedInterval(); // stop token fetch api
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
sendEvent(eventCategory, eventAction) {
|
|
64
|
+
// eslint-disable-next-line no-console
|
|
65
|
+
console?.log('Book action: ', { eventCategory, eventAction });
|
|
66
|
+
window?.archive_analytics?.send_event_no_sampling(
|
|
67
|
+
eventCategory,
|
|
68
|
+
eventAction,
|
|
69
|
+
`identifier=${this.identifier}`
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async handleLoanTokenPoller(isInitial = false) {
|
|
74
|
+
const action = 'create_token';
|
|
75
|
+
ActionsHandlerService({
|
|
76
|
+
identifier: this.identifier,
|
|
77
|
+
action,
|
|
78
|
+
error: data => {
|
|
79
|
+
this.disconnectedInterval(); // stop token fetch api
|
|
80
|
+
this.errorCallback({ detail: { action, data } });
|
|
81
|
+
},
|
|
82
|
+
success: () => {
|
|
83
|
+
if (isInitial) this.successCallback();
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
package/src/ia-book-actions.js
CHANGED
|
@@ -11,6 +11,7 @@ import './components/info-icon.js';
|
|
|
11
11
|
|
|
12
12
|
import GetLendingActions from './core/services/get-lending-actions.js';
|
|
13
13
|
import { mobileContainerWidth } from './core/config/constants.js';
|
|
14
|
+
import { LoanTokenPoller } from './core/services/loan-token-poller.js';
|
|
14
15
|
|
|
15
16
|
export default class IABookActions extends LitElement {
|
|
16
17
|
static get properties() {
|
|
@@ -21,10 +22,15 @@ export default class IABookActions extends LitElement {
|
|
|
21
22
|
lendingStatus: { type: Object },
|
|
22
23
|
width: { type: Number },
|
|
23
24
|
bwbPurchaseUrl: { type: String },
|
|
25
|
+
lendingBarPostInit: {
|
|
26
|
+
type: Function,
|
|
27
|
+
attribute: false,
|
|
28
|
+
},
|
|
24
29
|
barType: { type: String },
|
|
25
30
|
sharedObserver: { attribute: false },
|
|
26
31
|
disableActionGroup: { type: Boolean },
|
|
27
32
|
modal: { Object },
|
|
33
|
+
tokenDelay: { type: Number },
|
|
28
34
|
};
|
|
29
35
|
}
|
|
30
36
|
|
|
@@ -36,10 +42,12 @@ export default class IABookActions extends LitElement {
|
|
|
36
42
|
this.lendingStatus = {};
|
|
37
43
|
this.width = 0;
|
|
38
44
|
this.bwbPurchaseUrl = '';
|
|
45
|
+
this.lendingBarPostInit = () => {};
|
|
39
46
|
this.barType = 'action'; // 'title'|'action'
|
|
40
47
|
this.sharedObserver = undefined;
|
|
41
48
|
this.disableActionGroup = false;
|
|
42
49
|
this.modal = undefined;
|
|
50
|
+
this.tokenDelay = 120000; // 2 minutes
|
|
43
51
|
|
|
44
52
|
// private props
|
|
45
53
|
this.primaryActions = [];
|
|
@@ -47,6 +55,8 @@ export default class IABookActions extends LitElement {
|
|
|
47
55
|
this.primaryColor = 'primary';
|
|
48
56
|
this.secondaryActions = [];
|
|
49
57
|
this.lendingOptions = {};
|
|
58
|
+
this.borrowType = ''; // (browsed|borrowed)
|
|
59
|
+
this.consecutiveLoanCounts = 1; // consecutive loan count
|
|
50
60
|
}
|
|
51
61
|
|
|
52
62
|
disconnectedCallback() {
|
|
@@ -58,8 +68,6 @@ export default class IABookActions extends LitElement {
|
|
|
58
68
|
this.sharedObserver = new SharedResizeObserver();
|
|
59
69
|
this.setupResizeObserver();
|
|
60
70
|
}
|
|
61
|
-
|
|
62
|
-
this.setupLendingToolbarActions();
|
|
63
71
|
}
|
|
64
72
|
|
|
65
73
|
updated(changed) {
|
|
@@ -153,14 +161,52 @@ export default class IABookActions extends LitElement {
|
|
|
153
161
|
// when browse is completed, we shows browse-again button
|
|
154
162
|
this.startBrowseTimer();
|
|
155
163
|
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* enable access of borrowed/browsed books
|
|
167
|
+
*
|
|
168
|
+
* LoanTokenPoller is a class that polls the loan token
|
|
169
|
+
* it takes 5 params
|
|
170
|
+
* 1. this.identifier
|
|
171
|
+
* 2. this.borrowType
|
|
172
|
+
* 3. successCallback - it used to
|
|
173
|
+
* - disptach lendingFlow::PostInit event
|
|
174
|
+
* - initialize bookreader using br.init()
|
|
175
|
+
* 4. errorCallback
|
|
176
|
+
* 5. tokenPollerDelay
|
|
177
|
+
*/
|
|
178
|
+
if (this.barType === 'action') {
|
|
179
|
+
if (this.tokenPoller) {
|
|
180
|
+
this.tokenPoller.disconnectedInterval();
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const successCallback = () => {
|
|
184
|
+
this.lendingBarPostInit();
|
|
185
|
+
};
|
|
186
|
+
const errorCallback = eventObj => {
|
|
187
|
+
this.handleLendingActionError(eventObj);
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
this.tokenPoller = new LoanTokenPoller(
|
|
191
|
+
this.identifier,
|
|
192
|
+
this.borrowType,
|
|
193
|
+
successCallback,
|
|
194
|
+
errorCallback,
|
|
195
|
+
this.tokenDelay // 1000 ms = 1 sec
|
|
196
|
+
);
|
|
197
|
+
}
|
|
156
198
|
}
|
|
157
199
|
|
|
158
200
|
render() {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
${this.
|
|
162
|
-
</section
|
|
163
|
-
|
|
201
|
+
if (this.barType === 'title') {
|
|
202
|
+
return html`<section class="lending-wrapper">
|
|
203
|
+
${this.bookTitleBar}
|
|
204
|
+
</section>`;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return html`<section class="lending-wrapper">
|
|
208
|
+
${this.bookActionBar}
|
|
209
|
+
</section>`;
|
|
164
210
|
}
|
|
165
211
|
|
|
166
212
|
get bookTitleBar() {
|
|
@@ -257,12 +303,18 @@ export default class IABookActions extends LitElement {
|
|
|
257
303
|
});
|
|
258
304
|
|
|
259
305
|
if (action === 'create_token') {
|
|
306
|
+
const refreshButton = html`<button
|
|
307
|
+
style="background:none;font-size:inherit;border:0;padding:0;color:#0000ee;cursor:pointer;text-decoration:underline"
|
|
308
|
+
@click=${() => window.location.reload(true)}
|
|
309
|
+
>
|
|
310
|
+
refresh
|
|
311
|
+
</button>`;
|
|
312
|
+
|
|
260
313
|
modalConfig.closeOnBackdropClick = false;
|
|
261
314
|
modalConfig.showCloseButton = false;
|
|
262
315
|
modalConfig.message = html` Uh oh, something went wrong trying to access
|
|
263
316
|
this book.<br />
|
|
264
|
-
Please
|
|
265
|
-
try again or send us an email to
|
|
317
|
+
Please ${refreshButton} to try again or send us an email to
|
|
266
318
|
<a
|
|
267
319
|
href="mailto:info@archive.org?subject=Help: cannot access my borrowed book: ${this
|
|
268
320
|
.identifier}"
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { expect } from '@open-wc/testing';
|
|
2
|
+
import Sinon from 'sinon';
|
|
3
|
+
|
|
4
|
+
import { LocalCache } from '@internetarchive/local-cache';
|
|
5
|
+
import { LoanTokenPoller } from '../../../src/core/services/loan-token-poller.js';
|
|
6
|
+
|
|
7
|
+
describe('Get Loan Token', () => {
|
|
8
|
+
it('get loan token for browsed books', async () => {
|
|
9
|
+
const localCacheStub = new LocalCache();
|
|
10
|
+
const localCacheGetSpy = Sinon.spy(localCacheStub, 'get');
|
|
11
|
+
|
|
12
|
+
const tokenPoller = new LoanTokenPoller(
|
|
13
|
+
'identifier1',
|
|
14
|
+
'browsed',
|
|
15
|
+
() => {
|
|
16
|
+
console.log('success callback is executed!');
|
|
17
|
+
},
|
|
18
|
+
() => {
|
|
19
|
+
console.log('error callback is executed!');
|
|
20
|
+
},
|
|
21
|
+
2000 // 2 minutes'
|
|
22
|
+
);
|
|
23
|
+
const successCallbackSpy = Sinon.stub(tokenPoller, 'successCallback');
|
|
24
|
+
successCallbackSpy();
|
|
25
|
+
expect(successCallbackSpy.callCount).to.equal(1);
|
|
26
|
+
|
|
27
|
+
localCacheGetSpy();
|
|
28
|
+
expect(localCacheGetSpy.callCount).to.equal(1);
|
|
29
|
+
|
|
30
|
+
expect(tokenPoller.localCache.namespace).to.equal('LocalCache');
|
|
31
|
+
expect(tokenPoller.localCache.get).to.be.a('function');
|
|
32
|
+
|
|
33
|
+
tokenPoller.disconnectedInterval(); // clear token poller interval
|
|
34
|
+
expect(tokenPoller.loanTokenInterval).to.equal(undefined);
|
|
35
|
+
|
|
36
|
+
tokenPoller.handleLoanTokenPoller(true);
|
|
37
|
+
expect(tokenPoller.errorCallback).to.be.a('function');
|
|
38
|
+
expect(tokenPoller.successCallback).have;
|
|
39
|
+
});
|
|
40
|
+
});
|