diff --git a/views/projects/item/fediverse-madness/index.html b/views/projects/item/fediverse-madness/index.html index 6114c94..70992d8 100644 --- a/views/projects/item/fediverse-madness/index.html +++ b/views/projects/item/fediverse-madness/index.html @@ -18,11 +18,12 @@

Fediverse Madness

Competitive bracket-based comparisons of Fediverse users.

+

Requires JavaScript.

-
+ @@ -86,9 +95,9 @@

-

+
@@ -96,9 +105,9 @@

-

+
@@ -114,9 +123,9 @@

-

+

Bracket

@@ -129,10 +138,7 @@ diff --git a/views/projects/item/fediverse-madness/scripts/game.js b/views/projects/item/fediverse-madness/scripts/game.js index 57e9626..bfb3094 100644 --- a/views/projects/item/fediverse-madness/scripts/game.js +++ b/views/projects/item/fediverse-madness/scripts/game.js @@ -47,6 +47,8 @@ async function verify() { const user_req = await fetch(`https://${domain_url}/api/v1/accounts/lookup?acct=${username}`).catch(n); if (user_req == null || !domain.ok) return null; const user_json = await user_req.json(); + + window.localStorage.setItem("fediversemadness_game", JSON.stringify({ username, instance })); return { USER_ID: user_json.id, @@ -54,21 +56,27 @@ async function verify() { }; } +async function lookupUser() { + const lsGame = JSON.parse(window.localStorage.getItem("fediversemadness_game")); + if (lsGame != null) { + el_id_user.value = lsGame.username; + el_id_instance.value = lsGame.instance; + } +} + +lookupUser(); + el_id_submitwhoamiFollowers.addEventListener("click", async (e) => { el_id_errorwhoami.innerHTML = ""; el_id_submitwhoamiFollowers.disabled = true; el_id_submitwhoamiFollowing.disabled = true; - const result = await verify(); + game = await verify(); el_id_submitwhoamiFollowers.disabled = false; el_id_submitwhoamiFollowing.disabled = false; - if (result == null) { + if (game == null) { el_id_errorwhoami.innerHTML = "Invalid user!"; return false; } - game = { - ...game, - ...result - }; gamemodeFollowers = true; getFollowers(); @@ -76,15 +84,15 @@ el_id_submitwhoamiFollowers.addEventListener("click", async (e) => { el_id_submitwhoamiFollowing.addEventListener("click", async (e) => { el_id_errorwhoami.innerHTML = ""; - const result = await verify(); - if (result == null) { + el_id_submitwhoamiFollowers.disabled = true; + el_id_submitwhoamiFollowing.disabled = true; + game = await verify(); + el_id_submitwhoamiFollowers.disabled = false; + el_id_submitwhoamiFollowing.disabled = false; + if (game == null) { el_id_errorwhoami.innerHTML = "Invalid user!"; return false; } - game = { - ...game, - ...result - }; gamemodeFollowers = false; getFollowing(); @@ -102,22 +110,49 @@ const el_id_deselectrandomfollowers = document.querySelector("#deselectrandomfol const el_id_selectnofollowers = document.querySelector("#selectnofollowers"); const el_id_morefollowers = document.querySelector("#morefollowers"); const el_id_submitfollowers = document.querySelector("#submitfollowers"); +const el_id_savehandlesfollowers = document.querySelector("#savehandlesfollowers"); +const el_id_loadhandlesfollowers = document.querySelector("#loadhandlesfollowers"); let selectboxes = []; let userList = []; let lastId = ""; let selectedUsers = []; -function renderNameHTML(name, user) { - return escapeHtml(name).replace(/:([a-z0-9_-]+?):/gim, (m, p1) => { - const emoji = user.emojis.find(x => x.shortcode == p1); - if (emoji == null) return null; - return ``; +function generateFollowersMap() { + const checks = Array.from(el_id_listfollowers.querySelectorAll(".followers_checkbox")); + let codeMap = {}; + checks.forEach((checkbox) => { + const handle = checkbox.parentElement.parentElement.querySelector(".followers_checkname"); + codeMap[handle.innerHTML] = checkbox.checked; + }); + return Object.fromEntries(codeMap.entries()); +} + +function readFollowersMap(m) { + if (m == null) return; + const handles = Array.from(el_id_listfollowers.querySelectorAll(".followers_checkname")); + handles.forEach((handle) => { + const checkbox = handle.parentElement.parentElement.querySelector(".followers_checkbox"); + checkbox.checked = m[handle.innerHTML]; }); } +function renderNameHTML(name, user) { + return `${escapeHtml(name).replace(/:([a-z0-9_-]+?):/gim, (m, p1) => { + const emoji = user.emojis.find(x => x.shortcode == p1); + if (emoji == null) return null; + return ``; + })}`; +} + async function getFollowers(dontLoadNew = false) { - el_id_whoami.hidden = true; - el_id_followers.hidden = false; + goToState("followers"); + if (!dontLoadNew) { + selectboxes = []; + userList = []; + lastId = ""; + selectedUsers = []; + el_id_listfollowers.innerHTML = ""; + } el_id_submitfollowers.disabled = true; el_id_morefollowers.disabled = true; let res; @@ -144,7 +179,7 @@ async function getFollowers(dontLoadNew = false) { ${renderNameHTML(cuser.display_name, cuser)} - @${cuser.fqn || cuser.acct} + @${cuser.fqn || cuser.acct} ` }, ""); userList.push(...out.map(user => ({ @@ -157,8 +192,7 @@ async function getFollowers(dontLoadNew = false) { fields: user.fields, id: user.id, note: user.note, - username: user.url, - username: user.username + url: user.url }))); lastId = userList.at(-1).id; selectboxes = Array.from(el_id_listfollowers.querySelectorAll(".follower_checkbox")); @@ -192,15 +226,17 @@ el_id_selectnofollowers.addEventListener("click", () => { checkSelectedAmtFollowers(); }) +el_id_savehandlesfollowers.addEventListener("click", () => window.localStorage.setItem("fediversemadness_followerssel", JSON.stringify(generateFollowersMap()))); +el_id_loadhandlesfollowers.addEventListener("click", () => readFollowersMap(JSON.parse(window.localStorage.getItem("fediversemadness_followerssel")))); + el_id_morefollowers.addEventListener("click", () => getFollowers()); el_id_submitfollowers.addEventListener("click", () => { - console.log(selectedUsers); + selectedUsers = []; selectboxes.forEach(({checked}, i) => { if (checked) selectedUsers.push(userList.at(i)); }); if (selectedUsers.length < 2) { - selectedUsers = []; el_id_loadingfollowers.innerHTML = `Selected user count is less than 2! (${selectboxes.filter(x => x.checked).length} selected)`; return; } @@ -223,17 +259,44 @@ const el_id_deselectrandomfollowing = document.querySelector("#deselectrandomfol const el_id_selectnofollowing = document.querySelector("#selectnofollowing"); const el_id_morefollowing = document.querySelector("#morefollowing"); const el_id_submitfollowing = document.querySelector("#submitfollowing"); +const el_id_savehandlesfollowing = document.querySelector("#savehandlesfollowing"); +const el_id_loadhandlesfollowing = document.querySelector("#loadhandlesfollowing"); + +function generateFollowingMap() { + const checks = Array.from(el_id_listfollowing.querySelectorAll(".following_checkbox")); + let codeMap = new Map(); + checks.forEach((checkbox) => { + const handle = checkbox.parentElement.parentElement.querySelector(".following_checkname"); + codeMap.set(handle.innerHTML, checkbox.checked); + }); + return Object.fromEntries(codeMap.entries()); +} + +function readFollowingMap(m) { + if (m == null) return; + const handles = Array.from(el_id_listfollowing.querySelectorAll(".following_checkname")); + handles.forEach((handle) => { + const checkbox = handle.parentElement.parentElement.querySelector(".following_checkbox"); + checkbox.checked = m[handle.innerHTML]; + }); +} async function getFollowing(dontLoadNew = false) { - el_id_whoami.hidden = true; - el_id_following.hidden = false; + goToState("following"); + if (!dontLoadNew) { + selectboxes = []; + userList = []; + lastId = ""; + selectedUsers = []; + el_id_listfollowers.innerHTML = ""; + } el_id_submitfollowing.disabled = true; el_id_morefollowing.disabled = true; let res; let out; if (!dontLoadNew) { el_id_loadingfollowing.innerHTML = `Retrieving following... Please wait.`; - res = await fetch(`https://${game.INSTANCE}/api/v1/accounts/${game.USER_ID}/following?limit=68${lastId.length > 0 ? `&max_id=${lastId}` : ""}`); + res = await fetch(`https://${game.INSTANCE}/api/v1/accounts/${game.USER_ID}/following${lastId.length > 0 ? `?max_id=${lastId}` : ""}`); out = await res.json(); } el_id_submitfollowing.disabled = false; @@ -246,14 +309,14 @@ async function getFollowing(dontLoadNew = false) { el_id_listfollowing.hidden = false; el_id_listfollowing.innerHTML += out.reduce((pv, cuser) => { return pv + ` - + ${renderNameHTML(cuser.display_name, cuser)} - @${cuser.fqn} + @${cuser.fqn} ` }, ""); userList.push(...out.map(user => ({ @@ -266,11 +329,10 @@ async function getFollowing(dontLoadNew = false) { fields: user.fields, id: user.id, note: user.note, - username: user.url, - username: user.username + url: user.url }))); lastId = userList.at(-1).id; - selectboxes = Array.from(el_id_listfollowing.querySelectorAll(".follower_checkbox")); + selectboxes = Array.from(el_id_listfollowing.querySelectorAll(".following_checkbox")); selectboxes.forEach(x => x.addEventListener("change", checkSelectedAmtFollowing)); checkSelectedAmtFollowing(); } @@ -301,15 +363,17 @@ el_id_selectnofollowing.addEventListener("click", () => { checkSelectedAmtFollowing(); }) +el_id_savehandlesfollowing.addEventListener("click", () => window.localStorage.setItem("fediversemadness_followingsel", JSON.stringify(generateFollowingMap()))); +el_id_loadhandlesfollowing.addEventListener("click", () => readFollowingMap(JSON.parse(window.localStorage.getItem("fediversemadness_followingsel")))); + el_id_morefollowing.addEventListener("click", () => getFollowing()); el_id_submitfollowing.addEventListener("click", () => { - console.log(selectedUsers); + selectedUsers = []; selectboxes.forEach(({checked}, i) => { if (checked) selectedUsers.push(userList.at(i)); }); if (selectedUsers.length < 2) { - selectedUsers = []; el_id_loadingfollowing.innerHTML = `Selected user count is less than 2! (${selectboxes.filter(x => x.checked).length} selected)`; return; } @@ -346,14 +410,14 @@ const el_id_gameUserOne = document.querySelector("#gameUserOne"); const el_id_gameUserOneImage = document.querySelector("#gameUserOneImage"); const el_id_gameUserOneName = document.querySelector("#gameUserOneName"); const el_id_gameUserOneId = document.querySelector("#gameUserOneId"); -const el_id_gameUserOneNote = document.querySelector("#gameUserOneNote"); -const el_id_gameUserOneFields = document.querySelector("#gameUserOneFields"); +// const el_id_gameUserOneNote = document.querySelector("#gameUserOneNote"); +// const el_id_gameUserOneFields = document.querySelector("#gameUserOneFields"); const el_id_gameUserTwo = document.querySelector("#gameUserTwo"); const el_id_gameUserTwoImage = document.querySelector("#gameUserTwoImage"); const el_id_gameUserTwoName = document.querySelector("#gameUserTwoName"); const el_id_gameUserTwoId = document.querySelector("#gameUserTwoId"); -const el_id_gameUserTwoNote = document.querySelector("#gameUserTwoNote"); -const el_id_gameUserTwoFields = document.querySelector("#gameUserTwoFields"); +// const el_id_gameUserTwoNote = document.querySelector("#gameUserTwoNote"); +// const el_id_gameUserTwoFields = document.querySelector("#gameUserTwoFields"); const el_id_gameSubmitLeft = document.querySelector("#gameSubmitLeft"); const el_id_gameSubmitRight = document.querySelector("#gameSubmitRight"); const el_id_gameBracketLevel = document.querySelector("#gameBracketLevel"); @@ -364,19 +428,16 @@ let curDepth = 0; let curFight = 0; function sortSelectedUsers() { - if (gamemodeFollowers) - el_id_followers.hidden = true; - else - el_id_following.hidden = true; - el_id_game.hidden = false; + goToState("game"); + state = []; + curDepth = 0; + curFight = 0; state.push(chunk(shuf(selectedUsers), 2)); state.push([]); prepareGameStage(); } function prepareGameStage() { - el_id_gameSubmitLeft.disabled = true; - el_id_gameSubmitRight.disabled = true; el_id_gameSubmitLeft.innerHTML = "Please wait..."; el_id_gameSubmitRight.innerHTML = "Please wait..."; el_id_gameUserOneImage.src = ""; @@ -385,15 +446,15 @@ function prepareGameStage() { el_id_gameBracketFight.innerHTML = "Round " + (curFight + 1); const curSubStage = state[curDepth][curFight]; el_id_gameUserOneImage.src = curSubStage[0].avatar; - el_id_gameUserOneName.innerHTML = renderNameHTML(escapeHtml(curSubStage[0].display_name), curSubStage[0]); + el_id_gameUserOneName.innerHTML = `${renderNameHTML(escapeHtml(curSubStage[0].display_name), curSubStage[0])}`; el_id_gameUserOneId.innerHTML = "@" + curSubStage[0].fqn; - el_id_gameUserOneNote.innerHTML = escapeHtml(curSubStage[0].note); - el_id_gameUserOneFields.innerHTML = curSubStage[0].fields.reduce((pv, fields) => pv + `${escapeHtml(fields.name)}${escapeHtml(fields.value)}`, ""); + // el_id_gameUserOneNote.innerHTML = escapeHtml(curSubStage[0].note); + // el_id_gameUserOneFields.innerHTML = curSubStage[0].fields.reduce((pv, fields) => pv + `${escapeHtml(fields.name)}${escapeHtml(fields.value)}`, ""); el_id_gameUserTwoImage.src = curSubStage[1].avatar; - el_id_gameUserTwoName.innerHTML = renderNameHTML(escapeHtml(curSubStage[1].display_name), curSubStage[1]); + el_id_gameUserTwoName.innerHTML = `${renderNameHTML(escapeHtml(curSubStage[1].display_name), curSubStage[1])}`; el_id_gameUserTwoId.innerHTML = "@" + curSubStage[1].fqn; - el_id_gameUserTwoNote.innerHTML = escapeHtml(curSubStage[1].note); - el_id_gameUserTwoFields.innerHTML = curSubStage[1].fields.reduce((pv, fields) => pv + `${escapeHtml(fields.name)}${escapeHtml(fields.value)}`, ""); + // el_id_gameUserTwoNote.innerHTML = escapeHtml(curSubStage[1].note); + // el_id_gameUserTwoFields.innerHTML = curSubStage[1].fields.reduce((pv, fields) => pv + `${escapeHtml(fields.name)}${escapeHtml(fields.value)}`, ""); el_id_gameSubmitLeft.disabled = false; el_id_gameSubmitRight.disabled = false; el_id_gameSubmitLeft.innerHTML = "Pick " + "@" + curSubStage[0].fqn; @@ -442,21 +503,20 @@ const el_id_winnerUser = document.querySelector("#winnerUser"); const el_id_winnerUserImage = document.querySelector("#winnerUserImage"); const el_id_winnerUserName = document.querySelector("#winnerUserName"); const el_id_winnerUserId = document.querySelector("#winnerUserId"); -const el_id_winnerUserNote = document.querySelector("#winnerUserNote"); -const el_id_winnerUserFields = document.querySelector("#winnerUserFields"); +// const el_id_winnerUserNote = document.querySelector("#winnerUserNote"); +// const el_id_winnerUserFields = document.querySelector("#winnerUserFields"); const el_id_winnermessage = document.querySelector("#winnermessage"); const el_id_winnerusers = document.querySelector("#winnerusers"); const el_id_winnerPlayAgain = document.querySelector("#winnerPlayAgain"); function endGame() { - el_id_game.hidden = true; - el_id_winner.hidden = false; + goToState("winner"); const winningPlayer = state[curDepth][curFight][0]; el_id_winnerUserImage.src = winningPlayer.avatar; - el_id_winnerUserName.innerHTML = renderNameHTML(escapeHtml(winningPlayer.display_name), winningPlayer); + el_id_winnerUserName.innerHTML = `${renderNameHTML(escapeHtml(winningPlayer.display_name), winningPlayer)}`; el_id_winnerUserId.innerHTML = "@" + winningPlayer.fqn; - el_id_winnerUserNote.innerHTML = escapeHtml(winningPlayer.note); - el_id_winnerUserFields.innerHTML = winningPlayer.fields.reduce((pv, fields) => pv + `${escapeHtml(fields.name)}${escapeHtml(fields.value)}`, ""); + // el_id_winnerUserNote.innerHTML = escapeHtml(winningPlayer.note); + // el_id_winnerUserFields.innerHTML = winningPlayer.fields.reduce((pv, fields) => pv + `${escapeHtml(fields.name)}${escapeHtml(fields.value)}`, ""); el_id_winnermessage.innerHTML = `Congratulations, ${renderNameHTML(escapeHtml(winningPlayer.display_name), winningPlayer)}! You won the bracket!`; el_id_winnerusers.innerHTML = `
${state.reduce((pv, cs) => { @@ -464,7 +524,7 @@ function endGame() { return pvus + cus.reduce((pvu, cu) => { return pvu + `
-

${renderNameHTML(escapeHtml(cu.display_name), cu)}

+

${renderNameHTML(escapeHtml(cu.display_name), cu)}

@${cu.fqn}

` }, "") @@ -473,7 +533,6 @@ function endGame() { } el_id_winnerPlayAgain.addEventListener("click", (x) => { - el_id_winner.hidden = true; state = []; selectedUsers = []; curDepth = 0; @@ -484,11 +543,41 @@ el_id_winnerPlayAgain.addEventListener("click", (x) => { getFollowing(true); }) -function generateFollowersCode() { - return Array.from(document.querySelectorAll(".follower_checkbox")).reduce((p,x)=>p+(x.checked?"1":"0"),""); +// Game state + +const gameStates = new Map([ + ["whoami", { + state: el_id_whoami + }], + ["followers", { + state: el_id_followers + }], + ["following", { + state: el_id_following + }], + ["game", { + state: el_id_game, + links: { + "winner": () => gamemodeFollowers ? "followers" : "following" + } + }], + ["winner", { + state: el_id_winner + }] +]); + +let prevState = ""; +function goToState(state) { + window.location.hash = state; + const curState = gameStates.get(state); + if (curState.links != null) + if (curState.links[prevState]) + return goToState(curState.links[prevState]()); + gameStates.forEach(x => x.state.hidden = true); + curState.state.hidden = false; + prevState = state; } -function readFollowersCode(c, o = 0) { - const array = Array.from(document.querySelectorAll(".follower_checkbox")); - c.split("").forEach((x, i) => selectboxes[i + o].checked = parseInt(x)); -} \ No newline at end of file +goToState("whoami"); + +addEventListener("popstate", () => goToState(window.location.hash.slice(1))); \ No newline at end of file