+ + {record.author.displayName} + + + @{record.author.handle} + +
+{text}
} +{children}
+{toNiceDomain(content.external.uri)}
+{content.external.title}
+{content.external.description}
+{title}
+{subtitle}
+{description}
} + + ); +} + +// just the thumbnail and a play button +function VideoEmbed({ content }: { content: AppBskyEmbedVideo.View }) { + let aspectRatio = 1; + + if (content.aspectRatio) { + const { width, height } = content.aspectRatio; + aspectRatio = clamp(width / height, 1 / 1, 3 / 1); + } + + return ( +{content.record.name}
++ Starter pack by{" "} + {content.creator.displayName || `@${content.creator.handle}`} +
+{content.record.description}
+ )} + {!!content.joinedAllTimeCount && content.joinedAllTimeCount > 50 && ( ++ {content.joinedAllTimeCount} users have joined! +
+ )} +{author.displayName}
+ + +@{author.handle}
+ +{prettyNumber(likeCount)}
+{prettyNumber(repostCount)}
+Reply
++ {replyCount + ? `Read ${prettyNumber(replyCount)} ${ + replyCount > 1 ? "replies" : "reply" + } on Bluesky` + : "View on Bluesky"} +
++ View on Bluesky +
+{richText}
; +} diff --git a/packages/solid/src/components/PostContent/post-content.module.css b/packages/solid/src/components/PostContent/post-content.module.css new file mode 100644 index 0000000..d19b48e --- /dev/null +++ b/packages/solid/src/components/PostContent/post-content.module.css @@ -0,0 +1,19 @@ +.content { + font-size: 1rem; + line-height: 1.5rem; + overflow-wrap: break-word; + white-space: pre-wrap; +} +@media (min-width: 300px) { + .content { + font-size: 1.125rem; + line-height: 1.75rem; + } +} + +.richText { + color: var(--post-link-font-color); +} +.richText:hover { + text-decoration-line: underline; +} diff --git a/packages/solid/src/components/PostContent/unicode.ts b/packages/solid/src/components/PostContent/unicode.ts new file mode 100644 index 0000000..8a8809d --- /dev/null +++ b/packages/solid/src/components/PostContent/unicode.ts @@ -0,0 +1,28 @@ +const encoder = new TextEncoder(); +const decoder = new TextDecoder(); + +export class UnicodeString { + utf16: string; + utf8: Uint8Array; + + constructor(utf16: string) { + this.utf16 = utf16; + this.utf8 = encoder.encode(utf16); + } + + get length() { + return this.utf8.byteLength; + } + + slice(start?: number, end?: number): string { + return decoder.decode(this.utf8.slice(start, end)); + } + + utf16IndexToUtf8Index(i: number) { + return encoder.encode(this.utf16.slice(0, i)).byteLength; + } + + toString() { + return this.utf16; + } +} diff --git a/packages/solid/src/components/PostContent/utils.ts b/packages/solid/src/components/PostContent/utils.ts new file mode 100644 index 0000000..6355fb5 --- /dev/null +++ b/packages/solid/src/components/PostContent/utils.ts @@ -0,0 +1,106 @@ +import type { + AppBskyRichtextFacet, + Facet, + FacetLink, + FacetMention, + FacetTag, + RichTextProps, +} from "@atproto/api"; +import { hasProp, isObj } from "../../utils"; +import { UnicodeString } from "./unicode"; + +class RichTextSegment { + constructor( + public text: string, + public facet?: Facet, + ) {} + + get link(): FacetLink | undefined { + return this.facet?.features.find(isLink); + } + + get mention(): FacetMention | undefined { + return this.facet?.features.find(isMention); + } + + get tag(): FacetTag | undefined { + return this.facet?.features.find(isTag); + } +} + +export function* rtSegments(props: Pick{props.error}
+Post not found, it may have been deleted.
+