From 7b1a34e0642324b3301cf0355614992eb6d5a461 Mon Sep 17 00:00:00 2001 From: kakkokari-gtyih <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Tue, 19 Nov 2024 17:58:48 +0900 Subject: [PATCH 01/10] feat(embed): Support dark mode (wip) --- bskyembed/snippet/embed.ts | 5 +- bskyembed/src/color-mode.ts | 17 +++++ bskyembed/src/components/container.tsx | 2 +- bskyembed/src/components/embed.tsx | 34 +++++----- bskyembed/src/components/post.tsx | 18 +++--- bskyembed/src/index.css | 4 ++ bskyembed/src/screens/landing.tsx | 87 ++++++++++++++++++++------ bskyembed/src/screens/post.tsx | 9 ++- bskyembed/tailwind.config.cjs | 9 +++ bskyembed/tsconfig.json | 3 +- 10 files changed, 139 insertions(+), 49 deletions(-) create mode 100644 bskyembed/src/color-mode.ts diff --git a/bskyembed/snippet/embed.ts b/bskyembed/snippet/embed.ts index 380cda5fb93..4a56539b3ce 100644 --- a/bskyembed/snippet/embed.ts +++ b/bskyembed/snippet/embed.ts @@ -20,6 +20,7 @@ window.addEventListener('message', event => { return } + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const id = (event.data as {id: string}).id if (!id) { return @@ -33,6 +34,7 @@ window.addEventListener('message', event => { return } + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const height = (event.data as {height: number}).height if (height) { embed.style.height = `${height}px` @@ -47,7 +49,7 @@ window.addEventListener('message', event => { * @returns */ function scan(node = document) { - const embeds = node.querySelectorAll('[data-bluesky-uri]') + const embeds = node.querySelectorAll('[data-bluesky-uri]') for (let i = 0; i < embeds.length; i++) { const id = String(Math.random()).slice(2) @@ -66,6 +68,7 @@ function scan(node = document) { if (ref_url.startsWith('http')) { searchParams.set('ref_url', encodeURIComponent(ref_url)) } + searchParams.set('colorMode', embed.dataset.blueskyColorMode || 'auto') const iframe = document.createElement('iframe') iframe.setAttribute('data-bluesky-id', id) diff --git a/bskyembed/src/color-mode.ts b/bskyembed/src/color-mode.ts new file mode 100644 index 00000000000..2b392c6178e --- /dev/null +++ b/bskyembed/src/color-mode.ts @@ -0,0 +1,17 @@ +export function applyTheme(theme: 'light' | 'dark') { + document.documentElement.classList.remove('light', 'dark') + document.documentElement.classList.add(theme) +} + +export function initColorMode() { + applyTheme( + window.matchMedia('(prefers-color-scheme: dark)').matches + ? 'dark' + : 'light', + ) + window + .matchMedia('(prefers-color-scheme: dark)') + .addEventListener('change', mql => { + applyTheme(mql.matches ? 'dark' : 'light') + }) +} diff --git a/bskyembed/src/components/container.tsx b/bskyembed/src/components/container.tsx index 5b1b2b7fb43..906c1c46537 100644 --- a/bskyembed/src/components/container.tsx +++ b/bskyembed/src/components/container.tsx @@ -37,7 +37,7 @@ export function Container({ return (
{ if (ref.current && href) { // forwardRef requires preact/compat - let's keep it simple diff --git a/bskyembed/src/components/embed.tsx b/bskyembed/src/components/embed.tsx index 82c3fd60a04..980cd462f8a 100644 --- a/bskyembed/src/components/embed.tsx +++ b/bskyembed/src/components/embed.tsx @@ -79,9 +79,9 @@ export function Embed({ return ( + className="transition-colors hover:bg-neutral-100 dark:hover:bg-slate-700 border dark:border-slate-600 rounded-lg p-2 gap-1.5 w-full flex flex-col">
-
+

{record.author.displayName} - + @{record.author.handle}

@@ -210,7 +210,7 @@ function Info({children}: {children: ComponentChildren}) { return (
-

{children}

+

{children}

) } @@ -309,7 +309,7 @@ function ExternalEmbed({ return ( {content.external.thumb && ( )}
-

+

{toNiceDomain(content.external.uri)}

{content.external.title}

-

+

{content.external.description}

@@ -346,23 +346,29 @@ function GenericWithImageEmbed({ return ( + className="w-full rounded-lg border dark:border-slate-600 py-2 px-3 flex flex-col gap-2">
{image ? ( {title} ) : (
)}

{title}

-

{subtitle}

+

+ {subtitle} +

- {description &&

{description}

} + {description && ( +

+ {description} +

+ )} ) } @@ -407,7 +413,7 @@ function StarterPackEmbed({ return ( + className="w-full rounded-lg overflow-hidden border dark:border-slate-600 flex flex-col items-stretch">
@@ -416,7 +422,7 @@ function StarterPackEmbed({

{content.record.name}

-

+

Starter pack by{' '} {content.creator.displayName || `@${content.creator.handle}`}

@@ -426,7 +432,7 @@ function StarterPackEmbed({

{content.record.description}

)} {!!content.joinedAllTimeCount && content.joinedAllTimeCount > 50 && ( -

+

{content.joinedAllTimeCount} users have joined!

)} diff --git a/bskyembed/src/components/post.tsx b/bskyembed/src/components/post.tsx index 4db5eeb45ee..26945eb69d8 100644 --- a/bskyembed/src/components/post.tsx +++ b/bskyembed/src/components/post.tsx @@ -38,7 +38,7 @@ export function Post({thread}: Props) {
-
+
+ className="text-[15px] text-textLight dark:text-textDimmed hover:underline line-clamp-1">

@{post.author.handle}

@@ -69,15 +69,15 @@ export function Post({thread}: Props) { -
+
{!!post.likeCount && (
-

+

{prettyNumber(post.likeCount)}

@@ -85,17 +85,19 @@ export function Post({thread}: Props) { {!!post.repostCount && (
-

+

{prettyNumber(post.repostCount)}

)}
-

Reply

+

+ Reply +

-

+

{post.replyCount ? `Read ${prettyNumber(post.replyCount)} ${ post.replyCount > 1 ? 'replies' : 'reply' diff --git a/bskyembed/src/index.css b/bskyembed/src/index.css index 22b2b8be5c7..289e34cf00f 100644 --- a/bskyembed/src/index.css +++ b/bskyembed/src/index.css @@ -5,3 +5,7 @@ .break-word { word-break: break-word; } + +:root { + color-scheme: light dark; +} diff --git a/bskyembed/src/screens/landing.tsx b/bskyembed/src/screens/landing.tsx index a9e08cd3f27..14aa0497cd6 100644 --- a/bskyembed/src/screens/landing.tsx +++ b/bskyembed/src/screens/landing.tsx @@ -6,6 +6,7 @@ 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 {Container} from '../components/container' import {Link} from '../components/link' import {Post} from '../components/post' @@ -21,14 +22,22 @@ export const EMBED_SCRIPT = `${EMBED_SERVICE}/static/embed.js` const root = document.getElementById('app') if (!root) throw new Error('No root element') +initColorMode() + const agent = new BskyAgent({ service: 'https://public.api.bsky.app', }) render(, root) +type ColorModeValues = 'auto' | 'light' | 'dark' +function assertColorModeValues(value: string): value is ColorModeValues { + return ['auto', 'light', 'dark'].includes(value) +} + function LandingPage() { const [uri, setUri] = useState('') + const [colorMode, setColorMode] = useState<'auto' | 'light' | 'dark'>('auto') const [error, setError] = useState(null) const [loading, setLoading] = useState(false) const [thread, setThread] = useState( @@ -108,7 +117,7 @@ function LandingPage() { }, [uri]) return ( -

+
@@ -121,20 +130,50 @@ function LandingPage() { type="text" value={uri} onInput={e => setUri(e.currentTarget.value)} - className="border rounded-lg py-3 w-full max-w-[600px] px-4" + className="border rounded-lg py-3 w-full max-w-[600px] px-4 dark:bg-dimmedBg dark:border-slate-500" placeholder={DEFAULT_POST} /> - +
+ + Want more customize? + +
+
+ + +
+
+
+ + {loading ? ( - +
+ +
) : (
- {!error && thread && uri && } - {!error && thread && } + {!error && thread && uri && ( + + )} +
+ {!error && thread && } +
{error && ( -
+

{error}

)} @@ -149,21 +188,27 @@ function Skeleton() {
-
+
-
-
+
+
-
-
-
+
+
+
) } -function Snippet({thread}: {thread: AppBskyFeedDefs.ThreadViewPost}) { +function Snippet({ + thread, + colorMode, +}: { + thread: AppBskyFeedDefs.ThreadViewPost + colorMode: 'auto' | 'light' | 'dark' +}) { const ref = useRef(null) const [copied, setCopied] = useState(false) @@ -199,9 +244,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 `

${escapeHtml(record.text)}${ + )}" data-bluesky-cid="${escapeHtml( + thread.post.cid, + )}" data-bluesky-embed-color-mode="${escapeHtml( + colorMode, + )}">

${escapeHtml(record.text)}${ record.embed ? `

[image or embed]` : '' @@ -212,7 +259,7 @@ function Snippet({thread}: {thread: AppBskyFeedDefs.ThreadViewPost}) { )}) ${escapeHtml( niceDate(thread.post.indexedAt), )}

` - }, [thread]) + }, [thread, colorMode]) return (
@@ -220,7 +267,7 @@ function Snippet({thread}: {thread: AppBskyFeedDefs.ThreadViewPost}) { ref={ref} type="text" value={snippet} - className="border rounded-lg py-3 w-full px-4" + className="border rounded-lg py-3 w-full px-4 dark:bg-dimmedBg dark:border-slate-500" readOnly autoFocus onFocus={() => { @@ -228,7 +275,7 @@ function Snippet({thread}: {thread: AppBskyFeedDefs.ThreadViewPost}) { }} />