Bluesky app fork with some witchin' additions 💫

[Embed] Don't reuse DOM when changing embed (#3530)

* Don't reuse DOM when changing embed

* add skeleton loading state 💀

* autoselect text

---------

Co-authored-by: Samuel Newman <mozzius@protonmail.com>

authored by danabra.mov

Samuel Newman and committed by
GitHub
9fb20915 1390b1dc

+43 -12
+3 -3
bskyembed/src/components/container.tsx
··· 8 8 href, 9 9 }: { 10 10 children: ComponentChildren 11 - href: string 11 + href?: string 12 12 }) { 13 13 const ref = useRef<HTMLDivElement>(null) 14 14 const prevHeight = useRef(0) ··· 39 39 ref={ref} 40 40 className="w-full bg-white hover:bg-neutral-50 relative transition-colors max-w-[600px] min-w-[300px] flex border rounded-xl" 41 41 onClick={() => { 42 - if (ref.current) { 42 + if (ref.current && href) { 43 43 // forwardRef requires preact/compat - let's keep it simple 44 44 // to keep the bundle size down 45 45 const anchor = ref.current.querySelector('a') ··· 48 48 } 49 49 } 50 50 }}> 51 - <Link href={href} /> 51 + {href && <Link href={href} />} 52 52 <div className="flex-1 px-4 pt-3 pb-2.5">{children}</div> 53 53 </div> 54 54 )
+40 -9
bskyembed/src/screens/landing.tsx
··· 1 1 import '../index.css' 2 2 3 3 import {AppBskyFeedDefs, AppBskyFeedPost, AtUri, BskyAgent} from '@atproto/api' 4 - import {Fragment, h, render} from 'preact' 4 + import {h, render} from 'preact' 5 5 import {useEffect, useMemo, useRef, useState} from 'preact/hooks' 6 6 7 7 import arrowBottom from '../../assets/arrowBottom_stroke2_corner0_rounded.svg' ··· 30 30 function LandingPage() { 31 31 const [uri, setUri] = useState('') 32 32 const [error, setError] = useState<string | null>(null) 33 + const [loading, setLoading] = useState(false) 33 34 const [thread, setThread] = useState<AppBskyFeedDefs.ThreadViewPost | null>( 34 35 null, 35 36 ) ··· 37 38 useEffect(() => { 38 39 void (async () => { 39 40 setError(null) 41 + setThread(null) 42 + setLoading(true) 40 43 try { 41 44 let atUri = DEFAULT_URI 42 45 ··· 98 101 } catch (err) { 99 102 console.error(err) 100 103 setError(err instanceof Error ? err.message : 'Invalid Bluesky URL') 104 + } finally { 105 + setLoading(false) 101 106 } 102 107 })() 103 108 }, [uri]) ··· 122 127 123 128 <img src={arrowBottom as string} className="w-6" /> 124 129 125 - <div className="w-full max-w-[600px] gap-8 flex flex-col"> 126 - {uri && !error && thread && <Snippet thread={thread} />} 127 - {!error && thread && <Post thread={thread} key={thread.post.uri} />} 128 - {error && ( 129 - <div className="w-full border border-red-500 bg-red-50 px-4 py-3 rounded-lg"> 130 - <p className="text-red-500 text-center">{error}</p> 130 + {loading ? ( 131 + <Skeleton /> 132 + ) : ( 133 + <div className="w-full max-w-[600px] gap-8 flex flex-col"> 134 + {!error && thread && uri && <Snippet thread={thread} />} 135 + {!error && thread && <Post thread={thread} key={thread.post.uri} />} 136 + {error && ( 137 + <div className="w-full border border-red-500 bg-red-50 px-4 py-3 rounded-lg"> 138 + <p className="text-red-500 text-center">{error}</p> 139 + </div> 140 + )} 141 + </div> 142 + )} 143 + </main> 144 + ) 145 + } 146 + 147 + function Skeleton() { 148 + return ( 149 + <Container> 150 + <div className="flex-1 flex-col flex gap-2 pb-8"> 151 + <div className="flex gap-2.5 items-center"> 152 + <div className="w-10 h-10 overflow-hidden rounded-full bg-neutral-100 shrink-0 animate-pulse" /> 153 + <div className="flex-1"> 154 + <div className="bg-neutral-100 animate-pulse w-64 h-4 rounded" /> 155 + <div className="bg-neutral-100 animate-pulse w-32 h-3 mt-1 rounded" /> 131 156 </div> 132 - )} 157 + </div> 158 + <div className="w-full h-4 mt-2 bg-neutral-100 rounded animate-pulse" /> 159 + <div className="w-5/6 h-4 bg-neutral-100 rounded animate-pulse" /> 160 + <div className="w-3/4 h-4 bg-neutral-100 rounded animate-pulse" /> 133 161 </div> 134 - </main> 162 + </Container> 135 163 ) 136 164 } 137 165 ··· 195 223 className="border rounded-lg py-3 w-full px-4" 196 224 readOnly 197 225 autoFocus 226 + onFocus={() => { 227 + ref.current?.select() 228 + }} 198 229 /> 199 230 <button 200 231 className="rounded-lg bg-brand text-white color-white py-3 px-4 whitespace-nowrap min-w-28"