diff options
author | Samuel Newman <mozzius@protonmail.com> | 2024-04-13 03:58:40 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-04-13 03:58:40 +0100 |
commit | 4b3ec5573241b9c71504dfd0bd5f181cbde19a49 (patch) | |
tree | 698c2463b389cdf6e14536610e8f96f200ddaaa3 /bskyembed/src/components/post.tsx | |
parent | 8e29b1f63309ef9ac2da21f62e03b66d477244e9 (diff) | |
download | voidsky-4b3ec5573241b9c71504dfd0bd5f181cbde19a49.tar.zst |
[Embeds] Embed subdomain landing page (#3501)
* add build output to web build * simplify post-build step by copying everything at once * make script that converts placeholder -> iframe * dynamically resize iframe based on inner content Requires the iframe content to `postMessage` its height back up to the parent * add lang to embed * svg explicit height -> viewBox * add build output to web build * simplify post-build step by copying everything at once * attempt to fix go embed issue * rm changes to bskyweb * remove another bskyweb change * embed landing page * Drop xl breakpoint, too far down * Remove pointer enter behavior * Avoid button width jump * Escape HTML --------- Co-authored-by: Dan Abramov <dan.abramov@gmail.com>
Diffstat (limited to 'bskyembed/src/components/post.tsx')
-rw-r--r-- | bskyembed/src/components/post.tsx | 150 |
1 files changed, 150 insertions, 0 deletions
diff --git a/bskyembed/src/components/post.tsx b/bskyembed/src/components/post.tsx new file mode 100644 index 000000000..dcbf3e336 --- /dev/null +++ b/bskyembed/src/components/post.tsx @@ -0,0 +1,150 @@ +import {AppBskyFeedDefs, AppBskyFeedPost, RichText} from '@atproto/api' +import {h} from 'preact' + +import replyIcon from '../../assets/bubble_filled_stroke2_corner2_rounded.svg' +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 {getRkey, niceDate} from '../utils' +import {Container} from './container' +import {Embed} from './embed' +import {Link} from './link' + +interface Props { + thread: AppBskyFeedDefs.ThreadViewPost +} + +export function Post({thread}: Props) { + const post = thread.post + + let record: AppBskyFeedPost.Record | null = null + if (AppBskyFeedPost.isRecord(post.record)) { + record = post.record + } + + 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"> + <Link href={`/profile/${post.author.did}`} className="rounded-full"> + <img + src={post.author.avatar} + className="w-10 h-10 rounded-full bg-neutral-300 shrink-0" + /> + </Link> + <div className="flex-1"> + <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> + <Link + href={`/profile/${post.author.did}`} + className="text-[15px] text-textLight hover:underline line-clamp-1"> + <p>@{post.author.handle}</p> + </Link> + </div> + <Link + href={href} + className="transition-transform hover:scale-110 shrink-0 self-start"> + <img src={logo as string} className="h-8" /> + </Link> + </div> + <PostContent record={record} /> + <Embed content={post.embed} /> + <time + datetime={new Date(post.indexedAt).toISOString()} + className="text-textLight mt-1 text-sm"> + {niceDate(post.indexedAt)} + </time> + <div className="border-t w-full pt-2.5 flex items-center gap-5 text-sm"> + {!!post.likeCount && ( + <div className="flex items-center gap-2 cursor-pointer"> + <img src={likeIcon as string} className="w-5 h-5" /> + <p className="font-bold text-neutral-500 mb-px"> + {post.likeCount} + </p> + </div> + )} + {!!post.repostCount && ( + <div className="flex items-center gap-2 cursor-pointer"> + <img src={repostIcon as string} className="w-5 h-5" /> + <p className="font-bold text-neutral-500 mb-px"> + {post.repostCount} + </p> + </div> + )} + <div className="flex items-center gap-2 cursor-pointer"> + <img src={replyIcon as string} className="w-5 h-5" /> + <p className="font-bold text-neutral-500 mb-px">Reply</p> + </div> + <div className="flex-1" /> + <p className="cursor-pointer text-brand font-bold hover:underline hidden min-[450px]:inline"> + {post.replyCount + ? `Read ${post.replyCount} ${ + post.replyCount > 1 ? 'replies' : 'reply' + } on Bluesky` + : `View on Bluesky`} + </p> + <p className="cursor-pointer text-brand font-bold hover:underline min-[450px]:hidden"> + <span className="hidden min-[380px]:inline">View on </span>Bluesky + </p> + </div> + </div> + </Container> + ) +} + +function PostContent({record}: {record: AppBskyFeedPost.Record | null}) { + if (!record) return null + + const rt = new RichText({ + text: record.text, + facets: record.facets, + }) + + const richText = [] + + let counter = 0 + for (const segment of rt.segments()) { + if (segment.isLink() && segment.link) { + richText.push( + <Link + key={counter} + href={segment.link.uri} + className="text-blue-400 hover:underline"> + {segment.text} + </Link>, + ) + } else if (segment.isMention() && segment.mention) { + richText.push( + <Link + key={counter} + href={`/profile/${segment.mention.did}`} + className="text-blue-500 hover:underline"> + {segment.text} + </Link>, + ) + } else if (segment.isTag() && segment.tag) { + richText.push( + <Link + key={counter} + href={`/tag/${segment.tag.tag}`} + className="text-blue-500 hover:underline"> + {segment.text} + </Link>, + ) + } else { + richText.push(segment.text) + } + + counter++ + } + + return ( + <p className="min-[300px]:text-lg leading-6 break-word break-words whitespace-pre-wrap"> + {richText} + </p> + ) +} |