Skip to content

Commit 6486e1b

Browse files
committed
New feature: mutual-following
1 parent a0e4e02 commit 6486e1b

File tree

4 files changed

+176
-0
lines changed

4 files changed

+176
-0
lines changed

features/features.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
[
2+
{
3+
"version": 2,
4+
"id": "mutual-following",
5+
"versionAdded": "v5.0.0"
6+
},
27
{
38
"version": 2,
49
"id": "studio-invite-comments",
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"title": "Mutual Following",
3+
"description": "See your mutual following with other users on their profiles.",
4+
"credits": [
5+
{ "username": "rgantzos", "url": "https://scratch.mit.edu/users/rgantzos/" }
6+
],
7+
"type": ["Website"],
8+
"tags": ["New", "Featured"],
9+
"dynamic": true,
10+
"scripts": [{ "file": "script.js", "runOn": "/users/*", "module": true }],
11+
"styles": [{ "file": "style.css", "runOn": "/users/*" }]
12+
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
export default async function ({ feature, console, className }) {
2+
window.feature = feature
3+
4+
let auth = await feature.auth.fetch()
5+
if (!auth?.user?.username) return console.log("User not logged in.");
6+
7+
let user = auth.user.username
8+
let profile = Scratch.INIT_DATA.PROFILE.model.username
9+
10+
if (user === profile) return;
11+
12+
async function getFollow(username, type, maxRequests) {
13+
const LIMIT = 40
14+
15+
let url = `https://api.scratch.mit.edu/users/${username}/${type}`
16+
let follows = []
17+
18+
let keepGoing = true
19+
let offset = 0
20+
let requests = 0
21+
while (keepGoing) {
22+
let data = await (await fetch(url + `?offset=${offset}&limit=${LIMIT}`)).json()
23+
follows.push(...data)
24+
25+
requests += 1
26+
27+
if (data.length < 20) {
28+
keepGoing = false
29+
}
30+
31+
if (requests === maxRequests) {
32+
keepGoing = false
33+
}
34+
35+
offset += LIMIT
36+
}
37+
38+
return follows
39+
}
40+
41+
const profileFollowing = await getFollow(profile, "following", 10)
42+
const profileFollowers = await getFollow(profile, "followers", 10)
43+
const userFollowing = await getFollow(user, "following", 5)
44+
45+
const mutualFollowing = profileFollowing.filter((pF) => userFollowing.find((uF) => uF.username === pF.username))
46+
const mutualFollowers = profileFollowers.filter((pF) => userFollowing.find((uF) => uF.username === pF.username))
47+
48+
let followingUsernames = []
49+
let followersUsernames = []
50+
51+
for (var i in mutualFollowing) {
52+
followingUsernames.push(mutualFollowing[i].username)
53+
}
54+
55+
for (var i in mutualFollowers) {
56+
followersUsernames.push(mutualFollowers[i].username)
57+
}
58+
59+
const followingBox = document.querySelector(`div.box.slider-carousel-container a[href='/users/${profile}/following/']`).closest(".box")
60+
const followersBox = document.querySelector(`div.box.slider-carousel-container a[href='/users/${profile}/followers/']`).closest(".box")
61+
62+
let followingContainer = Object.assign(document.createElement("div"), {
63+
className: className("mutual following container")
64+
})
65+
followingContainer.title = followingUsernames.join(", ")
66+
let followersContainer = Object.assign(document.createElement("div"), {
67+
className: className("mutual followers container")
68+
})
69+
followersContainer.title = followersUsernames.join(", ")
70+
feature.self.hideOnDisable(followingContainer)
71+
feature.self.hideOnDisable(followersContainer)
72+
73+
followingBox.querySelector(".box-head").insertBefore(followingContainer, followingBox.querySelector(".box-head a"))
74+
followersBox.querySelector(".box-head").insertBefore(followersContainer, followersBox.querySelector(".box-head a"))
75+
76+
for (var i in mutualFollowing) {
77+
if (Number(i) < 5) {
78+
let mF = mutualFollowing[i]
79+
let image = Object.assign(document.createElement("img"), {
80+
src: mF.profile.images["90x90"]
81+
})
82+
image.setAttribute("style", "--i:"+i)
83+
followingContainer.appendChild(image)
84+
}
85+
}
86+
87+
if (mutualFollowing.length > 0) {
88+
let span = Object.assign(document.createElement("span"), {
89+
textContent: `Following ${mutualFollowing[0].username}${mutualFollowing.length > 1 ? ` and ${mutualFollowing.length - 1} ${mutualFollowing.length > 2 ? "others" : "other"}` : ""}`
90+
})
91+
followingContainer.appendChild(span)
92+
}
93+
94+
for (var i in mutualFollowers) {
95+
let mF = mutualFollowers[i]
96+
let image = Object.assign(document.createElement("img"), {
97+
src: mF.profile.images["90x90"]
98+
})
99+
image.setAttribute("style", "--i:"+i)
100+
followersContainer.appendChild(image)
101+
}
102+
103+
if (mutualFollowers.length > 0) {
104+
let span = Object.assign(document.createElement("span"), {
105+
textContent: `Followed by ${mutualFollowers[0].username}${mutualFollowers.length > 1 ? ` and ${mutualFollowers.length - 1} ${mutualFollowers.length > 2 ? "others" : "other"}` : ""}`
106+
})
107+
followersContainer.appendChild(span)
108+
}
109+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
.ste-mutual-following-container {
2+
margin-left: .5rem;
3+
display: inline-block;
4+
}
5+
6+
.ste-mutual-following-container img {
7+
height: 1.5rem;
8+
width: 1.5rem;
9+
border-radius: .35rem;
10+
margin-right: -.25rem;
11+
position: relative;
12+
top: .25rem;
13+
position: relative;
14+
z-index: calc(30 - var(--i));
15+
}
16+
17+
.ste-mutual-following-container span {
18+
margin-left: .75rem;
19+
opacity: .5;
20+
font-style: italic;
21+
position: relative;
22+
top: -.1rem;
23+
font-weight: 500;
24+
}
25+
26+
27+
.ste-mutual-followers-container {
28+
margin-left: .5rem;
29+
display: inline-block;
30+
}
31+
32+
.ste-mutual-followers-container img {
33+
height: 1.5rem;
34+
width: 1.5rem;
35+
border-radius: .35rem;
36+
margin-right: -.25rem;
37+
position: relative;
38+
top: .25rem;
39+
position: relative;
40+
z-index: calc(30 - var(--i));
41+
}
42+
43+
.ste-mutual-followers-container span {
44+
margin-left: .75rem;
45+
opacity: .5;
46+
font-style: italic;
47+
position: relative;
48+
top: -.1rem;
49+
font-weight: 500;
50+
}

0 commit comments

Comments
 (0)