@internetarchive/elements 1.1.2 → 1.1.3-webdev-8840.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/dist/src/elements/ia-radio-player/ia-radio-player.d.ts +0 -2
- package/dist/src/elements/ia-radio-player/ia-radio-player.d.ts.map +1 -1
- package/dist/src/elements/ia-radio-player/ia-radio-player.js +1 -6
- package/dist/src/elements/ia-radio-player/ia-radio-player.js.map +1 -1
- package/dist/src/elements/ia-reviews/assets/delete-icon.svg +8 -0
- package/dist/src/elements/ia-reviews/assets/star-basic.svg +6 -0
- package/dist/src/elements/ia-reviews/assets/star-selected.svg +8 -0
- package/dist/src/elements/ia-reviews/assets/star-unselected.svg +8 -0
- package/dist/src/elements/ia-reviews/ia-review-form.d.ts +99 -0
- package/dist/src/elements/ia-reviews/ia-review-form.d.ts.map +1 -0
- package/dist/src/elements/ia-reviews/ia-review-form.js +646 -0
- package/dist/src/elements/ia-reviews/ia-review-form.js.map +1 -0
- package/dist/src/elements/ia-reviews/ia-review.d.ts +50 -0
- package/dist/src/elements/ia-reviews/ia-review.d.ts.map +1 -0
- package/dist/src/elements/ia-reviews/ia-review.js +337 -0
- package/dist/src/elements/ia-reviews/ia-review.js.map +1 -0
- package/dist/src/elements/ia-reviews/ia-reviews.d.ts +68 -0
- package/dist/src/elements/ia-reviews/ia-reviews.d.ts.map +1 -0
- package/dist/src/elements/ia-reviews/ia-reviews.js +353 -0
- package/dist/src/elements/ia-reviews/ia-reviews.js.map +1 -0
- package/dist/src/elements/ia-reviews/mocks/mock-fetch-handler.d.ts +23 -0
- package/dist/src/elements/ia-reviews/mocks/mock-fetch-handler.d.ts.map +1 -0
- package/dist/src/elements/ia-reviews/mocks/mock-fetch-handler.js +37 -0
- package/dist/src/elements/ia-reviews/mocks/mock-fetch-handler.js.map +1 -0
- package/dist/src/elements/ia-reviews/mocks/mock-review-service.d.ts +12 -0
- package/dist/src/elements/ia-reviews/mocks/mock-review-service.d.ts.map +1 -0
- package/dist/src/elements/ia-reviews/mocks/mock-review-service.js +19 -0
- package/dist/src/elements/ia-reviews/mocks/mock-review-service.js.map +1 -0
- package/dist/src/elements/ia-reviews/models.d.ts +38 -0
- package/dist/src/elements/ia-reviews/models.d.ts.map +1 -0
- package/dist/src/elements/ia-reviews/models.js +2 -0
- package/dist/src/elements/ia-reviews/models.js.map +1 -0
- package/dist/src/elements/ia-reviews/review-service.d.ts +53 -0
- package/dist/src/elements/ia-reviews/review-service.d.ts.map +1 -0
- package/dist/src/elements/ia-reviews/review-service.js +97 -0
- package/dist/src/elements/ia-reviews/review-service.js.map +1 -0
- package/dist/src/elements/ia-reviews/utils/collapse-space.d.ts +13 -0
- package/dist/src/elements/ia-reviews/utils/collapse-space.d.ts.map +1 -0
- package/dist/src/elements/ia-reviews/utils/collapse-space.js +19 -0
- package/dist/src/elements/ia-reviews/utils/collapse-space.js.map +1 -0
- package/dist/src/elements/ia-reviews/utils/friendly-truncate.d.ts +14 -0
- package/dist/src/elements/ia-reviews/utils/friendly-truncate.d.ts.map +1 -0
- package/dist/src/elements/ia-reviews/utils/friendly-truncate.js +65 -0
- package/dist/src/elements/ia-reviews/utils/friendly-truncate.js.map +1 -0
- package/dist/src/elements/ia-reviews/utils/link-urls-in-text.d.ts +9 -0
- package/dist/src/elements/ia-reviews/utils/link-urls-in-text.d.ts.map +1 -0
- package/dist/src/elements/ia-reviews/utils/link-urls-in-text.js +40 -0
- package/dist/src/elements/ia-reviews/utils/link-urls-in-text.js.map +1 -0
- package/dist/src/elements/ia-reviews/utils/sanitize-review-body.d.ts +9 -0
- package/dist/src/elements/ia-reviews/utils/sanitize-review-body.d.ts.map +1 -0
- package/dist/src/elements/ia-reviews/utils/sanitize-review-body.js +29 -0
- package/dist/src/elements/ia-reviews/utils/sanitize-review-body.js.map +1 -0
- package/dist/src/elements/ia-reviews/utils/truncate-screenname.d.ts +13 -0
- package/dist/src/elements/ia-reviews/utils/truncate-screenname.d.ts.map +1 -0
- package/dist/src/elements/ia-reviews/utils/truncate-screenname.js +19 -0
- package/dist/src/elements/ia-reviews/utils/truncate-screenname.js.map +1 -0
- package/dist/src/elements/index.d.ts +5 -0
- package/dist/src/elements/index.d.ts.map +1 -1
- package/dist/src/elements/index.js +5 -0
- package/dist/src/elements/index.js.map +1 -1
- package/package.json +7 -1
|
@@ -0,0 +1,646 @@
|
|
|
1
|
+
import { __decorate } from "tslib";
|
|
2
|
+
import { html, css, LitElement, nothing, } from 'lit';
|
|
3
|
+
import { property, state, query } from 'lit/decorators.js';
|
|
4
|
+
import { customElement } from '../../util/custom-element.js';
|
|
5
|
+
import { msg } from '@lit/localize';
|
|
6
|
+
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
|
|
7
|
+
import DOMPurify from 'dompurify';
|
|
8
|
+
import '../../elements/ia-button/ia-button.js';
|
|
9
|
+
import { Review } from '@internetarchive/metadata-service';
|
|
10
|
+
import starSelected from './assets/star-selected.svg';
|
|
11
|
+
import starUnselected from './assets/star-unselected.svg';
|
|
12
|
+
import './ia-review.js';
|
|
13
|
+
/**
|
|
14
|
+
* Renders a form to edit a given IA review.
|
|
15
|
+
*/
|
|
16
|
+
let IAReviewForm = class IAReviewForm extends LitElement {
|
|
17
|
+
constructor() {
|
|
18
|
+
super(...arguments);
|
|
19
|
+
/** Form submitter's screenname, if applicable */
|
|
20
|
+
this.submitterScreenname = 'Anonymous';
|
|
21
|
+
/* Optionally avoid using recaptcha for the form */
|
|
22
|
+
this.bypassRecaptcha = false;
|
|
23
|
+
/* Number of stars currently selected */
|
|
24
|
+
this.currentStars = 0;
|
|
25
|
+
/* Length of review subject currently filled in */
|
|
26
|
+
this.currentSubjectLength = 0;
|
|
27
|
+
/* Length of review body currently filled in */
|
|
28
|
+
this.currentBodyLength = 0;
|
|
29
|
+
/* Whether to enable the submit button */
|
|
30
|
+
this.formCanSubmit = false;
|
|
31
|
+
/* Whether to show a loading indicator for the form */
|
|
32
|
+
this.submissionInProgress = false;
|
|
33
|
+
/* Error to show if recaptcha cannot be loaded */
|
|
34
|
+
this.RECAPTCHA_ERROR_MESSAGE = 'Could not validate review. Please try again later.';
|
|
35
|
+
this.GENERIC_ERROR_MESSAGE = "There's been a temporary error. Please wait a moment and try again.";
|
|
36
|
+
}
|
|
37
|
+
render() {
|
|
38
|
+
return html `<form id="review-form" @submit=${this.handleSubmit}>
|
|
39
|
+
${this.unrecoverableError
|
|
40
|
+
? this.unrecoverableErrorTemplate
|
|
41
|
+
: html `
|
|
42
|
+
<span class="inputs">
|
|
43
|
+
${this.starsInputTemplate} ${this.subjectInputTemplate}
|
|
44
|
+
${this.bodyInputTemplate}
|
|
45
|
+
</span>
|
|
46
|
+
`}
|
|
47
|
+
${this.recaptchaMessageTemplate} ${this.recoverableErrorTemplate}
|
|
48
|
+
${this.actionButtonsTemplate}
|
|
49
|
+
</form>`;
|
|
50
|
+
}
|
|
51
|
+
willUpdate(changed) {
|
|
52
|
+
// Fill in previous review, if provided
|
|
53
|
+
if (changed.has('oldReview')) {
|
|
54
|
+
this.currentStars = this.oldReview?.stars ?? 0;
|
|
55
|
+
this.currentSubjectLength = this.oldReview?.reviewtitle?.length ?? 0;
|
|
56
|
+
this.currentBodyLength = this.oldReview?.reviewbody?.length ?? 0;
|
|
57
|
+
}
|
|
58
|
+
// Set up ReCaptcha, if possible
|
|
59
|
+
if (changed.has('recaptchaManager') &&
|
|
60
|
+
!this.bypassRecaptcha &&
|
|
61
|
+
this.recaptchaManager) {
|
|
62
|
+
this.setupRecaptcha();
|
|
63
|
+
}
|
|
64
|
+
// Disable form if unrecoverable error added
|
|
65
|
+
if (changed.has('unrecoverableError')) {
|
|
66
|
+
this.formCanSubmit = this.checkSubmissionAllowed();
|
|
67
|
+
}
|
|
68
|
+
// Ensure new subject and body have correct length
|
|
69
|
+
if (changed.has('currentSubjectLength') ||
|
|
70
|
+
changed.has('currentBodyLength') ||
|
|
71
|
+
changed.has('maxSubjectLength') ||
|
|
72
|
+
changed.has('maxBodyLength')) {
|
|
73
|
+
this.formCanSubmit = this.checkSubmissionAllowed();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/** Error to render instead of form inputs */
|
|
77
|
+
get unrecoverableErrorTemplate() {
|
|
78
|
+
return this.unrecoverableError
|
|
79
|
+
? html `
|
|
80
|
+
<div class="unrecoverable-error">
|
|
81
|
+
<span class="error-msg">${msg(this.unrecoverableError)}</span>
|
|
82
|
+
</div>
|
|
83
|
+
`
|
|
84
|
+
: nothing;
|
|
85
|
+
}
|
|
86
|
+
/** Error to render next to submit button */
|
|
87
|
+
get recoverableErrorTemplate() {
|
|
88
|
+
return this.recoverableError
|
|
89
|
+
? html `
|
|
90
|
+
<div class="recoverable-error">
|
|
91
|
+
${unsafeHTML(this.sanitizeErrorMsg(msg(this.recoverableError)))}
|
|
92
|
+
</div>
|
|
93
|
+
`
|
|
94
|
+
: nothing;
|
|
95
|
+
}
|
|
96
|
+
get recaptchaMessageTemplate() {
|
|
97
|
+
if (this.bypassRecaptcha)
|
|
98
|
+
return nothing;
|
|
99
|
+
return html `
|
|
100
|
+
<span class="recaptcha-disclaimer"
|
|
101
|
+
>${msg(html `This site is protected by reCAPTCHA and the Google
|
|
102
|
+
<a
|
|
103
|
+
target="_blank"
|
|
104
|
+
class="inline-link"
|
|
105
|
+
href="https://policies.google.com/privacy"
|
|
106
|
+
>Privacy Policy</a
|
|
107
|
+
>
|
|
108
|
+
and
|
|
109
|
+
<a
|
|
110
|
+
target="_blank"
|
|
111
|
+
class="inline-link"
|
|
112
|
+
href="https://policies.google.com/terms"
|
|
113
|
+
>Terms of Service</a
|
|
114
|
+
>
|
|
115
|
+
apply.`)}</span
|
|
116
|
+
>
|
|
117
|
+
`;
|
|
118
|
+
}
|
|
119
|
+
/** Clickable group of stars */
|
|
120
|
+
get starsInputTemplate() {
|
|
121
|
+
return html `
|
|
122
|
+
<div class="form-heading rating">
|
|
123
|
+
<label for="stars-field">${msg('Rating (optional)')}</label>
|
|
124
|
+
</div>
|
|
125
|
+
<input
|
|
126
|
+
type="hidden"
|
|
127
|
+
name="field_stars"
|
|
128
|
+
id="stars-input"
|
|
129
|
+
.value=${this.currentStars.toString()}
|
|
130
|
+
required
|
|
131
|
+
/>
|
|
132
|
+
<div class="stars">
|
|
133
|
+
${[1, 2, 3, 4, 5].map((num) => this.renderStar(num))}
|
|
134
|
+
<button
|
|
135
|
+
type="button"
|
|
136
|
+
class="clear-stars-btn"
|
|
137
|
+
@click=${this.handleClearBtnClicked}
|
|
138
|
+
>
|
|
139
|
+
${msg('Clear')}
|
|
140
|
+
</button>
|
|
141
|
+
</div>
|
|
142
|
+
`;
|
|
143
|
+
}
|
|
144
|
+
/** Subject input and character counter */
|
|
145
|
+
get subjectInputTemplate() {
|
|
146
|
+
return html `
|
|
147
|
+
<span id="subject-input" class="input-box ${this.maxSubjectLength &&
|
|
148
|
+
this.currentSubjectLength > this.maxSubjectLength
|
|
149
|
+
? 'error'
|
|
150
|
+
: ''}"
|
|
151
|
+
><div class="form-heading">
|
|
152
|
+
<label for="field_reviewtitle">${msg('Subject')}</label>
|
|
153
|
+
${this.maxSubjectLength
|
|
154
|
+
? html `<div class="char-count subject">
|
|
155
|
+
${this.currentSubjectLength}/${this.maxSubjectLength}
|
|
156
|
+
</div>`
|
|
157
|
+
: nothing}
|
|
158
|
+
</div>
|
|
159
|
+
<input
|
|
160
|
+
type="text"
|
|
161
|
+
name="field_reviewtitle"
|
|
162
|
+
id="field_reviewtitle"
|
|
163
|
+
.value=${this.oldReview?.reviewtitle ?? ''}
|
|
164
|
+
@input=${this.handleSubjectChanged}
|
|
165
|
+
required
|
|
166
|
+
/>${this.maxSubjectLength
|
|
167
|
+
? html `
|
|
168
|
+
<div class="input-error">
|
|
169
|
+
${msg(`Subject may only have ${this.maxSubjectLength} characters`)}
|
|
170
|
+
</div>
|
|
171
|
+
`
|
|
172
|
+
: nothing}</div></span>
|
|
173
|
+
`;
|
|
174
|
+
}
|
|
175
|
+
/** Body input and character counter */
|
|
176
|
+
get bodyInputTemplate() {
|
|
177
|
+
return html `
|
|
178
|
+
<span
|
|
179
|
+
id="body-input"
|
|
180
|
+
class="input-box ${this.maxBodyLength &&
|
|
181
|
+
this.currentBodyLength > this.maxBodyLength
|
|
182
|
+
? 'error'
|
|
183
|
+
: ''}"
|
|
184
|
+
><div class="form-heading">
|
|
185
|
+
<label for="field_reviewbody">${msg('Review')}</label>
|
|
186
|
+
${this.maxBodyLength
|
|
187
|
+
? html `<div class="char-count body">
|
|
188
|
+
${this.currentBodyLength}/${this.maxBodyLength}
|
|
189
|
+
</div>`
|
|
190
|
+
: nothing}
|
|
191
|
+
</div>
|
|
192
|
+
<textarea
|
|
193
|
+
name="field_reviewbody"
|
|
194
|
+
id="field_reviewbody"
|
|
195
|
+
.value=${this.oldReview?.reviewbody ?? ''}
|
|
196
|
+
rows="10"
|
|
197
|
+
cols="50"
|
|
198
|
+
required
|
|
199
|
+
@input=${this.handleBodyChanged}
|
|
200
|
+
></textarea>
|
|
201
|
+
${this.maxBodyLength
|
|
202
|
+
? html `
|
|
203
|
+
<div class="input-error">
|
|
204
|
+
${msg(`Review may only have ${this.maxBodyLength} characters`)}
|
|
205
|
+
</div>
|
|
206
|
+
`
|
|
207
|
+
: nothing}
|
|
208
|
+
</span>
|
|
209
|
+
`;
|
|
210
|
+
}
|
|
211
|
+
/** Buttons to render at bottom of form */
|
|
212
|
+
get actionButtonsTemplate() {
|
|
213
|
+
return html `<div class="action-btns">
|
|
214
|
+
<ia-button
|
|
215
|
+
mode="secondary"
|
|
216
|
+
class="cancel-btn"
|
|
217
|
+
data-testid="cancel-btn"
|
|
218
|
+
@click=${this.cancelReviewEdit}
|
|
219
|
+
>
|
|
220
|
+
${msg('Cancel')}
|
|
221
|
+
</ia-button>
|
|
222
|
+
<ia-button
|
|
223
|
+
mode="primary"
|
|
224
|
+
type="submit"
|
|
225
|
+
class="submit-btn"
|
|
226
|
+
.disabled=${!this.formCanSubmit}
|
|
227
|
+
.loading=${this.submissionInProgress}
|
|
228
|
+
>
|
|
229
|
+
${msg('Submit review')}
|
|
230
|
+
</ia-button>
|
|
231
|
+
</div>`;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Renders a star icon, using its number and the current star rating
|
|
235
|
+
* to determine whether it should appear selected or not
|
|
236
|
+
*
|
|
237
|
+
* @param {number} num The number of the current star
|
|
238
|
+
* @returns {HTMLTemplateResult} The correct icon, rendered within a button
|
|
239
|
+
*/
|
|
240
|
+
renderStar(num) {
|
|
241
|
+
const isSelected = num === this.currentStars;
|
|
242
|
+
const ratingLabel = msg(`Rate ${num > 1 ? `${num} stars` : '1 star'}`);
|
|
243
|
+
return html `
|
|
244
|
+
<button
|
|
245
|
+
class="star star-${num}"
|
|
246
|
+
title=${isSelected ? msg('Clear rating') : ratingLabel}
|
|
247
|
+
@click=${(e) => this.handleStarClicked(e, num)}
|
|
248
|
+
>
|
|
249
|
+
${num <= this.currentStars
|
|
250
|
+
? html `<img
|
|
251
|
+
class="star-selected"
|
|
252
|
+
src=${starSelected}
|
|
253
|
+
alt=""
|
|
254
|
+
aria-hidden="true"
|
|
255
|
+
/>`
|
|
256
|
+
: html `<img
|
|
257
|
+
class="star-unselected"
|
|
258
|
+
src=${starUnselected}
|
|
259
|
+
alt=""
|
|
260
|
+
aria-hidden="true"
|
|
261
|
+
/>`}
|
|
262
|
+
</button>
|
|
263
|
+
`;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Sets up ReCaptcha for the form, if possible.
|
|
267
|
+
*
|
|
268
|
+
* On failure, replaces form inputs with the ReCaptcha error message.
|
|
269
|
+
*/
|
|
270
|
+
async setupRecaptcha() {
|
|
271
|
+
try {
|
|
272
|
+
this.recaptchaWidget = await this.recaptchaManager?.getRecaptchaWidget();
|
|
273
|
+
}
|
|
274
|
+
catch {
|
|
275
|
+
this.unrecoverableError = this.RECAPTCHA_ERROR_MESSAGE;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Uses DOMpurify to sanitize any error message before render.
|
|
280
|
+
*
|
|
281
|
+
* @param {string} errorMsg The error message to be sanitized
|
|
282
|
+
* @returns {string} The sanitized error message
|
|
283
|
+
*/
|
|
284
|
+
sanitizeErrorMsg(errorMsg) {
|
|
285
|
+
return DOMPurify.sanitize(errorMsg, { ALLOWED_TAGS: ['a', 'b', 'br'] });
|
|
286
|
+
}
|
|
287
|
+
/** Handles validation and recaptcha execution on form submission */
|
|
288
|
+
async handleSubmit(e) {
|
|
289
|
+
e.preventDefault();
|
|
290
|
+
// Prevent disallowed or double submission
|
|
291
|
+
if (!this.formCanSubmit || this.submissionInProgress)
|
|
292
|
+
return;
|
|
293
|
+
// Set loading behavior
|
|
294
|
+
this.submissionInProgress = true;
|
|
295
|
+
this.recoverableError = '';
|
|
296
|
+
// Check for HTML errors
|
|
297
|
+
if (!this.reviewForm.reportValidity()) {
|
|
298
|
+
return this.stopSubmission();
|
|
299
|
+
}
|
|
300
|
+
if (!this.reviewService || !this.identifier) {
|
|
301
|
+
this.recoverableError = this.GENERIC_ERROR_MESSAGE;
|
|
302
|
+
return this.stopSubmission();
|
|
303
|
+
}
|
|
304
|
+
try {
|
|
305
|
+
let recaptchaToken;
|
|
306
|
+
if (!this.bypassRecaptcha) {
|
|
307
|
+
recaptchaToken = await this.getRecaptchaToken();
|
|
308
|
+
if (!recaptchaToken)
|
|
309
|
+
return this.handleRecaptchaError();
|
|
310
|
+
}
|
|
311
|
+
const result = await this.reviewService.submitReview({
|
|
312
|
+
identifier: this.identifier,
|
|
313
|
+
title: this.reviewForm.field_reviewtitle.value,
|
|
314
|
+
body: this.reviewForm.field_reviewbody.value,
|
|
315
|
+
stars: this.reviewForm.field_stars.value,
|
|
316
|
+
recaptchaToken,
|
|
317
|
+
});
|
|
318
|
+
if (result?.success === true) {
|
|
319
|
+
this.submissionInProgress = false;
|
|
320
|
+
const newReview = this.generateSubmittedReview();
|
|
321
|
+
// Emit a reviewUpdated event
|
|
322
|
+
const event = new CustomEvent('reviewUpdated', {
|
|
323
|
+
detail: newReview,
|
|
324
|
+
});
|
|
325
|
+
this.dispatchEvent(event);
|
|
326
|
+
}
|
|
327
|
+
else {
|
|
328
|
+
this.recoverableError = result.error ?? this.GENERIC_ERROR_MESSAGE;
|
|
329
|
+
this.stopSubmission();
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
catch (e) {
|
|
333
|
+
console.error(e);
|
|
334
|
+
this.recoverableError = this.GENERIC_ERROR_MESSAGE;
|
|
335
|
+
this.stopSubmission();
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Generates a review object from the submitted review,
|
|
340
|
+
* for optimistic display on success.
|
|
341
|
+
*
|
|
342
|
+
* @returns {Review} The review to display
|
|
343
|
+
*/
|
|
344
|
+
generateSubmittedReview() {
|
|
345
|
+
const today = new Date().toDateString();
|
|
346
|
+
const newReview = new Review({
|
|
347
|
+
reviewtitle: this.reviewForm.field_reviewtitle.value,
|
|
348
|
+
reviewbody: this.reviewForm.field_reviewbody.value,
|
|
349
|
+
stars: this.reviewForm.field_stars.value,
|
|
350
|
+
reviewdate: today,
|
|
351
|
+
reviewer: this.oldReview?.reviewer ?? this.submitterScreenname,
|
|
352
|
+
reviewer_itemname: this.oldReview?.reviewer_itemname ?? this.submitterItemname,
|
|
353
|
+
createdate: this.dateToString(this.oldReview?.createdate) ?? today,
|
|
354
|
+
});
|
|
355
|
+
return newReview;
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Converts date to string if it is a Date, otherwise
|
|
359
|
+
* leaves unchanged.
|
|
360
|
+
*/
|
|
361
|
+
dateToString(date) {
|
|
362
|
+
if (date instanceof Date)
|
|
363
|
+
return date.toDateString();
|
|
364
|
+
return date;
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Executes the ReCaptcha challenge for the form.
|
|
368
|
+
* Attempts form submission on success.
|
|
369
|
+
* */
|
|
370
|
+
async getRecaptchaToken() {
|
|
371
|
+
if (!this.recaptchaWidget) {
|
|
372
|
+
this.handleRecaptchaError();
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
try {
|
|
376
|
+
const recaptchaToken = await this.recaptchaWidget.execute();
|
|
377
|
+
return recaptchaToken;
|
|
378
|
+
}
|
|
379
|
+
catch {
|
|
380
|
+
this.handleRecaptchaError();
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
/* Handles a recaptcha error during submission */
|
|
385
|
+
handleRecaptchaError() {
|
|
386
|
+
this.recoverableError = this.RECAPTCHA_ERROR_MESSAGE;
|
|
387
|
+
this.stopSubmission();
|
|
388
|
+
}
|
|
389
|
+
/* Handles canceled form submission */
|
|
390
|
+
stopSubmission() {
|
|
391
|
+
if (this.submissionInProgress) {
|
|
392
|
+
this.submissionInProgress = false;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
/* Emits an event to close the form */
|
|
396
|
+
cancelReviewEdit() {
|
|
397
|
+
const event = new CustomEvent('reviewEditCanceled');
|
|
398
|
+
this.dispatchEvent(event);
|
|
399
|
+
}
|
|
400
|
+
/* Prevents form submission and sets stars based on number clicked */
|
|
401
|
+
handleStarClicked(e, num) {
|
|
402
|
+
e.preventDefault();
|
|
403
|
+
this.setStars(num);
|
|
404
|
+
}
|
|
405
|
+
/* Clear stars if requested */
|
|
406
|
+
handleClearBtnClicked(e) {
|
|
407
|
+
e.preventDefault();
|
|
408
|
+
this.currentStars = 0;
|
|
409
|
+
}
|
|
410
|
+
/* Updates star count, resetting to 0 if the currently selected star is clicked */
|
|
411
|
+
setStars(num) {
|
|
412
|
+
this.currentStars = num === this.currentStars ? 0 : num;
|
|
413
|
+
}
|
|
414
|
+
/* Updates subject input length */
|
|
415
|
+
handleSubjectChanged(e) {
|
|
416
|
+
const subjectInput = e.target;
|
|
417
|
+
this.currentSubjectLength = subjectInput.value.length;
|
|
418
|
+
}
|
|
419
|
+
/* Updates body input length */
|
|
420
|
+
handleBodyChanged(e) {
|
|
421
|
+
const bodyInput = e.target;
|
|
422
|
+
this.currentBodyLength = bodyInput.value.length;
|
|
423
|
+
}
|
|
424
|
+
/* Checks if submission should be allowed */
|
|
425
|
+
checkSubmissionAllowed() {
|
|
426
|
+
// Form must not have an unrecoverable error
|
|
427
|
+
if (this.unrecoverableError) {
|
|
428
|
+
return false;
|
|
429
|
+
}
|
|
430
|
+
// Subject and body must not be empty
|
|
431
|
+
if (!this.currentBodyLength || !this.currentSubjectLength) {
|
|
432
|
+
return false;
|
|
433
|
+
}
|
|
434
|
+
// Subject must be correct length
|
|
435
|
+
if (!!this.maxSubjectLength &&
|
|
436
|
+
this.currentSubjectLength > this.maxSubjectLength) {
|
|
437
|
+
return false;
|
|
438
|
+
}
|
|
439
|
+
// Body must be correct length
|
|
440
|
+
if (!!this.maxBodyLength && this.currentBodyLength > this.maxBodyLength) {
|
|
441
|
+
return false;
|
|
442
|
+
}
|
|
443
|
+
return true;
|
|
444
|
+
}
|
|
445
|
+
static get styles() {
|
|
446
|
+
return [
|
|
447
|
+
css `
|
|
448
|
+
.form-heading {
|
|
449
|
+
display: flex;
|
|
450
|
+
flex-direction: row;
|
|
451
|
+
justify-content: space-between;
|
|
452
|
+
padding-top: 15px;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
.form-heading.rating {
|
|
456
|
+
padding-top: 0;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
.form-heading label {
|
|
460
|
+
font-size: 1.6rem;
|
|
461
|
+
font-weight: bold;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
label {
|
|
465
|
+
display: inline-block;
|
|
466
|
+
margin-bottom: 5px;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
textarea,
|
|
470
|
+
input[type='text'],
|
|
471
|
+
.unrecoverable-error {
|
|
472
|
+
padding: 5px;
|
|
473
|
+
width: calc(100% - 10px);
|
|
474
|
+
font-family: inherit;
|
|
475
|
+
border-radius: 5px;
|
|
476
|
+
border: 1px solid var(--container-border-color, #999999);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
.input-box.error input,
|
|
480
|
+
.input-box.error textarea {
|
|
481
|
+
border: 2px solid var(--container-error-color, #ea0202);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
.input-box.error .char-count,
|
|
485
|
+
.input-error,
|
|
486
|
+
.unrecoverable-error,
|
|
487
|
+
.recoverable-error {
|
|
488
|
+
color: var(--container-error-color, #ea0202);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
.input-error {
|
|
492
|
+
display: none;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
.input-box.error .input-error {
|
|
496
|
+
display: block;
|
|
497
|
+
text-align: right;
|
|
498
|
+
padding-top: 5px;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
.inline-link {
|
|
502
|
+
color: var(--container-link-color, #4f65f5);
|
|
503
|
+
text-decoration: none;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
.inline-link:hover {
|
|
507
|
+
text-decoration: underline;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
.stars {
|
|
511
|
+
display: flex;
|
|
512
|
+
flex-direction: row;
|
|
513
|
+
gap: 2px;
|
|
514
|
+
align-items: center;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
.star {
|
|
518
|
+
all: unset;
|
|
519
|
+
height: 30px;
|
|
520
|
+
width: 30px;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
.star-selected,
|
|
524
|
+
.star-unselected {
|
|
525
|
+
display: block;
|
|
526
|
+
width: 100%;
|
|
527
|
+
height: 100%;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
.star:hover {
|
|
531
|
+
cursor: pointer;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
.clear-stars-btn,
|
|
535
|
+
.recoverable-error a {
|
|
536
|
+
padding: 0 5px;
|
|
537
|
+
color: var(--container-link-color, #4f65f5);
|
|
538
|
+
font-family: inherit;
|
|
539
|
+
border: none;
|
|
540
|
+
background: transparent;
|
|
541
|
+
display: inline-block;
|
|
542
|
+
padding-top: 5px;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
.clear-stars-btn:hover,
|
|
546
|
+
.recoverable-error a:hover {
|
|
547
|
+
cursor: pointer;
|
|
548
|
+
text-decoration: underline;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
.action-btns {
|
|
552
|
+
width: 100%;
|
|
553
|
+
display: flex;
|
|
554
|
+
justify-content: flex-end;
|
|
555
|
+
gap: 10px;
|
|
556
|
+
padding-top: 15px;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
.ia-button:disabled {
|
|
560
|
+
opacity: 0.75;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
.ia-button:disabled:hover {
|
|
564
|
+
cursor: not-allowed;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
.unrecoverable-error {
|
|
568
|
+
min-height: 50px;
|
|
569
|
+
padding: 5px;
|
|
570
|
+
text-align: center;
|
|
571
|
+
display: flex;
|
|
572
|
+
flex-direction: column;
|
|
573
|
+
justify-content: center;
|
|
574
|
+
background-color: var(--container-bg-color, #fbfbfd);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
.recaptcha-disclaimer {
|
|
578
|
+
font-size: 1.2rem;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
@media only screen and (max-width: 350px) {
|
|
582
|
+
.action-btns {
|
|
583
|
+
flex-direction: column-reverse;
|
|
584
|
+
align-items: center;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
`,
|
|
588
|
+
];
|
|
589
|
+
}
|
|
590
|
+
};
|
|
591
|
+
__decorate([
|
|
592
|
+
property({ type: String })
|
|
593
|
+
], IAReviewForm.prototype, "identifier", void 0);
|
|
594
|
+
__decorate([
|
|
595
|
+
property({ type: String })
|
|
596
|
+
], IAReviewForm.prototype, "submitterScreenname", void 0);
|
|
597
|
+
__decorate([
|
|
598
|
+
property({ type: String })
|
|
599
|
+
], IAReviewForm.prototype, "submitterItemname", void 0);
|
|
600
|
+
__decorate([
|
|
601
|
+
property({ type: Object })
|
|
602
|
+
], IAReviewForm.prototype, "oldReview", void 0);
|
|
603
|
+
__decorate([
|
|
604
|
+
property({ type: String })
|
|
605
|
+
], IAReviewForm.prototype, "unrecoverableError", void 0);
|
|
606
|
+
__decorate([
|
|
607
|
+
property({ type: Number })
|
|
608
|
+
], IAReviewForm.prototype, "maxSubjectLength", void 0);
|
|
609
|
+
__decorate([
|
|
610
|
+
property({ type: Number })
|
|
611
|
+
], IAReviewForm.prototype, "maxBodyLength", void 0);
|
|
612
|
+
__decorate([
|
|
613
|
+
property({ type: Object })
|
|
614
|
+
], IAReviewForm.prototype, "reviewService", void 0);
|
|
615
|
+
__decorate([
|
|
616
|
+
property({ type: Object })
|
|
617
|
+
], IAReviewForm.prototype, "recaptchaManager", void 0);
|
|
618
|
+
__decorate([
|
|
619
|
+
property({ type: Boolean })
|
|
620
|
+
], IAReviewForm.prototype, "bypassRecaptcha", void 0);
|
|
621
|
+
__decorate([
|
|
622
|
+
state()
|
|
623
|
+
], IAReviewForm.prototype, "currentStars", void 0);
|
|
624
|
+
__decorate([
|
|
625
|
+
state()
|
|
626
|
+
], IAReviewForm.prototype, "currentSubjectLength", void 0);
|
|
627
|
+
__decorate([
|
|
628
|
+
state()
|
|
629
|
+
], IAReviewForm.prototype, "currentBodyLength", void 0);
|
|
630
|
+
__decorate([
|
|
631
|
+
state()
|
|
632
|
+
], IAReviewForm.prototype, "recoverableError", void 0);
|
|
633
|
+
__decorate([
|
|
634
|
+
state()
|
|
635
|
+
], IAReviewForm.prototype, "formCanSubmit", void 0);
|
|
636
|
+
__decorate([
|
|
637
|
+
state()
|
|
638
|
+
], IAReviewForm.prototype, "submissionInProgress", void 0);
|
|
639
|
+
__decorate([
|
|
640
|
+
query('#review-form')
|
|
641
|
+
], IAReviewForm.prototype, "reviewForm", void 0);
|
|
642
|
+
IAReviewForm = __decorate([
|
|
643
|
+
customElement('ia-review-form')
|
|
644
|
+
], IAReviewForm);
|
|
645
|
+
export { IAReviewForm };
|
|
646
|
+
//# sourceMappingURL=ia-review-form.js.map
|