diff --git a/app/blog/[identifier]/index.tsx b/app/blog/[identifier]/index.tsx
index 1281ed6..74700dc 100644
--- a/app/blog/[identifier]/index.tsx
+++ b/app/blog/[identifier]/index.tsx
@@ -47,7 +47,7 @@ export default function BlogPage() {
const profile = await resolveProfile(blogId!);
setProfile(profile);
const fetchedThreads = await fetchBlogPosts(profile, authCtx);
- console.log(fetchedThreads);
+ // console.log(fetchedThreads);
setThreads(fetchedThreads);
setLoading(false);
})();
diff --git a/src/components/primitive/Link.tsx b/src/components/primitive/Link.tsx
new file mode 100644
index 0000000..fa3c13f
--- /dev/null
+++ b/src/components/primitive/Link.tsx
@@ -0,0 +1,28 @@
+import { Linking, Platform, Text as RNText, TextProps } from "react-native";
+
+export function Link(props: TextProps & { uri?: string }) {
+ const { uri } = props;
+
+ const openLink = () => {
+ if (Platform.OS === "web") {
+ window.open(uri!, "_blank");
+ } else {
+ Linking.openURL(uri!);
+ }
+ };
+
+ return (
+
+ );
+}
diff --git a/src/lib/richtext/render.tsx b/src/lib/richtext/render.tsx
index dc087ab..9472ade 100644
--- a/src/lib/richtext/render.tsx
+++ b/src/lib/richtext/render.tsx
@@ -1,12 +1,13 @@
-import { ReactElement, useCallback } from "react";
-import { RichTextBlocks, RichTextFacet } from "./richtext";
-import { dev } from "@/src/lexicons";
+import { Link } from "@/src/components/primitive/Link";
import { Text } from "@/src/components/primitive/Text";
-import { StyleSheet } from "react-native";
+import { dev } from "@/src/lexicons";
+import { RichText as AtpRichTextAPI } from "@atproto/api";
+import { ReactElement, useMemo } from "react";
+import { StyleProp, StyleSheet, TextStyle } from "react-native";
+import { RichTextBlocks, RichTextFacet } from "./richtext";
const formatStyles = StyleSheet.create({
- regular: {
- },
+ regular: {},
h2: {
fontSize: 20,
},
@@ -27,28 +28,99 @@ const formatStyles = StyleSheet.create({
borderLeftColor: "#7e7e7e",
color: "#9c9c9c",
paddingLeft: 15,
- paddingVertical: 6
+ paddingVertical: 6,
+ },
+});
+
+const formatStyleMap = new Map(Object.entries(formatStyles));
+
+function renderText(text: string, facets?: RichTextFacet[]) {
+ const rtApi = new AtpRichTextAPI({
+ text: text,
+ facets: (facets as any) ?? [],
+ });
+
+ const elements: ReactElement[] = [];
+
+ let i = 0;
+ for (const segment of rtApi.segments()) {
+ const segmentStyles: StyleProp[] = [];
+ let linkHref: string | undefined;
+
+ for (const feature of segment.facet?.features ?? []) {
+ if (feature.$type === "dev.maelstrom.bramblr.richtext.facet#color") {
+ const colorFeature =
+ feature as dev.maelstrom.bramblr.richtext.facet.Color;
+
+ segmentStyles.push({
+ color: /(#[a-fA-F\d]{3,6})/.exec(colorFeature.color ?? "")?.[1] ?? "",
+ });
+ } else if (
+ feature.$type === "dev.maelstrom.bramblr.richtext.facet#link"
+ ) {
+ const linkFeature =
+ feature as dev.maelstrom.bramblr.richtext.facet.Link;
+
+ linkHref = /(^(?:https|http):\/\/.*)/.exec(linkFeature.uri ?? "")?.[1];
+ segmentStyles.push({ textDecorationLine: "underline" });
+ }
+ }
+
+ if (linkHref) {
+ // If link
+ elements.push(
+
+ {segment.text}
+ ,
+ );
+ } else {
+ // If normal text
+ elements.push(
+
+ {segment.text}
+ ,
+ );
+ }
+ i++;
}
-})
+ return elements;
+}
+
+function TextBlock({
+ text,
+ format,
+ facets,
+}: {
+ text: string;
+ format?: string;
+ facets?: RichTextFacet[];
+}) {
+ const formatStyle =
+ (format ? formatStyleMap.get(format) : undefined) ?? formatStyles.regular;
-const formatStyleMap = new Map(Object.entries(formatStyles))
+ const renderedText = useMemo(() => renderText(text, facets), [text, facets]);
-function TextBlock({ text, format, facets }: { text: string, format?: string, facets?: RichTextFacet[] }) {
- const formatStyle = (format ? formatStyleMap.get(format) : undefined) ?? formatStyles.regular
- return {text}
+ return {renderedText};
}
function renderRichText(richTextBlocks: RichTextBlocks) {
- const paragraphs: ReactElement[] = []
+ const paragraphs: ReactElement[] = [];
for (const [i, block] of richTextBlocks.entries()) {
if (block.$type === "dev.maelstrom.bramblr.richtext.block#text") {
- const textBlock = block as dev.maelstrom.bramblr.richtext.block.Text
- paragraphs.push()
+ const textBlock = block as dev.maelstrom.bramblr.richtext.block.Text;
+ paragraphs.push(
+ ,
+ );
}
}
- return paragraphs
+ return paragraphs;
}
export default function RichTextBody({
@@ -56,7 +128,7 @@ export default function RichTextBody({
}: {
richText: RichTextBlocks;
}) {
- const render = useCallback(() => renderRichText(richText), [richText]);
+ const renderedBlocks = useMemo(() => renderRichText(richText), [richText]);
- return <>{render()}>;
+ return <>{renderedBlocks}>;
}