this repo has no description
0
fork

Configure Feed

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

add mutuals feed

+49 -5
+1
apps/expo/src/app/(tabs)/search.tsx
··· 4 4 5 5 export default function SearchPage() { 6 6 const agent = useAuthedAgent(); 7 + 7 8 return ( 8 9 <View className="flex-1 justify-center"> 9 10 <Text className="text-center text-xl">Coming soon</Text>
+48 -5
apps/expo/src/app/(tabs)/timeline.tsx
··· 1 1 import { useMemo, useState } from "react"; 2 2 import { ActivityIndicator, Text, TouchableOpacity, View } from "react-native"; 3 3 import { Stack } from "expo-router"; 4 + import { AppBskyFeedDefs } from "@atproto/api"; 4 5 import { FlashList } from "@shopify/flash-list"; 5 6 import { useInfiniteQuery } from "@tanstack/react-query"; 6 7 7 8 import { Button } from "../../components/button"; 8 9 import { FeedPost } from "../../components/feed-post"; 9 10 import { useAuthedAgent } from "../../lib/agent"; 11 + import { assert } from "../../lib/utils/assert"; 10 12 import { cx } from "../../lib/utils/cx"; 11 13 14 + const actorFromPost = (item: AppBskyFeedDefs.FeedViewPost) => { 15 + if (AppBskyFeedDefs.isReasonRepost(item.reason)) { 16 + assert(AppBskyFeedDefs.validateReasonRepost(item.reason)); 17 + return item.reason.by.did; 18 + } else { 19 + return item.post.author.did; 20 + } 21 + }; 22 + 12 23 export default function Timeline() { 13 - const [mode, setMode] = useState<"popular" | "following">("following"); 24 + const [mode, setMode] = useState<"popular" | "following" | "mutuals">( 25 + "following", 26 + ); 14 27 const agent = useAuthedAgent(); 15 28 16 29 const timeline = useInfiniteQuery({ ··· 23 36 }); 24 37 return popular.data; 25 38 case "following": 26 - const timeline = await agent.getTimeline({ 39 + const following = await agent.getTimeline({ 40 + cursor: pageParam as string | undefined, 41 + }); 42 + return following.data; 43 + case "mutuals": 44 + const all = await agent.getTimeline({ 27 45 cursor: pageParam as string | undefined, 28 46 }); 29 - return timeline.data; 47 + const actors = new Set<string>(); 48 + for (const item of all.data.feed) { 49 + const actor = actorFromPost(item); 50 + actors.add(actor); 51 + } 52 + const profiles = await agent.getProfiles({ actors: [...actors] }); 53 + return { 54 + feed: all.data.feed.filter((item) => { 55 + const actor = actorFromPost(item); 56 + const profile = profiles.data.profiles.find( 57 + (profile) => profile.did === actor, 58 + ); 59 + if (!profile) return false; 60 + return profile.viewer?.following && profile.viewer?.followedBy; 61 + }), 62 + cursor: all.data.cursor, 63 + }; 30 64 } 31 65 }, 32 66 getNextPageParam: (lastPage) => lastPage.cursor, ··· 34 68 35 69 const data = useMemo(() => { 36 70 if (timeline.status !== "success") return []; 37 - const flat = timeline.data.pages.flatMap((page) => page.feed); 38 - return flat 71 + const flattened = timeline.data.pages.flatMap((page) => page.feed); 72 + return flattened 39 73 .map((item) => 40 74 item.reply 41 75 ? [ ··· 68 102 )} 69 103 > 70 104 <Text>What&apos;s Hot</Text> 105 + </TouchableOpacity> 106 + <TouchableOpacity 107 + onPress={() => setMode("mutuals")} 108 + className={cx( 109 + "ml-4 border-y-2 border-transparent py-3 text-xl", 110 + mode === "mutuals" && "border-b-black", 111 + )} 112 + > 113 + <Text>Mutuals</Text> 71 114 </TouchableOpacity> 72 115 </View> 73 116 </>