Hey is a decentralized and permissionless social media app built with Lens Protocol 馃尶
at main 201 lines 7.3 kB view raw
1import { MapPinIcon } from "@heroicons/react/24/outline"; 2import { BeakerIcon, CheckBadgeIcon } from "@heroicons/react/24/solid"; 3import { STATIC_IMAGES_URL, TRANSFORMS } from "@hey/data/constants"; 4import getAccount from "@hey/helpers/getAccount"; 5import getAvatar from "@hey/helpers/getAvatar"; 6import type { AccountFragment } from "@hey/indexer"; 7import type { ReactNode } from "react"; 8import { useCallback, useState } from "react"; 9import { Link, useNavigate } from "react-router"; 10import FollowUnfollowButton from "@/components/Shared/Account/FollowUnfollowButton"; 11import TipButton from "@/components/Shared/Account/TipButton"; 12import Markup from "@/components/Shared/Markup"; 13import Slug from "@/components/Shared/Slug"; 14import { Button, H3, Image, LightBox, Tooltip } from "@/components/Shared/UI"; 15import getAccountAttribute from "@/helpers/getAccountAttribute"; 16import getFavicon from "@/helpers/getFavicon"; 17import getMentions from "@/helpers/getMentions"; 18import { useTheme } from "@/hooks/useTheme"; 19import { useProModalStore } from "@/store/non-persisted/modal/useProModalStore"; 20import { useAccountStore } from "@/store/persisted/useAccountStore"; 21import ENSBadge from "../Shared/Account/ENSBadge"; 22import CreatorCoin from "./CreatorCoin"; 23import Followerings from "./Followerings"; 24import FollowersYouKnowOverview from "./FollowersYouKnowOverview"; 25import AccountMenu from "./Menu"; 26import MetaDetails from "./MetaDetails"; 27 28interface DetailsProps { 29 isBlockedByMe: boolean; 30 hasBlockedMe: boolean; 31 account: AccountFragment; 32} 33 34const Details = ({ 35 isBlockedByMe = false, 36 hasBlockedMe = false, 37 account 38}: DetailsProps) => { 39 const navigate = useNavigate(); 40 const { currentAccount } = useAccountStore(); 41 const { setShow: setShowProModal } = useProModalStore(); 42 const [showLightBox, setShowLightBox] = useState<boolean>(false); 43 const { theme } = useTheme(); 44 45 const handleShowLightBox = useCallback(() => { 46 setShowLightBox(true); 47 }, []); 48 49 const handleCloseLightBox = useCallback(() => { 50 setShowLightBox(false); 51 }, []); 52 53 const renderAccountAttribute = ( 54 attribute: "location" | "website" | "x", 55 icon: ReactNode 56 ) => { 57 if (isBlockedByMe || hasBlockedMe) return null; 58 59 const value = getAccountAttribute(attribute, account?.metadata?.attributes); 60 if (!value) return null; 61 62 return ( 63 <MetaDetails icon={icon}> 64 <Link 65 rel="noreferrer noopener" 66 target="_blank" 67 to={ 68 attribute === "website" 69 ? `https://${value.replace(/https?:\/\//, "")}` 70 : `https://x.com/${value.replace("https://x.com/", "")}` 71 } 72 > 73 {value.replace(/https?:\/\//, "")} 74 </Link> 75 </MetaDetails> 76 ); 77 }; 78 79 return ( 80 <div className="mb-4 space-y-3 px-5 md:px-0"> 81 <div className="flex items-start justify-between"> 82 <div className="-mt-14 sm:-mt-24 relative ml-5 size-20 sm:size-36"> 83 <Image 84 alt={account.address} 85 className="size-20 cursor-pointer rounded-full bg-gray-200 ring-3 ring-gray-50 sm:size-36 dark:bg-gray-700 dark:ring-black" 86 height={128} 87 onClick={handleShowLightBox} 88 src={getAvatar(account, TRANSFORMS.AVATAR_BIG)} 89 width={128} 90 /> 91 <LightBox 92 images={[getAvatar(account, TRANSFORMS.EXPANDED_AVATAR)]} 93 onClose={handleCloseLightBox} 94 show={showLightBox} 95 /> 96 </div> 97 <div className="flex items-center gap-x-2"> 98 {currentAccount?.address === account.address ? ( 99 <Button onClick={() => navigate("/settings")} outline> 100 Edit Account 101 </Button> 102 ) : isBlockedByMe || hasBlockedMe ? null : ( 103 <FollowUnfollowButton account={account} /> 104 )} 105 {!isBlockedByMe && !hasBlockedMe && account.hasSubscribed && ( 106 <TipButton account={account} /> 107 )} 108 <AccountMenu account={account} /> 109 </div> 110 </div> 111 <div className="space-y-1 py-2"> 112 <div className="flex items-center gap-1.5"> 113 <H3 className="truncate">{getAccount(account).name}</H3> 114 {account.hasSubscribed ? ( 115 <Tooltip content="Verified" placement="right"> 116 <CheckBadgeIcon className="size-5 text-brand-500" /> 117 </Tooltip> 118 ) : currentAccount?.address === account.address ? ( 119 <button 120 className="ml-1 flex items-center gap-x-1 rounded-full border border-gray-200 px-2 py-0.5 font-semibold text-xs dark:border-gray-700" 121 onClick={() => setShowProModal(true)} 122 type="button" 123 > 124 <CheckBadgeIcon className="size-4 text-brand-500" /> 125 Get Verified 126 </button> 127 ) : null} 128 {account.isBeta && ( 129 <Tooltip content="Beta" placement="right"> 130 <BeakerIcon className="size-5 text-green-500" /> 131 </Tooltip> 132 )} 133 <ENSBadge account={account} className="size-5" linkToDashboard /> 134 </div> 135 <div className="flex items-center space-x-3"> 136 <Slug 137 className="text-sm sm:text-base" 138 slug={getAccount(account).username} 139 /> 140 {account.operations?.isFollowingMe ? ( 141 <div className="rounded-full bg-gray-200 px-2 py-0.5 text-xs dark:bg-gray-700"> 142 Follows you 143 </div> 144 ) : null} 145 </div> 146 </div> 147 {!isBlockedByMe && !hasBlockedMe && account?.metadata?.bio ? ( 148 <div className="markup linkify"> 149 <Markup mentions={getMentions(account?.metadata.bio)}> 150 {account?.metadata.bio} 151 </Markup> 152 </div> 153 ) : null} 154 <div className="space-y-5"> 155 <Followerings account={account} /> 156 {!isBlockedByMe && 157 !hasBlockedMe && 158 currentAccount?.address !== account.address ? ( 159 <FollowersYouKnowOverview 160 address={account.address} 161 username={getAccount(account).username} 162 /> 163 ) : null} 164 <div className="flex flex-wrap gap-x-5 gap-y-2"> 165 {!isBlockedByMe && 166 !hasBlockedMe && 167 getAccountAttribute("location", account?.metadata?.attributes) && ( 168 <MetaDetails icon={<MapPinIcon className="size-4" />}> 169 {getAccountAttribute("location", account?.metadata?.attributes)} 170 </MetaDetails> 171 )} 172 {renderAccountAttribute( 173 "website", 174 <img 175 alt="Website" 176 className="size-4 rounded-full" 177 height={16} 178 src={getFavicon( 179 getAccountAttribute("website", account?.metadata?.attributes) 180 )} 181 width={16} 182 /> 183 )} 184 {renderAccountAttribute( 185 "x", 186 <Image 187 alt="X Logo" 188 className="size-4" 189 height={16} 190 src={`${STATIC_IMAGES_URL}/brands/${theme === "dark" ? "x-dark.png" : "x-light.png"}`} 191 width={16} 192 /> 193 )} 194 <CreatorCoin account={account} /> 195 </div> 196 </div> 197 </div> 198 ); 199}; 200 201export default Details;