a tool for shared writing and social publishing

fixed some layout with popover, now opens on tap rather than hover (#255)

* fixed some layout with popover, now opens on tap rather than hover

* little popover fixes

authored by cozylittle.house and committed by

GitHub 4e80ae0c 96ed3674

+90 -64
+56 -36
app/(home-pages)/p/[didOrHandle]/ProfileHeader.tsx
··· 1 "use client"; 2 import { Avatar } from "components/Avatar"; 3 - import { AppBskyActorProfile, PubLeafletPublication } from "lexicons/api"; 4 - import { blobRefToSrc } from "src/utils/blobRefToSrc"; 5 - import type { ProfileData } from "./layout"; 6 import { usePubTheme } from "components/ThemeManager/PublicationThemeProvider"; 7 import { colorToString } from "components/ThemeManager/useColorAttribute"; 8 import { PubIcon } from "components/ActionBar/Publications"; ··· 25 <Avatar 26 src={profileRecord.avatar} 27 displayName={profileRecord.displayName} 28 - className="mx-auto mt-3 sm:mt-4" 29 giant 30 /> 31 ); 32 33 const displayNameElement = ( 34 - <h3 className=" px-3 sm:px-4 pt-2 leading-tight"> 35 {profileRecord.displayName 36 ? profileRecord.displayName 37 : `@${props.profile.handle}`} ··· 40 41 const handleElement = profileRecord.displayName && ( 42 <div 43 - className={`text-tertiary ${props.popover ? "text-xs" : "text-sm"} pb-1 italic px-3 sm:px-4 truncate`} 44 > 45 @{props.profile.handle} 46 </div> 47 ); 48 49 return ( 50 <div 51 - className={`flex flex-col relative ${props.popover && "text-sm"}`} 52 id="profile-header" 53 > 54 - <ProfileLinks handle={props.profile.handle || ""} /> 55 - <div className="flex flex-col"> 56 - <div className="flex flex-col group"> 57 {props.popover ? ( 58 <SpeedyLink className={"hover:no-underline!"} href={profileUrl}> 59 {avatarElement} ··· 61 ) : ( 62 avatarElement 63 )} 64 - {props.popover ? ( 65 - <SpeedyLink 66 - className={" text-primary group-hover:underline"} 67 - href={profileUrl} 68 - > 69 - {displayNameElement} 70 - </SpeedyLink> 71 - ) : ( 72 - displayNameElement 73 - )} 74 - {props.popover && handleElement ? ( 75 - <SpeedyLink className={"group-hover:underline"} href={profileUrl}> 76 - {handleElement} 77 - </SpeedyLink> 78 - ) : ( 79 - handleElement 80 - )} 81 </div> 82 - <pre className="text-secondary px-3 sm:px-4 whitespace-pre-wrap"> 83 - {profileRecord.description 84 - ? parseDescription(profileRecord.description) 85 - : null} 86 - </pre> 87 - <div className=" w-full overflow-x-scroll py-3 mb-3 "> 88 <div 89 className={`grid grid-flow-col gap-2 mx-auto w-fit px-3 sm:px-4 ${props.popover ? "auto-cols-[164px]" : "auto-cols-[164px] sm:auto-cols-[240px]"}`} 90 > ··· 104 105 const ProfileLinks = (props: { handle: string }) => { 106 return ( 107 - <div className="absolute sm:top-4 top-3 sm:right-4 right-3 flex flex-row gap-2"> 108 <a 109 className="text-tertiary hover:text-accent-contrast hover:no-underline!" 110 href={`https://bsky.app/profile/${props.handle}`} ··· 124 return ( 125 <a 126 href={`https://${record.base_path}`} 127 - className="border border-border p-2 rounded-lg hover:no-underline! text-primary basis-1/2" 128 style={{ backgroundColor: `rgb(${colorToString(bgLeaflet, "rgb")})` }} 129 > 130 <div ··· 225 ? urlWithoutProtocol.slice(0, 50) + "…" 226 : urlWithoutProtocol; 227 parts.push( 228 - <a key={key++} href={match.href} target="_blank" rel="noopener noreferrer"> 229 {displayText} 230 </a>, 231 ); ··· 241 242 return parts; 243 }
··· 1 "use client"; 2 import { Avatar } from "components/Avatar"; 3 + import { PubLeafletPublication } from "lexicons/api"; 4 import { usePubTheme } from "components/ThemeManager/PublicationThemeProvider"; 5 import { colorToString } from "components/ThemeManager/useColorAttribute"; 6 import { PubIcon } from "components/ActionBar/Publications"; ··· 23 <Avatar 24 src={profileRecord.avatar} 25 displayName={profileRecord.displayName} 26 + className="profileAvatar mx-auto mt-3 sm:mt-4" 27 giant 28 /> 29 ); 30 31 const displayNameElement = ( 32 + <h3 className="profileName px-3 sm:px-4 pt-2 leading-tight"> 33 {profileRecord.displayName 34 ? profileRecord.displayName 35 : `@${props.profile.handle}`} ··· 38 39 const handleElement = profileRecord.displayName && ( 40 <div 41 + className={`profileHandle text-secondary ${props.popover ? "text-sm" : "text-sm"} px-3 sm:px-4 truncate`} 42 > 43 @{props.profile.handle} 44 </div> 45 ); 46 + console.log(props.profile); 47 48 return ( 49 <div 50 + className={`profileHeader flex flex-col relative `} 51 id="profile-header" 52 > 53 + {!props.popover && <ProfileLinks handle={props.profile.handle || ""} />} 54 + <div className="profileInfo flex flex-col gap-3"> 55 + <div className="profileNameAndHandle flex flex-col "> 56 {props.popover ? ( 57 <SpeedyLink className={"hover:no-underline!"} href={profileUrl}> 58 {avatarElement} ··· 60 ) : ( 61 avatarElement 62 )} 63 + {displayNameElement} 64 + 65 + {handleElement} 66 + <KnownFollowers 67 + viewer={props.profile.viewer} 68 + did={props.profile.did} 69 + /> 70 + 71 + <pre className="profileDescription pt-1 px-3 sm:px-4 whitespace-pre-wrap"> 72 + {profileRecord.description 73 + ? parseDescription(profileRecord.description) 74 + : null} 75 + </pre> 76 </div> 77 + 78 + <div className="profilePublicationCards w-full overflow-x-scroll"> 79 <div 80 className={`grid grid-flow-col gap-2 mx-auto w-fit px-3 sm:px-4 ${props.popover ? "auto-cols-[164px]" : "auto-cols-[164px] sm:auto-cols-[240px]"}`} 81 > ··· 95 96 const ProfileLinks = (props: { handle: string }) => { 97 return ( 98 + <div className="profileLinks absolute sm:top-4 top-3 sm:right-4 right-3 flex flex-row gap-2"> 99 <a 100 className="text-tertiary hover:text-accent-contrast hover:no-underline!" 101 href={`https://bsky.app/profile/${props.handle}`} ··· 115 return ( 116 <a 117 href={`https://${record.base_path}`} 118 + className="profilePublicationCard border border-border p-2 rounded-lg hover:no-underline! text-primary basis-1/2 " 119 style={{ backgroundColor: `rgb(${colorToString(bgLeaflet, "rgb")})` }} 120 > 121 <div ··· 216 ? urlWithoutProtocol.slice(0, 50) + "…" 217 : urlWithoutProtocol; 218 parts.push( 219 + <a 220 + key={key++} 221 + href={match.href} 222 + target="_blank" 223 + rel="noopener noreferrer" 224 + > 225 {displayText} 226 </a>, 227 ); ··· 237 238 return parts; 239 } 240 + 241 + const KnownFollowers = (props: { 242 + viewer: ProfileViewDetailed["viewer"]; 243 + did: string; 244 + }) => { 245 + if (!props.viewer?.knownFollowers) return null; 246 + let count = props.viewer.knownFollowers.count; 247 + 248 + return ( 249 + <> 250 + <div className="profileKnownFollowers sm:px-4 px-3 text-xs text-tertiary italic"> 251 + Followed by{" "} 252 + <a 253 + className="hover:underline" 254 + href={`https://bsky.app/profile/${props.did}/known-followers`} 255 + target="_blank" 256 + > 257 + {props.viewer?.knownFollowers?.followers[0]?.displayName}{" "} 258 + {count > 1 ? `and ${count - 1} other${count > 2 ? "s" : ""}` : ""} 259 + </a> 260 + </div> 261 + </> 262 + ); 263 + };
+1 -1
app/(home-pages)/p/[didOrHandle]/ProfileTabs.tsx
··· 41 const bgColor = cardBorderHidden ? "var(--bg-leaflet)" : "var(--bg-page)"; 42 43 return ( 44 - <div className="flex flex-col w-full sticky top-3 sm:top-4 z-20 sm:px-4 px-3"> 45 <div 46 style={ 47 scrollPosWithinTabContent < 20
··· 41 const bgColor = cardBorderHidden ? "var(--bg-leaflet)" : "var(--bg-page)"; 42 43 return ( 44 + <div className="flex flex-col w-full sticky top-3 sm:top-4 z-20 sm:px-4 px-3 pt-6"> 45 <div 46 style={ 47 scrollPosWithinTabContent < 20
+33 -27
components/ProfilePopover.tsx
··· 7 import { SpeedyLink } from "./SpeedyLink"; 8 import { Tooltip } from "./Tooltip"; 9 import { ProfileViewDetailed } from "@atproto/api/dist/client/types/app/bsky/actor/defs"; 10 11 export const ProfilePopover = (props: { 12 trigger: React.ReactNode; ··· 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); ··· 53 }} 54 > 55 {props.trigger} 56 - </a> 57 } 58 onOpenChange={setIsOpen} 59 > ··· 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 78 - let 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 };
··· 7 import { SpeedyLink } from "./SpeedyLink"; 8 import { Tooltip } from "./Tooltip"; 9 import { ProfileViewDetailed } from "@atproto/api/dist/client/types/app/bsky/actor/defs"; 10 + import { BlueskyTiny } from "./Icons/BlueskyTiny"; 11 + import { ArrowRightTiny } from "./Icons/ArrowRightTiny"; 12 13 export const ProfilePopover = (props: { 14 trigger: React.ReactNode; ··· 29 ); 30 31 return ( 32 + <Popover 33 className="max-w-sm p-0! text-center" 34 trigger={ 35 + <div 36 className="no-underline" 37 onPointerEnter={(e) => { 38 if (hoverTimeout.current) { 39 window.clearTimeout(hoverTimeout.current); ··· 52 }} 53 > 54 {props.trigger} 55 + </div> 56 } 57 onOpenChange={setIsOpen} 58 > ··· 65 publications={data.publications} 66 popover 67 /> 68 + 69 + <ProfileLinks handle={data.profile.handle} /> 70 </div> 71 ) : ( 72 + <div className="text-secondary py-2 px-4">No profile found...</div> 73 )} 74 + </Popover> 75 ); 76 }; 77 78 + const ProfileLinks = (props: { handle: string }) => { 79 + let linkClassName = 80 + "flex gap-1.5 text-tertiary items-center border border-transparent px-1 rounded-md hover:bg-[var(--accent-light)] hover:border-accent-contrast hover:text-accent-contrast no-underline hover:no-underline"; 81 return ( 82 + <div className="w-full flex-col"> 83 + <hr className="border-border-light mt-3" /> 84 + <div className="flex gap-2 justify-between sm:px-4 px-3 py-2"> 85 + <div className="flex gap-2"> 86 + <a 87 + href={`https://bsky.app/profile/${props.handle}`} 88 + target="_blank" 89 + className={linkClassName} 90 + > 91 + <BlueskyTiny /> 92 + Bluesky 93 + </a> 94 + </div> 95 + <SpeedyLink 96 + href={`https://leaflet.pub/p/${props.handle}`} 97 + className={linkClassName} 98 + > 99 + Full profile <ArrowRightTiny /> 100 + </SpeedyLink> 101 + </div> 102 + </div> 103 ); 104 };