···825 // Initial check
826 checkCurrentProfile();
827000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000828 // Set up a MutationObserver to watch for URL changes
829 const observeUrlChanges = () => {
830 let lastUrl = location.href;
···825 // Initial check
826 checkCurrentProfile();
827828+ const checkUserLinksOnPage = async () => {
829+ // Look for profile links with handles
830+ // Find all profile links and filter to get only one link per parent
831+ const allProfileLinks = Array.from(
832+ document.querySelectorAll('a[href^="/profile/"]:not(:has(*))'),
833+ );
834+835+ // Use a Map to keep track of parent elements and their first child link
836+ const parentMap = new Map();
837+838+ // For each link, store only the first one found for each parent
839+ for (const link of allProfileLinks) {
840+ const parent = link.parentElement;
841+ if (parent && !parentMap.has(parent)) {
842+ parentMap.set(parent, link);
843+ }
844+ }
845+846+ // Get only the first link for each parent
847+ const profileLinks = Array.from(parentMap.values());
848+849+ if (profileLinks.length === 0) return;
850+851+ console.log(`Found ${profileLinks.length} possible user links on page`);
852+853+ // Process profile links to identify user containers
854+ for (const link of profileLinks) {
855+ try {
856+ // Check if we already processed this link
857+ if (link.getAttribute("data-verification-checked") === "true") continue;
858+859+ // Mark as checked
860+ link.setAttribute("data-verification-checked", "true");
861+862+ // Extract handle from href
863+ const handle = link.getAttribute("href").split("/profile/")[1];
864+ if (!handle) continue;
865+866+ // check if there is anything after the handle
867+ const handleTrailing = handle.split("/").length > 1;
868+ if (handleTrailing) continue;
869+870+ // Find parent container that might contain the handle and verification icon
871+ // Look for containers where this link is followed by another link with the same handle
872+ const parent = link.parentElement;
873+874+ // If we found a container with the verification icon
875+ if (parent) {
876+ // Check if this user already has our verification badge
877+ if (parent.querySelector(".trusted-user-inline-badge")) continue;
878+879+ try {
880+ // Fetch user profile data to get DID
881+ const response = await fetch(
882+ `https://public.api.bsky.app/xrpc/com.atproto.repo.getRecord?repo=${handle}&collection=app.bsky.actor.profile&rkey=self`,
883+ );
884+ const data = await response.json();
885+886+ // Extract the DID from the profile data
887+ const did = data.uri.split("/")[2];
888+889+ // Check if this user is verified by our trusted users
890+ const trustedUsers = getTrustedUsers();
891+ let isVerified = false;
892+ const verifiers = [];
893+894+ // Check cache first for each trusted user
895+ for (const trustedUser of trustedUsers) {
896+ const cachedData = getCachedVerifications(trustedUser);
897+898+ if (cachedData && isCacheValid(cachedData)) {
899+ // Use cached verification data
900+ const records = cachedData.records;
901+902+ for (const record of records) {
903+ if (record.value && record.value.subject === did) {
904+ isVerified = true;
905+ verifiers.push(trustedUser);
906+ break;
907+ }
908+ }
909+ }
910+ }
911+912+ // If verified, add a small badge
913+ if (isVerified && verifiers.length > 0) {
914+ // Create a badge element
915+ const smallBadge = document.createElement("span");
916+ smallBadge.className = "trusted-user-inline-badge";
917+ smallBadge.innerHTML = "✓";
918+919+ // Create tooltip text with all verifiers
920+ const verifiersText =
921+ verifiers.length > 1
922+ ? `Verified by: ${verifiers.join(", ")}`
923+ : `Verified by ${verifiers[0]}`;
924+925+ smallBadge.title = verifiersText;
926+ smallBadge.style.cssText = `
927+ background-color: #0070ff;
928+ color: white;
929+ border-radius: 50%;
930+ width: 14px;
931+ height: 14px;
932+ font-size: 10px;
933+ font-weight: bold;
934+ cursor: help;
935+ display: inline-flex;
936+ align-items: center;
937+ justify-content: center;
938+ margin-left: 4px;
939+ `;
940+941+ // Add click event to show verifiers
942+ smallBadge.addEventListener("click", (e) => {
943+ e.stopPropagation();
944+ showVerifiersPopup(verifiers);
945+ });
946+947+ // Insert badge after the SVG element
948+ parent.firstChild.after(smallBadge);
949+ parent.style.flexDirection = "row";
950+ parent.style.alignItems = "center";
951+ }
952+ } catch (error) {
953+ console.error(`Error checking verification for ${handle}:`, error);
954+ }
955+ }
956+ } catch (error) {
957+ console.error("Error processing profile link:", error);
958+ }
959+ }
960+ };
961+962+ const observeContentChanges = () => {
963+ // Use a debounced function to check for new user links
964+ const debouncedCheck = () => {
965+ clearTimeout(window.userLinksCheckTimeout);
966+ window.userLinksCheckTimeout = setTimeout(() => {
967+ checkUserLinksOnPage();
968+ }, 300);
969+ };
970+971+ // Create a mutation observer that watches for DOM changes
972+ const observer = new MutationObserver((mutations) => {
973+ let hasRelevantChanges = false;
974+975+ // Check if any mutations involve adding new nodes
976+ for (const mutation of mutations) {
977+ if (mutation.addedNodes.length > 0) {
978+ for (const node of mutation.addedNodes) {
979+ if (node.nodeType === Node.ELEMENT_NODE) {
980+ // Check if this element or its children might contain profile links
981+ if (
982+ node.querySelector('a[href^="/profile/"]') ||
983+ (node.tagName === "A" &&
984+ node.getAttribute("href")?.startsWith("/profile/"))
985+ ) {
986+ hasRelevantChanges = true;
987+ break;
988+ }
989+ }
990+ }
991+ }
992+ if (hasRelevantChanges) break;
993+ }
994+995+ if (hasRelevantChanges) {
996+ debouncedCheck();
997+ }
998+ });
999+1000+ // Observe the entire document for content changes that might include profile links
1001+ observer.observe(document.body, { childList: true, subtree: true });
1002+1003+ // Also check periodically for posts that might have been loaded but not caught by the observer
1004+ setInterval(debouncedCheck, 5000);
1005+ };
1006+1007+ // Add these calls to the initialization section
1008+ // Initial check for user links
1009+ setTimeout(checkUserLinksOnPage, 1000); // Slight delay to ensure page has loaded
1010+1011+ // Start observing for content changes to detect newly loaded posts
1012+ observeContentChanges();
1013+1014 // Set up a MutationObserver to watch for URL changes
1015 const observeUrlChanges = () => {
1016 let lastUrl = location.href;