[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>
zio/stable
dan 2024-04-13 12:19:21 +01:00 committed by GitHub
parent 1390b1dc9e
commit 9fb20915e8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 44 additions and 13 deletions

View File

@ -8,7 +8,7 @@ export function Container({
href,
}: {
children: ComponentChildren
href: string
href?: string
}) {
const ref = useRef<HTMLDivElement>(null)
const prevHeight = useRef(0)
@ -39,7 +39,7 @@ export function Container({
ref={ref}
className="w-full bg-white hover:bg-neutral-50 relative transition-colors max-w-[600px] min-w-[300px] flex border rounded-xl"
onClick={() => {
if (ref.current) {
if (ref.current && href) {
// forwardRef requires preact/compat - let's keep it simple
// to keep the bundle size down
const anchor = ref.current.querySelector('a')
@ -48,7 +48,7 @@ export function Container({
}
}
}}>
<Link href={href} />
{href && <Link href={href} />}
<div className="flex-1 px-4 pt-3 pb-2.5">{children}</div>
</div>
)

View File

@ -1,7 +1,7 @@
import '../index.css'
import {AppBskyFeedDefs, AppBskyFeedPost, AtUri, BskyAgent} from '@atproto/api'
import {Fragment, h, render} from 'preact'
import {h, render} from 'preact'
import {useEffect, useMemo, useRef, useState} from 'preact/hooks'
import arrowBottom from '../../assets/arrowBottom_stroke2_corner0_rounded.svg'
@ -30,6 +30,7 @@ render(<LandingPage />, root)
function LandingPage() {
const [uri, setUri] = useState('')
const [error, setError] = useState<string | null>(null)
const [loading, setLoading] = useState(false)
const [thread, setThread] = useState<AppBskyFeedDefs.ThreadViewPost | null>(
null,
)
@ -37,6 +38,8 @@ function LandingPage() {
useEffect(() => {
void (async () => {
setError(null)
setThread(null)
setLoading(true)
try {
let atUri = DEFAULT_URI
@ -98,6 +101,8 @@ function LandingPage() {
} catch (err) {
console.error(err)
setError(err instanceof Error ? err.message : 'Invalid Bluesky URL')
} finally {
setLoading(false)
}
})()
}, [uri])
@ -122,8 +127,11 @@ function LandingPage() {
<img src={arrowBottom as string} className="w-6" />
{loading ? (
<Skeleton />
) : (
<div className="w-full max-w-[600px] gap-8 flex flex-col">
{uri && !error && thread && <Snippet thread={thread} />}
{!error && thread && uri && <Snippet thread={thread} />}
{!error && thread && <Post thread={thread} key={thread.post.uri} />}
{error && (
<div className="w-full border border-red-500 bg-red-50 px-4 py-3 rounded-lg">
@ -131,10 +139,30 @@ function LandingPage() {
</div>
)}
</div>
)}
</main>
)
}
function Skeleton() {
return (
<Container>
<div className="flex-1 flex-col flex gap-2 pb-8">
<div className="flex gap-2.5 items-center">
<div className="w-10 h-10 overflow-hidden rounded-full bg-neutral-100 shrink-0 animate-pulse" />
<div className="flex-1">
<div className="bg-neutral-100 animate-pulse w-64 h-4 rounded" />
<div className="bg-neutral-100 animate-pulse w-32 h-3 mt-1 rounded" />
</div>
</div>
<div className="w-full h-4 mt-2 bg-neutral-100 rounded animate-pulse" />
<div className="w-5/6 h-4 bg-neutral-100 rounded animate-pulse" />
<div className="w-3/4 h-4 bg-neutral-100 rounded animate-pulse" />
</div>
</Container>
)
}
function Snippet({thread}: {thread: AppBskyFeedDefs.ThreadViewPost}) {
const ref = useRef<HTMLInputElement>(null)
const [copied, setCopied] = useState(false)
@ -195,6 +223,9 @@ function Snippet({thread}: {thread: AppBskyFeedDefs.ThreadViewPost}) {
className="border rounded-lg py-3 w-full px-4"
readOnly
autoFocus
onFocus={() => {
ref.current?.select()
}}
/>
<button
className="rounded-lg bg-brand text-white color-white py-3 px-4 whitespace-nowrap min-w-28"