diff --git a/features/features.json b/features/features.json index a344f61e..ed3e4b16 100644 --- a/features/features.json +++ b/features/features.json @@ -1,4 +1,9 @@ [ + { + "version": 2, + "id": "total-stats", + "versionAdded": "v4.2.0" + }, { "version": 2, "id": "copy-paste-lists", @@ -159,11 +164,6 @@ "id": "project-descriptions", "versionAdded": "v3.8.0" }, - { - "version": 2, - "id": "total-stats", - "versionAdded": "v3.8.0" - }, { "version": 2, "id": "project-miniplayer", diff --git a/features/total-stats/data.json b/features/total-stats/data.json index a4fbb550..8fde2478 100644 --- a/features/total-stats/data.json +++ b/features/total-stats/data.json @@ -1,33 +1,25 @@ { - "title": "Total User Stats", - "description": "Next to shared projects on a user's profile, displays the total loves, favorites and views that the user has received across all of their shared projects.", - "credits": [ - { - "username": "RowanMoonBoy", - "url": "https://scratch.mit.edu/users/RowanMoonBoy/" - }, - { - "username": "rgantzos", - "url": "https://scratch.mit.edu/users/rgantzos/" - } - ], - "dynamic": true, - "styles": [ - { - "file": "style.css", - "runOn": "/users/*" - } - ], - "scripts": [ - { - "file": "script.js", - "runOn": "/users/*" - } - ], - "tags": [ - "New" - ], - "type": [ - "Website" - ] -} \ No newline at end of file + "title": "Total Project Stats", + "description": "Displays a box on user profiles showing the total loves, favorites, views, and remixes across all their shared projects.", + "credits": [ + { + "username": "MaterArc", + "url": "https://scratch.mit.edu/users/MaterArc/" + } + ], + "type": ["Website"], + "tags": ["New", "Featured"], + "dynamic": true, + "styles": [ + { + "file": "style.css", + "runOn": "/users/*" + } + ], + "scripts": [ + { + "file": "script.js", + "runOn": "/users/*" + } + ] +} diff --git a/features/total-stats/script.js b/features/total-stats/script.js index d4f3661e..bd0417b4 100644 --- a/features/total-stats/script.js +++ b/features/total-stats/script.js @@ -1,51 +1,76 @@ -export default async function ({ feature, console }) { - let div = await ScratchTools.waitForElement("div.box.slider-carousel-container") - if (!document.querySelector("#profile-data")) return; - - let stats = await getStats(Scratch.INIT_DATA.PROFILE.model.username) - - let span = document.createElement("span") - span.textContent = stats.loves.toLocaleString() + " loves • " + stats.favorites.toLocaleString() + " favorites • " + stats.views.toLocaleString() + " views" - span.className = "ste-total-stats" - feature.self.hideOnDisable(span) - div.querySelector(".box-head").insertBefore(span, div.querySelector("a")) - - async function getProjects(user) { - let projects = [] - let offset = 0 - let keepGoing = true - - while (keepGoing) { - let data = await (await fetch(`https://api.scratch.mit.edu/users/${user}/projects?limit=40&offset=${offset.toString()}`)).json() - - projects.push(...data) - - if (data.length === 40) { - offset += 40 - } else { - keepGoing = false - } - } - - return projects - } - - async function getStats(user) { - let projects = await getProjects(user) - let stats = { - loves: 0, - favorites: 0, - remixes: 0, - views: 0, - } - - for (var i in projects) { - stats.loves += projects[i].stats.loves - stats.favorites += projects[i].stats.favorites - stats.remixes += projects[i].stats.remixes - stats.views += projects[i].stats.views - } - - return stats - } -} \ No newline at end of file +export default async function ({ feature }) { + await ScratchTools.waitForElement(".box-head"); + + const username = window.location.pathname.split("/")[2]; + const res = await fetch(`https://scratchdata.vercel.app/api/user-stats/${username}`); + if (!res.ok) return; + + const data = await res.json(); + const format = (n) => n.toLocaleString(); + + const stats = [ + { + icon: "https://scratch.mit.edu/svgs/project/love-red.svg", + alt: "Loves", + value: data.totalLoves, + }, + { + icon: "https://scratch.mit.edu/svgs/project/fav-yellow.svg", + alt: "Favorites", + value: data.totalFavorites, + }, + { + icon: "https://scratch.mit.edu/svgs/project/views-gray.svg", + alt: "Views", + value: data.totalViews, + }, + { + icon: "https://raw.githubusercontent.com/scratchfoundation/scratch-www/a9cf52cd7fbec834897587dd9e17d648ba0a38b2/static/svgs/messages/remix.svg", + alt: "Remixes", + value: data.totalRemixes, + }, + ]; + + const box = document.createElement("div"); + box.className = "box slider-carousel-container prevent-select ste-tps-box"; + box.dataset.stFeature = "total-project-stats"; + + const head = document.createElement("div"); + head.className = "box-head"; + + const title = document.createElement("h4"); + title.textContent = "Total Project Stats"; + head.appendChild(title); + box.appendChild(head); + + const content = document.createElement("div"); + content.className = "box-content slider-carousel horizontal ste-tps-content"; + + for (const stat of stats) { + const statDiv = document.createElement("div"); + statDiv.className = "ste-tps-stat"; + + const icon = document.createElement("img"); + icon.src = stat.icon; + icon.width = 32; + icon.height = 32; + icon.alt = stat.alt; + icon.style.display = "block"; + + const value = document.createElement("div"); + value.className = "ste-tps-value"; + value.textContent = format(stat.value); + + statDiv.appendChild(icon); + statDiv.appendChild(value); + content.appendChild(statDiv); + } + + box.appendChild(content); + + const boxHeads = document.querySelectorAll(".box-head"); + if (boxHeads.length >= 5) { + const targetBox = boxHeads[4].parentNode; + targetBox.parentNode.insertBefore(box, targetBox.nextSibling); + } +} diff --git a/features/total-stats/style.css b/features/total-stats/style.css index 4da1b4d7..55338208 100644 --- a/features/total-stats/style.css +++ b/features/total-stats/style.css @@ -1,7 +1,22 @@ -.ste-total-stats { - font-weight: 500; - opacity: .6; - margin-left: 1rem; - font-style: italic; - font-size: .9rem; -} \ No newline at end of file +.ste-tps-content { + display: flex; + justify-content: space-evenly; + align-items: center; + height: 99.5px; +} + +.ste-tps-stat { + text-align: center; + display: flex; +} + +.ste-tps-stat img { + display: block; +} + +.ste-tps-value { + font-size: 21px; + font-weight: 600; + margin-top: 5px; + margin-left: 10px; +}