@internetarchive/ia-book-actions 0.0.6-6 → 0.0.6-7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/demo/index.html +11 -1
- package/package.json +2 -1
- package/src/components/collapsible-action-group.js +25 -0
- package/src/components/dialog-alert.js +139 -0
- package/src/core/services/actions-handler/actions-handler-service.js +6 -1
- package/src/core/services/actions-handler/actions-handler.js +46 -16
- package/src/ia-book-actions.js +49 -6
package/demo/index.html
CHANGED
|
@@ -71,6 +71,16 @@
|
|
|
71
71
|
float: left;
|
|
72
72
|
margin: 2px;
|
|
73
73
|
}
|
|
74
|
+
|
|
75
|
+
#ui-overlay {
|
|
76
|
+
position: fixed;
|
|
77
|
+
top: 0;
|
|
78
|
+
left: 0;
|
|
79
|
+
width: 100%;
|
|
80
|
+
height: 100%;
|
|
81
|
+
background: black;
|
|
82
|
+
opacity: 0.3;
|
|
83
|
+
}
|
|
74
84
|
</style>
|
|
75
85
|
</head>
|
|
76
86
|
|
|
@@ -146,7 +156,7 @@
|
|
|
146
156
|
if (e.target.value === 'user_has_browsed') {
|
|
147
157
|
lendingStatus1.user_has_browsed = true;
|
|
148
158
|
lendingStatus1.available_to_browse = false;
|
|
149
|
-
lendingStatus1.secondsLeftOnLoan =
|
|
159
|
+
lendingStatus1.secondsLeftOnLoan = 30;
|
|
150
160
|
lendingStatus1.browsingExpired = false;
|
|
151
161
|
} else if(e.target.value === 'browsingExpired') {
|
|
152
162
|
lendingStatus1.user_has_browsed = true;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@internetarchive/ia-book-actions",
|
|
3
|
-
"version": "0.0.6-
|
|
3
|
+
"version": "0.0.6-7",
|
|
4
4
|
"description": "Bookreader actions for borrowable book",
|
|
5
5
|
"author": "Internet Archive",
|
|
6
6
|
"license": "AGPL-3.0-only",
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"@internetarchive/ia-icons": "^1.2.2",
|
|
22
22
|
"@internetarchive/icon-bookmark": "^1.1.3",
|
|
23
23
|
"@internetarchive/icon-info": "^1.2.6",
|
|
24
|
+
"@internetarchive/icon-close": "^1.1.3",
|
|
24
25
|
"@internetarchive/shared-resize-observer": "^0.0.1",
|
|
25
26
|
"lit-element": "^2.4.0",
|
|
26
27
|
"lit-html": "^1.3.0"
|
|
@@ -27,6 +27,7 @@ export class CollapsibleActionGroup extends ActionsHandler {
|
|
|
27
27
|
hasAdminAccess: { type: Boolean },
|
|
28
28
|
dropdownArrow: { type: String },
|
|
29
29
|
disabled: { type: Boolean },
|
|
30
|
+
bookHasBrowsed: { type: Boolean },
|
|
30
31
|
};
|
|
31
32
|
}
|
|
32
33
|
|
|
@@ -45,6 +46,13 @@ export class CollapsibleActionGroup extends ActionsHandler {
|
|
|
45
46
|
this.title = '';
|
|
46
47
|
this.loaderIcon = 'https://archive.org/upload/images/tree/loading.gif';
|
|
47
48
|
this.disabled = false;
|
|
49
|
+
this.bookHasBrowsed = false;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
firstUpdated() {
|
|
53
|
+
if (this.bookHasBrowsed) {
|
|
54
|
+
this.emitFetchLoanToken();
|
|
55
|
+
}
|
|
48
56
|
}
|
|
49
57
|
|
|
50
58
|
updated(changed) {
|
|
@@ -53,6 +61,23 @@ export class CollapsibleActionGroup extends ActionsHandler {
|
|
|
53
61
|
this.resetActions();
|
|
54
62
|
}
|
|
55
63
|
}
|
|
64
|
+
|
|
65
|
+
// this is execute on demo page only
|
|
66
|
+
if (changed.has('bookHasBrowsed')) {
|
|
67
|
+
this.emitFetchLoanToken();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/* emit custom event to fetch loan token */
|
|
72
|
+
emitFetchLoanToken() {
|
|
73
|
+
this.dispatchEvent(
|
|
74
|
+
new CustomEvent('fetchLoanToken', {
|
|
75
|
+
detail: {
|
|
76
|
+
event: { category: 'action' },
|
|
77
|
+
bookHasBrowsed: this.bookHasBrowsed,
|
|
78
|
+
},
|
|
79
|
+
})
|
|
80
|
+
);
|
|
56
81
|
}
|
|
57
82
|
|
|
58
83
|
/**
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
import { LitElement, html, css } from 'lit-element';
|
|
3
|
+
import { classMap } from 'lit-html/directives/class-map';
|
|
4
|
+
|
|
5
|
+
import '@internetarchive/icon-close/icon-close.js';
|
|
6
|
+
import buttonBaseStyle from '../assets/styles/ia-button.js';
|
|
7
|
+
|
|
8
|
+
export class ShowDialog extends LitElement {
|
|
9
|
+
static get properties() {
|
|
10
|
+
return {
|
|
11
|
+
title: { type: String },
|
|
12
|
+
body: { type: String },
|
|
13
|
+
actions: { type: Array },
|
|
14
|
+
opened: { type: Boolean },
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
constructor() {
|
|
19
|
+
super();
|
|
20
|
+
this.title = 'Error';
|
|
21
|
+
this.body =
|
|
22
|
+
'Unexpected error: loan does not exist. Please try deleting your archive.org cookie.';
|
|
23
|
+
this.actions = [];
|
|
24
|
+
this.opened = false;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
updated(changed) {
|
|
28
|
+
if (changed.get('opened') === false) {
|
|
29
|
+
this.addOverlay();
|
|
30
|
+
} else {
|
|
31
|
+
this.removeOverlay();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
render() {
|
|
36
|
+
return html`
|
|
37
|
+
<div
|
|
38
|
+
class="${classMap({
|
|
39
|
+
dialog: true,
|
|
40
|
+
opened: this.opened,
|
|
41
|
+
closed: !this.opened,
|
|
42
|
+
})}"
|
|
43
|
+
>
|
|
44
|
+
<div class="dialog-head">
|
|
45
|
+
${this.title}
|
|
46
|
+
<ia-icon-close
|
|
47
|
+
@click="${() =>
|
|
48
|
+
this.dispatchEvent(new CustomEvent('dialogAlertClose'))}"
|
|
49
|
+
></ia-icon-close>
|
|
50
|
+
</div>
|
|
51
|
+
<div class="dialog-body">${this.body}</div>
|
|
52
|
+
<div class="dialog-foot">${this.renderActionButton}</div>
|
|
53
|
+
</div>
|
|
54
|
+
`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/* exceptional case to use dom element */
|
|
58
|
+
addOverlay() {
|
|
59
|
+
if (this.opened && !document.getElementById('ui-overlay')) {
|
|
60
|
+
const overlayElement = document.createElement('div');
|
|
61
|
+
overlayElement.id = 'ui-overlay';
|
|
62
|
+
document.getElementsByTagName('body')[0].appendChild(overlayElement);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/* exceptional case to use dom element */
|
|
67
|
+
removeOverlay() {
|
|
68
|
+
const overlayElement = document.getElementById('ui-overlay');
|
|
69
|
+
if (overlayElement) overlayElement.remove();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
get renderActionButton() {
|
|
73
|
+
if (!this.actions) return;
|
|
74
|
+
|
|
75
|
+
return this.actions.map(
|
|
76
|
+
action =>
|
|
77
|
+
html`<a class=${action.className} href=${action.href}>
|
|
78
|
+
${action.text}
|
|
79
|
+
</button>`
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
static get styles() {
|
|
84
|
+
const dialogStyle = css`
|
|
85
|
+
:host {
|
|
86
|
+
color: #343434;
|
|
87
|
+
font-size: 1.6rem;
|
|
88
|
+
}
|
|
89
|
+
.dialog {
|
|
90
|
+
position: absolute;
|
|
91
|
+
background: #fff;
|
|
92
|
+
border-radius: 1.2rem;
|
|
93
|
+
z-index: 3000;
|
|
94
|
+
flex-direction: column;
|
|
95
|
+
min-width: 300px;
|
|
96
|
+
max-width: 400px;
|
|
97
|
+
left: 50%;
|
|
98
|
+
transform: translateX(-50%);
|
|
99
|
+
|
|
100
|
+
border: 2px solid #ccc;
|
|
101
|
+
border-radius: 12px;
|
|
102
|
+
box-shadow: 0px 0px 27px 6px rgb(66 66 66 / 80%);
|
|
103
|
+
}
|
|
104
|
+
.dialog-head,
|
|
105
|
+
.dialog-body,
|
|
106
|
+
.dialog-foot {
|
|
107
|
+
padding: 1rem;
|
|
108
|
+
user-select: none;
|
|
109
|
+
text-align: left;
|
|
110
|
+
}
|
|
111
|
+
.dialog-body {
|
|
112
|
+
border: 1px solid #d8d8d8;
|
|
113
|
+
}
|
|
114
|
+
ia-icon-close {
|
|
115
|
+
position: absolute;
|
|
116
|
+
right: 0.5rem;
|
|
117
|
+
top: 0.5rem;
|
|
118
|
+
width: 2.5rem;
|
|
119
|
+
height: 2.5rem;
|
|
120
|
+
vertical-align: middle;
|
|
121
|
+
--iconFillColor: #333;
|
|
122
|
+
cursor: pointer;
|
|
123
|
+
}
|
|
124
|
+
.opened {
|
|
125
|
+
display: flex;
|
|
126
|
+
}
|
|
127
|
+
.closed {
|
|
128
|
+
display: none;
|
|
129
|
+
}
|
|
130
|
+
a {
|
|
131
|
+
text-decoration: none;
|
|
132
|
+
}
|
|
133
|
+
`;
|
|
134
|
+
|
|
135
|
+
return [buttonBaseStyle, dialogStyle];
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
customElements.define('show-dialog', ShowDialog);
|
|
@@ -34,6 +34,12 @@ export default function ActionsHandlerService(options) {
|
|
|
34
34
|
.then(response => {
|
|
35
35
|
// test changes, (won't affect you)
|
|
36
36
|
if (baseHost == '/demo/1' || baseHost == '/demo/') {
|
|
37
|
+
if (option.action == 'create_token') {
|
|
38
|
+
return {
|
|
39
|
+
error: 'loan token not found. please try again later.',
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
37
43
|
return {
|
|
38
44
|
error:
|
|
39
45
|
'This book is not available to borrow at this time. Please try again later.',
|
|
@@ -46,7 +52,6 @@ export default function ActionsHandlerService(options) {
|
|
|
46
52
|
})
|
|
47
53
|
.then(data => {
|
|
48
54
|
// `data` is the parsed version of the JSON returned from the above endpoint.
|
|
49
|
-
console.log('actual reponse for endpoint', data);
|
|
50
55
|
if (!data.error) {
|
|
51
56
|
option?.success(data);
|
|
52
57
|
} else {
|
|
@@ -15,6 +15,8 @@ export default class ActionsHandler extends LitElement {
|
|
|
15
15
|
super();
|
|
16
16
|
this.identifier = identifier;
|
|
17
17
|
this.ajaxTimeout = 6000;
|
|
18
|
+
this.loanTokenPollingDelay = 20000; // 20000 ms = 20 sec
|
|
19
|
+
this.loanTokenInterval = null;
|
|
18
20
|
this.bindEvents();
|
|
19
21
|
}
|
|
20
22
|
|
|
@@ -84,6 +86,22 @@ export default class ActionsHandler extends LitElement {
|
|
|
84
86
|
const { category, action } = detail.event;
|
|
85
87
|
this.sendEvent(category, action);
|
|
86
88
|
});
|
|
89
|
+
|
|
90
|
+
this.addEventListener('fetchLoanToken', ({ detail }) => {
|
|
91
|
+
const bookHasBrowsed = detail?.bookHasBrowsed;
|
|
92
|
+
|
|
93
|
+
// fetch loan token and set an interval
|
|
94
|
+
if (bookHasBrowsed) {
|
|
95
|
+
console.log('token poll started...');
|
|
96
|
+
|
|
97
|
+
this.loanTokenInterval = setInterval(() => {
|
|
98
|
+
this.handleLoanTokenPoller();
|
|
99
|
+
}, this.loanTokenPollingDelay);
|
|
100
|
+
} else {
|
|
101
|
+
// if book is not browsed, just clear token polling interval
|
|
102
|
+
clearInterval(this.loanTokenInterval);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
87
105
|
}
|
|
88
106
|
|
|
89
107
|
handleBrowseIt() {
|
|
@@ -97,7 +115,6 @@ export default class ActionsHandler extends LitElement {
|
|
|
97
115
|
this.handleReadItNow();
|
|
98
116
|
},
|
|
99
117
|
error: data => {
|
|
100
|
-
alert(data.error);
|
|
101
118
|
this.ActionError(context, data);
|
|
102
119
|
},
|
|
103
120
|
});
|
|
@@ -115,8 +132,7 @@ export default class ActionsHandler extends LitElement {
|
|
|
115
132
|
URLHelper.goToUrl(`/details/${this.identifier}`, true);
|
|
116
133
|
},
|
|
117
134
|
error: data => {
|
|
118
|
-
|
|
119
|
-
this.ActionError(context);
|
|
135
|
+
this.ActionError(context, data);
|
|
120
136
|
},
|
|
121
137
|
});
|
|
122
138
|
}
|
|
@@ -132,7 +148,6 @@ export default class ActionsHandler extends LitElement {
|
|
|
132
148
|
this.handleReadItNow();
|
|
133
149
|
},
|
|
134
150
|
error: data => {
|
|
135
|
-
alert(data.error);
|
|
136
151
|
this.ActionError(context, data);
|
|
137
152
|
},
|
|
138
153
|
});
|
|
@@ -149,8 +164,7 @@ export default class ActionsHandler extends LitElement {
|
|
|
149
164
|
URLHelper.goToUrl(URLHelper.getRedirectUrl(), true);
|
|
150
165
|
},
|
|
151
166
|
error: data => {
|
|
152
|
-
|
|
153
|
-
this.ActionError(context);
|
|
167
|
+
this.ActionError(context, data);
|
|
154
168
|
},
|
|
155
169
|
});
|
|
156
170
|
}
|
|
@@ -166,12 +180,36 @@ export default class ActionsHandler extends LitElement {
|
|
|
166
180
|
URLHelper.goToUrl(URLHelper.getRedirectUrl(), true);
|
|
167
181
|
},
|
|
168
182
|
error: data => {
|
|
169
|
-
|
|
170
|
-
|
|
183
|
+
this.ActionError(context, data);
|
|
184
|
+
},
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
handleLoanTokenPoller() {
|
|
189
|
+
const context = 'create_token';
|
|
190
|
+
this.ActionError(context);
|
|
191
|
+
|
|
192
|
+
ActionsHandlerService({
|
|
193
|
+
identifier: this.identifier,
|
|
194
|
+
action: context,
|
|
195
|
+
loader: false,
|
|
196
|
+
success: () => {
|
|
197
|
+
this.handleReadItNow();
|
|
198
|
+
},
|
|
199
|
+
error: data => {
|
|
200
|
+
this.ActionError(context, data);
|
|
171
201
|
},
|
|
172
202
|
});
|
|
173
203
|
}
|
|
174
204
|
|
|
205
|
+
ActionError(context, data = {}) {
|
|
206
|
+
this.dispatchEvent(
|
|
207
|
+
new CustomEvent('lendingActionError', {
|
|
208
|
+
detail: { context, data },
|
|
209
|
+
})
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
|
|
175
213
|
handleLoginOk() {
|
|
176
214
|
/* eslint class-methods-use-this: "off" */
|
|
177
215
|
const target = `/account/login?referer=${encodeURIComponent(
|
|
@@ -218,12 +256,4 @@ export default class ActionsHandler extends LitElement {
|
|
|
218
256
|
cookie += '; path=/; domain=.archive.org;';
|
|
219
257
|
document.cookie = cookie;
|
|
220
258
|
}
|
|
221
|
-
|
|
222
|
-
ActionError(context, data = {}) {
|
|
223
|
-
this.dispatchEvent(
|
|
224
|
-
new CustomEvent('lendingActionError', {
|
|
225
|
-
detail: { context, data },
|
|
226
|
-
})
|
|
227
|
-
);
|
|
228
|
-
}
|
|
229
259
|
}
|
package/src/ia-book-actions.js
CHANGED
|
@@ -7,6 +7,7 @@ import './components/collapsible-action-group.js';
|
|
|
7
7
|
import './components/book-title-bar.js';
|
|
8
8
|
import './components/text-group.js';
|
|
9
9
|
import './components/info-icon.js';
|
|
10
|
+
import './components/dialog-alert.js';
|
|
10
11
|
|
|
11
12
|
import GetLendingActions from './core/services/get-lending-actions.js';
|
|
12
13
|
import { mobileContainerWidth } from './core/config/constants.js';
|
|
@@ -23,6 +24,8 @@ export default class IABookActions extends LitElement {
|
|
|
23
24
|
barType: { type: String },
|
|
24
25
|
sharedObserver: { attribute: false },
|
|
25
26
|
disable: { type: Boolean },
|
|
27
|
+
dialogVisible: { type: Boolean },
|
|
28
|
+
bookHasBrowsed: { type: Boolean },
|
|
26
29
|
};
|
|
27
30
|
}
|
|
28
31
|
|
|
@@ -41,7 +44,9 @@ export default class IABookActions extends LitElement {
|
|
|
41
44
|
this.primaryColor = 'primary';
|
|
42
45
|
this.secondaryActions = [];
|
|
43
46
|
this.lendingOptions = {};
|
|
44
|
-
this.
|
|
47
|
+
this.disableActionGroup = false;
|
|
48
|
+
this.dialogVisible = false;
|
|
49
|
+
this.bookHasBrowsed = false;
|
|
45
50
|
}
|
|
46
51
|
|
|
47
52
|
disconnectedCallback() {
|
|
@@ -141,10 +146,12 @@ export default class IABookActions extends LitElement {
|
|
|
141
146
|
return action != null;
|
|
142
147
|
});
|
|
143
148
|
|
|
144
|
-
|
|
149
|
+
this.bookHasBrowsed =
|
|
145
150
|
this.lendingStatus.user_has_browsed &&
|
|
146
|
-
!this.lendingStatus.
|
|
147
|
-
) {
|
|
151
|
+
!this.lendingStatus.browsingExpired;
|
|
152
|
+
if (this.bookHasBrowsed) {
|
|
153
|
+
// start timer for 1-hour borrowed book.
|
|
154
|
+
// when 1 hour is completed, we shows browse-again button
|
|
148
155
|
this.startBrowseTimer();
|
|
149
156
|
}
|
|
150
157
|
}
|
|
@@ -154,9 +161,21 @@ export default class IABookActions extends LitElement {
|
|
|
154
161
|
<section class="lending-wrapper">
|
|
155
162
|
${this.barType === 'title' ? this.bookTitleBar : this.bookActionBar}
|
|
156
163
|
</section>
|
|
164
|
+
|
|
165
|
+
<show-dialog
|
|
166
|
+
.title=${this.dialogTitle}
|
|
167
|
+
.body=${this.dialogBody}
|
|
168
|
+
.actions=${this.dialogActions}
|
|
169
|
+
?opened="${this.dialogVisible}"
|
|
170
|
+
@dialogAlertClose="${() => this.closeDialogAlert()}"
|
|
171
|
+
></show-dialog>
|
|
157
172
|
`;
|
|
158
173
|
}
|
|
159
174
|
|
|
175
|
+
closeDialogAlert() {
|
|
176
|
+
this.dialogVisible = false;
|
|
177
|
+
}
|
|
178
|
+
|
|
160
179
|
get bookTitleBar() {
|
|
161
180
|
return html`<book-title-bar
|
|
162
181
|
.identifier=${this.identifier}
|
|
@@ -174,7 +193,8 @@ export default class IABookActions extends LitElement {
|
|
|
174
193
|
.secondaryActions=${this.secondaryActions}
|
|
175
194
|
.width=${this.width}
|
|
176
195
|
.hasAdminAccess=${this.hasAdminAccess}
|
|
177
|
-
.disabled=${this.
|
|
196
|
+
.disabled=${this.disableActionGroup}
|
|
197
|
+
.bookHasBrowsed=${this.bookHasBrowsed}
|
|
178
198
|
@lendingActionError=${this.handleLendingActionError}
|
|
179
199
|
>
|
|
180
200
|
</collapsible-action-group>
|
|
@@ -184,11 +204,16 @@ export default class IABookActions extends LitElement {
|
|
|
184
204
|
|
|
185
205
|
handleLendingActionError(e) {
|
|
186
206
|
// toggle activity loader
|
|
187
|
-
this.
|
|
207
|
+
this.disableActionGroup = !this.disableActionGroup;
|
|
188
208
|
|
|
189
209
|
const context = e?.detail?.context;
|
|
190
210
|
const errorMsg = e?.detail?.data?.error;
|
|
191
211
|
|
|
212
|
+
// early exist if you don't have errorMsg
|
|
213
|
+
if (!errorMsg) return;
|
|
214
|
+
|
|
215
|
+
this.initializeDialogAlert(context, errorMsg);
|
|
216
|
+
|
|
192
217
|
// update action bar state if book is not available to browse or borrow.
|
|
193
218
|
if (errorMsg && errorMsg.match(/not available to borrow/gm)) {
|
|
194
219
|
let currStatus = this.lendingStatus;
|
|
@@ -207,6 +232,24 @@ export default class IABookActions extends LitElement {
|
|
|
207
232
|
}
|
|
208
233
|
}
|
|
209
234
|
|
|
235
|
+
/* set props value to show dialog alert */
|
|
236
|
+
initializeDialogAlert(context, errorMsg) {
|
|
237
|
+
this.dialogVisible = true;
|
|
238
|
+
this.dialogTitle = 'Sorry!';
|
|
239
|
+
this.dialogBody = errorMsg;
|
|
240
|
+
this.dialogActions = [];
|
|
241
|
+
|
|
242
|
+
if (context === 'create_token') {
|
|
243
|
+
this.dialogActions = [
|
|
244
|
+
{
|
|
245
|
+
className: 'ia-button primary',
|
|
246
|
+
text: 'Back to Item Details',
|
|
247
|
+
href: `/details/${this.lendingStatus.bookUrl}`,
|
|
248
|
+
},
|
|
249
|
+
];
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
210
253
|
get iconClass() {
|
|
211
254
|
return this.width <= mobileContainerWidth ? 'mobile' : 'desktop';
|
|
212
255
|
}
|