@internetarchive/ia-book-actions 1.1.0-alpha8 → 1.1.0
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/.github/workflows/ci.yml +26 -0
- package/.github/workflows/gh-pages-main.yml +39 -0
- package/.github/workflows/pr-preview.yml +37 -0
- package/demo/index.html +44 -8
- package/index.html +474 -0
- package/package.json +15 -7
- package/src/assets/data/lending-get-status-service.js +1 -1
- package/src/components/timer-countdown.js +3 -2
- package/src/core/services/actions-handler/actions-handler-service.js +8 -3
- package/src/core/services/log.js +2 -1
- package/src/ia-book-actions.js +115 -112
- package/test/components/timer-countdown.test.js +3 -2
- package/test/ia-book-actions.test.js +57 -1
- package/vite.config.ts +22 -0
- package/web-dev-server.config.mjs +1 -1
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
name: App CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ main ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ main ]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
build:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
- uses: actions/setup-node@v3
|
|
16
|
+
with:
|
|
17
|
+
node-version: 16.x
|
|
18
|
+
|
|
19
|
+
- name: Install dependencies
|
|
20
|
+
run: yarn install
|
|
21
|
+
|
|
22
|
+
# - name: Run tests
|
|
23
|
+
# run: yarn run test
|
|
24
|
+
|
|
25
|
+
# - name: Upload reports
|
|
26
|
+
# run: npx codecov
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# This workflow will generate the static page under `main` subdirectory inside the `gh-pages` branch
|
|
2
|
+
|
|
3
|
+
# This workflow will run every time new changes were pushed to the `main` branch
|
|
4
|
+
|
|
5
|
+
name: App build CI/CD to main branch
|
|
6
|
+
|
|
7
|
+
on:
|
|
8
|
+
push:
|
|
9
|
+
branches: [ main ]
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
build:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
with:
|
|
18
|
+
persist-credentials: false
|
|
19
|
+
|
|
20
|
+
- uses: actions/setup-node@v3
|
|
21
|
+
with:
|
|
22
|
+
node-version: 20
|
|
23
|
+
|
|
24
|
+
- name: Install dependencies
|
|
25
|
+
run: yarn install
|
|
26
|
+
|
|
27
|
+
- name: Create build files for gh-pages deploy
|
|
28
|
+
run: yarn prepare:ghpages
|
|
29
|
+
|
|
30
|
+
# Reference: https://github.com/JamesIves/github-pages-deploy-action
|
|
31
|
+
- name: Deploy 🚀
|
|
32
|
+
uses: JamesIves/github-pages-deploy-action@v4.4.3
|
|
33
|
+
with:
|
|
34
|
+
branch: gh-pages
|
|
35
|
+
folder: ghpages
|
|
36
|
+
clean-exclude: pr/
|
|
37
|
+
force: false
|
|
38
|
+
target-folder: main
|
|
39
|
+
token: ${{ secrets.GH_TOKEN }}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# This workflow will generate the static page under `pr` subdirectory inside the `gh-pages` branch
|
|
2
|
+
|
|
3
|
+
# This workflow will run every time there's a PR opened, reopened, synchronize, or closed
|
|
4
|
+
|
|
5
|
+
name: Deploy PR previews
|
|
6
|
+
|
|
7
|
+
on:
|
|
8
|
+
pull_request:
|
|
9
|
+
types:
|
|
10
|
+
- opened
|
|
11
|
+
- reopened
|
|
12
|
+
- synchronize
|
|
13
|
+
- closed
|
|
14
|
+
|
|
15
|
+
concurrency: preview-${{ github.ref }}
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
deploy-preview:
|
|
19
|
+
runs-on: ubuntu-20.04
|
|
20
|
+
steps:
|
|
21
|
+
- name: Checkout
|
|
22
|
+
uses: actions/checkout@v4
|
|
23
|
+
|
|
24
|
+
- uses: actions/setup-node@v3
|
|
25
|
+
with:
|
|
26
|
+
node-version: 20.x
|
|
27
|
+
|
|
28
|
+
- name: Install and Build
|
|
29
|
+
run: |
|
|
30
|
+
yarn install
|
|
31
|
+
yarn prepare:ghpages
|
|
32
|
+
# Reference: https://github.com/rossjrw/pr-preview-action
|
|
33
|
+
- name: Deploy preview
|
|
34
|
+
uses: rossjrw/pr-preview-action@v1
|
|
35
|
+
with:
|
|
36
|
+
source-dir: ./ghpages/
|
|
37
|
+
umbrella-dir: pr
|
package/demo/index.html
CHANGED
|
@@ -76,6 +76,13 @@
|
|
|
76
76
|
display: inline-grid;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
.action-button button {
|
|
80
|
+
padding: 6px;
|
|
81
|
+
cursor: pointer;
|
|
82
|
+
border: 2px solid #194880;
|
|
83
|
+
border-radius: 10px;
|
|
84
|
+
}
|
|
85
|
+
|
|
79
86
|
body.modal-manager-open {
|
|
80
87
|
overflow: hidden;
|
|
81
88
|
}
|
|
@@ -128,10 +135,11 @@
|
|
|
128
135
|
iaBookActions.modalConfig = modalConfig;
|
|
129
136
|
iaBookActions.lendingBarPostInit = lendingBarPostInit;
|
|
130
137
|
iaBookActions.tokenDelay = 2; // 2 seconds
|
|
138
|
+
iaBookActions.timerExecutionSeconds = 4 // 4 seconds
|
|
131
139
|
iaBookActions.returnUrl = '';
|
|
132
140
|
iaBookActions.localCache = localCache;
|
|
133
141
|
|
|
134
|
-
let loanTotalTime2Min = { loanTotalTime:
|
|
142
|
+
let loanTotalTime2Min = { loanTotalTime: 120, loanRenewAtLast: 80, pageChangedInLast: 15 };
|
|
135
143
|
let loanTotalTime5Min = { loanTotalTime: 300, loanRenewAtLast: 180, pageChangedInLast: 15 };
|
|
136
144
|
let loanTotalTime1Hour = { loanTotalTime: 3600, loanRenewAtLast: 660, pageChangedInLast: 900 };
|
|
137
145
|
|
|
@@ -286,6 +294,19 @@
|
|
|
286
294
|
iaBookActions.browseHasExpired();
|
|
287
295
|
});
|
|
288
296
|
|
|
297
|
+
document.querySelector('#resync_timer').addEventListener('click', async (e) => {
|
|
298
|
+
if (iaBookActions.lendingStatus.user_has_browsed) {
|
|
299
|
+
document.querySelector('ia-book-actions').dispatchEvent(
|
|
300
|
+
new Event('visibilitychange', {
|
|
301
|
+
detail: {},
|
|
302
|
+
bubbles: true,
|
|
303
|
+
composed: true,
|
|
304
|
+
})
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
308
|
+
});
|
|
309
|
+
|
|
289
310
|
document.querySelectorAll('.availableToBorrow input[type=radio]').forEach((item) => {
|
|
290
311
|
item.addEventListener('click', (e) => {
|
|
291
312
|
if (e.target.value === 'available_to_borrow') {
|
|
@@ -359,8 +380,9 @@
|
|
|
359
380
|
</td>
|
|
360
381
|
<td class="searchParam">
|
|
361
382
|
<label><input type="checkbox" name="search" value="Search Param" />Search Param</label>
|
|
383
|
+
</td>
|
|
362
384
|
<td>
|
|
363
|
-
<button class="pageChangedEvent">Page Change Event</button>
|
|
385
|
+
<label class="ia-button"><button class="pageChangedEvent">Page Change Event</button></label>
|
|
364
386
|
</td>
|
|
365
387
|
</tr>
|
|
366
388
|
</table>
|
|
@@ -385,12 +407,6 @@
|
|
|
385
407
|
<label><input type="radio" name="browse" value="available_to_browse" />available_to_browse</label>
|
|
386
408
|
<label><input type="radio" name="browse" value="user_has_browsed" />user_has_browsed</label>
|
|
387
409
|
<label><input type="radio" name="browse" value="browsingExpired" />browsingExpired</label>
|
|
388
|
-
<div>
|
|
389
|
-
<button id="show_warning_modal">When Browsing (`user_has_browsed`), show Warning Modal</button>
|
|
390
|
-
</div>
|
|
391
|
-
<div>
|
|
392
|
-
<button id="show_expired_modal">While Browsing (`user_has_browsed`), Expire book & show its modal</button>
|
|
393
|
-
</div>
|
|
394
410
|
</td>
|
|
395
411
|
<td class="availableToBorrow">
|
|
396
412
|
<label><input type="radio" name="borrow" value="available_to_borrow" />available_to_borrow</label>
|
|
@@ -410,6 +426,26 @@
|
|
|
410
426
|
font-weight: bold; display: none" name="purchase" value="Reflect" />
|
|
411
427
|
</td>
|
|
412
428
|
</tr>
|
|
429
|
+
<tr>
|
|
430
|
+
<td>
|
|
431
|
+
<fieldset style="margin-top: 10px;">
|
|
432
|
+
<legend style="color: black;">controls for browsed book</legend>
|
|
433
|
+
<table aria-hidden="true">
|
|
434
|
+
<tbody><tr>
|
|
435
|
+
<td class="action-button">
|
|
436
|
+
<button id="show_warning_modal">When Browsing (`user_has_browsed`),<br> show Warning Modal</button>
|
|
437
|
+
</td>
|
|
438
|
+
<td class="action-button">
|
|
439
|
+
<button id="show_expired_modal">While Browsing (`user_has_browsed`),<br> Expire book & show its modal</button>
|
|
440
|
+
</td>
|
|
441
|
+
<td class="action-button">
|
|
442
|
+
<button id="resync_timer">While Browsing (`user_has_browsed`),<br> resync timer</button>
|
|
443
|
+
</td>
|
|
444
|
+
</tr>
|
|
445
|
+
</tbody></table>
|
|
446
|
+
</fieldset>
|
|
447
|
+
</td>
|
|
448
|
+
</tr>
|
|
413
449
|
</table>
|
|
414
450
|
</fieldset>
|
|
415
451
|
</div>
|
package/index.html
ADDED
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en-GB">
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="utf-8">
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
|
7
|
+
<style>
|
|
8
|
+
html {
|
|
9
|
+
font-size: 10px;
|
|
10
|
+
/* This is to match petabox's base font size */
|
|
11
|
+
}
|
|
12
|
+
body {
|
|
13
|
+
margin: 0;
|
|
14
|
+
text-align: center;
|
|
15
|
+
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
|
16
|
+
}
|
|
17
|
+
#demo {
|
|
18
|
+
height: 100px;
|
|
19
|
+
}
|
|
20
|
+
ia-book-actions {
|
|
21
|
+
--white: #fff;
|
|
22
|
+
--black: #000;
|
|
23
|
+
--primaryBGColor: var(--black);
|
|
24
|
+
--secondaryBGColor: #222;
|
|
25
|
+
--tertiaryBGColor: #333;
|
|
26
|
+
--primaryTextColor: var(--white);
|
|
27
|
+
--primaryCTAFill: #194880;
|
|
28
|
+
--primaryCTABorder: #c5d1df;
|
|
29
|
+
--secondaryCTAFill: #333;
|
|
30
|
+
--secondaryCTABorder: #999;
|
|
31
|
+
--secondaryCTAFillRGB: 51, 51, 51;
|
|
32
|
+
--primaryErrorCTAFill: #e51c26;
|
|
33
|
+
--primaryErrorCTABorder: #f8c6c8;
|
|
34
|
+
--primaryCTAFillRGB: 25, 72, 128;
|
|
35
|
+
--primaryErrorCTAFillRGB: 229, 28, 38;
|
|
36
|
+
--primaryDisableCTAFill: #767676;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
fieldset {
|
|
40
|
+
width: fit-content;
|
|
41
|
+
margin-top: 50px;
|
|
42
|
+
}
|
|
43
|
+
fieldset legend {
|
|
44
|
+
text-align: left;
|
|
45
|
+
font-size: 12px;
|
|
46
|
+
}
|
|
47
|
+
td {
|
|
48
|
+
color: white;
|
|
49
|
+
font-size: 14px;
|
|
50
|
+
border-radius: 5%;
|
|
51
|
+
display: table-cell;
|
|
52
|
+
text-align: initial;
|
|
53
|
+
margin: 5px 0;
|
|
54
|
+
width: max-content;
|
|
55
|
+
display: block;
|
|
56
|
+
}
|
|
57
|
+
label {
|
|
58
|
+
padding: 10px;
|
|
59
|
+
display: block;
|
|
60
|
+
cursor: pointer;
|
|
61
|
+
margin: 2px;
|
|
62
|
+
background: #4e545a;
|
|
63
|
+
border-radius: 10px;
|
|
64
|
+
}
|
|
65
|
+
input[type="radio"], input[type="checkbox"] {
|
|
66
|
+
margin-top: 0;
|
|
67
|
+
vertical-align: middle;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.initial td {
|
|
71
|
+
float: left;
|
|
72
|
+
margin: 2px;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
fieldset {
|
|
76
|
+
display: inline-grid;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.action-button button {
|
|
80
|
+
padding: 6px;
|
|
81
|
+
cursor: pointer;
|
|
82
|
+
border: 2px solid #194880;
|
|
83
|
+
border-radius: 10px;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
body.modal-manager-open {
|
|
87
|
+
overflow: hidden;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
modal-manager[mode='closed'] {
|
|
91
|
+
display: none;
|
|
92
|
+
}
|
|
93
|
+
</style>
|
|
94
|
+
</head>
|
|
95
|
+
|
|
96
|
+
<body>
|
|
97
|
+
<ia-book-actions></ia-book-actions>
|
|
98
|
+
|
|
99
|
+
<script type="module">
|
|
100
|
+
/* eslint-disable */
|
|
101
|
+
|
|
102
|
+
import './src/ia-book-actions.js';
|
|
103
|
+
import {
|
|
104
|
+
defaultLendingStatus,
|
|
105
|
+
canBrowseAndBorrow,
|
|
106
|
+
} from './src/assets/data/lending-get-status-service.js';
|
|
107
|
+
import { ModalConfig } from '@internetarchive/modal-manager';
|
|
108
|
+
import { LocalCache } from '@internetarchive/local-cache';
|
|
109
|
+
|
|
110
|
+
const localCache = new LocalCache({
|
|
111
|
+
namespace: 'loanRenew',
|
|
112
|
+
defaultTTL: 1 * 60, // 1 minute
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const userid = '@neeraj-archive';
|
|
116
|
+
const identifier = 'naturalhistoryof00unse_4111';
|
|
117
|
+
let bwbPurchaseUrl = 'https://www.betterworldbooks.com/product/detail/cambridge-ancient-hist-v04-0521044863';
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
const modalConfig = new ModalConfig();
|
|
121
|
+
modalConfig.headerColor = '#d9534f';
|
|
122
|
+
|
|
123
|
+
let lendingBarPostInit = function lendingBarPostInit() {
|
|
124
|
+
setTimeout(function() {}, 100);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// set defaultLendingStatus for unavailable (without borrowables) book
|
|
128
|
+
let iaBookActions = document.querySelector('ia-book-actions');
|
|
129
|
+
|
|
130
|
+
iaBookActions.userid = userid;
|
|
131
|
+
iaBookActions.identifier = identifier;
|
|
132
|
+
iaBookActions.bookTitle = 'Contemporary Black biography. Volume 39 : profiles from the interContemporary Black biography. Volume 39';
|
|
133
|
+
iaBookActions.lendingStatus = canBrowseAndBorrow;
|
|
134
|
+
iaBookActions.bwbPurchaseUrl = '';
|
|
135
|
+
iaBookActions.modalConfig = modalConfig;
|
|
136
|
+
iaBookActions.lendingBarPostInit = lendingBarPostInit;
|
|
137
|
+
iaBookActions.tokenDelay = 10; // 2 seconds
|
|
138
|
+
iaBookActions.timerExecutionSeconds = 3 // 4 seconds
|
|
139
|
+
iaBookActions.returnUrl = '';
|
|
140
|
+
iaBookActions.localCache = localCache;
|
|
141
|
+
|
|
142
|
+
let loanTotalTime2Min = { loanTotalTime: 120, loanRenewAtLast: 110, pageChangedInLast: 30 };
|
|
143
|
+
let loanTotalTime5Min = { loanTotalTime: 300, loanRenewAtLast: 240, pageChangedInLast: 60 };
|
|
144
|
+
let loanTotalTime10Min = { loanTotalTime: 600, loanRenewAtLast: 480, pageChangedInLast: 120 };
|
|
145
|
+
let loanTotalTime30Min = { loanTotalTime: 1800, loanRenewAtLast: 1500, pageChangedInLast: 600 };
|
|
146
|
+
let loanTotalTime1Hour = { loanTotalTime: 3600, loanRenewAtLast: 660, pageChangedInLast: 900 };
|
|
147
|
+
|
|
148
|
+
iaBookActions.loanRenewTimeConfig = loanTotalTime2Min;
|
|
149
|
+
let params = new URLSearchParams(document.location.search);
|
|
150
|
+
switch (params.get("timer")) {
|
|
151
|
+
case '5':
|
|
152
|
+
iaBookActions.loanRenewTimeConfig = loanTotalTime5Min;
|
|
153
|
+
break;
|
|
154
|
+
case '10':
|
|
155
|
+
iaBookActions.loanRenewTimeConfig = loanTotalTime10Min;
|
|
156
|
+
break;
|
|
157
|
+
case '30':
|
|
158
|
+
iaBookActions.loanRenewTimeConfig = loanTotalTime30Min;
|
|
159
|
+
break;
|
|
160
|
+
case '60':
|
|
161
|
+
iaBookActions.loanRenewTimeConfig = loanTotalTime1Hour;
|
|
162
|
+
break;
|
|
163
|
+
default:
|
|
164
|
+
iaBookActions.loanRenewTimeConfig = loanTotalTime2Min;
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
let lendingStatus1 = iaBookActions.lendingStatus;
|
|
169
|
+
let secondsLeftOnLoan = iaBookActions.loanRenewTimeConfig.loanTotalTime;
|
|
170
|
+
|
|
171
|
+
document.querySelectorAll('.titleBar input[type=checkbox]').forEach((item) => {
|
|
172
|
+
item.addEventListener('click', (e) => {
|
|
173
|
+
if (e.target.checked) {
|
|
174
|
+
iaBookActions.barType = 'title';
|
|
175
|
+
} else {
|
|
176
|
+
iaBookActions.barType = 'action';
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
document.querySelectorAll('.searchParam input[type=checkbox]').forEach((item) => {
|
|
182
|
+
item.addEventListener('click', (e) => {
|
|
183
|
+
var searchParams = new URLSearchParams(window.location.search);
|
|
184
|
+
var separate = '';
|
|
185
|
+
if (e.target.checked) {
|
|
186
|
+
separate = '?';
|
|
187
|
+
searchParams.set('q', 'test');
|
|
188
|
+
} else {
|
|
189
|
+
separate = '';
|
|
190
|
+
searchParams.delete('q');
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (history.pushState) {
|
|
194
|
+
var newurl = window.location.protocol + "//" + window.location.host + window.location.pathname + separate + searchParams;
|
|
195
|
+
window.history.pushState({path:newurl},'',newurl);
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
document.querySelectorAll('.errorEnable input[type=checkbox]').forEach((item) => {
|
|
201
|
+
if (window.location.href.indexOf('?error=true') !== -1) {
|
|
202
|
+
item.checked = true;
|
|
203
|
+
}
|
|
204
|
+
item.addEventListener('click', (e) => {
|
|
205
|
+
var searchParams = new URLSearchParams(window.location.search);
|
|
206
|
+
var separate = '';
|
|
207
|
+
if (e.target.checked) {
|
|
208
|
+
separate = '?';
|
|
209
|
+
searchParams.set('error', true);
|
|
210
|
+
} else {
|
|
211
|
+
separate = '';
|
|
212
|
+
searchParams.delete('error');
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (history.pushState) {
|
|
216
|
+
var newurl = window.location.protocol + "//" + window.location.host + window.location.pathname + separate + searchParams;
|
|
217
|
+
window.history.pushState({path:newurl},'',newurl);
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
document.querySelectorAll('.userState input[type=checkbox]').forEach((item) => {
|
|
224
|
+
item.addEventListener('click', (e) => {
|
|
225
|
+
if (e.target.checked) {
|
|
226
|
+
if (e.target.value === 'isAdmin') lendingStatus1.isAdmin = true;
|
|
227
|
+
if (e.target.value === 'isLoggedIn') iaBookActions.userid = '@neeraj';
|
|
228
|
+
} else {
|
|
229
|
+
if (e.target.value === 'isAdmin') lendingStatus1.isAdmin = false;
|
|
230
|
+
if (e.target.value === 'isLoggedIn') iaBookActions.userid = '';
|
|
231
|
+
}
|
|
232
|
+
let lending = {...defaultLendingStatus, ...lendingStatus1};
|
|
233
|
+
iaBookActions.lendingStatus = lending;
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
document.querySelectorAll('.printDisabled input[type=checkbox]').forEach((item) => {
|
|
238
|
+
item.addEventListener('click', (e) => {
|
|
239
|
+
if (e.target.checked) {
|
|
240
|
+
if (e.target.value === 'is_printdisabled') lendingStatus1.is_printdisabled = true;
|
|
241
|
+
if (e.target.value === 'user_is_printdisabled') lendingStatus1.user_is_printdisabled = true;
|
|
242
|
+
} else {
|
|
243
|
+
if (e.target.value === 'is_printdisabled') lendingStatus1.is_printdisabled = false;
|
|
244
|
+
if (e.target.value === 'user_is_printdisabled') lendingStatus1.user_is_printdisabled = false;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
let lending = {...defaultLendingStatus, ...lendingStatus1};
|
|
248
|
+
iaBookActions.lendingStatus = lending;
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
document.querySelectorAll('.availableToBrowse input[type=radio]').forEach((item) => {
|
|
253
|
+
item.addEventListener('click', async (e) => {
|
|
254
|
+
if (e.target.value === 'user_has_browsed') {
|
|
255
|
+
lendingStatus1.user_has_browsed = true;
|
|
256
|
+
lendingStatus1.available_to_browse = false;
|
|
257
|
+
lendingStatus1.secondsLeftOnLoan = secondsLeftOnLoan;
|
|
258
|
+
lendingStatus1.browsingExpired = false;
|
|
259
|
+
|
|
260
|
+
const expireDate = new Date(
|
|
261
|
+
new Date().getTime() + iaBookActions.loanRenewTimeConfig.loanTotalTime * 1000
|
|
262
|
+
);
|
|
263
|
+
try {
|
|
264
|
+
// set a value
|
|
265
|
+
await localCache.set({
|
|
266
|
+
key: `${identifier}-loanTime`,
|
|
267
|
+
value: expireDate,
|
|
268
|
+
ttl: Number(iaBookActions.loanRenewTimeConfig.loanTotalTime),
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
await localCache.delete(`${identifier}-pageChangedTime`);
|
|
272
|
+
} catch (error) {}
|
|
273
|
+
|
|
274
|
+
} else if(e.target.value === 'browsingExpired') {
|
|
275
|
+
lendingStatus1.user_has_browsed = true;
|
|
276
|
+
lendingStatus1.available_to_browse = false;
|
|
277
|
+
lendingStatus1.secondsLeftOnLoan = 0;
|
|
278
|
+
lendingStatus1.browsingExpired = true;
|
|
279
|
+
} else if (e.target.value === 'available_to_browse') {
|
|
280
|
+
lendingStatus1.available_to_browse = true;
|
|
281
|
+
lendingStatus1.user_has_browsed = false;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// wait for a while
|
|
285
|
+
setTimeout(() => {
|
|
286
|
+
let lending = {...defaultLendingStatus, ...lendingStatus1};
|
|
287
|
+
iaBookActions.lendingStatus = lending;
|
|
288
|
+
}, 10);
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
document.querySelector('#show_warning_modal').addEventListener('click', async () => {
|
|
293
|
+
if (!iaBookActions.lendingStatus.user_has_browsed) {
|
|
294
|
+
const newLendingStatus = { ...iaBookActions.lendingStatus, user_has_browsed: true };
|
|
295
|
+
iaBookActions.lendingStatus = newLendingStatus;
|
|
296
|
+
await iaBookActions.updateComplete;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
iaBookActions.showWarningModal();
|
|
300
|
+
|
|
301
|
+
if (iaBookActions.lendingStatus.user_has_browsed) {
|
|
302
|
+
iaBookActions.showWarningModal();
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
document.querySelector('#show_expired_modal').addEventListener('click', async () => {
|
|
307
|
+
if (!iaBookActions.lendingStatus.user_has_browsed) {
|
|
308
|
+
const newLendingStatus = { ...iaBookActions.lendingStatus, user_has_browsed: true };
|
|
309
|
+
iaBookActions.lendingStatus = newLendingStatus;
|
|
310
|
+
await iaBookActions.updateComplete;
|
|
311
|
+
}
|
|
312
|
+
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
313
|
+
|
|
314
|
+
iaBookActions.browseHasExpired();
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
document.querySelector('#resync_timer').addEventListener('click', async (e) => {
|
|
318
|
+
if (iaBookActions.lendingStatus.user_has_browsed) {
|
|
319
|
+
document.querySelector('ia-book-actions').dispatchEvent(
|
|
320
|
+
new Event('visibilitychange', {
|
|
321
|
+
detail: {},
|
|
322
|
+
bubbles: true,
|
|
323
|
+
composed: true,
|
|
324
|
+
})
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
document.querySelectorAll('.availableToBorrow input[type=radio]').forEach((item) => {
|
|
331
|
+
item.addEventListener('click', (e) => {
|
|
332
|
+
if (e.target.value === 'available_to_borrow') {
|
|
333
|
+
lendingStatus1.available_to_borrow = true;
|
|
334
|
+
lendingStatus1.user_on_waitlist = false;
|
|
335
|
+
lendingStatus1.available_to_waitlist = false;
|
|
336
|
+
lendingStatus1.user_has_borrowed = false;
|
|
337
|
+
} else if (e.target.value === 'user_can_claim_waitlist') {
|
|
338
|
+
lendingStatus1.available_to_borrow = true;
|
|
339
|
+
lendingStatus1.user_on_waitlist = true;
|
|
340
|
+
lendingStatus1.user_can_claim_waitlist = true;
|
|
341
|
+
lendingStatus1.user_has_borrowed = false;
|
|
342
|
+
lendingStatus1.available_to_waitlist = false;
|
|
343
|
+
} else if (e.target.value === 'user_on_waitlist') {
|
|
344
|
+
lendingStatus1.available_to_borrow = false;
|
|
345
|
+
lendingStatus1.user_on_waitlist = true;
|
|
346
|
+
lendingStatus1.available_to_waitlist = false;
|
|
347
|
+
lendingStatus1.user_has_borrowed = false;
|
|
348
|
+
} else if (e.target.value === 'available_to_waitlist') {
|
|
349
|
+
lendingStatus1.available_to_borrow = false;
|
|
350
|
+
lendingStatus1.user_on_waitlist = false;
|
|
351
|
+
lendingStatus1.available_to_waitlist = true;
|
|
352
|
+
lendingStatus1.user_has_borrowed = false;
|
|
353
|
+
} else if (e.target.value === 'user_has_borrowed') {
|
|
354
|
+
lendingStatus1.available_to_borrow = false;
|
|
355
|
+
lendingStatus1.user_on_waitlist = false;
|
|
356
|
+
lendingStatus1.available_to_waitlist = false;
|
|
357
|
+
lendingStatus1.user_has_borrowed = true;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
let lending = {...defaultLendingStatus, ...lendingStatus1};
|
|
361
|
+
iaBookActions.lendingStatus = lending;
|
|
362
|
+
});
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
document.querySelectorAll('.purchase input[type=checkbox]').forEach((item) => {
|
|
366
|
+
item.addEventListener('click', (e) => {
|
|
367
|
+
if (e.target.checked) {
|
|
368
|
+
bwbPurchaseUrl = 'https://www.google.com';
|
|
369
|
+
} else {
|
|
370
|
+
bwbPurchaseUrl = ''
|
|
371
|
+
}
|
|
372
|
+
iaBookActions.bwbPurchaseUrl = bwbPurchaseUrl;
|
|
373
|
+
})
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
document.querySelector('.pageChangedEvent').addEventListener('click', () => {
|
|
377
|
+
// dispatch event to say book page has flipped
|
|
378
|
+
document.querySelector('ia-book-actions').dispatchEvent(
|
|
379
|
+
new CustomEvent('BookReader:userAction', {
|
|
380
|
+
detail: {},
|
|
381
|
+
bubbles: true,
|
|
382
|
+
composed: true,
|
|
383
|
+
})
|
|
384
|
+
);
|
|
385
|
+
})
|
|
386
|
+
|
|
387
|
+
// listen for IABookReader:BrowsingHasExpired
|
|
388
|
+
window.addEventListener('IABookReader:BrowsingHasExpired', () => {
|
|
389
|
+
console.log('IABookReader:BrowsingHasExpired EVENT FIRED');
|
|
390
|
+
});
|
|
391
|
+
</script>
|
|
392
|
+
|
|
393
|
+
<div>
|
|
394
|
+
<fieldset>
|
|
395
|
+
<legend>Dev testing for book title bar</legend>
|
|
396
|
+
<table class="initial">
|
|
397
|
+
<tr>
|
|
398
|
+
<td class="titleBar">
|
|
399
|
+
<label><input type="checkbox" name="title" value="title" />Title Bar</label>
|
|
400
|
+
</td>
|
|
401
|
+
<td class="searchParam">
|
|
402
|
+
<label><input type="checkbox" name="search" value="Search Param" />Search Param</label>
|
|
403
|
+
</td>
|
|
404
|
+
<td>
|
|
405
|
+
<label class="ia-button"><button class="pageChangedEvent">Page Change Event</button></label>
|
|
406
|
+
</td>
|
|
407
|
+
</tr>
|
|
408
|
+
</table>
|
|
409
|
+
</fieldset>
|
|
410
|
+
|
|
411
|
+
<fieldset>
|
|
412
|
+
<legend>Dev testing for book action bar</legend>
|
|
413
|
+
<table class="initial">
|
|
414
|
+
<tr colspan='5'>
|
|
415
|
+
<td class="errorEnable"><label><input type="checkbox" name="errorEnable" value="errorEnable" />Do you want to see error response?</label></td>
|
|
416
|
+
</tr>
|
|
417
|
+
<tr>
|
|
418
|
+
<td class="userState">
|
|
419
|
+
<label><input type="checkbox" name="user" value="isAdmin" />isAdmin</label>
|
|
420
|
+
<label><input type="checkbox" name="user" value="isLoggedIn" />isLoggedIn</label>
|
|
421
|
+
</td>
|
|
422
|
+
<td class="printDisabled">
|
|
423
|
+
<label><input type="checkbox" name="is_printdisabled" value="is_printdisabled" />is_printdisabled</label>
|
|
424
|
+
<label><input type="checkbox" name="user_is_printdisabled" value="user_is_printdisabled" />user_is_printdisabled</label>
|
|
425
|
+
</td>
|
|
426
|
+
<td class="availableToBrowse">
|
|
427
|
+
<label><input type="radio" name="browse" value="available_to_browse" />available_to_browse</label>
|
|
428
|
+
<label><input type="radio" name="browse" value="user_has_browsed" />user_has_browsed</label>
|
|
429
|
+
<label><input type="radio" name="browse" value="browsingExpired" />browsingExpired</label>
|
|
430
|
+
</td>
|
|
431
|
+
<td class="availableToBorrow">
|
|
432
|
+
<label><input type="radio" name="borrow" value="available_to_borrow" />available_to_borrow</label>
|
|
433
|
+
<label><input type="radio" name="borrow" value="user_on_waitlist" />user_on_waitlist</label>
|
|
434
|
+
<label><input type="radio" name="borrow" value="user_can_claim_waitlist" />user_can_claim_waitlist</label>
|
|
435
|
+
<label><input type="radio" name="borrow" value="available_to_waitlist" />available_to_waitlist</label>
|
|
436
|
+
<label><input type="radio" name="borrow" value="user_has_borrowed" />user_has_borrowed</label>
|
|
437
|
+
</td>
|
|
438
|
+
<td class="purchase">
|
|
439
|
+
<label><input type="checkbox" name="purchase" value="purchase" />purchase</label>
|
|
440
|
+
</td>
|
|
441
|
+
|
|
442
|
+
<td class="reflect">
|
|
443
|
+
<input type="button" style="padding: 10px;
|
|
444
|
+
background: #194880;
|
|
445
|
+
color: white;
|
|
446
|
+
font-weight: bold; display: none" name="purchase" value="Reflect" />
|
|
447
|
+
</td>
|
|
448
|
+
</tr>
|
|
449
|
+
<tr>
|
|
450
|
+
<td>
|
|
451
|
+
<fieldset style="margin-top: 10px;">
|
|
452
|
+
<legend style="color: black;">controls for browsed book</legend>
|
|
453
|
+
<table aria-hidden="true">
|
|
454
|
+
<tbody><tr>
|
|
455
|
+
<td class="action-button">
|
|
456
|
+
<button id="show_warning_modal">When Browsing (`user_has_browsed`),<br> show Warning Modal</button>
|
|
457
|
+
</td>
|
|
458
|
+
<td class="action-button">
|
|
459
|
+
<button id="show_expired_modal">While Browsing (`user_has_browsed`),<br> Expire book & show its modal</button>
|
|
460
|
+
</td>
|
|
461
|
+
<td class="action-button">
|
|
462
|
+
<button id="resync_timer">While Browsing (`user_has_browsed`),<br> resync timer</button>
|
|
463
|
+
</td>
|
|
464
|
+
</tr>
|
|
465
|
+
</tbody></table>
|
|
466
|
+
</fieldset>
|
|
467
|
+
</td>
|
|
468
|
+
</tr>
|
|
469
|
+
</table>
|
|
470
|
+
</fieldset>
|
|
471
|
+
</div>
|
|
472
|
+
<modal-manager></modal-manager>
|
|
473
|
+
</body>
|
|
474
|
+
</html>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@internetarchive/ia-book-actions",
|
|
3
|
-
"version": "1.1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Bookreader actions for borrowable book",
|
|
5
5
|
"author": "Internet Archive",
|
|
6
6
|
"license": "AGPL-3.0-only",
|
|
@@ -11,11 +11,16 @@
|
|
|
11
11
|
},
|
|
12
12
|
"scripts": {
|
|
13
13
|
"start": "web-dev-server",
|
|
14
|
-
"
|
|
15
|
-
"
|
|
14
|
+
"prepare:ghpages": "rimraf ghpages && yarn run prepare && vite build",
|
|
15
|
+
"prepare": "rimraf dist && husky install",
|
|
16
|
+
"lint": "eslint --ext \"./src/*.js\",.html . --ignore-path .gitignore && prettier \"./src/*.js\" --check --ignore-path .gitignore",
|
|
17
|
+
"format": "eslint --ext \"./src/*.js\",.html . --fix --ignore-path .gitignore && prettier \"./src/*.js\" --write --ignore-path .gitignore",
|
|
16
18
|
"test": "yarn run lint && yarn test:fast",
|
|
17
|
-
"test:fast": "web-test-runner --coverage",
|
|
18
|
-
"test:watch": "web-test-runner --watch"
|
|
19
|
+
"test:fast": "web-test-runner --coverage --playwright --browsers chromium firefox webkit",
|
|
20
|
+
"test:watch": "web-test-runner --watch --playwright --browsers chromium firefox webkit",
|
|
21
|
+
"deploy": "yarn run deploy:run -e $(git branch --show-current)",
|
|
22
|
+
"deploy:run": "yarn run prepare:ghpages && touch ghpages/.nojekyll && yarn run deploy:gh",
|
|
23
|
+
"deploy:gh": "gh-pages -t -d ghpages -m \"Build for $(git log --pretty=format:\"%h %an %ai %s\" -n1) [skip ci]\""
|
|
19
24
|
},
|
|
20
25
|
"dependencies": {
|
|
21
26
|
"@internetarchive/icon-info": "^1.2.6",
|
|
@@ -38,10 +43,13 @@
|
|
|
38
43
|
"eslint-plugin-lit": "^1.3.0",
|
|
39
44
|
"eslint-plugin-lit-a11y": "^1.0.1",
|
|
40
45
|
"eslint-plugin-wc": "^1.3.0",
|
|
41
|
-
"
|
|
46
|
+
"gh-pages": "^4.0.0",
|
|
47
|
+
"husky": "^7.0.0",
|
|
42
48
|
"lint-staged": "^10.0.0",
|
|
43
49
|
"prettier": "^2.0.4",
|
|
44
|
-
"sinon": "^13.0.2"
|
|
50
|
+
"sinon": "^13.0.2",
|
|
51
|
+
"vite": "^2.9.9",
|
|
52
|
+
"rimraf": "^3.0.2"
|
|
45
53
|
},
|
|
46
54
|
"eslintConfig": {
|
|
47
55
|
"extends": [
|
|
@@ -118,7 +118,7 @@ export const canBrowseAndBorrow = {
|
|
|
118
118
|
loanStartDate: '2021-07-30 09:57:40',
|
|
119
119
|
loansUrl: '/details/@neeraj-archive?tab=loans#loans-on-loan',
|
|
120
120
|
maxLoans: 10,
|
|
121
|
-
secondsLeftOnLoan:
|
|
121
|
+
secondsLeftOnLoan: 0,
|
|
122
122
|
shouldProtectImages: true,
|
|
123
123
|
totalWaitlistCount: 0,
|
|
124
124
|
userHasBorrowed: false,
|
|
@@ -47,7 +47,7 @@ export default class TimerCountdown extends LitElement {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
render() {
|
|
50
|
-
const viewClass = this.displayTime ? 'view' : '
|
|
50
|
+
const viewClass = this.displayTime ? 'view' : 'hide';
|
|
51
51
|
return html`
|
|
52
52
|
<button
|
|
53
53
|
id="timer-counter"
|
|
@@ -57,7 +57,8 @@ export default class TimerCountdown extends LitElement {
|
|
|
57
57
|
}}
|
|
58
58
|
role="timer"
|
|
59
59
|
>
|
|
60
|
-
<span>${this.minutesLeftOnLoan}</span>
|
|
60
|
+
<span>${this.minutesLeftOnLoan} - </span>
|
|
61
|
+
<span class="second">${Number(this.secondsLeftOnLoan)}</span>
|
|
61
62
|
<span class="sr-only">${this.remainingTime} left</span>
|
|
62
63
|
</button>
|
|
63
64
|
`;
|
|
@@ -32,7 +32,12 @@ export default async function ActionsHandlerService(options) {
|
|
|
32
32
|
location?.href?.indexOf('?error=true') !== -1 &&
|
|
33
33
|
location?.hostname !== 'archive.org';
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
const testHostname = ['localhost', 'internetarchive.github.io'];
|
|
36
|
+
let isTest = false;
|
|
37
|
+
if (testHostname.includes(location.hostname)) {
|
|
38
|
+
isTest = true;
|
|
39
|
+
baseHost = location.href;
|
|
40
|
+
}
|
|
36
41
|
|
|
37
42
|
let formData = new FormData();
|
|
38
43
|
formData.append('action', option.action);
|
|
@@ -51,8 +56,8 @@ export default async function ActionsHandlerService(options) {
|
|
|
51
56
|
};
|
|
52
57
|
}
|
|
53
58
|
|
|
54
|
-
// return success response for
|
|
55
|
-
if (
|
|
59
|
+
// return success response for localhost server...
|
|
60
|
+
if (isTest) {
|
|
56
61
|
if (
|
|
57
62
|
option?.action == 'renew_loan' ||
|
|
58
63
|
option?.action == 'return_loan'
|
package/src/core/services/log.js
CHANGED
|
@@ -8,7 +8,8 @@ const log =
|
|
|
8
8
|
location.host.match(/^(www|cat)-[a-z0-9]+\.archive\.org$/) ||
|
|
9
9
|
location.host.match(/\.code\.archive\.org$/) ||
|
|
10
10
|
location.host.match(/\.dev\.archive\.org$/) ||
|
|
11
|
-
location.host.match(/^ia-petabox-/)
|
|
11
|
+
location.host.match(/^ia-petabox-/) ||
|
|
12
|
+
location.host.match(/^internetarchive/)
|
|
12
13
|
? // eslint-disable-next-line no-console
|
|
13
14
|
console.log.bind(console) // convenient, no? Stateless function
|
|
14
15
|
: () => {};
|
package/src/ia-book-actions.js
CHANGED
|
@@ -4,7 +4,6 @@ import { html, css, LitElement, nothing } from 'lit';
|
|
|
4
4
|
|
|
5
5
|
import { SharedResizeObserver } from '@internetarchive/shared-resize-observer';
|
|
6
6
|
import { ModalConfig } from '@internetarchive/modal-manager';
|
|
7
|
-
// import { ToastConfig } from '@internetarchive/toast-manager';
|
|
8
7
|
import { LocalCache } from '@internetarchive/local-cache';
|
|
9
8
|
|
|
10
9
|
import './components/collapsible-action-group.js';
|
|
@@ -60,6 +59,7 @@ export default class IABookActions extends LitElement {
|
|
|
60
59
|
disableActionGroup: { type: Boolean },
|
|
61
60
|
modal: { Object },
|
|
62
61
|
tokenDelay: { type: Number },
|
|
62
|
+
timerExecutionSeconds: { type: Number },
|
|
63
63
|
localCache: { type: Object },
|
|
64
64
|
loanRenewTimeConfig: { type: Object },
|
|
65
65
|
loanRenewResult: { type: Object },
|
|
@@ -80,6 +80,7 @@ export default class IABookActions extends LitElement {
|
|
|
80
80
|
this.sharedObserver = undefined;
|
|
81
81
|
this.disableActionGroup = false;
|
|
82
82
|
this.tokenDelay = 120; // in seconds
|
|
83
|
+
this.timerExecutionSeconds = 30;
|
|
83
84
|
|
|
84
85
|
// private props
|
|
85
86
|
this.postInitComplete = false;
|
|
@@ -90,10 +91,7 @@ export default class IABookActions extends LitElement {
|
|
|
90
91
|
this.lendingOptions = {};
|
|
91
92
|
this.borrowType = null; // 'browsed'|'borrowed'
|
|
92
93
|
this.browseTimer = undefined; // timeout
|
|
93
|
-
this.
|
|
94
|
-
|
|
95
|
-
/** @deprecated */
|
|
96
|
-
this.toast = undefined;
|
|
94
|
+
this.timeWhenTimerStart = undefined;
|
|
97
95
|
|
|
98
96
|
/**
|
|
99
97
|
* when user click on [return the book] button on warning modal
|
|
@@ -118,7 +116,7 @@ export default class IABookActions extends LitElement {
|
|
|
118
116
|
* contains one hour auto-loan-renew response
|
|
119
117
|
*
|
|
120
118
|
* @type {object} loanRenewResult
|
|
121
|
-
* @property {string} texts - texts messages
|
|
119
|
+
* @property {string} texts - texts messages shows in modal
|
|
122
120
|
* @property {boolean} renewNow - key to determine if need to renew now
|
|
123
121
|
* @property {number} secondsLeft - seconds left in active loan
|
|
124
122
|
*/
|
|
@@ -129,10 +127,19 @@ export default class IABookActions extends LitElement {
|
|
|
129
127
|
// clear all intervals for lending system
|
|
130
128
|
window?.IALendingIntervals?.clearAll();
|
|
131
129
|
|
|
132
|
-
|
|
130
|
+
this.sentryCaptureMsg(sentryLogs.disconnectedCallback);
|
|
133
131
|
this.disconnectResizeObserver();
|
|
134
132
|
}
|
|
135
133
|
|
|
134
|
+
/**
|
|
135
|
+
* send log messages to sentry
|
|
136
|
+
* @param {string} msg
|
|
137
|
+
*/
|
|
138
|
+
sentryCaptureMsg(msg) {
|
|
139
|
+
log(window?.Sentry);
|
|
140
|
+
window?.Sentry?.captureMessage(msg);
|
|
141
|
+
}
|
|
142
|
+
|
|
136
143
|
firstUpdated() {
|
|
137
144
|
// bind auto loan renew events
|
|
138
145
|
this.bindLoanRenewEvents();
|
|
@@ -224,7 +231,7 @@ export default class IABookActions extends LitElement {
|
|
|
224
231
|
log('setupLendingToolbarActions > hasExpired --- ');
|
|
225
232
|
|
|
226
233
|
if (!this.tokenPoller) {
|
|
227
|
-
|
|
234
|
+
this.sentryCaptureMsg(sentryLogs.bookWasExpired);
|
|
228
235
|
}
|
|
229
236
|
window?.IALendingIntervals?.clearAll();
|
|
230
237
|
|
|
@@ -286,12 +293,36 @@ export default class IABookActions extends LitElement {
|
|
|
286
293
|
|
|
287
294
|
/**
|
|
288
295
|
* auto-renew interval may stale when tab is in background,
|
|
289
|
-
* so
|
|
296
|
+
* so when user open that tab again,
|
|
297
|
+
* the [visibilitychange] event can trigger timer to show relavent messages
|
|
290
298
|
*/
|
|
291
|
-
document.addEventListener(
|
|
292
|
-
log('visibilitychange');
|
|
299
|
+
document.addEventListener('visibilitychange', async () => {
|
|
293
300
|
if (!document.hidden) {
|
|
294
|
-
|
|
301
|
+
log(
|
|
302
|
+
'visibilitychange event execute:------------------ ',
|
|
303
|
+
new Date().getMinutes(),
|
|
304
|
+
new Date().getSeconds(),
|
|
305
|
+
this.borrowType
|
|
306
|
+
);
|
|
307
|
+
|
|
308
|
+
if (
|
|
309
|
+
this.borrowType === 'browsed' &&
|
|
310
|
+
this.lendingStatus.browsingExpired === false
|
|
311
|
+
) {
|
|
312
|
+
const loanTime = await this.localCache.get(
|
|
313
|
+
`${this.identifier}-loanTime`
|
|
314
|
+
);
|
|
315
|
+
|
|
316
|
+
// number of seconds left in current loan
|
|
317
|
+
const secondsLeft = Math.round((loanTime - new Date()) / 1000);
|
|
318
|
+
|
|
319
|
+
if (secondsLeft >= this.timerExecutionSeconds) {
|
|
320
|
+
this.loanStatusCheckInterval(Number(secondsLeft));
|
|
321
|
+
} else {
|
|
322
|
+
this.browseHasExpired();
|
|
323
|
+
this.disconnectedCallback();
|
|
324
|
+
}
|
|
325
|
+
}
|
|
295
326
|
}
|
|
296
327
|
});
|
|
297
328
|
}
|
|
@@ -473,61 +504,6 @@ export default class IABookActions extends LitElement {
|
|
|
473
504
|
await this.modal?.showModal({ config, customModalContent });
|
|
474
505
|
}
|
|
475
506
|
|
|
476
|
-
/**
|
|
477
|
-
* @deprecated
|
|
478
|
-
* create / select the toast-template component on DOM
|
|
479
|
-
*
|
|
480
|
-
* @memberof IABookActions
|
|
481
|
-
*/
|
|
482
|
-
async useToastManager() {
|
|
483
|
-
if (!this.toast) {
|
|
484
|
-
this.toast = this.shadowRoot.querySelector('toast-template');
|
|
485
|
-
|
|
486
|
-
if (!this.toast) this.toast = document.createElement('toast-template');
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
await this.shadowRoot.appendChild(this.toast);
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
/**
|
|
493
|
-
* @deprecated
|
|
494
|
-
* close/hide toast message
|
|
495
|
-
*/
|
|
496
|
-
async closeToastManager() {
|
|
497
|
-
this.toast = this.shadowRoot.querySelector('toast-template');
|
|
498
|
-
this.toast?.remove();
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
/**
|
|
502
|
-
* @deprecated
|
|
503
|
-
* Show toast messages on some specific loan renew features. e.g.
|
|
504
|
-
* - show message when book is auto renewed
|
|
505
|
-
* - show message when book is expired
|
|
506
|
-
* - show message having remaining time when book is about to auto returned
|
|
507
|
-
*/
|
|
508
|
-
// async showToastMessage() {
|
|
509
|
-
// await this.useToastManager();
|
|
510
|
-
|
|
511
|
-
// // if secondsLeft < 60, consider it 1 minute
|
|
512
|
-
// let { secondsLeft } = this.loanRenewResult;
|
|
513
|
-
// if (secondsLeft === undefined) {
|
|
514
|
-
// secondsLeft = this.lendingStatus.secondsLeftOnLoan;
|
|
515
|
-
// } else {
|
|
516
|
-
// secondsLeft = secondsLeft > 60 ? secondsLeft : 60;
|
|
517
|
-
// }
|
|
518
|
-
|
|
519
|
-
// const config = new ToastConfig();
|
|
520
|
-
// config.dismisOnClick = true;
|
|
521
|
-
// config.texts = this.loanRenewHelper?.getMessageTexts(
|
|
522
|
-
// this.loanRenewResult.texts,
|
|
523
|
-
// secondsLeft
|
|
524
|
-
// );
|
|
525
|
-
|
|
526
|
-
// this.toast?.showToast({
|
|
527
|
-
// config,
|
|
528
|
-
// });
|
|
529
|
-
// }
|
|
530
|
-
|
|
531
507
|
/**
|
|
532
508
|
* Execute when loan is expired
|
|
533
509
|
*/
|
|
@@ -553,7 +529,7 @@ export default class IABookActions extends LitElement {
|
|
|
553
529
|
|
|
554
530
|
await this.showExpiredModal();
|
|
555
531
|
|
|
556
|
-
|
|
532
|
+
this.sentryCaptureMsg(sentryLogs.browseHasExpired);
|
|
557
533
|
}
|
|
558
534
|
|
|
559
535
|
async startBrowseTimer() {
|
|
@@ -656,7 +632,10 @@ export default class IABookActions extends LitElement {
|
|
|
656
632
|
if (this.loanRenewResult.renewNow) {
|
|
657
633
|
// Now, let's reset loan duration & this.lendingStatus
|
|
658
634
|
const loanTime = await this.localCache.get(`${this.identifier}-loanTime`);
|
|
659
|
-
|
|
635
|
+
|
|
636
|
+
// number of seconds left in current loan
|
|
637
|
+
const secondsLeft = Math.round((loanTime - new Date()) / 1000);
|
|
638
|
+
log(loanTime, secondsLeft);
|
|
660
639
|
|
|
661
640
|
log('IABookActions: handleLoanAutoRenewed --- ', {
|
|
662
641
|
ajaxResponse: detail?.data,
|
|
@@ -676,7 +655,7 @@ export default class IABookActions extends LitElement {
|
|
|
676
655
|
this.modal?.closeModal();
|
|
677
656
|
this.modal.removeAttribute('id');
|
|
678
657
|
this.modal.customModalContent = nothing;
|
|
679
|
-
|
|
658
|
+
this.sentryCaptureMsg(sentryLogs.bookHasRenewed);
|
|
680
659
|
}
|
|
681
660
|
}
|
|
682
661
|
|
|
@@ -685,59 +664,80 @@ export default class IABookActions extends LitElement {
|
|
|
685
664
|
*
|
|
686
665
|
* @memberof IABookActions
|
|
687
666
|
*/
|
|
688
|
-
startTimerCountdown() {
|
|
667
|
+
async startTimerCountdown() {
|
|
689
668
|
window?.IALendingIntervals?.clearTimerCountdown();
|
|
690
669
|
|
|
691
|
-
|
|
670
|
+
const secondsLeft = Number(this.lendingStatus.secondsLeftOnLoan);
|
|
692
671
|
this.timeWhenTimerStart = new Date();
|
|
693
672
|
|
|
694
673
|
window.IALendingIntervals.timerCountdown = setInterval(async () => {
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
// side effect: updates this.lendingStatus & kicks off lifecycle,
|
|
700
|
-
// if really updated, escape from here
|
|
701
|
-
const resyncd = this.reSyncTimerIfGoneOff(secondsLeft);
|
|
702
|
-
|
|
703
|
-
if (resyncd.hasSynced) {
|
|
704
|
-
secondsLeft = resyncd.whatShouldLeft;
|
|
674
|
+
// interval execution block
|
|
675
|
+
await this.loanStatusCheckInterval(secondsLeft);
|
|
676
|
+
}, this.timerExecutionSeconds * 1000);
|
|
677
|
+
}
|
|
705
678
|
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
679
|
+
/**
|
|
680
|
+
* setInterval execution function do following things
|
|
681
|
+
* - if setInterval timer gone off, let's resync
|
|
682
|
+
* - loan-renew attempt to renew the loan OR show relavant message
|
|
683
|
+
* - if loan has expired, clear interval timers
|
|
684
|
+
*
|
|
685
|
+
* @param {Number} secondsLeftOnLoan
|
|
686
|
+
*/
|
|
687
|
+
async loanStatusCheckInterval(secondsLeftOnLoan) {
|
|
688
|
+
let secondsLeft = secondsLeftOnLoan;
|
|
689
|
+
secondsLeft -= this.timerExecutionSeconds;
|
|
690
|
+
secondsLeft = Math.round(secondsLeft); // round number
|
|
691
|
+
|
|
692
|
+
// re-sync timer if gone off because of background window
|
|
693
|
+
// side effect: updates this.lendingStatus & kicks off lifecycle,
|
|
694
|
+
// if really updated, escape from here
|
|
695
|
+
const resyncd = await this.reSyncTimerIfGoneOff(secondsLeft);
|
|
696
|
+
|
|
697
|
+
if (resyncd.hasSynced) {
|
|
698
|
+
secondsLeft = resyncd.whatShouldLeft;
|
|
699
|
+
log('startTimerCountdown --- stale, timer has resyncd', {
|
|
700
|
+
secondsLeft,
|
|
701
|
+
});
|
|
702
|
+
}
|
|
710
703
|
|
|
711
|
-
|
|
704
|
+
log(
|
|
705
|
+
'startTimerCountdown --- countdown still valid. continue...',
|
|
706
|
+
{
|
|
712
707
|
resyncd,
|
|
713
708
|
secondsLeft,
|
|
714
709
|
loanRenewAtLast: this.loanRenewTimeConfig.loanRenewAtLast,
|
|
715
|
-
},
|
|
710
|
+
},
|
|
711
|
+
'time: ',
|
|
712
|
+
new Date().getMinutes(),
|
|
713
|
+
':',
|
|
714
|
+
new Date().getSeconds(),
|
|
715
|
+
', timerDelay: ',
|
|
716
|
+
this.timerExecutionSeconds
|
|
717
|
+
);
|
|
716
718
|
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
719
|
+
/**
|
|
720
|
+
* execute from last 10th minutes to 0th minute
|
|
721
|
+
* - 10th - to check if user has viewed
|
|
722
|
+
* - till 0th - to show warning msg with remaining time to auto expired
|
|
723
|
+
* @see IABookActions::bindLoanRenewEvents
|
|
724
|
+
*/
|
|
725
|
+
if (secondsLeft <= this.loanRenewTimeConfig.loanRenewAtLast) {
|
|
726
|
+
await this.loanRenewAttempt(secondsLeft);
|
|
727
|
+
}
|
|
726
728
|
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
}, this.timerExecutionSeconds * 1000);
|
|
729
|
+
// clear interval in secondsLeft if less
|
|
730
|
+
if (secondsLeft <= this.timerExecutionSeconds) {
|
|
731
|
+
this.disconnectedCallback();
|
|
732
|
+
this.sentryCaptureMsg(sentryLogs.clearOneHourTimer);
|
|
733
|
+
}
|
|
733
734
|
}
|
|
734
735
|
|
|
735
736
|
/**
|
|
736
737
|
* helper function to determine if timer is not in sync properly
|
|
737
738
|
*
|
|
738
739
|
* @param {number} timerSecondsLeft - actual seconds left get from setInterval
|
|
739
|
-
*
|
|
740
|
-
* @memberof TimerCountdown
|
|
740
|
+
* @returns {Object} { hasSynced: [boolean], whatShouldLeft: [number] }
|
|
741
741
|
*/
|
|
742
742
|
async reSyncTimerIfGoneOff(timerSecondsLeft) {
|
|
743
743
|
const currentTime = new Date();
|
|
@@ -749,6 +749,15 @@ export default class IABookActions extends LitElement {
|
|
|
749
749
|
const secondsShouldLeft =
|
|
750
750
|
this.lendingStatus.secondsLeftOnLoan - diffInSeconds;
|
|
751
751
|
|
|
752
|
+
log('currentTime: ', currentTime);
|
|
753
|
+
log('timeWhenTimerStart: ', this.timeWhenTimerStart);
|
|
754
|
+
log('diffInSeconds: ', diffInSeconds);
|
|
755
|
+
log('secondsShouldLeft: ', secondsShouldLeft);
|
|
756
|
+
log(
|
|
757
|
+
'this.lendingStatus.secondsLeftOnLoan: ',
|
|
758
|
+
this.lendingStatus.secondsLeftOnLoan
|
|
759
|
+
);
|
|
760
|
+
|
|
752
761
|
// convert in minutes
|
|
753
762
|
const whatIsleft = Math.round(timerSecondsLeft);
|
|
754
763
|
const whatShouldLeft = Math.round(secondsShouldLeft);
|
|
@@ -772,9 +781,9 @@ export default class IABookActions extends LitElement {
|
|
|
772
781
|
|
|
773
782
|
if (whatIsleft !== whatShouldLeft) {
|
|
774
783
|
log(`reSyncTimerIfGoneOff --- let's re-sync.`);
|
|
775
|
-
|
|
776
784
|
return { hasSynced: true, whatShouldLeft };
|
|
777
785
|
}
|
|
786
|
+
|
|
778
787
|
return { hasSynced: false, whatShouldLeft };
|
|
779
788
|
}
|
|
780
789
|
|
|
@@ -994,12 +1003,6 @@ export default class IABookActions extends LitElement {
|
|
|
994
1003
|
justify-content: center;
|
|
995
1004
|
flex-wrap: wrap;
|
|
996
1005
|
}
|
|
997
|
-
|
|
998
|
-
toast-template {
|
|
999
|
-
--toastTopMargin: 80px;
|
|
1000
|
-
--toastBGColor: #333;
|
|
1001
|
-
--toastFontColor: #fff;
|
|
1002
|
-
}
|
|
1003
1006
|
`;
|
|
1004
1007
|
}
|
|
1005
1008
|
}
|
|
@@ -13,7 +13,9 @@ const container = ({
|
|
|
13
13
|
.loanRenewAtLast=${loanRenewAtLast}
|
|
14
14
|
></timer-countdown>`;
|
|
15
15
|
|
|
16
|
-
describe('<timer-countdown>', () => {
|
|
16
|
+
describe('<timer-countdown>', async () => {
|
|
17
|
+
await aTimeout(5000);
|
|
18
|
+
|
|
17
19
|
it('timer interval is undefined when loan is expired', async () => {
|
|
18
20
|
const el = await fixture(
|
|
19
21
|
container({
|
|
@@ -36,7 +38,6 @@ describe('<timer-countdown>', () => {
|
|
|
36
38
|
})
|
|
37
39
|
);
|
|
38
40
|
|
|
39
|
-
await aTimeout(1900);
|
|
40
41
|
await el.updateComplete;
|
|
41
42
|
|
|
42
43
|
expect(el.secondsLeftOnLoan).to.equal(2);
|
|
@@ -376,7 +376,8 @@ describe('Browsing expired status', () => {
|
|
|
376
376
|
});
|
|
377
377
|
});
|
|
378
378
|
|
|
379
|
-
describe('Auto renew one hour loan', () => {
|
|
379
|
+
describe('Auto renew one hour loan', async () => {
|
|
380
|
+
await aTimeout(5000);
|
|
380
381
|
it('Book is browsing and renewed it now', async () => {
|
|
381
382
|
const loanTime = 88;
|
|
382
383
|
const el = await fixture(
|
|
@@ -449,6 +450,61 @@ describe('Auto renew one hour loan', () => {
|
|
|
449
450
|
});
|
|
450
451
|
});
|
|
451
452
|
|
|
453
|
+
describe('Visibility change API for document', async () => {
|
|
454
|
+
await aTimeout(5000);
|
|
455
|
+
|
|
456
|
+
beforeEach(() => {
|
|
457
|
+
Object.defineProperty(document, 'hidden', {
|
|
458
|
+
value: false,
|
|
459
|
+
configurable: true,
|
|
460
|
+
});
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
afterEach(() => {
|
|
464
|
+
Object.defineProperty(document, 'hidden', {
|
|
465
|
+
value: true,
|
|
466
|
+
configurable: true,
|
|
467
|
+
});
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
it('when book is not expired', async () => {
|
|
471
|
+
const el = await fixture(
|
|
472
|
+
container({
|
|
473
|
+
userid: '@user1',
|
|
474
|
+
identifier: 'foobar',
|
|
475
|
+
lendingStatus: {
|
|
476
|
+
user_has_browsed: true,
|
|
477
|
+
browsingExpired: false,
|
|
478
|
+
},
|
|
479
|
+
})
|
|
480
|
+
);
|
|
481
|
+
await el.updateComplete;
|
|
482
|
+
|
|
483
|
+
const spy = Sinon.spy(el, 'loanStatusCheckInterval');
|
|
484
|
+
document.dispatchEvent(new Event('visibilitychange'));
|
|
485
|
+
|
|
486
|
+
expect(spy.calledOnce).to.be.true;
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
it('when book is already expired', async () => {
|
|
490
|
+
const el = await fixture(
|
|
491
|
+
container({
|
|
492
|
+
userid: '@user1',
|
|
493
|
+
identifier: 'foobar',
|
|
494
|
+
lendingStatus: {
|
|
495
|
+
user_has_browsed: true,
|
|
496
|
+
},
|
|
497
|
+
})
|
|
498
|
+
);
|
|
499
|
+
await el.updateComplete;
|
|
500
|
+
|
|
501
|
+
const spy = Sinon.spy(el, 'browseHasExpired');
|
|
502
|
+
document.dispatchEvent(new Event('visibilitychange'));
|
|
503
|
+
|
|
504
|
+
expect(spy.calledOnce).to.be.true;
|
|
505
|
+
});
|
|
506
|
+
});
|
|
507
|
+
|
|
452
508
|
describe('Shared Resize Observer', () => {
|
|
453
509
|
it('can receive a Shared Resize Observer', async () => {
|
|
454
510
|
const sharedObserverStub = new SharedResizeObserver();
|
package/vite.config.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { defineConfig } from 'vite';
|
|
2
|
+
import { resolve } from 'path';
|
|
3
|
+
|
|
4
|
+
// https://vitejs.dev/config/
|
|
5
|
+
export default defineConfig({
|
|
6
|
+
base: '',
|
|
7
|
+
build: {
|
|
8
|
+
outDir: 'ghpages',
|
|
9
|
+
manifest: true,
|
|
10
|
+
rollupOptions: {
|
|
11
|
+
input: {
|
|
12
|
+
main: resolve(__dirname, 'index.html'),
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
server: {
|
|
17
|
+
host: true,
|
|
18
|
+
port: 8080,
|
|
19
|
+
open: true,
|
|
20
|
+
cors: true,
|
|
21
|
+
},
|
|
22
|
+
});
|
|
@@ -5,7 +5,7 @@ const hmr = process.argv.includes('--hmr');
|
|
|
5
5
|
|
|
6
6
|
export default /** @type {import('@web/dev-server').DevServerConfig} */ ({
|
|
7
7
|
nodeResolve: true,
|
|
8
|
-
open: '/
|
|
8
|
+
open: '/',
|
|
9
9
|
watch: !hmr,
|
|
10
10
|
|
|
11
11
|
/** Compile JS for older browsers. Requires @web/dev-server-esbuild plugin */
|