From 759c4ce33dfe2875806dc464dff78f468a2f6877 Mon Sep 17 00:00:00 2001 From: kerolloz Date: Sat, 27 Jul 2024 15:40:01 +0300 Subject: [PATCH] fix: handle undefined on user profile --- follows-you.js | 78 ++++++++++++++++++++++++++------------------------ manifest.json | 4 +-- 2 files changed, 42 insertions(+), 40 deletions(-) diff --git a/follows-you.js b/follows-you.js index 3d6cbbd..6d777df 100644 --- a/follows-you.js +++ b/follows-you.js @@ -1,5 +1,7 @@ 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 @@ -7,89 +9,89 @@ const HOVER_CARD_SELECTOR = ".Popover.js-hovercard-content.position-absolute"; * @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); diff --git a/manifest.json b/manifest.json index 7781f1f..4eee26c 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { - "manifest_version": 2, + "manifest_version": 3, "name": "Follows You GitHub", - "version": "0.4.4", + "version": "1.0.0", "description": "Adds a \"follows you\" label on a GitHub follower profile or hovercard", "icons": { "16": "icons/16.png",