@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 CHANGED
@@ -1,69 +1,115 @@
1
- [![Build Status](https://travis-ci.com/internetarchive/iaux-your-webcomponent.svg?branch=master)](https://travis-ci.com/internetarchive/iaux-your-webcomponent) [![codecov](https://codecov.io/gh/internetarchive/iaux-your-webcomponent/branch/master/graph/badge.svg)](https://codecov.io/gh/internetarchive/iaux-your-webcomponent)
1
+ # Component for lendable and embed items
2
2
 
3
- # Internet Archive Javascript WebComponent Template
3
+ The book action button component build on LitElement with support for custom content and light DOM elements.
4
4
 
5
- This is a base template for creating Javascript WebComponents. It is based off of the [Open WebComponents generator](https://open-wc.org/docs/development/generator/) with some IA-specific customizations and some development niceities.
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 start
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
- ## Testing with Web Test Runner
27
- To run the suite of Web Test Runner tests, run
28
- ```bash
29
- yarn run test
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
- To run the tests in watch mode (for &lt;abbr title=&#34;test driven development&#34;&gt;TDD&lt;/abbr&gt;, for example), run
66
+ ![Book Action Bar](./assets/ia-book-action-screen.png "Book Action Bar Demo")
33
67
 
34
- ```bash
35
- yarn run test:watch
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
+ ![Book Title Bar](./assets/ia-book-title-screen.png "Book Title Bar Demo")
37
76
 
38
- ## Linting with ESLint, Prettier, and Types
39
- To scan the project for linting errors, run
40
- ```bash
41
- yarn run lint
42
- ```
77
+ ### Config Options
43
78
 
44
- You can lint with ESLint and Prettier individually as well
45
- ```bash
46
- yarn run lint:eslint
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 run lint:prettier
99
+ yarn install
50
100
  ```
51
101
 
52
- To automatically fix many linting errors, run
102
+ ## Start Development Server
53
103
  ```bash
54
- yarn run format
104
+ yarn start
55
105
  ```
56
106
 
57
- You can format using ESLint and Prettier individually as well
107
+ ## Testing
58
108
  ```bash
59
- yarn run format:eslint
109
+ yarn test
60
110
  ```
111
+
112
+ ## Linting
61
113
  ```bash
62
- yarn run format:prettier
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
@@ -76,13 +76,9 @@
76
76
  overflow: hidden;
77
77
  }
78
78
 
79
- modal-manager {
79
+ modal-manager[mode='closed'] {
80
80
  display: none;
81
81
  }
82
-
83
- modal-manager[mode='open'] {
84
- display: block;
85
- }
86
82
  </style>
87
83
  </head>
88
84
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@internetarchive/ia-book-actions",
3
- "version": "0.0.7-1",
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
- /* emit custom event to fetch loan token */
67
- emitEnableBookAccess() {
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
- this.consecutiveLoanCounts =
72
- localStorage.getItem('consecutive-loan-count') ?? 1;
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: `${this.borrowType}BookAccess`, // browsedBookAccess | borrowedBookAccess
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
- * Click handler to emit custom event on action click
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
- if (window.location.pathname === '/demo/') {
18
- baseHost = `/demo/`;
19
- } else {
20
- baseHost = `/services/loans/loan`;
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 if query param has ?error=true param...
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.action)) {
41
+ if (shouldReturnError && erroneousActions.includes(option?.action)) {
41
42
  return {
42
43
  success: false,
43
- error: option.action === 'create_token' ? tokenError : borrowError,
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.error) {
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 = 6000;
19
+ this.ajaxTimeout = 7000;
18
20
  this.loanTokenPollingDelay =
19
21
  window.location.pathname === '/demo/' ? 2000 : 120000; // 120000 ms = 2 min
20
- this.loanTokenInterval = null;
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 context = 'browse_book';
125
- this.ActionError(context);
124
+ const action = 'browse_book';
125
+ this.dispatchToggleActionGroup();
126
126
 
127
127
  ActionsHandlerService({
128
- action: context,
128
+ action,
129
129
  identifier: this.identifier,
130
130
  success: () => {
131
131
  this.handleReadItNow();
132
132
  },
133
133
  error: data => {
134
- this.ActionError(context, data);
134
+ this.dispatchActionError(action, data);
135
135
  },
136
136
  });
137
137
  }
138
138
 
139
139
  handleReturnIt() {
140
- const context = 'return_loan';
141
- this.ActionError(context);
140
+ const action = 'return_loan';
141
+ this.dispatchToggleActionGroup();
142
142
 
143
143
  ActionsHandlerService({
144
- action: context,
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.ActionError(context, data);
151
+ this.dispatchActionError(action, data);
152
152
  },
153
153
  });
154
154
  }
155
155
 
156
156
  handleBorrowIt() {
157
- const context = 'borrow_book';
158
- this.ActionError(context);
157
+ const action = 'borrow_book';
158
+ this.dispatchToggleActionGroup();
159
159
 
160
160
  ActionsHandlerService({
161
- action: context,
161
+ action,
162
162
  identifier: this.identifier,
163
163
  success: () => {
164
164
  this.handleReadItNow();
165
165
  },
166
166
  error: data => {
167
- this.ActionError(context, data);
167
+ this.dispatchActionError(action, data);
168
168
  },
169
169
  });
170
170
  }
171
171
 
172
172
  handleReserveIt() {
173
- const context = 'join_waitlist';
174
- this.ActionError(context);
173
+ const action = 'join_waitlist';
174
+ this.dispatchToggleActionGroup();
175
175
 
176
176
  ActionsHandlerService({
177
- action: context,
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.ActionError(context, data);
183
+ this.dispatchActionError(action, data);
184
184
  },
185
185
  });
186
186
  }
187
187
 
188
188
  handleRemoveFromWaitingList() {
189
- const context = 'leave_waitlist';
190
- this.ActionError(context);
189
+ const action = 'leave_waitlist';
190
+ this.dispatchToggleActionGroup();
191
191
 
192
192
  ActionsHandlerService({
193
- action: context,
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.ActionError(context, data);
199
+ this.dispatchActionError(action, data);
200
200
  },
201
201
  });
202
202
  }
203
203
 
204
204
  handleLoanTokenPoller() {
205
- const context = 'create_token';
205
+ const action = 'create_token';
206
206
  ActionsHandlerService({
207
207
  identifier: this.identifier,
208
- action: context,
208
+ action,
209
209
  error: data => {
210
- this.ActionError(context, data);
210
+ this.dispatchActionError(action, data);
211
211
  clearInterval(this.loanTokenInterval); // stop token fetch api
212
212
  },
213
213
  });
214
214
  }
215
215
 
216
- ActionError(context, data = {}) {
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: { context, data },
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
- const { localStorage } = window;
259
- if (localStorage) {
276
+ async setConsecutiveLoanCounts(action = '') {
277
+ try {
260
278
  let newCount = 1;
261
279
  const storageKey = `consecutive-loan-count`;
262
- const existingCount = Number(localStorage.getItem(storageKey));
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
- // want to increase browse-count by 1 if,
265
- // you consecutive reading a book.
266
- if (action === 'browseAgain') {
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
- localStorage.setItem(storageKey, newCount);
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 isBrowsed =
78
+ let isBrowsing =
79
79
  lendingStatus.user_has_browsed && !lendingStatus.browsingExpired;
80
- if (isBrowsed) {
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: isBrowsed ? 'browsed' : 'borrowed',
99
+ borrowType: isBrowsing ? 'browsed' : 'borrowed',
100
100
  };
101
101
  }
102
102
 
@@ -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
- modalConfig: { type: Object },
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
- * handle lending errors occure on different operation like
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(e) {
211
+ handleLendingActionError(event) {
216
212
  // toggle activity loader
217
- this.disableActionGroup = !this.disableActionGroup;
213
+ this.handleToggleActionGroup();
218
214
 
219
- const context = e?.detail?.context;
220
- const errorMsg = e?.detail?.data?.error;
215
+ const action = event?.detail?.action;
216
+ const errorMsg = event?.detail?.data?.error;
221
217
 
222
- if (errorMsg) this.showErrorModal(context, errorMsg);
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 (context === 'browse_book') {
223
+ if (action === 'browse_book') {
228
224
  currStatus = {
229
225
  ...this.lendingStatus,
230
226
  available_to_browse: false,
231
227
  };
232
- } else if (context === 'borrow_book') {
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(context, errorMsg) {
244
- // add modal-manager in DOM to show alert message
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
- // fallback if <modal-manager> is not found!
242
+ // check if this.modal passed as prop
250
243
  if (!this.modal) {
251
- alert(errorMsg);
252
- return;
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.disableActionGroup = false;
256
- this.modalConfig.title = 'Lending error';
257
- this.modalConfig.message = errorMsg;
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: this.modalConfig,
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
- context: 'browse_book',
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
- context: 'borrow_book',
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
- }