-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: handle undefined on user profile
- Loading branch information
Showing
2 changed files
with
42 additions
and
40 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,95 +1,97 @@ | ||
const $ = document.querySelector.bind(document); | ||
const HOVER_CARD_SELECTOR = ".Popover.js-hovercard-content.position-absolute"; | ||
const HOVER_CARD_FOLLOWS_YOU_ID = "hovercard-follows-you"; | ||
const PROFILE_FOLLOWS_YOU_ID = "profile-follows-you"; | ||
|
||
/** | ||
* returns whether user X is following user Y | ||
* @param {string} x | ||
* @param {string} y | ||
*/ | ||
async function isFollowing(x, y) { | ||
const cachekey = new Date().toISOString().split(":")[0]; | ||
const cacheKey = new Date().toISOString().split(":")[0]; | ||
// toISOString() '2022-01-02T13:36:43.370Z' => split(":")[0] '2022-01-02T13' Caching by Date & Hour | ||
// cache is considered valid for an hour | ||
// https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limiting | ||
// X-RateLimit-Limit: 60 => The maximum number of requests you're permitted to make per hour. | ||
const url = `https://api.github.com/users/${x}/following/${y}?${cachekey}`; | ||
const url = `https://api.github.com/users/${x}/following/${y}?${cacheKey}`; | ||
const response = await fetch(url, { cache: "force-cache" }); | ||
return response.status === 204; // according to github docs 204 means follows | ||
return response.status === 204; // according to github docs, 204 means follows | ||
} | ||
|
||
/** | ||
* Follows You | ||
*/ | ||
class FY { | ||
const FY = { | ||
/** | ||
* creates and returns a `follows you` element | ||
* @param {...string} classes | ||
* @returns {HTMLSpanElement} follows you element | ||
*/ | ||
static createElement(...classes) { | ||
createElement(...classes) { | ||
const el = document.createElement("span"); | ||
el.innerText = "follows you"; | ||
el.style.fontSize = "10px"; | ||
el.classList.add(...classes, "label", "text-uppercase", "v-align-middle"); | ||
return el; | ||
} | ||
}, | ||
|
||
/** | ||
* shows `follows-you` label on the profile page | ||
*/ | ||
static showOnProfilePage() { | ||
const stickyBarUsername = $( | ||
"span.d-table-cell.v-align-middle.lh-condensed.pr-2 > div" | ||
); | ||
stickyBarUsername?.after(FY.createElement()); | ||
$("h1.vcard-names").appendChild(FY.createElement()); | ||
} | ||
showOnProfilePage() { | ||
const element = FY.createElement(); | ||
element.id = PROFILE_FOLLOWS_YOU_ID; | ||
|
||
$("h1.vcard-names")?.appendChild(element); | ||
}, | ||
|
||
/** | ||
* shows `follows-you` label on the user hover card | ||
*/ | ||
static showOnHoverCard() { | ||
const hovercardLabel = FY.createElement( | ||
"mt-2", | ||
"d-flex", | ||
"flex-justify-center" | ||
); | ||
hovercardLabel.id = "hovercard-follows-you"; | ||
showOnHoverCard() { | ||
const element = FY.createElement("mt-2", "d-flex", "flex-justify-center"); | ||
element.id = HOVER_CARD_FOLLOWS_YOU_ID; | ||
|
||
if ($(`#${hovercardLabel.id}`)) return; // already added | ||
$(`${HOVER_CARD_SELECTOR} .px-3`)?.append(hovercardLabel); | ||
} | ||
} | ||
$(`${HOVER_CARD_SELECTOR} .px-3`)?.append(element); | ||
}, | ||
}; | ||
|
||
(async () => { | ||
const followsMe = (x) => isFollowing(x, loggedInUsername); | ||
async function init() { | ||
const loggedInUsername = $("meta[name='user-login']")?.content; | ||
const followsMe = (x) => isFollowing(x, loggedInUsername); | ||
|
||
// PART 1 -------------------------------------------------- | ||
// if I'm logged in and viewing some user's profile page who follows me | ||
const isProfilePage = !!$(".vcard-fullname"); | ||
const openedProfileUsername = $("command-palette-token")?.dataset.text; | ||
// extract from current url: github.com/username | ||
const openedProfileUsername = location.pathname.split("/").pop(); | ||
const shouldShowOnProfile = | ||
isProfilePage && | ||
loggedInUsername !== openedProfileUsername && // not my profile | ||
!$(`#${PROFILE_FOLLOWS_YOU_ID}`) && // not already shown | ||
(await followsMe(openedProfileUsername)); | ||
|
||
if (shouldShowOnProfile) { | ||
const PROFILE_TAB_SWITCH = "pjax:end"; | ||
document.addEventListener(PROFILE_TAB_SWITCH, FY.showOnProfilePage); | ||
document.dispatchEvent(new Event(PROFILE_TAB_SWITCH)); | ||
FY.showOnProfilePage(); | ||
} | ||
|
||
// PART 2 -------------------------------------------------- | ||
// show on hover card | ||
const callback = async () => { | ||
const userimage = $(`${HOVER_CARD_SELECTOR} .avatar-user`); | ||
if (!userimage) return; | ||
const username = userimage.getAttribute("alt").substring(1); // @username => username | ||
if (await followsMe(username)) { | ||
const hoverCardCallback = async () => { | ||
// already has the label, return | ||
if ($(`#${HOVER_CARD_FOLLOWS_YOU_ID}`)) return; | ||
const userImage = $(`${HOVER_CARD_SELECTOR} .avatar-user`); | ||
const username = userImage?.getAttribute("alt")?.substring(1); // @username => username | ||
if (username && (await followsMe(username))) { | ||
FY.showOnHoverCard(); | ||
} | ||
}; | ||
const hovercard = $(HOVER_CARD_SELECTOR); | ||
new MutationObserver(callback).observe(hovercard, { attributes: true }); | ||
})(); | ||
|
||
const selectedHoverCard = $(HOVER_CARD_SELECTOR); | ||
new MutationObserver(hoverCardCallback).observe(selectedHoverCard, { | ||
attributes: true, | ||
}); | ||
} | ||
|
||
document.documentElement.addEventListener("turbo:load", init); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters