@internetarchive/ia-book-actions 0.0.7-1 → 0.0.7-4
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/README.md +93 -47
- package/assets/ia-book-action-screen.png +0 -0
- package/assets/ia-book-title-screen.png +0 -0
- package/demo/index.html +1 -5
- package/package.json +2 -1
- package/src/components/collapsible-action-group.js +29 -12
- package/src/core/services/actions-handler/actions-handler-service.js +13 -12
- package/src/core/services/actions-handler/actions-handler.js +66 -38
- package/src/core/services/get-lending-actions.js +3 -3
- package/src/ia-book-actions.js +58 -44
- package/test/components/collapsible-action-group.test.js +0 -25
- package/test/ia-book-actions.test.js +2 -2
- package/src/core/services/get-realtime-lending-status.js +0 -31
package/README.md
CHANGED
|
@@ -1,69 +1,115 @@
|
|
|
1
|
-
|
|
1
|
+
# Component for lendable and embed items
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The book action button component build on LitElement with support for custom content and light DOM elements.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
## Usage
|
|
8
|
-
|
|
9
|
-
1. Click the "Use this Template" button in GitHub to create a new repository based on this one.
|
|
10
|
-
2. Clone your new repo and update the things below.
|
|
11
|
-
|
|
12
|
-
### Things to update in your copy
|
|
13
|
-
1. Remove this section
|
|
14
|
-
2. Search for the strings `your-webcomponent` and `YourWebComponent` and those are most of the spots that need to be updated.
|
|
15
|
-
3. `README.md` (this file). Update the readme in general, but also the badge URLs
|
|
16
|
-
4. `package.json` Update the name and description
|
|
17
|
-
5. Rename the `your-webcomponent.ts` and its associated `.test` file
|
|
18
|
-
6. Update `.travis.yml` with the proper secure key. See the [Travis docs](https://blog.travis-ci.com/2014-03-13-slack-notifications/) for more information.
|
|
19
|
-
|
|
20
|
-
## Local Demo with `web-dev-server`
|
|
5
|
+
## Installation
|
|
21
6
|
```bash
|
|
22
|
-
yarn
|
|
7
|
+
yarn install --save @internetarchive/ia-book-actions
|
|
23
8
|
```
|
|
24
|
-
To run a local development server that serves the basic demo located in `demo/index.html`
|
|
25
9
|
|
|
26
|
-
##
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
10
|
+
## Usage of Book Actions
|
|
11
|
+
```html
|
|
12
|
+
<!-- index.html -->
|
|
13
|
+
<style>
|
|
14
|
+
html {
|
|
15
|
+
font-size: 10px;
|
|
16
|
+
/* This is to match petabox's base font size */
|
|
17
|
+
}
|
|
18
|
+
body {
|
|
19
|
+
margin: 0;
|
|
20
|
+
text-align: center;
|
|
21
|
+
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
|
22
|
+
}
|
|
23
|
+
#demo {
|
|
24
|
+
height: 50px;
|
|
25
|
+
}
|
|
26
|
+
ia-book-actions {
|
|
27
|
+
--white: #fff;
|
|
28
|
+
--black: #000;
|
|
29
|
+
--primaryBGColor: var(--black);
|
|
30
|
+
--secondaryBGColor: #222;
|
|
31
|
+
--tertiaryBGColor: #333;
|
|
32
|
+
--primaryTextColor: var(--white);
|
|
33
|
+
--primaryCTAFill: #194880;
|
|
34
|
+
--primaryCTABorder: #c5d1df;
|
|
35
|
+
--secondaryCTAFill: #333;
|
|
36
|
+
--secondaryCTABorder: #999;
|
|
37
|
+
--secondaryCTAFillRGB: 51, 51, 51;
|
|
38
|
+
--primaryErrorCTAFill: #e51c26;
|
|
39
|
+
--primaryErrorCTABorder: #f8c6c8;
|
|
40
|
+
--primaryCTAFillRGB: 25, 72, 128;
|
|
41
|
+
--primaryErrorCTAFillRGB: 229, 28, 38;
|
|
42
|
+
--primaryDisableCTAFill: #767676;
|
|
43
|
+
}
|
|
44
|
+
</style>
|
|
45
|
+
|
|
46
|
+
<ia-book-actions></ia-book-actions>
|
|
47
|
+
|
|
48
|
+
<script type="module">
|
|
49
|
+
import '@internetarchive/ia-book-actions.js';
|
|
50
|
+
import { ModalConfig } from '@internetarchive/modal-manager';
|
|
51
|
+
import {
|
|
52
|
+
defaultLendingStatus,
|
|
53
|
+
canBrowseAndBorrow,
|
|
54
|
+
} from '../src/assets/data/lending-get-status-service.js';
|
|
55
|
+
|
|
56
|
+
let iaBookActions = document.querySelector('ia-book-actions');
|
|
57
|
+
iaBookActions.userid = '@neeraj-archive';
|
|
58
|
+
iaBookActions.identifier = 'naturalhistoryof00unse_4111';
|
|
59
|
+
iaBookActions.bookTitle = 'This is test title for any book';
|
|
60
|
+
iaBookActions.lendingStatus = {}; // lendinStatus in json
|
|
61
|
+
iaBookActions.bwbPurchaseUrl = 'https://bwb.com/produce/123';
|
|
62
|
+
iaBookActions.barType = ''; // title|action
|
|
63
|
+
</script>
|
|
30
64
|
```
|
|
31
65
|
|
|
32
|
-
|
|
66
|
+

|
|
33
67
|
|
|
34
|
-
|
|
35
|
-
|
|
68
|
+
## Usage of Book Title
|
|
69
|
+
```html
|
|
70
|
+
<!-- along with above properties, set barType to 'title' -->
|
|
71
|
+
<script type="module">
|
|
72
|
+
iaBookActions.barType = 'title'; // title|action
|
|
73
|
+
</script>
|
|
36
74
|
```
|
|
75
|
+

|
|
37
76
|
|
|
38
|
-
|
|
39
|
-
To scan the project for linting errors, run
|
|
40
|
-
```bash
|
|
41
|
-
yarn run lint
|
|
42
|
-
```
|
|
77
|
+
### Config Options
|
|
43
78
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
79
|
+
All of the config options:
|
|
80
|
+
|
|
81
|
+
```javascript
|
|
82
|
+
let iaBookActions = document.querySelector('ia-book-actions');
|
|
83
|
+
const modalConfig = new ModalConfig(); // to show info/error modal
|
|
84
|
+
|
|
85
|
+
// set defaultLendingStatus for unavailable (without borrowables) book
|
|
86
|
+
iaBookActions.userid = '@jack';
|
|
87
|
+
iaBookActions.identifier = 'jack-sparrow';
|
|
88
|
+
iaBookActions.bookTitle = 'Contemporary Black biography. Volume 39 : profiles from the interContemporary Black biography. Volume 39';
|
|
89
|
+
iaBookActions.lendingStatus = {active_borrows: 0, active_browses: 0}, // complete lendingStatus object
|
|
90
|
+
iaBookActions.bwbPurchaseUrl = ''; // BWB purchase URl if any
|
|
91
|
+
iaBookActions.barType = '';
|
|
92
|
+
iaBookActions.modalConfig = modalConfig;
|
|
47
93
|
```
|
|
94
|
+
|
|
95
|
+
# Development
|
|
96
|
+
|
|
97
|
+
## Prerequisite
|
|
48
98
|
```bash
|
|
49
|
-
yarn
|
|
99
|
+
yarn install
|
|
50
100
|
```
|
|
51
101
|
|
|
52
|
-
|
|
102
|
+
## Start Development Server
|
|
53
103
|
```bash
|
|
54
|
-
yarn
|
|
104
|
+
yarn start
|
|
55
105
|
```
|
|
56
106
|
|
|
57
|
-
|
|
107
|
+
## Testing
|
|
58
108
|
```bash
|
|
59
|
-
yarn
|
|
109
|
+
yarn test
|
|
60
110
|
```
|
|
111
|
+
|
|
112
|
+
## Linting
|
|
61
113
|
```bash
|
|
62
|
-
yarn
|
|
114
|
+
yarn lint
|
|
63
115
|
```
|
|
64
|
-
|
|
65
|
-
## Tooling configs
|
|
66
|
-
|
|
67
|
-
For most of the tools, the configuration is in the `package.json` to reduce the amount of files in your project.
|
|
68
|
-
|
|
69
|
-
If you customize the configuration a lot, you can consider moving them to individual files.
|
|
Binary file
|
|
Binary file
|
package/demo/index.html
CHANGED
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-4",
|
|
4
4
|
"description": "Bookreader actions for borrowable book",
|
|
5
5
|
"author": "Internet Archive",
|
|
6
6
|
"license": "AGPL-3.0-only",
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"@internetarchive/icon-info": "^1.2.6",
|
|
25
25
|
"@internetarchive/modal-manager": "^0.1.0",
|
|
26
26
|
"@internetarchive/shared-resize-observer": "^0.0.1",
|
|
27
|
+
"idb-keyval": "^6.0.3",
|
|
27
28
|
"lit-element": "^2.4.0",
|
|
28
29
|
"lit-html": "^1.3.0"
|
|
29
30
|
},
|
|
@@ -2,6 +2,7 @@ import { html } from 'lit-element';
|
|
|
2
2
|
import { nothing } from 'lit-html';
|
|
3
3
|
import { classMap } from 'lit-html/directives/class-map';
|
|
4
4
|
|
|
5
|
+
import { get as indexedDBGet } from 'idb-keyval';
|
|
5
6
|
import ActionsHandler from '../core/services/actions-handler/actions-handler.js';
|
|
6
7
|
|
|
7
8
|
import buttonBaseStyle from '../assets/styles/ia-button.js';
|
|
@@ -63,23 +64,37 @@ export class CollapsibleActionGroup extends ActionsHandler {
|
|
|
63
64
|
}
|
|
64
65
|
}
|
|
65
66
|
|
|
66
|
-
|
|
67
|
-
|
|
67
|
+
/**
|
|
68
|
+
* Dispatches event when patron is borrowing book.
|
|
69
|
+
* Notes borrow type, and consecutive borrow counts
|
|
70
|
+
*
|
|
71
|
+
* @fires CollapsibleActionGroup#enableBookAccess
|
|
72
|
+
*/
|
|
73
|
+
async emitEnableBookAccess() {
|
|
68
74
|
// send consecutiveLoanCounts for browsed books only.
|
|
69
|
-
// for borrowed books, we just send [Counts-1]
|
|
70
75
|
if (this.borrowType === 'browsed') {
|
|
71
|
-
|
|
72
|
-
|
|
76
|
+
try {
|
|
77
|
+
const existingCount = await indexedDBGet('consecutive-loan-count');
|
|
78
|
+
if (existingCount !== undefined) {
|
|
79
|
+
this.consecutiveLoanCounts = existingCount.value ?? 1;
|
|
80
|
+
}
|
|
81
|
+
} catch (error) {
|
|
82
|
+
console.log(error);
|
|
83
|
+
}
|
|
73
84
|
}
|
|
74
85
|
|
|
86
|
+
// event category and action for browsing book access
|
|
87
|
+
const eventCategory = `${this.borrowType}BookAccess`;
|
|
88
|
+
const eventAction = `${
|
|
89
|
+
this.borrowType === 'browsed' ? 'BrowseCounts-' : 'Counts-'
|
|
90
|
+
}${this.consecutiveLoanCounts}`;
|
|
91
|
+
|
|
75
92
|
this.dispatchEvent(
|
|
76
93
|
new CustomEvent('enableBookAccess', {
|
|
77
94
|
detail: {
|
|
78
95
|
event: {
|
|
79
|
-
category:
|
|
80
|
-
action:
|
|
81
|
-
this.borrowType === 'browsed' ? 'BrowseCounts-' : 'Counts-'
|
|
82
|
-
}${this.consecutiveLoanCounts}`, // (BrowseCounts-1|Counts-X)
|
|
96
|
+
category: eventCategory, // browsedBookAccess | borrowedBookAccess
|
|
97
|
+
action: eventAction, // (BrowseCounts-N|Counts-N)
|
|
83
98
|
},
|
|
84
99
|
borrowType: this.borrowType,
|
|
85
100
|
},
|
|
@@ -219,11 +234,13 @@ export class CollapsibleActionGroup extends ActionsHandler {
|
|
|
219
234
|
}
|
|
220
235
|
|
|
221
236
|
/**
|
|
222
|
-
*
|
|
223
|
-
* @param { string } eventName
|
|
224
|
-
* @param { object } gaEvent
|
|
237
|
+
* Dispatches click events when patron clicks on action buttons
|
|
238
|
+
* @param { string } eventName - actions like 'browseBook', 'borrowBook' etc...
|
|
239
|
+
* @param { object } gaEvent - contains analytics event action and category
|
|
225
240
|
* @param { string } gaEvent.category
|
|
226
241
|
* @param { string } gaEvent.action
|
|
242
|
+
*
|
|
243
|
+
* @fires CollapsibleActionGroup#{eventName} - (will be browseBook, borrowBook etc...)
|
|
227
244
|
*/
|
|
228
245
|
clickHandler(eventName, gaEvent) {
|
|
229
246
|
this.dropdownState = 'close';
|
|
@@ -13,12 +13,15 @@ export default function ActionsHandlerService(options) {
|
|
|
13
13
|
...options,
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
-
let baseHost = '';
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
let baseHost = '/services/loans/loan';
|
|
17
|
+
const location = window?.location;
|
|
18
|
+
|
|
19
|
+
// return intentional error on dev environment
|
|
20
|
+
const shouldReturnError =
|
|
21
|
+
location?.href?.indexOf('?error=true') !== -1 &&
|
|
22
|
+
location?.hostname !== 'archive.org';
|
|
23
|
+
|
|
24
|
+
if (location?.pathname === '/demo/') baseHost = `/demo/`;
|
|
22
25
|
|
|
23
26
|
let formData = new FormData();
|
|
24
27
|
formData.append('action', option.action);
|
|
@@ -29,18 +32,16 @@ export default function ActionsHandlerService(options) {
|
|
|
29
32
|
body: formData,
|
|
30
33
|
})
|
|
31
34
|
.then(response => {
|
|
32
|
-
let shouldReturnError =
|
|
33
|
-
window.location.href.indexOf('?error=true') !== -1;
|
|
34
35
|
const tokenError = 'loan token not found. please try again later.';
|
|
35
36
|
const borrowError =
|
|
36
37
|
'This book is not available to borrow at this time. Please try again later.';
|
|
37
38
|
|
|
38
|
-
// return error reponse
|
|
39
|
+
// return error reponse when not production and has ?error=true param...
|
|
39
40
|
const erroneousActions = ['browse_book', 'borrow_book', 'create_token'];
|
|
40
|
-
if (shouldReturnError && erroneousActions.includes(option
|
|
41
|
+
if (shouldReturnError && erroneousActions.includes(option?.action)) {
|
|
41
42
|
return {
|
|
42
43
|
success: false,
|
|
43
|
-
error: option
|
|
44
|
+
error: option?.action === 'create_token' ? tokenError : borrowError,
|
|
44
45
|
};
|
|
45
46
|
}
|
|
46
47
|
|
|
@@ -58,7 +59,7 @@ export default function ActionsHandlerService(options) {
|
|
|
58
59
|
})
|
|
59
60
|
.then(data => {
|
|
60
61
|
// `data` is the parsed version of the JSON returned from the above endpoint.
|
|
61
|
-
if (!data
|
|
62
|
+
if (!data?.error) {
|
|
62
63
|
option?.success(data);
|
|
63
64
|
} else {
|
|
64
65
|
option?.error(data);
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { LitElement } from 'lit-element';
|
|
2
|
+
import { get as indexedDBGet, set as indexedDBSet } from 'idb-keyval';
|
|
3
|
+
|
|
2
4
|
import { URLHelper } from '../../config/url-helper.js';
|
|
3
5
|
import ActionsHandlerService from './actions-handler-service.js';
|
|
4
6
|
|
|
@@ -14,10 +16,10 @@ 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
|
-
this.loanTokenInterval =
|
|
22
|
+
this.loanTokenInterval = undefined;
|
|
21
23
|
this.bindEvents();
|
|
22
24
|
}
|
|
23
25
|
|
|
@@ -105,8 +107,6 @@ export default class ActionsHandler extends LitElement {
|
|
|
105
107
|
|
|
106
108
|
// fetch loan token for browsed/borrowed book and set an interval
|
|
107
109
|
if (borrowType) {
|
|
108
|
-
console.log('token poll started...');
|
|
109
|
-
|
|
110
110
|
this.loanTokenInterval = setInterval(() => {
|
|
111
111
|
this.handleLoanTokenPoller();
|
|
112
112
|
}, this.loanTokenPollingDelay);
|
|
@@ -121,106 +121,125 @@ export default class ActionsHandler extends LitElement {
|
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
handleBrowseIt() {
|
|
124
|
-
const
|
|
125
|
-
this.
|
|
124
|
+
const action = 'browse_book';
|
|
125
|
+
this.dispatchToggleActionGroup();
|
|
126
126
|
|
|
127
127
|
ActionsHandlerService({
|
|
128
|
-
action
|
|
128
|
+
action,
|
|
129
129
|
identifier: this.identifier,
|
|
130
130
|
success: () => {
|
|
131
131
|
this.handleReadItNow();
|
|
132
132
|
},
|
|
133
133
|
error: data => {
|
|
134
|
-
this.
|
|
134
|
+
this.dispatchActionError(action, data);
|
|
135
135
|
},
|
|
136
136
|
});
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
handleReturnIt() {
|
|
140
|
-
const
|
|
141
|
-
this.
|
|
140
|
+
const action = 'return_loan';
|
|
141
|
+
this.dispatchToggleActionGroup();
|
|
142
142
|
|
|
143
143
|
ActionsHandlerService({
|
|
144
|
-
action
|
|
144
|
+
action,
|
|
145
145
|
identifier: this.identifier,
|
|
146
146
|
success: () => {
|
|
147
147
|
this.deleteLoanCookies();
|
|
148
148
|
URLHelper.goToUrl(`/details/${this.identifier}`, true);
|
|
149
149
|
},
|
|
150
150
|
error: data => {
|
|
151
|
-
this.
|
|
151
|
+
this.dispatchActionError(action, data);
|
|
152
152
|
},
|
|
153
153
|
});
|
|
154
154
|
}
|
|
155
155
|
|
|
156
156
|
handleBorrowIt() {
|
|
157
|
-
const
|
|
158
|
-
this.
|
|
157
|
+
const action = 'borrow_book';
|
|
158
|
+
this.dispatchToggleActionGroup();
|
|
159
159
|
|
|
160
160
|
ActionsHandlerService({
|
|
161
|
-
action
|
|
161
|
+
action,
|
|
162
162
|
identifier: this.identifier,
|
|
163
163
|
success: () => {
|
|
164
164
|
this.handleReadItNow();
|
|
165
165
|
},
|
|
166
166
|
error: data => {
|
|
167
|
-
this.
|
|
167
|
+
this.dispatchActionError(action, data);
|
|
168
168
|
},
|
|
169
169
|
});
|
|
170
170
|
}
|
|
171
171
|
|
|
172
172
|
handleReserveIt() {
|
|
173
|
-
const
|
|
174
|
-
this.
|
|
173
|
+
const action = 'join_waitlist';
|
|
174
|
+
this.dispatchToggleActionGroup();
|
|
175
175
|
|
|
176
176
|
ActionsHandlerService({
|
|
177
|
-
action
|
|
177
|
+
action,
|
|
178
178
|
identifier: this.identifier,
|
|
179
179
|
success: () => {
|
|
180
180
|
URLHelper.goToUrl(URLHelper.getRedirectUrl(), true);
|
|
181
181
|
},
|
|
182
182
|
error: data => {
|
|
183
|
-
this.
|
|
183
|
+
this.dispatchActionError(action, data);
|
|
184
184
|
},
|
|
185
185
|
});
|
|
186
186
|
}
|
|
187
187
|
|
|
188
188
|
handleRemoveFromWaitingList() {
|
|
189
|
-
const
|
|
190
|
-
this.
|
|
189
|
+
const action = 'leave_waitlist';
|
|
190
|
+
this.dispatchToggleActionGroup();
|
|
191
191
|
|
|
192
192
|
ActionsHandlerService({
|
|
193
|
-
action
|
|
193
|
+
action,
|
|
194
194
|
identifier: this.identifier,
|
|
195
195
|
success: () => {
|
|
196
196
|
URLHelper.goToUrl(URLHelper.getRedirectUrl(), true);
|
|
197
197
|
},
|
|
198
198
|
error: data => {
|
|
199
|
-
this.
|
|
199
|
+
this.dispatchActionError(action, data);
|
|
200
200
|
},
|
|
201
201
|
});
|
|
202
202
|
}
|
|
203
203
|
|
|
204
204
|
handleLoanTokenPoller() {
|
|
205
|
-
const
|
|
205
|
+
const action = 'create_token';
|
|
206
206
|
ActionsHandlerService({
|
|
207
207
|
identifier: this.identifier,
|
|
208
|
-
action
|
|
208
|
+
action,
|
|
209
209
|
error: data => {
|
|
210
|
-
this.
|
|
210
|
+
this.dispatchActionError(action, data);
|
|
211
211
|
clearInterval(this.loanTokenInterval); // stop token fetch api
|
|
212
212
|
},
|
|
213
213
|
});
|
|
214
214
|
}
|
|
215
215
|
|
|
216
|
-
|
|
216
|
+
/**
|
|
217
|
+
* Dispatches event when an error occured on action
|
|
218
|
+
* Notes:- toggle <collapsible-action-group> visibility (enable/disable).
|
|
219
|
+
*
|
|
220
|
+
* @param { String } action - name of action like browse_book, borrow_book
|
|
221
|
+
* @param { Object } data - erroneous response from api call
|
|
222
|
+
*
|
|
223
|
+
* @fires ActionsHandler#lendingActionError
|
|
224
|
+
*/
|
|
225
|
+
dispatchActionError(action, data = {}) {
|
|
217
226
|
this.dispatchEvent(
|
|
218
227
|
new CustomEvent('lendingActionError', {
|
|
219
|
-
detail: {
|
|
228
|
+
detail: { action, data },
|
|
220
229
|
})
|
|
221
230
|
);
|
|
222
231
|
}
|
|
223
232
|
|
|
233
|
+
/**
|
|
234
|
+
* Dispatches event when patron is clicked on action buttons.
|
|
235
|
+
* Notes:- toggle <collapsible-action-group> disable/enable.
|
|
236
|
+
*
|
|
237
|
+
* @fires ActionsHandler#toggleActionGroup
|
|
238
|
+
*/
|
|
239
|
+
dispatchToggleActionGroup() {
|
|
240
|
+
this.dispatchEvent(new CustomEvent('toggleActionGroup'));
|
|
241
|
+
}
|
|
242
|
+
|
|
224
243
|
handleLoginOk() {
|
|
225
244
|
/* eslint class-methods-use-this: "off" */
|
|
226
245
|
const target = `/account/login?referer=${encodeURIComponent(
|
|
@@ -254,19 +273,28 @@ export default class ActionsHandler extends LitElement {
|
|
|
254
273
|
}
|
|
255
274
|
|
|
256
275
|
// save consecutive loan count for borrow
|
|
257
|
-
setConsecutiveLoanCounts(action = '') {
|
|
258
|
-
|
|
259
|
-
if (localStorage) {
|
|
276
|
+
async setConsecutiveLoanCounts(action = '') {
|
|
277
|
+
try {
|
|
260
278
|
let newCount = 1;
|
|
261
279
|
const storageKey = `consecutive-loan-count`;
|
|
262
|
-
const existingCount =
|
|
280
|
+
const existingCount = await indexedDBGet(storageKey);
|
|
281
|
+
|
|
282
|
+
// expire indexDB key-value after 2 hours
|
|
283
|
+
const expireTime = new Date(
|
|
284
|
+
Date.now() + (1 / 12) * 24 * 60 * 60 * 1000
|
|
285
|
+
).toString();
|
|
263
286
|
|
|
264
|
-
//
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
newCount = existingCount ? existingCount + 1 : 1;
|
|
287
|
+
// increase browse-count by 1 when you consecutive reading a book.
|
|
288
|
+
if (action === 'browseAgain' && existingCount !== undefined) {
|
|
289
|
+
newCount = existingCount.value ? existingCount.value + 1 : 1;
|
|
268
290
|
}
|
|
269
|
-
|
|
291
|
+
|
|
292
|
+
await indexedDBSet(storageKey, {
|
|
293
|
+
value: newCount,
|
|
294
|
+
expire: expireTime,
|
|
295
|
+
});
|
|
296
|
+
} catch (error) {
|
|
297
|
+
console.log(error);
|
|
270
298
|
}
|
|
271
299
|
}
|
|
272
300
|
|
|
@@ -75,9 +75,9 @@ export default class GetLendingActions {
|
|
|
75
75
|
const disableBorrow = lendingStatus.loanCount >= lendingStatus.maxLoans;
|
|
76
76
|
|
|
77
77
|
let primaryTitleText = '';
|
|
78
|
-
let
|
|
78
|
+
let isBrowsing =
|
|
79
79
|
lendingStatus.user_has_browsed && !lendingStatus.browsingExpired;
|
|
80
|
-
if (
|
|
80
|
+
if (isBrowsing) {
|
|
81
81
|
primaryTitleText = this.getBrowseCountdownTitle();
|
|
82
82
|
} else {
|
|
83
83
|
primaryTitleText = `Your loan of this book has ${lendingStatus.daysLeftOnLoan} days left.`;
|
|
@@ -96,7 +96,7 @@ export default class GetLendingActions {
|
|
|
96
96
|
this.actionsConfig.adminAccessConfig(),
|
|
97
97
|
this.actionsConfig.purchaseConfig(),
|
|
98
98
|
],
|
|
99
|
-
borrowType:
|
|
99
|
+
borrowType: isBrowsing ? 'browsed' : 'borrowed',
|
|
100
100
|
};
|
|
101
101
|
}
|
|
102
102
|
|
package/src/ia-book-actions.js
CHANGED
|
@@ -10,8 +10,6 @@ import './components/text-group.js';
|
|
|
10
10
|
import './components/info-icon.js';
|
|
11
11
|
|
|
12
12
|
import GetLendingActions from './core/services/get-lending-actions.js';
|
|
13
|
-
import getRealTimeLendingStatus from './core/services/get-realtime-lending-status.js';
|
|
14
|
-
|
|
15
13
|
import { mobileContainerWidth } from './core/config/constants.js';
|
|
16
14
|
|
|
17
15
|
export default class IABookActions extends LitElement {
|
|
@@ -26,7 +24,7 @@ export default class IABookActions extends LitElement {
|
|
|
26
24
|
barType: { type: String },
|
|
27
25
|
sharedObserver: { attribute: false },
|
|
28
26
|
disableActionGroup: { type: Boolean },
|
|
29
|
-
|
|
27
|
+
modal: { Object },
|
|
30
28
|
};
|
|
31
29
|
}
|
|
32
30
|
|
|
@@ -40,14 +38,15 @@ export default class IABookActions extends LitElement {
|
|
|
40
38
|
this.bwbPurchaseUrl = '';
|
|
41
39
|
this.barType = 'action'; // 'title'|'action'
|
|
42
40
|
this.sharedObserver = undefined;
|
|
41
|
+
this.disableActionGroup = false;
|
|
42
|
+
this.modal = undefined;
|
|
43
|
+
|
|
44
|
+
// private props
|
|
43
45
|
this.primaryActions = [];
|
|
44
46
|
this.primaryTitle = '';
|
|
45
47
|
this.primaryColor = 'primary';
|
|
46
48
|
this.secondaryActions = [];
|
|
47
49
|
this.lendingOptions = {};
|
|
48
|
-
this.disableActionGroup = false;
|
|
49
|
-
this.modalConfig = {};
|
|
50
|
-
this.realtimeInternal = 0; // store intervalID
|
|
51
50
|
}
|
|
52
51
|
|
|
53
52
|
disconnectedCallback() {
|
|
@@ -60,25 +59,7 @@ export default class IABookActions extends LitElement {
|
|
|
60
59
|
this.setupResizeObserver();
|
|
61
60
|
}
|
|
62
61
|
|
|
63
|
-
if (!Object.keys(this.modalConfig).length) {
|
|
64
|
-
this.modalConfig = new ModalConfig();
|
|
65
|
-
this.modalConfig.headerColor = '#d9534f';
|
|
66
|
-
}
|
|
67
62
|
this.setupLendingToolbarActions();
|
|
68
|
-
|
|
69
|
-
// fetch latest lending status after an internal
|
|
70
|
-
if (
|
|
71
|
-
this.lendingStatus.user_has_browsed ||
|
|
72
|
-
this.lendingStatus.user_has_borrowed
|
|
73
|
-
) {
|
|
74
|
-
this.realtimeInternal = setInterval(() => {
|
|
75
|
-
const latestLendingStatus = getRealTimeLendingStatus(
|
|
76
|
-
'naturalhistoryof00unse_4'
|
|
77
|
-
);
|
|
78
|
-
console.log(latestLendingStatus);
|
|
79
|
-
if (latestLendingStatus) this.lendingStatus = latestLendingStatus;
|
|
80
|
-
}, 30000); // 60000 ms = 1 min
|
|
81
|
-
}
|
|
82
63
|
}
|
|
83
64
|
|
|
84
65
|
updated(changed) {
|
|
@@ -202,6 +183,7 @@ export default class IABookActions extends LitElement {
|
|
|
202
183
|
?hasAdminAccess=${this.hasAdminAccess}
|
|
203
184
|
?disabled=${this.disableActionGroup}
|
|
204
185
|
@lendingActionError=${this.handleLendingActionError}
|
|
186
|
+
@toggleActionGroup=${this.handleToggleActionGroup}
|
|
205
187
|
>
|
|
206
188
|
</collapsible-action-group>
|
|
207
189
|
${this.textGroupTemplate} ${this.infoIconTemplate}
|
|
@@ -209,27 +191,41 @@ export default class IABookActions extends LitElement {
|
|
|
209
191
|
}
|
|
210
192
|
|
|
211
193
|
/*
|
|
212
|
-
*
|
|
194
|
+
* custom event handler to toggle action group visibility
|
|
195
|
+
*
|
|
196
|
+
* @event IABookActions#toggleActionGroup
|
|
197
|
+
*/
|
|
198
|
+
handleToggleActionGroup() {
|
|
199
|
+
this.disableActionGroup = !this.disableActionGroup;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/*
|
|
203
|
+
* handle lending errors occure during different operation like
|
|
213
204
|
* browse book, borrow book, borrowed book token etc...
|
|
205
|
+
*
|
|
206
|
+
* @event IABookActions#lendingActionError
|
|
207
|
+
* @param {Object} event - The employee who is responsible for the project
|
|
208
|
+
* @param {string} event.detail.action - action when error occurred like 'browseBook', 'borrowBook'
|
|
209
|
+
* @param {string} event.detail.data.error - error message
|
|
214
210
|
*/
|
|
215
|
-
handleLendingActionError(
|
|
211
|
+
handleLendingActionError(event) {
|
|
216
212
|
// toggle activity loader
|
|
217
|
-
this.
|
|
213
|
+
this.handleToggleActionGroup();
|
|
218
214
|
|
|
219
|
-
const
|
|
220
|
-
const errorMsg =
|
|
215
|
+
const action = event?.detail?.action;
|
|
216
|
+
const errorMsg = event?.detail?.data?.error;
|
|
221
217
|
|
|
222
|
-
if (errorMsg) this.showErrorModal(
|
|
218
|
+
if (errorMsg) this.showErrorModal(errorMsg, action);
|
|
223
219
|
|
|
224
220
|
// update action bar state if book is not available to browse or borrow.
|
|
225
221
|
if (errorMsg && errorMsg.match(/not available to borrow/gm)) {
|
|
226
222
|
let currStatus = this.lendingStatus;
|
|
227
|
-
if (
|
|
223
|
+
if (action === 'browse_book') {
|
|
228
224
|
currStatus = {
|
|
229
225
|
...this.lendingStatus,
|
|
230
226
|
available_to_browse: false,
|
|
231
227
|
};
|
|
232
|
-
} else if (
|
|
228
|
+
} else if (action === 'borrow_book') {
|
|
233
229
|
currStatus = {
|
|
234
230
|
...this.lendingStatus,
|
|
235
231
|
available_to_borrow: false,
|
|
@@ -240,24 +236,42 @@ export default class IABookActions extends LitElement {
|
|
|
240
236
|
}
|
|
241
237
|
|
|
242
238
|
/* show error message if something went wrong */
|
|
243
|
-
async showErrorModal(
|
|
244
|
-
|
|
245
|
-
this.modal = document.createElement('modal-manager');
|
|
246
|
-
this.modal.id = 'action-bar-modal';
|
|
247
|
-
await document.body.appendChild(this.modal);
|
|
239
|
+
async showErrorModal(errorMsg, action) {
|
|
240
|
+
this.disableActionGroup = false;
|
|
248
241
|
|
|
249
|
-
//
|
|
242
|
+
// check if this.modal passed as prop
|
|
250
243
|
if (!this.modal) {
|
|
251
|
-
|
|
252
|
-
|
|
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');
|
|
253
248
|
}
|
|
254
249
|
|
|
255
|
-
this.
|
|
256
|
-
this.
|
|
257
|
-
|
|
250
|
+
this.modal.id = 'action-bar-modal';
|
|
251
|
+
await document.body.appendChild(this.modal);
|
|
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
|
+
}
|
|
258
272
|
|
|
259
273
|
this.modal.showModal({
|
|
260
|
-
config:
|
|
274
|
+
config: modalConfig,
|
|
261
275
|
});
|
|
262
276
|
}
|
|
263
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
|
-
beforeEach(() => {
|
|
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
|
});
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Helper function to fetch real-time lending status
|
|
3
|
-
*/
|
|
4
|
-
export default function getRealTimeLendingStatus(identifier) {
|
|
5
|
-
let url = `https://ia-petabox-neeraj.archive.org/services/book/lending-info.php?identifier=${identifier}`;
|
|
6
|
-
|
|
7
|
-
if (window.location.pathname === '/demo/') {
|
|
8
|
-
url = ''; // change URL to prevent error on localhost
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
fetch(url)
|
|
12
|
-
.then(response => {
|
|
13
|
-
// return { response_code: '400', data: 'error happened' };
|
|
14
|
-
return response.json();
|
|
15
|
-
})
|
|
16
|
-
.then(response => {
|
|
17
|
-
console.log(response);
|
|
18
|
-
if (response?.response_code === 200) {
|
|
19
|
-
const lendingInfo = response?.body?.data?.lendingInfo;
|
|
20
|
-
let newLendingInfo = lendingInfo;
|
|
21
|
-
const newLendingStatus = lendingInfo?.lendingStatus;
|
|
22
|
-
newLendingInfo = { ...newLendingInfo, ...newLendingStatus };
|
|
23
|
-
newLendingInfo.lendingStatus = '';
|
|
24
|
-
return newLendingInfo;
|
|
25
|
-
}
|
|
26
|
-
return null;
|
|
27
|
-
})
|
|
28
|
-
.catch(response => {
|
|
29
|
-
return response;
|
|
30
|
-
});
|
|
31
|
-
}
|