about summary refs log tree commit diff
path: root/bskyembed/src/components
diff options
context:
space:
mode:
Diffstat (limited to 'bskyembed/src/components')
-rw-r--r--bskyembed/src/components/container.tsx2
-rw-r--r--bskyembed/src/components/embed.tsx40
-rw-r--r--bskyembed/src/components/post.tsx50
-rw-r--r--bskyembed/src/components/verification-check.tsx56
4 files changed, 124 insertions, 24 deletions
diff --git a/bskyembed/src/components/container.tsx b/bskyembed/src/components/container.tsx
index 8e142a25b..bafc497ae 100644
--- a/bskyembed/src/components/container.tsx
+++ b/bskyembed/src/components/container.tsx
@@ -49,7 +49,7 @@ export function Container({
         }
       }}>
       {href && <Link href={href} />}
-      <div className="flex-1 px-4 pt-3 pb-2.5">{children}</div>
+      <div className="flex-1 px-4 pt-3 pb-2.5 max-w-full">{children}</div>
     </div>
   )
 }
diff --git a/bskyembed/src/components/embed.tsx b/bskyembed/src/components/embed.tsx
index 428782b64..52618a89d 100644
--- a/bskyembed/src/components/embed.tsx
+++ b/bskyembed/src/components/embed.tsx
@@ -17,8 +17,11 @@ import infoIcon from '../../assets/circleInfo_stroke2_corner0_rounded.svg'
 import playIcon from '../../assets/play_filled_corner2_rounded.svg'
 import starterPackIcon from '../../assets/starterPack.svg'
 import {CONTENT_LABELS, labelsToInfo} from '../labels'
-import {getRkey} from '../utils'
+import * as bsky from '../types/bsky'
+import {getRkey} from '../util/rkey'
+import {getVerificationState} from '../util/verification-state'
 import {Link} from './link'
