@internetarchive/ia-book-actions 1.4.4-alpha16 → 1.4.4-alpha18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -106,9 +106,9 @@ export default async function ActionsHandlerService(options) {
|
|
|
106
106
|
|
|
107
107
|
/**
|
|
108
108
|
* Report it, don't just swallow it. A rejected fetch (offline, dropped
|
|
109
|
-
* connection) or a non-JSON body (a 405 returning HTML
|
|
110
|
-
*
|
|
111
|
-
*
|
|
109
|
+
* connection) or a non-JSON body (a 405 returning HTML) lands here, and
|
|
110
|
+
* calling neither `success` nor `error` leaves every caller waiting on a
|
|
111
|
+
* callback that never comes.
|
|
112
112
|
*
|
|
113
113
|
* For renew_loan that's not merely a missing modal: IABookActions
|
|
114
114
|
* clears its loanRenewInProgress guard from these callbacks, so a
|
|
@@ -117,5 +117,6 @@ export default async function ActionsHandlerService(options) {
|
|
|
117
117
|
* connection is exactly when this fires.
|
|
118
118
|
*/
|
|
119
119
|
log(`[IABookActions] ✗ ${option.action} threw`, error);
|
|
120
|
+
option?.error?.({ error: String(error) });
|
|
120
121
|
}
|
|
121
122
|
}
|
|
@@ -185,7 +185,7 @@ export default class ActionsHandler extends LitElement {
|
|
|
185
185
|
log('RENEW_LOAN --- ', data, this.identifier);
|
|
186
186
|
const activeLoan = data.loan ? data.loan : undefined;
|
|
187
187
|
// Optional chaining: `data.loan` is genuinely absent on some
|
|
188
|
-
// failures, and
|
|
188
|
+
// failures, and `activeLoan` can be undefined here.
|
|
189
189
|
const isRenewal = activeLoan?.renewal;
|
|
190
190
|
|
|
191
191
|
if (activeLoan && isRenewal) {
|
|
@@ -210,11 +210,9 @@ export default class ActionsHandler extends LitElement {
|
|
|
210
210
|
this.identifier
|
|
211
211
|
);
|
|
212
212
|
|
|
213
|
-
// Dispatch the success outcome ONLY for a confirmed renewal
|
|
214
|
-
//
|
|
215
|
-
//
|
|
216
|
-
// success: the error modal went up while handleLoanAutoRenewed
|
|
217
|
-
// simultaneously put a fresh countdown back on the bar.
|
|
213
|
+
// Dispatch the success outcome ONLY for a confirmed renewal —
|
|
214
|
+
// a `{loan: {renewal: false}}` response goes through
|
|
215
|
+
// dispatchActionError below instead, never both.
|
|
218
216
|
this.dispatchEvent(
|
|
219
217
|
new CustomEvent('loanAutoRenewed', {
|
|
220
218
|
detail: { action, data: { ...data, loan: activeLoan } },
|
package/src/ia-book-actions.js
CHANGED
|
@@ -970,7 +970,12 @@ export default class IABookActions extends LitElement {
|
|
|
970
970
|
this.disableActionGroup = false;
|
|
971
971
|
|
|
972
972
|
const action = event?.detail?.action;
|
|
973
|
-
|
|
973
|
+
// handleLoanRenewNow's failure path sets `.error` to a boolean flag and
|
|
974
|
+
// puts the real text in `.message` — only take `.error` when it's
|
|
975
|
+
// actually a string, or the modal renders the literal word "true".
|
|
976
|
+
const rawErrorMsg = event?.detail?.data?.error;
|
|
977
|
+
const errorMsg =
|
|
978
|
+
typeof rawErrorMsg === 'string' ? rawErrorMsg : undefined;
|
|
974
979
|
const isInitial = event?.detail?.isInitial === true;
|
|
975
980
|
|
|
976
981
|
log('[IABookActions] handleLendingActionError', {
|
|
@@ -983,20 +988,14 @@ export default class IABookActions extends LitElement {
|
|
|
983
988
|
});
|
|
984
989
|
|
|
985
990
|
if (action === 'create_token') {
|
|
986
|
-
//
|
|
987
|
-
//
|
|
988
|
-
//
|
|
989
|
-
//
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
return;
|
|
995
|
-
}
|
|
996
|
-
|
|
997
|
-
// The book never opened, and we don't retry — reset the bar to
|
|
998
|
-
// Borrow and the timer to 0 rather than leaving a red "Return now"
|
|
999
|
-
// with a stale countdown for a session that isn't accessible.
|
|
991
|
+
// Any create_token failure — initial or a routine interval refresh —
|
|
992
|
+
// means access can no longer be confirmed (e.g. a stale loan-record
|
|
993
|
+
// cache on some other node can make this request fail even though
|
|
994
|
+
// the loan is fine, or the loan may genuinely have just been
|
|
995
|
+
// returned via BookReaderImages.php's revokeAccess()). Don't retry
|
|
996
|
+
// and don't stay silent either way: reset the bar to Borrow and the
|
|
997
|
+
// timer to 0 rather than leaving a stale countdown for a session
|
|
998
|
+
// that isn't accessible.
|
|
1000
999
|
window?.IALendingIntervals?.clearAll();
|
|
1001
1000
|
this.tokenPoller?.disconnectedCallback();
|
|
1002
1001
|
this.lendingStatus = {
|
|
@@ -1007,8 +1006,9 @@ export default class IABookActions extends LitElement {
|
|
|
1007
1006
|
};
|
|
1008
1007
|
|
|
1009
1008
|
// showErrorModal has dedicated create_token messaging (refresh
|
|
1010
|
-
// button + support email)
|
|
1011
|
-
|
|
1009
|
+
// button + support email) and shows regardless of whether a
|
|
1010
|
+
// specific error string came back.
|
|
1011
|
+
this.showErrorModal(errorMsg, action);
|
|
1012
1012
|
} else if (action === 'renew_loan') {
|
|
1013
1013
|
window?.IALendingIntervals?.clearAll();
|
|
1014
1014
|
this.loanRenewInProgress = false;
|
|
@@ -1076,7 +1076,7 @@ export default class IABookActions extends LitElement {
|
|
|
1076
1076
|
.identifier}"
|
|
1077
1077
|
>info@archive.org</a
|
|
1078
1078
|
><br /><br />
|
|
1079
|
-
|
|
1079
|
+
${errorMsg ? html`<code>errorLog: ${errorMsg}</code>` : nothing}`;
|
|
1080
1080
|
}
|
|
1081
1081
|
|
|
1082
1082
|
await this.modal?.showModal({
|
|
@@ -1130,6 +1130,10 @@ export default class IABookActions extends LitElement {
|
|
|
1130
1130
|
justify-content: center;
|
|
1131
1131
|
flex-wrap: wrap;
|
|
1132
1132
|
}
|
|
1133
|
+
|
|
1134
|
+
#action-bar-modal {
|
|
1135
|
+
--modalWidth: 36rem;
|
|
1136
|
+
}
|
|
1133
1137
|
`;
|
|
1134
1138
|
}
|
|
1135
1139
|
}
|
|
@@ -1170,75 +1170,75 @@ describe('handleLendingActionError - loanRenewInProgress reset', () => {
|
|
|
1170
1170
|
});
|
|
1171
1171
|
});
|
|
1172
1172
|
|
|
1173
|
-
describe('handleLendingActionError - create_token
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1173
|
+
describe('handleLendingActionError - every create_token failure resets the bar (WEBDEV-8322 follow-up)', () => {
|
|
1174
|
+
// A create_token failure can't be told apart from "the loan was actually
|
|
1175
|
+
// returned" (e.g. a stale loan-record cache on another node, or a genuine
|
|
1176
|
+
// revokeAccess() in BookReaderImages.php) from here, so initial and
|
|
1177
|
+
// routine interval failures are treated identically: no retry, reset to
|
|
1178
|
+
// Borrow/0, show the modal every time.
|
|
1179
|
+
for (const isInitial of [true, false]) {
|
|
1180
|
+
it(`shows the error modal and resets to Borrow/0 on a failed create_token (isInitial=${isInitial})`, async () => {
|
|
1181
|
+
const el = await fixture(
|
|
1182
|
+
container({
|
|
1183
|
+
userid: '@user1',
|
|
1184
|
+
identifier: 'foobar',
|
|
1185
|
+
lendingStatus: {
|
|
1186
|
+
user_has_browsed: true,
|
|
1187
|
+
browsingExpired: false,
|
|
1188
|
+
secondsLeftOnLoan: 100,
|
|
1189
|
+
},
|
|
1190
|
+
})
|
|
1191
|
+
);
|
|
1192
|
+
await el.updateComplete;
|
|
1193
|
+
|
|
1194
|
+
const showErrorModalSpy = Sinon.spy(el, 'showErrorModal');
|
|
1195
|
+
const errorMsg = 'loan token not found. please try again later.';
|
|
1196
|
+
|
|
1197
|
+
el.handleLendingActionError({
|
|
1198
|
+
detail: {
|
|
1199
|
+
action: 'create_token',
|
|
1200
|
+
isInitial,
|
|
1201
|
+
data: { error: errorMsg },
|
|
1183
1202
|
},
|
|
1184
|
-
})
|
|
1185
|
-
);
|
|
1186
|
-
await el.updateComplete;
|
|
1187
|
-
expect(window.IALendingIntervals.timerCountdown).to.not.equal(0);
|
|
1203
|
+
});
|
|
1188
1204
|
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1205
|
+
expect(showErrorModalSpy.calledOnceWith(errorMsg, 'create_token')).to.be
|
|
1206
|
+
.true;
|
|
1207
|
+
expect(el.lendingStatus.user_has_browsed).to.be.false;
|
|
1208
|
+
expect(el.lendingStatus.available_to_browse).to.be.true;
|
|
1209
|
+
expect(el.lendingStatus.secondsLeftOnLoan).to.equal(0);
|
|
1194
1210
|
});
|
|
1195
1211
|
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1212
|
+
it(`stops the countdown/expiry timers on a failed create_token (isInitial=${isInitial})`, async () => {
|
|
1213
|
+
const el = await fixture(
|
|
1214
|
+
container({
|
|
1215
|
+
userid: '@user1',
|
|
1216
|
+
identifier: 'foobar',
|
|
1217
|
+
lendingStatus: {
|
|
1218
|
+
user_has_browsed: true,
|
|
1219
|
+
browsingExpired: false,
|
|
1220
|
+
secondsLeftOnLoan: 100,
|
|
1221
|
+
},
|
|
1222
|
+
})
|
|
1223
|
+
);
|
|
1224
|
+
await el.updateComplete;
|
|
1225
|
+
expect(window.IALendingIntervals.timerCountdown).to.not.equal(0);
|
|
1226
|
+
expect(window.IALendingIntervals.browseExpireTimeout).to.not.equal(0);
|
|
1200
1227
|
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
const el = await fixture(
|
|
1207
|
-
container({
|
|
1208
|
-
userid: '@user1',
|
|
1209
|
-
identifier: 'foobar',
|
|
1210
|
-
lendingStatus: {
|
|
1211
|
-
user_has_browsed: true,
|
|
1212
|
-
browsingExpired: false,
|
|
1213
|
-
secondsLeftOnLoan: 100,
|
|
1228
|
+
el.handleLendingActionError({
|
|
1229
|
+
detail: {
|
|
1230
|
+
action: 'create_token',
|
|
1231
|
+
isInitial,
|
|
1232
|
+
data: { error: 'loan token not found. please try again later.' },
|
|
1214
1233
|
},
|
|
1215
|
-
})
|
|
1216
|
-
);
|
|
1217
|
-
await el.updateComplete;
|
|
1218
|
-
|
|
1219
|
-
const showErrorModalSpy = Sinon.spy(el, 'showErrorModal');
|
|
1220
|
-
const errorMsg = 'loan token not found. please try again later.';
|
|
1234
|
+
});
|
|
1221
1235
|
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
action: 'create_token',
|
|
1225
|
-
isInitial: true,
|
|
1226
|
-
data: { error: errorMsg },
|
|
1227
|
-
},
|
|
1236
|
+
expect(window.IALendingIntervals.timerCountdown).to.equal(0);
|
|
1237
|
+
expect(window.IALendingIntervals.browseExpireTimeout).to.equal(0);
|
|
1228
1238
|
});
|
|
1239
|
+
}
|
|
1229
1240
|
|
|
1230
|
-
|
|
1231
|
-
.true;
|
|
1232
|
-
expect(el.lendingStatus.user_has_browsed).to.be.false;
|
|
1233
|
-
expect(el.lendingStatus.available_to_browse).to.be.true;
|
|
1234
|
-
expect(el.lendingStatus.secondsLeftOnLoan).to.equal(0);
|
|
1235
|
-
});
|
|
1236
|
-
|
|
1237
|
-
it('stops the countdown/expiry timers on a failed INITIAL create_token — nothing to read yet', async () => {
|
|
1238
|
-
// Unlike the interval-refresh case above (pages already loaded, keep
|
|
1239
|
-
// ticking), an initial create_token failure means BookReader never got
|
|
1240
|
-
// page images and we're resetting the bar to Borrow — both timers
|
|
1241
|
-
// should stop rather than keep counting down a dead session.
|
|
1241
|
+
it('shows the modal even without a specific error message', async () => {
|
|
1242
1242
|
const el = await fixture(
|
|
1243
1243
|
container({
|
|
1244
1244
|
userid: '@user1',
|
|
@@ -1251,19 +1251,14 @@ describe('handleLendingActionError - create_token failures must not stop the cou
|
|
|
1251
1251
|
})
|
|
1252
1252
|
);
|
|
1253
1253
|
await el.updateComplete;
|
|
1254
|
-
|
|
1255
|
-
|
|
1254
|
+
|
|
1255
|
+
const showErrorModalSpy = Sinon.spy(el, 'showErrorModal');
|
|
1256
1256
|
|
|
1257
1257
|
el.handleLendingActionError({
|
|
1258
|
-
detail: {
|
|
1259
|
-
action: 'create_token',
|
|
1260
|
-
isInitial: true,
|
|
1261
|
-
data: { error: 'loan token not found. please try again later.' },
|
|
1262
|
-
},
|
|
1258
|
+
detail: { action: 'create_token', isInitial: false, data: {} },
|
|
1263
1259
|
});
|
|
1264
1260
|
|
|
1265
|
-
expect(
|
|
1266
|
-
expect(window.IALendingIntervals.browseExpireTimeout).to.equal(0);
|
|
1261
|
+
expect(showErrorModalSpy.calledOnce).to.be.true;
|
|
1267
1262
|
});
|
|
1268
1263
|
|
|
1269
1264
|
it('still clears everything on a renew_loan failure', async () => {
|
|
@@ -1608,7 +1603,10 @@ describe('WEBDEV-8322 review fixes', () => {
|
|
|
1608
1603
|
expect(el.loanRenewInProgress).to.be.true;
|
|
1609
1604
|
});
|
|
1610
1605
|
|
|
1611
|
-
it('
|
|
1606
|
+
it('resets the bar and shows the modal on an interval create_token refresh failure too', async () => {
|
|
1607
|
+
// A routine refresh failing is treated the same as the initial one --
|
|
1608
|
+
// there's no reliable way to tell "harmless blip" from "the loan is
|
|
1609
|
+
// actually gone" from here (see handleLendingActionError).
|
|
1612
1610
|
const el = await fixture(
|
|
1613
1611
|
container({
|
|
1614
1612
|
userid: '@user1',
|
|
@@ -1634,9 +1632,8 @@ describe('WEBDEV-8322 review fixes', () => {
|
|
|
1634
1632
|
});
|
|
1635
1633
|
await el.updateComplete;
|
|
1636
1634
|
|
|
1637
|
-
expect(spy.
|
|
1638
|
-
|
|
1639
|
-
expect(el.lendingStatus.user_has_browsed).to.be.true;
|
|
1635
|
+
expect(spy.calledOnce).to.be.true;
|
|
1636
|
+
expect(el.lendingStatus.user_has_browsed).to.be.false;
|
|
1640
1637
|
});
|
|
1641
1638
|
|
|
1642
1639
|
it('tears down the previous poller before starting a new one', async () => {
|