From 28828a2e93de32e758b62107f0af0a429b911b90 Mon Sep 17 00:00:00 2001 From: Cyra Westmere Date: Sun, 30 Mar 2025 19:29:55 -0700 Subject: [PATCH] web/js: Added a wait with button continue + 30 second auto continue after 30s if you click "Why am I seeing this? (#166) * web/js: update page to allow users to read the "Why am I seeing this?", complete with a button to send them through after challenge completed, and a 30s timeout that does the same. * .gitignore: added .DS_store. * docs/docs/CHANGELOG: added to the Unreleased section as requested in code quality guidelines * web: pushing index_templ.go alongside this update. * package.json: added postcss to dependencies list. * package-lock: added postcss to dependencies * Revert "package-lock: added postcss to dependencies" This reverts commit bf02e7ba56e8bf8705821d4f4864c66b1ef614bf. * Revert "package.json: added postcss to dependencies list." This reverts commit 1a38c63049dc75099dc652ed725c7862eef4b3e4. * web/js: OG comments are important --------- Signed-off-by: Xe Iaso Co-authored-by: Xe Iaso --- .gitignore | 5 +- docs/docs/CHANGELOG.md | 1 + web/index_templ.go | 4 +- web/js/main.mjs | 138 +++++++++++++++++++++++++++++++++-------- xess/xess_templ.go | 2 +- 5 files changed, 119 insertions(+), 31 deletions(-) diff --git a/.gitignore b/.gitignore index 5194493..9c21c75 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,7 @@ main *.test -node_modules \ No newline at end of file +node_modules + +# MacOS +.DS_store \ No newline at end of file diff --git a/docs/docs/CHANGELOG.md b/docs/docs/CHANGELOG.md index eeab0ef..931ab9d 100644 --- a/docs/docs/CHANGELOG.md +++ b/docs/docs/CHANGELOG.md @@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Use `TrimSuffix` instead of `TrimRight` on containerbuild - Fix the startup logs to correctly show the address and port the server is listening on - Add [LibreJS](https://www.gnu.org/software/librejs/) banner to Anubis JavaScript to allow LibreJS users to run the challenge +- Added a wait with button continue + 30 second auto continue after 30s if you click "Why am I seeing this?" - Fixed a typo in the challenge page title. - Disabled running integration tests on Windows hosts due to it's reliance on posix features (see [#133](https://github.com/TecharoHQ/anubis/pull/133#issuecomment-2764732309)). diff --git a/web/index_templ.go b/web/index_templ.go index 2e3ac49..eb99b3a 100644 --- a/web/index_templ.go +++ b/web/index_templ.go @@ -251,7 +251,7 @@ func bench() templ.Component { templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs("/.within.website/x/cmd/anubis/static/img/pensive.webp?cacheBuster=" + anubis.Version) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 247, Col: 19} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 148, Col: 19} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13)) if templ_7745c5c3_Err != nil { @@ -264,7 +264,7 @@ func bench() templ.Component { var templ_7745c5c3_Var14 string templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs("/.within.website/x/cmd/anubis/static/js/bench.mjs?cacheBuster=" + anubis.Version) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 250, Col: 118} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 151, Col: 118} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14)) if templ_7745c5c3_Err != nil { diff --git a/web/js/main.mjs b/web/js/main.mjs index 3203e4a..7a86504 100644 --- a/web/js/main.mjs +++ b/web/js/main.mjs @@ -10,10 +10,7 @@ const algorithms = { // from Xeact const u = (url = "", params = {}) => { let result = new URL(url, window.location.href); - Object.entries(params).forEach((kv) => { - let [k, v] = kv; - result.searchParams.set(k, v); - }); + Object.entries(params).forEach(([k, v]) => result.searchParams.set(k, v)); return result.toString(); }; @@ -33,16 +30,69 @@ const dependencies = [ }, ]; +function showContinueBar(hash, nonce, t0, t1) { + const barContainer = document.createElement("div"); + barContainer.style.marginTop = "1rem"; + barContainer.style.width = "100%"; + barContainer.style.maxWidth = "32rem"; + barContainer.style.background = "#3c3836"; + barContainer.style.borderRadius = "4px"; + barContainer.style.overflow = "hidden"; + barContainer.style.cursor = "pointer"; + barContainer.style.height = "2rem"; + barContainer.style.marginLeft = "auto"; + barContainer.style.marginRight = "auto"; + barContainer.title = "Click to continue"; + + const barInner = document.createElement("div"); + barInner.className = "bar-inner"; + barInner.style.display = "flex"; + barInner.style.alignItems = "center"; + barInner.style.justifyContent = "center"; + barInner.style.color = "white"; + barInner.style.fontWeight = "bold"; + barInner.style.height = "100%"; + barInner.style.width = "0"; + barInner.innerText = "I've finished reading, continue →"; + + barContainer.appendChild(barInner); + document.body.appendChild(barContainer); + + requestAnimationFrame(() => { + barInner.style.width = "100%"; + }); + + barContainer.onclick = () => { + const redir = window.location.href; + window.location.replace( + u("/.within.website/x/cmd/anubis/api/pass-challenge", { + response: hash, + nonce, + redir, + elapsedTime: t1 - t0 + }) + ); + }; +} + (async () => { const status = document.getElementById('status'); const image = document.getElementById('image'); const title = document.getElementById('title'); const progress = document.getElementById('progress'); const anubisVersion = JSON.parse(document.getElementById('anubis_version').textContent); + const details = document.querySelector('details'); + let userReadDetails = false; - const ohNoes = ({ - titleMsg, statusMsg, imageSrc, - }) => { + if (details) { + details.addEventListener("toggle", () => { + if (details.open) { + userReadDetails = true; + } + }); + } + + const ohNoes = ({ titleMsg, statusMsg, imageSrc }) => { title.innerHTML = titleMsg; status.innerHTML = statusMsg; image.src = imageSrc; @@ -73,22 +123,19 @@ const dependencies = [ status.innerHTML = 'Calculating...'; - for (const val of dependencies) { - const { value, name, msg } = val; + for (const { value, name, msg } of dependencies) { if (!value) { ohNoes({ titleMsg: `Missing feature ${name}`, statusMsg: msg, imageSrc: imageURL("sad", anubisVersion), - }) + }); } } const { challenge, rules } = await fetch("/.within.website/x/cmd/anubis/api/make-challenge", { method: "POST" }) .then(r => { - if (!r.ok) { - throw new Error("Failed to fetch config"); - } + if (!r.ok) throw new Error("Failed to fetch config"); return r.json(); }) .catch(err => { @@ -112,7 +159,7 @@ const dependencies = [ status.innerHTML = `Calculating...
Difficulty: ${rules.report_as}, `; progress.style.display = "inline-block"; - + // the whole text, including "Speed:", as a single node, because some browsers // (Firefox mobile) present screen readers with each node as a separate piece // of text. @@ -122,6 +169,7 @@ const dependencies = [ let lastSpeedUpdate = 0; let showingApology = false; const likelihood = Math.pow(16, -rules.report_as); + try { const t0 = Date.now(); const { hash, nonce } = await process( @@ -135,12 +183,12 @@ const dependencies = [ lastSpeedUpdate = delta; rateText.data = `Speed: ${(iters / delta).toFixed(3)}kH/s`; } - // the probability of still being on the page is (1 - likelihood) ^ iters. // by definition, half of the time the progress bar only gets to half, so // apply a polynomial ease-out function to move faster in the beginning // and then slow down as things get increasingly unlikely. quadratic felt // the best in testing, but this may need adjustment in the future. + const probability = Math.pow(1 - likelihood, iters); const distance = (1 - Math.pow(probability, 2)) * 100; progress["aria-valuenow"] = distance; @@ -165,18 +213,54 @@ const dependencies = [ image.src = imageURL("happy", anubisVersion); progress.style.display = "none"; - setTimeout(() => { - const redir = window.location.href; - - window.location.replace( - u("/.within.website/x/cmd/anubis/api/pass-challenge", { - response: hash, - nonce, - redir, - elapsedTime: t1 - t0 - }), - ); - }, 250); + if (userReadDetails) { + const container = document.getElementById("progress"); + + // Style progress bar as a continue button + container.style.display = "flex"; + container.style.alignItems = "center"; + container.style.justifyContent = "center"; + container.style.height = "2rem"; + container.style.borderRadius = "1rem"; + container.style.cursor = "pointer"; + container.style.background = "#b16286"; + container.style.color = "white"; + container.style.fontWeight = "bold"; + container.style.outline = "4px solid #b16286"; + container.style.outlineOffset = "2px"; + container.style.width = "min(20rem, 90%)"; + container.style.margin = "1rem auto 2rem"; + container.innerHTML = "I've finished reading, continue →"; + + function onDetailsExpand() { + const redir = window.location.href; + window.location.replace( + u("/.within.website/x/cmd/anubis/api/pass-challenge", { + response: hash, + nonce, + redir, + elapsedTime: t1 - t0 + }), + ); + } + + container.onclick = onDetailsExpand; + setTimeout(onDetailsExpand, 30000); + + } else { + setTimeout(() => { + const redir = window.location.href; + window.location.replace( + u("/.within.website/x/cmd/anubis/api/pass-challenge", { + response: hash, + nonce, + redir, + elapsedTime: t1 - t0 + }), + ); + }, 250); + } + } catch (err) { ohNoes({ titleMsg: "Calculation error!", diff --git a/xess/xess_templ.go b/xess/xess_templ.go index c1fa412..a01a382 100644 --- a/xess/xess_templ.go +++ b/xess/xess_templ.go @@ -1,6 +1,6 @@ // Code generated by templ - DO NOT EDIT. -// templ: version: v0.3.857 +// templ: version: v0.3.833 package xess //lint:file-ignore SA4006 This context is only used if a nested component is present.