Fork of atp.tools as a universal profile for people on the ATmosphere
at main 90 lines 2.7 kB view raw
1import { AppBskyEmbedImages } from "@atcute/client/lexicons"; 2import { X } from "lucide-react"; 3import { useState } from "preact/hooks"; 4 5export const getBlueskyCdnLink = ( 6 did: string, 7 cid: string, 8 ext: string, 9 type: string = "feed_fullsize", 10) => { 11 return `https://cdn.bsky.app/img/${type}/plain/${did}/${cid}@${ext}`; 12}; 13 14export const AppBskyEmbedImagesLayout = ({ 15 did, 16 images, 17}: { 18 did: string; 19 images: AppBskyEmbedImages.Image[]; 20}) => { 21 const [selectedImage, setSelectedImage] = useState<number | null>(null); 22 const imageCount = images.length; 23 24 // Different grid layouts based on number of images 25 const gridClassName = 26 { 27 1: "grid-cols-1", 28 2: "grid-cols-2", 29 3: "grid-cols-2", 30 4: "grid-cols-2", 31 }[Math.min(imageCount, 4)] || "grid-cols-2"; 32 33 return ( 34 <> 35 <div className={`grid ${gridClassName} gap-2 w-full`}> 36 {images.map((image, i) => ( 37 <div 38 key={i} 39 className={`relative overflow-hidden rounded-lg cursor-pointer ${ 40 imageCount === 3 && i === 0 ? "col-span-2" : "" 41 }`} 42 onClick={() => setSelectedImage(i)} 43 > 44 <img 45 src={getBlueskyCdnLink(did, image.image.ref.$link, "jpeg")} 46 alt="" 47 className={`w-full max-w-96 h-full cursor-pointer object-cover transition-transform duration-300 hover:scale-[101%] ${imageCount > 1 && "max-h-64"}`} 48 style={{ 49 aspectRatio: imageCount === 1 ? "" : "1/1", 50 }} 51 loading="lazy" 52 /> 53 </div> 54 ))} 55 </div> 56 57 {selectedImage !== null && ( 58 <> 59 {/* Image Preview */} 60 <div 61 className="fixed inset-0 bg-black/80 flex flex-col gap-2 items-center justify-center z-50" 62 onClick={() => setSelectedImage(null)} 63 > 64 <img 65 src={getBlueskyCdnLink( 66 did, 67 images[selectedImage].image.ref.$link, 68 "png", 69 )} 70 className="max-h-[90vh] max-w-[90vw] object-contain" 71 /> 72 {images[selectedImage].alt && ( 73 <div className="text-white"> 74 Alt text: {images[selectedImage].alt} 75 </div> 76 )} 77 </div> 78 <div className="fixed top-2 right-2 z-50"> 79 <button 80 className="text-blue-100 hover:text-red-400 transition-colors duration-300" 81 onClick={() => setSelectedImage(null)} 82 > 83 <X /> 84 </button> 85 </div> 86 </> 87 )} 88 </> 89 ); 90};