diff --git a/installer-rp/renderer.js b/installer-rp/renderer.js index 34e0d38..5e789cb 100644 --- a/installer-rp/renderer.js +++ b/installer-rp/renderer.js @@ -189,76 +189,110 @@ function showAgreementError(message) { section.querySelector('#retry').addEventListener('click', renderAgreement) } +// 약관을 한 건씩 페이지로 보여준다. 각 약관마다 "해당 약관에 동의합니다" 체크가 있고, +// 끝까지 스크롤해 읽어야 체크할 수 있으며, [다음] 을 누르면 다음 약관 페이지가 화면 +// 맨 위에서부터 나온다. 마지막 약관까지 동의하면 다음 단계(step2)로 진행한다. function renderAgreementWithKinds(KINDS) { + var idx = 0 + var accepted = {} // kind -> true (뒤로 갔다 와도 동의 상태 유지) + var cache = {} // kind -> 렌더된 HTML + var section = document.createElement('section') section.className = 'page' - section.innerHTML = - '
' + escapeHtml(tt('agreement.intro')) + '
' + - '' + escapeHtml(tt('agreement.stepLabel', { current: idx + 1, total: KINDS.length, label: k.tab })) + '
' + + '' + escapeHtml(tt('agreement.loadFailed', { message: res.message || '' })) + '
' + // 새 약관 페이지는 화면 맨 위에서부터 보이도록 스크롤을 올린다. + if (pageHost) pageHost.scrollTop = 0 + + function markReadable() { + if (!accept.disabled) return + accept.disabled = false + hint.textContent = '' + } + function checkScrolled() { + if (body.scrollHeight - body.scrollTop - body.clientHeight <= 6) markReadable() + } + body.addEventListener('scroll', checkScrolled) + + accept.addEventListener('change', function () { + accepted[k.id] = accept.checked + nextBtn.disabled = !accept.checked + if (accept.checked) { msg.textContent = ''; msg.classList.remove('error') } + }) + + nextBtn.addEventListener('click', function () { + if (!accept.checked) { + msg.textContent = tt('agreement.agreeRequired') + msg.classList.add('error') return } - var html = renderTermsMarkdown(res.content || '') - cache[kind] = html - body.innerHTML = html - }).catch(function (err) { - body.innerHTML = '' + escapeHtml(tt('agreement.loadFailed', { message: err.message })) + '
' + if (isLast) renderStep2() + else { idx++; renderCurrent() } }) + section.querySelector('#back').addEventListener('click', function () { + if (idx === 0) renderStep1() + else { idx--; renderCurrent() } + }) + + function afterLoad() { + body.scrollTop = 0 + if (accepted[k.id]) { + accept.disabled = false + accept.checked = true + nextBtn.disabled = false + hint.textContent = '' + } + setTimeout(function () { + if (body.scrollHeight - body.clientHeight <= 6) markReadable() + else checkScrolled() + }, 0) + } + + if (cache[k.id]) { + body.innerHTML = cache[k.id] + afterLoad() + } else { + body.textContent = tt('agreement.loading') + api.getTerm(k.id).then(function (res) { + if (!res.ok) { + body.innerHTML = '' + escapeHtml(tt('agreement.loadFailed', { message: res.message || '' })) + '
' + markReadable() + return + } + var html = renderTermsMarkdown(res.content || '') + cache[k.id] = html + body.innerHTML = html + afterLoad() + }).catch(function (err) { + body.innerHTML = '' + escapeHtml(tt('agreement.loadFailed', { message: err.message })) + '
' + markReadable() + }) + } } - tabs.forEach(function (b) { - b.addEventListener('click', function () { - tabs.forEach(function (x) { x.classList.remove('active') }) - b.classList.add('active') - showKind(b.getAttribute('data-ag')) - }) - }) - - accept.addEventListener('change', function () { - nextBtn.disabled = !accept.checked - if (accept.checked) msg.textContent = '' - }) - - nextBtn.addEventListener('click', function () { - if (!accept.checked) { - msg.textContent = tt('agreement.agreeRequired') - msg.classList.add('error') - return - } - renderStep2() - }) - section.querySelector('#back').addEventListener('click', renderStep1) - - showKind(KINDS[0].id) + renderCurrent() } // 인스톨러용 미니 markdown 렌더러. 사이트 termsEditor 와 같은 규칙을 처리한다. diff --git a/installer/renderer.js b/installer/renderer.js index d124519..6da0a9a 100644 --- a/installer/renderer.js +++ b/installer/renderer.js @@ -232,76 +232,110 @@ function showAgreementError(message) { section.querySelector('#retry').addEventListener('click', renderAgreement) } +// 약관을 한 건씩 페이지로 보여준다. 각 약관마다 "해당 약관에 동의합니다" 체크가 있고, +// 끝까지 스크롤해 읽어야 체크할 수 있으며, [다음] 을 누르면 다음 약관 페이지가 화면 +// 맨 위에서부터 나온다. 마지막 약관까지 동의하면 다음 단계(step2)로 진행한다. function renderAgreementWithKinds(KINDS) { + var idx = 0 + var accepted = {} // kind -> true (뒤로 갔다 와도 동의 상태 유지) + var cache = {} // kind -> 렌더된 HTML + var section = document.createElement('section') section.className = 'page' - section.innerHTML = - '' + tt('agreement.intro') + '
' + - '' + escapeHtml(tt('agreement.stepLabel', { current: idx + 1, total: KINDS.length, label: k.tab })) + '
' + + '' + tt('agreement.loadFailed', { message: res.message || '' }) + '
' + function checkScrolled() { + // 본문을 끝까지(하단 근처) 내렸으면 동의 체크를 허용한다. + if (body.scrollHeight - body.scrollTop - body.clientHeight <= 6) markReadable() + } + body.addEventListener('scroll', checkScrolled) + + accept.addEventListener('change', function () { + accepted[k.id] = accept.checked + nextBtn.disabled = !accept.checked + if (accept.checked) { msg.textContent = ''; msg.classList.remove('error') } + }) + + nextBtn.addEventListener('click', function () { + if (!accept.checked) { + msg.textContent = tt('agreement.agreeRequired') + msg.classList.add('error') return } - var html = renderTermsMarkdown(res.content || '') - cache[kind] = html - body.innerHTML = html - }).catch(function (err) { - body.innerHTML = '' + tt('agreement.loadFailed', { message: err.message }) + '
' + if (isLast) renderStep2() + else { idx++; renderCurrent() } }) + section.querySelector('#back').addEventListener('click', function () { + if (idx === 0) renderStep1() + else { idx--; renderCurrent() } + }) + + function afterLoad() { + body.scrollTop = 0 + // 이미 동의했던 약관으로 되돌아온 경우: 체크/다음 활성화 유지. + if (accepted[k.id]) { + accept.disabled = false + accept.checked = true + nextBtn.disabled = false + hint.textContent = '' + } + // 레이아웃 확정 후: 스크롤이 필요 없을 만큼 짧은 약관이면 바로 동의 허용. + setTimeout(function () { + if (body.scrollHeight - body.clientHeight <= 6) markReadable() + else checkScrolled() + }, 0) + } + + if (cache[k.id]) { + body.innerHTML = cache[k.id] + afterLoad() + } else { + body.textContent = tt('agreement.loading') + installerApi.getTerm(k.id).then(function (res) { + if (!res.ok) { + body.innerHTML = '' + tt('agreement.loadFailed', { message: res.message || '' }) + '
' + markReadable() + return + } + var html = renderTermsMarkdown(res.content || '') + cache[k.id] = html + body.innerHTML = html + afterLoad() + }).catch(function (err) { + body.innerHTML = '' + tt('agreement.loadFailed', { message: err.message }) + '
' + markReadable() + }) + } } - tabs.forEach(function (b) { - b.addEventListener('click', function () { - tabs.forEach(function (x) { x.classList.remove('active') }) - b.classList.add('active') - showKind(b.getAttribute('data-ag')) - }) - }) - - accept.addEventListener('change', function () { - nextBtn.disabled = !accept.checked - if (accept.checked) msg.textContent = '' - }) - - nextBtn.addEventListener('click', function () { - if (!accept.checked) { - msg.textContent = tt('agreement.agreeRequired') - msg.classList.add('error') - return - } - renderStep2() - }) - section.querySelector('#back').addEventListener('click', renderStep1) - - showKind(KINDS[0].id) + renderCurrent() } // 인스톨러용 미니 markdown 렌더러. 사이트 termsEditor 와 동일한 규칙을 처리한다. diff --git a/locales/installer-rp/ko-kr.json b/locales/installer-rp/ko-kr.json index 5fe64b0..795d72f 100644 --- a/locales/installer-rp/ko-kr.json +++ b/locales/installer-rp/ko-kr.json @@ -33,7 +33,7 @@ }, "agreement": { "heading": "약관 동의", - "intro": "리소스팩을 설치하기 전에 아래 약관을 모두 확인하고 동의해 주세요.", + "intro": "리소스팩을 설치하기 전에 아래 약관을 한 건씩 끝까지 읽고 동의해 주세요.", "tabResourcepack": "리소스팩 약관", "tabInstaller": "리소스팩 설치기 약관", "loading": "약관을 불러오는 중...", @@ -41,6 +41,9 @@ "listLoadFailed": "약관 표시에 실패하여 설치를 진행할 수 없습니다.\n사유: {{message}}\n네트워크 상태를 확인하고 다시 시도하거나, 처음 단계로 돌아가 주세요.", "retry": "다시 시도", "agreeAll": "위 모든 약관에 동의합니다.", + "agreeThis": "해당 약관에 동의합니다.", + "readToBottom": "약관을 끝까지 내려서 확인하면 동의할 수 있습니다.", + "stepLabel": "약관 {{current}}/{{total}} — {{label}}", "agreeRequired": "약관에 동의해야 다음 단계로 진행할 수 있습니다.", "cancelling": "취소 중…" }, diff --git a/locales/installer/ko-kr.json b/locales/installer/ko-kr.json index 849d0c5..7b14c11 100644 --- a/locales/installer/ko-kr.json +++ b/locales/installer/ko-kr.json @@ -41,7 +41,7 @@ }, "agreement": { "heading": "약관 동의", - "intro": "설치 전에 아래 약관을 모두 확인하고 동의해 주세요.", + "intro": "설치 전에 아래 약관을 한 건씩 끝까지 읽고 동의해 주세요.", "tabMap": "맵 약관", "tabMod": "모드 약관", "tabInstaller": "설치기 약관", @@ -50,6 +50,9 @@ "listLoadFailed": "약관 표시에 실패하여 설치를 진행할 수 없습니다.\n사유: {{message}}\n네트워크 상태를 확인하고 다시 시도하거나, 처음 단계로 돌아가 주세요.", "retry": "다시 시도", "agreeAll": "위 모든 약관에 동의합니다.", + "agreeThis": "해당 약관에 동의합니다.", + "readToBottom": "약관을 끝까지 내려서 확인하면 동의할 수 있습니다.", + "stepLabel": "약관 {{current}}/{{total}} — {{label}}", "agreeRequired": "약관에 동의해야 다음 단계로 진행할 수 있습니다." }, "step1": { diff --git a/package.json b/package.json index 9b526d9..cc30d96 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "minecraft-music-quiz-installer", - "version": "0.4.0", + "version": "0.4.1", "description": "마인크래프트 음악퀴즈 간편설치기 + 관리 사이트", "main": "dist/installer/main.js", "scripts": {