Style reading quiz feedback by result

This commit is contained in:
Codex committed 2026-10-14 21:48:31 +02:00
1 parent f607994a5d
commit 77553bbece
3 files changed
+38 -9

No files matched your search

+5 -1
View File
@@ -395,7 +395,11 @@ body.reader { position: fixed; top: 0; left: 0; right: 0; bottom: 0; overflow: h
.reading-gate-question { margin: 20px 0; }
.reading-gate-question input { display: block; box-sizing: border-box; width: 100%; margin: 14px 0; padding: 10px; }
.reading-gate-actions { display: flex; flex-wrap: wrap; gap: 10px; }
.reading-gate-message { color: var(--accent); min-height: 1.5em; }
.reading-gate-message { margin: 18px 0 0; padding: 11px 13px; border: 1px solid #46505b;
border-radius: 9px; background: #222a33; color: #d8e6f4; }
.reading-gate-message[data-tone="success"] { border-color: #39765a; background: #193528; color: #b8f2d0; }
.reading-gate-message[data-tone="retry"] { border-color: #9b7233; background: #382b1a; color: #ffdaa0; }
.reading-gate-message[data-tone="error"] { border-color: #944c55; background: #3c2025; color: #ffc0c6; }
@media (max-width: 660px) { .reading-gate { margin: 28px 12px 100px; padding: 18px; } }
.pg { position: absolute; overflow: hidden; background: #19191c; }
.pg img { display: block; width: 100%; height: 100%; -webkit-user-drag: none; }
+13 -8
View File
@@ -22,7 +22,12 @@
h('h1', {}, 'Reading is closed right now'),
h('p', {}, `The reading hours are set by this server's deployment (${access.timezone}). The library and your progress remain available.`),
h('p', {}, `Answer five questions drawn from manga you have already read to unlock ${access.unlockMinutes} minutes of reading. There is no answer timer or retry limit.`));
const message = h('p', { class: 'reading-gate-message', role: 'status' });
const message = h('p', { class: 'reading-gate-message', role: 'status', hidden: true });
const setMessage = (text, tone = 'info') => {
message.textContent = text;
message.dataset.tone = tone;
message.hidden = !text;
};
const question = h('div', { class: 'reading-gate-question' });
const start = h('button', { class: 'btn', type: 'button' }, 'Start five-question challenge');
const back = h('a', { class: 'btn', href: mangaUrl(P.get('m')) }, 'Back to manga');
@@ -39,22 +44,22 @@
event.preventDefault();
try {
const result = await api('api/reading-quiz/answer', { m: P.get('m'), challenge, answer: input.value });
if (!result.correct) { message.textContent = 'Not quite. Try another spelling or answer—there is no attempt limit.'; input.select(); return; }
if (result.complete) { message.textContent = 'Unlocked. Opening your chapter…'; location.reload(); return; }
message.textContent = 'Correct.';
if (!result.correct) { setMessage('Not quite. Try another spelling or answer—there is no attempt limit.', 'retry'); input.select(); return; }
if (result.complete) { setMessage('Unlocked. Opening your chapter…', 'success'); location.reload(); return; }
setMessage('Correct.', 'success');
showQuestion(result);
} catch (error) {
message.textContent = error.message;
setMessage(error.message, 'error');
if (/challenge expired/i.test(error.message)) { challenge = null; question.textContent = ''; start.hidden = false; }
}
});
question.append(form); input.focus();
};
start.addEventListener('click', async () => {
try { showQuestion(await api('api/reading-quiz/start', { m: P.get('m') })); start.hidden = true; message.textContent = ''; }
catch (error) { message.textContent = error.message; }
try { showQuestion(await api('api/reading-quiz/start', { m: P.get('m') })); start.hidden = true; setMessage(''); }
catch (error) { setMessage(error.message, 'error'); }
});
if (!access.quizAvailable) { start.hidden = true; message.textContent = 'Fewer than five questions match your completed chapters. Please return during reading hours.'; }
if (!access.quizAvailable) { start.hidden = true; setMessage('Fewer than five questions match your completed chapters. Please return during reading hours.', 'info'); }
window.addEventListener('pagehide', () => {
if (challenge) navigator.sendBeacon('api/reading-quiz/abandon', new Blob([JSON.stringify({ challenge })], { type: 'application/json' }));
});
+20
View File
@@ -109,6 +109,26 @@ let keep = false;
await page.getByRole('heading', { name: 'Reading is closed right now' }).waitFor();
await page.getByRole('button', { name: 'Start five-question challenge' }).click();
await page.getByText('Question 1 of 5').waitFor();
const firstPrompt = await page.locator('.reading-gate-question h2').textContent();
const firstTitle = await page.locator('.reading-gate-question .muted').textContent();
const firstNumber = /question (\d)/.exec(firstPrompt)[1];
const answerBox = page.getByRole('textbox', { name: 'Your answer' });
await answerBox.fill('wrong');
await page.getByRole('button', { name: 'Check answer' }).click();
const retryNotice = page.locator('.reading-gate-message[data-tone="retry"]');
await retryNotice.waitFor();
const retryStyle = await retryNotice.evaluate(el => ({ color: getComputedStyle(el).color,
border: getComputedStyle(el).borderTopWidth, background: getComputedStyle(el).backgroundColor }));
assert.notEqual(retryStyle.border, '0px');
await answerBox.fill((firstTitle === 'Moon Garden' ? 'moonanswer' : 'rainanswer') + firstNumber);
await page.getByRole('button', { name: 'Check answer' }).click();
const successNotice = page.locator('.reading-gate-message[data-tone="success"]');
await successNotice.waitFor();
const successStyle = await successNotice.evaluate(el => ({ color: getComputedStyle(el).color,
background: getComputedStyle(el).backgroundColor }));
assert.notEqual(successStyle.color, retryStyle.color);
assert.notEqual(successStyle.background, retryStyle.background);
await page.getByText('Question 2 of 5').waitFor();
await page.goto(url + '/manga.html?m=' + encodeURIComponent(manga));
await page.goto(url + '/read.html?m=' + encodeURIComponent(manga) + '&c=' + encodeURIComponent(chapter));
await page.getByRole('button', { name: 'Start five-question challenge' }).waitFor();