this repo has no description
0
fork

Configure Feed

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

profile tabs + following indicator

+140 -71
+15 -26
apps/expo/src/app/(tabs)/timeline.tsx
··· 7 7 8 8 import { Button } from "../../components/button"; 9 9 import { FeedPost } from "../../components/feed-post"; 10 + import { Tab, Tabs } from "../../components/tabs"; 10 11 import { useAuthedAgent } from "../../lib/agent"; 11 12 import { assert } from "../../lib/utils/assert"; 12 13 import { cx } from "../../lib/utils/cx"; ··· 84 85 const header = ( 85 86 <> 86 87 <Stack.Screen options={{ headerShown: true }} /> 87 - <View className="w-full flex-row border-b border-neutral-200 bg-white"> 88 - <TouchableOpacity 88 + <Tabs> 89 + <Tab 90 + text="Following" 89 91 onPress={() => setMode("following")} 90 - className={cx( 91 - "ml-4 border-y-2 border-transparent py-3 text-xl", 92 - mode === "following" && "border-b-black", 93 - )} 94 - > 95 - <Text className="font-medium">Following</Text> 96 - </TouchableOpacity> 97 - <TouchableOpacity 92 + active={mode === "following"} 93 + /> 94 + <Tab 95 + text="What's Hot" 98 96 onPress={() => setMode("popular")} 99 - className={cx( 100 - "ml-4 border-y-2 border-transparent py-3 text-xl", 101 - mode === "popular" && "border-b-black", 102 - )} 103 - > 104 - <Text>What&apos;s Hot</Text> 105 - </TouchableOpacity> 106 - <TouchableOpacity 97 + active={mode === "popular"} 98 + /> 99 + <Tab 100 + text="Mutuals" 107 101 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> 114 - </TouchableOpacity> 115 - </View> 102 + active={mode === "mutuals"} 103 + /> 104 + </Tabs> 116 105 </> 117 106 ); 118 107
+13 -1
apps/expo/src/components/profile-info.tsx
··· 50 50 )} 51 51 </View> 52 52 <Text className="mt-1 text-2xl font-medium">{profile.displayName}</Text> 53 - <Text className="text-neutral-500">@{profile.handle}</Text> 53 + <Text> 54 + {profile.viewer?.followedBy && ( 55 + <> 56 + <Text className="bg-neutral-100 px-1 font-semibold"> 57 + {" Follows you "} 58 + </Text>{" "} 59 + </> 60 + )} 61 + <Text className="text-neutral-500">@{profile.handle}</Text> 62 + </Text> 54 63 <View className="mt-3 flex-row"> 55 64 <Text> 56 65 <Text className="font-bold">{profile.followersCount}</Text>{" "} ··· 58 67 </Text> 59 68 <Text className="ml-4"> 60 69 <Text className="font-bold">{profile.followsCount}</Text> Following 70 + </Text> 71 + <Text className="ml-4"> 72 + <Text className="font-bold">{profile.postsCount ?? 0}</Text> Posts 61 73 </Text> 62 74 </View> 63 75 {profile.description && (
+50 -21
apps/expo/src/components/profile-view.tsx
··· 8 8 import { useAuthedAgent } from "../lib/agent"; 9 9 import { FeedPost } from "./feed-post"; 10 10 import { ProfileInfo } from "./profile-info"; 11 + import { Tab, Tabs } from "./tabs"; 11 12 12 13 interface Props { 13 14 handle: string; 14 15 } 15 16 16 17 export const ProfileView = ({ handle }: Props) => { 17 - const [withReplies] = useState(false); 18 + const [mode, setMode] = useState<"posts" | "replies" | "likes">("posts"); 18 19 const [atTop, setAtTop] = useState(true); 19 20 const agent = useAuthedAgent(); 20 21 ··· 26 27 }); 27 28 28 29 const timeline = useInfiniteQuery({ 29 - queryKey: ["profile", handle, "feed"], 30 + queryKey: ["profile", handle, "feed", mode], 30 31 queryFn: async ({ pageParam }) => { 31 - const timeline = await agent.getAuthorFeed({ 32 - actor: handle, 33 - cursor: pageParam as string | undefined, 34 - }); 35 - return timeline.data; 32 + switch (mode) { 33 + case "posts": 34 + case "replies": 35 + const feed = await agent.getAuthorFeed({ 36 + actor: handle, 37 + cursor: pageParam as string | undefined, 38 + }); 39 + return feed.data; 40 + case "likes": 41 + // ???????? 42 + return { 43 + feed: [], 44 + cursor: undefined, 45 + }; 46 + } 36 47 }, 37 48 getNextPageParam: (lastPage) => lastPage.cursor, 38 49 }); ··· 42 53 const flat = timeline.data.pages.flatMap((page) => page.feed); 43 54 return flat 44 55 .map((item) => 45 - item.reply 46 - ? withReplies 47 - ? [ 48 - { item: { post: item.reply.parent }, hasReply: true }, 49 - { item, hasReply: false }, 50 - ] 51 - : [] 56 + mode === "replies" && item.reply 57 + ? [ 58 + { item: { post: item.reply.parent }, hasReply: true }, 59 + { item, hasReply: false }, 60 + ] 52 61 : [{ item, hasReply: false }], 53 62 ) 54 - .flat() 55 - .filter(Boolean); 56 - }, [timeline, withReplies]); 63 + .flat(); 64 + }, [timeline, mode]); 57 65 58 66 switch (profile.status) { 59 67 case "loading": ··· 107 115 }} 108 116 /> 109 117 <FlashList 110 - data={data} 111 - renderItem={({ item: { hasReply, item } }) => ( 112 - <FeedPost item={item} hasReply={hasReply} /> 113 - )} 118 + data={[null, ...data]} 119 + renderItem={({ item }) => 120 + item === null ? ( 121 + <Tabs> 122 + <Tab 123 + text="Posts" 124 + active={mode === "posts"} 125 + onPress={() => void setMode("posts")} 126 + /> 127 + <Tab 128 + text="Posts & Replies" 129 + active={mode === "replies"} 130 + onPress={() => void setMode("replies")} 131 + /> 132 + <Tab 133 + text="Likes" 134 + active={mode === "likes"} 135 + onPress={() => void setMode("likes")} 136 + /> 137 + </Tabs> 138 + ) : ( 139 + <FeedPost {...item} /> 140 + ) 141 + } 142 + stickyHeaderIndices={[0]} 114 143 onEndReachedThreshold={0.5} 115 144 onEndReached={() => void timeline.fetchNextPage()} 116 145 // onRefresh={() => {
+39
apps/expo/src/components/tabs.tsx
··· 1 + import { Text, TouchableOpacity, View } from "react-native"; 2 + 3 + import { cx } from "../lib/utils/cx"; 4 + 5 + export const Tabs = ({ 6 + children, 7 + className, 8 + }: React.PropsWithChildren<{ className?: string }>) => { 9 + return ( 10 + <View 11 + className={cx( 12 + "w-full flex-row border-b border-neutral-200 bg-white", 13 + className, 14 + )} 15 + > 16 + {children} 17 + </View> 18 + ); 19 + }; 20 + 21 + interface Props { 22 + active: boolean; 23 + onPress: () => void; 24 + text: string; 25 + } 26 + 27 + export const Tab = ({ active, onPress, text }: Props) => { 28 + return ( 29 + <TouchableOpacity 30 + onPress={onPress} 31 + className={cx( 32 + "ml-4 border-y-2 border-transparent py-3 text-xl", 33 + active && "border-b-black", 34 + )} 35 + > 36 + <Text className="font-medium">{text}</Text> 37 + </TouchableOpacity> 38 + ); 39 + };
+1 -1
package.json
··· 27 27 "eslint": "^8.38.0", 28 28 "prettier": "^2.8.7", 29 29 "prettier-plugin-tailwindcss": "^0.2.7", 30 - "turbo": "^1.9.1", 30 + "turbo": "^1.9.3", 31 31 "typescript": "^5.0.4" 32 32 } 33 33 }
+22 -22
pnpm-lock.yaml
··· 26 26 specifier: ^0.2.7 27 27 version: 0.2.7(@ianvs/prettier-plugin-sort-imports@3.7.2)(prettier@2.8.7) 28 28 turbo: 29 - specifier: ^1.9.1 30 - version: 1.9.1 29 + specifier: ^1.9.3 30 + version: 1.9.3 31 31 typescript: 32 32 specifier: ^5.0.4 33 33 version: 5.0.4 ··· 10004 10004 typescript: 5.0.4 10005 10005 dev: false 10006 10006 10007 - /turbo-darwin-64@1.9.1: 10008 - resolution: {integrity: sha512-IX/Ph4CO80lFKd9pPx3BWpN2dynt6mcUFifyuHUNVkOP1Usza/G9YuZnKQFG6wUwKJbx40morFLjk1TTeLe04w==} 10007 + /turbo-darwin-64@1.9.3: 10008 + resolution: {integrity: sha512-0dFc2cWXl82kRE4Z+QqPHhbEFEpUZho1msHXHWbz5+PqLxn8FY0lEVOHkq5tgKNNEd5KnGyj33gC/bHhpZOk5g==} 10009 10009 cpu: [x64] 10010 10010 os: [darwin] 10011 10011 requiresBuild: true 10012 10012 dev: false 10013 10013 optional: true 10014 10014 10015 - /turbo-darwin-arm64@1.9.1: 10016 - resolution: {integrity: sha512-6tCbmIboy9dTbhIZ/x9KIpje73nvxbiyVnHbr9xKnsxLJavD0xqjHZzbL5U2tHp8chqmYf0E4WYOXd+XCNg+OQ==} 10015 + /turbo-darwin-arm64@1.9.3: 10016 + resolution: {integrity: sha512-1cYbjqLBA2zYE1nbf/qVnEkrHa4PkJJbLo7hnuMuGM0bPzh4+AnTNe98gELhqI1mkTWBu/XAEeF5u6dgz0jLNA==} 10017 10017 cpu: [arm64] 10018 10018 os: [darwin] 10019 10019 requiresBuild: true 10020 10020 dev: false 10021 10021 optional: true 10022 10022 10023 - /turbo-linux-64@1.9.1: 10024 - resolution: {integrity: sha512-ti8XofnJFO1XaadL92lYJXgxb0VBl03Yu9VfhxkOTywFe7USTLBkJcdvQ4EpFk/KZwLiTdCmT2NQVxsG4AxBiQ==} 10023 + /turbo-linux-64@1.9.3: 10024 + resolution: {integrity: sha512-UuBPFefawEwpuxh5pM9Jqq3q4C8M0vYxVYlB3qea/nHQ80pxYq7ZcaLGEpb10SGnr3oMUUs1zZvkXWDNKCJb8Q==} 10025 10025 cpu: [x64] 10026 10026 os: [linux] 10027 10027 requiresBuild: true 10028 10028 dev: false 10029 10029 optional: true 10030 10030 10031 - /turbo-linux-arm64@1.9.1: 10032 - resolution: {integrity: sha512-XYvIbeiCCCr+ENujd2Jtck/lJPTKWb8T2MSL/AEBx21Zy3Sa7HgrQX6LX0a0pNHjaleHz00XXt1D0W5hLeP+tA==} 10031 + /turbo-linux-arm64@1.9.3: 10032 + resolution: {integrity: sha512-vUrNGa3hyDtRh9W0MkO+l1dzP8Co2gKnOVmlJQW0hdpOlWlIh22nHNGGlICg+xFa2f9j4PbQlWTsc22c019s8Q==} 10033 10033 cpu: [arm64] 10034 10034 os: [linux] 10035 10035 requiresBuild: true 10036 10036 dev: false 10037 10037 optional: true 10038 10038 10039 - /turbo-windows-64@1.9.1: 10040 - resolution: {integrity: sha512-x7lWAspe4/v3XQ0gaFRWDX/X9uyWdhwFBPEfb8BA0YKtnsrPOHkV0mRHCRrXzvzjA7pcDCl2agGzb7o863O+Jg==} 10039 + /turbo-windows-64@1.9.3: 10040 + resolution: {integrity: sha512-0BZ7YaHs6r+K4ksqWus1GKK3W45DuDqlmfjm/yuUbTEVc8szmMCs12vugU2Zi5GdrdJSYfoKfEJ/PeegSLIQGQ==} 10041 10041 cpu: [x64] 10042 10042 os: [win32] 10043 10043 requiresBuild: true 10044 10044 dev: false 10045 10045 optional: true 10046 10046 10047 - /turbo-windows-arm64@1.9.1: 10048 - resolution: {integrity: sha512-QSLNz8dRBLDqXOUv/KnoesBomSbIz2Huef/a3l2+Pat5wkQVgMfzFxDOnkK5VWujPYXz+/prYz+/7cdaC78/kw==} 10047 + /turbo-windows-arm64@1.9.3: 10048 + resolution: {integrity: sha512-QJUYLSsxdXOsR1TquiOmLdAgtYcQ/RuSRpScGvnZb1hY0oLc7JWU0llkYB81wVtWs469y8H9O0cxbKwCZGR4RQ==} 10049 10049 cpu: [arm64] 10050 10050 os: [win32] 10051 10051 requiresBuild: true 10052 10052 dev: false 10053 10053 optional: true 10054 10054 10055 - /turbo@1.9.1: 10056 - resolution: {integrity: sha512-Rqe8SP96e53y4Pk29kk2aZbA8EF11UtHJ3vzXJseadrc1T3V6UhzvAWwiKJL//x/jojyOoX1axnoxmX3UHbZ0g==} 10055 + /turbo@1.9.3: 10056 + resolution: {integrity: sha512-ID7mxmaLUPKG/hVkp+h0VuucB1U99RPCJD9cEuSEOdIPoSIuomcIClEJtKamUsdPLhLCud+BvapBNnhgh58Nzw==} 10057 10057 hasBin: true 10058 10058 requiresBuild: true 10059 10059 optionalDependencies: 10060 - turbo-darwin-64: 1.9.1 10061 - turbo-darwin-arm64: 1.9.1 10062 - turbo-linux-64: 1.9.1 10063 - turbo-linux-arm64: 1.9.1 10064 - turbo-windows-64: 1.9.1 10065 - turbo-windows-arm64: 1.9.1 10060 + turbo-darwin-64: 1.9.3 10061 + turbo-darwin-arm64: 1.9.3 10062 + turbo-linux-64: 1.9.3 10063 + turbo-linux-arm64: 1.9.3 10064 + turbo-windows-64: 1.9.3 10065 + turbo-windows-arm64: 1.9.3 10066 10066 dev: false 10067 10067 10068 10068 /type-check@0.4.0: