this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

add morrrrrreeeee reply logic

+68 -21
+11 -9
apps/expo/src/app/(tabs)/notifications.tsx
··· 8 8 } from "react-native"; 9 9 import { Link, Stack } from "expo-router"; 10 10 import { 11 + AppBskyEmbedImages, 11 12 AppBskyFeedDefs, 12 13 AppBskyFeedPost, 13 14 AppBskyNotificationListNotifications, ··· 275 276 </View> 276 277 <Text className="mt-2 text-base"> 277 278 <Text className="font-medium"> 278 - {actors[0].displayName?.trim() ?? actors[0].handle} 279 + {actors[0].displayName?.trim() ?? `@${actors[0].handle}`} 279 280 {actors.length > 1 && ` and ${actors.length - 1} others`} 280 281 </Text> 281 282 {" " + action} ··· 348 349 <Text className="text-neutral-500"> 349 350 <RichText value={post.data.post.record.text} size="sm" /> 350 351 </Text> 351 - {post.data.post.embed && ( 352 - <Embed 353 - uri={post.data.post.uri} 354 - content={post.data.post.embed} 355 - truncate 356 - depth={1} 357 - /> 358 - )} 352 + {post.data.post.embed && 353 + AppBskyEmbedImages.isView(post.data.post.embed) && ( 354 + <Embed 355 + uri={post.data.post.uri} 356 + content={post.data.post.embed} 357 + truncate 358 + depth={1} 359 + /> 360 + )} 359 361 </View> 360 362 ); 361 363 }
+1
apps/expo/src/app/(tabs)/timeline.tsx
··· 161 161 item={item} 162 162 hasReply={hasReply} 163 163 isReply={data[index - 1]?.hasReply} 164 + inlineParent={!data[index - 1]?.hasReply} 164 165 /> 165 166 )} 166 167 onEndReachedThreshold={0.5}
+56 -12
apps/expo/src/components/feed-post.tsx
··· 1 1 import { Image, Pressable, Text, TouchableOpacity, View } from "react-native"; 2 2 import { Link } from "expo-router"; 3 3 import { AppBskyFeedDefs, AppBskyFeedPost } from "@atproto/api"; 4 + import { useQuery } from "@tanstack/react-query"; 4 5 import { 5 6 Heart, 6 7 MessageCircle, ··· 9 10 User, 10 11 } from "lucide-react-native"; 11 12 13 + import { useAuthedAgent } from "../lib/agent"; 12 14 import { useLike, useRepost } from "../lib/hooks"; 13 15 import { assert } from "../lib/utils/assert"; 14 16 import { cx } from "../lib/utils/cx"; ··· 45 47 assert(AppBskyFeedPost.validateRecord(item.post.record)); 46 48 47 49 const displayInlineParent = inlineParent || !!item.reason; 48 - 49 - // TODO - don't nest feedposts! 50 50 51 51 return ( 52 52 <View ··· 102 102 </TouchableOpacity> 103 103 </Link> 104 104 {/* inline "replying to so-and-so" */} 105 - {displayInlineParent && !!item.reply && ( 106 - <TouchableOpacity className="flex-row items-center"> 107 - <MessageCircle size={12} color="#737373" /> 108 - <Text className="ml-1 text-neutral-500"> 109 - replying to{" "} 110 - {item.reply.parent.author.displayName ?? 111 - `@${item.reply.parent.author.handle}`} 112 - </Text> 113 - </TouchableOpacity> 114 - )} 105 + {displayInlineParent && 106 + (!!item.reply ? ( 107 + <Link 108 + href={`/profile/${ 109 + item.reply.parent.author.handle 110 + }/post/${item.reply.parent.uri.split("/").pop()}`} 111 + asChild 112 + > 113 + <TouchableOpacity className="flex-row items-center"> 114 + <MessageCircle size={12} color="#737373" /> 115 + <Text className="ml-1 text-neutral-500"> 116 + replying to{" "} 117 + {item.reply.parent.author.displayName ?? 118 + `@${item.reply.parent.author.handle}`} 119 + </Text> 120 + </TouchableOpacity> 121 + </Link> 122 + ) : ( 123 + !!item.post.record.reply && ( 124 + <ReplyParentAuthor uri={item.post.record.reply!.parent.uri} /> 125 + ) 126 + ))} 115 127 {/* text content */} 116 128 <Link href={postHref} asChild> 117 129 <Pressable className="my-0.5"> ··· 183 195 </Link> 184 196 ); 185 197 }; 198 + 199 + const ReplyParentAuthor = ({ uri }: { uri: string }) => { 200 + const agent = useAuthedAgent(); 201 + const { data } = useQuery({ 202 + queryKey: ["post", uri], 203 + queryFn: async () => { 204 + const thread = await agent.getPostThread({ 205 + uri, 206 + depth: 0, 207 + }); 208 + if (AppBskyFeedDefs.isThreadViewPost(thread.data.thread)) { 209 + assert(AppBskyFeedDefs.validateThreadViewPost(thread.data.thread)); 210 + return thread.data.thread.post; 211 + } 212 + throw new Error("invalid post"); 213 + }, 214 + }); 215 + if (!data) return null; 216 + return ( 217 + <Link 218 + href={`/profile/${data.author.handle}/post/${data.uri.split("/").pop()}`} 219 + asChild 220 + > 221 + <TouchableOpacity className="flex-row items-center"> 222 + <MessageCircle size={12} color="#737373" /> 223 + <Text className="ml-1 text-neutral-500"> 224 + replying to {data.author.displayName ?? `@${data.author.handle}`} 225 + </Text> 226 + </TouchableOpacity> 227 + </Link> 228 + ); 229 + };