@internetarchive/ia-book-actions 0.0.7-2 → 0.0.7-5
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 +2 -1
- package/src/components/collapsible-action-group.js +25 -10
- package/src/core/services/actions-handler/actions-handler-service.js +6 -2
- package/src/core/services/actions-handler/actions-handler.js +53 -29
- package/src/core/services/get-lending-actions.js +1 -1
- package/src/ia-book-actions.js +42 -19
- package/test/components/collapsible-action-group.test.js +0 -25
- package/test/ia-book-actions.test.js +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@internetarchive/ia-book-actions",
|
|
3
|
-
"version": "0.0.7-
|
|
3
|
+
"version": "0.0.7-5",
|
|
4
4
|
"description": "Bookreader actions for borrowable book",
|
|
5
5
|
"author": "Internet Archive",
|
|
6
6
|
"license": "AGPL-3.0-only",
|
|
@@ -22,6 +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.1.1",
|
|
25
26
|
"@internetarchive/modal-manager": "^0.1.0",
|
|
26
27
|
"@internetarchive/shared-resize-observer": "^0.0.1",
|
|
27
28
|
"lit-element": "^2.4.0",
|
|
@@ -63,25 +63,38 @@ export class CollapsibleActionGroup extends ActionsHandler {
|
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
|
|
67
|
-
|
|
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() {
|
|
68
73
|
// send consecutiveLoanCounts for browsed books only.
|
|
69
|
-
if (this.borrowType === '
|
|
70
|
-
|
|
71
|
-
|
|
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
|
+
}
|
|
72
85
|
}
|
|
73
86
|
|
|
74
87
|
// event category and action for browsing book access
|
|
75
88
|
const eventCategory = `${this.borrowType}BookAccess`;
|
|
76
89
|
const eventAction = `${
|
|
77
|
-
this.borrowType === '
|
|
90
|
+
this.borrowType === 'browsed' ? 'BrowseCounts-' : 'Counts-'
|
|
78
91
|
}${this.consecutiveLoanCounts}`;
|
|
79
92
|
|
|
80
93
|
this.dispatchEvent(
|
|
81
94
|
new CustomEvent('enableBookAccess', {
|
|
82
95
|
detail: {
|
|
83
96
|
event: {
|
|
84
|
-
category: eventCategory, //
|
|
97
|
+
category: eventCategory, // browsedBookAccess | borrowedBookAccess
|
|
85
98
|
action: eventAction, // (BrowseCounts-N|Counts-N)
|
|
86
99
|
},
|
|
87
100
|
borrowType: this.borrowType,
|
|
@@ -222,11 +235,13 @@ export class CollapsibleActionGroup extends ActionsHandler {
|
|
|
222
235
|
}
|
|
223
236
|
|
|
224
237
|
/**
|
|
225
|
-
*
|
|
226
|
-
* @param { string } eventName
|
|
227
|
-
* @param { object } gaEvent
|
|
238
|
+
* Dispatches click events when patron clicks on action buttons
|
|
239
|
+
* @param { string } eventName - actions like 'browseBook', 'borrowBook' etc...
|
|
240
|
+
* @param { object } gaEvent - contains analytics event action and category
|
|
228
241
|
* @param { string } gaEvent.category
|
|
229
242
|
* @param { string } gaEvent.action
|
|
243
|
+
*
|
|
244
|
+
* @fires CollapsibleActionGroup#{eventName} - (will be browseBook, borrowBook etc...)
|
|
230
245
|
*/
|
|
231
246
|
clickHandler(eventName, gaEvent) {
|
|
232
247
|
this.dropdownState = 'close';
|
|
@@ -16,6 +16,11 @@ export default function ActionsHandlerService(options) {
|
|
|
16
16
|
let baseHost = '/services/loans/loan';
|
|
17
17
|
const location = window?.location;
|
|
18
18
|
|
|
19
|
+
// return intentional error on dev environment
|
|
20
|
+
const shouldReturnError =
|
|
21
|
+
location?.href?.indexOf('?error=true') !== -1 &&
|
|
22
|
+
location?.hostname !== 'archive.org';
|
|
23
|
+
|
|
19
24
|
if (location?.pathname === '/demo/') baseHost = `/demo/`;
|
|
20
25
|
|
|
21
26
|
let formData = new FormData();
|
|
@@ -27,12 +32,11 @@ export default function ActionsHandlerService(options) {
|
|
|
27
32
|
body: formData,
|
|
28
33
|
})
|
|
29
34
|
.then(response => {
|
|
30
|
-
let shouldReturnError = location?.href?.indexOf('?error=true') !== -1;
|
|
31
35
|
const tokenError = 'loan token not found. please try again later.';
|
|
32
36
|
const borrowError =
|
|
33
37
|
'This book is not available to borrow at this time. Please try again later.';
|
|
34
38
|
|
|
35
|
-
// return error reponse
|
|
39
|
+
// return error reponse when not production and has ?error=true param...
|
|
36
40
|
const erroneousActions = ['browse_book', 'borrow_book', 'create_token'];
|
|
37
41
|
if (shouldReturnError && erroneousActions.includes(option?.action)) {
|
|
38
42
|
return {
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { LitElement } from 'lit-element';
|
|
2
|
+
import { LocalCache } from '@internetarchive/local-cache';
|
|
3
|
+
|
|
2
4
|
import { URLHelper } from '../../config/url-helper.js';
|
|
3
5
|
import ActionsHandlerService from './actions-handler-service.js';
|
|
4
6
|
|
|
@@ -14,10 +16,11 @@ export default class ActionsHandler extends LitElement {
|
|
|
14
16
|
constructor(identifier) {
|
|
15
17
|
super();
|
|
16
18
|
this.identifier = identifier;
|
|
17
|
-
this.ajaxTimeout =
|
|
19
|
+
this.ajaxTimeout = 7000;
|
|
18
20
|
this.loanTokenPollingDelay =
|
|
19
21
|
window.location.pathname === '/demo/' ? 2000 : 120000; // 120000 ms = 2 min
|
|
20
22
|
this.loanTokenInterval = undefined;
|
|
23
|
+
this.localCache = new LocalCache();
|
|
21
24
|
this.bindEvents();
|
|
22
25
|
}
|
|
23
26
|
|
|
@@ -119,106 +122,121 @@ export default class ActionsHandler extends LitElement {
|
|
|
119
122
|
}
|
|
120
123
|
|
|
121
124
|
handleBrowseIt() {
|
|
122
|
-
const
|
|
125
|
+
const action = 'browse_book';
|
|
123
126
|
this.dispatchToggleActionGroup();
|
|
124
127
|
|
|
125
128
|
ActionsHandlerService({
|
|
126
|
-
action
|
|
129
|
+
action,
|
|
127
130
|
identifier: this.identifier,
|
|
128
131
|
success: () => {
|
|
129
132
|
this.handleReadItNow();
|
|
130
133
|
},
|
|
131
134
|
error: data => {
|
|
132
|
-
this.dispatchActionError(
|
|
135
|
+
this.dispatchActionError(action, data);
|
|
133
136
|
},
|
|
134
137
|
});
|
|
135
138
|
}
|
|
136
139
|
|
|
137
140
|
handleReturnIt() {
|
|
138
|
-
const
|
|
141
|
+
const action = 'return_loan';
|
|
139
142
|
this.dispatchToggleActionGroup();
|
|
140
143
|
|
|
141
144
|
ActionsHandlerService({
|
|
142
|
-
action
|
|
145
|
+
action,
|
|
143
146
|
identifier: this.identifier,
|
|
144
147
|
success: () => {
|
|
145
148
|
this.deleteLoanCookies();
|
|
146
149
|
URLHelper.goToUrl(`/details/${this.identifier}`, true);
|
|
147
150
|
},
|
|
148
151
|
error: data => {
|
|
149
|
-
this.dispatchActionError(
|
|
152
|
+
this.dispatchActionError(action, data);
|
|
150
153
|
},
|
|
151
154
|
});
|
|
152
155
|
}
|
|
153
156
|
|
|
154
157
|
handleBorrowIt() {
|
|
155
|
-
const
|
|
158
|
+
const action = 'borrow_book';
|
|
156
159
|
this.dispatchToggleActionGroup();
|
|
157
160
|
|
|
158
161
|
ActionsHandlerService({
|
|
159
|
-
action
|
|
162
|
+
action,
|
|
160
163
|
identifier: this.identifier,
|
|
161
164
|
success: () => {
|
|
162
165
|
this.handleReadItNow();
|
|
163
166
|
},
|
|
164
167
|
error: data => {
|
|
165
|
-
this.dispatchActionError(
|
|
168
|
+
this.dispatchActionError(action, data);
|
|
166
169
|
},
|
|
167
170
|
});
|
|
168
171
|
}
|
|
169
172
|
|
|
170
173
|
handleReserveIt() {
|
|
171
|
-
const
|
|
174
|
+
const action = 'join_waitlist';
|
|
172
175
|
this.dispatchToggleActionGroup();
|
|
173
176
|
|
|
174
177
|
ActionsHandlerService({
|
|
175
|
-
action
|
|
178
|
+
action,
|
|
176
179
|
identifier: this.identifier,
|
|
177
180
|
success: () => {
|
|
178
181
|
URLHelper.goToUrl(URLHelper.getRedirectUrl(), true);
|
|
179
182
|
},
|
|
180
183
|
error: data => {
|
|
181
|
-
this.dispatchActionError(
|
|
184
|
+
this.dispatchActionError(action, data);
|
|
182
185
|
},
|
|
183
186
|
});
|
|
184
187
|
}
|
|
185
188
|
|
|
186
189
|
handleRemoveFromWaitingList() {
|
|
187
|
-
const
|
|
190
|
+
const action = 'leave_waitlist';
|
|
188
191
|
this.dispatchToggleActionGroup();
|
|
189
192
|
|
|
190
193
|
ActionsHandlerService({
|
|
191
|
-
action
|
|
194
|
+
action,
|
|
192
195
|
identifier: this.identifier,
|
|
193
196
|
success: () => {
|
|
194
197
|
URLHelper.goToUrl(URLHelper.getRedirectUrl(), true);
|
|
195
198
|
},
|
|
196
199
|
error: data => {
|
|
197
|
-
this.dispatchActionError(
|
|
200
|
+
this.dispatchActionError(action, data);
|
|
198
201
|
},
|
|
199
202
|
});
|
|
200
203
|
}
|
|
201
204
|
|
|
202
205
|
handleLoanTokenPoller() {
|
|
203
|
-
const
|
|
206
|
+
const action = 'create_token';
|
|
204
207
|
ActionsHandlerService({
|
|
205
208
|
identifier: this.identifier,
|
|
206
|
-
action
|
|
209
|
+
action,
|
|
207
210
|
error: data => {
|
|
208
|
-
this.dispatchActionError(
|
|
211
|
+
this.dispatchActionError(action, data);
|
|
209
212
|
clearInterval(this.loanTokenInterval); // stop token fetch api
|
|
210
213
|
},
|
|
211
214
|
});
|
|
212
215
|
}
|
|
213
216
|
|
|
214
|
-
|
|
217
|
+
/**
|
|
218
|
+
* Dispatches event when an error occured on action
|
|
219
|
+
* Notes:- toggle <collapsible-action-group> visibility (enable/disable).
|
|
220
|
+
*
|
|
221
|
+
* @param { String } action - name of action like browse_book, borrow_book
|
|
222
|
+
* @param { Object } data - erroneous response from api call
|
|
223
|
+
*
|
|
224
|
+
* @fires ActionsHandler#lendingActionError
|
|
225
|
+
*/
|
|
226
|
+
dispatchActionError(action, data = {}) {
|
|
215
227
|
this.dispatchEvent(
|
|
216
228
|
new CustomEvent('lendingActionError', {
|
|
217
|
-
detail: {
|
|
229
|
+
detail: { action, data },
|
|
218
230
|
})
|
|
219
231
|
);
|
|
220
232
|
}
|
|
221
233
|
|
|
234
|
+
/**
|
|
235
|
+
* Dispatches event when patron is clicked on action buttons.
|
|
236
|
+
* Notes:- toggle <collapsible-action-group> disable/enable.
|
|
237
|
+
*
|
|
238
|
+
* @fires ActionsHandler#toggleActionGroup
|
|
239
|
+
*/
|
|
222
240
|
dispatchToggleActionGroup() {
|
|
223
241
|
this.dispatchEvent(new CustomEvent('toggleActionGroup'));
|
|
224
242
|
}
|
|
@@ -256,19 +274,25 @@ export default class ActionsHandler extends LitElement {
|
|
|
256
274
|
}
|
|
257
275
|
|
|
258
276
|
// save consecutive loan count for borrow
|
|
259
|
-
setConsecutiveLoanCounts(action = '') {
|
|
260
|
-
|
|
261
|
-
if (localStorage) {
|
|
277
|
+
async setConsecutiveLoanCounts(action = '') {
|
|
278
|
+
try {
|
|
262
279
|
let newCount = 1;
|
|
263
280
|
const storageKey = `consecutive-loan-count`;
|
|
264
|
-
const existingCount =
|
|
281
|
+
const existingCount = await this.localCache.get(storageKey);
|
|
265
282
|
|
|
266
|
-
//
|
|
267
|
-
|
|
268
|
-
if (action === 'browseAgain') {
|
|
283
|
+
// increase browse-count by 1 when you consecutive reading a book.
|
|
284
|
+
if (action === 'browseAgain' && existingCount !== undefined) {
|
|
269
285
|
newCount = existingCount ? existingCount + 1 : 1;
|
|
270
286
|
}
|
|
271
|
-
|
|
287
|
+
|
|
288
|
+
// set new value
|
|
289
|
+
await this.localCache.set({
|
|
290
|
+
key: storageKey,
|
|
291
|
+
value: newCount,
|
|
292
|
+
ttl: 7200, // 2 hours
|
|
293
|
+
});
|
|
294
|
+
} catch (error) {
|
|
295
|
+
console.log(error);
|
|
272
296
|
}
|
|
273
297
|
}
|
|
274
298
|
|
package/src/ia-book-actions.js
CHANGED
|
@@ -24,6 +24,7 @@ export default class IABookActions extends LitElement {
|
|
|
24
24
|
barType: { type: String },
|
|
25
25
|
sharedObserver: { attribute: false },
|
|
26
26
|
disableActionGroup: { type: Boolean },
|
|
27
|
+
modal: { Object },
|
|
27
28
|
};
|
|
28
29
|
}
|
|
29
30
|
|
|
@@ -37,12 +38,15 @@ export default class IABookActions extends LitElement {
|
|
|
37
38
|
this.bwbPurchaseUrl = '';
|
|
38
39
|
this.barType = 'action'; // 'title'|'action'
|
|
39
40
|
this.sharedObserver = undefined;
|
|
41
|
+
this.disableActionGroup = false;
|
|
42
|
+
this.modal = undefined;
|
|
43
|
+
|
|
44
|
+
// private props
|
|
40
45
|
this.primaryActions = [];
|
|
41
46
|
this.primaryTitle = '';
|
|
42
47
|
this.primaryColor = 'primary';
|
|
43
48
|
this.secondaryActions = [];
|
|
44
49
|
this.lendingOptions = {};
|
|
45
|
-
this.disableActionGroup = false;
|
|
46
50
|
}
|
|
47
51
|
|
|
48
52
|
disconnectedCallback() {
|
|
@@ -144,8 +148,8 @@ export default class IABookActions extends LitElement {
|
|
|
144
148
|
});
|
|
145
149
|
|
|
146
150
|
this.borrowType = actions.borrowType;
|
|
147
|
-
if (this.borrowType === '
|
|
148
|
-
// start timer for
|
|
151
|
+
if (this.borrowType === 'browsed') {
|
|
152
|
+
// start timer for browsed.
|
|
149
153
|
// when browse is completed, we shows browse-again button
|
|
150
154
|
this.startBrowseTimer();
|
|
151
155
|
}
|
|
@@ -201,28 +205,27 @@ export default class IABookActions extends LitElement {
|
|
|
201
205
|
*
|
|
202
206
|
* @event IABookActions#lendingActionError
|
|
203
207
|
* @param {Object} event - The employee who is responsible for the project
|
|
204
|
-
*
|
|
205
|
-
*
|
|
208
|
+
* @param {string} event.detail.action - action when error occurred like 'browseBook', 'borrowBook'
|
|
209
|
+
* @param {string} event.detail.data.error - error message
|
|
206
210
|
*/
|
|
207
211
|
handleLendingActionError(event) {
|
|
208
|
-
console.log(event?.detail?.data);
|
|
209
212
|
// toggle activity loader
|
|
210
213
|
this.handleToggleActionGroup();
|
|
211
214
|
|
|
212
|
-
const
|
|
215
|
+
const action = event?.detail?.action;
|
|
213
216
|
const errorMsg = event?.detail?.data?.error;
|
|
214
217
|
|
|
215
|
-
if (errorMsg) this.showErrorModal(errorMsg);
|
|
218
|
+
if (errorMsg) this.showErrorModal(errorMsg, action);
|
|
216
219
|
|
|
217
220
|
// update action bar state if book is not available to browse or borrow.
|
|
218
221
|
if (errorMsg && errorMsg.match(/not available to borrow/gm)) {
|
|
219
222
|
let currStatus = this.lendingStatus;
|
|
220
|
-
if (
|
|
223
|
+
if (action === 'browse_book') {
|
|
221
224
|
currStatus = {
|
|
222
225
|
...this.lendingStatus,
|
|
223
226
|
available_to_browse: false,
|
|
224
227
|
};
|
|
225
|
-
} else if (
|
|
228
|
+
} else if (action === 'borrow_book') {
|
|
226
229
|
currStatus = {
|
|
227
230
|
...this.lendingStatus,
|
|
228
231
|
available_to_borrow: false,
|
|
@@ -233,22 +236,42 @@ export default class IABookActions extends LitElement {
|
|
|
233
236
|
}
|
|
234
237
|
|
|
235
238
|
/* show error message if something went wrong */
|
|
236
|
-
async showErrorModal(errorMsg) {
|
|
239
|
+
async showErrorModal(errorMsg, action) {
|
|
237
240
|
this.disableActionGroup = false;
|
|
238
241
|
|
|
239
|
-
this.modal
|
|
242
|
+
// check if this.modal passed as prop
|
|
240
243
|
if (!this.modal) {
|
|
241
|
-
this.modal = document.
|
|
242
|
-
|
|
244
|
+
this.modal = document.querySelector('modal-manager');
|
|
245
|
+
|
|
246
|
+
// check the DOM if <modal-manager> already there
|
|
247
|
+
if (!this.modal) this.modal = document.createElement('modal-manager');
|
|
243
248
|
}
|
|
249
|
+
|
|
250
|
+
this.modal.id = 'action-bar-modal';
|
|
244
251
|
await document.body.appendChild(this.modal);
|
|
245
252
|
|
|
253
|
+
const modalConfig = new ModalConfig({
|
|
254
|
+
title: 'Lending error',
|
|
255
|
+
message: errorMsg,
|
|
256
|
+
headerColor: '#d9534f',
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
if (action === 'create_token') {
|
|
260
|
+
modalConfig.closeOnBackdropClick = false;
|
|
261
|
+
modalConfig.showCloseButton = false;
|
|
262
|
+
modalConfig.message = html` Uh oh, something went wrong trying to access
|
|
263
|
+
this book.<br />
|
|
264
|
+
Please <a href="${() => window.location.reload(true)}">refresh</a> to
|
|
265
|
+
try again or send us an email to
|
|
266
|
+
<a
|
|
267
|
+
href="mailto:info@archive.org?subject=Help: cannot access my borrowed book: ${this
|
|
268
|
+
.identifier}"
|
|
269
|
+
>info@archive.org</a
|
|
270
|
+
>`;
|
|
271
|
+
}
|
|
272
|
+
|
|
246
273
|
this.modal.showModal({
|
|
247
|
-
config:
|
|
248
|
-
title: 'Lending error',
|
|
249
|
-
message: errorMsg,
|
|
250
|
-
headerColor: '#d9534f',
|
|
251
|
-
}),
|
|
274
|
+
config: modalConfig,
|
|
252
275
|
});
|
|
253
276
|
}
|
|
254
277
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { html, fixture, expect } from '@open-wc/testing';
|
|
2
2
|
import '../../src/components/collapsible-action-group.js';
|
|
3
|
-
import Sinon from 'sinon';
|
|
4
3
|
|
|
5
4
|
const container = ({
|
|
6
5
|
primaryActions = [
|
|
@@ -85,27 +84,3 @@ describe('<collapsible-action-group>', () => {
|
|
|
85
84
|
expect(eAnalytics.action).to.equal('Browse');
|
|
86
85
|
});
|
|
87
86
|
});
|
|
88
|
-
|
|
89
|
-
describe('fetch loan token and localStorage for browsed/borrowsed book', () => {
|
|
90
|
-
afterEach(() => {
|
|
91
|
-
const localStorageMock = {
|
|
92
|
-
getItem: Sinon.spy(),
|
|
93
|
-
setItem: Sinon.spy(),
|
|
94
|
-
};
|
|
95
|
-
Object.defineProperty(window, 'localStorage', {
|
|
96
|
-
value: localStorageMock,
|
|
97
|
-
});
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
it('test localStorage function when borrowType is browsed', async () => {
|
|
101
|
-
await fixture(container({ borrowType: 'browsed' }));
|
|
102
|
-
expect(localStorage.getItem).to.be.calledOnce;
|
|
103
|
-
expect(localStorage.getItem).to.be.calledWith('consecutive-loan-count');
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it('test localStorage function when borrowType is borrowed', async () => {
|
|
107
|
-
await fixture(container({ borrowType: 'borrowed' }));
|
|
108
|
-
expect(localStorage.getItem).to.not.be.calledOnce;
|
|
109
|
-
expect(localStorage.getItem).to.not.be.calledWith('consecutive-loan-count');
|
|
110
|
-
});
|
|
111
|
-
});
|
|
@@ -122,7 +122,7 @@ describe('Borrow status actions', () => {
|
|
|
122
122
|
el.addEventListener('lendingActionError', () => {
|
|
123
123
|
el.handleLendingActionError({
|
|
124
124
|
detail: {
|
|
125
|
-
|
|
125
|
+
action: 'browse_book',
|
|
126
126
|
data: { error: 'not available to borrow' },
|
|
127
127
|
},
|
|
128
128
|
});
|
|
@@ -151,7 +151,7 @@ describe('Borrow status actions', () => {
|
|
|
151
151
|
el.addEventListener('lendingActionError', () => {
|
|
152
152
|
el.handleLendingActionError({
|
|
153
153
|
detail: {
|
|
154
|
-
|
|
154
|
+
action: 'borrow_book',
|
|
155
155
|
data: { error: 'not available to borrow' },
|
|
156
156
|
},
|
|
157
157
|
});
|