+import {VerificationCheck} from './verification-check'
 
 export function Embed({
   content,
@@ -75,23 +78,35 @@ export function Embed({
           CONTENT_LABELS.includes(label.val),
         )
 
+        const verification = getVerificationState({profile: record.author})
+
         return (
           <Link
             href={`/profile/${record.author.did}/post/${getRkey(record)}`}
             className="transition-colors hover:bg-neutral-100 dark:hover:bg-slate-700 border dark:border-slate-600 rounded-xl p-2 gap-1.5 w-full flex flex-col">
             <div className="flex gap-1.5 items-center">
-              <div className="w-4 h-4 overflow-hidden rounded-full bg-neutral-300 dark:bg-slate-700 shrink-0">
+              <div className="w-4 h-4 rounded-full bg-neutral-300 dark:bg-slate-700 shrink-0">
                 <img
+                  className="rounded-full"
                   src={record.author.avatar}
                   style={isAuthorLabeled ? {filter: 'blur(1.5px)'} : undefined}
                 />
               </div>
-              <p className="line-clamp-1 text-sm">
-                <span className="font-bold">{record.author.displayName}</span>
-                <span className="text-textLight dark:text-textDimmed ml-1">
+              <div className="flex flex-1 items-center shrink min-w-0 min-h-0">
+                <p className="block text-sm shrink-0 font-bold max-w-[70%] line-clamp-1">
+                  {record.author.displayName?.trim() || record.author.handle}
+                </p>
+                {verification.isVerified && (
+                  <VerificationCheck
+                    className="ml-[3px] mt-px shrink-0 self-center"
+                    verifier={verification.role === 'verifier'}
+                    size={12}
+                  />
+                )}
+                <p className="block line-clamp-1 text-sm text-textLight dark:text-textDimmed shrink-[10] ml-1">
                   @{record.author.handle}
-                </span>
-              </p>
+                </p>
+              </div>
             </div>
             {text && <p className="text-sm">{text}</p>}
             {record.embeds?.map(embed => (
@@ -404,7 +419,12 @@ function StarterPackEmbed({
 }: {
   content: AppBskyGraphDefs.StarterPackViewBasic
 }) {
-  if (!AppBskyGraphStarterpack.isRecord(content.record)) {
+  if (
+    !bsky.dangerousIsType<AppBskyGraphStarterpack.Record>(
+      content.record,
+      AppBskyGraphStarterpack.isRecord,
+    )
+  ) {
     return null
   }
 
@@ -443,7 +463,9 @@ function StarterPackEmbed({
 }
 
 // from #/lib/strings/starter-pack.ts
-function getStarterPackImage(starterPack: AppBskyGraphDefs.StarterPackView) {
+function getStarterPackImage(
+  starterPack: AppBskyGraphDefs.StarterPackViewBasic,
+) {
   const rkey = getRkey({uri: starterPack.uri})
   return `https://ogcard.cdn.bsky.app/start/${starterPack.creator.did}/${rkey}`
 }
diff --git a/bskyembed/src/components/post.tsx b/bskyembed/src/components/post.tsx
index 6ecac5796..d216ce0e5 100644
--- a/bskyembed/src/components/post.tsx
+++ b/bskyembed/src/components/post.tsx
@@ -11,10 +11,15 @@ import likeIcon from '../../assets/heart2_filled_stroke2_corner0_rounded.svg'
 import logo from '../../assets/logo.svg'
 import repostIcon from '../../assets/repost_stroke2_corner2_rounded.svg'
 import {CONTENT_LABELS} from '../labels'
-import {getRkey, niceDate, prettyNumber} from '../utils'
+import * as bsky from '../types/bsky'
+import {niceDate} from '../util/nice-date'
+import {prettyNumber} from '../util/pretty-number'
+import {getRkey} from '../util/rkey'
+import {getVerificationState} from '../util/verification-state'
 import {Container} from './container'
 import {Embed} from './embed'
 import {Link} from './link'
+import {VerificationCheck} from './verification-check'
 
 interface Props {
   thread: AppBskyFeedDefs.ThreadViewPost
@@ -28,16 +33,25 @@ export function Post({thread}: Props) {
   )
 
   let record: AppBskyFeedPost.Record | null = null
-  if (AppBskyFeedPost.isRecord(post.record)) {
+  if (
+    bsky.dangerousIsType<AppBskyFeedPost.Record>(
+      post.record,
+      AppBskyFeedPost.isRecord,
+    )
+  ) {
     record = post.record
   }
 
+  const verification = getVerificationState({profile: post.author})
+
   const href = `/profile/${post.author.did}/post/${getRkey(post)}`
   return (
     <Container href={href}>
       <div className="flex-1 flex-col flex gap-2" lang={record?.langs?.[0]}>
-        <div className="flex gap-2.5 items-center cursor-pointer">
-          <Link href={`/profile/${post.author.did}`} className="rounded-full">
+        <div className="flex gap-2.5 items-center cursor-pointer w-full max-w-full">
+          <Link
+            href={`/profile/${post.author.did}`}
+            className="rounded-full shrink-0">
             <div className="w-10 h-10 overflow-hidden rounded-full bg-neutral-300 dark:bg-slate-700 shrink-0">
               <img
                 src={post.author.avatar}
@@ -45,19 +59,27 @@ export function Post({thread}: Props) {
               />
             </div>
           </Link>
-          <div>
-            <Link
-              href={`/profile/${post.author.did}`}
-              className="font-bold text-[17px] leading-5 line-clamp-1 hover:underline underline-offset-2 decoration-2">
-              <p>{post.author.displayName}</p>
-            </Link>
+          <div className="flex flex-1 flex-col min-w-0">
+            <div className="flex flex-1 items-center">
+              <Link
+                href={`/profile/${post.author.did}`}
+                className="block font-bold text-[17px] leading-5 line-clamp-1 hover:underline underline-offset-2 text-ellipsis decoration-2">
+                {post.author.displayName?.trim() || post.author.handle}
+              </Link>
+              {verification.isVerified && (
+                <VerificationCheck
+                  className="pl-[3px] mt-px shrink-0"
+                  verifier={verification.role === 'verifier'}
+                  size={15}
+                />
+              )}
+            </div>
             <Link
               href={`/profile/${post.author.did}`}
-              className="text-[15px] text-textLight dark:text-textDimmed hover:underline line-clamp-1">
-              <p>@{post.author.handle}</p>
+              className="block text-[15px] text-textLight dark:text-textDimmed hover:underline line-clamp-1">
+              @{post.author.handle}
             </Link>
           </div>
-          <div className="flex-1" />
           <Link
             href={href}
             className="transition-transform hover:scale-110 shrink-0 self-start">
@@ -133,7 +155,7 @@ function PostContent({record}: {record: AppBskyFeedPost.Record | null}) {
         <Link
           key={counter}
           href={segment.link.uri}
-          className="text-blue-400 hover:underline"
+          className="text-blue-500 hover:underline"
           disableTracking={
             !segment.link.uri.startsWith('https://bsky.app') &&
             !segment.link.uri.startsWith('https://go.bsky.app')
diff --git a/bskyembed/src/components/verification-check.tsx b/bskyembed/src/components/verification-check.tsx
new file mode 100644
index 000000000..a9710ea2f
--- /dev/null
+++ b/bskyembed/src/components/verification-check.tsx
@@ -0,0 +1,56 @@
+import {h} from 'preact'
+
+type IconProps = {
+  size: number
+  className?: string
+}
+
+export function VerificationCheck({
+  verifier,
+  ...rest
+}: {verifier: boolean} & IconProps) {
+  return verifier ? <VerifierCheck {...rest} /> : <VerifiedCheck {...rest} />
+}
+
+export function VerifiedCheck({size, className}: IconProps) {
+  return (
+    <svg
+      fill="none"
+      viewBox="0 0 24 24"
+      width={size}
+      height={size}
+      className={className}>
+      <circle cx="12" cy="12" r="11.5" fill="hsl(211, 99%, 53%)" />
+      <path
+        fill="#fff"
+        fillRule="evenodd"
+        clipRule="evenodd"
+        d="M17.659 8.175a1.361 1.361 0 0 1 0 1.925l-6.224 6.223a1.361 1.361 0 0 1-1.925 0L6.4 13.212a1.361 1.361 0 0 1 1.925-1.925l2.149 2.148 5.26-5.26a1.361 1.361 0 0 1 1.925 0Z"
+      />
+    </svg>
+  )
+}
+
+export function VerifierCheck({size, className}: IconProps) {
+  return (
+    <svg
+      fill="none"
+      viewBox="0 0 24 24"
+      width={size}
+      height={size}
+      className={className}>
+      <path
+        fill="hsl(211, 99%, 53%)"
+        fillRule="evenodd"
+        clipRule="evenodd"
+        d="M8.792 1.615a4.154 4.154 0 0 1 6.416 0 4.154 4.154 0 0 0 3.146 1.515 4.154 4.154 0 0 1 4 5.017 4.154 4.154 0 0 0 .777 3.404 4.154 4.154 0 0 1-1.427 6.255 4.153 4.153 0 0 0-2.177 2.73 4.154 4.154 0 0 1-5.781 2.784 4.154 4.154 0 0 0-3.492 0 4.154 4.154 0 0 1-5.78-2.784 4.154 4.154 0 0 0-2.178-2.73A4.154 4.154 0 0 1 .87 11.551a4.154 4.154 0 0 0 .776-3.404A4.154 4.154 0 0 1 5.646 3.13a4.154 4.154 0 0 0 3.146-1.515Z"
+      />
+      <path
+        fill="#fff"
+        fillRule="evenodd"
+        clipRule="evenodd"
+        d="M17.861 8.26a1.438 1.438 0 0 1 0 2.033l-6.571 6.571a1.437 1.437 0 0 1-2.033 0L5.97 13.58a1.438 1.438 0 0 1 2.033-2.033l2.27 2.269 5.554-5.555a1.437 1.437 0 0 1 2.033 0Z"
+      />
+    </svg>
+  )
+}