about summary refs log tree commit diff
path: root/bskyembed
diff options
context:
space:
mode:
authordan <dan.abramov@gmail.com>2024-04-13 12:19:21 +0100
committerGitHub <noreply@github.com>2024-04-13 12:19:21 +0100
commit9fb20915e890be0993c15ad19b59105b78cf8f12 (patch)
tree5428a2f5c2eb93fa116af1afe6c71bfbc0a86f44 /bskyembed
parent1390b1dc9e35aafda328877c46e90860c6268453 (diff)
downloadvoidsky-9fb20915e890be0993c15ad19b59105b78cf8f12.tar.zst
[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>
Diffstat (limited to 'bskyembed')
-rw-r--r--bskyembed/src/components/container.tsx6
-rw-r--r--bskyembed/src/screens/landing.tsx49
2 files changed, 43 insertions, 12 deletions
diff --git a/bskyembed/src/components/container.tsx b/bskyembed/src/components/container.tsx
index a96addc8c..5b1b2b7fb 100644
--- a/bskyembed/src/components/container.tsx
+++ b/bskyembed/src/components/container.tsx
@@ -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>
   )
diff --git a/bskyembed/src/screens/landing.tsx b/bskyembed/src/screens/landing.tsx
index f10100baa..0c5508935 100644
--- a/bskyembed/src/screens/landing.tsx
+++ b/bskyembed/src/screens/landing.tsx
@@ -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,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>
   )
 }
 
@@ -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"