Compare commits

...

1 Commits

Author SHA1 Message Date
10362e204a fix(installer): 약관 본문 로드 실패 시 동의 차단 + 다시 시도
로드 실패 경로에서 markReadable()로 체크박스가 활성화돼, 약관을 못 본 채로
동의할 수 있던 문제 수정. termLoaded 플래그를 두어 본문이 정상 로드된 뒤에만
동의 체크를 허용하고, 실패 시에는 체크/다음을 막고 "다시 시도" 버튼만 노출.
installer / installer-rp 동일 적용. 버전 0.4.1 → 0.4.2.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-16 00:09:51 +09:00
3 changed files with 63 additions and 19 deletions

View File

@@ -226,8 +226,12 @@ function renderAgreementWithKinds(KINDS) {
// 새 약관 페이지는 화면 맨 위에서부터 보이도록 스크롤을 올린다.
if (pageHost) pageHost.scrollTop = 0
// 약관 본문이 정상 로드된 뒤에만 동의를 허용한다. 로드 실패 상태에서는
// 스크롤을 해도 동의 체크가 켜지지 않는다("무조건 약관을 읽도록").
var termLoaded = false
function markReadable() {
if (!accept.disabled) return
if (!termLoaded || !accept.disabled) return
accept.disabled = false
hint.textContent = ''
}
@@ -257,6 +261,7 @@ function renderAgreementWithKinds(KINDS) {
})
function afterLoad() {
termLoaded = true
body.scrollTop = 0
if (accepted[k.id]) {
accept.disabled = false
@@ -270,15 +275,31 @@ function renderAgreementWithKinds(KINDS) {
}, 0)
}
if (cache[k.id]) {
body.innerHTML = cache[k.id]
afterLoad()
} else {
// 약관 본문 로드 실패: 동의 불가 상태를 유지하고 다시 시도만 허용한다.
function showLoadError(message) {
termLoaded = false
accept.disabled = true
accept.checked = false
accepted[k.id] = false
nextBtn.disabled = true
hint.textContent = tt('agreement.readToBottom')
body.innerHTML =
'<p class="formMessage error">' + escapeHtml(tt('agreement.loadFailed', { message: message || '' })) + '</p>' +
'<div class="actionRow" style="margin-top:10px;"><button class="secondaryBtn" id="agRetry">' + escapeHtml(tt('agreement.retry')) + '</button></div>'
var retry = section.querySelector('#agRetry')
if (retry) retry.addEventListener('click', loadTerm)
}
function loadTerm() {
if (cache[k.id]) {
body.innerHTML = cache[k.id]
afterLoad()
return
}
body.textContent = tt('agreement.loading')
api.getTerm(k.id).then(function (res) {
if (!res.ok) {
body.innerHTML = '<p class="formMessage error">' + escapeHtml(tt('agreement.loadFailed', { message: res.message || '' })) + '</p>'
markReadable()
showLoadError(res.message || '')
return
}
var html = renderTermsMarkdown(res.content || '')
@@ -286,10 +307,11 @@ function renderAgreementWithKinds(KINDS) {
body.innerHTML = html
afterLoad()
}).catch(function (err) {
body.innerHTML = '<p class="formMessage error">' + escapeHtml(tt('agreement.loadFailed', { message: err.message })) + '</p>'
markReadable()
showLoadError(err && err.message ? err.message : '')
})
}
loadTerm()
}
renderCurrent()

View File

@@ -266,8 +266,12 @@ function renderAgreementWithKinds(KINDS) {
// 새 약관 페이지는 화면 맨 위에서부터 보이도록 스크롤을 올린다.
if (pageHost) pageHost.scrollTop = 0
// 약관 본문이 정상 로드된 뒤에만 동의를 허용한다. 로드 실패 상태에서는
// 스크롤을 해도 동의 체크가 켜지지 않는다("무조건 약관을 읽도록").
var termLoaded = false
function markReadable() {
if (!accept.disabled) return
if (!termLoaded || !accept.disabled) return
accept.disabled = false
hint.textContent = ''
}
@@ -298,6 +302,7 @@ function renderAgreementWithKinds(KINDS) {
})
function afterLoad() {
termLoaded = true
body.scrollTop = 0
// 이미 동의했던 약관으로 되돌아온 경우: 체크/다음 활성화 유지.
if (accepted[k.id]) {
@@ -313,15 +318,31 @@ function renderAgreementWithKinds(KINDS) {
}, 0)
}
if (cache[k.id]) {
body.innerHTML = cache[k.id]
afterLoad()
} else {
// 약관 본문 로드 실패: 동의 불가 상태를 유지하고 다시 시도만 허용한다.
function showLoadError(message) {
termLoaded = false
accept.disabled = true
accept.checked = false
accepted[k.id] = false
nextBtn.disabled = true
hint.textContent = tt('agreement.readToBottom')
body.innerHTML =
'<p class="formMessage error">' + tt('agreement.loadFailed', { message: message || '' }) + '</p>' +
'<div class="actionRow" style="margin-top:10px;"><button class="secondaryBtn" id="agRetry">' + tt('agreement.retry') + '</button></div>'
var retry = section.querySelector('#agRetry')
if (retry) retry.addEventListener('click', loadTerm)
}
function loadTerm() {
if (cache[k.id]) {
body.innerHTML = cache[k.id]
afterLoad()
return
}
body.textContent = tt('agreement.loading')
installerApi.getTerm(k.id).then(function (res) {
if (!res.ok) {
body.innerHTML = '<p class="formMessage error">' + tt('agreement.loadFailed', { message: res.message || '' }) + '</p>'
markReadable()
showLoadError(res.message || '')
return
}
var html = renderTermsMarkdown(res.content || '')
@@ -329,10 +350,11 @@ function renderAgreementWithKinds(KINDS) {
body.innerHTML = html
afterLoad()
}).catch(function (err) {
body.innerHTML = '<p class="formMessage error">' + tt('agreement.loadFailed', { message: err.message }) + '</p>'
markReadable()
showLoadError(err && err.message ? err.message : '')
})
}
loadTerm()
}
renderCurrent()

View File

@@ -1,6 +1,6 @@
{
"name": "minecraft-music-quiz-installer",
"version": "0.4.1",
"version": "0.4.2",
"description": "마인크래프트 음악퀴즈 간편설치기 + 관리 사이트",
"main": "dist/installer/main.js",
"scripts": {