about summary refs log tree commit diff
path: root/bskyembed/src/screens
diff options
context:
space:
mode:
Diffstat (limited to 'bskyembed/src/screens')
-rw-r--r--bskyembed/src/screens/landing.tsx77
-rw-r--r--bskyembed/src/screens/post.tsx20
2 files changed, 76 insertions, 21 deletions
diff --git a/bskyembed/src/screens/landing.tsx b/bskyembed/src/screens/landing.tsx
index a3448e90a..880b71337 100644
--- a/bskyembed/src/screens/landing.tsx
+++ b/bskyembed/src/screens/landing.tsx
@@ -1,12 +1,16 @@
 import '../index.css'
 
-import {AppBskyFeedDefs, AppBskyFeedPost, AtUri, BskyAgent} from '@atproto/api'
+import {AppBskyFeedDefs, AppBskyFeedPost, AtpAgent, AtUri} from '@atproto/api'
 import {h, render} from 'preact'
 import {useEffect, useMemo, useRef, useState} from 'preact/hooks'
 
 import arrowBottom from '../../assets/arrowBottom_stroke2_corner0_rounded.svg'
 import logo from '../../assets/logo.svg'
-import {initColorMode} from '../color-mode'
+import {
+  assertColorModeValues,
+  ColorModeValues,
+  initSystemColorMode,
+} from '../color-mode'
 import {Container} from '../components/container'
 import {Link} from '../components/link'
 import {Post} from '../components/post'
@@ -22,9 +26,9 @@ export const EMBED_SCRIPT = `${EMBED_SERVICE}/static/embed.js`
 const root = document.getElementById('app')
 if (!root) throw new Error('No root element')
 
-initColorMode()
+initSystemColorMode()
 
-const agent = new BskyAgent({
+const agent = new AtpAgent({
   service: 'https://public.api.bsky.app',
 })
 
@@ -32,6 +36,7 @@ render(<LandingPage />, root)
 
 function LandingPage() {
   const [uri, setUri] = useState('')
+  const [colorMode, setColorMode] = useState<ColorModeValues>('system')
   const [error, setError] = useState<string | null>(null)
   const [loading, setLoading] = useState(false)
   const [thread, setThread] = useState<AppBskyFeedDefs.ThreadViewPost | null>(
@@ -120,24 +125,50 @@ function LandingPage() {
 
       <h1 className="text-4xl font-bold text-center">Embed a Bluesky Post</h1>
 
-      <input
-        type="text"
-        value={uri}
-        onInput={e => setUri(e.currentTarget.value)}
-        className="border rounded-lg py-3 w-full max-w-[600px] px-4 dark:bg-dimmedBg dark:border-slate-500"
-        placeholder={DEFAULT_POST}
-      />
+      <div className="flex flex-col w-full max-w-[600px] gap-6">
+        <input
+          type="text"
+          value={uri}
+          onInput={e => setUri(e.currentTarget.value)}
+          className="border rounded-lg py-3 px-4 dark:bg-dimmedBg dark:border-slate-500"
+          placeholder={DEFAULT_POST}
+        />
+
+        <div className="flex flex-col gap-1.5">
+          <label className="text-sm font-medium" for="colorModeSelect">
+            Theme
+          </label>
+          <select
+            value={colorMode}
+            onChange={e => {
+              const value = e.currentTarget.value
+              if (assertColorModeValues(value)) {
+                setColorMode(value)
+              }
+            }}
+            id="colorModeSelect"
+            className="appearance-none bg-white border w-full rounded-lg text-sm px-3 py-2 dark:bg-dimmedBg dark:border-slate-500">
+            <option value="system">System</option>
+            <option value="light">Light</option>
+            <option value="dark">Dark</option>
+          </select>
+        </div>
+      </div>
 
       <img src={arrowBottom} className="w-6 dark:invert" />
 
       {loading ? (
-        <div className="w-full max-w-[600px]">
+        <div className={`${colorMode} w-full max-w-[600px]`}>
           <Skeleton />
         </div>
       ) : (
         <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 && thread && uri && (
+            <Snippet thread={thread} colorMode={colorMode} />
+          )}
+          <div className={colorMode}>
+            {!error && thread && <Post thread={thread} key={thread.post.uri} />}
+          </div>
           {error && (
             <div className="w-full border border-red-500 bg-red-500/10 px-4 py-3 rounded-lg">
               <p className="text-red-500 text-center">{error}</p>
@@ -168,7 +199,13 @@ function Skeleton() {
   )
 }
 
-function Snippet({thread}: {thread: AppBskyFeedDefs.ThreadViewPost}) {
+function Snippet({
+  thread,
+  colorMode,
+}: {
+  thread: AppBskyFeedDefs.ThreadViewPost
+  colorMode: ColorModeValues
+}) {
   const ref = useRef<HTMLInputElement>(null)
   const [copied, setCopied] = useState(false)
 
@@ -204,9 +241,11 @@ function Snippet({thread}: {thread: AppBskyFeedDefs.ThreadViewPost}) {
     // x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x
     return `<blockquote class="bluesky-embed" data-bluesky-uri="${escapeHtml(
       thread.post.uri,
-    )}" data-bluesky-cid="${escapeHtml(thread.post.cid)}"><p lang="${escapeHtml(
-      lang,
-    )}">${escapeHtml(record.text)}${
+    )}" data-bluesky-cid="${escapeHtml(
+      thread.post.cid,
+    )}" data-bluesky-embed-color-mode="${escapeHtml(
+      colorMode,
+    )}"><p lang="${escapeHtml(lang)}">${escapeHtml(record.text)}${
       record.embed
         ? `<br><br><a href="${escapeHtml(href)}">[image or embed]</a>`
         : ''
@@ -217,7 +256,7 @@ function Snippet({thread}: {thread: AppBskyFeedDefs.ThreadViewPost}) {
     )}</a>) <a href="${escapeHtml(href)}">${escapeHtml(
       niceDate(thread.post.indexedAt),
     )}</a></blockquote><script async src="${EMBED_SCRIPT}" charset="utf-8"></script>`
-  }, [thread])
+  }, [thread, colorMode])
 
   return (
     <div className="flex gap-2 w-full">
diff --git a/bskyembed/src/screens/post.tsx b/bskyembed/src/screens/post.tsx
index 1764442b7..4cd72b69b 100644
--- a/bskyembed/src/screens/post.tsx
+++ b/bskyembed/src/screens/post.tsx
@@ -4,7 +4,7 @@ import {AppBskyFeedDefs, AtpAgent} from '@atproto/api'
 import {h, render} from 'preact'
 
 import logo from '../../assets/logo.svg'
-import {initColorMode} from '../color-mode'
+import {applyTheme, initSystemColorMode} from '../color-mode'
 import {Container} from '../components/container'
 import {Link} from '../components/link'
 import {Post} from '../components/post'
@@ -22,7 +22,23 @@ if (!uri) {
   throw new Error('No uri in path')
 }
 
-initColorMode()
+const query = new URLSearchParams(window.location.search)
+
+// theme - default to light mode
+const colorMode = query.get('colorMode')
+
+switch (colorMode) {
+  case 'dark':
+    applyTheme('dark')
+    break
+  case 'system':
+    initSystemColorMode()
+    break
+  case 'light':
+  default:
+    applyTheme('light')
+    break
+}
 
 agent
   .getPostThread({