Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions features/features.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
[
{
"version": 2,
"id": "total-stats",
"versionAdded": "v4.2.0"
},
{
"version": 2,
"id": "copy-paste-lists",
Expand Down Expand Up @@ -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",
Expand Down
56 changes: 24 additions & 32 deletions features/total-stats/data.json
Original file line number Diff line number Diff line change
@@ -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"
]
}
"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/*"
}
]
}
127 changes: 76 additions & 51 deletions features/total-stats/script.js
Original file line number Diff line number Diff line change
@@ -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
}
}
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}`);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. This server isn't working
  2. Let's not use new servers, let's try to only use Scratch or integrate any APIs with the ScratchTools server

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);
}
}
29 changes: 22 additions & 7 deletions features/total-stats/style.css
Original file line number Diff line number Diff line change
@@ -1,7 +1,22 @@
.ste-total-stats {
font-weight: 500;
opacity: .6;
margin-left: 1rem;
font-style: italic;
font-size: .9rem;
}
.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;
}