Hover Cards for users, get info without opening their profile
Hover Cards for users, get info without opening their profile
Wanted to do this for a while. Did it today instead of sleeping.
Screenshot:
You can install it from here: https://greasyfork.org/en/scripts/468948-user-details-on-hover
Link to GitHub repo: https://github.com/lemmygod/lemmy-hovercards/tree/main
Or you can copy-paste the following code:
::: spoiler click here to view code.
js
// ==UserScript== // @name User Details on Hover // @namespace http://tampermonkey.net/ // @version 0.12 // @description Show user details on hover // @author You // @match *://*/* // @grant none // ==/UserScript== (function () { "use strict"; const isLemmy = document.head.querySelector("[name~=Description][content]").content === "Lemmy"; if (!isLemmy) return; // Inject styles for the user card function main() { const style = document.createElement("style"); style.innerHTML = ` .user-card { position: absolute; display: none; width: 350px; background-color: #242424; color: white; padding: 15px; border-radius: 10px; box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); z-index: 1000; grid-gap: 10px; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; line-height: 1.4; } .user-card .header { display: flex; align-items: center; margin-bottom: 10px; } .user-card img { width: 80px; height: 80px; object-fit: cover; border-radius: 50%; margin-right: 15px; } .user-card .username { font-size: 1.3em; font-weight: bold; } .user-card .instance { font-size: 0.8em; color: #888; } .user-card .body { display: grid; grid-template-columns: 1fr 1fr; grid-gap: 10px; } .user-card .key { font-weight: bold; } .user-card .value { color: #ddd; margin-top: 10px; } .user-card .bio { grid-column: 1 / -1; font-style: italic; }`; document.head.appendChild(style); // Create the user card const userCard = document.createElement("div"); userCard.classList.add("user-card"); userCard.id = "user-card"; document.body.appendChild(userCard); let timer; // Find all user links const userLinks = document.querySelectorAll('a.text-info[href*="/u/"]'); userLinks.forEach((userLink) => { userLink.setAttribute("title", ""); // When mouse enters, show the user card userLink.addEventListener("mouseenter", async (event) => { const username = userLink.href.split("/u/")[1]; // Fetch user details const userInfo = await getUserInfo(username); // Format the date const date = new Date(userInfo.creationDate); const formattedDate = `${date.getFullYear()}/${String( date.getMonth() + 1 ).padStart(2, "0")}/${String(date.getDate()).padStart(2, "0")}`; // Update the user card userCard.innerHTML = ` <div class="header"> <img src="${ userInfo.profilePicture || `https://api.dicebear.com/6.x/identicon/svg?seed=${username}` }" alt="User avatar"> <div> <div class="username">${ userInfo.name || username.split("@")[0] }</div> <a href="https://${ userInfo.instance }/u/${username}" class="instance">${username}${ username.indexOf("@") === -1 ? "@" + userInfo.instance : "" } </a> </div> </div> <div class="body"> <div><span class="key">ID:</span> <span class="value">${ userInfo.id }</span></div> <div style="display:flex; flex-direction: column; gap: 3px"><span class="key"> <svg class="icon"><use xlink:href="/static/assets/symbols.svg#icon-cake"></use><div class="sr-only"><title>cake</title></div></svg> Cake Day:</span> <span class="value">${formattedDate}</span></div> <div><span class="key">Posts:</span> <span class="value">${ userInfo.post_count }</span></div> <div><span class="key">Comments:</span> <span class="value">${ userInfo.comment_count }</span></div> <div><span class="key">Post Score:</span> <span class="value">${ userInfo.post_score }</span></div> <div><span class="key">Comment Score:</span> <span class="value">${ userInfo.comment_score }</span></div> ${ userInfo.bio ? `<div class="bio">${userInfo.bio}</div>` : "" } </div>`; // Show the user card at the cursor const rect = userLink.getBoundingClientRect(); userCard.style.left = `${window.pageXOffset + rect.left}px`; userCard.style.top = `${window.pageYOffset + rect.bottom + 5}px`; // setTimeout(() => { if (userLink.querySelector(":hover")) { userCard.style.display = "block"; } // }, 250); timer = setTimeout(() => { // check if username is not being hovered anymore after 150ms, after which point we must change display to none if (!userLink.querySelector(":hover")) { userCard.style.display = "none"; } }, 150); }); // When mouse leaves, hide the user card after a slight delay userLink.addEventListener("mouseleave", () => { // after a slight delay, delete the node timer = setTimeout(() => { // delete the node // userCard.parentElement.removeChild(userCard); userCard.style.display = "none"; }, 250); setTimeout(() => { // check if both are unhovered after 260ms, and if that's the case, removeChild anyway if (!userCard.parentElement) return; if (!userCard.querySelector(":hover")) { // userCard.parentElement.removeChild(userCard); userCard.style.display = "none"; } }, 250); // timer = setTimeout(() => { // userCard.style.display = "none"; // }, 250); }); }); userCard.addEventListener("mouseenter", () => { clearTimeout(timer); }); userCard.addEventListener("mouseleave", () => { userCard.style.display = "none"; // userCard.parentElement.removeChild(userCard); }); // Fetch user info from the API async function getUserInfo(userName) { const instanceName = location.href.split("/")[2]; const response = await fetch( `https://${instanceName}/api/v3/user?username=${userName}`, { method: "GET", headers: { "Content-Type": "application/json", }, } ); const user = await response.json(); const { published: creationDate, avatar: profilePicture, bio, display_name: name, name: username, id, banner, } = user.person_view.person; const { comment_count, comment_score, post_count, post_score } = user.person_view.counts; return { creationDate, profilePicture, bio, name, username, id, banner, instance: instanceName, comment_count, comment_score, post_count, post_score, }; } } // detect react changed url but didn't reload the page by checking for url change var oldHref = document.location.href; setInterval(function () { if (document.location.href !== oldHref) { oldHref = document.location.href; // Wait for the page to load setTimeout(main, 1000); console.log("url changed!"); } }, 500); // run on page load main(); })();
I am quite surprised, this works on my iPad as well not just my Linux laptop!
Thank you for the user script!
P.s. If you need the Safari Mobile extension it can be found on the App Store as well as GitHub and is GPL licensed
I didn't know you u guys had extensions in iOS browsers! First time I see that. You could install an adblocking user script maybe? I thought iOS was ad hell. Gives me a bit of hope.