this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 102 lines 3.4 kB view raw
1import type { ProfileRow } from "../../lib/registry.ts"; 2import { PUBLIC_CATEGORIES } from "../../lib/lexicons.ts"; 3import { useT } from "../../i18n/mod.ts"; 4import { bskyCdnAvatarUrl } from "../../lib/avatar.ts"; 5import VerifiedBadge from "../VerifiedBadge.tsx"; 6 7interface Props { 8 profile: ProfileRow; 9} 10 11/** 12 * Listing-grid card. Clicking the card opens the project's profile 13 * detail page (/explore/<handle>) — visitors get the description, 14 * Atmosphere services, Web / iOS / Android links, and any custom buttons 15 * on the detail page. 16 */ 17export default function ProfileCard({ profile }: Props) { 18 const t = useT(); 19 const tCat = t.categories as Record<string, string>; 20 const publicCategories = profile.categories.filter((c) => 21 (PUBLIC_CATEGORIES as readonly string[]).includes(c) 22 ); 23 const appSubcategories = publicCategories.includes("app") 24 ? profile.subcategories.slice(0, 2) 25 : []; 26 const featured = profile.featured; 27 28 return ( 29 <a 30 href={`/explore/${encodeURIComponent(profile.handle)}`} 31 class="glass profile-card" 32 > 33 <div class="profile-card-avatar"> 34 {profile.avatarCid 35 ? ( 36 <img 37 src={bskyCdnAvatarUrl(profile.did, profile.avatarCid)} 38 alt="" 39 loading="lazy" 40 decoding="async" 41 /> 42 ) 43 : ( 44 <div class="profile-card-avatar-fallback" aria-hidden="true"> 45 {profile.name.slice(0, 1).toUpperCase()} 46 </div> 47 )} 48 </div> 49 <div class="profile-card-body"> 50 <div class="profile-card-title-row"> 51 <h3 class="profile-card-name">{profile.name}</h3> 52 {profile.iconAccessStatus === "granted" && ( 53 <VerifiedBadge 54 size={16} 55 /> 56 )} 57 {featured?.badges?.includes("official") && ( 58 <span class="profile-badge profile-badge--official"> 59 {t.badges.official} 60 </span> 61 )} 62 {featured?.badges?.includes("verified") && 63 !featured.badges.includes("official") && ( 64 <span class="profile-badge profile-badge--verified"> 65 {t.badges.verified} 66 </span> 67 )} 68 </div> 69 <p class="profile-card-handle">@{profile.handle}</p> 70 {profile.description && ( 71 <p class="profile-card-description">{profile.description}</p> 72 )} 73 {(publicCategories.length > 0 || appSubcategories.length > 0) && ( 74 <div class="profile-card-meta"> 75 {publicCategories.length > 0 && ( 76 <div class="profile-card-categories"> 77 {publicCategories.map((c) => ( 78 <span key={c} class="profile-card-category"> 79 {tCat[c] ?? c} 80 </span> 81 ))} 82 </div> 83 )} 84 {appSubcategories.length > 0 && ( 85 <div class="profile-card-subcategories"> 86 {appSubcategories.map((s) => { 87 const sub = (t.subcategories as Record<string, string>)[s] ?? 88 s; 89 return ( 90 <span key={s} class="profile-card-sub"> 91 {sub} 92 </span> 93 ); 94 })} 95 </div> 96 )} 97 </div> 98 )} 99 </div> 100 </a> 101 ); 102}