a tool for shared writing and social publishing
at main 2.9 kB view raw
1"use client"; 2import { Popover } from "./Popover"; 3import useSWR from "swr"; 4import { callRPC } from "app/api/rpc/client"; 5import { useRef, useState } from "react"; 6import { ProfileHeader } from "app/(home-pages)/p/[didOrHandle]/ProfileHeader"; 7import { SpeedyLink } from "./SpeedyLink"; 8import { Tooltip } from "./Tooltip"; 9import { ProfileViewDetailed } from "@atproto/api/dist/client/types/app/bsky/actor/defs"; 10 11export const ProfilePopover = (props: { 12 trigger: React.ReactNode; 13 didOrHandle: string; 14}) => { 15 const [isOpen, setIsOpen] = useState(false); 16 let [isHovered, setIsHovered] = useState(false); 17 const hoverTimeout = useRef<null | number>(null); 18 19 const { data, isLoading } = useSWR( 20 isHovered ? ["profile-data", props.didOrHandle] : null, 21 async () => { 22 const response = await callRPC("get_profile_data", { 23 didOrHandle: props.didOrHandle, 24 }); 25 return response.result; 26 }, 27 ); 28 29 return ( 30 <Tooltip 31 className="max-w-sm p-0! text-center" 32 asChild 33 trigger={ 34 <a 35 className="no-underline" 36 href={`https://leaflet.pub/p/${props.didOrHandle}`} 37 target="_blank" 38 onPointerEnter={(e) => { 39 if (hoverTimeout.current) { 40 window.clearTimeout(hoverTimeout.current); 41 } 42 hoverTimeout.current = window.setTimeout(async () => { 43 setIsHovered(true); 44 }, 150); 45 }} 46 onPointerLeave={() => { 47 if (isHovered) return; 48 if (hoverTimeout.current) { 49 window.clearTimeout(hoverTimeout.current); 50 hoverTimeout.current = null; 51 } 52 setIsHovered(false); 53 }} 54 > 55 {props.trigger} 56 </a> 57 } 58 onOpenChange={setIsOpen} 59 > 60 {isLoading ? ( 61 <div className="text-secondary p-4">Loading...</div> 62 ) : data ? ( 63 <div> 64 <ProfileHeader 65 profile={data.profile} 66 publications={data.publications} 67 popover 68 /> 69 <KnownFollowers viewer={data.profile.viewer} did={data.profile.did} /> 70 </div> 71 ) : ( 72 <div className="text-secondary py-2 px-4">Profile not found</div> 73 )} 74 </Tooltip> 75 ); 76}; 77 78let KnownFollowers = (props: { 79 viewer: ProfileViewDetailed["viewer"]; 80 did: string; 81}) => { 82 if (!props.viewer?.knownFollowers) return null; 83 let count = props.viewer.knownFollowers.count; 84 return ( 85 <> 86 <hr className="border-border" /> 87 Followed by{" "} 88 <a 89 className="hover:underline" 90 href={`https://bsky.social/profile/${props.did}/known-followers`} 91 target="_blank" 92 > 93 {props.viewer?.knownFollowers?.followers[0]?.displayName}{" "} 94 {count > 1 ? `and ${count - 1} other${count > 2 ? "s" : ""}` : ""} 95 </a> 96 </> 97 ); 98};