Hey is a decentralized and permissionless social media app built with Lens Protocol 馃尶
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;