an independent Bluesky client using Constellation, PDS Queries, and other services reddwarf.app
frontend spa bluesky reddwarf microcosm client app
99
fork

Configure Feed

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

at 24efdc839aa8e9a9a563f2c8ef1f7055a100787f 165 lines 6.1 kB view raw
1import { 2 createFileRoute, 3 useNavigate, 4 type UseNavigateResult, 5} from "@tanstack/react-router"; 6import { useEffect, useState } from "react"; 7import { createPortal } from "react-dom"; 8 9import { ProfilePostComponent } from "./post.$rkey"; 10 11export const Route = createFileRoute("/profile/$did/post/$rkey/image/$i")({ 12 component: Lightbox, 13}); 14 15export type LightboxProps = { 16 images: { src: string; alt?: string }[]; 17}; 18 19function nextprev({ 20 index, 21 images, 22 navigate, 23 did, 24 rkey, 25 prev, 26}: { 27 index?: number; 28 images?: LightboxProps["images"]; 29 navigate: UseNavigateResult<string>; 30 did: string; 31 rkey: string; 32 prev?: boolean; 33}) { 34 const len = images?.length ?? 0; 35 if (len === 0) return; 36 37 const nextIndex = ((index ?? 0) + (prev ? -1 : 1) + len) % len; 38 39 navigate({ 40 to: "/profile/$did/post/$rkey/image/$i", 41 params: { 42 did, 43 rkey, 44 i: nextIndex.toString(), 45 }, 46 replace: true, 47 }); 48} 49 50export function Lightbox() { 51 console.log("hey the $i route is loaded w!!!"); 52 const { did, rkey, i } = Route.useParams(); 53 const [images, setImages] = useState<LightboxProps["images"] | undefined>( 54 undefined 55 ); 56 const index = Number(i); 57 const navigate = useNavigate(); 58 const post = true; 59 const image = images?.[index] ?? undefined; 60 61 function lightboxCallback(d: LightboxProps) { 62 console.log("callback actually called!"); 63 setImages(d.images); 64 } 65 66 useEffect(() => { 67 function handleKey(e: KeyboardEvent) { 68 if (e.key === "Escape") window.history.back(); 69 if (e.key === "ArrowRight") 70 nextprev({ index, images, navigate, did, rkey }); 71 //onNavigate((index + 1) % images.length); 72 if (e.key === "ArrowLeft") 73 nextprev({ index, images, navigate, did, rkey, prev: true }); 74 //onNavigate((index - 1 + images.length) % images.length); 75 } 76 window.addEventListener("keydown", handleKey); 77 return () => window.removeEventListener("keydown", handleKey); 78 }, [index, navigate, did, rkey, images]); 79 80 return createPortal( 81 <> 82 {post && ( 83 <div 84 onClick={(e) => { 85 e.stopPropagation(); 86 e.nativeEvent.stopImmediatePropagation(); 87 }} 88 className="lightbox-sidebar hidden lg:flex overscroll-none disablegutter disablescroll border-l dark:border-gray-800 was7 border-gray-300 fixed z-50 top-0 right-0 flex-col max-w-[350px] min-w-[350px] max-h-screen overflow-y-scroll dark:bg-gray-950 bg-white" 89 > 90 <ProfilePostComponent 91 key={`/profile/${did}/post/${rkey}`} 92 did={did} 93 rkey={rkey} 94 nopics 95 lightboxCallback={lightboxCallback} 96 /> 97 </div> 98 )} 99 <div 100 className="lightbox fixed inset-0 z-50 flex items-center justify-center bg-black/80 w-screen lg:w-[calc(100vw-350px)] lg:max-w-[calc(100vw-350px)]" 101 onClick={(e) => { 102 e.stopPropagation(); 103 window.history.back(); 104 }} 105 > 106 <img 107 src={image?.src} 108 alt={image?.alt} 109 className="max-h-[90%] max-w-[90%] object-contain rounded-lg shadow-lg" 110 onClick={(e) => e.stopPropagation()} 111 /> 112 113 {(images?.length ?? 0) > 1 && ( 114 <> 115 <button 116 onClick={(e) => { 117 e.stopPropagation(); 118 nextprev({ index, images, navigate, did, rkey, prev: true }); 119 }} 120 className="absolute left-4 top-1/2 -translate-y-1/2 text-white text-4xl h-8 w-8 rounded-full bg-gray-900 flex items-center justify-center" 121 > 122 <svg 123 xmlns="http://www.w3.org/2000/svg" 124 width={28} 125 height={28} 126 viewBox="0 0 24 24" 127 > 128 <g fill="none" fillRule="evenodd"> 129 <path d="M24 0v24H0V0zM12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.019-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z"></path> 130 <path 131 fill="currentColor" 132 d="M8.293 12.707a1 1 0 0 1 0-1.414l5.657-5.657a1 1 0 1 1 1.414 1.414L10.414 12l4.95 4.95a1 1 0 0 1-1.414 1.414z" 133 ></path> 134 </g> 135 </svg> 136 </button> 137 <button 138 onClick={(e) => { 139 e.stopPropagation(); 140 nextprev({ index, images, navigate, did, rkey }); 141 }} 142 className="absolute right-4 top-1/2 -translate-y-1/2 text-white text-4xl h-8 w-8 rounded-full bg-gray-900 flex items-center justify-center" 143 > 144 <svg 145 xmlns="http://www.w3.org/2000/svg" 146 width={28} 147 height={28} 148 viewBox="0 0 24 24" 149 > 150 <g fill="none" fillRule="evenodd"> 151 <path d="M24 0v24H0V0zM12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.019-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z"></path> 152 <path 153 fill="currentColor" 154 d="M15.707 11.293a1 1 0 0 1 0 1.414l-5.657 5.657a1 1 0 1 1-1.414-1.414l4.95-4.95l-4.95-4.95a1 1 0 0 1 1.414-1.414z" 155 ></path> 156 </g> 157 </svg> 158 </button> 159 </> 160 )} 161 </div> 162 </>, 163 document.body 164 ); 165}