Skip to content

Commit

Permalink
[Embed] Don't reuse DOM when changing embed (#3530)
Browse files Browse the repository at this point in the history
* Don't reuse DOM when changing embed

* add skeleton loading state 💀

* autoselect text

---------

Co-authored-by: Samuel Newman <[email protected]>
  • Loading branch information
gaearon and mozzius authored Apr 13, 2024
1 parent 1390b1d commit 9fb2091
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 12 deletions.
6 changes: 3 additions & 3 deletions bskyembed/src/components/container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export function Container({
href,
}: {
children: ComponentChildren
href: string
href?: string
}) {
const ref = useRef<HTMLDivElement>(null)
const prevHeight = useRef(0)
Expand Down Expand Up @@ -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')
Expand All @@ -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>
)
Expand Down
49 changes: 40 additions & 9 deletions bskyembed/src/screens/landing.tsx
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -30,13 +30,16 @@ 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,
)

useEffect(() => {
void (async () => {
setError(null)
setThread(null)
setLoading(true)
try {
let atUri = DEFAULT_URI

Expand Down Expand Up @@ -98,6 +101,8 @@ function LandingPage() {
} catch (err) {
console.error(err)
setError(err instanceof Error ? err.message : 'Invalid Bluesky URL')
} finally {
setLoading(false)
}
})()
}, [uri])
Expand All @@ -122,16 +127,39 @@ function LandingPage() {

<img src={arrowBottom as string} className="w-6" />

<div className="w-full max-w-[600px] gap-8 flex flex-col">
{uri && !error && thread && <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">
<p className="text-red-500 text-center">{error}</p>
{loading ? (
<Skeleton />
) : (
<div className="w-full max-w-[600px] gap-8 flex flex-col">
{!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">
<p className="text-red-500 text-center">{error}</p>
</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>
</main>
</Container>
)
}

Expand Down Expand Up @@ -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"
Expand Down

0 comments on commit 9fb2091

Please sign in to comment.